2026-03-19 07:28:52 +00:00
|
|
|
package channels
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"crypto/md5"
|
|
|
|
|
"encoding/hex"
|
|
|
|
|
"encoding/json"
|
2026-06-12 06:03:00 +00:00
|
|
|
"log"
|
2026-03-19 07:28:52 +00:00
|
|
|
|
|
|
|
|
"github.com/sipeed/picoclaw/pkg/config"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
func toChannelHashes(cfg *config.Config) map[string]string {
|
|
|
|
|
result := make(map[string]string)
|
|
|
|
|
ch := cfg.Channels
|
2026-06-12 06:03:00 +00:00
|
|
|
marshal, err := json.Marshal(ch)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("[manager_channel] failed to marshal channels config: %v", err)
|
|
|
|
|
return result
|
|
|
|
|
}
|
2026-03-19 07:28:52 +00:00
|
|
|
var channelConfig map[string]map[string]any
|
2026-06-12 06:03:00 +00:00
|
|
|
if err := json.Unmarshal(marshal, &channelConfig); err != nil {
|
|
|
|
|
log.Printf("[manager_channel] failed to unmarshal channels config: %v", err)
|
|
|
|
|
return result
|
|
|
|
|
}
|
2026-03-19 07:28:52 +00:00
|
|
|
|
|
|
|
|
for key, value := range channelConfig {
|
2026-06-05 01:49:44 +00:00
|
|
|
if enabled, ok := value["enabled"].(bool); !ok || !enabled {
|
2026-03-19 07:28:52 +00:00
|
|
|
continue
|
|
|
|
|
}
|
2026-04-11 16:57:26 +00:00
|
|
|
hiddenValues(key, value, ch.Get(key))
|
2026-06-12 06:03:00 +00:00
|
|
|
valueBytes, err := json.Marshal(value)
|
|
|
|
|
if err != nil {
|
|
|
|
|
log.Printf("[manager_channel] failed to marshal channel %s config: %v", key, err)
|
|
|
|
|
continue
|
|
|
|
|
}
|
2026-03-19 07:28:52 +00:00
|
|
|
hash := md5.Sum(valueBytes)
|
|
|
|
|
result[key] = hex.EncodeToString(hash[:])
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return result
|
|
|
|
|
}
|
|
|
|
|
|
2026-04-11 16:57:26 +00:00
|
|
|
func hiddenValues(key string, value map[string]any, ch *config.Channel) {
|
|
|
|
|
v, err := ch.GetDecoded()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
switch key {
|
|
|
|
|
case "pico":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.PicoSettings); ok {
|
|
|
|
|
value["token"] = settings.Token.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "telegram":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.TelegramSettings); ok {
|
|
|
|
|
value["token"] = settings.Token.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "discord":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.DiscordSettings); ok {
|
|
|
|
|
value["token"] = settings.Token.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "slack":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.SlackSettings); ok {
|
|
|
|
|
value["bot_token"] = settings.BotToken.String()
|
|
|
|
|
value["app_token"] = settings.AppToken.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "matrix":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.MatrixSettings); ok {
|
|
|
|
|
value["token"] = settings.AccessToken.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "onebot":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.OneBotSettings); ok {
|
|
|
|
|
value["token"] = settings.AccessToken.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "line":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.LINESettings); ok {
|
|
|
|
|
value["token"] = settings.ChannelAccessToken.String()
|
|
|
|
|
value["secret"] = settings.ChannelSecret.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "wecom":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.WeComSettings); ok {
|
|
|
|
|
value["secret"] = settings.Secret.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "dingtalk":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.DingTalkSettings); ok {
|
|
|
|
|
value["secret"] = settings.ClientSecret.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "qq":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.QQSettings); ok {
|
|
|
|
|
value["secret"] = settings.AppSecret.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "irc":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.IRCSettings); ok {
|
|
|
|
|
value["password"] = settings.Password.String()
|
|
|
|
|
value["serv_password"] = settings.NickServPassword.String()
|
|
|
|
|
value["sasl_password"] = settings.SASLPassword.String()
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
case "feishu":
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.FeishuSettings); ok {
|
|
|
|
|
value["app_secret"] = settings.AppSecret.String()
|
|
|
|
|
value["encrypt_key"] = settings.EncryptKey.String()
|
|
|
|
|
value["verification_token"] = settings.VerificationToken.String()
|
|
|
|
|
}
|
2026-04-07 11:24:27 +00:00
|
|
|
case "teams_webhook":
|
|
|
|
|
// Expose webhook URLs for hash computation (they contain secrets)
|
2026-04-11 16:57:26 +00:00
|
|
|
vv := value["webhooks"]
|
2026-04-07 11:24:27 +00:00
|
|
|
webhooks := make(map[string]string)
|
2026-04-11 16:57:26 +00:00
|
|
|
if vv != nil {
|
2026-06-05 01:49:44 +00:00
|
|
|
if m, ok := vv.(map[string]string); ok {
|
|
|
|
|
webhooks = m
|
|
|
|
|
} else if m, ok := vv.(map[string]any); ok {
|
|
|
|
|
for k, w := range m {
|
|
|
|
|
if s, ok := w.(string); ok {
|
|
|
|
|
webhooks[k] = s
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2026-04-11 16:57:26 +00:00
|
|
|
}
|
2026-04-13 16:00:13 +00:00
|
|
|
if settings, ok := v.(*config.TeamsWebhookSettings); ok {
|
|
|
|
|
for name, target := range settings.Webhooks {
|
|
|
|
|
webhooks[name] = target.WebhookURL.String()
|
|
|
|
|
}
|
2026-04-07 11:24:27 +00:00
|
|
|
}
|
|
|
|
|
value["webhooks"] = webhooks
|
2026-05-08 06:48:43 +00:00
|
|
|
case "mqtt":
|
|
|
|
|
if settings, ok := v.(*config.MQTTSettings); ok {
|
|
|
|
|
value["username"] = settings.Username.String()
|
|
|
|
|
value["password"] = settings.Password.String()
|
|
|
|
|
}
|
2026-05-11 07:16:18 +00:00
|
|
|
case "slack_webhook":
|
|
|
|
|
// Expose webhook URLs for hash computation (they contain secrets)
|
|
|
|
|
if settings, ok := v.(*config.SlackWebhookSettings); ok {
|
|
|
|
|
webhooks := make(map[string]any)
|
|
|
|
|
for name, target := range settings.Webhooks {
|
|
|
|
|
webhooks[name] = map[string]any{
|
|
|
|
|
"webhook_url": target.WebhookURL.String(),
|
|
|
|
|
"username": target.Username,
|
|
|
|
|
"icon_emoji": target.IconEmoji,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
value["webhooks"] = webhooks
|
|
|
|
|
}
|
2026-03-21 17:55:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-19 07:28:52 +00:00
|
|
|
func compareChannels(old, news map[string]string) (added, removed []string) {
|
|
|
|
|
for key, newHash := range news {
|
|
|
|
|
if oldHash, ok := old[key]; ok {
|
|
|
|
|
if newHash != oldHash {
|
|
|
|
|
removed = append(removed, key)
|
|
|
|
|
added = append(added, key)
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
added = append(added, key)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for key := range old {
|
|
|
|
|
if _, ok := news[key]; !ok {
|
|
|
|
|
removed = append(removed, key)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return added, removed
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func toChannelConfig(cfg *config.Config, list []string) (*config.ChannelsConfig, error) {
|
2026-04-11 16:57:26 +00:00
|
|
|
result := make(config.ChannelsConfig)
|
|
|
|
|
for _, name := range list {
|
|
|
|
|
bc, ok := cfg.Channels[name]
|
|
|
|
|
if !ok || !bc.Enabled {
|
2026-03-19 07:28:52 +00:00
|
|
|
continue
|
|
|
|
|
}
|
2026-04-11 16:57:26 +00:00
|
|
|
result[name] = bc
|
2026-04-07 11:24:27 +00:00
|
|
|
}
|
2026-04-11 16:57:26 +00:00
|
|
|
return &result, nil
|
2026-03-21 17:55:00 +00:00
|
|
|
}
|