feat: add banned words filter functionality
Add banned words configuration and filtering logic to block messages containing prohibited content. Includes: - Config file support for banned_words list - Engine methods to set and check banned words - Case-insensitive matching during message processing - Localization support for blocked message response - Documentation updates in config example generated by llmgit Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
1 parent
0667e970bf
commit
d6a3a2253e
5 files changed
+76
-9
No files matched your search
@@ -205,6 +205,11 @@ func main() {
|
||||
return config.RemoveAlias(name)
|
||||
})
|
||||
|
||||
// Wire banned words
|
||||
if len(cfg.BannedWords) > 0 {
|
||||
engine.SetBannedWords(cfg.BannedWords)
|
||||
}
|
||||
|
||||
// Wire display truncation settings
|
||||
{
|
||||
dcfg := core.DisplayCfg{
|
||||
@@ -569,6 +574,9 @@ func reloadConfig(configPath, projName string, engine *core.Engine) (*core.Confi
|
||||
engine.AddAlias(a.Name, a.Command)
|
||||
}
|
||||
|
||||
// Reload banned words
|
||||
engine.SetBannedWords(cfg.BannedWords)
|
||||
|
||||
slog.Info("config reloaded", "project", projName)
|
||||
return result, nil
|
||||
}
|
||||
@@ -132,6 +132,16 @@ level = "info" # debug, info, warn, error
|
||||
# directory (e.g. .claude/commands/*.md, .gemini/commands/*.md).
|
||||
# 此外,cc-connect 还会自动发现 Agent 的 commands 目录下的 .md 文件。
|
||||
|
||||
# =============================================================================
|
||||
# Banned Words / 违禁词
|
||||
# =============================================================================
|
||||
#
|
||||
# Messages containing any of these words will be blocked and NOT forwarded to
|
||||
# the AI agent. Matching is case-insensitive.
|
||||
# 消息中包含以下任一违禁词时,将被拦截,不会转发给 AI Agent。匹配不区分大小写。
|
||||
|
||||
# banned_words = ["违禁词1", "违禁词2", "secret_project"]
|
||||
|
||||
# =============================================================================
|
||||
# Command Aliases / 命令别名
|
||||
# =============================================================================
|
||||
|
||||
+10
-9
@@ -16,15 +16,16 @@ var configMu sync.Mutex
|
||||
var ConfigPath string
|
||||
|
||||
type Config struct {
|
||||
DataDir string `toml:"data_dir"` // session store directory, default ~/.cc-connect
|
||||
Projects []ProjectConfig `toml:"projects"`
|
||||
Commands []CommandConfig `toml:"commands"` // global custom slash commands
|
||||
Aliases []AliasConfig `toml:"aliases"` // global command aliases
|
||||
Log LogConfig `toml:"log"`
|
||||
Language string `toml:"language"` // "en" or "zh", default is "en"
|
||||
Speech SpeechConfig `toml:"speech"`
|
||||
Display DisplayConfig `toml:"display"`
|
||||
Quiet *bool `toml:"quiet,omitempty"` // global default for quiet mode; project-level overrides this
|
||||
DataDir string `toml:"data_dir"` // session store directory, default ~/.cc-connect
|
||||
Projects []ProjectConfig `toml:"projects"`
|
||||
Commands []CommandConfig `toml:"commands"` // global custom slash commands
|
||||
Aliases []AliasConfig `toml:"aliases"` // global command aliases
|
||||
BannedWords []string `toml:"banned_words"` // messages containing any of these words are blocked
|
||||
Log LogConfig `toml:"log"`
|
||||
Language string `toml:"language"` // "en" or "zh", default is "en"
|
||||
Speech SpeechConfig `toml:"speech"`
|
||||
Display DisplayConfig `toml:"display"`
|
||||
Quiet *bool `toml:"quiet,omitempty"` // global default for quiet mode; project-level overrides this
|
||||
}
|
||||
|
||||
// DisplayConfig controls how intermediate messages (thinking, tool output) are shown.
|
||||
|
||||
@@ -139,6 +139,9 @@ type Engine struct {
|
||||
aliasSaveAddFunc func(name, command string) error
|
||||
aliasSaveDelFunc func(name string) error
|
||||
|
||||
bannedWords []string
|
||||
bannedMu sync.RWMutex
|
||||
|
||||
// Interactive agent session management
|
||||
interactiveMu sync.Mutex
|
||||
interactiveStates map[string]*interactiveState // key = sessionKey
|
||||
@@ -293,6 +296,17 @@ func (e *Engine) ClearAliases() {
|
||||
e.aliases = make(map[string]string)
|
||||
}
|
||||
|
||||
// SetBannedWords replaces the banned words list.
|
||||
func (e *Engine) SetBannedWords(words []string) {
|
||||
e.bannedMu.Lock()
|
||||
defer e.bannedMu.Unlock()
|
||||
lower := make([]string, len(words))
|
||||
for i, w := range words {
|
||||
lower[i] = strings.ToLower(w)
|
||||
}
|
||||
e.bannedWords = lower
|
||||
}
|
||||
|
||||
// RemoveCommand removes a custom command by name. Returns false if not found.
|
||||
func (e *Engine) RemoveCommand(name string) bool {
|
||||
return e.commands.Remove(name)
|
||||
@@ -402,6 +416,22 @@ func (e *Engine) Stop() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// matchBannedWord returns the first banned word found in content, or "".
|
||||
func (e *Engine) matchBannedWord(content string) string {
|
||||
e.bannedMu.RLock()
|
||||
defer e.bannedMu.RUnlock()
|
||||
if len(e.bannedWords) == 0 {
|
||||
return ""
|
||||
}
|
||||
lower := strings.ToLower(content)
|
||||
for _, w := range e.bannedWords {
|
||||
if strings.Contains(lower, w) {
|
||||
return w
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// resolveAlias checks if the content (or its first word) matches an alias and replaces it.
|
||||
func (e *Engine) resolveAlias(content string) string {
|
||||
e.aliasMu.RLock()
|
||||
@@ -443,6 +473,15 @@ func (e *Engine) handleMessage(p Platform, msg *Message) {
|
||||
content = e.resolveAlias(content)
|
||||
msg.Content = content
|
||||
|
||||
// Banned words check (skip for slash commands)
|
||||
if !strings.HasPrefix(content, "/") {
|
||||
if word := e.matchBannedWord(content); word != "" {
|
||||
slog.Info("message blocked by banned word", "word", word, "user", msg.UserName)
|
||||
e.reply(p, msg.ReplyCtx, e.i18n.T(MsgBannedWordBlocked))
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
if len(msg.Images) == 0 && strings.HasPrefix(content, "/") {
|
||||
if e.handleCommand(p, msg, content) {
|
||||
return
|
||||
|
||||
@@ -279,6 +279,8 @@ const (
|
||||
MsgDeleteSuccess MsgKey = "delete_success"
|
||||
MsgDeleteActiveDenied MsgKey = "delete_active_denied"
|
||||
MsgDeleteNotSupported MsgKey = "delete_not_supported"
|
||||
|
||||
MsgBannedWordBlocked MsgKey = "banned_word_blocked"
|
||||
)
|
||||
|
||||
var messages = map[MsgKey]map[Language]string{
|
||||
@@ -1520,6 +1522,13 @@ var messages = map[MsgKey]map[Language]string{
|
||||
LangJapanese: "❌ このエージェントはセッション削除をサポートしていません。",
|
||||
LangSpanish: "❌ Este agente no admite la eliminación de sesiones.",
|
||||
},
|
||||
MsgBannedWordBlocked: {
|
||||
LangEnglish: "⚠️ Your message was blocked because it contains a prohibited word.",
|
||||
LangChinese: "⚠️ 消息已被拦截,包含违禁词。",
|
||||
LangTraditionalChinese: "⚠️ 訊息已被攔截,包含違禁詞。",
|
||||
LangJapanese: "⚠️ 禁止ワードが含まれているため、メッセージがブロックされました。",
|
||||
LangSpanish: "⚠️ Su mensaje fue bloqueado porque contiene una palabra prohibida.",
|
||||
},
|
||||
}
|
||||
|
||||
func (i *I18n) T(key MsgKey) string {
|
||||
|
||||
Reference in new issue
Block a user