From 0a972f6a3f52cb501060aa86ca18fbde334bd51e Mon Sep 17 00:00:00 2001 From: Rainy21 Date: Wed, 22 Apr 2026 03:44:29 -0400 Subject: [PATCH] fix some bug that doesn't let user to sign in and connect with discord, added some test --- bot.log | 68 +++++++++++ bot.py | 41 +++++-- bot_output.log | 30 +++++ commands/__pycache__/login.cpython-313.pyc | Bin 0 -> 4984 bytes commands/__pycache__/profile.cpython-313.pyc | Bin 0 -> 5950 bytes commands/__pycache__/recent.cpython-313.pyc | Bin 0 -> 8437 bytes commands/login.py | 14 ++- commands/profile.py | 3 +- commands/recent.py | 2 +- database/__pycache__/db.cpython-313.pyc | Bin 0 -> 1468 bytes services/riot_api.py | 2 +- test_all_commands.py | 120 +++++++++++++++++++ user.db | Bin 0 -> 12288 bytes 13 files changed, 262 insertions(+), 18 deletions(-) create mode 100644 bot_output.log create mode 100644 commands/__pycache__/login.cpython-313.pyc create mode 100644 commands/__pycache__/profile.cpython-313.pyc create mode 100644 commands/__pycache__/recent.cpython-313.pyc create mode 100644 database/__pycache__/db.cpython-313.pyc create mode 100644 test_all_commands.py create mode 100644 user.db diff --git a/bot.log b/bot.log index 44fed83..c86b3e6 100644 --- a/bot.log +++ b/bot.log @@ -82,3 +82,71 @@ Traceback (most recent call last): File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/aiohttp/connector.py", line 1327, in _wrap_create_connection raise ClientConnectorCertificateError(req.connection_key, exc) from exc aiohttp.client_exceptions.ClientConnectorCertificateError: Cannot connect to host discord.com:443 ssl:True [SSLCertVerificationError: (1, '[SSL: CERTIFICATE_VERIFY_FAILED] certificate verify failed: unable to get local issuer certificate (_ssl.c:1018)')] +2026-04-22 00:50:03,180 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported +2026-04-22 00:50:03,181 [WARNING] discord.client: davey is not installed, voice will NOT be supported +2026-04-22 00:50:03,182 [INFO] discord.client: logging in using static token +2026-04-22 00:50:03,704 [INFO] bot: ✅ Loaded extension: commands.login +2026-04-22 00:50:03,708 [ERROR] bot: ❌ Failed to load commands.profile: Extension 'commands.profile' raised an error: NameError: name 'discord' is not defined +2026-04-22 00:50:03,714 [INFO] bot: ✅ Loaded extension: commands.recent +2026-04-22 00:50:03,910 [INFO] bot: 🔄 Slash commands synced +2026-04-22 00:50:04,352 [WARNING] bot: ⚠️ Bot disconnected from Discord +2026-04-22 00:54:47,504 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported +2026-04-22 00:54:47,505 [WARNING] discord.client: davey is not installed, voice will NOT be supported +2026-04-22 00:54:47,506 [INFO] discord.client: logging in using static token +2026-04-22 00:54:47,891 [INFO] bot: ✅ Loaded extension: commands.login +2026-04-22 00:54:47,899 [INFO] bot: ✅ Loaded extension: commands.profile +2026-04-22 00:54:47,900 [INFO] bot: ✅ Loaded extension: commands.recent +2026-04-22 00:54:48,183 [INFO] bot: 🔄 Slash commands synced +2026-04-22 00:54:48,576 [WARNING] bot: ⚠️ Bot disconnected from Discord +2026-04-22 01:02:58,262 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported +2026-04-22 01:02:58,263 [WARNING] discord.client: davey is not installed, voice will NOT be supported +2026-04-22 01:02:58,264 [INFO] discord.client: logging in using static token +2026-04-22 01:02:58,661 [INFO] bot: ✅ Loaded extension: commands.login +2026-04-22 01:02:58,661 [INFO] bot: ✅ Loaded extension: commands.profile +2026-04-22 01:02:58,663 [INFO] bot: ✅ Loaded extension: commands.recent +2026-04-22 01:02:58,798 [INFO] bot: 🔄 Slash commands synced +2026-04-22 01:03:01,276 [INFO] discord.gateway: Shard ID None has connected to Gateway (Session ID: 8ffd3e5eaf4edabab0603b0294b5b94f). +2026-04-22 01:03:03,284 [INFO] bot: ✅ Logged in as league of legend bot#3877 (ID: 1494467149186465862) +2026-04-22 01:03:03,284 [INFO] bot: Guilds : 1 +2026-04-22 01:03:03,285 [INFO] bot: Ping : 42ms +2026-04-22 01:03:32,882 [ERROR] discord.app_commands.tree: Ignoring exception in command tree +Traceback (most recent call last): + File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/tree.py", line 1138, in wrapper + await self._call(interaction) + File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/tree.py", line 1270, in _call + command, options = self._get_app_command_options(data) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^ + File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/app_commands/tree.py", line 1174, in _get_app_command_options + raise CommandNotFound(name, parents) +discord.app_commands.errors.CommandNotFound: Application command 'ping' not found +2026-04-22 01:03:53,411 [ERROR] discord.app_commands.tree: Ignoring exception in command 'login' +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 883, in _invoke_with_namespace + transformed_values = await self._transform_arguments(interaction, namespace) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 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:20:37,191 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported +2026-04-22 03:20:37,192 [WARNING] discord.client: davey is not installed, voice will NOT be supported +2026-04-22 03:20:37,193 [INFO] discord.client: logging in using static token +2026-04-22 03:20:37,715 [INFO] bot: ✅ Loaded extension: commands.login +2026-04-22 03:20:37,726 [INFO] bot: ✅ Loaded extension: commands.profile +2026-04-22 03:20:37,731 [INFO] bot: ✅ Loaded extension: commands.recent +2026-04-22 03:20:37,899 [INFO] bot: 🔄 Slash commands synced +2026-04-22 03:20:38,668 [INFO] discord.gateway: Shard ID None has connected to Gateway (Session ID: c6e5a2c0cdf32e5c69cdbabeed40a2d4). +2026-04-22 03:20:40,672 [INFO] bot: ✅ Logged in as league of legend bot#3877 (ID: 1494467149186465862) +2026-04-22 03:20:40,674 [INFO] bot: Guilds : 1 +2026-04-22 03:20:40,674 [INFO] bot: Ping : 45ms +2026-04-22 03:21:03,101 [ERROR] discord.app_commands.tree: Ignoring exception in command 'login' +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 883, in _invoke_with_namespace + transformed_values = await self._transform_arguments(interaction, namespace) + ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + 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. diff --git a/bot.py b/bot.py index a367973..91165d9 100644 --- a/bot.py +++ b/bot.py @@ -3,7 +3,7 @@ from discord.ext import commands, tasks import asyncio import os import logging -from datetime import datetime +from datetime import datetime, timezone from dotenv import load_dotenv load_dotenv() @@ -52,8 +52,19 @@ class LoLBot(commands.Bot): except Exception as e: log.error(f"❌ Failed to load {ext}: {e}") + # If SERVER_ID is provided, sync specifically to that guild for instant updates + guild_id = os.getenv("SERVER_ID") + if guild_id: + try: + guild = discord.Object(id=int(guild_id)) + self.tree.copy_global_to(guild=guild) + await self.tree.sync(guild=guild) + log.info(f"🔄 Slash commands synced to guild {guild_id}") + except Exception as e: + log.error(f"❌ Failed to sync to guild {guild_id}: {e}") + await self.tree.sync() - log.info("🔄 Slash commands synced") + log.info("🔄 Global slash commands synced") async def on_ready(self): log.info(f"✅ Logged in as {self.user} (ID: {self.user.id})") @@ -127,11 +138,17 @@ bot = LoLBot() @bot.command(name="sync") @commands.is_owner() -async def sync(ctx): - """Force re-sync slash commands (owner only).""" - await bot.tree.sync() - await ctx.send("✅ Slash commands synced.") - log.info("🔄 Manual sync triggered") +async def sync(ctx, guild: str = None): + """Force re-sync slash commands. Use 'guild' to sync to current guild.""" + if guild == "guild": + bot.tree.copy_global_to(guild=ctx.guild) + await bot.tree.sync(guild=ctx.guild) + await ctx.send(f"✅ Slash commands synced to **{ctx.guild.name}**.") + log.info(f"🔄 Manual sync triggered for guild {ctx.guild.id}") + else: + await bot.tree.sync() + await ctx.send("✅ Global slash commands synced (may take an hour).") + log.info("🔄 Manual global sync triggered") @bot.command(name="reload") @@ -147,17 +164,17 @@ async def reload(ctx, extension: str): log.error(f"Reload failed for {extension}: {e}") -@bot.command(name="ping") -async def ping(ctx): +@bot.tree.command(name="ping", description="Check bot latency") +async def ping(interaction: discord.Interaction): """Check bot latency.""" - await ctx.send(f"🏓 Pong! `{round(bot.latency * 1000)}ms`") + await interaction.response.send_message(f"🏓 Pong! `{round(bot.latency * 1000)}ms`") @bot.command(name="status") @commands.is_owner() async def status(ctx): """Show bot status info (owner only).""" - uptime = datetime.utcnow() - bot.start_time if hasattr(bot, "start_time") else "N/A" + uptime = datetime.now(timezone.utc) - bot.start_time if hasattr(bot, "start_time") else "N/A" embed = discord.Embed(title="🤖 Bot Status", color=0x1A78C2) embed.add_field(name="📡 Ping", value=f"{round(bot.latency * 1000)}ms", inline=True) embed.add_field(name="🏠 Guilds", value=str(len(bot.guilds)), inline=True) @@ -175,7 +192,7 @@ async def main(): return async with bot: - bot.start_time = datetime.utcnow() + bot.start_time = datetime.now(timezone.utc) await bot.start(token) diff --git a/bot_output.log b/bot_output.log new file mode 100644 index 0000000..6bb704a --- /dev/null +++ b/bot_output.log @@ -0,0 +1,30 @@ +2026-04-22 00:54:47,504 [WARNING] discord.client: PyNaCl is not installed, voice will NOT be supported +2026-04-22 00:54:47,505 [WARNING] discord.client: davey is not installed, voice will NOT be supported +/Users/simonzheng/Documents/vscode/lolBot/bot.py:178: DeprecationWarning: datetime.datetime.utcnow() is deprecated and scheduled for removal in a future version. Use timezone-aware objects to represent datetimes in UTC: datetime.datetime.now(datetime.UTC). + bot.start_time = datetime.utcnow() +2026-04-22 00:54:47,506 [INFO] discord.client: logging in using static token +2026-04-22 00:54:47,891 [INFO] bot: ✅ Loaded extension: commands.login +2026-04-22 00:54:47,899 [INFO] bot: ✅ Loaded extension: commands.profile +2026-04-22 00:54:47,900 [INFO] bot: ✅ Loaded extension: commands.recent +2026-04-22 00:54:48,183 [INFO] bot: 🔄 Slash commands synced +2026-04-22 00:54:48,576 [WARNING] bot: ⚠️ Bot disconnected from Discord +Traceback (most recent call last): + File "/Users/simonzheng/Documents/vscode/lolBot/bot.py", line 182, in + asyncio.run(main()) + ~~~~~~~~~~~^^^^^^^^ + File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 194, in run + return runner.run(main) + ~~~~~~~~~~^^^^^^ + File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/runners.py", line 118, in run + return self._loop.run_until_complete(task) + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^ + File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/asyncio/base_events.py", line 720, in run_until_complete + return future.result() + ~~~~~~~~~~~~~^^ + File "/Users/simonzheng/Documents/vscode/lolBot/bot.py", line 179, in main + await bot.start(token) + File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/client.py", line 851, in start + await self.connect(reconnect=reconnect) + File "/Library/Frameworks/Python.framework/Versions/3.13/lib/python3.13/site-packages/discord/client.py", line 775, in connect + raise PrivilegedIntentsRequired(exc.shard_id) from None +discord.errors.PrivilegedIntentsRequired: Shard ID None is requesting privileged intents that have not been explicitly enabled in the developer portal. It is recommended to go to https://discord.com/developers/applications/ and explicitly enable the privileged intents within your application's page. If this is not possible, then consider disabling the privileged intents instead. diff --git a/commands/__pycache__/login.cpython-313.pyc b/commands/__pycache__/login.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..5cb66b29763a7c15d8899e0fdedd04fc2c84825f GIT binary patch literal 4984 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 literal 0 HcmV?d00001 diff --git a/commands/__pycache__/profile.cpython-313.pyc b/commands/__pycache__/profile.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..a712673064c4fa24ba5e67f3c3fa6d89db1b7d20 GIT binary patch literal 5950 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 literal 0 HcmV?d00001 diff --git a/commands/__pycache__/recent.cpython-313.pyc b/commands/__pycache__/recent.cpython-313.pyc new file mode 100644 index 0000000000000000000000000000000000000000..c9f9ed025272f7a1c9d88ae2249a0ee52d75ffd5 GIT binary patch literal 8437 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 literal 0 HcmV?d00001 diff --git a/services/riot_api.py b/services/riot_api.py index f0fffe2..3b277e3 100644 --- a/services/riot_api.py +++ b/services/riot_api.py @@ -67,7 +67,7 @@ async def get_rank(summoner_id: str) -> dict | None: ) -async def get_match_ids(puuid: str, cont: int = 5) -> dict | None: +async def get_match_ids(puuid: str, count: int = 5) -> dict | None: return await riot_get( f"https://{CLUSTER}.api.riotgames.com" f"/lol/match/v5/matches/by-puuid/{puuid}/ids?count={count}" diff --git a/test_all_commands.py b/test_all_commands.py new file mode 100644 index 0000000..ee50af7 --- /dev/null +++ b/test_all_commands.py @@ -0,0 +1,120 @@ +import unittest +import asyncio +from unittest.mock import AsyncMock, MagicMock, patch +import discord +from commands.login import LoginCog +from commands.profile import ProfileCog +from commands.recent import RecentCog + +class TestCommands(unittest.IsolatedAsyncioTestCase): + def setUp(self): + self.bot = MagicMock() + self.login_cog = LoginCog(self.bot) + self.profile_cog = ProfileCog(self.bot) + self.recent_cog = RecentCog(self.bot) + + async def test_login_success(self): + interaction = AsyncMock(spec=discord.Interaction) + interaction.user.id = 12345 + interaction.response.defer = AsyncMock() + interaction.followup.send = AsyncMock() + + with patch("commands.login.save_user") as mock_save, \ + patch("aiohttp.ClientSession.get") as mock_get: + + # Mock Riot Account API response + mock_resp = AsyncMock() + mock_resp.status = 200 + mock_resp.json.return_value = {"puuid": "fake_puuid"} + mock_get.return_value.__aenter__.return_value = mock_resp + + await self.login_cog.login.callback(self.login_cog, interaction, "TestUser#TAG") + + interaction.response.defer.assert_called_once() + mock_save.assert_called_once_with("12345", "fake_puuid", "TestUser", "TAG") + interaction.followup.send.assert_called_once() + self.assertIn("Linked **TestUser#TAG**", interaction.followup.send.call_args[0][0]) + + async def test_login_not_found(self): + interaction = AsyncMock(spec=discord.Interaction) + interaction.response.defer = AsyncMock() + interaction.followup.send = AsyncMock() + + with patch("aiohttp.ClientSession.get") as mock_get: + mock_resp = AsyncMock() + mock_resp.status = 404 + mock_get.return_value.__aenter__.return_value = mock_resp + + await self.login_cog.login.callback(self.login_cog, interaction, "NonExistent#TAG") + + interaction.followup.send.assert_called_once() + self.assertIn("Summoner not found", interaction.followup.send.call_args[0][0]) + + async def test_profile_not_linked(self): + interaction = AsyncMock(spec=discord.Interaction) + interaction.user.id = 12345 + interaction.response.defer = AsyncMock() + interaction.followup.send = AsyncMock() + + with patch("commands.profile.get_user", return_value=None): + await self.profile_cog.profile.callback(self.profile_cog, interaction) + + interaction.followup.send.assert_called_once() + self.assertIn("haven't linked your Riot account", interaction.followup.send.call_args[0][0]) + + async def test_profile_success(self): + interaction = AsyncMock(spec=discord.Interaction) + interaction.user.id = 12345 + interaction.response.defer = AsyncMock() + interaction.followup.send = AsyncMock() + + # (discord_id, puuid, game_name, tag_line) + mock_user = ("12345", "fake_puuid", "TestUser", "TAG") + + with patch("commands.profile.get_user", return_value=mock_user), \ + patch("commands.profile.fetch") as mock_fetch: + + # Mock Summoner Data + mock_fetch.side_effect = [ + {"id": "summoner_id", "summonerLevel": 100}, # Summoner info + [{"queueType": "RANKED_SOLO_5x5", "tier": "GOLD", "rank": "I", "leaguePoints": 50, "wins": 10, "losses": 5}] # League info + ] + + await self.profile_cog.profile.callback(self.profile_cog, interaction) + + interaction.followup.send.assert_called_once() + embed = interaction.followup.send.call_args[1]["embed"] + self.assertEqual(embed.title, "📊 TestUser#TAG") + self.assertIn("Gold I", embed.fields[1].value) + + async def test_recent_success(self): + interaction = AsyncMock(spec=discord.Interaction) + interaction.user.id = 12345 + interaction.response.defer = AsyncMock() + interaction.followup.send = AsyncMock() + + mock_user = ("12345", "fake_puuid", "TestUser", "TAG") + + with patch("commands.recent.get_user", return_value=mock_user), \ + patch("commands.recent.fetch") as mock_fetch, \ + patch("commands.recent.fetch_all") as mock_fetch_all: + + mock_fetch.return_value = ["match1"] + mock_fetch_all.return_value = [{ + "metadata": {"matchId": "match1"}, + "info": { + "participants": [{"puuid": "fake_puuid", "win": True, "championName": "Lux", "kills": 10, "deaths": 2, "assists": 15, "totalMinionsKilled": 100, "neutralMinionsKilled": 10}], + "gameDuration": 1800, + "queueId": 420 + } + }] + + await self.recent_cog.recent.callback(self.recent_cog, interaction, count=1) + + interaction.followup.send.assert_called_once() + embeds = interaction.followup.send.call_args[1]["embeds"] + self.assertEqual(len(embeds), 1) + self.assertIn("Victory — Lux", embeds[0].title) + +if __name__ == "__main__": + unittest.main() diff --git a/user.db b/user.db new file mode 100644 index 0000000000000000000000000000000000000000..82db6d16e366ba77cad96820cbcbf9e721136c1f GIT binary patch literal 12288 zcmeI#ze~eF6u|MjDk_DNZW+DN77@f*FiHk7tr{cPDMS-XAWbws#8p@SHvb<-bEmOm zN1=Ql+&kXAYe$n z6SyjHPCZwdrM zA_?<0nGKffbu-u{+7Xk<%fB*srAb