MCPcopy Create free account
hub / github.com/adcontextprotocol/adcp / handleChannelMessage

Function handleChannelMessage

server/src/addie/bolt-app.ts:3708–4247  ·  view source on GitHub ↗

* Handle channel messages (not mentions) for HITL proposed responses * * When Addie sees a message in a channel it's in, it uses the router to * determine if/how to respond. Responses are queued for admin approval.

({
  event,
  context,
}: SlackEventMiddlewareArgs<'message'> & { context: { botUserId?: string } })

Source from the content-addressed store, hash-verified

3706 * determine if/how to respond. Responses are queued for admin approval.
3707 */
3708async function handleChannelMessage({
3709 event,
3710 context,
3711}: SlackEventMiddlewareArgs<'message'> & { context: { botUserId?: string } }): Promise<void> {
3712 // Skip if not initialized
3713 if (!claudeClient || !addieDb || !addieRouter) {
3714 return;
3715 }
3716
3717 // Skip bot messages (including our own)
3718 if ('bot_id' in event && event.bot_id) {
3719 return;
3720 }
3721
3722 // Skip subtypes (edits, deletes, etc.) - but only for non-DM messages
3723 // DMs can have forwarded messages where text is empty but attachments have content
3724 const hasText = 'text' in event && event.text;
3725 const hasAttachments = 'attachments' in event && Array.isArray(event.attachments) && event.attachments.length > 0;
3726 const hasFiles = 'files' in event && Array.isArray(event.files) && event.files.length > 0;
3727 const hasSubtype = 'subtype' in event && event.subtype;
3728
3729 const userId = 'user' in event ? event.user : undefined;
3730
3731 // Handle message_deleted for any channel type (DMs included)
3732 if (hasSubtype && 'subtype' in event && event.subtype === 'message_deleted') {
3733 const deletedTs = 'deleted_ts' in event ? (event as { deleted_ts: string }).deleted_ts : undefined;
3734 if (deletedTs) {
3735 const externalId = `${event.channel}:${deletedTs}`;
3736 const threadService = getThreadService();
3737 const deleted = await threadService.markSlackDeleted('slack', externalId);
3738 if (deleted) {
3739 logger.info({ channelId: event.channel, deletedTs }, 'Addie Bolt: Marked thread slack_deleted for deleted Slack message');
3740 }
3741 }
3742 return;
3743 }
3744
3745 // Handle DMs differently - route to the user message handler
3746 // For DMs, allow messages with attachments or files even if text is empty
3747 if (event.channel_type === 'im') {
3748 // Log detailed info for DM debugging (use same check as middleware for consistency)
3749 const hasThreadTs = 'thread_ts' in event && event.thread_ts !== undefined;
3750 logger.debug({
3751 channelId: event.channel,
3752 userId,
3753 hasText,
3754 hasAttachments,
3755 hasFiles,
3756 hasSubtype,
3757 subtype: 'subtype' in event ? event.subtype : undefined,
3758 hasThreadTs,
3759 threadTs: 'thread_ts' in event ? event.thread_ts : undefined,
3760 ts: 'ts' in event ? event.ts : undefined,
3761 }, 'Addie Bolt: Received DM in handleChannelMessage');
3762
3763 // DMs with thread_ts are handled by the Assistant framework (handleUserMessage).
3764 // Only handle first DMs (no thread_ts) here to avoid sending duplicate responses.
3765 if (hasThreadTs) {

Callers

nothing calls this directly

Calls 15

getThreadServiceFunction · 0.85
handleDirectMessageFunction · 0.85
indexChannelMessageFunction · 0.85
extractArticleUrlsFunction · 0.85
isManagedChannelFunction · 0.85
getSlackUserFunction · 0.85
queueCommunityArticleFunction · 0.85
handleDigestEditReplyFunction · 0.85
isMultiPartyThreadFunction · 0.85
isDirectedAtAddieFunction · 0.85
isAddressedToAnotherUserFunction · 0.85

Tested by

no test coverage detected