fix some the signin and check recent but need to fix profile

This commit is contained in:
Rainy21
2026-04-23 00:36:34 -04:00
parent 0a972f6a3f
commit a8ddd363a7
13 changed files with 233 additions and 109 deletions
+114
View File
@@ -150,3 +150,117 @@ Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/commands.py", line 847, in _transform_arguments
raise CommandSignatureMismatch(self) from None
discord.app_commands.errors.CommandSignatureMismatch: The signature for command 'login' is different from the one provided by Discord. This can happen because either your code is out of date or you have not synced the commands with Discord, causing the mismatch in data. It is recommended to sync the command tree to fix this issue.
2026-04-22 03:44:35,358 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported
2026-04-22 03:44:35,358 [WARNING] discord.client: davey is not installed, voice will NOT be supported
2026-04-22 03:44:35,359 [INFO] discord.client: logging in using static token
2026-04-22 03:44:35,751 [INFO] bot: ✅ Loaded extension: commands.login
2026-04-22 03:44:35,751 [INFO] bot: ✅ Loaded extension: commands.profile
2026-04-22 03:44:35,752 [INFO] bot: ✅ Loaded extension: commands.recent
2026-04-22 03:44:35,884 [INFO] bot: 🔄 Slash commands synced to guild 1055571472611033160
2026-04-22 03:44:36,069 [INFO] bot: 🔄 Global slash commands synced
2026-04-22 03:44:36,466 [INFO] discord.gateway: Shard ID None has connected to Gateway (Session ID: b5af520b7608ba76313f4e32f6045ed4).
2026-04-22 03:44:38,469 [INFO] bot: ✅ Logged in as league of legend bot#3877 (ID: 1494467149186465862)
2026-04-22 03:44:38,470 [INFO] bot: Guilds : 1
2026-04-22 03:44:38,470 [INFO] bot: Ping : 47ms
2026-04-23 00:15:51,686 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported
2026-04-23 00:15:51,688 [WARNING] discord.client: davey is not installed, voice will NOT be supported
2026-04-23 00:15:51,690 [INFO] discord.client: logging in using static token
2026-04-23 00:15:52,218 [INFO] bot: ✅ Loaded extension: commands.login
2026-04-23 00:15:52,220 [INFO] bot: ✅ Loaded extension: commands.profile
2026-04-23 00:15:52,223 [INFO] bot: ✅ Loaded extension: commands.recent
2026-04-23 00:15:52,333 [INFO] bot: 🔄 Slash commands synced to guild 1055571472611033160
2026-04-23 00:15:52,674 [INFO] bot: 🔄 Global slash commands synced
2026-04-23 00:15:53,798 [INFO] discord.gateway: Shard ID None has connected to Gateway (Session ID: c95d0a5504b0553dfd47d07f17ad9d72).
2026-04-23 00:15:55,804 [INFO] bot: ✅ Logged in as league of legend bot#3877 (ID: 1494467149186465862)
2026-04-23 00:15:55,807 [INFO] bot: Guilds : 1
2026-04-23 00:15:55,807 [INFO] bot: Ping : 39ms
2026-04-23 00:23:23,262 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported
2026-04-23 00:23:23,263 [WARNING] discord.client: davey is not installed, voice will NOT be supported
2026-04-23 00:23:23,264 [INFO] discord.client: logging in using static token
2026-04-23 00:23:23,782 [INFO] bot: ✅ Loaded extension: commands.login
2026-04-23 00:23:23,784 [ERROR] bot: ❌ Failed to load commands.profile: Extension 'commands.profile' raised an error: NameError: name 'aiohttp' is not defined
2026-04-23 00:23:23,788 [ERROR] bot: ❌ Failed to load commands.recent: Extension 'commands.recent' raised an error: NameError: name 'aiohttp' is not defined
2026-04-23 00:23:23,926 [INFO] bot: 🔄 Slash commands synced to guild 1055571472611033160
2026-04-23 00:23:24,080 [INFO] bot: 🔄 Global slash commands synced
2026-04-23 00:23:24,693 [INFO] discord.gateway: Shard ID None has connected to Gateway (Session ID: 1fe2f819a2266ec9c68c874572e3ca7c).
2026-04-23 00:23:26,699 [INFO] bot: ✅ Logged in as league of legend bot#3877 (ID: 1494467149186465862)
2026-04-23 00:23:26,699 [INFO] bot: Guilds : 1
2026-04-23 00:23:26,700 [INFO] bot: Ping : 42ms
2026-04-23 00:25:58,295 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported
2026-04-23 00:25:58,295 [WARNING] discord.client: davey is not installed, voice will NOT be supported
2026-04-23 00:25:58,296 [INFO] discord.client: logging in using static token
2026-04-23 00:25:58,617 [INFO] bot: ✅ Loaded extension: commands.login
2026-04-23 00:25:58,619 [ERROR] bot: ❌ Failed to load commands.profile: Extension 'commands.profile' raised an error: NameError: name 'aiohttp' is not defined
2026-04-23 00:25:58,622 [ERROR] bot: ❌ Failed to load commands.recent: Extension 'commands.recent' raised an error: NameError: name 'aiohttp' is not defined
2026-04-23 00:25:58,729 [INFO] bot: 🔄 Slash commands synced to guild 1055571472611033160
2026-04-23 00:25:58,910 [INFO] bot: 🔄 Global slash commands synced
2026-04-23 00:25:59,706 [INFO] discord.gateway: Shard ID None has connected to Gateway (Session ID: 6be4de7a798203f744639beaeeb3bc77).
2026-04-23 00:26:01,749 [INFO] bot: ✅ Logged in as league of legend bot#3877 (ID: 1494467149186465862)
2026-04-23 00:26:01,760 [INFO] bot: Guilds : 1
2026-04-23 00:26:01,761 [INFO] bot: Ping : 42ms
2026-04-23 00:29:04,989 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported
2026-04-23 00:29:04,989 [WARNING] discord.client: davey is not installed, voice will NOT be supported
2026-04-23 00:29:04,990 [INFO] discord.client: logging in using static token
2026-04-23 00:29:05,366 [INFO] bot: ✅ Loaded extension: commands.login
2026-04-23 00:29:05,370 [INFO] bot: ✅ Loaded extension: commands.profile
2026-04-23 00:29:05,373 [INFO] bot: ✅ Loaded extension: commands.recent
2026-04-23 00:29:05,663 [INFO] bot: 🔄 Slash commands synced to guild 1055571472611033160
2026-04-23 00:29:05,860 [INFO] bot: 🔄 Global slash commands synced
2026-04-23 00:29:06,727 [INFO] discord.gateway: Shard ID None has connected to Gateway (Session ID: 1caf69eb498bb76c6a22762831c789e3).
2026-04-23 00:29:08,757 [INFO] bot: ✅ Logged in as league of legend bot#3877 (ID: 1494467149186465862)
2026-04-23 00:29:08,761 [INFO] bot: Guilds : 1
2026-04-23 00:29:08,762 [INFO] bot: Ping : 43ms
2026-04-23 00:29:22,375 [ERROR] discord.app_commands.tree: Ignoring exception in command 'profile'
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/commands.py", line 858, in _do_call
return await self._callback(self.binding, interaction, **params) # type: ignore
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/simonzheng/Documents/vscode/lolBot/commands/profile.py", line 41, in profile
summoner_id = summoner["id"]
~~~~~~~~^^^^^^
KeyError: 'id'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/tree.py", line 1302, in _call
await command._invoke_with_namespace(interaction, namespace)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/commands.py", line 884, in _invoke_with_namespace
return await self._do_call(interaction, transformed_values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/commands.py", line 877, in _do_call
raise CommandInvokeError(self, e) from e
discord.app_commands.errors.CommandInvokeError: Command 'profile' raised an exception: KeyError: 'id'
2026-04-23 00:30:43,277 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported
2026-04-23 00:30:43,278 [WARNING] discord.client: davey is not installed, voice will NOT be supported
2026-04-23 00:30:43,279 [INFO] discord.client: logging in using static token
2026-04-23 00:30:44,221 [INFO] bot: ✅ Loaded extension: commands.login
2026-04-23 00:30:44,227 [INFO] bot: ✅ Loaded extension: commands.profile
2026-04-23 00:30:44,231 [INFO] bot: ✅ Loaded extension: commands.recent
2026-04-23 00:30:44,745 [INFO] bot: 🔄 Slash commands synced to guild 1055571472611033160
2026-04-23 00:30:44,922 [INFO] bot: 🔄 Global slash commands synced
2026-04-23 00:30:45,429 [INFO] discord.gateway: Shard ID None has connected to Gateway (Session ID: 07f28f18598c25062c3300ff0d0bf20f).
2026-04-23 00:30:47,479 [INFO] bot: ✅ Logged in as league of legend bot#3877 (ID: 1494467149186465862)
2026-04-23 00:30:47,481 [INFO] bot: Guilds : 1
2026-04-23 00:30:47,481 [INFO] bot: Ping : 50ms
2026-04-23 00:31:43,436 [ERROR] discord.app_commands.tree: Ignoring exception in command 'profile'
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/commands.py", line 858, in _do_call
return await self._callback(self.binding, interaction, **params) # type: ignore
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Users/simonzheng/Documents/vscode/lolBot/commands/profile.py", line 41, in profile
summoner_id = summoner["id"]
~~~~~~~~^^^^^^
KeyError: 'id'
The above exception was the direct cause of the following exception:
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/tree.py", line 1302, in _call
await command._invoke_with_namespace(interaction, namespace)
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/commands.py", line 884, in _invoke_with_namespace
return await self._do_call(interaction, transformed_values)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/commands.py", line 877, in _do_call
raise CommandInvokeError(self, e) from e
discord.app_commands.errors.CommandInvokeError: Command 'profile' raised an exception: KeyError: 'id'
Binary file not shown.
Binary file not shown.
Binary file not shown.
+21 -24
View File
@@ -1,11 +1,9 @@
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")
from services.riot_api import get_account
class LoginCog(commands.Cog):
def __init__(self, bot):
@@ -26,41 +24,40 @@ class LoginCog(commands.Cog):
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}
# Use the service instead of direct API call
data = await get_account(game_name, tag_line)
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
if data is None:
await interaction.followup.send("❌ Summoner not found. Check your Riot ID.", ephemeral=True)
return
data = await resp.json()
puuid = data["puuid"]
if isinstance(data, dict) and "error" in data:
if data["error"] == 401:
await interaction.followup.send(
"❌ Riot API error (401): Unauthorized. "
"This usually means the bot's API key has expired. "
"Please notify the bot owner to refresh it at developer.riotgames.com.",
ephemeral=True
)
else:
await interaction.followup.send(
f"❌ Riot API error ({data['error']}). Try again later.",
ephemeral=True
)
return
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
)
@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 self._handle_login(interaction, summoner)
@app_commands.command(name="signin", description="Link your Riot account")
@app_commands.describe(summoner="Your Riot ID (e.g. Name#TAG)")
async def signin(self, interaction: discord.Interaction, summoner: str):
await self._handle_login(interaction, summoner)
async def setup(bot):
await bot.add_cog(LoginCog(bot))
+32 -32
View File
@@ -1,13 +1,10 @@
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"
from services.riot_api import get_summoner, get_rank
class ProfileCog(commands.Cog):
def __init__(self, bot):
@@ -29,28 +26,38 @@ class ProfileCog(commands.Cog):
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
# 1. Get summoner data (level) via PUUID
summoner = await get_summoner(puuid)
summoner_id = summoner["id"]
level = summoner["summonerLevel"]
if not summoner:
await interaction.followup.send("❌ Could not fetch summoner data (Summoner not found).")
return
if isinstance(summoner, dict) and "error" in summoner:
if summoner["error"] == 401:
await interaction.followup.send("❌ Riot API error (401): Unauthorized. API Key might be expired.")
else:
await interaction.followup.send(f"❌ Riot API error ({summoner['error']}).")
return
# Ensure 'id' exists before accessing it
if "id" not in summoner:
await interaction.followup.send("❌ Unexpected data format from Riot API.")
return
summoner_id = summoner["id"]
level = summoner.get("summonerLevel", "N/A")
# 2. Get rank & LP (League-v4)
leagues = await get_rank(summoner_id)
if leagues is None:
leagues = []
elif isinstance(leagues, dict) and "error" in leagues:
await interaction.followup.send(f"❌ Riot API error ({leagues['error']}).")
return
# 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)
@@ -81,16 +88,9 @@ class ProfileCog(commands.Cog):
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:
def get_solo_rank(leagues) -> dict:
"""Extract solo queue entry from league data."""
if not leagues:
if not isinstance(leagues, list) or not leagues:
return {}
for entry in leagues:
if entry.get("queueType") == "RANKED_SOLO_5x5":
+27 -46
View File
@@ -1,27 +1,25 @@
import discord
from discord import app_commands
from discord.ext import commands
import aiohttp
import os
import asyncio
from database.db import get_user
from services.riot_api import get_match_ids, get_match, get_latest_patch
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
self.patch = "14.8.1"
async def _update_patch(self):
new_patch = await get_latest_patch()
if new_patch:
self.patch = new_patch
@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)
await self._update_patch()
count = max(1, min(count, 10)) # clamp between 1 and 10
discord_id = str(interaction.user.id)
@@ -36,32 +34,28 @@ class RecentCog(commands.Cog):
#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
# 1. Fetch match ID list
match_ids = await get_match_ids(puuid, count=count)
if match_ids is None:
await interaction.followup.send("❌ Could not fetch match history.")
return
if isinstance(match_ids, dict) and "error" in match_ids:
await interaction.followup.send(f"❌ Riot API error ({match_ids['error']}).")
return
# 2. Fetch each match in parallel (get_match handles caching internally)
match_data = await asyncio.gather(*[get_match(mid) for mid in match_ids])
# 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 match and isinstance(match, dict) and "error" not in match:
embed = build_match_embed(match , puuid, game_name, tag_line, self.patch)
if embed:
embeds.append(embed)
if not embeds:
await interaction.followup.send("❌ No recent matches found.")
return
@@ -78,19 +72,6 @@ class RecentCog(commands.Cog):
# ── 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"]
@@ -118,7 +99,7 @@ def queue_name(queue_id: int) -> str:
return queues.get(queue_id, "Other")
def build_match_embed(match: dict, puuid: str, game_name: str, tag_line: str):
def build_match_embed(match: dict, puuid: str, game_name: str, tag_line: str, patch: str):
player = get_player(match, puuid)
if not player:
return None
@@ -145,7 +126,7 @@ def build_match_embed(match: dict, puuid: str, game_name: str, tag_line: str):
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"
url=f"https://ddragon.leagueoflegends.com/cdn/{patch}/img/champion/{champion}.png"
)
return embed
Binary file not shown.
+22
View File
@@ -11,6 +11,14 @@ cursor.execute("""
tag_line TEXT
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS matches(
match_id TEXT PRIMARY KEY,
data TEXT NOT NULL,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
)
""")
conn.commit()
@@ -32,3 +40,17 @@ def save_user(discord_id: str, puuid: str, game_name: str, tag_line: str):
def get_user(discord_id: str):
cursor.execute("SELECT * FROM users WHERE discord_id = ?", (discord_id,))
return cursor.fetchone()
def save_match(match_id: str, data: str):
cursor.execute(
"INSERT OR REPLACE INTO matches(match_id, data) VALUES (?, ?)",
(match_id, data),
)
conn.commit()
def get_match(match_id: str):
cursor.execute("SELECT data FROM matches WHERE match_id = ?", (match_id,))
row = cursor.fetchone()
return row[0] if row else None
Binary file not shown.
+15 -5
View File
@@ -3,7 +3,9 @@ import asyncio
import os
import json
from dotenv import load_dotenv
from db import save_match, get_match as get_cached_match
from database.db import save_match, get_match as get_cached_match
load_dotenv()
RIOT_API_KEY = os.getenv("LEAGUEAPI")
REGION = os.getenv("REGION", "na1")
@@ -24,21 +26,29 @@ CLUSTER = CLUSTER_MAP.get(REGION, "americas")
async def riot_get(url: str, retries: int = 3) -> dict | list | None:
if not RIOT_API_KEY:
print("❌ RIOT_API_KEY not found in environment")
return {"error": 401, "message": "API Key missing"}
headers = {"X-Riot-Token": RIOT_API_KEY}
async with aiohttp.ClientSession() as session:
for attempt in range(retries):
async with session.get(url, headers=headers) as resp:
if resp.status == 200:
return await resp.json()
elif resp.status == 492:
elif resp.status == 429:
wait = int(resp.headers.get("Retry-After", 5))
print(f"⚠️ Rate limited. Retrying in {wait}s...")
await asyncio.sleep(wait)
elif resp.status == 401:
print(f"❌ 401 Unauthorized: RIOT_API_KEY is invalid or expired.")
return {"error": 401, "message": "Unauthorized (Key expired/invalid)"}
elif resp.status == 404:
return None
else:
print(f"❌ Error {resp.status} (attempt {attempt + 1}): {url}")
await asyncio.sleep(1)
if attempt < retries - 1:
await asyncio.sleep(1)
return None
@@ -80,9 +90,9 @@ async def get_match(match_id: str) -> dict | None:
if cached:
return json.loads(cached)
data = await riot_get(
f"https://{CLUSTER}.api.riotgames.com" f"/lol/match/v5/matches/{match_id}"
f"https://{CLUSTER}.api.riotgames.com/lol/match/v5/matches/{match_id}"
)
if data:
if data and isinstance(data, dict) and "error" not in data:
save_match(match_id, json.dumps(data))
return data
BIN
View File
Binary file not shown.