feat(discord): include channel name in dynamic context

This commit is contained in:
XYSK-lilong007 2026-03-13 08:33:33 +08:00
parent 9222351871
commit 621836cbe9
4 changed files with 63 additions and 1 deletions

View file

@ -55,6 +55,7 @@ type processOptions struct {
SessionKey string // Session identifier for history/context SessionKey string // Session identifier for history/context
Channel string // Target channel for tool execution Channel string // Target channel for tool execution
ChatID string // Target chat ID for tool execution ChatID string // Target chat ID for tool execution
ChatName string // Optional human-readable chat name for prompt context
UserMessage string // User message content (may include prefix) UserMessage string // User message content (may include prefix)
Media []string // media:// refs from inbound message Media []string // media:// refs from inbound message
DefaultResponse string // Response when LLM returns empty DefaultResponse string // Response when LLM returns empty
@ -67,6 +68,7 @@ const (
defaultResponse = "I've completed processing but have no response to give. Increase `max_tool_iterations` in config.json." defaultResponse = "I've completed processing but have no response to give. Increase `max_tool_iterations` in config.json."
sessionKeyAgentPrefix = "agent:" sessionKeyAgentPrefix = "agent:"
metadataKeyAccountID = "account_id" metadataKeyAccountID = "account_id"
metadataKeyChatName = "chat_name"
metadataKeyGuildID = "guild_id" metadataKeyGuildID = "guild_id"
metadataKeyTeamID = "team_id" metadataKeyTeamID = "team_id"
metadataKeyParentPeerKind = "parent_peer_kind" metadataKeyParentPeerKind = "parent_peer_kind"
@ -619,6 +621,7 @@ func (al *AgentLoop) processMessage(ctx context.Context, msg bus.InboundMessage)
SessionKey: sessionKey, SessionKey: sessionKey,
Channel: msg.Channel, Channel: msg.Channel,
ChatID: msg.ChatID, ChatID: msg.ChatID,
ChatName: inboundMetadata(msg, metadataKeyChatName),
UserMessage: msg.Content, UserMessage: msg.Content,
Media: msg.Media, Media: msg.Media,
DefaultResponse: defaultResponse, DefaultResponse: defaultResponse,
@ -761,7 +764,7 @@ func (al *AgentLoop) runAgentLoop(
opts.UserMessage, opts.UserMessage,
opts.Media, opts.Media,
opts.Channel, opts.Channel,
opts.ChatID, displayChatID(opts.ChatID, opts.ChatName),
) )
// Resolve media:// refs to base64 data URLs (streaming) // Resolve media:// refs to base64 data URLs (streaming)
@ -1818,6 +1821,13 @@ func inboundMetadata(msg bus.InboundMessage, key string) string {
return msg.Metadata[key] return msg.Metadata[key]
} }
func displayChatID(chatID, chatName string) string {
if chatID == "" || chatName == "" {
return chatID
}
return fmt.Sprintf("%s (%s)", chatID, chatName)
}
// extractParentPeer extracts the parent peer (reply-to) from inbound message metadata. // extractParentPeer extracts the parent peer (reply-to) from inbound message metadata.
func extractParentPeer(msg bus.InboundMessage) *routing.RoutePeer { func extractParentPeer(msg bus.InboundMessage) *routing.RoutePeer {
parentKind := inboundMetadata(msg, metadataKeyParentPeerKind) parentKind := inboundMetadata(msg, metadataKeyParentPeerKind)

View file

@ -889,6 +889,16 @@ func TestTargetReasoningChannelID_AllChannels(t *testing.T) {
} }
} }
func TestDisplayChatID_AppendsChatName(t *testing.T) {
if got := displayChatID("123456789012345678", "#general"); got != "123456789012345678 (#general)" {
t.Fatalf("displayChatID() = %q, want %q", got, "123456789012345678 (#general)")
}
if got := displayChatID("123456789012345678", ""); got != "123456789012345678" {
t.Fatalf("displayChatID() without chatName = %q, want %q", got, "123456789012345678")
}
}
func TestHandleReasoning(t *testing.T) { func TestHandleReasoning(t *testing.T) {
newLoop := func(t *testing.T) (*AgentLoop, *bus.MessageBus) { newLoop := func(t *testing.T) (*AgentLoop, *bus.MessageBus) {
t.Helper() t.Helper()

View file

@ -449,12 +449,31 @@ func (c *DiscordChannel) handleMessage(s *discordgo.Session, m *discordgo.Messag
"display_name": sender.DisplayName, "display_name": sender.DisplayName,
"guild_id": m.GuildID, "guild_id": m.GuildID,
"channel_id": m.ChannelID, "channel_id": m.ChannelID,
"chat_name": resolveDiscordChannelName(c.session, m.ChannelID),
"is_dm": fmt.Sprintf("%t", m.GuildID == ""), "is_dm": fmt.Sprintf("%t", m.GuildID == ""),
} }
c.HandleMessage(c.ctx, peer, m.ID, senderID, m.ChannelID, content, mediaPaths, metadata, sender) c.HandleMessage(c.ctx, peer, m.ID, senderID, m.ChannelID, content, mediaPaths, metadata, sender)
} }
func resolveDiscordChannelName(session *discordgo.Session, channelID string) string {
if session == nil || channelID == "" {
return ""
}
if session.State != nil {
if channel, err := session.State.Channel(channelID); err == nil && channel != nil && channel.Name != "" {
return "#" + channel.Name
}
}
channel, err := session.Channel(channelID)
if err != nil || channel == nil || channel.Name == "" {
return ""
}
return "#" + channel.Name
}
// startTyping starts a continuous typing indicator loop for the given chatID. // startTyping starts a continuous typing indicator loop for the given chatID.
// It stops any existing typing loop for that chatID before starting a new one. // It stops any existing typing loop for that chatID before starting a new one.
func (c *DiscordChannel) startTyping(chatID string) { func (c *DiscordChannel) startTyping(chatID string) {

View file

@ -2,6 +2,8 @@ package discord
import ( import (
"testing" "testing"
"github.com/bwmarrin/discordgo"
) )
func TestChannelRefRegex(t *testing.T) { func TestChannelRefRegex(t *testing.T) {
@ -96,3 +98,24 @@ func TestMsgLinkRegex_MultipleMatches(t *testing.T) {
t.Errorf("3rd match = %v, want guild=7 chan=8 msg=9", matches[2]) t.Errorf("3rd match = %v, want guild=7 chan=8 msg=9", matches[2])
} }
} }
func TestResolveDiscordChannelName_FromState(t *testing.T) {
session, err := discordgo.New("Bot test-token")
if err != nil {
t.Fatalf("discordgo.New() error = %v", err)
}
session.StateEnabled = true
session.State = discordgo.NewState()
session.State.GuildAdd(&discordgo.Guild{ID: "guild-1"})
session.State.ChannelAdd(&discordgo.Channel{
ID: "123",
Name: "general",
Type: discordgo.ChannelTypeGuildText,
GuildID: "guild-1",
})
if got := resolveDiscordChannelName(session, "123"); got != "#general" {
t.Fatalf("resolveDiscordChannelName() = %q, want %q", got, "#general")
}
}