88from discord .ext .commands import Cog , group
99
1010from arthur .apis .directory import ldap
11- from arthur .apis .directory .keycloak import all_github_identities
11+ from arthur .apis .directory .keycloak import all_github_identities , get_discord_id , get_user_id
1212from arthur .apis .github import (
1313 GitHubError ,
1414 add_member_to_team ,
@@ -389,30 +389,67 @@ async def _notify_failed_invites(self, skipped_failed: list[str]) -> None:
389389 f"`{ username } ` because a failed invitation record already exists."
390390 )
391391
392- async def _try_dm_user_invite (self , github_username : str , keycloak_user : dict | None ) -> None :
393- """DM a user using their Discord ID from Keycloak."""
394- if not keycloak_user :
395- logger .debug (f"No Keycloak user data for GitHub username { github_username } " )
392+ def _get_keycloak_username_for_github_username (
393+ self ,
394+ github_username : str ,
395+ keycloak_identities : dict [str , dict [str , str ]],
396+ resolved_logins_by_user_id : dict [str , str ],
397+ ) -> str | None :
398+ """Find the Keycloak username mapped to a GitHub username."""
399+ normalised_target = self ._normalise_login (github_username )
400+
401+ for keycloak_username , identity in keycloak_identities .items ():
402+ user_id = identity .get ("user_id" , "" ).strip ()
403+ if not user_id :
404+ continue
405+
406+ resolved_login = resolved_logins_by_user_id .get (user_id )
407+ if resolved_login and self ._normalise_login (resolved_login ) == normalised_target :
408+ return keycloak_username
409+
410+ return None
411+
412+ async def _try_dm_user_invite (
413+ self ,
414+ github_username : str ,
415+ keycloak_username : str | None ,
416+ ) -> None :
417+ """DM a user using their Discord ID from Keycloak user attributes."""
418+ if not keycloak_username :
419+ logger .debug (f"No Keycloak username for GitHub username { github_username } " )
396420 return
397421
398- # Extract Discord ID from Keycloak attributes
399- discord_ids = keycloak_user .get ("attributes" , {}).get ("discordId" )
400- if not discord_ids or not isinstance (discord_ids , list ) or not discord_ids :
422+ keycloak_user_id = await get_user_id (keycloak_username )
423+ if not keycloak_user_id :
424+ logger .debug (f"No Keycloak user ID found for { keycloak_username } " )
425+ return
426+
427+ discord_id = await get_discord_id (keycloak_user_id )
428+ if discord_id is None :
401429 logger .debug (
402- f"No Discord ID found in Keycloak for { keycloak_user . get ( 'username' , 'unknown' ) } (GitHub: { github_username } )"
430+ f"No valid Discord ID found in Keycloak for { keycloak_username } (GitHub: { github_username } )"
403431 )
404432 return
405433
406- try :
407- discord_id = int ( discord_ids [ 0 ])
408- member = self . bot . get_user ( discord_id ) or await self .bot .fetch_user ( discord_id )
434+ guild = self . bot . get_guild ( CONFIG . guild_id )
435+ if guild is None :
436+ guild = await self .bot .fetch_guild ( CONFIG . guild_id )
409437
438+ member = guild .get_member (discord_id )
439+ if member is None :
440+ try :
441+ member = await guild .fetch_member (discord_id )
442+ except discord .NotFound :
443+ logger .debug (
444+ f"User { discord_id } is not a member of guild { CONFIG .guild_id } ; skipping GitHub invite DM"
445+ )
446+ return
447+
448+ try :
410449 await member .send (
411- f "You've been invited to join the { CONFIG . github_org } GitHub organization!\n \n "
412- f "Accept your invitation here: https://github.com/orgs/{ CONFIG . github_org } /invitation"
450+ "You've been invited to join the python-discord GitHub organization!\n \n "
451+ "Accept your invitation here: https://github.com/orgs/python-discord /invitation"
413452 )
414- except ValueError :
415- logger .debug (f"Invalid Discord ID in Keycloak: { discord_ids [0 ]} " )
416453 except discord .Forbidden :
417454 logger .debug (f"Could not DM user { discord_id } - DMs disabled" )
418455 except discord .HTTPException as e :
@@ -427,21 +464,17 @@ async def _apply_org_additions(
427464 resolved_logins_by_user_id : dict [str , str ],
428465 ) -> list [str ]:
429466 """Apply organisation additions and return successfully added logins."""
430- # Build reverse map: GitHub login -> Keycloak user data
431- github_login_to_keycloak = {}
432- for identity in keycloak_identities .values ():
433- user_id = identity .get ("user_id" , "" ).strip ()
434- if user_id in resolved_logins_by_user_id :
435- github_login = resolved_logins_by_user_id [user_id ]
436- github_login_to_keycloak [github_login ] = identity
437-
438467 added = []
439468 for username in usernames :
440469 try :
441470 await add_org_member (username )
442471 added .append (username )
443- keycloak_user = github_login_to_keycloak .get (username )
444- await self ._try_dm_user_invite (username , keycloak_user )
472+ keycloak_username = self ._get_keycloak_username_for_github_username (
473+ username ,
474+ keycloak_identities ,
475+ resolved_logins_by_user_id ,
476+ )
477+ await self ._try_dm_user_invite (username , keycloak_username )
445478 except GitHubError as e :
446479 logger .opt (exception = e ).error (f"GitHub: Failed to add { username } to org" )
447480
0 commit comments