feat: add channel match metadata logs

Co-authored-by: thewilloftheshadow <thewilloftheshadow@users.noreply.github.com>
This commit is contained in:
Peter Steinberger
2026-01-17 23:48:39 +00:00
parent 794bab45ff
commit 4c12c4fc04
10 changed files with 133 additions and 6 deletions

View File

@@ -0,0 +1,2 @@
export type { ChannelEntryMatch } from "../channel-config.js";
export { buildChannelKeyCandidates, resolveChannelEntryMatch } from "../channel-config.js";

View File

@@ -84,4 +84,9 @@ export {
listWhatsAppDirectoryGroupsFromConfig,
listWhatsAppDirectoryPeersFromConfig,
} from "./directory-config.js";
export {
buildChannelKeyCandidates,
resolveChannelEntryMatch,
type ChannelEntryMatch,
} from "./channel-config.js";
export type { ChannelId, ChannelPlugin } from "./types.js";

View File

@@ -249,6 +249,34 @@ describe("discord mention gating", () => {
}),
).toBe(false);
});
it("inherits parent channel mention rules for threads", () => {
const guildInfo: DiscordGuildEntryResolved = {
requireMention: true,
channels: {
"parent-1": { allow: true, requireMention: false },
},
};
const channelConfig = resolveDiscordChannelConfigWithFallback({
guildInfo,
channelId: "thread-1",
channelName: "topic",
channelSlug: "topic",
parentId: "parent-1",
parentName: "Parent",
parentSlug: "parent",
scope: "thread",
});
expect(channelConfig?.matchSource).toBe("parent");
expect(
resolveDiscordShouldRequireMention({
isGuildMessage: true,
isThread: true,
channelConfig,
guildInfo,
}),
).toBe(false);
});
});
describe("discord groupPolicy gating", () => {

View File

@@ -252,8 +252,11 @@ export async function preflightDiscordMessage(
scope: threadChannel ? "thread" : "channel",
})
: null;
const channelMatchMeta = `matchKey=${channelConfig?.matchKey ?? "none"} matchSource=${
channelConfig?.matchSource ?? "none"
}`;
if (isGuildMessage && channelConfig?.enabled === false) {
logVerbose(`Blocked discord channel ${message.channelId} (channel disabled)`);
logVerbose(`Blocked discord channel ${message.channelId} (channel disabled, ${channelMatchMeta})`);
return null;
}
@@ -280,21 +283,28 @@ export async function preflightDiscordMessage(
})
) {
if (params.groupPolicy === "disabled") {
logVerbose("discord: drop guild message (groupPolicy: disabled)");
logVerbose(`discord: drop guild message (groupPolicy: disabled, ${channelMatchMeta})`);
} else if (!channelAllowlistConfigured) {
logVerbose("discord: drop guild message (groupPolicy: allowlist, no channel allowlist)");
logVerbose(
`discord: drop guild message (groupPolicy: allowlist, no channel allowlist, ${channelMatchMeta})`,
);
} else {
logVerbose(
`Blocked discord channel ${message.channelId} not in guild channel allowlist (groupPolicy: allowlist)`,
`Blocked discord channel ${message.channelId} not in guild channel allowlist (groupPolicy: allowlist, ${channelMatchMeta})`,
);
}
return null;
}
if (isGuildMessage && channelConfig?.allowed === false) {
logVerbose(`Blocked discord channel ${message.channelId} not in guild channel allowlist`);
logVerbose(
`Blocked discord channel ${message.channelId} not in guild channel allowlist (${channelMatchMeta})`,
);
return null;
}
if (isGuildMessage) {
logVerbose(`discord: allow channel ${message.channelId} (${channelMatchMeta})`);
}
const textForHistory = resolveDiscordMessageText(message, {
includeForwarded: true,

View File

@@ -28,4 +28,18 @@ describe("resolveSlackChannelConfig", () => {
});
expect(res).toMatchObject({ requireMention: true });
});
it("uses wildcard entries when no direct channel config exists", () => {
const res = resolveSlackChannelConfig({
channelId: "C1",
channels: { "*": { allow: true, requireMention: false } },
defaultRequireMention: true,
});
expect(res).toMatchObject({
allowed: true,
requireMention: false,
matchKey: "*",
matchSource: "wildcard",
});
});
});

View File

@@ -310,6 +310,9 @@ export function createSlackMonitorContext(params: {
channels: params.channelsConfig,
defaultRequireMention,
});
const channelMatchMeta = `matchKey=${channelConfig?.matchKey ?? "none"} matchSource=${
channelConfig?.matchSource ?? "none"
}`;
const channelAllowed = channelConfig?.allowed !== false;
const channelAllowlistConfigured =
Boolean(params.channelsConfig) && Object.keys(params.channelsConfig ?? {}).length > 0;
@@ -320,9 +323,16 @@ export function createSlackMonitorContext(params: {
channelAllowed,
})
) {
logVerbose(
`slack: drop channel ${p.channelId} (groupPolicy=${params.groupPolicy}, ${channelMatchMeta})`,
);
return false;
}
if (!channelAllowed) return false;
if (!channelAllowed) {
logVerbose(`slack: drop channel ${p.channelId} (${channelMatchMeta})`);
return false;
}
logVerbose(`slack: allow channel ${p.channelId} (${channelMatchMeta})`);
}
return true;

View File

@@ -1159,6 +1159,50 @@ describe("createTelegramBot", () => {
expect(replySpy).toHaveBeenCalledTimes(1);
});
it("inherits group allowlist + requireMention in topics", async () => {
onSpy.mockReset();
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;
replySpy.mockReset();
loadConfig.mockReturnValue({
channels: {
telegram: {
groupPolicy: "allowlist",
groups: {
"-1001234567890": {
requireMention: false,
allowFrom: ["123456789"],
topics: {
"99": {},
},
},
},
},
},
});
createTelegramBot({ token: "tok" });
const handler = getOnHandler("message") as (ctx: Record<string, unknown>) => Promise<void>;
await handler({
message: {
chat: {
id: -1001234567890,
type: "supergroup",
title: "Forum Group",
is_forum: true,
},
from: { id: 123456789, username: "testuser" },
text: "hello",
date: 1736380800,
message_thread_id: 99,
},
me: { username: "clawdbot_bot" },
getFile: async () => ({ download: async () => new Uint8Array() }),
});
expect(replySpy).toHaveBeenCalledTimes(1);
});
it("honors groups default when no explicit group override exists", async () => {
onSpy.mockReset();
const replySpy = replyModule.__replySpy as unknown as ReturnType<typeof vi.fn>;