MCPcopy Create free account
hub / github.com/adcontextprotocol/adcp / autoLinkUnmappedSlackUsers

Function autoLinkUnmappedSlackUsers

server/src/slack/sync.ts:712–786  ·  view source on GitHub ↗
()

Source from the content-addressed store, hash-verified

710 * Used by both the admin endpoint and the daily background job.
711 */
712export async function autoLinkUnmappedSlackUsers(): Promise<{
713 linked: number;
714 chapters_joined: number;
715 organizations_assigned: number;
716 pending_org_prospects_set: number;
717 errors: number;
718}> {
719 // Ensure all current workspace members have rows before attempting to link.
720 // Users who joined before the team_join listener was active have no row otherwise.
721 const syncResult = await syncSlackUsers();
722 if (syncResult.errors.length > 0) {
723 logger.warn({ errors: syncResult.errors }, 'Slack user sync had errors; auto-link results may be incomplete');
724 }
725
726 // excludeOptedOut: false — opt-out applies to nudge notifications, not account linking.
727 // Linking is a system operation; it doesn't send any messages.
728 const unmappedSlack = await slackDb.getUnmappedUsers({
729 excludeOptedOut: false,
730 excludeRecentlyNudged: false,
731 });
732
733 const aaoEmailToUserId = await buildAaoEmailToUserIdMap();
734 const mappedWorkosUserIds = await slackDb.getMappedWorkosUserIds();
735
736 // Set pending_organization_id for unmapped Slack users whose email domain matches an org.
737 // This is a DB-only operation and is safe regardless of whether Slack is configured.
738 // It is idempotent and covers users who joined Slack before the listener was active.
739 const backfillResult = await slackDb.backfillPendingOrganizations();
740
741 let linked = 0;
742 let chaptersJoined = 0;
743 let orgsAssigned = 0;
744 let errors = 0;
745
746 for (const slackUser of unmappedSlack) {
747 if (!slackUser.slack_email) continue;
748
749 const workosUserId = aaoEmailToUserId.get(slackUser.slack_email.toLowerCase());
750 if (!workosUserId || mappedWorkosUserIds.has(workosUserId)) continue;
751
752 try {
753 await slackDb.mapUser({
754 slack_user_id: slackUser.slack_user_id,
755 workos_user_id: workosUserId,
756 mapping_source: 'email_auto',
757 });
758 linked++;
759 mappedWorkosUserIds.add(workosUserId);
760 // Clear cached admin status so Addie recognizes newly linked admins immediately.
761 invalidateAdminStatusCache(slackUser.slack_user_id);
762
763 const chapterResult = await syncUserToChaptersFromSlackChannels(workosUserId, slackUser.slack_user_id);
764 chaptersJoined += chapterResult.chapters_joined;
765
766 const orgResult = await checkAndAssignOrganizationByDomain(workosUserId);
767 if (orgResult?.assigned) orgsAssigned++;
768 } catch (err) {
769 logger.error({ err, slackUserId: slackUser.slack_user_id, email: slackUser.slack_email }, 'Failed to auto-link user');

Callers 1

createAdminSlackRouterFunction · 0.85

Calls 15

syncSlackUsersFunction · 0.85
buildAaoEmailToUserIdMapFunction · 0.85
warnMethod · 0.80
getUnmappedUsersMethod · 0.80
hasMethod · 0.80

Tested by

no test coverage detected