// server/utils/checkSlackJoins.js import Member from '../models/member.js' import { connectDB } from './mongoose.js' import { WebClient } from '@slack/web-api' const BATCH_SIZE = 15 const BATCH_DELAY_MS = 1000 /** * Check members with pending Slack invites to see if they've joined the workspace. * Processes in batches of 15 with 1-second delays (Slack Tier 2 rate limit). */ export async function checkSlackJoins(slackBotToken) { await connectDB() const client = new WebClient(slackBotToken) const members = await Member.find({ slackInviteStatus: { $in: ['sent', 'accepted'] } }).select('_id email slackInviteStatus') if (members.length === 0) return { checked: 0, joined: 0 } let joined = 0 for (let i = 0; i < members.length; i += BATCH_SIZE) { const batch = members.slice(i, i + BATCH_SIZE) for (const member of batch) { try { const response = await client.users.lookupByEmail({ email: member.email }) const userId = response.user?.id if (userId) { await Member.findByIdAndUpdate(member._id, { slackInviteStatus: 'joined', slackUserId: userId }) joined++ console.log(`[check-slack-joins] ${member.email} joined Slack (${userId})`) } } catch (err) { // users_not_found is expected for members who haven't joined yet if (err.data?.error === 'users_not_found') continue console.error(`[check-slack-joins] Error checking ${member.email}:`, err.message || err) // Continue processing remaining members } } // Delay between batches (skip after last batch) if (i + BATCH_SIZE < members.length) { await new Promise((resolve) => setTimeout(resolve, BATCH_DELAY_MS)) } } console.log(`[check-slack-joins] Done: ${joined}/${members.length} members joined`) return { checked: members.length, joined } }