fix: 消息日志增强 + 历史消息抑制 + SSE实时追踪 + 群聊上下文优化
- 日志:收/发消息均显示群名称,管理员显示真实QQ昵称而非"开拓者" - 历史消息:服务重启后NapCat回放的历史消息不再触发回复,静默注入上下文 - 消息时间戳:转发给AI时附带【消息时间: HH:MM:SS (XmXs前)】标记 - ♪ 分割符:QQ消息支持♪作为句子断点 - AI-Core SSE端点:全链路追踪实时推送,ethend不再5秒轮询 - 群聊上下文:AI-Core明确被告知消息来自群聊,以实际发送者为主语 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -4,10 +4,14 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene/platform-bridge/internal/permissions"
|
||||
)
|
||||
|
||||
|
||||
const participantTTL = 5 * time.Minute
|
||||
|
||||
// adapterKey returns the unique key for an adapter in the router map.
|
||||
// Uses ConfigName() if the adapter implements it, otherwise PlatformName().
|
||||
func adapterKey(a PlatformAdapter) string {
|
||||
@@ -31,13 +35,14 @@ type PlatformRouter struct {
|
||||
|
||||
// ChannelContext stores the active conversation state for a channel.
|
||||
type ChannelContext struct {
|
||||
Platform string
|
||||
ChannelID string
|
||||
ChannelType string
|
||||
LastUserMsg string
|
||||
LastSenderUID string
|
||||
RecentSenders []string // last 5 sender UIDs (original platform UIDs)
|
||||
MessageCount int
|
||||
Platform string
|
||||
ChannelID string
|
||||
ChannelType string
|
||||
LastUserMsg string
|
||||
LastSenderUID string
|
||||
RecentSenders []string // last 5 sender UIDs (original platform UIDs)
|
||||
ActiveParticipants map[string]time.Time // UID -> last bot reply time (for multi-user conversation continuity)
|
||||
MessageCount int
|
||||
}
|
||||
|
||||
func NewPlatformRouter(mapper *IdentityMapper, checker *permissions.Checker) *PlatformRouter {
|
||||
@@ -137,6 +142,7 @@ func (r *PlatformRouter) RouteMessage(adapterKey string, rawMsg interface{}) (*U
|
||||
|
||||
// Preserve original platform UID before identity mapping.
|
||||
unified.OriginalSenderUID = unified.SenderID
|
||||
unified.OriginalSenderName = unified.SenderName
|
||||
unified.OriginalRawMessage = rawMsg
|
||||
|
||||
// Capture bot's own UID for @mention detection.
|
||||
@@ -228,3 +234,37 @@ func (r *PlatformRouter) GetContext(platform, channelID string) *ChannelContext
|
||||
defer r.mu.RUnlock()
|
||||
return r.contexts[platform+":"+channelID]
|
||||
}
|
||||
|
||||
// NoteBotReply records that the bot just replied to a specific user in a channel.
|
||||
// Used for conversation continuity: subsequent messages from this user continue the
|
||||
// conversation even without an explicit @mention, within the participant TTL window.
|
||||
func (r *PlatformRouter) NoteBotReply(platform, channelID, recipientUID string) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
key := r.channelKey(platform, channelID)
|
||||
ctx, ok := r.contexts[key]
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
if ctx.ActiveParticipants == nil {
|
||||
ctx.ActiveParticipants = make(map[string]time.Time)
|
||||
}
|
||||
ctx.ActiveParticipants[recipientUID] = time.Now()
|
||||
}
|
||||
|
||||
// IsActiveParticipant checks if a user was recently engaged by the bot.
|
||||
// TTL controls how long the continuity window stays open after the last bot reply.
|
||||
func (r *PlatformRouter) IsActiveParticipant(platform, channelID, uid string) bool {
|
||||
r.mu.RLock()
|
||||
defer r.mu.RUnlock()
|
||||
key := r.channelKey(platform, channelID)
|
||||
ctx, ok := r.contexts[key]
|
||||
if !ok || ctx.ActiveParticipants == nil {
|
||||
return false
|
||||
}
|
||||
t, ok := ctx.ActiveParticipants[uid]
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
return time.Since(t) < participantTTL
|
||||
}
|
||||
|
||||
@@ -23,10 +23,12 @@ type UnifiedMessage struct {
|
||||
Timestamp time.Time `json:"timestamp"`
|
||||
|
||||
// Routing metadata.
|
||||
RouteType string `json:"route_type,omitempty"` // "normal", "silent", "admin_mention"
|
||||
OriginalSenderUID string `json:"original_sender_uid,omitempty"` // preserved before identity mapping
|
||||
OriginalRawMessage interface{} `json:"-"` // preserved for SendMessage wiring
|
||||
BotUID string `json:"-"` // bot's own platform UID, set by router
|
||||
RouteType string `json:"route_type,omitempty"` // "normal", "silent", "admin_mention"
|
||||
OriginalSenderUID string `json:"original_sender_uid,omitempty"` // preserved before identity mapping
|
||||
OriginalSenderName string `json:"original_sender_name,omitempty"` // preserved before identity mapping
|
||||
GroupName string `json:"group_name,omitempty"` // resolved group name for group chats
|
||||
OriginalRawMessage interface{} `json:"-"` // preserved for SendMessage wiring
|
||||
BotUID string `json:"-"` // bot's own platform UID, set by router
|
||||
}
|
||||
|
||||
// Attachment represents a file/image/voice attachment.
|
||||
|
||||
Reference in New Issue
Block a user