diff options
Diffstat (limited to 'bot')
-rw-r--r-- | bot/seasons/evergreen/fun.py | 57 | ||||
-rw-r--r-- | bot/utils/__init__.py | 56 |
2 files changed, 109 insertions, 4 deletions
diff --git a/bot/seasons/evergreen/fun.py b/bot/seasons/evergreen/fun.py index ce3484f7..87077f36 100644 --- a/bot/seasons/evergreen/fun.py +++ b/bot/seasons/evergreen/fun.py @@ -2,20 +2,34 @@ import logging import random from discord.ext import commands +from discord.ext.commands import Bot, Cog, Context, MessageConverter +from bot import utils from bot.constants import Emojis log = logging.getLogger(__name__) +UWU_WORDS = { + "fi": "fwi", + "l": "w", + "r": "w", + "th": "d", + "thing": "fing", + "tho": "fo", + "you're": "yuw'we", + "your": "yur", + "you": "yuw", +} -class Fun(commands.Cog): + +class Fun(Cog): """A collection of general commands for fun.""" - def __init__(self, bot): + def __init__(self, bot: Bot) -> None: self.bot = bot @commands.command() - async def roll(self, ctx, num_rolls: int = 1): + async def roll(self, ctx: Context, num_rolls: int = 1) -> None: """Outputs a number of random dice emotes (up to 6).""" output = "" if num_rolls > 6: @@ -27,8 +41,43 @@ class Fun(commands.Cog): output += getattr(Emojis, terning, '') await ctx.send(output) + @commands.command(name="uwu", aliases=("uwuwize", "uwuify",)) + async def uwu_command(self, ctx: Context, *, text: str) -> None: + """Converts a given `text` into it's uwu equivalent.""" + text = await Fun.get_discord_message(ctx, text) + converted = utils.replace_many(text, UWU_WORDS, ignore_case=True, match_case=True) + await ctx.send(f">>> {converted}") + + @commands.command(name="randomcase", aliases=("rcase", "randomcaps", "rcaps",)) + async def randomcase_command(self, ctx: Context, *, text: str) -> None: + """Randomly converts the casing of a given `text`.""" + text = await Fun.get_discord_message(ctx, text) + converted = ( + char.upper() if round(random.random()) else char.lower() for char in text + ) + await ctx.send(f">>> {''.join(converted)}") + + @staticmethod + async def get_discord_message(ctx: Context, text: str) -> str: + """ + Attempts to convert a given `text` to a discord Message object, then return the contents. + + Useful if the user enters a link or an id to a valid Discord message, because the contents + of the message get returned. + + Returns `text` if the conversion fails. + """ + try: + message = await MessageConverter().convert(ctx, text) + except commands.BadArgument: + log.debug(f"Input '{text:.20}...' is not a valid Discord Message") + else: + text = message.content + finally: + return text + -def setup(bot): +def setup(bot) -> None: """Fun Cog load.""" bot.add_cog(Fun(bot)) log.info("Fun cog loaded") diff --git a/bot/utils/__init__.py b/bot/utils/__init__.py index 15c4b5db..3249a9cf 100644 --- a/bot/utils/__init__.py +++ b/bot/utils/__init__.py @@ -1,4 +1,6 @@ import asyncio +import re +import string from typing import List import discord @@ -71,3 +73,57 @@ async def disambiguate( return entries[index - 1] except IndexError: raise BadArgument('Invalid choice.') + + +def replace_many( + sentence: str, replacements: dict, *, ignore_case: bool = False, match_case: bool = False +) -> str: + """ + Replaces multiple substrings in a string given a mapping of strings. + + By default replaces long strings before short strings, and lowercase before uppercase. + Example: + var = replace_many("This is a sentence", {"is": "was", "This": "That"}) + assert var == "That was a sentence" + + If `ignore_case` is given, does a case insensitive match. + Example: + var = replace_many("THIS is a sentence", {"IS": "was", "tHiS": "That"}, ignore_case=True) + assert var == "That was a sentence" + + If `match_case` is given, matches the case of the replacement with the replaced word. + Example: + var = replace_many( + "This IS a sentence", {"is": "was", "this": "that"}, ignore_case=True, match_case=True + ) + assert var == "That WAS a sentence" + """ + if ignore_case: + replacements = dict( + (word.lower(), replacement) for word, replacement in replacements.items() + ) + + words_to_replace = sorted(replacements, key=lambda s: (-len(s), s)) + + # Join and compile words to replace into a regex + pattern = "|".join(re.escape(word) for word in words_to_replace) + regex = re.compile(pattern, re.I if ignore_case else 0) + + def _repl(match): + """Returns replacement depending on `ignore_case` and `match_case`""" + word = match.group(0) + replacement = replacements[word.lower() if ignore_case else word] + + if not match_case: + return replacement + + # Clean punctuation from word so string methods work + cleaned_word = word.translate(str.maketrans('', '', string.punctuation)) + if cleaned_word.isupper(): + return replacement.upper() + elif cleaned_word[0].isupper(): + return replacement.capitalize() + else: + return replacement.lower() + + return regex.sub(_repl, sentence) |