feat: add group chat and mention-gating to channel adapters

Slack: add requireMention option, resolve bot user ID on connect.
Telegram: add group chat mention/reply-to-bot detection, strip @mention
from message text, default requireMention=true for groups.
WhatsApp: add allowedGroupIds for group chat support, mention detection
via mentionedIds and body text, strip bot mention from messages.
This commit is contained in:
William Valentin
2026-02-06 16:51:52 -08:00
parent 20930a4816
commit 647d7779c7
6 changed files with 510 additions and 16 deletions
+72 -1
View File
@@ -7,6 +7,9 @@ const mockPostMessage = vi.fn();
const mockStart = vi.fn();
const mockStop = vi.fn();
const mockAuthTest = vi.fn().mockResolvedValue({ user_id: 'UBOT123' });
const mockUsersInfo = vi.fn().mockResolvedValue({ user: { real_name: 'Test User', name: 'testuser' } });
/** Create a fresh mock Bolt App instance. */
function createMockApp() {
capturedMessageHandler = null;
@@ -20,6 +23,12 @@ function createMockApp() {
chat: {
postMessage: mockPostMessage,
},
auth: {
test: mockAuthTest,
},
users: {
info: mockUsersInfo,
},
},
};
}
@@ -172,7 +181,7 @@ describe('SlackAdapter', () => {
const msg: InboundMessage = handler.mock.calls[0][0];
expect(msg.channel).toBe('slack');
expect(msg.senderId).toBe('C123:1111.0000');
expect(msg.senderName).toBe('U456');
expect(msg.senderName).toBe('Test User');
expect(msg.text).toBe('Hello Flynn');
expect(msg.id).toBe('1234.5678');
});
@@ -374,4 +383,66 @@ describe('SlackAdapter', () => {
const msg: InboundMessage = handler.mock.calls[0][0];
expect(msg.text).toBe('Hello');
});
// ── Mention gating ─────────────────────────────────────────────
it('ignores messages without mention when requireMention is true', async () => {
const mentionAdapter = new SlackAdapter({
...baseConfig,
requireMention: true,
});
const handler = vi.fn();
mentionAdapter.onMessage(handler);
await mentionAdapter.connect();
await simulateMessage({
ts: '1234.5678',
channel: 'C123',
user: 'U456',
text: 'Hello everyone',
});
expect(handler).not.toHaveBeenCalled();
});
it('processes messages with bot mention when requireMention is true', async () => {
const mentionAdapter = new SlackAdapter({
...baseConfig,
requireMention: true,
});
const handler = vi.fn();
mentionAdapter.onMessage(handler);
await mentionAdapter.connect();
await simulateMessage({
ts: '1234.5678',
channel: 'C123',
user: 'U456',
text: '<@UBOT123> What is the weather?',
});
expect(handler).toHaveBeenCalledTimes(1);
const msg: InboundMessage = handler.mock.calls[0][0];
expect(msg.text).toBe('What is the weather?');
});
it('processes all messages when requireMention is false (default)', async () => {
const handler = vi.fn();
adapter.onMessage(handler);
await adapter.connect();
await simulateMessage({
ts: '1234.5678',
channel: 'C123',
user: 'U456',
text: 'Hello without mention',
});
expect(handler).toHaveBeenCalledTimes(1);
const msg: InboundMessage = handler.mock.calls[0][0];
expect(msg.text).toBe('Hello without mention');
});
});