diff --git a/src/message-runtime-role-channels.test.ts b/src/message-runtime-role-channels.test.ts new file mode 100644 index 0000000..02ccc02 --- /dev/null +++ b/src/message-runtime-role-channels.test.ts @@ -0,0 +1,56 @@ +import { describe, expect, it, vi } from 'vitest'; + +import { resolvePairedRoleChannels } from './message-runtime-role-channels.js'; +import type { Channel } from './types.js'; + +function makeChannel(name: string): Channel { + return { + name, + connect: vi.fn(), + sendMessage: vi.fn(), + isConnected: vi.fn(() => true), + ownsJid: vi.fn(() => false), + disconnect: vi.fn(), + } as unknown as Channel; +} + +describe('message-runtime-role-channels', () => { + it('maps owner, reviewer, and arbiter roles to their runtime channels', () => { + const ownerChannel = makeChannel('discord-owner'); + const reviewerChannel = makeChannel('discord-review'); + const arbiterChannel = makeChannel('discord-arbiter'); + + const result = resolvePairedRoleChannels( + [ownerChannel, reviewerChannel, arbiterChannel], + ownerChannel, + ); + + expect(result).toEqual({ + roleToChannel: { + owner: ownerChannel, + reviewer: reviewerChannel, + arbiter: arbiterChannel, + }, + reviewerChannelName: 'discord-review', + foundReviewerChannel: reviewerChannel, + arbiterChannelName: 'discord-arbiter', + foundArbiterChannel: arbiterChannel, + }); + }); + + it('keeps missing reviewer and arbiter channels explicit as null', () => { + const ownerChannel = makeChannel('discord-owner'); + + expect(resolvePairedRoleChannels([ownerChannel], ownerChannel)).toEqual({ + roleToChannel: { + owner: ownerChannel, + reviewer: null, + arbiter: null, + }, + reviewerChannelName: 'discord-review', + foundReviewerChannel: null, + arbiterChannelName: 'discord-arbiter', + foundArbiterChannel: null, + }); + }); +}); diff --git a/src/message-runtime-role-channels.ts b/src/message-runtime-role-channels.ts new file mode 100644 index 0000000..2847c41 --- /dev/null +++ b/src/message-runtime-role-channels.ts @@ -0,0 +1,37 @@ +import { findChannelByName } from './router.js'; +import type { Channel, PairedRoomRole } from './types.js'; + +export type PairedRoleChannels = Record; + +export interface PairedRoleChannelResolution { + roleToChannel: PairedRoleChannels; + reviewerChannelName: string; + foundReviewerChannel: Channel | null; + arbiterChannelName: string; + foundArbiterChannel: Channel | null; +} + +export function resolvePairedRoleChannels( + channels: Channel[], + ownerChannel: Channel, +): PairedRoleChannelResolution { + const reviewerChannelName = 'discord-review'; + const foundReviewerChannel = + findChannelByName(channels, reviewerChannelName) ?? null; + + const arbiterChannelName = 'discord-arbiter'; + const foundArbiterChannel = + findChannelByName(channels, arbiterChannelName) ?? null; + + return { + roleToChannel: { + owner: ownerChannel, + reviewer: foundReviewerChannel, + arbiter: foundArbiterChannel, + }, + reviewerChannelName, + foundReviewerChannel, + arbiterChannelName, + foundArbiterChannel, + }; +} diff --git a/src/message-runtime.ts b/src/message-runtime.ts index aa72b36..0b4de29 100644 --- a/src/message-runtime.ts +++ b/src/message-runtime.ts @@ -13,7 +13,7 @@ import { SERVICE_SESSION_SCOPE, } from './config.js'; import { GroupQueue, GroupRunContext } from './group-queue.js'; -import { findChannel, findChannelByName, formatMessages } from './router.js'; +import { findChannel, formatMessages } from './router.js'; import { isTriggerAllowed, loadSenderAllowlist } from './sender-allowlist.js'; import { enqueueGenericFollowUpAfterDeliveryRetry as enqueueDeliveryRetryFollowUp } from './message-runtime-dispatch.js'; import { @@ -73,6 +73,7 @@ import { isDuplicateOfLastBotFinal, labelPairedSenders, } from './message-runtime-turns.js'; +import { resolvePairedRoleChannels } from './message-runtime-role-channels.js'; export { isDuplicateOfLastBotFinal }; @@ -381,28 +382,13 @@ export function createMessageRuntime(deps: MessageRuntimeDeps): { return true; } - // For paired rooms, reviewer/arbiter outputs use their fixed role bots. - const reviewerChannelName = 'discord-review'; - const foundReviewerChannel = findChannelByName( - deps.channels, + const { + roleToChannel, reviewerChannelName, - ); - - const arbiterChannelName = 'discord-arbiter'; - const foundArbiterChannel = findChannelByName( - deps.channels, + foundReviewerChannel, arbiterChannelName, - ); - - // Resolve the correct Discord channel for a given task status. - const roleToChannel: Record< - 'owner' | 'reviewer' | 'arbiter', - Channel | null - > = { - owner: channel, - reviewer: foundReviewerChannel || null, - arbiter: foundArbiterChannel || null, - }; + foundArbiterChannel, + } = resolvePairedRoleChannels(deps.channels, channel); if (hasReviewerLease(chatJid)) { log.info( {