Skip to content

Commit 98c77f7

Browse files
committed
Add DM invitation for users added to GitHub organization
1 parent 8a104c6 commit 98c77f7

1 file changed

Lines changed: 50 additions & 2 deletions

File tree

arthur/exts/github/management.py

Lines changed: 50 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -390,13 +390,57 @@ async def _notify_failed_invites(self, skipped_failed: list[str]) -> None:
390390
f"`{username}` because a failed invitation record already exists."
391391
)
392392

393-
async def _apply_org_additions(self, usernames: list[str]) -> list[str]:
393+
async def _try_dm_user_invite(self, github_username: str, keycloak_user: dict | None) -> None:
394+
"""DM a user using their Discord ID from Keycloak."""
395+
if not keycloak_user:
396+
logger.debug(f"No Keycloak user data for GitHub username {github_username}")
397+
return
398+
399+
# Extract Discord ID from Keycloak attributes
400+
discord_ids = keycloak_user.get("attributes", {}).get("discordId")
401+
if not discord_ids or not isinstance(discord_ids, list) or not discord_ids:
402+
logger.debug(f"No Discord ID found in Keycloak for {keycloak_user.get('username', 'unknown')} (GitHub: {github_username})")
403+
return
404+
405+
try:
406+
discord_id = int(discord_ids[0])
407+
member = self.bot.get_user(discord_id) or await self.bot.fetch_user(discord_id)
408+
409+
await member.send(
410+
f"You've been invited to join the {CONFIG.github_org} GitHub organization!\n\n"
411+
f"Accept your invitation here: https://github.com/orgs/{CONFIG.github_org}/invitation"
412+
)
413+
except ValueError:
414+
logger.debug(f"Invalid Discord ID in Keycloak: {discord_ids[0]}")
415+
except discord.Forbidden:
416+
logger.debug(f"Could not DM user {discord_id} - DMs disabled")
417+
except discord.HTTPException as e:
418+
logger.opt(exception=e).warning(f"Failed to DM user {discord_id}")
419+
except Exception as e: # noqa: BLE001
420+
logger.opt(exception=e).warning(f"Error sending DM invite to {github_username}")
421+
422+
async def _apply_org_additions(
423+
self,
424+
usernames: list[str],
425+
keycloak_identities: dict[str, dict[str, str]],
426+
resolved_logins_by_user_id: dict[str, str],
427+
) -> list[str]:
394428
"""Apply organisation additions and return successfully added logins."""
429+
# Build reverse map: GitHub login -> Keycloak user data
430+
github_login_to_keycloak = {}
431+
for identity in keycloak_identities.values():
432+
user_id = identity.get("user_id", "").strip()
433+
if user_id in resolved_logins_by_user_id:
434+
github_login = resolved_logins_by_user_id[user_id]
435+
github_login_to_keycloak[github_login] = identity
436+
395437
added = []
396438
for username in usernames:
397439
try:
398440
await add_org_member(username)
399441
added.append(username)
442+
keycloak_user = github_login_to_keycloak.get(username)
443+
await self._try_dm_user_invite(username, keycloak_user)
400444
except GitHubError as e:
401445
logger.opt(exception=e).error(f"GitHub: Failed to add {username} to org")
402446

@@ -526,7 +570,11 @@ async def _sync_github_members(
526570
await self._report_org_sync_plan(report_thread, plan)
527571
await self._notify_failed_invites(plan.skipped_failed)
528572

529-
added = await self._apply_org_additions(plan.diff.to_add)
573+
added = await self._apply_org_additions(
574+
plan.diff.to_add,
575+
common_info.keycloak_identities,
576+
common_info.resolved_logins_by_user_id,
577+
)
530578
removed = await self._apply_org_removals(plan.diff.to_remove)
531579

532580
return added, removed

0 commit comments

Comments
 (0)