discordbot/cogs/dictionary_cog.py

150 lines
5.3 KiB
Python

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 <word>`")
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))