feat(members): assign team roles from the tournament registration sheet

Match members by Discord username against the lineup nicknames in the
separate registration spreadsheet (TEAM_SHEET_ID) and give each their
team's role. One team per person: switching teams removes the old team
role, and a team with no Discord role yet is auto-created. Only role
names present in the sheet are ever touched, so organisation/field/base
roles are never at risk; the feature is a no-op when TEAM_SHEET_ID is
unset.

The sheet is not a single table (merged rows, stacked CS2/LoL sections
with different layouts), so it is parsed via raw-row scanning rather than
get_all_records. Citizenship markers like "(EST)" are used only as
player delimiters and discarded.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rene Arumetsa
2026-08-28 14:01:22 +03:00
parent 567a82b9f2
commit 52002c37fc
7 changed files with 438 additions and 1 deletions

View File

@@ -182,6 +182,10 @@ def register_dev_member_commands(
except Exception as e:
await interaction.followup.send(S.ERR["sheet_error"].format(error=e), ephemeral=True)
return
try:
await sheets.refresh_teams()
except Exception as e:
log.warning("/check: team sheet refresh failed, using stale team cache: %s", e)
ids_filled = 0
for row in data:
@@ -233,6 +237,8 @@ def register_dev_member_commands(
parts.append(S.CHECK_UI["detail_nickname"])
if result.roles_added:
parts.append(S.CHECK_UI["detail_roles_added"].format(roles=", ".join(result.roles_added)))
if result.roles_removed:
parts.append(S.CHECK_UI["detail_roles_removed"].format(roles=", ".join(result.roles_removed)))
details.append(S.CHECK_UI["detail_changed"].format(name=member.display_name, parts=", ".join(parts)))
else:
already_ok += 1