From 4b4e28ef6a3a77a9d60d1d4c29b826b9cbc5412d Mon Sep 17 00:00:00 2001 From: Brian O'Kelley Date: Fri, 2 Jan 2026 17:13:40 -0500 Subject: [PATCH] Remove AAO bot; use Addie for all Slack operations MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Consolidate to single Slack bot (Addie) instead of dual-bot setup: - ADDIE_BOT_TOKEN is now primary, with SLACK_BOT_TOKEN fallback for migration - Remove useAddieToken parameter from sendChannelMessage and getThreadReplies - Remove getSlackUserWithAddieToken helper function (use getSlackUser instead) - Update all callers to use simplified API - Update error messages to reference ADDIE_BOT_TOKEN This fixes the industry alerts "not_in_channel" error since Addie (not the AAO bot) is the bot added to notification channels. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .changeset/fix-industry-alerts-token.md | 11 ++++ server/src/addie/bolt-app.ts | 8 +-- server/src/addie/handler.ts | 10 +-- server/src/slack/client.ts | 88 +++++-------------------- server/src/slack/sync.ts | 4 +- 5 files changed, 40 insertions(+), 81 deletions(-) create mode 100644 .changeset/fix-industry-alerts-token.md diff --git a/.changeset/fix-industry-alerts-token.md b/.changeset/fix-industry-alerts-token.md new file mode 100644 index 0000000000..90ecfea9e9 --- /dev/null +++ b/.changeset/fix-industry-alerts-token.md @@ -0,0 +1,11 @@ +--- +"adcontextprotocol": patch +--- + +Remove AAO bot dependency; use Addie bot for all Slack operations + +- Consolidate to single Slack bot (Addie) instead of dual-bot setup +- ADDIE_BOT_TOKEN is now primary, with SLACK_BOT_TOKEN fallback for migration +- Remove useAddieToken parameter from sendChannelMessage and getThreadReplies +- Remove getSlackUserWithAddieToken helper function +- Update all callers to use simplified API diff --git a/server/src/addie/bolt-app.ts b/server/src/addie/bolt-app.ts index 24b073e3d7..b949f62bac 100644 --- a/server/src/addie/bolt-app.ts +++ b/server/src/addie/bolt-app.ts @@ -63,7 +63,7 @@ import type { RequestTools } from './claude-client.js'; import type { SuggestedPrompt } from './types.js'; import { DatabaseThreadContextStore } from './thread-context-store.js'; import { getThreadService, type ThreadContext } from './thread-service.js'; -import { getThreadReplies, getSlackUserWithAddieToken, getChannelInfo } from '../slack/client.js'; +import { getThreadReplies, getSlackUser, getChannelInfo } from '../slack/client.js'; import { AddieRouter, type RoutingContext, type ExecutionPlan } from './router.js'; import { getCachedInsights, prefetchInsights } from './insights-cache.js'; @@ -803,7 +803,7 @@ async function handleAppMention({ let threadContext = ''; if (isInThread && event.thread_ts) { try { - const threadMessages = await getThreadReplies(channelId, event.thread_ts, true); + const threadMessages = await getThreadReplies(channelId, event.thread_ts); if (threadMessages.length > 0) { // Filter out Addie's own messages and format the thread history const filteredMessages = threadMessages @@ -828,7 +828,7 @@ async function handleAppMention({ if (mentionedUserIds.size > 0) { const lookups = await Promise.all( Array.from(mentionedUserIds).map(async (uid) => { - const user = await getSlackUserWithAddieToken(uid); + const user = await getSlackUser(uid); return { uid, name: user?.profile?.display_name || user?.real_name || user?.name || null }; }) ); @@ -1150,7 +1150,7 @@ async function indexChannelMessage( try { // Fetch user and channel info const [user, channel] = await Promise.all([ - getSlackUserWithAddieToken(userId), + getSlackUser(userId), getChannelInfo(channelId), ]); diff --git a/server/src/addie/handler.ts b/server/src/addie/handler.ts index 47b283f902..4076bd3661 100644 --- a/server/src/addie/handler.ts +++ b/server/src/addie/handler.ts @@ -314,12 +314,12 @@ export async function handleAssistantMessage( // Validate output const outputValidation = validateOutput(response.text); - // Send response using Addie's bot token + // Send response try { await sendChannelMessage(channelId, { text: outputValidation.sanitized, thread_ts: event.thread_ts, - }, true); // useAddieToken = true + }); } catch (error) { logger.error({ error }, 'Addie: Failed to send response'); } @@ -433,12 +433,12 @@ export async function handleAppMention(event: AppMentionEvent): Promise { // Validate output const outputValidation = validateOutput(response.text); - // Send response in thread using Addie's bot token + // Send response in thread try { await sendChannelMessage(event.channel, { text: outputValidation.sanitized, thread_ts: event.thread_ts || event.ts, - }, true); // useAddieToken = true + }); } catch (error) { logger.error({ error }, 'Addie: Failed to send mention response'); } @@ -579,7 +579,7 @@ export async function sendAccountLinkedMessage( await sendChannelMessage(recentThread.channel_id, { text: message, thread_ts: recentThread.thread_ts, - }, true); // useAddieToken = true + }); logger.info({ slackUserId, channelId: recentThread.channel_id }, 'Addie: Sent account linked message'); return true; } catch (error) { diff --git a/server/src/slack/client.ts b/server/src/slack/client.ts index b3c86c207c..fe92bb0b1c 100644 --- a/server/src/slack/client.ts +++ b/server/src/slack/client.ts @@ -1,8 +1,8 @@ /** - * Slack Web API client for AAO integration + * Slack Web API client * * Provides methods for user lookup, DM sending, and channel management. - * Uses bot token authentication. + * Uses Addie's bot token for all operations. */ import { logger } from '../logger.js'; @@ -13,8 +13,8 @@ import type { SlackBlockMessage, } from './types.js'; -const SLACK_BOT_TOKEN = process.env.SLACK_BOT_TOKEN; -const ADDIE_BOT_TOKEN = process.env.ADDIE_BOT_TOKEN; +// Use ADDIE_BOT_TOKEN as the primary token (fall back to SLACK_BOT_TOKEN for migration) +const SLACK_BOT_TOKEN = process.env.ADDIE_BOT_TOKEN || process.env.SLACK_BOT_TOKEN; const SLACK_API_BASE = 'https://slack.com/api'; // Rate limiting: Slack's tier 2 methods allow ~20 requests per minute @@ -43,7 +43,7 @@ async function slackRequest( retries = 3 ): Promise { if (!SLACK_BOT_TOKEN) { - throw new Error('SLACK_BOT_TOKEN is not configured'); + throw new Error('ADDIE_BOT_TOKEN is not configured'); } const url = new URL(`${SLACK_API_BASE}/${method}`); @@ -99,17 +99,14 @@ async function slackRequest( /** * Make a POST request to the Slack API (for chat.postMessage, etc.) - * @param useAddieToken - If true, uses ADDIE_BOT_TOKEN instead of SLACK_BOT_TOKEN */ async function slackPostRequest( method: string, body: Record, - retries = 3, - useAddieToken = false + retries = 3 ): Promise { - const token = useAddieToken ? (ADDIE_BOT_TOKEN || SLACK_BOT_TOKEN) : SLACK_BOT_TOKEN; - if (!token) { - throw new Error(useAddieToken ? 'ADDIE_BOT_TOKEN is not configured' : 'SLACK_BOT_TOKEN is not configured'); + if (!SLACK_BOT_TOKEN) { + throw new Error('ADDIE_BOT_TOKEN is not configured'); } const url = `${SLACK_API_BASE}/${method}`; @@ -119,7 +116,7 @@ async function slackPostRequest( const response = await fetch(url, { method: 'POST', headers: { - Authorization: `Bearer ${token}`, + Authorization: `Bearer ${SLACK_BOT_TOKEN}`, 'Content-Type': 'application/json; charset=utf-8', }, body: JSON.stringify(body), @@ -160,10 +157,10 @@ function sleep(ms: number): Promise { } /** - * Check if Slack integration is configured + * Check if Slack integration is configured (Addie bot token) */ export function isSlackConfigured(): boolean { - return Boolean(SLACK_BOT_TOKEN); + return Boolean(process.env.ADDIE_BOT_TOKEN || process.env.SLACK_BOT_TOKEN); } /** @@ -211,42 +208,6 @@ export async function getSlackUser(userId: string): Promise { } } -/** - * Get a single user by ID using Addie's bot token - * Use this when Addie needs to look up users in channels it has access to - */ -export async function getSlackUserWithAddieToken(userId: string): Promise { - if (!ADDIE_BOT_TOKEN) { - logger.warn('ADDIE_BOT_TOKEN not configured, cannot look up user'); - return null; - } - - try { - const url = new URL(`${SLACK_API_BASE}/users.info`); - url.searchParams.set('user', userId); - - const response = await fetch(url.toString(), { - method: 'GET', - headers: { - Authorization: `Bearer ${ADDIE_BOT_TOKEN}`, - 'Content-Type': 'application/x-www-form-urlencoded', - }, - }); - - const data = await response.json() as { ok: boolean; user?: SlackUser; error?: string }; - - if (!data.ok) { - logger.warn({ error: data.error, userId }, 'Failed to get Slack user with Addie token'); - return null; - } - - return data.user || null; - } catch (error) { - logger.error({ error, userId }, 'Error fetching Slack user with Addie token'); - return null; - } -} - /** * Look up a user by email address */ @@ -299,12 +260,10 @@ export async function sendDirectMessage( /** * Send a message to a channel - * @param useAddieToken - If true, uses ADDIE_BOT_TOKEN for Addie's DM channels */ export async function sendChannelMessage( channelId: string, - message: SlackBlockMessage, - useAddieToken = false + message: SlackBlockMessage ): Promise<{ ok: boolean; ts?: string; error?: string }> { try { const response = await slackPostRequest<{ ts: string }>('chat.postMessage', { @@ -313,9 +272,9 @@ export async function sendChannelMessage( blocks: message.blocks, thread_ts: message.thread_ts, reply_broadcast: message.reply_broadcast, - }, 3, useAddieToken); + }); - logger.info({ channelId, ts: response.ts, useAddieToken }, 'Sent Slack channel message'); + logger.info({ channelId, ts: response.ts }, 'Sent Slack channel message'); return { ok: true, ts: response.ts }; } catch (error) { const errorMessage = error instanceof Error ? error.message : 'Unknown error'; @@ -491,24 +450,13 @@ export interface SlackThreadMessage { /** * Get thread replies (conversations.replies) * Returns all messages in a thread, including the parent message - * @param useAddieToken - If true, uses ADDIE_BOT_TOKEN */ export async function getThreadReplies( channelId: string, - threadTs: string, - useAddieToken = false + threadTs: string ): Promise { - let token: string | undefined; - if (useAddieToken) { - if (!ADDIE_BOT_TOKEN) { - throw new Error('ADDIE_BOT_TOKEN is not configured'); - } - token = ADDIE_BOT_TOKEN; - } else { - if (!SLACK_BOT_TOKEN) { - throw new Error('SLACK_BOT_TOKEN is not configured'); - } - token = SLACK_BOT_TOKEN; + if (!SLACK_BOT_TOKEN) { + throw new Error('ADDIE_BOT_TOKEN is not configured'); } try { @@ -520,7 +468,7 @@ export async function getThreadReplies( const response = await fetch(url.toString(), { method: 'GET', headers: { - Authorization: `Bearer ${token}`, + Authorization: `Bearer ${SLACK_BOT_TOKEN}`, 'Content-Type': 'application/x-www-form-urlencoded', }, }); diff --git a/server/src/slack/sync.ts b/server/src/slack/sync.ts index 7f7a9994cb..7ed1bbe0d9 100644 --- a/server/src/slack/sync.ts +++ b/server/src/slack/sync.ts @@ -30,7 +30,7 @@ export async function syncSlackUsers(): Promise { new_users: 0, updated_users: 0, auto_mapped: 0, - errors: ['Slack is not configured (SLACK_BOT_TOKEN missing)'], + errors: ['Slack is not configured (ADDIE_BOT_TOKEN missing)'], }; } @@ -197,7 +197,7 @@ export async function syncWorkingGroupMembersFromSlack( members_added: 0, members_already_in_group: 0, unmapped_slack_users: 0, - errors: ['Slack is not configured (SLACK_BOT_TOKEN missing)'], + errors: ['Slack is not configured (ADDIE_BOT_TOKEN missing)'], }; }