diff --git a/__pycache__/seed.cpython-313.pyc b/__pycache__/seed.cpython-313.pyc index 32c0875..ffb56f6 100644 Binary files a/__pycache__/seed.cpython-313.pyc and b/__pycache__/seed.cpython-313.pyc differ diff --git a/discord_bot.py b/discord_bot.py index 765463f..ae84f21 100644 --- a/discord_bot.py +++ b/discord_bot.py @@ -341,7 +341,14 @@ class TeamTryoutsBot(commands.Bot): player = request.player coach = request.coach - if not player or not player.discord_user_id: + if not player: + logger.warning(f"Player not found for request {request.id}") + return + if not coach: + logger.warning(f"Coach not found for request {request.id}") + return + + if not player.discord_user_id: logger.warning(f"Player has no Discord user ID for request {request.id}") return @@ -353,7 +360,7 @@ class TeamTryoutsBot(commands.Bot): if approved: message = ( "🎉 **One on One Session Confirmed!**\n\n" - f"Your coach **{request.coach.full_name}** has approved your request:\n" + f"Your coach **{coach.full_name}** has approved your request:\n" f"**Date:** {request.date.strftime('%A, %B %d, %Y')}\n" f"**Time:** {request.start_time.strftime('%I:%M %p')} - {request.end_time.strftime('%I:%M %p')}\n" f"**Discussion Points:** {request.points or 'No specific points provided'}\n\n" @@ -363,21 +370,22 @@ class TeamTryoutsBot(commands.Bot): if refusal_note: message = ( "😞 **One on One Session Rejected**\n\n" - f"Your coach **{request.coach.full_name}** has declined:\n" + f"Your coach **{coach.full_name}** has declined:\n" f"**Reason:** {refusal_note}\n\n" "Please try selecting a different time slot." ) else: message = ( "😞 **One on One Session Unavailable**\n\n" - f"Your coach **{request.coach.full_name}** is not available.\n\n" + f"Your coach **{coach.full_name}** is not available.\n\n" "Please try selecting a different time slot." ) await player_user.send(message) + logger.info(f"Sent One on One notification to player {player.full_name} (request {request.id})") except Exception as e: - logger.error(f"Error notifying player: {e}") + logger.error(f"Error notifying player about One on One: {e}") async def send_daily_reminders(self): """Send daily reminders at 18:00 EDT for events in 24-48 hours.""" diff --git a/instance/team_tryouts.db b/instance/team_tryouts.db index ca98f70..283fc39 100644 Binary files a/instance/team_tryouts.db and b/instance/team_tryouts.db differ diff --git a/routes/__pycache__/matches.cpython-313.pyc b/routes/__pycache__/matches.cpython-313.pyc index f22592f..6ee2ed9 100644 Binary files a/routes/__pycache__/matches.cpython-313.pyc and b/routes/__pycache__/matches.cpython-313.pyc differ diff --git a/routes/matches.py b/routes/matches.py index 3817c0a..3b8255a 100644 --- a/routes/matches.py +++ b/routes/matches.py @@ -8,6 +8,7 @@ from flask_login import login_required, current_user from extensions import db from models import User, Tryout, Match, MatchParticipant, Team, TeamMember, OrgTeam, TryoutRegistration, PlayerDisponibility from datetime import datetime, time, timedelta +from discord_bot import send_schedule_notification matches_bp = Blueprint('matches', __name__, url_prefix='/matches') @@ -346,12 +347,20 @@ def create_match(tryout_id): db.session.add(match) db.session.flush() # Get match.id before commit + # Collect player IDs for Discord notifications + notified_player_ids = [] + # Handle team vs team matches if match_type == 'team_vs_team': team1_id = request.form.get('team1_id') team2_id = request.form.get('team2_id') match.team1_id = int(team1_id) if team1_id else None match.team2_id = int(team2_id) if team2_id else None + # Get players from both teams + if match.team1_id: + notified_player_ids.extend([m.player_id for m in TeamMember.query.filter_by(team_id=match.team1_id).all()]) + if match.team2_id: + notified_player_ids.extend([m.player_id for m in TeamMember.query.filter_by(team_id=match.team2_id).all()]) # Handle player vs player matches elif match_type == 'player_vs_player': @@ -359,21 +368,60 @@ def create_match(tryout_id): team2_player_ids = request.form.get('team2_player_ids', '') team1_ids = [int(p) for p in team1_player_ids.split(',') if p] if team1_player_ids else [] team2_ids = [int(p) for p in team2_player_ids.split(',') if p] if team2_player_ids else [] + notified_participant_ids = [] for pid in team1_ids: participant = MatchParticipant(match_id=match.id, player_id=pid, team_side=1) db.session.add(participant) + db.session.flush() + notified_participant_ids.append(participant.id) for pid in team2_ids: participant = MatchParticipant(match_id=match.id, player_id=pid, team_side=2) db.session.add(participant) + db.session.flush() + notified_participant_ids.append(participant.id) + notified_player_ids = team1_ids + team2_ids # Handle player scrim matches elif match_type == 'player_scrim': player_ids = request.form.getlist('player_ids') + notified_participant_ids = [] for pid in player_ids: participant = MatchParticipant(match_id=match.id, player_id=int(pid)) db.session.add(participant) + db.session.flush() + notified_participant_ids.append(participant.id) + notified_player_ids = [int(p) for p in player_ids] db.session.commit() + + # Send Discord notifications to players (one per participant for proper attendance tracking) + event_date_str = date_obj.strftime('%Y-%m-%d') + event_time_str = f"{start_time.strftime('%I:%M %p')} - {end_time.strftime('%I:%M %p')}" if start_time and end_time else 'TBD' + + # For team_vs_team, notify all players but use match.id (no individual confirmation) + # For player_vs_player/player_scrim, notify each player individually with participant ID + if match_type == 'team_vs_team': + for player_id in notified_player_ids: + send_schedule_notification( + user_id=player_id, + event_type='match', + event_title=match.title, + event_date=event_date_str, + event_time=event_time_str, + reference_id=match.id + ) + else: + for i, player_id in enumerate(notified_player_ids): + reference_id = notified_participant_ids[i] if i < len(notified_participant_ids) else match.id + send_schedule_notification( + user_id=player_id, + event_type='match', + event_title=match.title, + event_date=event_date_str, + event_time=event_time_str, + reference_id=reference_id + ) + flash('Match scheduled successfully!', 'success') return redirect(url_for('tryouts.view_tryout', tryout_id=tryout_id)) @@ -439,40 +487,91 @@ def edit_match(match_id): end_dt = start_dt + timedelta(minutes=30) match.end_time = end_dt.time() except ValueError: - pass + match.start_time = None match.location = location if status in ['scheduled', 'completed', 'cancelled']: match.status = status + # Collect player IDs for Discord notifications + notified_player_ids = [] + # Handle team vs team matches if match.match_type == 'team_vs_team': team1_id = request.form.get('team1_id') team2_id = request.form.get('team2_id') match.team1_id = int(team1_id) if team1_id else None match.team2_id = int(team2_id) if team2_id else None + # Get players from both teams + if match.team1_id: + notified_player_ids.extend([m.player_id for m in TeamMember.query.filter_by(team_id=match.team1_id).all()]) + if match.team2_id: + notified_player_ids.extend([m.player_id for m in TeamMember.query.filter_by(team_id=match.team2_id).all()]) # Handle player vs player matches - update participants elif match.match_type == 'player_vs_player': MatchParticipant.query.filter_by(match_id=match.id).delete() team1_player_ids = request.form.getlist('team1_player_ids') team2_player_ids = request.form.getlist('team2_player_ids') + notified_participant_ids = [] for pid in team1_player_ids: participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=1) db.session.add(participant) + db.session.flush() + notified_participant_ids.append(participant.id) for pid in team2_player_ids: participant = MatchParticipant(match_id=match.id, player_id=int(pid), team_side=2) db.session.add(participant) + db.session.flush() + notified_participant_ids.append(participant.id) + notified_player_ids = [int(p) for p in team1_player_ids] + [int(p) for p in team2_player_ids] # Handle player scrim matches - update participants elif match.match_type == 'player_scrim': MatchParticipant.query.filter_by(match_id=match.id).delete() player_ids = request.form.getlist('player_ids') + notified_participant_ids = [] for pid in player_ids: participant = MatchParticipant(match_id=match.id, player_id=int(pid)) db.session.add(participant) + db.session.flush() + notified_participant_ids.append(participant.id) + notified_player_ids = [int(p) for p in player_ids] db.session.commit() + + # Send Discord notifications to players + if match.match_type in ['player_vs_player', 'player_scrim']: + end_time_val = match.end_time if match.end_time else match.start_time if match.start_time else None + if match.start_time and end_time_val: + event_time_str = f"{match.start_time.strftime('%I:%M %p')} - {end_time_val.strftime('%I:%M %p')}" + else: + event_time_str = 'TBD' + event_date_str = match.date.strftime('%Y-%m-%d') + for i, player_id in enumerate(notified_player_ids): + reference_id = notified_participant_ids[i] if i < len(notified_participant_ids) else match.id + send_schedule_notification( + user_id=player_id, + event_type='match', + event_title=match.title, + event_date=event_date_str, + event_time=event_time_str, + reference_id=reference_id + ) + elif match.match_type == 'team_vs_team': + event_date_str = match.date.strftime('%Y-%m-%d') + end_time_val = match.end_time if match.end_time else match.start_time if match.start_time else None + event_time_str = f"{match.start_time.strftime('%I:%M %p')} - {end_time_val.strftime('%I:%M %p')}" if match.start_time and end_time_val else 'TBD' + for player_id in notified_player_ids: + send_schedule_notification( + user_id=player_id, + event_type='match', + event_title=match.title, + event_date=event_date_str, + event_time=event_time_str, + reference_id=match.id + ) + flash('Match updated successfully!', 'success') return redirect(url_for('tryouts.view_tryout', tryout_id=tryout.id)) diff --git a/seed.py b/seed.py index 8886abf..18bab11 100644 --- a/seed.py +++ b/seed.py @@ -58,31 +58,31 @@ def seed_database(): 'discord': 'nordjan', 'discord_user_id': '484107446298738689', 'league_os': 'https://leagueos.gg/player/nordjan'}, {'username': 'jplayer2', 'full_name': 'Emma Garcia', 'email': 'emma@email.com', 'games': 'League of Legends,Valorant', 'gamertags': {'League of Legends': 'emmagarcia_lol', 'Valorant': 'emmagarcia_val'}, - 'discord': 'EmmaG#4452', 'league_os': 'https://leagueos.gg/player/emmagarcia'}, + 'discord': 'EmmaG#4452', 'discord_user_id': '', 'league_os': 'https://leagueos.gg/player/emmagarcia'}, {'username': 'jplayer3', 'full_name': 'Liam Brown', 'email': 'liam@email.com', 'games': 'Apex Legends,Fortnite', 'gamertags': {'Apex Legends': 'liambrown_apex', 'platform': 'PC', 'Fortnite': 'liambrown_fn', 'platform_fn': 'PC'}, - 'discord': 'LiamB#8103', 'league_os': 'https://leagueos.gg/player/liambrown'}, + 'discord': 'LiamB#8103', 'discord_user_id': '', 'league_os': 'https://leagueos.gg/player/liambrown'}, {'username': 'jplayer4', 'full_name': 'Sophia Lee', 'email': 'sophia@email.com', 'games': 'Overwatch 2,Valorant', 'gamertags': {'Overwatch 2': 'sophialee_ow', 'platform_ow': 'PC', 'Valorant': 'sophialee_val'}, - 'discord': 'SophiaL#3327', 'league_os': 'https://leagueos.gg/player/sophialee'}, + 'discord': 'SophiaL#3327', 'discord_user_id': '', 'league_os': 'https://leagueos.gg/player/sophialee'}, {'username': 'jplayer5', 'full_name': 'Noah Taylor', 'email': 'noah@email.com', 'games': 'Counter-Strike 2,Rainbow Six Siege', 'gamertags': {'Counter-Strike 2': 'noahtaylor_cs', 'Rainbow Six Siege': 'noahtaylor_r6', 'platform_r6': 'PC'}, - 'discord': 'NoahT#6614', 'league_os': 'https://leagueos.gg/player/noahtaylor'}, + 'discord': 'NoahT#6614', 'discord_user_id': '', 'league_os': 'https://leagueos.gg/player/noahtaylor'}, {'username': 'jplayer6', 'full_name': 'Olivia Martin', 'email': 'olivia@email.com', 'games': 'Rocket League,Fortnite', 'gamertags': {'Rocket League': 'oliviamartin_rl', 'platform_rl': 'PC', 'Fortnite': 'oliviamartin_fn', 'platform_fn': 'PC'}, - 'discord': 'OliviaM#2298', 'league_os': 'https://leagueos.gg/player/oliviamartin'}, + 'discord': 'OliviaM#2298', 'discord_user_id': '', 'league_os': 'https://leagueos.gg/player/oliviamartin'}, {'username': 'jplayer7', 'full_name': 'Ethan Clark', 'email': 'ethan@email.com', 'games': 'Valorant,Apex Legends', 'gamertags': {'Valorant': 'ethanclark_val', 'Apex Legends': 'ethanclark_apex', 'platform_apex': 'PC'}, - 'discord': 'EthanC#7743', 'league_os': 'https://leagueos.gg/player/ethanclark'}, + 'discord': 'EthanC#7743', 'discord_user_id': '', 'league_os': 'https://leagueos.gg/player/ethanclark'}, {'username': 'jplayer8', 'full_name': 'Ava White', 'email': 'ava@email.com', 'games': 'League of Legends,Counter-Strike 2', 'gamertags': {'League of Legends': 'avawhite_lol', 'Counter-Strike 2': 'avawhite_cs'}, - 'discord': 'AvaW#5561', 'league_os': 'https://leagueos.gg/player/avawhite'}, + 'discord': 'AvaW#5561', 'discord_user_id': '', 'league_os': 'https://leagueos.gg/player/avawhite'}, {'username': 'jplayer9', 'full_name': 'Mason Hall', 'email': 'mason@email.com', 'games': 'Call of Duty,Rocket League', 'gamertags': {'Call of Duty': 'masonhall_cod', 'platform_cod': 'PC', 'Rocket League': 'masonhall_rl', 'platform_rl': 'PC'}, - 'discord': 'MasonH#1189', 'league_os': 'https://leagueos.gg/player/masonhall'}, + 'discord': 'MasonH#1189', 'discord_user_id': '', 'league_os': 'https://leagueos.gg/player/masonhall'}, {'username': 'jplayer10', 'full_name': 'Isabella Adams', 'email': 'isabella@email.com', 'games': 'Overwatch 2,Dota 2', 'gamertags': {'Overwatch 2': 'isabellaadams_ow', 'platform_ow': 'PC', 'Dota 2': 'isabellaadams_dota'}, - 'discord': 'IsabellaA#4437', 'league_os': 'https://leagueos.gg/player/isabellaadams'}, + 'discord': 'IsabellaA#4437', 'discord_user_id': '', 'league_os': 'https://leagueos.gg/player/isabellaadams'}, ] for i, data in enumerate(player_data, start=10):