From 89fc69557ae5c84a975b20ef7834b8bfca790941 Mon Sep 17 00:00:00 2001 From: Hash Borgir Date: Thu, 16 Oct 2025 00:38:47 -0500 Subject: [PATCH] Urban Dictionary plugin added --- funguy.py | 2 +- plugins/loadplugin.py | 3 +- plugins/urbandictionary.py | 193 +++++++++++++++++++++++++++++++++++++ 3 files changed, 196 insertions(+), 2 deletions(-) create mode 100644 plugins/urbandictionary.py diff --git a/funguy.py b/funguy.py index fa4aeaf..34c8d8f 100755 --- a/funguy.py +++ b/funguy.py @@ -20,7 +20,7 @@ from plugins.config import FunguyConfig # Whitelist of allowed plugins to prevent arbitrary code execution ALLOWED_PLUGINS = {'ai', 'config', 'cron', 'date', 'fortune', 'help', 'isup', 'karma', 'loadplugin', 'plugins', 'proxy', 'sd_text', 'stable-diffusion', - 'xkcd', 'youtube-preview', 'youtube-search', 'weather'} + 'xkcd', 'youtube-preview', 'youtube-search', 'weather', 'urbandictionary'} class FunguyBot: """ diff --git a/plugins/loadplugin.py b/plugins/loadplugin.py index 592e609..a0b5bd5 100644 --- a/plugins/loadplugin.py +++ b/plugins/loadplugin.py @@ -56,7 +56,8 @@ async def load_plugin(plugin_name): 'xkcd': 'plugins.xkcd', 'youtube-preview': 'plugins.youtube-preview', 'youtube-search': 'plugins.youtube-search', - 'weather': 'plugins.weather' + 'weather': 'plugins.weather', + 'urbandictionary': 'plugins.urbandictionary' } # Get the module path from the mapping diff --git a/plugins/urbandictionary.py b/plugins/urbandictionary.py new file mode 100644 index 0000000..08b364d --- /dev/null +++ b/plugins/urbandictionary.py @@ -0,0 +1,193 @@ +""" +This plugin provides a command to fetch definitions from Urban Dictionary. +""" + +import logging +import requests +import simplematrixbotlib as botlib +import html + +URBAN_API_URL = "https://api.urbandictionary.com/v0/define" +RANDOM_API_URL = "https://api.urbandictionary.com/v0/random" + + +def format_definition(term, definition, example, author, thumbs_up, thumbs_down, permalink, index=None, total=None): + """ + Format an Urban Dictionary definition for display. + + Args: + term (str): The term being defined. + definition (str): The definition text. + example (str): Example usage. + author (str): Author of the definition. + thumbs_up (int): Number of upvotes. + thumbs_down (int): Number of downvotes. + permalink (str): URL to the definition. + index (int, optional): Current definition index. + total (int, optional): Total number of definitions. + + Returns: + str: Formatted HTML message. + """ + # Clean up the text - Urban Dictionary uses [brackets] for links + definition = definition.replace('[', '').replace(']', '') + example = example.replace('[', '').replace(']', '') + + # Escape any HTML that might be in the original text + term = html.escape(term) + author = html.escape(author) + + # Build the message + header = f"📖 Urban Dictionary: {term}" + if index is not None and total is not None: + header += f" (Definition {index}/{total})" + + message = f"""{header} +Definition: +{definition} +""" + if example and example.strip(): + message += f""" +Example: +{example} +""" + message += f""" +Author: {author} | 👍 {thumbs_up} 👎 {thumbs_down} +View on Urban Dictionary +""" + + return message + + +async def handle_command(room, message, bot, prefix, config): + """ + Function to handle the !ud (Urban Dictionary) command. + + Args: + room (Room): The Matrix room where the command was invoked. + message (RoomMessage): The message object containing the command. + bot (Bot): The bot object. + prefix (str): The command prefix. + config (dict): Configuration parameters. + + Returns: + None + """ + match = botlib.MessageMatch(room, message, bot, prefix) + if match.is_not_from_this_bot() and match.prefix() and match.command("ud"): + logging.info("Received !ud command") + + args = match.args() + + try: + # Case 1: No arguments - get random definition + if len(args) == 0: + logging.info("Fetching random Urban Dictionary definition") + response = requests.get(RANDOM_API_URL, timeout=10) + response.raise_for_status() + data = response.json() + + if not data.get('list'): + await bot.api.send_text_message(room.room_id, "No random definition found.") + return + + # Get first random entry + entry = data['list'][0] + formatted = format_definition( + term=entry['word'], + definition=entry['definition'], + example=entry.get('example', ''), + author=entry['author'], + thumbs_up=entry['thumbs_up'], + thumbs_down=entry['thumbs_down'], + permalink=entry['permalink'] + ) + + await bot.api.send_markdown_message(room.room_id, formatted) + logging.info(f"Sent random definition: {entry['word']}") + return + + # Case 2: One or more arguments - search for term + # Check if last argument is a number (definition index) + index = None + search_term = ' '.join(args) + + if args[-1].isdigit(): + index = int(args[-1]) + search_term = ' '.join(args[:-1]) + + if not search_term: + await bot.api.send_text_message( + room.room_id, + "Usage: !ud [term] [index]\nExamples:\n !ud - random definition\n !ud yeet - first definition of 'yeet'\n !ud yeet 2 - second definition of 'yeet'" + ) + return + + logging.info(f"Searching Urban Dictionary for: {search_term}") + params = {'term': search_term} + response = requests.get(URBAN_API_URL, params=params, timeout=10) + response.raise_for_status() + data = response.json() + + definitions = data.get('list', []) + + if not definitions: + await bot.api.send_text_message( + room.room_id, + f"No definition found for '{search_term}'" + ) + logging.info(f"No definition found for: {search_term}") + return + + total = len(definitions) + + # If no index specified, use first definition + if index is None: + index = 1 + + # Validate index + if index < 1 or index > total: + await bot.api.send_text_message( + room.room_id, + f"Invalid index. '{search_term}' has {total} definition(s). Use !ud {search_term} [1-{total}]" + ) + return + + # Get the requested definition (convert to 0-based index) + entry = definitions[index - 1] + + formatted = format_definition( + term=entry['word'], + definition=entry['definition'], + example=entry.get('example', ''), + author=entry['author'], + thumbs_up=entry['thumbs_up'], + thumbs_down=entry['thumbs_down'], + permalink=entry['permalink'], + index=index, + total=total + ) + + await bot.api.send_markdown_message(room.room_id, formatted) + logging.info(f"Sent definition {index}/{total} for: {search_term}") + + except requests.exceptions.Timeout: + await bot.api.send_text_message( + room.room_id, + "Request timed out. Urban Dictionary may be slow or unavailable." + ) + logging.error("Urban Dictionary API timeout") + + except requests.exceptions.RequestException as e: + await bot.api.send_text_message( + room.room_id, + f"Error fetching from Urban Dictionary: {e}" + ) + logging.error(f"Error fetching from Urban Dictionary: {e}") + + except Exception as e: + await bot.api.send_text_message( + room.room_id, + "An error occurred while processing the Urban Dictionary request." + ) + logging.error(f"Unexpected error in Urban Dictionary plugin: {e}", exc_info=True)