fix(hooks): include guildId and channelName in message_received metadata (#26115)

* fix(hooks): include guildId and channelName in message_received metadata

The message_received hook (both plugin and internal) already exposes
sender identity fields (senderId, senderName, senderUsername, senderE164)
but omits the guild/channel context. Plugins that track per-channel
activity receive NULL values for channel identification.

Add guildId (ctx.GroupSpace) and channelName (ctx.GroupChannel) to the
metadata block in both the plugin hook and internal hook dispatch paths.
These properties are already populated by channel providers (e.g. Discord
sets GroupSpace to the guild ID and GroupChannel to #channel-name) and
used elsewhere in the codebase (channels/conversation-label.ts).

* test: cover guild/channel hook metadata propagation (#26115) (thanks @davidrudduck)

---------

Co-authored-by: Peter Steinberger <steipete@gmail.com>
This commit is contained in:
David Rudduck
2026-02-25 14:56:19 +10:00
committed by GitHub
parent 2e84017f23
commit 24a60799be
3 changed files with 15 additions and 0 deletions

View File

@@ -14,6 +14,7 @@ Docs: https://docs.openclaw.ai
- Followups/Routing: when explicit origin routing fails, allow same-channel fallback dispatch (while still blocking cross-channel fallback) so followup replies do not get dropped on transient origin-adapter failures. (#26109) Thanks @Sid-Qin.
- Agents/Model fallback: continue fallback traversal on unrecognized errors when candidates remain, while still throwing the original unknown error on the last candidate. (#26106) Thanks @Sid-Qin.
- Telegram/Markdown spoilers: keep valid `||spoiler||` pairs while leaving unmatched trailing `||` delimiters as literal text, avoiding false all-or-nothing spoiler suppression. (#26105) Thanks @Sid-Qin.
- Hooks/Inbound metadata: include `guildId` and `channelName` in `message_received` metadata for both plugin and internal hook paths. (#26115) Thanks @davidrudduck.
## 2026.2.24

View File

@@ -407,6 +407,8 @@ describe("dispatchReplyFromConfig", () => {
SenderUsername: "alice",
SenderE164: "+15555550123",
AccountId: "acc-1",
GroupSpace: "guild-123",
GroupChannel: "alerts",
});
const replyResolver = async () => ({ text: "hi" }) satisfies ReplyPayload;
@@ -425,6 +427,8 @@ describe("dispatchReplyFromConfig", () => {
senderName: "Alice",
senderUsername: "alice",
senderE164: "+15555550123",
guildId: "guild-123",
channelName: "alerts",
}),
}),
expect.objectContaining({
@@ -445,6 +449,8 @@ describe("dispatchReplyFromConfig", () => {
SessionKey: "agent:main:main",
CommandBody: "/help",
MessageSid: "msg-42",
GroupSpace: "guild-456",
GroupChannel: "ops-room",
});
const replyResolver = async () => ({ text: "hi" }) satisfies ReplyPayload;
@@ -459,6 +465,10 @@ describe("dispatchReplyFromConfig", () => {
content: "/help",
channelId: "telegram",
messageId: "msg-42",
metadata: expect.objectContaining({
guildId: "guild-456",
channelName: "ops-room",
}),
}),
);
expect(internalHookMocks.triggerInternalHook).toHaveBeenCalledTimes(1);

View File

@@ -187,6 +187,8 @@ export async function dispatchReplyFromConfig(params: {
senderName: ctx.SenderName,
senderUsername: ctx.SenderUsername,
senderE164: ctx.SenderE164,
guildId: ctx.GroupSpace,
channelName: ctx.GroupChannel,
},
},
{
@@ -220,6 +222,8 @@ export async function dispatchReplyFromConfig(params: {
senderName: ctx.SenderName,
senderUsername: ctx.SenderUsername,
senderE164: ctx.SenderE164,
guildId: ctx.GroupSpace,
channelName: ctx.GroupChannel,
},
}),
).catch((err) => {