mirror of
https://github.com/Rainyy21/lol_discordbot
synced 2026-10-11 00:00:32 -04:00
fix some the signin and check recent but need to fix profile
This commit is contained in:
Binary file not shown.
@@ -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
|
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
|
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.
|
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.
+18
-21
@@ -1,11 +1,9 @@
|
|||||||
import discord
|
import discord
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import aiohttp
|
|
||||||
import os
|
import os
|
||||||
from database.db import save_user
|
from database.db import save_user
|
||||||
|
from services.riot_api import get_account
|
||||||
RIOT_API_KEY = os.getenv("LEAGUEAPI")
|
|
||||||
|
|
||||||
class LoginCog(commands.Cog):
|
class LoginCog(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
@@ -26,41 +24,40 @@ class LoginCog(commands.Cog):
|
|||||||
|
|
||||||
discord_id = str(interaction.user.id)
|
discord_id = str(interaction.user.id)
|
||||||
|
|
||||||
url = (
|
# Use the service instead of direct API call
|
||||||
f"https://americas.api.riotgames.com/riot/account/v1/accounts"
|
data = await get_account(game_name, tag_line)
|
||||||
f"/by-riot-id/{game_name}/{tag_line}"
|
|
||||||
)
|
|
||||||
headers = {"X-Riot-Token": RIOT_API_KEY}
|
|
||||||
|
|
||||||
async with aiohttp.ClientSession() as session:
|
if data is None:
|
||||||
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)
|
await interaction.followup.send("❌ Summoner not found. Check your Riot ID.", ephemeral=True)
|
||||||
return
|
return
|
||||||
if resp.status != 200:
|
|
||||||
await interaction.followup.send(f"❌ Riot API error ({resp.status}). Try again later.", ephemeral=True)
|
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
|
return
|
||||||
|
|
||||||
data = await resp.json()
|
|
||||||
puuid = data["puuid"]
|
puuid = data["puuid"]
|
||||||
|
|
||||||
save_user(discord_id, puuid, game_name, tag_line)
|
save_user(discord_id, puuid, game_name, tag_line)
|
||||||
|
|
||||||
await interaction.followup.send(
|
await interaction.followup.send(
|
||||||
f"✅ Linked **{game_name}#{tag_line}** to your Discord account!",
|
f"✅ Linked **{game_name}#{tag_line}** to your Discord account!",
|
||||||
ephemeral=True
|
ephemeral=True
|
||||||
)
|
)
|
||||||
|
|
||||||
@app_commands.command(name="login", description="Link your Riot account")
|
@app_commands.command(name="login", description="Link your Riot account")
|
||||||
@app_commands.describe(summoner="Your Riot ID (e.g. Name#TAG)")
|
@app_commands.describe(summoner="Your Riot ID (e.g. Name#TAG)")
|
||||||
async def login(self, interaction: discord.Interaction, summoner: str):
|
async def login(self, interaction: discord.Interaction, summoner: str):
|
||||||
await self._handle_login(interaction, summoner)
|
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):
|
async def setup(bot):
|
||||||
await bot.add_cog(LoginCog(bot))
|
await bot.add_cog(LoginCog(bot))
|
||||||
|
|||||||
+27
-27
@@ -1,13 +1,10 @@
|
|||||||
import discord
|
import discord
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import aiohttp
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
from database.db import get_user
|
from database.db import get_user
|
||||||
|
from services.riot_api import get_summoner, get_rank
|
||||||
RIOT_API_KEY = os.getenv("LEAGUEAPI")
|
|
||||||
REGION = "na1"
|
|
||||||
|
|
||||||
class ProfileCog(commands.Cog):
|
class ProfileCog(commands.Cog):
|
||||||
def __init__(self, bot):
|
def __init__(self, bot):
|
||||||
@@ -29,28 +26,38 @@ class ProfileCog(commands.Cog):
|
|||||||
return
|
return
|
||||||
|
|
||||||
_, puuid, game_name, tag_line = user
|
_, 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
|
# 1. Get summoner data (level) via PUUID
|
||||||
summoner = await fetch(
|
summoner = await get_summoner(puuid)
|
||||||
session,
|
|
||||||
f"https://{REGION}.api.riotgames.com/lol/summoner/v4/summoners/by-puuid/{puuid}",
|
|
||||||
headers
|
|
||||||
)
|
|
||||||
if not summoner:
|
if not summoner:
|
||||||
await interaction.followup.send("❌ Could not fetch summoner data.")
|
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
|
return
|
||||||
|
|
||||||
summoner_id = summoner["id"]
|
summoner_id = summoner["id"]
|
||||||
level = summoner["summonerLevel"]
|
level = summoner.get("summonerLevel", "N/A")
|
||||||
|
|
||||||
# 2. Get rank & LP (League-v4)
|
# 2. Get rank & LP (League-v4)
|
||||||
leagues = await fetch(
|
leagues = await get_rank(summoner_id)
|
||||||
session,
|
|
||||||
f"https://{REGION}.api.riotgames.com/lol/league/v4/entries/by-summoner/{summoner_id}",
|
if leagues is None:
|
||||||
headers
|
leagues = []
|
||||||
)
|
elif isinstance(leagues, dict) and "error" in leagues:
|
||||||
|
await interaction.followup.send(f"❌ Riot API error ({leagues['error']}).")
|
||||||
|
return
|
||||||
|
|
||||||
# Parse ranked solo queue data
|
# Parse ranked solo queue data
|
||||||
rank_info = get_solo_rank(leagues)
|
rank_info = get_solo_rank(leagues)
|
||||||
|
|
||||||
@@ -81,16 +88,9 @@ class ProfileCog(commands.Cog):
|
|||||||
|
|
||||||
await interaction.followup.send(embed=embed)
|
await interaction.followup.send(embed=embed)
|
||||||
# ── Helpers ────────────────────────────────────────────────────────────────────
|
# ── Helpers ────────────────────────────────────────────────────────────────────
|
||||||
#try to get the information
|
def get_solo_rank(leagues) -> dict:
|
||||||
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."""
|
"""Extract solo queue entry from league data."""
|
||||||
if not leagues:
|
if not isinstance(leagues, list) or not leagues:
|
||||||
return {}
|
return {}
|
||||||
for entry in leagues:
|
for entry in leagues:
|
||||||
if entry.get("queueType") == "RANKED_SOLO_5x5":
|
if entry.get("queueType") == "RANKED_SOLO_5x5":
|
||||||
|
|||||||
+24
-43
@@ -1,27 +1,25 @@
|
|||||||
import discord
|
import discord
|
||||||
from discord import app_commands
|
from discord import app_commands
|
||||||
from discord.ext import commands
|
from discord.ext import commands
|
||||||
import aiohttp
|
import asyncio
|
||||||
import os
|
|
||||||
from database.db import get_user
|
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):
|
class RecentCog(commands.Cog):
|
||||||
def __init__(self, bot) -> None:
|
def __init__(self, bot) -> None:
|
||||||
self.bot = bot
|
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.command(name = "recent", description="Show your last N matches")
|
||||||
@app_commands.describe(count="Number of matches to show (1–10, default 5)")
|
@app_commands.describe(count="Number of matches to show (1–10, default 5)")
|
||||||
async def recent(self , interaction: discord.Interaction, count: int = 5):
|
async def recent(self , interaction: discord.Interaction, count: int = 5):
|
||||||
await interaction.response.defer(ephemeral=False)
|
await interaction.response.defer(ephemeral=False)
|
||||||
|
await self._update_patch()
|
||||||
|
|
||||||
count = max(1, min(count, 10)) # clamp between 1 and 10
|
count = max(1, min(count, 10)) # clamp between 1 and 10
|
||||||
discord_id = str(interaction.user.id)
|
discord_id = str(interaction.user.id)
|
||||||
@@ -36,32 +34,28 @@ class RecentCog(commands.Cog):
|
|||||||
|
|
||||||
#get user
|
#get user
|
||||||
_, puuid, game_name, tag_line = 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
|
# 1. Fetch match ID list
|
||||||
match_ids = await fetch(
|
match_ids = await get_match_ids(puuid, count=count)
|
||||||
session,
|
|
||||||
f"https://{cluster}.api.riotgames.com/lol/match/v5/matches/by-puuid/{puuid}/ids?count={count}",
|
if match_ids is None:
|
||||||
headers
|
|
||||||
)
|
|
||||||
if not match_ids:
|
|
||||||
await interaction.followup.send("❌ Could not fetch match history.")
|
await interaction.followup.send("❌ Could not fetch match history.")
|
||||||
return
|
return
|
||||||
|
|
||||||
# 2. Fetch each match in parallel
|
if isinstance(match_ids, dict) and "error" in match_ids:
|
||||||
match_data = await fetch_all(session, [
|
await interaction.followup.send(f"❌ Riot API error ({match_ids['error']}).")
|
||||||
f"https://{cluster}.api.riotgames.com/lol/match/v5/matches/{mid}"
|
return
|
||||||
for mid in match_ids
|
|
||||||
], headers)
|
# 2. Fetch each match in parallel (get_match handles caching internally)
|
||||||
|
match_data = await asyncio.gather(*[get_match(mid) for mid in match_ids])
|
||||||
|
|
||||||
embeds = []
|
embeds = []
|
||||||
for match in match_data:
|
for match in match_data:
|
||||||
if match:
|
if match and isinstance(match, dict) and "error" not in match:
|
||||||
embed = build_match_embed(match , puuid, game_name,tag_line)
|
embed = build_match_embed(match , puuid, game_name, tag_line, self.patch)
|
||||||
if embed:
|
if embed:
|
||||||
embeds.append(embed)
|
embeds.append(embed)
|
||||||
|
|
||||||
if not embeds:
|
if not embeds:
|
||||||
await interaction.followup.send("❌ No recent matches found.")
|
await interaction.followup.send("❌ No recent matches found.")
|
||||||
return
|
return
|
||||||
@@ -78,19 +72,6 @@ class RecentCog(commands.Cog):
|
|||||||
|
|
||||||
# ── Helpers ────────────────────────────────────────────────────────────────────
|
# ── 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:
|
def get_player(match: dict, puuid: str) -> dict:
|
||||||
"""Find this player's participant data within the match."""
|
"""Find this player's participant data within the match."""
|
||||||
participants = match["info"]["participants"]
|
participants = match["info"]["participants"]
|
||||||
@@ -118,7 +99,7 @@ def queue_name(queue_id: int) -> str:
|
|||||||
return queues.get(queue_id, "Other")
|
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)
|
player = get_player(match, puuid)
|
||||||
if not player:
|
if not player:
|
||||||
return None
|
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="🌾 CS", value=f"{cs} ({cs_per_min}/min)", inline=True)
|
||||||
embed.add_field(name="👤 Player", value=f"{game_name}#{tag_line}", inline=True)
|
embed.add_field(name="👤 Player", value=f"{game_name}#{tag_line}", inline=True)
|
||||||
embed.set_thumbnail(
|
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
|
return embed
|
||||||
|
|||||||
Binary file not shown.
@@ -11,6 +11,14 @@ cursor.execute("""
|
|||||||
tag_line TEXT
|
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()
|
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):
|
def get_user(discord_id: str):
|
||||||
cursor.execute("SELECT * FROM users WHERE discord_id = ?", (discord_id,))
|
cursor.execute("SELECT * FROM users WHERE discord_id = ?", (discord_id,))
|
||||||
return cursor.fetchone()
|
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.
+14
-4
@@ -3,7 +3,9 @@ import asyncio
|
|||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
from dotenv import load_dotenv
|
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")
|
RIOT_API_KEY = os.getenv("LEAGUEAPI")
|
||||||
REGION = os.getenv("REGION", "na1")
|
REGION = os.getenv("REGION", "na1")
|
||||||
@@ -24,20 +26,28 @@ CLUSTER = CLUSTER_MAP.get(REGION, "americas")
|
|||||||
|
|
||||||
|
|
||||||
async def riot_get(url: str, retries: int = 3) -> dict | list | None:
|
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}
|
headers = {"X-Riot-Token": RIOT_API_KEY}
|
||||||
async with aiohttp.ClientSession() as session:
|
async with aiohttp.ClientSession() as session:
|
||||||
for attempt in range(retries):
|
for attempt in range(retries):
|
||||||
async with session.get(url, headers=headers) as resp:
|
async with session.get(url, headers=headers) as resp:
|
||||||
if resp.status == 200:
|
if resp.status == 200:
|
||||||
return await resp.json()
|
return await resp.json()
|
||||||
elif resp.status == 492:
|
elif resp.status == 429:
|
||||||
wait = int(resp.headers.get("Retry-After", 5))
|
wait = int(resp.headers.get("Retry-After", 5))
|
||||||
print(f"⚠️ Rate limited. Retrying in {wait}s...")
|
print(f"⚠️ Rate limited. Retrying in {wait}s...")
|
||||||
await asyncio.sleep(wait)
|
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:
|
elif resp.status == 404:
|
||||||
return None
|
return None
|
||||||
else:
|
else:
|
||||||
print(f"❌ Error {resp.status} (attempt {attempt + 1}): {url}")
|
print(f"❌ Error {resp.status} (attempt {attempt + 1}): {url}")
|
||||||
|
if attempt < retries - 1:
|
||||||
await asyncio.sleep(1)
|
await asyncio.sleep(1)
|
||||||
|
|
||||||
return None
|
return None
|
||||||
@@ -80,9 +90,9 @@ async def get_match(match_id: str) -> dict | None:
|
|||||||
if cached:
|
if cached:
|
||||||
return json.loads(cached)
|
return json.loads(cached)
|
||||||
data = await riot_get(
|
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))
|
save_match(match_id, json.dumps(data))
|
||||||
return data
|
return data
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user