forked from sass/tipibot
feat(teams): add Fienta as primary Discord->team source (sheet fallback)
The registration-log sheet only has in-game nicknames, so matching Discord
users to teams failed for ~10 of 40 teams. Fienta collects each competitor's
Discord username (+ sometimes user ID) and team name per ticket, giving a
reliable Discord-identity -> team mapping (validated: 201 usernames, 42 teams).
- core/fienta.py: token-auth client; fetch /events/{id}/tickets?attendees=true,
parse competitor/coach/substitute tickets into {username|id -> team} and
{team -> game}; exclude visitor/supporter/LAN/early-bird/waiting-list. No-op
when FIENTA_API_TOKEN/FIENTA_EVENT_ID unset.
- member_sync: resolve_team() tries Fienta (id, then username) then the sheet;
all_managed_team_names() and team_dividers() merge both sources.
- /teamsync + hourly task refresh Fienta alongside the sheet; enabled when
either source is configured.
- config + .env.example: FIENTA_API_TOKEN, FIENTA_EVENT_ID.
- tests: fienta parsing (game detection, inclusion rules, id/username mapping).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01XPRsW4tazVtYi2jNzVQkre
This commit is contained in:
25
bot.py
25
bot.py
@@ -21,7 +21,7 @@ import psutil
|
||||
|
||||
import config
|
||||
import strings as S
|
||||
from core import economy, pb_client, sheets
|
||||
from core import economy, fienta, pb_client, sheets
|
||||
from core.admin import is_bot_admin
|
||||
from core.member_sync import SyncResult, sync_all_team_roles
|
||||
from commands.dev_member_commands import register_dev_member_commands
|
||||
@@ -339,31 +339,35 @@ async def before_birthday_daily():
|
||||
|
||||
@tasks.loop(hours=1)
|
||||
async def team_sync_hourly():
|
||||
"""Reload the tournament registration sheet and re-apply team roles.
|
||||
"""Reload the tournament registration (Fienta + sheet) and re-apply team roles.
|
||||
|
||||
Economy profile only (the tournament players live in the community guild).
|
||||
Runs the first iteration immediately on start, so this also covers the
|
||||
initial load at boot. No-op when TEAM_SHEET_ID is unset.
|
||||
initial load at boot. No-op when neither Fienta nor the team sheet is set.
|
||||
"""
|
||||
if IS_DEV_PROFILE or not config.TEAM_SHEET_ID:
|
||||
if IS_DEV_PROFILE or not (config.TEAM_SHEET_ID or config.FIENTA_API_TOKEN):
|
||||
return
|
||||
try:
|
||||
rosters = await sheets.refresh_teams()
|
||||
fienta_teams = await fienta.refresh_teams()
|
||||
except Exception as e:
|
||||
log.error("team_sync_hourly: failed to load team sheet: %s", e)
|
||||
log.error("team_sync_hourly: failed to load team data: %s", e)
|
||||
return
|
||||
if not rosters:
|
||||
if not rosters and not fienta_teams:
|
||||
return
|
||||
guild = bot.get_guild(config.GUILD_ID)
|
||||
if guild is None:
|
||||
log.warning("team_sync_hourly: guild %s not found", config.GUILD_ID)
|
||||
return
|
||||
summary = await sync_all_team_roles(guild, log)
|
||||
if summary.assigned or summary.removed or summary.created or summary.positioned or summary.errors:
|
||||
if (summary.assigned or summary.removed or summary.created or summary.positioned
|
||||
or summary.divider_assigned or summary.divider_removed or summary.errors):
|
||||
log.info(
|
||||
"team_sync_hourly: assigned=%d, removed=%d, created=%d, positioned=%d, errors=%d",
|
||||
"team_sync_hourly: assigned=%d, removed=%d, created=%d, positioned=%d, "
|
||||
"divider_assigned=%d, divider_removed=%d, errors=%d",
|
||||
summary.assigned, summary.removed, len(summary.created),
|
||||
summary.positioned, len(summary.errors),
|
||||
summary.positioned, summary.divider_assigned, summary.divider_removed,
|
||||
len(summary.errors),
|
||||
)
|
||||
for err in summary.errors:
|
||||
log.warning("team_sync_hourly: %s", err)
|
||||
@@ -472,7 +476,8 @@ async def on_ready():
|
||||
log.info("Birthday daily task started (fires 09:00 Tallinn time)")
|
||||
|
||||
# Start hourly tournament team-role sync (economy/community guild)
|
||||
if not IS_DEV_PROFILE and config.TEAM_SHEET_ID and not team_sync_hourly.is_running():
|
||||
if (not IS_DEV_PROFILE and (config.TEAM_SHEET_ID or config.FIENTA_API_TOKEN)
|
||||
and not team_sync_hourly.is_running()):
|
||||
team_sync_hourly.start()
|
||||
log.info("Team-role sync task started (hourly, from the registration sheet)")
|
||||
|
||||
|
||||
Reference in New Issue
Block a user