Skip to content
This repository was archived by the owner on Feb 21, 2026. It is now read-only.

Commit 63c53c8

Browse files
feat: auto-respond in bot-created Discord threads (#144)
* feat: auto-respond in bot-created Discord threads (#77) Messages sent in Discord threads created by this bot now trigger the agent automatically — no @mention required, same as DMs. **Changes to handleMessage:** - Detect thread messages via `message.channel.isThread()` - For threads, resolve the parent channel JID for group lookup (threads aren't registered groups, but their parent channels are) - Bot filter block updated to also use parent channel JID for threads (preserves per-group trigger patterns for agent-to-agent comms) - Auto-trigger logic: if `message.channel.ownerId === botId`, prepend trigger and thread context (`[In thread: <name>]`) to the message - Responses currently go to the parent channel; full in-thread reply routing can be added in a follow-up PR **Not changed:** - Thread registration: no new DB entries needed - Message storage: thread messages stored under parent channel JID - Reactions and other Discord events: unaffected Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> * fix(discord): address CodeRabbit review issues in PR #144 - Use per-group trigger name/pattern for auto-trigger in bot threads (fixes multi-agent setups where group trigger differs from ASSISTANT_NAME) - Check original content before prepending thread context to avoid potential double trigger prefix edge case - Fix mentionsOtherUsersOnly TS2345 error: add botId != null guard so Map.has() receives string (not string | undefined) Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent e8110e3 commit 63c53c8

1 file changed

Lines changed: 31 additions & 5 deletions

File tree

src/channels/discord.ts

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -443,20 +443,29 @@ export class DiscordChannel implements Channel {
443443
}
444444
}
445445

446+
const isDM = message.channel.type === ChannelType.DM;
447+
448+
// Detect thread messages: route via parent channel for group lookup
449+
const isThread = !isDM && message.channel.isThread();
450+
const threadParentId = isThread && message.channel.parent
451+
? message.channel.parent.id
452+
: null;
453+
446454
// Allow bot messages through only if they contain our trigger (agent-to-agent comms).
447455
// This prevents infinite loops — bots must explicitly @mention us.
448456
// Use per-channel trigger so agent-to-agent comms work across multi-agent servers.
457+
// For threads, resolve the parent channel's group for the trigger pattern.
449458
{
450-
const _isDM = message.channel.type === ChannelType.DM;
451-
const _chatJid = _isDM
459+
const _chatJid = isDM
452460
? `dc:dm:${message.author.id}`
453-
: `dc:${message.channelId}`;
461+
: isThread && threadParentId
462+
? `dc:${threadParentId}`
463+
: `dc:${message.channelId}`;
454464
const _group = getAllRegisteredGroups()[_chatJid];
455465
const _triggerPattern = buildTriggerPattern(_group?.trigger);
456466
if (message.author.bot && !_triggerPattern.test(content)) return;
457467
}
458468

459-
const isDM = message.channel.type === ChannelType.DM;
460469
// In guild channels, only process messages that mention THIS bot OR reply to the bot.
461470
// Prevents responding when another agent (e.g. @PeytonOmni) is mentioned instead.
462471
const isReplyToBot = message.mentions.repliedUser?.id === botId;
@@ -498,9 +507,13 @@ export class DiscordChannel implements Channel {
498507
}
499508
}
500509

510+
// For thread messages, route via parent channel JID so we find the registered group.
511+
// Responses go to the parent channel (thread-reply routing is a future enhancement).
501512
const chatJid = isDM
502513
? `dc:dm:${message.author.id}`
503-
: `dc:${message.channelId}`;
514+
: isThread && threadParentId
515+
? `dc:${threadParentId}`
516+
: `dc:${message.channelId}`;
504517

505518
const timestamp = message.createdAt.toISOString();
506519
const senderName =
@@ -631,6 +644,19 @@ export class DiscordChannel implements Channel {
631644
'Reply to bot — treating as triggered',
632645
);
633646
content = `@${ASSISTANT_NAME} ${content}`;
647+
} else if (isThread && message.channel.ownerId === botId) {
648+
// Auto-trigger in threads created by this bot — no @mention needed.
649+
// Use per-group trigger name for consistency with multi-agent setups.
650+
const threadName = message.channel.name || 'thread';
651+
const agentName = group?.trigger?.replace(/^@/, '') || ASSISTANT_NAME;
652+
const groupTriggerPattern = buildTriggerPattern(group?.trigger);
653+
logger.info({ chatJid, threadId: message.channelId, threadName, sender: senderName }, 'Auto-triggering in bot-created thread');
654+
// Check original content before prepending thread context to avoid double trigger prefix
655+
const hasGroupTrigger = groupTriggerPattern.test(content);
656+
content = `[In thread: ${threadName}] ${content}`;
657+
if (!hasGroupTrigger) {
658+
content = `@${agentName} ${content}`;
659+
}
634660
} else if (this.shouldAutoRespond(content, group)) {
635661
logger.debug(
636662
{

0 commit comments

Comments
 (0)