-
Notifications
You must be signed in to change notification settings - Fork 6
feat: count videos/tags within thread replies #409
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: test
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -25,18 +25,39 @@ interface ConversationsHistoryResponse { | |
| text?: string; | ||
| ts?: string; | ||
| bot_id?: string; | ||
| reply_count?: number; | ||
| thread_ts?: string; | ||
| }>; | ||
| response_metadata?: { next_cursor?: string }; | ||
| } | ||
|
|
||
| interface ConversationsRepliesResponse { | ||
| ok: boolean; | ||
| error?: string; | ||
| messages?: Array<{ | ||
| type: string; | ||
| user?: string; | ||
| text?: string; | ||
| ts?: string; | ||
| bot_id?: string; | ||
| }>; | ||
| } | ||
|
|
||
| interface RawMention { | ||
| userId: string; | ||
| prompt: string; | ||
| ts: string; | ||
| threadTs: string; | ||
| channelId: string; | ||
| channelName: string; | ||
| } | ||
|
|
||
| interface ThreadParent { | ||
| channelId: string; | ||
| channelName: string; | ||
| ts: string; | ||
| } | ||
|
|
||
| interface FetchBotMentionsOptions { | ||
| tokenEnvVar: string; | ||
| period: AdminPeriod; | ||
|
|
@@ -47,8 +68,8 @@ interface FetchBotMentionsOptions { | |
| } | ||
|
|
||
| /** | ||
| * Fetches all Slack messages where a bot was mentioned. | ||
| * Generic over the bot token and thread response extractor. | ||
| * Fetches all Slack messages where a bot was mentioned, including mentions | ||
| * within thread replies. Generic over the bot token and thread response extractor. | ||
| * | ||
| * @param options - Configuration for which bot and how to extract thread responses | ||
| * @returns Array of BotTag objects representing each mention event, sorted newest first | ||
|
|
@@ -66,6 +87,8 @@ export async function fetchBotMentions(options: FetchBotMentionsOptions): Promis | |
| const cutoffTs = getCutoffTs(period); | ||
| const mentions: RawMention[] = []; | ||
| const userCache: Record<string, { name: string; avatar: string | null }> = {}; | ||
| const threadParents: ThreadParent[] = []; | ||
| const topLevelMentionThreads = new Set<string>(); | ||
|
|
||
| for (const channel of channels) { | ||
| let cursor: string | undefined; | ||
|
|
@@ -84,9 +107,19 @@ export async function fetchBotMentions(options: FetchBotMentionsOptions): Promis | |
|
|
||
| for (const msg of history.messages ?? []) { | ||
| if (msg.bot_id) continue; | ||
| if (!msg.ts) continue; | ||
|
|
||
| // Track threaded messages for scanning thread replies | ||
| if (msg.reply_count && msg.reply_count > 0) { | ||
| threadParents.push({ | ||
| channelId: channel.id, | ||
| channelName: channel.name, | ||
| ts: msg.ts, | ||
| }); | ||
| } | ||
|
|
||
| if (!msg.user) continue; | ||
| if (!msg.text?.includes(mentionPattern)) continue; | ||
| if (!msg.ts) continue; | ||
|
|
||
| if (!userCache[msg.user]) { | ||
| userCache[msg.user] = await getSlackUserInfo(token, msg.user); | ||
|
|
@@ -96,18 +129,72 @@ export async function fetchBotMentions(options: FetchBotMentionsOptions): Promis | |
| userId: msg.user, | ||
| prompt: (msg.text ?? "").replace(new RegExp(`<@${botUserId}>\\s*`, "g"), "").trim(), | ||
| ts: msg.ts, | ||
| threadTs: msg.ts, | ||
| channelId: channel.id, | ||
| channelName: channel.name, | ||
| }); | ||
|
|
||
| topLevelMentionThreads.add(`${channel.id}:${msg.ts}`); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P1: Custom agent: Flag AI Slop and Fabricated Changes
Prompt for AI agents |
||
| } | ||
|
|
||
| cursor = history.response_metadata?.next_cursor || undefined; | ||
| } while (cursor); | ||
| } | ||
|
|
||
| // Scan thread replies for additional bot mentions | ||
| const THREAD_BATCH_SIZE = 5; | ||
| const THREAD_BATCH_DELAY_MS = 1100; | ||
|
|
||
| for (let i = 0; i < threadParents.length; i += THREAD_BATCH_SIZE) { | ||
| if (i > 0) { | ||
| await new Promise(resolve => setTimeout(resolve, THREAD_BATCH_DELAY_MS)); | ||
| } | ||
| const batch = threadParents.slice(i, i + THREAD_BATCH_SIZE); | ||
| const batchResults = await Promise.allSettled( | ||
| batch.map(async tp => { | ||
| const replies = await slackGet<ConversationsRepliesResponse>( | ||
| "conversations.replies", | ||
| token, | ||
| { channel: tp.channelId, ts: tp.ts }, | ||
| ); | ||
| if (!replies.ok) return []; | ||
|
|
||
| const threadMentions: RawMention[] = []; | ||
| for (const msg of replies.messages ?? []) { | ||
| if (msg.bot_id) continue; | ||
| if (!msg.user) continue; | ||
| if (!msg.text?.includes(mentionPattern)) continue; | ||
| if (!msg.ts) continue; | ||
| // Skip the parent message (already captured as top-level) | ||
| if (msg.ts === tp.ts) continue; | ||
|
|
||
| if (!userCache[msg.user]) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. P2: Race condition on Prompt for AI agents |
||
| userCache[msg.user] = await getSlackUserInfo(token, msg.user); | ||
| } | ||
|
|
||
| threadMentions.push({ | ||
| userId: msg.user, | ||
| prompt: (msg.text ?? "").replace(new RegExp(`<@${botUserId}>\\s*`, "g"), "").trim(), | ||
| ts: msg.ts, | ||
| threadTs: tp.ts, | ||
| channelId: tp.channelId, | ||
| channelName: tp.channelName, | ||
| }); | ||
| } | ||
| return threadMentions; | ||
| }), | ||
| ); | ||
|
|
||
| for (const result of batchResults) { | ||
| if (result.status === "fulfilled") { | ||
| mentions.push(...result.value); | ||
| } | ||
| } | ||
| } | ||
|
|
||
| const responses = await fetchThreadResponses( | ||
| token, | ||
| mentions.map(m => ({ channelId: m.channelId, ts: m.ts })), | ||
| mentions.map(m => ({ channelId: m.channelId, ts: m.threadTs })), | ||
| ); | ||
|
|
||
| const tags: BotTag[] = mentions.map((m, i) => { | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
P3:
vi.resetAllMocks()deviates from the project convention (vi.clearAllMocks()) documented in CLAUDE.md and used across all other test files. If stronger isolation is intentionally desired here, consider updating the project convention; otherwise, revert tovi.clearAllMocks()for consistency.Prompt for AI agents