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