mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-07-20 20:18:43 +00:00
133 lines
4.1 KiB
Go
133 lines
4.1 KiB
Go
package telegram
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/nextlevelbuilder/goclaw/internal/config"
|
|
)
|
|
|
|
// resolvedTopicConfig holds the merged config for a specific group+topic combination.
|
|
// Fields are resolved in order: global TelegramConfig → wildcard group ("*") → specific group → specific topic.
|
|
// TS ref: resolveTelegramGroupConfig() in src/telegram/bot.ts + resolveTelegramGroupPromptSettings() in group-config-helpers.ts.
|
|
type resolvedTopicConfig struct {
|
|
groupPolicy string
|
|
requireMention *bool
|
|
allowFrom []string
|
|
enabled *bool
|
|
skills []string // nil = inherit, non-nil = override (empty = no skills)
|
|
tools []string // nil = inherit (all tools), non-nil = override (supports "group:xxx")
|
|
systemPrompt string // concatenated group + topic prompts
|
|
}
|
|
|
|
// resolveTopicConfig resolves the effective config for a chat/topic by merging layers.
|
|
// chatIDStr is the raw Telegram chat ID (e.g., "-100123456").
|
|
// topicID is the forum topic thread ID (0 = not a forum topic).
|
|
func resolveTopicConfig(cfg config.TelegramConfig, chatIDStr string, topicID int) resolvedTopicConfig {
|
|
result := resolvedTopicConfig{
|
|
groupPolicy: cfg.GroupPolicy,
|
|
requireMention: cfg.RequireMention,
|
|
allowFrom: cfg.AllowFrom,
|
|
}
|
|
|
|
if cfg.Groups == nil {
|
|
return result
|
|
}
|
|
|
|
// Layer 1: wildcard group config ("*") — applies to all groups unless overridden.
|
|
if wildcard, ok := cfg.Groups["*"]; ok && wildcard != nil {
|
|
mergeGroupInto(&result, wildcard)
|
|
}
|
|
|
|
// Layer 2: specific group config — overrides wildcard.
|
|
groupCfg, ok := cfg.Groups[chatIDStr]
|
|
if !ok || groupCfg == nil {
|
|
return result
|
|
}
|
|
mergeGroupInto(&result, groupCfg)
|
|
|
|
// Layer 3: specific topic config — overrides group.
|
|
if topicID > 0 && groupCfg.Topics != nil {
|
|
topicIDStr := fmt.Sprintf("%d", topicID)
|
|
if topicCfg, ok := groupCfg.Topics[topicIDStr]; ok && topicCfg != nil {
|
|
mergeTopicInto(&result, topicCfg, groupCfg)
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// mergeGroupInto applies non-zero group config values over the current result.
|
|
func mergeGroupInto(dst *resolvedTopicConfig, src *config.TelegramGroupConfig) {
|
|
if src.GroupPolicy != "" {
|
|
dst.groupPolicy = src.GroupPolicy
|
|
}
|
|
if src.RequireMention != nil {
|
|
dst.requireMention = src.RequireMention
|
|
}
|
|
if len(src.AllowFrom) > 0 {
|
|
dst.allowFrom = src.AllowFrom
|
|
}
|
|
if src.Enabled != nil {
|
|
dst.enabled = src.Enabled
|
|
}
|
|
if src.Skills != nil {
|
|
dst.skills = src.Skills
|
|
}
|
|
if src.Tools != nil {
|
|
dst.tools = src.Tools
|
|
}
|
|
if src.SystemPrompt != "" {
|
|
dst.systemPrompt = src.SystemPrompt
|
|
}
|
|
}
|
|
|
|
// mergeTopicInto applies topic config values, with special handling for systemPrompt
|
|
// (concatenated: group + topic, matching TS resolveTelegramGroupPromptSettings).
|
|
func mergeTopicInto(dst *resolvedTopicConfig, src *config.TelegramTopicConfig, groupCfg *config.TelegramGroupConfig) {
|
|
if src.GroupPolicy != "" {
|
|
dst.groupPolicy = src.GroupPolicy
|
|
}
|
|
if src.RequireMention != nil {
|
|
dst.requireMention = src.RequireMention
|
|
}
|
|
if len(src.AllowFrom) > 0 {
|
|
dst.allowFrom = src.AllowFrom
|
|
}
|
|
if src.Enabled != nil {
|
|
dst.enabled = src.Enabled
|
|
}
|
|
// Skills: topic overrides group (firstDefined pattern, matching TS).
|
|
if src.Skills != nil {
|
|
dst.skills = src.Skills
|
|
}
|
|
// Tools: topic overrides group (same firstDefined pattern as skills).
|
|
if src.Tools != nil {
|
|
dst.tools = src.Tools
|
|
}
|
|
// SystemPrompt: concatenate group + topic (both may exist, matching TS).
|
|
if src.SystemPrompt != "" {
|
|
parts := []string{}
|
|
if groupCfg.SystemPrompt != "" {
|
|
parts = append(parts, strings.TrimSpace(groupCfg.SystemPrompt))
|
|
}
|
|
parts = append(parts, strings.TrimSpace(src.SystemPrompt))
|
|
dst.systemPrompt = strings.Join(parts, "\n\n")
|
|
}
|
|
}
|
|
|
|
// isEnabled returns whether the resolved config allows the bot to operate.
|
|
// nil = enabled (default), false = disabled.
|
|
func (r *resolvedTopicConfig) isEnabled() bool {
|
|
return r.enabled == nil || *r.enabled
|
|
}
|
|
|
|
// effectiveRequireMention returns the resolved require_mention value.
|
|
// Falls back to the provided default if not overridden.
|
|
func (r *resolvedTopicConfig) effectiveRequireMention(defaultVal bool) bool {
|
|
if r.requireMention != nil {
|
|
return *r.requireMention
|
|
}
|
|
return defaultVal
|
|
}
|