Files
Memoh/internal/channel/adapters/wecom/config.go
T
BBQ 599bfb5ca8 fix(wecom): pass lint and typo checks
Fix WeCom adapter typos and strict Go lint findings (gosec/bodyclose/errcheck/revive) while keeping runtime behavior unchanged.
2026-03-11 02:14:00 +08:00

211 lines
5.1 KiB
Go

package wecom
import (
"errors"
"strconv"
"strings"
"github.com/memohai/memoh/internal/channel"
)
type adapterConfig struct {
BotID string
Credential string
WSURL string
HeartbeatSeconds int
AckTimeoutSeconds int
WriteTimeoutSeconds int
ReadTimeoutSeconds int
}
type UserConfig struct {
ChatID string
UserID string
}
func normalizeConfig(raw map[string]any) (map[string]any, error) {
cfg, err := parseConfig(raw)
if err != nil {
return nil, err
}
out := map[string]any{
"botId": cfg.BotID,
"secret": cfg.Credential,
}
if cfg.WSURL != "" {
out["wsUrl"] = cfg.WSURL
}
if cfg.HeartbeatSeconds > 0 {
out["heartbeatSeconds"] = cfg.HeartbeatSeconds
}
if cfg.AckTimeoutSeconds > 0 {
out["ackTimeoutSeconds"] = cfg.AckTimeoutSeconds
}
if cfg.WriteTimeoutSeconds > 0 {
out["writeTimeoutSeconds"] = cfg.WriteTimeoutSeconds
}
if cfg.ReadTimeoutSeconds > 0 {
out["readTimeoutSeconds"] = cfg.ReadTimeoutSeconds
}
return out, nil
}
func normalizeUserConfig(raw map[string]any) (map[string]any, error) {
cfg, err := parseUserConfig(raw)
if err != nil {
return nil, err
}
out := map[string]any{}
if cfg.ChatID != "" {
out["chat_id"] = cfg.ChatID
}
if cfg.UserID != "" {
out["user_id"] = cfg.UserID
}
return out, nil
}
func parseConfig(raw map[string]any) (adapterConfig, error) {
cfg := adapterConfig{
BotID: strings.TrimSpace(channel.ReadString(raw, "botId", "bot_id")),
Credential: strings.TrimSpace(channel.ReadString(raw, "secret")),
WSURL: strings.TrimSpace(channel.ReadString(raw, "wsUrl", "ws_url")),
}
if value, ok := readInt(raw, "heartbeatSeconds", "heartbeat_seconds"); ok {
cfg.HeartbeatSeconds = value
}
if value, ok := readInt(raw, "ackTimeoutSeconds", "ack_timeout_seconds"); ok {
cfg.AckTimeoutSeconds = value
}
if value, ok := readInt(raw, "writeTimeoutSeconds", "write_timeout_seconds"); ok {
cfg.WriteTimeoutSeconds = value
}
if value, ok := readInt(raw, "readTimeoutSeconds", "read_timeout_seconds"); ok {
cfg.ReadTimeoutSeconds = value
}
if cfg.BotID == "" || cfg.Credential == "" {
return adapterConfig{}, errors.New("wecom botId and secret are required")
}
return cfg, nil
}
func parseUserConfig(raw map[string]any) (UserConfig, error) {
cfg := UserConfig{
ChatID: strings.TrimSpace(channel.ReadString(raw, "chatId", "chat_id")),
UserID: strings.TrimSpace(channel.ReadString(raw, "userId", "user_id")),
}
if cfg.ChatID == "" && cfg.UserID == "" {
return UserConfig{}, errors.New("wecom user config requires chat_id or user_id")
}
return cfg, nil
}
func resolveTarget(raw map[string]any) (string, error) {
cfg, err := parseUserConfig(raw)
if err != nil {
return "", err
}
if cfg.ChatID != "" {
return "chat_id:" + cfg.ChatID, nil
}
return "user_id:" + cfg.UserID, nil
}
func normalizeTarget(raw string) string {
kind, id, ok := parseTarget(raw)
if !ok {
return ""
}
return kind + ":" + id
}
func parseTarget(raw string) (kind string, id string, ok bool) {
v := strings.TrimSpace(raw)
if v == "" {
return "", "", false
}
v = strings.TrimPrefix(v, "wecom:")
v = strings.TrimPrefix(v, "workwx:")
v = strings.TrimSpace(v)
lv := strings.ToLower(v)
switch {
case strings.HasPrefix(lv, "chat_id:"):
id = strings.TrimSpace(v[len("chat_id:"):])
return "chat_id", id, id != ""
case strings.HasPrefix(lv, "chat:"):
id = strings.TrimSpace(v[len("chat:"):])
return "chat_id", id, id != ""
case strings.HasPrefix(lv, "group:"):
id = strings.TrimSpace(v[len("group:"):])
return "chat_id", id, id != ""
case strings.HasPrefix(lv, "user_id:"):
id = strings.TrimSpace(v[len("user_id:"):])
return "user_id", id, id != ""
case strings.HasPrefix(lv, "user:"):
id = strings.TrimSpace(v[len("user:"):])
return "user_id", id, id != ""
default:
return "chat_id", v, true
}
}
func matchBinding(raw map[string]any, criteria channel.BindingCriteria) bool {
cfg, err := parseUserConfig(raw)
if err != nil {
return false
}
if value := strings.TrimSpace(criteria.Attribute("chat_id")); value != "" && value == cfg.ChatID {
return true
}
if value := strings.TrimSpace(criteria.Attribute("user_id")); value != "" && value == cfg.UserID {
return true
}
if criteria.SubjectID != "" && (criteria.SubjectID == cfg.ChatID || criteria.SubjectID == cfg.UserID) {
return true
}
return false
}
func buildUserConfig(identity channel.Identity) map[string]any {
out := map[string]any{}
if v := strings.TrimSpace(identity.Attribute("chat_id")); v != "" {
out["chat_id"] = v
}
if v := strings.TrimSpace(identity.Attribute("user_id")); v != "" {
out["user_id"] = v
}
return out
}
func readInt(raw map[string]any, keys ...string) (int, bool) {
for _, key := range keys {
value, ok := raw[key]
if !ok {
continue
}
switch v := value.(type) {
case int:
return v, true
case int32:
return int(v), true
case int64:
return int(v), true
case float64:
return int(v), true
case float32:
return int(v), true
case string:
trimmed := strings.TrimSpace(v)
if trimmed == "" {
continue
}
parsed, err := strconv.Atoi(trimmed)
if err != nil {
continue
}
return parsed, true
}
}
return 0, false
}