// Spec: docs/specs/wave-based-slack-onboarding.md // // Auto-detect existing Slack workspace membership for a Ghost Guild member // and flag them as `slackInvited` without sending a duplicate invite. // // Contract: // - Caller awaits this helper, but it never throws — all errors swallowed. // - Internal lookup races against a 3s timeout (resolves null on hang). // - No-op if Slack service isn't configured, lookup misses, or member is // already flagged. import Member from '../models/member.js' import { getSlackService } from './slack.ts' const LOOKUP_TIMEOUT_MS = 3000 export async function autoFlagPreExistingSlackAccess(member) { try { if (!member || !member._id || !member.email) return if (member.slackInvited === true) return const slackService = getSlackService() if (!slackService) return const timeoutPromise = new Promise((resolve) => { setTimeout(() => resolve(null), LOOKUP_TIMEOUT_MS) }) let userId = null try { userId = await Promise.race([ slackService.findUserByEmail(member.email), timeoutPromise ]) } catch (err) { console.error('[slackAccess] findUserByEmail failed:', err) return } if (!userId) return await Member.findByIdAndUpdate( member._id, { slackInvited: true, slackInvitedAt: new Date(), slackUserId: userId }, { runValidators: false } ) await logActivity( member._id, 'slack_access_auto_detected', { slackUserId: userId }, { visibility: 'member' } ) } catch (err) { console.error('[slackAccess] autoFlagPreExistingSlackAccess error:', err) } }