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:
@@ -294,6 +294,11 @@ func (a *Adapter) ToUnified(rawMessage interface{}) (*bridge.UnifiedMessage, err
|
||||
return nil, fmt.Errorf("expected *OBv11Message, got %T", rawMessage)
|
||||
}
|
||||
|
||||
// Handle notice events (e.g., poke/"戳一戳").
|
||||
if msg.PostType == "notice" {
|
||||
return a.noticeToUnified(msg)
|
||||
}
|
||||
|
||||
content := extractText(msg)
|
||||
|
||||
senderID := ""
|
||||
@@ -393,6 +398,55 @@ func (a *Adapter) ToUnified(rawMessage interface{}) (*bridge.UnifiedMessage, err
|
||||
}, nil
|
||||
}
|
||||
|
||||
// noticeToUnified converts an OBv11 notice event (poke/戳一戳 etc.) to a UnifiedMessage.
|
||||
func (a *Adapter) noticeToUnified(msg *OBv11Message) (*bridge.UnifiedMessage, error) {
|
||||
senderID := fmt.Sprintf("%d", msg.UserID)
|
||||
senderName := senderID
|
||||
|
||||
channelType := "direct"
|
||||
channelID := fmt.Sprintf("private_%d", msg.UserID)
|
||||
var groupName string
|
||||
|
||||
if msg.GroupID != 0 {
|
||||
channelType = "group"
|
||||
channelID = fmt.Sprintf("%d", msg.GroupID)
|
||||
groupName = a.GroupName(msg.GroupID)
|
||||
}
|
||||
|
||||
// Build a human-readable action description.
|
||||
// Group/private context is prepended by the message handler, so keep content to the action itself.
|
||||
var content string
|
||||
switch {
|
||||
case msg.SubType == "poke":
|
||||
detail := "戳了戳"
|
||||
if msg.PokeDetail != nil {
|
||||
if msg.PokeDetail.Action != "" {
|
||||
detail = msg.PokeDetail.Action
|
||||
}
|
||||
if msg.PokeDetail.Suffix != "" {
|
||||
detail += " " + msg.PokeDetail.Suffix
|
||||
}
|
||||
}
|
||||
content = fmt.Sprintf("【动作】%s 昔涟", detail)
|
||||
default:
|
||||
content = fmt.Sprintf("【动作】%s/%s", msg.NoticeType, msg.SubType)
|
||||
}
|
||||
|
||||
return &bridge.UnifiedMessage{
|
||||
SenderID: senderID,
|
||||
SenderName: senderName,
|
||||
Platform: "qq",
|
||||
ChannelID: channelID,
|
||||
ChannelType: channelType,
|
||||
Content: content,
|
||||
ContentType: "action",
|
||||
MessageID: fmt.Sprintf("notice_%d", time.Now().UnixNano()),
|
||||
GroupName: groupName,
|
||||
RawData: msg,
|
||||
Timestamp: time.Unix(msg.Time, 0),
|
||||
}, nil
|
||||
}
|
||||
|
||||
// FromUnified converts a unified response to QQ platform messages.
|
||||
func (a *Adapter) FromUnified(response *bridge.UnifiedResponse) ([]bridge.PlatformMessage, error) {
|
||||
var msgs []bridge.PlatformMessage
|
||||
@@ -411,6 +465,11 @@ func (a *Adapter) FromUnified(response *bridge.UnifiedResponse) ([]bridge.Platfo
|
||||
return msgs, nil
|
||||
}
|
||||
|
||||
// SendProactive implements bridge.ProactiveSender for proactive message delivery.
|
||||
func (a *Adapter) SendProactive(chatType string, userID, groupID int64, content string) error {
|
||||
return a.SendMessage(chatType, userID, groupID, content)
|
||||
}
|
||||
|
||||
// SendMessage sends a message through the QQ WebSocket connection.
|
||||
func (a *Adapter) SendMessage(msgType string, userID, groupID int64, content string) error {
|
||||
a.connMu.Lock()
|
||||
@@ -499,7 +558,8 @@ func (a *Adapter) ReadMessages(ctx context.Context, msgCh chan<- *OBv11Message)
|
||||
fmt.Printf("[qq:%s] self ID captured: %s\n", a.configName, a.selfID)
|
||||
}
|
||||
|
||||
if msg.PostType == "message" {
|
||||
// Dispatch message and notice (poke, etc.) events.
|
||||
if msg.PostType == "message" || (msg.PostType == "notice" && msg.NoticeType == "notify" && msg.SubType == "poke") {
|
||||
select {
|
||||
case msgCh <- &msg:
|
||||
case <-ctx.Done():
|
||||
|
||||
@@ -26,7 +26,17 @@ type OBv11Message struct {
|
||||
MessageSeq int64 `json:"message_seq"`
|
||||
|
||||
// Notice fields.
|
||||
NoticeType string `json:"notice_type"`
|
||||
NoticeType string `json:"notice_type"`
|
||||
|
||||
// Poke detail (sub_type === "poke").
|
||||
PokeDetail *OBv11PokeDetail `json:"poke_detail,omitempty"`
|
||||
}
|
||||
|
||||
// OBv11PokeDetail contains extra info for poke ("戳一戳") notice events.
|
||||
type OBv11PokeDetail struct {
|
||||
Action string `json:"action"` // e.g. "戳了戳"
|
||||
Suffix string `json:"suffix"` // e.g. "你的脸蛋"
|
||||
PokePic string `json:"poke_pic"` // e.g. "https://..."
|
||||
}
|
||||
|
||||
// OBv11Sender represents a message sender.
|
||||
|
||||
Reference in New Issue
Block a user