premier fix pour le discord bot qui envoie des reminders sur des versions de matchs qui n'existent plus
CI - Security, Lint & Tests / validate (push) Failing after 1m5s
CI - Security, Lint & Tests / validate (push) Failing after 1m5s
This commit is contained in:
+50
-3
@@ -492,6 +492,19 @@ class TeamTryoutsBot(commands.Bot):
|
||||
await self._send_schedule_notification(**item['data'])
|
||||
elif kind == 'one_on_one_response':
|
||||
await self._send_one_on_one_response_dm(**item['data'])
|
||||
elif kind == 'purge_pending':
|
||||
participant_ids = item.get('data', {}).get('participant_ids', [])
|
||||
if participant_ids:
|
||||
changed = False
|
||||
for msg_id, record in list(self.pending_requests.items()):
|
||||
if (
|
||||
record.get('type') == 'schedule_addition'
|
||||
and record.get('id') in participant_ids
|
||||
):
|
||||
del self.pending_requests[msg_id]
|
||||
changed = True
|
||||
if changed:
|
||||
self._save_pending()
|
||||
else:
|
||||
logger.error('Unknown queue item type %r; the notification was dropped', kind)
|
||||
|
||||
@@ -992,6 +1005,12 @@ class TeamTryoutsBot(commands.Bot):
|
||||
player.id,
|
||||
reference_id,
|
||||
)
|
||||
else:
|
||||
# The row no longer exists (e.g. match was edited, participant
|
||||
# was deleted). Silently drop the stale pending entry so the
|
||||
# reaction is not answerable again. No user-facing reply.
|
||||
self._forget_pending(message_id)
|
||||
return
|
||||
except SQLAlchemyError:
|
||||
db.session.rollback()
|
||||
logger.exception(
|
||||
@@ -1040,6 +1059,11 @@ class TeamTryoutsBot(commands.Bot):
|
||||
db.session.delete(row)
|
||||
else:
|
||||
row.status = 'no_show'
|
||||
else:
|
||||
# The row no longer exists. Silently drop the stale pending
|
||||
# entry so the reaction is not answerable again.
|
||||
self._forget_pending(message_id)
|
||||
return
|
||||
except SQLAlchemyError:
|
||||
db.session.rollback()
|
||||
logger.exception(
|
||||
@@ -1222,10 +1246,11 @@ class TeamTryoutsBot(commands.Bot):
|
||||
|
||||
async def send_match_reminder(self, player, match):
|
||||
"""Send match reminder to player. True if it was delivered."""
|
||||
date_str = match.date.strftime('%A, %B %d, %Y') if match.date else 'TBD'
|
||||
message = (
|
||||
"🔔 **Match Reminder**\n\n"
|
||||
f"Your match **{match.title}** is scheduled for tomorrow:\n"
|
||||
f"**Date:** {match.date.strftime('%A, %B %d, %Y')}\n"
|
||||
f"**Date:** {date_str}\n"
|
||||
f"**Time:** {match.start_time.strftime('%I:%M %p') if match.start_time else 'TBD'} - "
|
||||
f"{match.end_time.strftime('%I:%M %p') if match.end_time else 'TBD'}\n"
|
||||
f"**Location:** {match.location or 'TBD'}\n\n"
|
||||
@@ -1241,10 +1266,11 @@ class TeamTryoutsBot(commands.Bot):
|
||||
|
||||
async def send_tryout_reminder(self, player, tryout):
|
||||
"""Send tryout reminder to player. True if it was delivered."""
|
||||
date_str = tryout.date.strftime('%A, %B %d, %Y') if tryout.date else 'TBD'
|
||||
message = (
|
||||
"🔔 **Tryout Reminder**\n\n"
|
||||
f"Your tryout **{tryout.title}** is scheduled for tomorrow:\n"
|
||||
f"**Date:** {tryout.date.strftime('%A, %B %d, %Y')}\n"
|
||||
f"**Date:** {date_str}\n"
|
||||
f"**Location:** {tryout.location or 'TBD'}\n\n"
|
||||
"Please confirm your attendance in the app."
|
||||
)
|
||||
@@ -1314,10 +1340,11 @@ class TeamTryoutsBot(commands.Bot):
|
||||
|
||||
async def send_one_on_one_reminder(self, player, session):
|
||||
"""Send One on One reminder to player. True if it was delivered."""
|
||||
date_str = session.date.strftime('%A, %B %d, %Y') if session.date else 'TBD'
|
||||
message = (
|
||||
"🔔 **One on One Reminder**\n\n"
|
||||
f"Your One on One session with **{session.coach.full_name}** is scheduled for tomorrow:\n"
|
||||
f"**Date:** {session.date.strftime('%A, %B %d, %Y')}\n"
|
||||
f"**Date:** {date_str}\n"
|
||||
f"**Time:** {session.start_time.strftime('%I:%M %p')} - {session.end_time.strftime('%I:%M %p')}\n"
|
||||
f"**Discussion Points:** {session.points or 'No specific points provided'}\n\n"
|
||||
"Please prepare for your session!"
|
||||
@@ -1433,6 +1460,26 @@ def send_schedule_notification(
|
||||
)
|
||||
|
||||
|
||||
def purge_pending_for_participants(participant_ids):
|
||||
"""Queue a purge of pending Discord entries for stale participant rows.
|
||||
|
||||
Called after a match edit deletes participants. The bot thread removes
|
||||
matching ``schedule_addition`` pending entries so that a reaction on an
|
||||
old message cannot produce a false confirmation.
|
||||
|
||||
Args:
|
||||
participant_ids: list[int] of MatchParticipant primary keys that no
|
||||
longer exist.
|
||||
"""
|
||||
if not participant_ids:
|
||||
return None
|
||||
return _enqueue(
|
||||
'purge_pending',
|
||||
{'participant_ids': participant_ids},
|
||||
description=f'purge of {len(participant_ids)} stale participant records',
|
||||
)
|
||||
|
||||
|
||||
def send_one_on_one_response(
|
||||
player_discord_id: str,
|
||||
player_full_name: str,
|
||||
|
||||
@@ -11,7 +11,7 @@ class BaseMatch(db.Model):
|
||||
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
description = db.Column(db.Text, nullable=True)
|
||||
date = db.Column(db.Date, nullable=False)
|
||||
date = db.Column(db.Date, nullable=False, index=True)
|
||||
start_time = db.Column(db.Time, nullable=True)
|
||||
end_time = db.Column(db.Time, nullable=True)
|
||||
location = db.Column(db.String(200), nullable=True)
|
||||
|
||||
@@ -12,7 +12,7 @@ class OneOnOneRequest(db.Model):
|
||||
player_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
coach_id = db.Column(db.Integer, db.ForeignKey('users.id'), nullable=False)
|
||||
org_team_id = db.Column(db.Integer, db.ForeignKey('org_teams.id'), nullable=True)
|
||||
date = db.Column(db.Date, nullable=False)
|
||||
date = db.Column(db.Date, nullable=False, index=True)
|
||||
start_time = db.Column(db.Time, nullable=False)
|
||||
end_time = db.Column(db.Time, nullable=False)
|
||||
points = db.Column(db.Text, nullable=True)
|
||||
|
||||
@@ -13,7 +13,7 @@ class Tryout(db.Model):
|
||||
title = db.Column(db.String(200), nullable=False)
|
||||
description = db.Column(db.Text, nullable=True)
|
||||
game = db.Column(db.String(50), nullable=False)
|
||||
date = db.Column(db.Date, nullable=False)
|
||||
date = db.Column(db.Date, nullable=False, index=True)
|
||||
end_date = db.Column(db.Date, nullable=True)
|
||||
location = db.Column(db.String(200), nullable=True)
|
||||
status = db.Column(db.String(20), default='upcoming')
|
||||
|
||||
+25
-3
@@ -583,6 +583,12 @@ def edit_match(match_id):
|
||||
notified_player_ids = []
|
||||
notified_participant_ids = []
|
||||
|
||||
# Capture old participant ids before any deletion, so we can purge
|
||||
# their pending Discord notifications once the change is committed.
|
||||
stale_participant_ids = [
|
||||
p.id for p in MatchParticipant.query.filter_by(match_id=match.id).all()
|
||||
]
|
||||
|
||||
if match.match_type == 'team_vs_team':
|
||||
teams_changed = data['team1_id'] != match.team1_id or data['team2_id'] != match.team2_id
|
||||
if teams_changed:
|
||||
@@ -593,17 +599,33 @@ def edit_match(match_id):
|
||||
else:
|
||||
# Same teams: the roster stands, but everyone is told again,
|
||||
# because the date or the time may have moved.
|
||||
# Reuse the existing participant ids so reference_id is always
|
||||
# a real participant row, never the match id (fallback).
|
||||
for team_id in (match.team1_id, match.team2_id):
|
||||
if team_id:
|
||||
notified_player_ids.extend(
|
||||
m.player_id for m in TeamMember.query.filter_by(team_id=team_id).all()
|
||||
)
|
||||
for member in TeamMember.query.filter_by(team_id=team_id).all():
|
||||
notified_player_ids.append(member.player_id)
|
||||
existing = MatchParticipant.query.filter_by(
|
||||
match_id=match.id, player_id=member.player_id
|
||||
).first()
|
||||
if existing:
|
||||
notified_participant_ids.append(existing.id)
|
||||
stale_participant_ids = [
|
||||
pid for pid in stale_participant_ids if pid != existing.id
|
||||
]
|
||||
else:
|
||||
MatchParticipant.query.filter_by(match_id=match.id).delete()
|
||||
notified_player_ids, notified_participant_ids = create_participants(match, data)
|
||||
|
||||
db.session.commit()
|
||||
|
||||
# Purge Discord pending entries for participants that no longer exist.
|
||||
# This runs after the commit so the bot only sees the durable state.
|
||||
if stale_participant_ids:
|
||||
from app.discord_bot import purge_pending_for_participants
|
||||
|
||||
purge_pending_for_participants(stale_participant_ids)
|
||||
|
||||
notify_participants(
|
||||
title=match.title,
|
||||
date=match.date,
|
||||
|
||||
Reference in New Issue
Block a user