test: cover replyToMode behavior
This commit is contained in:
@@ -7,6 +7,7 @@ import {
|
|||||||
resolveDiscordChannelConfig,
|
resolveDiscordChannelConfig,
|
||||||
resolveDiscordGuildEntry,
|
resolveDiscordGuildEntry,
|
||||||
resolveGroupDmAllow,
|
resolveGroupDmAllow,
|
||||||
|
resolveDiscordReplyTarget,
|
||||||
} from "./monitor.js";
|
} from "./monitor.js";
|
||||||
|
|
||||||
const fakeGuild = (id: string, name: string) =>
|
const fakeGuild = (id: string, name: string) =>
|
||||||
@@ -160,3 +161,49 @@ describe("discord group DM gating", () => {
|
|||||||
).toBe(false);
|
).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("discord reply target selection", () => {
|
||||||
|
it("skips replies when mode is off", () => {
|
||||||
|
expect(
|
||||||
|
resolveDiscordReplyTarget({
|
||||||
|
replyToMode: "off",
|
||||||
|
replyToId: "123",
|
||||||
|
hasReplied: false,
|
||||||
|
}),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("replies only once when mode is first", () => {
|
||||||
|
expect(
|
||||||
|
resolveDiscordReplyTarget({
|
||||||
|
replyToMode: "first",
|
||||||
|
replyToId: "123",
|
||||||
|
hasReplied: false,
|
||||||
|
}),
|
||||||
|
).toBe("123");
|
||||||
|
expect(
|
||||||
|
resolveDiscordReplyTarget({
|
||||||
|
replyToMode: "first",
|
||||||
|
replyToId: "123",
|
||||||
|
hasReplied: true,
|
||||||
|
}),
|
||||||
|
).toBeUndefined();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("replies on every message when mode is all", () => {
|
||||||
|
expect(
|
||||||
|
resolveDiscordReplyTarget({
|
||||||
|
replyToMode: "all",
|
||||||
|
replyToId: "123",
|
||||||
|
hasReplied: false,
|
||||||
|
}),
|
||||||
|
).toBe("123");
|
||||||
|
expect(
|
||||||
|
resolveDiscordReplyTarget({
|
||||||
|
replyToMode: "all",
|
||||||
|
replyToId: "123",
|
||||||
|
hasReplied: true,
|
||||||
|
}),
|
||||||
|
).toBe("123");
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
@@ -67,6 +67,18 @@ export type DiscordChannelConfigResolved = {
|
|||||||
requireMention?: boolean;
|
requireMention?: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export function resolveDiscordReplyTarget(opts: {
|
||||||
|
replyToMode: ReplyToMode;
|
||||||
|
replyToId?: string;
|
||||||
|
hasReplied: boolean;
|
||||||
|
}): string | undefined {
|
||||||
|
if (opts.replyToMode === "off") return undefined;
|
||||||
|
const replyToId = opts.replyToId?.trim();
|
||||||
|
if (!replyToId) return undefined;
|
||||||
|
if (opts.replyToMode === "all") return replyToId;
|
||||||
|
return opts.hasReplied ? undefined : replyToId;
|
||||||
|
}
|
||||||
|
|
||||||
function summarizeAllowList(list?: Array<string | number>) {
|
function summarizeAllowList(list?: Array<string | number>) {
|
||||||
if (!list || list.length === 0) return "any";
|
if (!list || list.length === 0) return "any";
|
||||||
const sample = list.slice(0, 4).map((entry) => String(entry));
|
const sample = list.slice(0, 4).map((entry) => String(entry));
|
||||||
@@ -1000,19 +1012,20 @@ async function deliverReplies({
|
|||||||
const mediaList =
|
const mediaList =
|
||||||
payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
|
payload.mediaUrls ?? (payload.mediaUrl ? [payload.mediaUrl] : []);
|
||||||
const text = payload.text ?? "";
|
const text = payload.text ?? "";
|
||||||
const replyToId =
|
const replyToId = payload.replyToId;
|
||||||
replyToMode === "off" ? undefined : payload.replyToId?.trim();
|
|
||||||
if (!text && mediaList.length === 0) continue;
|
if (!text && mediaList.length === 0) continue;
|
||||||
if (mediaList.length === 0) {
|
if (mediaList.length === 0) {
|
||||||
for (const chunk of chunkText(text, 2000)) {
|
for (const chunk of chunkText(text, 2000)) {
|
||||||
|
const replyTo = resolveDiscordReplyTarget({
|
||||||
|
replyToMode,
|
||||||
|
replyToId,
|
||||||
|
hasReplied,
|
||||||
|
});
|
||||||
await sendMessageDiscord(target, chunk, {
|
await sendMessageDiscord(target, chunk, {
|
||||||
token,
|
token,
|
||||||
replyTo:
|
replyTo,
|
||||||
replyToId && (replyToMode === "all" || !hasReplied)
|
|
||||||
? replyToId
|
|
||||||
: undefined,
|
|
||||||
});
|
});
|
||||||
if (replyToId && !hasReplied) {
|
if (replyTo && !hasReplied) {
|
||||||
hasReplied = true;
|
hasReplied = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1021,15 +1034,17 @@ async function deliverReplies({
|
|||||||
for (const mediaUrl of mediaList) {
|
for (const mediaUrl of mediaList) {
|
||||||
const caption = first ? text : "";
|
const caption = first ? text : "";
|
||||||
first = false;
|
first = false;
|
||||||
|
const replyTo = resolveDiscordReplyTarget({
|
||||||
|
replyToMode,
|
||||||
|
replyToId,
|
||||||
|
hasReplied,
|
||||||
|
});
|
||||||
await sendMessageDiscord(target, caption, {
|
await sendMessageDiscord(target, caption, {
|
||||||
token,
|
token,
|
||||||
mediaUrl,
|
mediaUrl,
|
||||||
replyTo:
|
replyTo,
|
||||||
replyToId && (replyToMode === "all" || !hasReplied)
|
|
||||||
? replyToId
|
|
||||||
: undefined,
|
|
||||||
});
|
});
|
||||||
if (replyToId && !hasReplied) {
|
if (replyTo && !hasReplied) {
|
||||||
hasReplied = true;
|
hasReplied = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -189,6 +189,74 @@ describe("createTelegramBot", () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("honors replyToMode=first for threaded replies", async () => {
|
||||||
|
onSpy.mockReset();
|
||||||
|
sendMessageSpy.mockReset();
|
||||||
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<
|
||||||
|
typeof vi.fn
|
||||||
|
>;
|
||||||
|
replySpy.mockReset();
|
||||||
|
replySpy.mockResolvedValue({
|
||||||
|
text: "a".repeat(4500),
|
||||||
|
replyToId: "101",
|
||||||
|
});
|
||||||
|
|
||||||
|
createTelegramBot({ token: "tok", replyToMode: "first" });
|
||||||
|
const handler = onSpy.mock.calls[0][1] as (
|
||||||
|
ctx: Record<string, unknown>,
|
||||||
|
) => Promise<void>;
|
||||||
|
await handler({
|
||||||
|
message: {
|
||||||
|
chat: { id: 5, type: "private" },
|
||||||
|
text: "hi",
|
||||||
|
date: 1736380800,
|
||||||
|
message_id: 101,
|
||||||
|
},
|
||||||
|
me: { username: "clawdis_bot" },
|
||||||
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sendMessageSpy.mock.calls.length).toBeGreaterThan(1);
|
||||||
|
const [first, ...rest] = sendMessageSpy.mock.calls;
|
||||||
|
expect(first?.[2]?.reply_to_message_id).toBe(101);
|
||||||
|
for (const call of rest) {
|
||||||
|
expect(call[2]?.reply_to_message_id).toBeUndefined();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("honors replyToMode=all for threaded replies", async () => {
|
||||||
|
onSpy.mockReset();
|
||||||
|
sendMessageSpy.mockReset();
|
||||||
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<
|
||||||
|
typeof vi.fn
|
||||||
|
>;
|
||||||
|
replySpy.mockReset();
|
||||||
|
replySpy.mockResolvedValue({
|
||||||
|
text: "a".repeat(4500),
|
||||||
|
replyToId: "101",
|
||||||
|
});
|
||||||
|
|
||||||
|
createTelegramBot({ token: "tok", replyToMode: "all" });
|
||||||
|
const handler = onSpy.mock.calls[0][1] as (
|
||||||
|
ctx: Record<string, unknown>,
|
||||||
|
) => Promise<void>;
|
||||||
|
await handler({
|
||||||
|
message: {
|
||||||
|
chat: { id: 5, type: "private" },
|
||||||
|
text: "hi",
|
||||||
|
date: 1736380800,
|
||||||
|
message_id: 101,
|
||||||
|
},
|
||||||
|
me: { username: "clawdis_bot" },
|
||||||
|
getFile: async () => ({ download: async () => new Uint8Array() }),
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(sendMessageSpy.mock.calls.length).toBeGreaterThan(1);
|
||||||
|
for (const call of sendMessageSpy.mock.calls) {
|
||||||
|
expect(call[2]?.reply_to_message_id).toBe(101);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it("skips group messages without mention when requireMention is enabled", async () => {
|
it("skips group messages without mention when requireMention is enabled", async () => {
|
||||||
onSpy.mockReset();
|
onSpy.mockReset();
|
||||||
const replySpy = replyModule.__replySpy as unknown as ReturnType<
|
const replySpy = replyModule.__replySpy as unknown as ReturnType<
|
||||||
|
|||||||
Reference in New Issue
Block a user