import discord from discord import app_commands from db import get_db from constants import VALORANT_RANKS, WORKOUTS from helpers import apply_rr import random def register(tree: app_commands.CommandTree): # ------------------- # /create # ------------------- @tree.command(name="create", description="Link your Valorant account") @app_commands.describe( val_tag="VALUSERNAME#TAG", rank="Your current Valorant rank", rr="Current RR (0-99)" ) @app_commands.choices( rank=[app_commands.Choice(name=r, value=r) for r in VALORANT_RANKS] ) async def create( interaction: discord.Interaction, val_tag: str, rank: str, rr: int ): db = get_db() cur = db.cursor() cur.execute(""" INSERT INTO users (discord_id, val_tag, rank, rr) VALUES (%s, %s, %s, %s) ON DUPLICATE KEY UPDATE val_tag=VALUES(val_tag), rank=VALUES(rank), rr=VALUES(rr) """, (str(interaction.user.id), val_tag, rank, rr)) cur.close() db.close() await interaction.response.send_message("✅ Account linked", ephemeral=True) # ------------------- # /match # ------------------- from constants import ENABLED_WORKOUTS enabled_workouts = [ app_commands.Choice(name=name, value=name) for name in ENABLED_WORKOUTS.keys() ] @tree.command(name="match_temp", description="Log a Valorant match") @app_commands.describe( result="Match result", rr_change="RR gained or lost", kd="Kills/Deaths (e.g. 20/15)", workout="Workout assigned for deaths" ) @app_commands.choices( result=[ app_commands.Choice(name="WIN", value="WIN"), app_commands.Choice(name="LOSS", value="LOSS") ], workout=enabled_workouts ) async def match( interaction: discord.Interaction, result: str, rr_change: int, kd: str, workout: str ): try: kills, deaths = map(int, kd.split("/")) except ValueError: await interaction.response.send_message( "❌ K/D must be in format `kills/deaths` (e.g. 20/15)", ephemeral=True ) return db = get_db() cur = db.cursor(dictionary=True) cur.execute( "SELECT * FROM users WHERE discord_id=%s", (str(interaction.user.id),) ) user = cur.fetchone() if not user: await interaction.response.send_message( "❌ You must run `/create` first", ephemeral=True ) return # Enforce RR direction based on result rr_delta = abs(rr_change) if result == "LOSS": rr_delta *= -1 new_rank, new_rr = apply_rr( user["rank"], user["rr"], rr_delta ) per_death = ENABLED_WORKOUTS.get(workout, 0) if per_death == 0: await interaction.response.send_message( "❌ That workout is currently disabled.", ephemeral=True ) return # Base workout calculation workout_total = deaths * per_death # Loss penalty if result == "LOSS": workout_total += 10 bonus_text = " (+10 loss penalty)" if result == "LOSS" else "" await interaction.response.send_message( f"🏆 Match logged\n" f"**Result:** {result}\n" f"**Rank:** {new_rank} ({new_rr} RR)\n" f"**Workout:** {workout} × {workout_total}{bonus_text}" ) cur.execute(""" UPDATE users SET kills = kills + %s, deaths = deaths + %s, rank = %s, rr = %s WHERE discord_id = %s """, (kills, deaths, new_rank, new_rr, str(interaction.user.id))) cur.execute(""" INSERT INTO workouts (discord_id, workout, amount) VALUES (%s, %s, %s) ON DUPLICATE KEY UPDATE amount = amount + %s """, (str(interaction.user.id), workout, workout_total, workout_total)) cur.close() db.close() await interaction.response.send_message( f"🏆 Match logged\n" f"**Rank:** {new_rank} ({new_rr} RR)\n" f"**Workout:** {workout} × {workout_total}" ) # ------------------- # /stats # ------------------- @tree.command(name="stats", description="Show Momentum stats") async def stats( interaction: discord.Interaction, user: discord.User | None = None ): target = user or interaction.user db = get_db() cur = db.cursor(dictionary=True) cur.execute( "SELECT * FROM users WHERE discord_id=%s", (str(target.id),) ) u = cur.fetchone() if not u: await interaction.response.send_message( "❌ User not found", ephemeral=True ) return cur.execute( "SELECT workout, amount FROM workouts WHERE discord_id=%s", (str(target.id),) ) workout_rows = cur.fetchall() embed = discord.Embed( title=f"{target.display_name}'s Momentum Stats", color=discord.Color.red() ) embed.add_field( name="Rank", value=f"{u['rank']} ({u['rr']} RR)", inline=False ) embed.add_field( name="Valorant", value=u["val_tag"], inline=False ) embed.add_field( name="Kills / Deaths", value=f"{u['kills']} / {u['deaths']}", inline=False ) workout_text = ( "\n".join(f"{w['workout']}: {w['amount']}" for w in workout_rows) if workout_rows else "None" ) embed.add_field( name="Workouts Completed", value=workout_text, inline=False ) cur.close() db.close() await interaction.response.send_message(embed=embed) # ------------------- # /workout # ------------------- @tree.command(name="workout", description="Get a random workout") async def workout(interaction: discord.Interaction): enabled = [w for w, v in WORKOUTS.items() if v > 0] choice = random.choice(enabled) await interaction.response.send_message( f"💪 **{choice}**\n" f"{WORKOUTS[choice]} per death" ) # ------------------- # /edit (admin) # ------------------- @tree.command(name="edit", description="Edit a user's stats (Admin)") @app_commands.choices( rank=[app_commands.Choice(name=r, value=r) for r in VALORANT_RANKS] ) async def edit( interaction: discord.Interaction, user: discord.User, rank: str | None = None, rr: int | None = None, val_tag: str | None = None ): if not interaction.user.guild_permissions.administrator: await interaction.response.send_message( "❌ Admins only", ephemeral=True ) return fields = [] values = [] if rank: fields.append("rank=%s") values.append(rank) if rr is not None: fields.append("rr=%s") values.append(rr) if val_tag: fields.append("val_tag=%s") values.append(val_tag) if not fields: await interaction.response.send_message( "Nothing to update", ephemeral=True ) return values.append(str(user.id)) db = get_db() cur = db.cursor() cur.execute( f"UPDATE users SET {', '.join(fields)} WHERE discord_id=%s", values ) cur.close() db.close() await interaction.response.send_message("✏️ User updated")