refactor: land approved runtime cleanup and room binding cutover
This commit is contained in:
@@ -133,7 +133,7 @@ function createTestOpts(
|
||||
return {
|
||||
onMessage: vi.fn(),
|
||||
onChatMetadata: vi.fn(),
|
||||
registeredGroups: vi.fn(() => ({
|
||||
roomBindings: vi.fn(() => ({
|
||||
'dc:1234567890123456': {
|
||||
name: 'Test Server #general',
|
||||
folder: 'test-server',
|
||||
@@ -441,7 +441,7 @@ describe('DiscordChannel', () => {
|
||||
|
||||
it('uses sender name for DM chats (no guild)', async () => {
|
||||
const opts = createTestOpts({
|
||||
registeredGroups: vi.fn(() => ({
|
||||
roomBindings: vi.fn(() => ({
|
||||
'dc:1234567890123456': {
|
||||
name: 'DM',
|
||||
folder: 'dm',
|
||||
@@ -491,10 +491,10 @@ describe('DiscordChannel', () => {
|
||||
});
|
||||
});
|
||||
|
||||
// --- @mention translation ---
|
||||
// --- bot mention handling ---
|
||||
|
||||
describe('@mention translation', () => {
|
||||
it('translates <@botId> mention to trigger format', async () => {
|
||||
describe('bot mention handling', () => {
|
||||
it('passes through <@botId> mentions without rewriting them', async () => {
|
||||
const opts = createTestOpts();
|
||||
const channel = new DiscordChannel('test-token', opts);
|
||||
await channel.connect();
|
||||
@@ -509,12 +509,12 @@ describe('DiscordChannel', () => {
|
||||
expect(opts.onMessage).toHaveBeenCalledWith(
|
||||
'dc:1234567890123456',
|
||||
expect.objectContaining({
|
||||
content: '@Andy what time is it?',
|
||||
content: '<@999888777> what time is it?',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
it('does not translate if message already matches trigger', async () => {
|
||||
it('leaves mixed text and mentions untouched', async () => {
|
||||
const opts = createTestOpts();
|
||||
const channel = new DiscordChannel('test-token', opts);
|
||||
await channel.connect();
|
||||
@@ -531,7 +531,7 @@ describe('DiscordChannel', () => {
|
||||
expect(opts.onMessage).toHaveBeenCalledWith(
|
||||
'dc:1234567890123456',
|
||||
expect.objectContaining({
|
||||
content: '@Andy hello',
|
||||
content: '@Andy hello <@999888777>',
|
||||
}),
|
||||
);
|
||||
});
|
||||
@@ -555,7 +555,7 @@ describe('DiscordChannel', () => {
|
||||
);
|
||||
});
|
||||
|
||||
it('handles <@!botId> (nickname mention format)', async () => {
|
||||
it('passes through <@!botId> nickname mentions without rewriting them', async () => {
|
||||
const opts = createTestOpts();
|
||||
const channel = new DiscordChannel('test-token', opts);
|
||||
await channel.connect();
|
||||
@@ -570,7 +570,7 @@ describe('DiscordChannel', () => {
|
||||
expect(opts.onMessage).toHaveBeenCalledWith(
|
||||
'dc:1234567890123456',
|
||||
expect.objectContaining({
|
||||
content: '@Andy check this',
|
||||
content: '<@!999888777> check this',
|
||||
}),
|
||||
);
|
||||
});
|
||||
|
||||
@@ -208,7 +208,7 @@ import {
|
||||
export interface DiscordChannelOpts {
|
||||
onMessage: OnInboundMessage;
|
||||
onChatMetadata: OnChatMetadata;
|
||||
registeredGroups: () => Record<string, RegisteredGroup>;
|
||||
roomBindings: () => Record<string, RegisteredGroup>;
|
||||
}
|
||||
|
||||
export class DiscordChannel implements Channel {
|
||||
@@ -279,29 +279,6 @@ export class DiscordChannel implements Channel {
|
||||
chatName = senderName;
|
||||
}
|
||||
|
||||
// Translate Discord @bot mentions into TRIGGER_PATTERN format.
|
||||
// Discord mentions look like <@botUserId> — these won't match
|
||||
// TRIGGER_PATTERN (e.g., ^@Andy\b), so we prepend the trigger
|
||||
// when the bot is @mentioned.
|
||||
if (this.client?.user) {
|
||||
const botId = this.client.user.id;
|
||||
const isBotMentioned =
|
||||
message.mentions.users.has(botId) ||
|
||||
content.includes(`<@${botId}>`) ||
|
||||
content.includes(`<@!${botId}>`);
|
||||
|
||||
if (isBotMentioned) {
|
||||
// Strip the <@botId> mention to avoid visual clutter
|
||||
content = content
|
||||
.replace(new RegExp(`<@!?${botId}>`, 'g'), '')
|
||||
.trim();
|
||||
// Prepend trigger if not already present
|
||||
if (!TRIGGER_PATTERN.test(content)) {
|
||||
content = `@${ASSISTANT_NAME} ${content}`;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle attachments — transcribe voice messages, download files
|
||||
const isVoiceMessage = message.flags.has(MessageFlags.IsVoiceMessage);
|
||||
if (message.attachments.size > 0) {
|
||||
@@ -403,7 +380,7 @@ export class DiscordChannel implements Channel {
|
||||
|
||||
// Only deliver full message for registered groups. Secondary role bots
|
||||
// are configured as outbound-only, while the owner bot receives inbound.
|
||||
const group = this.opts.registeredGroups()[chatJid];
|
||||
const group = this.opts.roomBindings()[chatJid];
|
||||
if (!group) {
|
||||
logger.debug(
|
||||
{ chatJid, chatName },
|
||||
@@ -627,7 +604,7 @@ export class DiscordChannel implements Channel {
|
||||
if (!jid.startsWith('dc:')) return false;
|
||||
if (!this.ownsDiscordJids) return false;
|
||||
if (!this.agentTypeFilter) return true;
|
||||
const group = this.opts.registeredGroups()[jid];
|
||||
const group = this.opts.roomBindings()[jid];
|
||||
if (!group) return false;
|
||||
const groupType = group.agentType || 'claude-code';
|
||||
return groupType === this.agentTypeFilter;
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
export interface ChannelOpts {
|
||||
onMessage: OnInboundMessage;
|
||||
onChatMetadata: OnChatMetadata;
|
||||
registeredGroups: () => Record<string, RegisteredGroup>;
|
||||
roomBindings: () => Record<string, RegisteredGroup>;
|
||||
}
|
||||
|
||||
export type ChannelFactory = (opts: ChannelOpts) => Channel | null;
|
||||
|
||||
Reference in New Issue
Block a user