feat: add DictionaryCog for word definition and dictionary lookup commands
This commit is contained in:
parent
62c752a569
commit
632a876d6c
149
cogs/dictionary_cog.py
Normal file
149
cogs/dictionary_cog.py
Normal file
@ -0,0 +1,149 @@
|
||||
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))
|
@ -7,6 +7,7 @@ import asyncio
|
||||
COG_DISPLAY_NAMES = {
|
||||
"AICog": "🤖 AI Chat",
|
||||
"AudioCog": "🎵 Audio Player",
|
||||
"DictionaryCog": "📖 Dictionary",
|
||||
"GamesCog": "🎮 Games",
|
||||
"HelpCog": "❓ Help",
|
||||
"MultiConversationCog": "🤖 Multi-Conversation AI Chat",
|
||||
|
@ -105,6 +105,7 @@ class NeruBot(commands.Bot):
|
||||
"cogs.shell_command_cog",
|
||||
"cogs.marriage_cog",
|
||||
"cogs.upload_cog",
|
||||
"cogs.dictionary_cog",
|
||||
]
|
||||
|
||||
# Load each cog individually
|
||||
|
Loading…
x
Reference in New Issue
Block a user