Eh, kinda working

This commit is contained in:
2026-02-09 14:08:41 -06:00
parent 377b6b496b
commit 6f693dc19c
7 changed files with 384 additions and 0 deletions

14
.env.example Normal file
View File

@@ -0,0 +1,14 @@
DISCORD_TOKEN=YOUR_DISCORD_BOT_TOKEN
MYSQL_HOST=localhost
MYSQL_USER=root
MYSQL_PASSWORD=password
MYSQL_DATABASE=momentum
SITUPS=3
BICEP_CURLS=4
PUSHUPS=5
SQUATS=5
LUNGES=2
RUSSIAN_TWISTS=3
WALL_PUSHUPS=5

26
bot.py Normal file
View File

@@ -0,0 +1,26 @@
import discord
from discord import app_commands
from dotenv import load_dotenv
import os
from db import init_db
from commands import register
load_dotenv()
class Momentum(discord.Client):
def __init__(self):
super().__init__(intents=discord.Intents.default())
self.tree = app_commands.CommandTree(self)
async def setup_hook(self):
register(self.tree)
await self.tree.sync()
print("🌐 Commands synced")
async def on_ready(self):
print(f"🔥 Momentum online as {self.user}")
init_db()
bot = Momentum()
bot.run(os.getenv("DISCORD_TOKEN"))

264
commands.py Normal file
View File

@@ -0,0 +1,264 @@
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
# -------------------
enabled_workouts = [
app_commands.Choice(name=name, value=name)
for name, value in WORKOUTS.items()
if value > 0
]
@tree.command(name="match", 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
new_rank, new_rr = apply_rr(user["rank"], user["rr"], rr_change)
per_death = WORKOUTS[workout]
workout_total = deaths * per_death
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")

23
constants.py Normal file
View File

@@ -0,0 +1,23 @@
import os
VALORANT_RANKS = [
"Iron 1","Iron 2","Iron 3",
"Bronze 1","Bronze 2","Bronze 3",
"Silver 1","Silver 2","Silver 3",
"Gold 1","Gold 2","Gold 3",
"Platinum 1","Platinum 2","Platinum 3",
"Diamond 1","Diamond 2","Diamond 3",
"Ascendant 1","Ascendant 2","Ascendant 3",
"Immortal 1","Immortal 2","Immortal 3",
"Radiant"
]
WORKOUTS = {
"Sit Ups": int(os.getenv("SITUPS", 0)),
"Bicep Curls": int(os.getenv("BICEP_CURLS", 0)),
"Pushups": int(os.getenv("PUSHUPS", 0)),
"Squats": int(os.getenv("SQUATS", 0)),
"Lunges": int(os.getenv("LUNGES", 0)),
"Russian Twists": int(os.getenv("RUSSIAN_TWISTS", 0)),
"Wall Pushups": int(os.getenv("WALL_PUSHUPS", 0)),
}

43
db.py Normal file
View File

@@ -0,0 +1,43 @@
import mysql.connector
import os
def get_db():
return mysql.connector.connect(
host=os.getenv("MYSQL_HOST"),
user=os.getenv("MYSQL_USER"),
password=os.getenv("MYSQL_PASSWORD"),
database=os.getenv("MYSQL_DATABASE"),
autocommit=True
)
def init_db():
db = get_db()
cur = db.cursor()
cur.execute("""
CREATE TABLE IF NOT EXISTS users (
discord_id VARCHAR(32) PRIMARY KEY,
val_tag VARCHAR(32) NOT NULL,
rank VARCHAR(32) NOT NULL,
rr INT DEFAULT 0,
kills INT DEFAULT 0,
deaths INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
cur.execute("""
CREATE TABLE IF NOT EXISTS workouts (
discord_id VARCHAR(32),
workout VARCHAR(32),
amount INT DEFAULT 0,
PRIMARY KEY (discord_id, workout),
FOREIGN KEY (discord_id)
REFERENCES users(discord_id)
ON DELETE CASCADE
)
""")
cur.close()
db.close()
print("✅ Database ready")

11
helpers.py Normal file
View File

@@ -0,0 +1,11 @@
from constants import VALORANT_RANKS
def apply_rr(rank: str, rr: int, change: int):
rr += change
idx = VALORANT_RANKS.index(rank)
while rr >= 100 and idx < len(VALORANT_RANKS) - 1:
rr -= 100
idx += 1
return VALORANT_RANKS[idx], rr

3
requirements.txt Normal file
View File

@@ -0,0 +1,3 @@
discord.py>=2.3.2
mysql-connector-python>=8.3.0
python-dotenv>=1.0.1