diff --git a/discordbot.py b/discordbot.py index eb59b6a..f963110 100644 --- a/discordbot.py +++ b/discordbot.py @@ -52,6 +52,24 @@ async def on_member_join(member): logger.error(f"Failed to add Member role to {member.name}: {str(e)}") else: logger.error(f"'Member' role not found in the server") + + # Send a welcome DM to the new member + try: + welcome_message = ( + f"Welcome to the Resonance Rumble Discord server, {member.name}!\n\n" + "We're excited to have you join our community. Here are a few things to get you started:\n" + "• Come play the game here https://resonancerumble.com\n" + "• Introduce yourself in the #introductions channel.\n" + "• If you need any help, feel free to ask in the #help channel.\n\n" + "Don't forget to link your Discord account in the game to unlock an exclusive class!\n" + "Have fun and happy rumbling!" + ) + await member.send(welcome_message) + logger.info(f"Sent welcome DM to {member.name}") + except discord.Forbidden: + logger.error(f"Failed to send welcome DM to {member.name}: User has DMs disabled") + except discord.HTTPException as e: + logger.error(f"Failed to send welcome DM to {member.name}: {str(e)}") async def class_autocomplete(interaction: discord.Interaction, current: str) -> list[app_commands.Choice[str]]: classes = classes_collection.find({}, {"name": 1}) diff --git a/game_server.py b/game_server.py index 6e3d308..f5f79f6 100644 --- a/game_server.py +++ b/game_server.py @@ -30,6 +30,8 @@ import requests from urllib.parse import urlencode import secrets from werkzeug.http import generate_etag +import discord +import asyncio from config import DISCORD_CLIENT_ID, DISCORD_CLIENT_SECRET, DISCORD_REDIRECT_URI, DISCORD_BOT_TOKEN, DISCORD_GUILD_ID, ACCOUNT_LINKED_ROLE_ID, DISCORD_WEBHOOK_URL secret_key = secrets.token_hex(16) @@ -85,6 +87,25 @@ main_room = 'main_game_room' MAP_WIDTH = 3000 MAP_HEIGHT = 2000 +async def send_discord_dm(user_id, message): + try: + intents = discord.Intents.default() + intents.members = True + client = discord.Client(intents=intents) + await client.login(DISCORD_BOT_TOKEN) + + user = await client.fetch_user(user_id) + if user: + await user.send(message) + print(f"Sent achievement DM to user {user_id}") + else: + print(f"Couldn't find Discord user with ID {user_id}") + except Exception as e: + print(f"Error sending Discord DM: {str(e)}") + finally: + await client.close() + + def award_achievement(user_id, achievement_id): user = users_collection.find_one({'_id': ObjectId(user_id)}) if not user: @@ -105,6 +126,15 @@ def award_achievement(user_id, achievement_id): send_discord_alert(f"🏆 Player {user['username']} has earned the '{achievement['name']}' achievement!", user['username']) + # Send a DM to the user if they have a linked Discord account + if 'discord_id' in user: + achievement_message = ( + f"Congratulations! You've earned the '{achievement['name']}' achievement in Resonance Rumble!\n\n" + f"Achievement description: {achievement['description']}\n\n" + "Keep up the great work and continue resonating!" + ) + asyncio.run(send_discord_dm(user['discord_id'], achievement_message)) + # Check for classes to unlock classes_to_unlock = list(classes_collection.find({'unlock_requirements.achievement': achievement_id})) for class_doc in classes_to_unlock: @@ -118,6 +148,7 @@ def award_achievement(user_id, achievement_id): return False + def send_discord_alert(message, username=None): if username: user = users_collection.find_one({'username': username}) @@ -295,10 +326,14 @@ def initiate_discord_link(user_id): @app.route('/discord-callback') def discord_callback(): + logging.info("Discord callback route hit") code = request.args.get('code') state = request.args.get('state') + logging.info(f"Received code: {code}, state: {state}") + if not code or not state: + logging.error("Missing code or state") return "Error: Missing parameters", 400 # Verify state and get user_id @@ -308,9 +343,11 @@ def discord_callback(): }) if not oauth_state: + logging.error("Invalid or expired state") return "Error: Invalid or expired state", 400 user_id = oauth_state['user_id'] + logging.info(f"Valid state for user_id: {user_id}") # Exchange code for token data = { @@ -323,50 +360,60 @@ def discord_callback(): headers = { 'Content-Type': 'application/x-www-form-urlencoded' } + logging.info("Exchanging code for token") r = requests.post(f'{DISCORD_API_ENDPOINT}/oauth2/token', data=data, headers=headers) if r.status_code != 200: + logging.error(f"Failed to exchange code for token. Status: {r.status_code}, Response: {r.text}") return "Error: Failed to exchange code for token", 400 tokens = r.json() + logging.info("Successfully exchanged code for token") # Get user info headers = { 'Authorization': f'Bearer {tokens["access_token"]}' } + logging.info("Getting user info from Discord") r = requests.get(f'{DISCORD_API_ENDPOINT}/users/@me', headers=headers) if r.status_code != 200: + logging.error(f"Failed to get user info. Status: {r.status_code}, Response: {r.text}") return "Error: Failed to get user info", 400 user_data = r.json() + logging.info(f"Retrieved user info for Discord user: {user_data['username']}#{user_data['discriminator']}") # Update user in database - users_collection.update_one( - {'_id': ObjectId(user_id)}, - {'$set': { - 'discord_id': user_data['id'], - 'discord_username': f"{user_data['username']}#{user_data['discriminator']}", - 'discord_avatar': user_data['avatar'] - }} - ) + try: + result = users_collection.update_one( + {'_id': ObjectId(user_id)}, + {'$set': { + 'discord_id': user_data['id'], + 'discord_username': f"{user_data['username']}#{user_data['discriminator']}", + 'discord_avatar': user_data['avatar'] + }} + ) + logging.info(f"Updated user in database. Modified count: {result.modified_count}") + except Exception as e: + logging.error(f"Failed to update user in database: {str(e)}") + return "Error: Failed to update user information", 500 # Award the achievement if award_achievement(user_id, 'discord_linked'): - socketio.emit('achievement_unlocked', { - 'achievement_id': 'discord_linked', - 'name': 'Discord Linked', - 'description': 'Successfully linked your Discord account', - 'image': 'discord_achievement.png' - }, room=request.sid) + logging.info(f"User {user_id} unlocked the 'Discord Linked' achievement") + # Add Discord role + try: + add_discord_role(user_data['id'], ACCOUNT_LINKED_ROLE_ID) + logging.info(f"Added Discord role to user {user_data['id']}") + except Exception as e: + logging.error(f"Failed to add Discord role: {str(e)}") - # After successfully updating the user in the database: - add_discord_role(user_data['id'], ACCOUNT_LINKED_ROLE_ID) - - + logging.info("Discord callback completed successfully") return """