47dce276a4
- platform_silent模式接入Orchestrator记忆提取:被动观察群聊时提取值得记住的信息到对应命名空间 - post_chat后台思考注入平台观察:对话后思考也能看到群聊摘要 - QQ适配器:OneBot v11 self_id动态捕获、CQ图片URL提取、视觉+OCR并行处理 - Router解耦:ConfigName/PlatformName分离,支持多QQ实例独立连接 - 黑白名单功能:后端API + Ethend代理 + UI面板 - \n\n双换行断句:AI回复按双换行分割为多条消息按间隔发送 - @提及修复:bot自感知UID进行@检测 - 群聊上下文共享:channel-based userID避免记忆碎片化 - 消息日志显示处理后内容而非原始SSE数据 - platform-bridge Dockerfile + docker-compose.yml更新 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
151 lines
3.6 KiB
Go
151 lines
3.6 KiB
Go
package config
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"os"
|
|
"sync"
|
|
)
|
|
|
|
// BlocklistMode is either "blacklist" or "whitelist".
|
|
type BlocklistSettings struct {
|
|
Mode string `json:"mode"` // "blacklist" (default) or "whitelist"
|
|
GroupIDs []string `json:"group_ids"` // group IDs to block/allow
|
|
UserIDs []string `json:"user_ids"` // private chat user IDs to block/allow
|
|
}
|
|
|
|
// BlocklistStore manages persistence of blocklist settings.
|
|
type BlocklistStore struct {
|
|
mu sync.RWMutex
|
|
path string
|
|
settings BlocklistSettings
|
|
}
|
|
|
|
// NewBlocklistStore loads or creates blocklist settings file.
|
|
func NewBlocklistStore(path string) (*BlocklistStore, error) {
|
|
s := &BlocklistStore{
|
|
path: path,
|
|
settings: BlocklistSettings{
|
|
Mode: "blacklist",
|
|
GroupIDs: []string{},
|
|
UserIDs: []string{},
|
|
},
|
|
}
|
|
if err := s.load(); err != nil {
|
|
return nil, err
|
|
}
|
|
return s, nil
|
|
}
|
|
|
|
func (s *BlocklistStore) load() error {
|
|
data, err := os.ReadFile(s.path)
|
|
if err != nil {
|
|
if os.IsNotExist(err) {
|
|
return s.save() // write defaults
|
|
}
|
|
return fmt.Errorf("read blocklist file: %w", err)
|
|
}
|
|
if len(data) == 0 {
|
|
return nil
|
|
}
|
|
s.mu.Lock()
|
|
defer s.mu.Unlock()
|
|
if err := json.Unmarshal(data, &s.settings); err != nil {
|
|
return fmt.Errorf("parse blocklist file: %w", err)
|
|
}
|
|
if s.settings.Mode == "" {
|
|
s.settings.Mode = "blacklist"
|
|
}
|
|
if s.settings.GroupIDs == nil {
|
|
s.settings.GroupIDs = []string{}
|
|
}
|
|
if s.settings.UserIDs == nil {
|
|
s.settings.UserIDs = []string{}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (s *BlocklistStore) save() error {
|
|
s.mu.RLock()
|
|
data, err := json.MarshalIndent(s.settings, "", " ")
|
|
s.mu.RUnlock()
|
|
if err != nil {
|
|
return fmt.Errorf("marshal blocklist: %w", err)
|
|
}
|
|
tmpPath := s.path + ".tmp"
|
|
if err := os.WriteFile(tmpPath, data, 0640); err != nil {
|
|
return fmt.Errorf("write blocklist: %w", err)
|
|
}
|
|
return os.Rename(tmpPath, s.path)
|
|
}
|
|
|
|
// Get returns current blocklist settings.
|
|
func (s *BlocklistStore) Get() BlocklistSettings {
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
cp := BlocklistSettings{
|
|
Mode: s.settings.Mode,
|
|
GroupIDs: make([]string, len(s.settings.GroupIDs)),
|
|
UserIDs: make([]string, len(s.settings.UserIDs)),
|
|
}
|
|
copy(cp.GroupIDs, s.settings.GroupIDs)
|
|
copy(cp.UserIDs, s.settings.UserIDs)
|
|
return cp
|
|
}
|
|
|
|
// Set updates and persists blocklist settings.
|
|
func (s *BlocklistStore) Set(bs BlocklistSettings) error {
|
|
if bs.Mode != "blacklist" && bs.Mode != "whitelist" {
|
|
return fmt.Errorf("invalid mode: %s (must be blacklist or whitelist)", bs.Mode)
|
|
}
|
|
if bs.GroupIDs == nil {
|
|
bs.GroupIDs = []string{}
|
|
}
|
|
if bs.UserIDs == nil {
|
|
bs.UserIDs = []string{}
|
|
}
|
|
s.mu.Lock()
|
|
s.settings = bs
|
|
s.mu.Unlock()
|
|
return s.save()
|
|
}
|
|
|
|
// IsBlocked checks whether a message should be blocked based on channel type and ID.
|
|
// In blacklist mode: returns true if the id is IN the list.
|
|
// In whitelist mode: returns true if the id is NOT in the list.
|
|
// Admin users should call this with isAdmin=true to always bypass.
|
|
func (s *BlocklistStore) IsBlocked(channelType, channelID, senderID string, isAdmin bool) bool {
|
|
if isAdmin {
|
|
return false
|
|
}
|
|
s.mu.RLock()
|
|
defer s.mu.RUnlock()
|
|
|
|
switch s.settings.Mode {
|
|
case "whitelist":
|
|
// Block if NOT in the whitelist.
|
|
if channelType == "group" {
|
|
return !contains(s.settings.GroupIDs, channelID)
|
|
}
|
|
return !contains(s.settings.UserIDs, senderID)
|
|
|
|
case "blacklist":
|
|
fallthrough
|
|
default:
|
|
// Block if IN the blacklist.
|
|
if channelType == "group" {
|
|
return contains(s.settings.GroupIDs, channelID)
|
|
}
|
|
return contains(s.settings.UserIDs, senderID)
|
|
}
|
|
}
|
|
|
|
func contains(list []string, val string) bool {
|
|
for _, v := range list {
|
|
if v == val {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|