diff --git a/extensions/zalouser/src/channel.directory.test.ts b/extensions/zalouser/src/channel.directory.test.ts new file mode 100644 index 00000000000..70b18e0c1fe --- /dev/null +++ b/extensions/zalouser/src/channel.directory.test.ts @@ -0,0 +1,52 @@ +import { describe, expect, it, vi } from "vitest"; + +const listZaloGroupMembersMock = vi.hoisted(() => vi.fn(async () => [])); + +vi.mock("./zalo-js.js", async (importOriginal) => { + const actual = (await importOriginal()) as Record; + return { + ...actual, + listZaloGroupMembers: listZaloGroupMembersMock, + }; +}); + +vi.mock("./accounts.js", async (importOriginal) => { + const actual = (await importOriginal()) as Record; + return { + ...actual, + resolveZalouserAccountSync: () => ({ + accountId: "default", + profile: "default", + name: "test", + enabled: true, + authenticated: true, + config: {}, + }), + }; +}); + +import { zalouserPlugin } from "./channel.js"; + +describe("zalouser directory group members", () => { + it("accepts prefixed group ids from directory groups list output", async () => { + await zalouserPlugin.directory!.listGroupMembers!({ + cfg: {}, + accountId: "default", + groupId: "group:1471383327500481391", + runtime: undefined, + }); + + expect(listZaloGroupMembersMock).toHaveBeenCalledWith("default", "1471383327500481391"); + }); + + it("keeps backward compatibility for raw group ids", async () => { + await zalouserPlugin.directory!.listGroupMembers!({ + cfg: {}, + accountId: "default", + groupId: "1471383327500481391", + runtime: undefined, + }); + + expect(listZaloGroupMembersMock).toHaveBeenCalledWith("default", "1471383327500481391"); + }); +}); diff --git a/extensions/zalouser/src/channel.ts b/extensions/zalouser/src/channel.ts index a0414b7b245..24ecb2367e3 100644 --- a/extensions/zalouser/src/channel.ts +++ b/extensions/zalouser/src/channel.ts @@ -117,6 +117,25 @@ function parseZalouserOutboundTarget(raw: string): { return { threadId: normalized, isGroup: false }; } +function parseZalouserDirectoryGroupId(raw: string): string { + const normalized = normalizePrefixedTarget(raw); + if (!normalized) { + throw new Error("Zalouser group target is required"); + } + const lowered = normalized.toLowerCase(); + if (lowered.startsWith("group:")) { + const groupId = normalized.slice("group:".length).trim(); + if (!groupId) { + throw new Error("Zalouser group target is missing group id"); + } + return groupId; + } + if (lowered.startsWith("user:")) { + throw new Error("Zalouser group members lookup requires a group target (group:)"); + } + return normalized; +} + function resolveZalouserQrProfile(accountId?: string | null): string { const normalized = normalizeAccountId(accountId); if (!normalized || normalized === DEFAULT_ACCOUNT_ID) { @@ -501,7 +520,8 @@ export const zalouserPlugin: ChannelPlugin = { }, listGroupMembers: async ({ cfg, accountId, groupId, limit }) => { const account = resolveZalouserAccountSync({ cfg: cfg, accountId }); - const members = await listZaloGroupMembers(account.profile, groupId); + const normalizedGroupId = parseZalouserDirectoryGroupId(groupId); + const members = await listZaloGroupMembers(account.profile, normalizedGroupId); const rows = members.map((member) => mapUser({ id: member.userId,