Improves command synchronization handling

Adds a smart command sync strategy to handle potential command conflicts during bot startup.

This implementation includes:
- Initial attempt at a normal sync.
- Conflict resolution mechanism via Discord's bulk update endpoint if "Entry Point command" errors are encountered.
- Error handling and logging for increased reliability.
This commit is contained in:
MiguVT
2025-08-18 16:29:34 +02:00
parent 8cd6b33d89
commit fa0d410d93

80
main.py
View File

@@ -25,6 +25,7 @@ import discord
import sys
import os
import aiohttp
import asyncio
import update
import logging
import voicelink
@@ -45,7 +46,7 @@ class Translator(discord.app_commands.Translator):
async def translate(self, string: discord.app_commands.locale_str, locale: discord.Locale, context: discord.app_commands.TranslationContext):
locale_key = str(locale)
if locale_key in func.LOCAL_LANGS:
translated_text = func.LOCAL_LANGS[locale_key].get(string.message)
@@ -53,9 +54,9 @@ class Translator(discord.app_commands.Translator):
missing_translations = func.MISSING_TRANSLATOR.setdefault(locale_key, [])
if string.message not in missing_translations:
missing_translations.append(string.message)
return translated_text
return None
class Vocard(commands.Bot):
@@ -89,13 +90,13 @@ class Vocard(commands.Bot):
elif message.attachments:
for attachment in message.attachments:
await cmd(ctx, query=attachment.url)
except Exception as e:
await func.send(ctx, str(e), ephemeral=True)
finally:
return await message.delete()
await self.process_commands(message)
async def connect_db(self) -> None:
@@ -110,19 +111,19 @@ class Vocard(commands.Bot):
except Exception as e:
func.logger.error("Not able to connect MongoDB! Reason:", exc_info=e)
exit()
func.SETTINGS_DB = func.MONGO_DB[db_name]["Settings"]
func.USERS_DB = func.MONGO_DB[db_name]["Users"]
async def setup_hook(self) -> None:
func.langs_setup()
# Connecting to MongoDB
await self.connect_db()
# Set translator
await self.tree.set_translator(Translator())
# Loading all the module in `cogs` folder
for module in os.listdir(func.ROOT_DIR + '/cogs'):
if module.endswith('.py'):
@@ -139,12 +140,63 @@ class Vocard(commands.Bot):
except Exception as e:
func.logger.error(f"Cannot connected to dashboard! - Reason: {e}")
# Smart command sync strategy
await self._smart_command_sync()
# Update version tracking
if not func.settings.version or func.settings.version != update.__version__:
await self.tree.sync()
func.update_json("settings.json", new_data={"version": update.__version__})
for locale_key, values in func.MISSING_TRANSLATOR.items():
func.logger.warning(f'Missing translation for "{", ".join(values)}" in "{locale_key}"')
async def _smart_command_sync(self):
"""Production-ready command sync with intelligent error handling"""
try:
# Method 1: Try normal sync first
await self.tree.sync()
func.logger.info("Commands synced successfully")
except discord.HTTPException as e:
if "Entry Point command" in str(e) or e.status == 400:
func.logger.warning("Command sync conflict detected, attempting resolution...")
await self._resolve_command_conflicts()
else:
func.logger.error(f"Command sync failed: {e}")
raise
async def _resolve_command_conflicts(self):
"""Clean resolution of command conflicts"""
try:
# Get application info
app_info = await self.application_info()
# Use Discord's bulk update endpoint to clear all commands
url = f"https://discord.com/api/v10/applications/{app_info.id}/commands"
headers = {"Authorization": f"Bot {func.settings.token}"}
async with aiohttp.ClientSession() as session:
# Send empty array to clear all commands
async with session.put(url, json=[], headers=headers) as resp:
if resp.status == 200:
func.logger.info("Cleared all global commands")
# Wait for Discord to process the change
await asyncio.sleep(2)
# Clear local command tree and resync
self.tree.clear_commands(guild=None)
await self.tree.sync()
func.logger.info("Commands resynced after conflict resolution")
else:
func.logger.error(f"Failed to clear commands: {resp.status}")
raise discord.HTTPException(resp, "Failed to resolve command conflicts")
except Exception as e:
func.logger.error(f"Error resolving command conflicts: {e}")
raise
async def on_ready(self):
func.logger.info("------------------")
func.logger.info(f"Logging As {self.user}")
@@ -162,7 +214,7 @@ class Vocard(commands.Bot):
error = getattr(exception, 'original', exception)
if ctx.interaction:
error = getattr(error, 'original', error)
if isinstance(error, (commands.CommandNotFound, aiohttp.client_exceptions.ClientOSError, discord.errors.NotFound)):
return
@@ -184,7 +236,7 @@ class Vocard(commands.Bot):
elif not issubclass(error.__class__, voicelink.VoicelinkException):
error = await func.get_lang(ctx.guild.id, "unknownException") + func.settings.invite_link
func.logger.error(f"An unexpected error occurred in the {ctx.command.name} command on the {ctx.guild.name}({ctx.guild.id}).", exc_info=exception)
try:
return await ctx.reply(error, ephemeral=True)
except:
@@ -225,7 +277,7 @@ if (LOG_FILE := LOG_SETTINGS.get("file", {})).get("enable", True):
for log_name, log_level in LOG_SETTINGS.get("level", {}).items():
_logger = logging.getLogger(log_name)
_logger.setLevel(log_level)
# Setup the bot object
intents = discord.Intents.default()
intents.message_content = False if func.settings.bot_prefix is None else True
@@ -244,4 +296,4 @@ bot = Vocard(
if __name__ == "__main__":
update.check_version(with_msg=True)
bot.run(func.settings.token, root_logger=True)
bot.run(func.settings.token, root_logger=True)