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:
2026-05-31 11:49:36 +08:00
parent 677385ec17
commit 3ad728406e
13 changed files with 587 additions and 156 deletions
@@ -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
}