Improve Discord gateway error handling and logging

This commit is contained in:
aydnOktay 2026-03-09 14:33:21 +03:00
parent 7b63a787b3
commit d82fcef91b

View file

@ -72,11 +72,11 @@ class DiscordAdapter(BasePlatformAdapter):
async def connect(self) -> bool: async def connect(self) -> bool:
"""Connect to Discord and start receiving events.""" """Connect to Discord and start receiving events."""
if not DISCORD_AVAILABLE: if not DISCORD_AVAILABLE:
print(f"[{self.name}] discord.py not installed. Run: pip install discord.py") logger.error("[%s] discord.py not installed. Run: pip install discord.py", self.name)
return False return False
if not self.config.token: if not self.config.token:
print(f"[{self.name}] No bot token configured") logger.error("[%s] No bot token configured", self.name)
return False return False
try: try:
@ -105,7 +105,7 @@ class DiscordAdapter(BasePlatformAdapter):
# Register event handlers # Register event handlers
@self._client.event @self._client.event
async def on_ready(): async def on_ready():
print(f"[{adapter_self.name}] Connected as {adapter_self._client.user}") logger.info("[%s] Connected as %s", adapter_self.name, adapter_self._client.user)
# Resolve any usernames in the allowed list to numeric IDs # Resolve any usernames in the allowed list to numeric IDs
await adapter_self._resolve_allowed_usernames() await adapter_self._resolve_allowed_usernames()
@ -113,9 +113,9 @@ class DiscordAdapter(BasePlatformAdapter):
# Sync slash commands with Discord # Sync slash commands with Discord
try: try:
synced = await adapter_self._client.tree.sync() synced = await adapter_self._client.tree.sync()
print(f"[{adapter_self.name}] Synced {len(synced)} slash command(s)") logger.info("[%s] Synced %d slash command(s)", adapter_self.name, len(synced))
except Exception as e: except Exception as e: # pragma: no cover - defensive logging
print(f"[{adapter_self.name}] Slash command sync failed: {e}") logger.warning("[%s] Slash command sync failed: %s", adapter_self.name, e, exc_info=True)
adapter_self._ready_event.set() adapter_self._ready_event.set()
@self._client.event @self._client.event
@ -138,10 +138,10 @@ class DiscordAdapter(BasePlatformAdapter):
return True return True
except asyncio.TimeoutError: except asyncio.TimeoutError:
print(f"[{self.name}] Timeout waiting for connection") logger.error("[%s] Timeout waiting for connection to Discord", self.name, exc_info=True)
return False return False
except Exception as e: except Exception as e: # pragma: no cover - defensive logging
print(f"[{self.name}] Failed to connect: {e}") logger.error("[%s] Failed to connect to Discord: %s", self.name, e, exc_info=True)
return False return False
async def disconnect(self) -> None: async def disconnect(self) -> None:
@ -149,13 +149,13 @@ class DiscordAdapter(BasePlatformAdapter):
if self._client: if self._client:
try: try:
await self._client.close() await self._client.close()
except Exception as e: except Exception as e: # pragma: no cover - defensive logging
print(f"[{self.name}] Error during disconnect: {e}") logger.warning("[%s] Error during disconnect: %s", self.name, e, exc_info=True)
self._running = False self._running = False
self._client = None self._client = None
self._ready_event.clear() self._ready_event.clear()
print(f"[{self.name}] Disconnected") logger.info("[%s] Disconnected", self.name)
async def send( async def send(
self, self,
@ -204,7 +204,8 @@ class DiscordAdapter(BasePlatformAdapter):
raw_response={"message_ids": message_ids} raw_response={"message_ids": message_ids}
) )
except Exception as e: except Exception as e: # pragma: no cover - defensive logging
logger.error("[%s] Failed to send Discord message: %s", self.name, e, exc_info=True)
return SendResult(success=False, error=str(e)) return SendResult(success=False, error=str(e))
async def edit_message( async def edit_message(
@ -226,7 +227,8 @@ class DiscordAdapter(BasePlatformAdapter):
formatted = formatted[:self.MAX_MESSAGE_LENGTH - 3] + "..." formatted = formatted[:self.MAX_MESSAGE_LENGTH - 3] + "..."
await msg.edit(content=formatted) await msg.edit(content=formatted)
return SendResult(success=True, message_id=message_id) return SendResult(success=True, message_id=message_id)
except Exception as e: except Exception as e: # pragma: no cover - defensive logging
logger.error("[%s] Failed to edit Discord message %s: %s", self.name, message_id, e, exc_info=True)
return SendResult(success=False, error=str(e)) return SendResult(success=False, error=str(e))
async def send_voice( async def send_voice(
@ -263,8 +265,8 @@ class DiscordAdapter(BasePlatformAdapter):
) )
return SendResult(success=True, message_id=str(msg.id)) return SendResult(success=True, message_id=str(msg.id))
except Exception as e: except Exception as e: # pragma: no cover - defensive logging
print(f"[{self.name}] Failed to send audio: {e}") logger.error("[%s] Failed to send audio, falling back to base adapter: %s", self.name, e, exc_info=True)
return await super().send_voice(chat_id, audio_path, caption, reply_to) return await super().send_voice(chat_id, audio_path, caption, reply_to)
async def send_image_file( async def send_image_file(
@ -300,8 +302,8 @@ class DiscordAdapter(BasePlatformAdapter):
) )
return SendResult(success=True, message_id=str(msg.id)) return SendResult(success=True, message_id=str(msg.id))
except Exception as e: except Exception as e: # pragma: no cover - defensive logging
print(f"[{self.name}] Failed to send local image: {e}") logger.error("[%s] Failed to send local image, falling back to base adapter: %s", self.name, e, exc_info=True)
return await super().send_image_file(chat_id, image_path, caption, reply_to) return await super().send_image_file(chat_id, image_path, caption, reply_to)
async def send_image( async def send_image(
@ -353,10 +355,19 @@ class DiscordAdapter(BasePlatformAdapter):
return SendResult(success=True, message_id=str(msg.id)) return SendResult(success=True, message_id=str(msg.id))
except ImportError: except ImportError:
print(f"[{self.name}] aiohttp not installed, falling back to URL. Run: pip install aiohttp") logger.warning(
"[%s] aiohttp not installed, falling back to URL. Run: pip install aiohttp",
self.name,
exc_info=True,
)
return await super().send_image(chat_id, image_url, caption, reply_to) return await super().send_image(chat_id, image_url, caption, reply_to)
except Exception as e: except Exception as e: # pragma: no cover - defensive logging
print(f"[{self.name}] Failed to send image attachment, falling back to URL: {e}") logger.error(
"[%s] Failed to send image attachment, falling back to URL: %s",
self.name,
e,
exc_info=True,
)
return await super().send_image(chat_id, image_url, caption, reply_to) return await super().send_image(chat_id, image_url, caption, reply_to)
async def send_typing(self, chat_id: str) -> None: async def send_typing(self, chat_id: str) -> None:
@ -404,7 +415,8 @@ class DiscordAdapter(BasePlatformAdapter):
"guild_id": str(channel.guild.id) if hasattr(channel, "guild") and channel.guild else None, "guild_id": str(channel.guild.id) if hasattr(channel, "guild") and channel.guild else None,
"guild_name": channel.guild.name if hasattr(channel, "guild") and channel.guild else None, "guild_name": channel.guild.name if hasattr(channel, "guild") and channel.guild else None,
} }
except Exception as e: except Exception as e: # pragma: no cover - defensive logging
logger.error("[%s] Failed to get chat info for %s: %s", self.name, chat_id, e, exc_info=True)
return {"name": str(chat_id), "type": "dm", "error": str(e)} return {"name": str(chat_id), "type": "dm", "error": str(e)}
async def _resolve_allowed_usernames(self) -> None: async def _resolve_allowed_usernames(self) -> None: