feat: QQ主动消息管道 + 戳一戳事件 + thinker提示词优化

- ProactiveTarget 结构体 + QQ目标正则解析(extractProactiveMessage 返回双值)
- storeThought 双路推送:platform消息 → platformMessagePusher,web消息 → messagePusher
- platform-bridge: SendProactive接口 + ProactiveSender + /api/v1/internal/send-proactive端点
- QQ戳一戳/notice事件 → noticeToUnified → ContentType "action"
- thinker提示词注入QQ频道上下文:群名称(群号)、活跃时间、trigger-aware引导
- SetBotUID/SetPlatformMessagePusher/AddOrUpdatePlatformChannel 方法
- ai-core source 增加 BotUID/GroupName,platform-bridge source 增加 bot_uid/group_name
- 支持 [CQ:at,qq=xxx] @提及标签自动拼接

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-22 20:26:36 +08:00
parent cd053194a9
commit cadd5f2233
9 changed files with 469 additions and 37 deletions
+47
View File
@@ -373,6 +373,37 @@ func main() {
}
})
log.Printf("[主动消息] 推送已启用 (Gateway=%s)", gatewayURL)
// 设置平台主动消息推送回调(调用 Platform Bridge 内部 API
platformBridgeURL := getEnv("PLATFORM_BRIDGE_URL", "http://localhost:8082")
thinker.SetPlatformMessagePusher(func(target background.ProactiveTarget, message string) {
reqBody, _ := json.Marshal(map[string]string{
"platform": target.Platform,
"chat_type": target.ChatType,
"user_id": target.UserID,
"group_id": target.GroupID,
"at_user_id": target.AtUserID,
"content": message,
})
req, _ := http.NewRequest("POST",
platformBridgeURL+"/api/v1/internal/send-proactive",
strings.NewReader(string(reqBody)))
req.Header.Set("Content-Type", "application/json")
req.Header.Set("X-Internal-Token", internalToken)
resp, err := proactiveClient.Do(req)
if err != nil {
log.Printf("[主动消息] 平台推送失败: %v", err)
return
}
resp.Body.Close()
if resp.StatusCode == http.StatusOK {
log.Printf("[主动消息] 已推送到 Platform Bridge: target=%s/%s user=%s group=%s len=%d",
target.Platform, target.ChatType, target.UserID, target.GroupID, len(message))
} else {
log.Printf("[主动消息] Platform Bridge 返回 %d", resp.StatusCode)
}
})
log.Printf("[主动消息] 平台推送已启用 (PlatformBridge=%s)", platformBridgeURL)
} else {
log.Println("[主动消息] 未配置 INTERNAL_SERVICE_TOKEN,主动消息推送已禁用")
}
@@ -825,6 +856,8 @@ func handleChat(
ChannelType string `json:"channel_type"`
SenderName string `json:"sender_name"`
OriginalUID string `json:"original_uid"`
BotUID string `json:"bot_uid"`
GroupName string `json:"group_name,omitempty"`
} `json:"source,omitempty"`
}
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
@@ -840,6 +873,12 @@ func handleChat(
if req.Mode == "platform_silent" {
if thinker != nil {
thinker.RecordUserMessage(req.SessionID)
if req.Source.Platform != "" && req.Source.BotUID != "" {
thinker.SetBotUID(req.Source.Platform, req.Source.BotUID)
}
if req.Source.Platform != "" && req.Source.ChannelID != "" {
thinker.AddOrUpdatePlatformChannel(req.Source.Platform, req.Source.ChannelType, req.Source.ChannelID, req.Source.GroupName)
}
}
// 图片预处理:静默观察时也分析图片内容,供后台思考使用
message := req.Message
@@ -870,6 +909,12 @@ func handleChat(
// 0. 记录用户活动(重置闲置计时器)
if thinker != nil {
thinker.RecordUserMessage(req.SessionID)
if req.Source.Platform != "" && req.Source.BotUID != "" {
thinker.SetBotUID(req.Source.Platform, req.Source.BotUID)
}
if req.Source.Platform != "" && req.Source.ChannelID != "" {
thinker.AddOrUpdatePlatformChannel(req.Source.Platform, req.Source.ChannelType, req.Source.ChannelID, req.Source.GroupName)
}
}
// Admin private messages: redirect to the main admin session so conversation
@@ -911,6 +956,8 @@ func handleChat(
Mode: req.Mode,
Nickname: userNickname,
ChannelType: req.Source.ChannelType,
ChannelID: req.Source.ChannelID,
BotUID: req.Source.BotUID,
})
if err != nil {
errData, _ := json.Marshal(map[string]string{"delta": "", "error": fmt.Sprintf("处理失败: %v", err)})