import discord from discord.ext import commands from discord import app_commands import aiohttp import logging from typing import Optional, List, Dict, Any log = logging.getLogger(__name__) class DictionaryCog(commands.Cog, name="Dictionary"): """Cog for word definition and dictionary lookup commands""" def __init__(self, bot): self.bot = bot self.api_base_url = "https://api.dictionaryapi.dev/api/v2/entries/en" async def _fetch_definition(self, word: str) -> Optional[Dict[str, Any]]: """Fetch word definition from the Free Dictionary API.""" try: async with aiohttp.ClientSession() as session: url = f"{self.api_base_url}/{word.lower()}" async with session.get(url) as response: if response.status == 200: data = await response.json() return data[0] if data else None elif response.status == 404: return None else: log.error( f"Dictionary API returned status {response.status} for word '{word}'" ) return None except Exception as e: log.error(f"Error fetching definition for '{word}': {e}") return None def _format_definition_embed( self, word: str, data: Dict[str, Any] ) -> discord.Embed: """Format the dictionary data into a Discord embed.""" embed = discord.Embed( title=f"📖 Definition: {data.get('word', word).title()}", color=discord.Color.blue(), ) # Add phonetic pronunciation if available phonetics = data.get("phonetics", []) if phonetics: for phonetic in phonetics: if phonetic.get("text"): embed.add_field( name="🔊 Pronunciation", value=phonetic["text"], inline=True ) break # Add meanings meanings = data.get("meanings", []) definition_count = 0 for meaning in meanings[:3]: # Limit to first 3 parts of speech part_of_speech = meaning.get("partOfSpeech", "Unknown") definitions = meaning.get("definitions", []) if definitions: definition_text = definitions[0].get( "definition", "No definition available" ) example = definitions[0].get("example") field_value = f"**{definition_text}**" if example: field_value += f"\n*Example: {example}*" embed.add_field( name=f"📝 {part_of_speech.title()}", value=field_value, inline=False ) definition_count += 1 # Add etymology if available etymology = data.get("etymology") if etymology: embed.add_field( name="📚 Etymology", value=etymology[:200] + "..." if len(etymology) > 200 else etymology, inline=False, ) # Add source attribution embed.set_footer(text="Powered by Free Dictionary API") return embed async def _define_logic(self, word: str) -> Dict[str, Any]: """Core logic for the define command.""" if not word or len(word.strip()) == 0: return {"error": "Please provide a word to define.", "embed": None} # Clean the word input clean_word = word.strip().lower() # Fetch definition definition_data = await self._fetch_definition(clean_word) if definition_data is None: return { "error": f"❌ Sorry, I couldn't find a definition for '{word}'. Please check the spelling and try again.", "embed": None, } # Create embed embed = self._format_definition_embed(word, definition_data) return {"error": None, "embed": embed} # --- Prefix Command --- @commands.command(name="define", aliases=["def", "definition"]) async def define(self, ctx: commands.Context, *, word: str = None): """Look up the definition of a word.""" if word is None: await ctx.reply("Please provide a word to define. Usage: `!define `") return result = await self._define_logic(word) if result["error"]: await ctx.reply(result["error"]) else: await ctx.reply(embed=result["embed"]) # --- Slash Command --- @app_commands.command(name="define", description="Look up the definition of a word") @app_commands.describe(word="The word you want to define") async def define_slash(self, interaction: discord.Interaction, word: str): """Slash command for word definition lookup.""" await interaction.response.defer() result = await self._define_logic(word) if result["error"]: await interaction.followup.send(result["error"]) else: await interaction.followup.send(embed=result["embed"]) async def setup(bot: commands.Bot): await bot.add_cog(DictionaryCog(bot))