complete the bot

This commit is contained in:
Rainy21
2026-04-22 00:49:43 -04:00
parent b2f09d6cca
commit c6257c5260
8 changed files with 845 additions and 42 deletions
+59
View File
@@ -0,0 +1,59 @@
import discord
from discord import app_commands
from discord.ext import commands
import aiohttp
import os
from database.db import save_user
RIOT_API_KEY = os.getenv("LEAGUEAPI")
class LoginCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="login", description="Link your Riot account")
@app_commands.describe(summoner="Your Riot ID (e.g. Name#TAG)")
async def login(self, interaction: discord.Interaction, summoner: str):
await interaction.response.defer(ephemeral=True)
if "#" not in summoner:
await interaction.followup.send("❌ Use the format `Name#TAG`", ephemeral=True)
return
try:
game_name, tag_line = summoner.split("#", 1)
except ValueError:
await interaction.followup.send("❌ Invalid format. Use `Name#TAG`", ephemeral=True)
return
discord_id = str(interaction.user.id)
url = (
f"https://americas.api.riotgames.com/riot/account/v1/accounts"
f"/by-riot-id/{game_name}/{tag_line}"
)
headers = {"X-Riot-Token": RIOT_API_KEY}
async with aiohttp.ClientSession() as session:
async with session.get(url, headers=headers) as resp:
if resp.status == 404:
await interaction.followup.send("❌ Summoner not found. Check your Riot ID.", ephemeral=True)
return
if resp.status != 200:
await interaction.followup.send(f"❌ Riot API error ({resp.status}). Try again later.", ephemeral=True)
return
data = await resp.json()
puuid = data["puuid"]
save_user(discord_id, puuid, game_name, tag_line)
await interaction.followup.send(
f"✅ Linked **{game_name}#{tag_line}** to your Discord account!",
ephemeral=True
)
async def setup(bot):
await bot.add_cog(LoginCog(bot))
+110
View File
@@ -0,0 +1,110 @@
from discord import app_commands
from discord.ext import commands
import aiohttp
import os
from database.db import get_user
RIOT_API_KEY = os.getenv("LEAGUEAPI")
REGION = "na1"
class ProfileCog(commands.Cog):
def __init__(self, bot):
self.bot = bot
@app_commands.command(name="profile", description="Show your LoL rank, level, and win rate")
async def profile(self, interaction: discord.Interaction):
await interaction.response.defer(ephemeral = False)
# get user from the db
discord_id = str(interaction.user.id)
user = get_user(discord_id)
#check if user login
if not user:
await interaction.followup.send(
"❌ You haven't linked your Riot account yet. Use `/login` first."
)
return
_, puuid, game_name, tag_line = user
headers = {"X-Riot-Token": RIOT_API_KEY}
async with aiohttp.ClientSession() as session:
# 1. Get summoner data (level) via PUUID
summoner = await fetch(
session,
f"https://{REGION}.api.riotgames.com/lol/summoner/v4/summoners/by-puuid/{puuid}",
headers
)
if not summoner:
await interaction.followup.send("❌ Could not fetch summoner data.")
return
summoner_id = summoner["id"]
level = summoner["summonerLevel"]
# 2. Get rank & LP (League-v4)
leagues = await fetch(
session,
f"https://{REGION}.api.riotgames.com/lol/league/v4/entries/by-summoner/{summoner_id}",
headers
)
# Parse ranked solo queue data
rank_info = get_solo_rank(leagues)
# Build win rate
wins = rank_info.get("wins", 0)
losses = rank_info.get("losses", 0)
total = wins + losses
win_rate = f"{round(wins / total * 100)}%" if total > 0 else "N/A"
# Build Discord embed
embed = discord.Embed(
title=f"📊 {game_name}#{tag_line}",
color=0x1a78c2
)
embed.add_field(name="🎮 Level", value=str(level), inline=True)
embed.add_field(
name="🏆 Rank",
value=format_rank(rank_info),
inline=True
)
embed.add_field(name="📈 Win Rate", value=win_rate, inline=True)
embed.add_field(
name="📋 Record",
value=f"{wins}W / {losses}L" if total > 0 else "Unranked",
inline=True
)
embed.set_footer(text="Powered by Riot Games API")
await interaction.followup.send(embed=embed)
# ── Helpers ────────────────────────────────────────────────────────────────────
#try to get the information
async def fetch(session:aiohttp.ClientSession, url: str, headers: dict):
async with session.get(url, headers=headers) as resp:
if resp.status != 200:
return None
return await resp.json()
def get_solo_rank(leagues: list) -> dict:
"""Extract solo queue entry from league data."""
if not leagues:
return {}
for entry in leagues:
if entry.get("queueType") == "RANKED_SOLO_5x5":
return entry
return {}
def format_rank(rank_info:dict) -> str:
"""Format rank into a readable string."""
if not rank_info:
return "Unranked"
tier = rank_info.get("tier", "Unranked").capitalize()
division = rank_info.get("rank", "")
lp = rank_info.get("leaguePoints", 0)
return f"{tier} {division} — {lp} LP"
async def setup(bot):
await bot.add_cog(ProfileCog(bot))
+157
View File
@@ -0,0 +1,157 @@
import discord
from discord import app_commands
from discord.ext import commands
import aiohttp
import os
from database.db import get_user
RIOT_API_KEY = os.getenv("LEAGUEAPI")
REGION = "na1"
CLUSTER = "americas"
CLUSTER_MAP = {
"na1": "americas", "br1": "americas", "la1": "americas", "la2": "americas",
"euw1": "europe", "eun1": "europe", "tr1": "europe", "ru": "europe",
"kr": "asia", "jp1": "asia",
}
class RecentCog(commands.Cog):
def __init__(self, bot) -> None:
self.bot = bot
@app_commands.command(name = "recent", description="Show your last N matches")
@app_commands.describe(count="Number of matches to show (1–10, default 5)")
async def recent(self , interaction: discord.Interaction, count: int = 5):
await interaction.response.defer(ephemeral=False)
count = max(1, min(count, 10)) # clamp between 1 and 10
discord_id = str(interaction.user.id)
user = get_user(discord_id)
#check if user
if not user:
await interaction.followup.send(
"❌ You haven't linked your account yet. Use `/login` first."
)
return
#get user
_, puuid, game_name, tag_line = user
cluster = CLUSTER_MAP.get(REGION, "americas")
headers = {"X-Riot-Token": RIOT_API_KEY}
#get user data
async with aiohttp.ClientSession() as session:
# 1. Fetch match ID list
match_ids = await fetch(
session,
f"https://{cluster}.api.riotgames.com/lol/match/v5/matches/by-puuid/{puuid}/ids?count={count}",
headers
)
if not match_ids:
await interaction.followup.send("❌ Could not fetch match history.")
return
# 2. Fetch each match in parallel
match_data = await fetch_all(session, [
f"https://{cluster}.api.riotgames.com/lol/match/v5/matches/{mid}"
for mid in match_ids
], headers)
embeds = []
for match in match_data:
if match:
embed = build_match_embed(match , puuid, game_name,tag_line)
if embed:
embeds.append(embed)
if not embeds:
await interaction.followup.send("❌ No recent matches found.")
return
# Discord allows max 10 embeds per message
await interaction.followup.send(embeds=embeds[:10])
# ── Helpers ────────────────────────────────────────────────────────────────────
async def fetch(session: aiohttp.ClientSession, url: str, headers: dict):
async with session.get(url, headers=headers) as resp:
if resp.status != 200:
return None
return await resp.json()
async def fetch_all(session: aiohttp.ClientSession, urls: list, headers: dict):
"""Fetch multiple URLs concurrently."""
import asyncio
return await asyncio.gather(*[fetch(session, url, headers) for url in urls])
def get_player(match: dict, puuid: str) -> dict:
"""Find this player's participant data within the match."""
participants = match["info"]["participants"]
return next((p for p in participants if p["puuid"] == puuid), None)
def format_kda(kills, deaths, assists) -> str:
ratio = (kills + assists) / max(deaths, 1)
return f"{kills}/{deaths}/{assists} ({ratio:.2f} KDA)"
def format_duration(seconds: int) -> str:
m, s = divmod(seconds, 60)
return f"{m}m {s}s"
def queue_name(queue_id: int) -> str:
queues = {
420: "Ranked Solo",
440: "Ranked Flex",
450: "ARAM",
400: "Normal Draft",
430: "Normal Blind",
}
return queues.get(queue_id, "Other")
def build_match_embed(match: dict, puuid: str, game_name: str, tag_line: str):
player = get_player(match, puuid)
if not player:
return None
won = player["win"]
champion = player["championName"]
kills = player["kills"]
deaths = player["deaths"]
assists = player["assists"]
cs = player["totalMinionsKilled"] + player["neutralMinionsKilled"]
duration = match["info"]["gameDuration"]
queue_id = match["info"]["queueId"]
cs_per_min = round(cs / max(duration / 60, 1), 1)
color = 0x2ecc71 if won else 0xe74c3c # green = win, red = loss
result = "✅ Victory" if won else "❌ Defeat"
embed = discord.Embed(
title=f"{result} — {champion}",
description=f"**{queue_name(queue_id)}** · {format_duration(duration)}",
color=color
)
embed.add_field(name="⚔️ KDA", value=format_kda(kills, deaths, assists), inline=True)
embed.add_field(name="🌾 CS", value=f"{cs} ({cs_per_min}/min)", inline=True)
embed.add_field(name="👤 Player", value=f"{game_name}#{tag_line}", inline=True)
embed.set_thumbnail(
url=f"https://ddragon.leagueoflegends.com/cdn/14.8.1/img/champion/{champion}.png"
)
return embed
#---------------------------------------------------------------------------
#how to start the bot
async def setup(bot):
await bot.add_cog(RecentCog(bot))