Skip to content

Commit cc8311b

Browse files
author
Joshua Chittick
committed
sync workspace on mention for unseen channels
1 parent f4dced3 commit cc8311b

File tree

3 files changed

+78
-12
lines changed

3 files changed

+78
-12
lines changed

.agents/skills/slack-developer-researcher/SKILL.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@ Ask clarifying questions if you need to focus on specific endpoints, scopes, or
1313

1414
## Sources
1515
- https://docs.slack.dev/apis/
16+
- https://docs.slack.dev/reference/events
1617
- https://docs.slack.dev/ai/developing-ai-apps

packages/ims/slack/client.ts

Lines changed: 45 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -365,14 +365,14 @@ function syncWorkspaceInBackground(workspace: WorkspaceAuth, channelId: string):
365365
void syncSlackWorkspace(workspace.workspaceId)
366366
.then((updatedWorkspace) => {
367367
invalidateOdeConfigCache();
368-
log.info("Slack workspace synced after bot joined channel", {
368+
log.info("Slack workspace synced after channel event", {
369369
workspaceId: workspace.workspaceId,
370370
workspaceName: updatedWorkspace.name,
371371
channelId,
372372
});
373373
})
374374
.catch((error) => {
375-
log.warn("Slack workspace sync failed after bot joined channel", {
375+
log.warn("Slack workspace sync failed after channel event", {
376376
workspaceId: workspace.workspaceId,
377377
channelId,
378378
error: String(error),
@@ -383,6 +383,48 @@ function syncWorkspaceInBackground(workspace: WorkspaceAuth, channelId: string):
383383
});
384384
}
385385

386+
async function syncWorkspaceAfterMention(
387+
channelId: string,
388+
workspace: { workspaceId?: string; workspaceName?: string } | undefined
389+
): Promise<boolean> {
390+
if (!workspace?.workspaceId) {
391+
log.warn("Skipping Slack workspace sync; workspace id missing", {
392+
channelId,
393+
workspaceName: workspace?.workspaceName,
394+
});
395+
return false;
396+
}
397+
398+
if (backgroundWorkspaceSyncInFlight.has(workspace.workspaceId)) {
399+
log.debug("Skipping Slack workspace sync; already in flight", {
400+
workspaceId: workspace.workspaceId,
401+
channelId,
402+
});
403+
return false;
404+
}
405+
406+
backgroundWorkspaceSyncInFlight.add(workspace.workspaceId);
407+
try {
408+
const updatedWorkspace = await syncSlackWorkspace(workspace.workspaceId);
409+
invalidateOdeConfigCache();
410+
log.info("Slack workspace synced after mention in unseen channel", {
411+
workspaceId: workspace.workspaceId,
412+
workspaceName: updatedWorkspace.name,
413+
channelId,
414+
});
415+
return true;
416+
} catch (error) {
417+
log.warn("Slack workspace sync failed after mention in unseen channel", {
418+
workspaceId: workspace.workspaceId,
419+
channelId,
420+
error: String(error),
421+
});
422+
return false;
423+
} finally {
424+
backgroundWorkspaceSyncInFlight.delete(workspace.workspaceId);
425+
}
426+
}
427+
386428
async function fetchWorkspaceAuth(
387429
appToken: string,
388430
botToken: string,
@@ -583,6 +625,7 @@ export function setupMessageHandlers(): void {
583625
app: slackApp,
584626
isAuthorizedChannel,
585627
resolveWorkspaceAuth,
628+
syncWorkspaceForChannel: syncWorkspaceAfterMention,
586629
getChannelWorkspaceName: (channelId) => channelWorkspaceMap.get(channelId),
587630
setChannelWorkspaceName: (channelId, workspaceName) => {
588631
channelWorkspaceMap.set(channelId, workspaceName);

packages/ims/slack/message-router.ts

Lines changed: 32 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,16 @@ type RouterDeps = {
66
resolveWorkspaceAuth: (
77
teamId?: string,
88
enterpriseId?: string
9-
) => { workspaceName?: string; botToken?: string; [key: string]: unknown } | undefined;
9+
) => { workspaceId?: string; workspaceName?: string; botToken?: string; [key: string]: unknown } | undefined;
10+
syncWorkspaceForChannel: (
11+
channelId: string,
12+
workspaceAuth: { workspaceId?: string; workspaceName?: string; botToken?: string; [key: string]: unknown } | undefined
13+
) => Promise<boolean>;
1014
getChannelWorkspaceName: (channelId: string) => string | undefined;
1115
setChannelWorkspaceName: (channelId: string, workspaceName: string) => void;
1216
setChannelWorkspaceAuth: (
1317
channelId: string,
14-
auth: { workspaceName?: string; botToken?: string; [key: string]: unknown } | undefined
18+
auth: { workspaceId?: string; workspaceName?: string; botToken?: string; [key: string]: unknown } | undefined
1519
) => void;
1620
isThreadActive: (channelId: string, threadId: string) => boolean;
1721
markThreadActive: (channelId: string, threadId: string) => void;
@@ -100,6 +104,18 @@ async function maybeHandleStopCommand(
100104
return true;
101105
}
102106

107+
async function maybeRefreshWorkspaceForMention(params: {
108+
deps: RouterDeps;
109+
channelId: string;
110+
isMention: boolean;
111+
workspaceAuth: WorkspaceAuth;
112+
}): Promise<void> {
113+
const { deps, channelId, isMention, workspaceAuth } = params;
114+
if (!isMention) return;
115+
if (deps.isAuthorizedChannel(channelId)) return;
116+
await deps.syncWorkspaceForChannel(channelId, workspaceAuth);
117+
}
118+
103119
async function maybeNotifySettingsIssues(
104120
deps: RouterDeps,
105121
channelId: string,
@@ -155,14 +171,9 @@ export function registerSlackMessageRouter(deps: RouterDeps): void {
155171

156172
const { channelId, userId, text, threadId, messageId } = incoming;
157173

158-
if (!deps.isAuthorizedChannel(channelId)) {
159-
log.info("[DROP] Unauthorized channel", { channelId });
160-
return;
161-
}
162-
163174
const authResult = await client.auth.test();
164175
const currentBotUserId = authResult.user_id as string;
165-
syncWorkspaceAuth(
176+
const workspaceAuth = syncWorkspaceAuth(
166177
deps,
167178
channelId,
168179
authResult.team_id,
@@ -174,11 +185,22 @@ export function registerSlackMessageRouter(deps: RouterDeps): void {
174185
return;
175186
}
176187

177-
if (await maybeHandleStopCommand(deps, text, channelId, threadId, say)) {
188+
const isMention = currentBotUserId ? text.includes(`<@${currentBotUserId}>`) : false;
189+
await maybeRefreshWorkspaceForMention({
190+
deps,
191+
channelId,
192+
isMention,
193+
workspaceAuth,
194+
});
195+
196+
if (!deps.isAuthorizedChannel(channelId)) {
197+
log.info("[DROP] Unauthorized channel", { channelId, isMention });
178198
return;
179199
}
180200

181-
const isMention = currentBotUserId ? text.includes(`<@${currentBotUserId}>`) : false;
201+
if (await maybeHandleStopCommand(deps, text, channelId, threadId, say)) {
202+
return;
203+
}
182204
const threadActive = deps.isThreadActive(channelId, threadId);
183205

184206
if (shouldDropForThreadContext(isMention, threadActive)) {

0 commit comments

Comments
 (0)