feat(groups): resolve requireMention for discord/slack
This commit is contained in:
61
src/auto-reply/reply/groups.test.ts
Normal file
61
src/auto-reply/reply/groups.test.ts
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import type { ClawdbotConfig } from "../../config/config.js";
|
||||||
|
import type { GroupKeyResolution } from "../../config/sessions.js";
|
||||||
|
import type { TemplateContext } from "../templating.js";
|
||||||
|
import { resolveGroupRequireMention } from "./groups.js";
|
||||||
|
|
||||||
|
describe("resolveGroupRequireMention", () => {
|
||||||
|
it("respects Discord guild/channel requireMention settings", () => {
|
||||||
|
const cfg: ClawdbotConfig = {
|
||||||
|
discord: {
|
||||||
|
guilds: {
|
||||||
|
"145": {
|
||||||
|
requireMention: false,
|
||||||
|
channels: {
|
||||||
|
general: { allow: true },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const ctx: TemplateContext = {
|
||||||
|
Surface: "discord",
|
||||||
|
From: "group:123",
|
||||||
|
GroupRoom: "#general",
|
||||||
|
GroupSpace: "145",
|
||||||
|
};
|
||||||
|
const groupResolution: GroupKeyResolution = {
|
||||||
|
surface: "discord",
|
||||||
|
id: "123",
|
||||||
|
chatType: "group",
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(resolveGroupRequireMention({ cfg, ctx, groupResolution })).toBe(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("respects Slack channel requireMention settings", () => {
|
||||||
|
const cfg: ClawdbotConfig = {
|
||||||
|
slack: {
|
||||||
|
channels: {
|
||||||
|
C123: { requireMention: false },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
const ctx: TemplateContext = {
|
||||||
|
Surface: "slack",
|
||||||
|
From: "slack:channel:C123",
|
||||||
|
GroupSubject: "#general",
|
||||||
|
};
|
||||||
|
const groupResolution: GroupKeyResolution = {
|
||||||
|
surface: "slack",
|
||||||
|
id: "C123",
|
||||||
|
chatType: "group",
|
||||||
|
};
|
||||||
|
|
||||||
|
expect(resolveGroupRequireMention({ cfg, ctx, groupResolution })).toBe(
|
||||||
|
false,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
});
|
||||||
@@ -6,6 +6,43 @@ import type {
|
|||||||
import { normalizeGroupActivation } from "../group-activation.js";
|
import { normalizeGroupActivation } from "../group-activation.js";
|
||||||
import type { TemplateContext } from "../templating.js";
|
import type { TemplateContext } from "../templating.js";
|
||||||
|
|
||||||
|
function normalizeDiscordSlug(value?: string | null) {
|
||||||
|
if (!value) return "";
|
||||||
|
let text = value.trim().toLowerCase();
|
||||||
|
if (!text) return "";
|
||||||
|
text = text.replace(/^[@#]+/, "");
|
||||||
|
text = text.replace(/[\s_]+/g, "-");
|
||||||
|
text = text.replace(/[^a-z0-9-]+/g, "-");
|
||||||
|
text = text.replace(/-{2,}/g, "-").replace(/^-+|-+$/g, "");
|
||||||
|
return text;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeSlackSlug(raw?: string | null) {
|
||||||
|
const trimmed = raw?.trim().toLowerCase() ?? "";
|
||||||
|
if (!trimmed) return "";
|
||||||
|
const dashed = trimmed.replace(/\s+/g, "-");
|
||||||
|
const cleaned = dashed.replace(/[^a-z0-9#@._+-]+/g, "-");
|
||||||
|
return cleaned.replace(/-{2,}/g, "-").replace(/^[-.]+|[-.]+$/g, "");
|
||||||
|
}
|
||||||
|
|
||||||
|
function resolveDiscordGuildEntry(
|
||||||
|
guilds: NonNullable<ClawdbotConfig["discord"]>["guilds"],
|
||||||
|
groupSpace?: string,
|
||||||
|
) {
|
||||||
|
if (!guilds || Object.keys(guilds).length === 0) return null;
|
||||||
|
const space = groupSpace?.trim();
|
||||||
|
if (space && guilds[space]) return guilds[space];
|
||||||
|
const normalized = normalizeDiscordSlug(space);
|
||||||
|
if (normalized && guilds[normalized]) return guilds[normalized];
|
||||||
|
if (normalized) {
|
||||||
|
const match = Object.values(guilds).find(
|
||||||
|
(entry) => normalizeDiscordSlug(entry?.slug ?? undefined) === normalized,
|
||||||
|
);
|
||||||
|
if (match) return match;
|
||||||
|
}
|
||||||
|
return guilds["*"] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
export function resolveGroupRequireMention(params: {
|
export function resolveGroupRequireMention(params: {
|
||||||
cfg: ClawdbotConfig;
|
cfg: ClawdbotConfig;
|
||||||
ctx: TemplateContext;
|
ctx: TemplateContext;
|
||||||
@@ -14,6 +51,8 @@ export function resolveGroupRequireMention(params: {
|
|||||||
const { cfg, ctx, groupResolution } = params;
|
const { cfg, ctx, groupResolution } = params;
|
||||||
const surface = groupResolution?.surface ?? ctx.Surface?.trim().toLowerCase();
|
const surface = groupResolution?.surface ?? ctx.Surface?.trim().toLowerCase();
|
||||||
const groupId = groupResolution?.id ?? ctx.From?.replace(/^group:/, "");
|
const groupId = groupResolution?.id ?? ctx.From?.replace(/^group:/, "");
|
||||||
|
const groupRoom = ctx.GroupRoom?.trim() ?? ctx.GroupSubject?.trim();
|
||||||
|
const groupSpace = ctx.GroupSpace?.trim();
|
||||||
if (surface === "telegram") {
|
if (surface === "telegram") {
|
||||||
if (groupId) {
|
if (groupId) {
|
||||||
const groupConfig = cfg.telegram?.groups?.[groupId];
|
const groupConfig = cfg.telegram?.groups?.[groupId];
|
||||||
@@ -47,6 +86,58 @@ export function resolveGroupRequireMention(params: {
|
|||||||
if (typeof groupDefault === "boolean") return groupDefault;
|
if (typeof groupDefault === "boolean") return groupDefault;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
if (surface === "discord") {
|
||||||
|
const guildEntry = resolveDiscordGuildEntry(
|
||||||
|
cfg.discord?.guilds,
|
||||||
|
groupSpace,
|
||||||
|
);
|
||||||
|
const channelEntries = guildEntry?.channels;
|
||||||
|
if (channelEntries && Object.keys(channelEntries).length > 0) {
|
||||||
|
const channelSlug = normalizeDiscordSlug(groupRoom);
|
||||||
|
const entry =
|
||||||
|
(groupId ? channelEntries[groupId] : undefined) ??
|
||||||
|
(channelSlug
|
||||||
|
? (channelEntries[channelSlug] ?? channelEntries[`#${channelSlug}`])
|
||||||
|
: undefined) ??
|
||||||
|
(groupRoom
|
||||||
|
? channelEntries[normalizeDiscordSlug(groupRoom)]
|
||||||
|
: undefined);
|
||||||
|
if (entry && typeof entry.requireMention === "boolean") {
|
||||||
|
return entry.requireMention;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (typeof guildEntry?.requireMention === "boolean") {
|
||||||
|
return guildEntry.requireMention;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (surface === "slack") {
|
||||||
|
const channels = cfg.slack?.channels ?? {};
|
||||||
|
const keys = Object.keys(channels);
|
||||||
|
if (keys.length === 0) return true;
|
||||||
|
const channelId = groupId?.trim();
|
||||||
|
const channelName = groupRoom?.replace(/^#/, "");
|
||||||
|
const normalizedName = normalizeSlackSlug(channelName);
|
||||||
|
const candidates = [
|
||||||
|
channelId ?? "",
|
||||||
|
channelName ? `#${channelName}` : "",
|
||||||
|
channelName ?? "",
|
||||||
|
normalizedName,
|
||||||
|
].filter(Boolean);
|
||||||
|
let matched: { requireMention?: boolean } | undefined;
|
||||||
|
for (const candidate of candidates) {
|
||||||
|
if (candidate && channels[candidate]) {
|
||||||
|
matched = channels[candidate];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const fallback = channels["*"];
|
||||||
|
const resolved = matched ?? fallback;
|
||||||
|
if (typeof resolved?.requireMention === "boolean") {
|
||||||
|
return resolved.requireMention;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user