From 0794f26ea267c355e106366a1226f3e986a682fc Mon Sep 17 00:00:00 2001 From: Gintautas Kazlauskas Date: Tue, 21 Sep 2021 20:16:45 +0300 Subject: Replaced the old implementation for uwuification with a smarter approach --- bot/exts/fun/fun.py | 31 ------------------------------- 1 file changed, 31 deletions(-) (limited to 'bot/exts/fun/fun.py') diff --git a/bot/exts/fun/fun.py b/bot/exts/fun/fun.py index b148f1f3..773c2ffb 100644 --- a/bot/exts/fun/fun.py +++ b/bot/exts/fun/fun.py @@ -17,20 +17,6 @@ from bot.utils import helpers log = logging.getLogger(__name__) -UWU_WORDS = { - "fi": "fwi", - "l": "w", - "r": "w", - "some": "sum", - "th": "d", - "thing": "fing", - "tho": "fo", - "you're": "yuw'we", - "your": "yur", - "you": "yuw", -} - - def caesar_cipher(text: str, offset: int) -> Iterable[str]: """ Implements a lazy Caesar Cipher algorithm. @@ -74,23 +60,6 @@ class Fun(Cog): else: raise BadArgument(f"`{Client.prefix}roll` only supports between 1 and 6 rolls.") - @commands.command(name="uwu", aliases=("uwuwize", "uwuify",)) - async def uwu_command(self, ctx: Context, *, text: clean_content(fix_channel_mentions=True)) -> None: - """Converts a given `text` into it's uwu equivalent.""" - conversion_func = functools.partial( - utils.replace_many, replacements=UWU_WORDS, ignore_case=True, match_case=True - ) - text, embed = await Fun._get_text_and_embed(ctx, text) - # Convert embed if it exists - if embed is not None: - embed = Fun._convert_embed(conversion_func, embed) - converted_text = conversion_func(text) - converted_text = helpers.suppress_links(converted_text) - # Don't put >>> if only embed present - if converted_text: - converted_text = f">>> {converted_text.lstrip('> ')}" - await ctx.send(content=converted_text, embed=embed) - @commands.command(name="randomcase", aliases=("rcase", "randomcaps", "rcaps",)) async def randomcase_command(self, ctx: Context, *, text: clean_content(fix_channel_mentions=True)) -> None: """Randomly converts the casing of a given `text`.""" -- cgit v1.2.3 From ae1b43e1187f0d787ddd39f56a8c568517e46db8 Mon Sep 17 00:00:00 2001 From: Gintautas Kazlauskas Date: Wed, 22 Sep 2021 00:36:25 +0300 Subject: Linting --- bot/exts/fun/fun.py | 3 +-- bot/exts/fun/uwu.py | 40 ++++++++++++++++++++++++---------------- 2 files changed, 25 insertions(+), 18 deletions(-) (limited to 'bot/exts/fun/fun.py') diff --git a/bot/exts/fun/fun.py b/bot/exts/fun/fun.py index 773c2ffb..a27ad85f 100644 --- a/bot/exts/fun/fun.py +++ b/bot/exts/fun/fun.py @@ -1,4 +1,3 @@ -import functools import json import logging import random @@ -10,13 +9,13 @@ from discord import Embed, Message from discord.ext import commands from discord.ext.commands import BadArgument, Cog, Context, MessageConverter, clean_content -from bot import utils from bot.bot import Bot from bot.constants import Client, Colours, Emojis from bot.utils import helpers log = logging.getLogger(__name__) + def caesar_cipher(text: str, offset: int) -> Iterable[str]: """ Implements a lazy Caesar Cipher algorithm. diff --git a/bot/exts/fun/uwu.py b/bot/exts/fun/uwu.py index a6be7c3d..ef838d56 100644 --- a/bot/exts/fun/uwu.py +++ b/bot/exts/fun/uwu.py @@ -1,8 +1,5 @@ -#!/usr/bin/env python3 - import re import random -import re from functools import partial from bot.utils import helpers @@ -11,7 +8,6 @@ from bot.bot import Bot from bot.exts.fun.fun import Fun -from discord import Message from discord.ext import commands from discord.ext.commands import Cog, Context, clean_content @@ -46,7 +42,7 @@ EMOJI_LUT = [ "( ͡o ω ͡o )", "ʘwʘ", ":3", - ":3", # important enough to have twice + ":3", # important enough to have twice "XD", "nyaa~~", "mya", @@ -66,60 +62,71 @@ EMOJI_LUT = [ wepwace_regex = re.compile(r"(? str: for word in WEPWACE_HASH: input_string = input_string.replace(word, WEPWACE_HASH[word]) return input_string + def char_replace(input_string: str) -> str: return wepwace_regex.sub("w", input_string) + # Stuttering stutter_regex = re.compile(r"(\s)([a-zA-Z])") stutter_subst = "\\g<1>\\g<2>-\\g<2>" + def stutter(strength: float, input_string: str): - return stutter_regex.sub(partial(stutter_replace, strength=strength), input_string, 0) + return stutter_regex.sub(partial(stutter_replace, strength=strength), input_string, 0) -def stutter_replace(match, strength = 0.0): + +def stutter_replace(match, strength=0.0): match_string = match.string[slice(*match.span())] if random.random() < strength: char = match_string[-1] return f"{match_string}-{char}" return match_string + # Nyaification nya_regex = re.compile(r"n([aeou])([^aeiou])") nya_subst = "ny\\g<1>\\g<2>" + def nyaify(input_string): return nya_regex.sub(nya_subst, input_string, 0) + # Emoji punctuation_regex = re.compile(r"\s+") + def emoji(strength: float, input_string: str): - return punctuation_regex.sub(partial(emoji_replace, strength=strength), input_string, 0) + return punctuation_regex.sub(partial(emoji_replace, strength=strength), input_string, 0) -def emoji_replace(match, strength = 0.0): +def emoji_replace(match, strength=0.0): match_string = match.string[slice(*match.span())] if random.random() < strength: return f" {EMOJI_LUT[random.randint(0, len(EMOJI_LUT) - 1)]} " return match_string + # Main def uwuify(input_string: str, *, stutter_strength: float = 0.2, emoji_strength: float = 0.2) -> str: - input_string = input_string.lower() - input_string = word_replace(input_string) - input_string = nyaify(input_string) - input_string = char_replace(input_string) - input_string = stutter(stutter_strength, input_string) - input_string = emoji(emoji_strength, input_string) - return input_string + input_string = input_string.lower() + input_string = word_replace(input_string) + input_string = nyaify(input_string) + input_string = char_replace(input_string) + input_string = stutter(stutter_strength, input_string) + input_string = emoji(emoji_strength, input_string) + return input_string + class Uwu(Cog): """ @@ -142,6 +149,7 @@ class Uwu(Cog): converted_text = f">>> {converted_text.lstrip('> ')}" await ctx.send(content=converted_text, embed=embed) + def setup(bot: Bot) -> None: """Load the uwu cog.""" bot.add_cog(Uwu(bot)) -- cgit v1.2.3 From a7624603983a7d04fb40de78066ee421ded5e36c Mon Sep 17 00:00:00 2001 From: Diabolical5777 <84365102+Diabolical5777@users.noreply.github.com> Date: Wed, 27 Jul 2022 16:37:24 +0400 Subject: Add jokes command (#1081) Add jokes command --- bot/exts/fun/fun.py | 10 ++++++++-- poetry.lock | 17 +++++++++++++++-- pyproject.toml | 1 + 3 files changed, 24 insertions(+), 4 deletions(-) (limited to 'bot/exts/fun/fun.py') diff --git a/bot/exts/fun/fun.py b/bot/exts/fun/fun.py index a27ad85f..9ec9b9ee 100644 --- a/bot/exts/fun/fun.py +++ b/bot/exts/fun/fun.py @@ -3,8 +3,9 @@ import logging import random from collections.abc import Iterable from pathlib import Path -from typing import Callable, Optional, Union +from typing import Callable, Literal, Optional, Union +import pyjokes from discord import Embed, Message from discord.ext import commands from discord.ext.commands import BadArgument, Cog, Context, MessageConverter, clean_content @@ -41,7 +42,6 @@ class Fun(Cog): def __init__(self, bot: Bot): self.bot = bot - self._caesar_cipher_embed = json.loads(Path("bot/resources/fun/caesar_info.json").read_text("UTF-8")) @staticmethod @@ -212,6 +212,12 @@ class Fun(Cog): return Embed.from_dict(embed_dict) + @commands.command() + async def joke(self, ctx: commands.Context, category: Literal["neutral", "chuck", "all"] = "all") -> None: + """Retrieves a joke of the specified `category` from the pyjokes api.""" + joke = pyjokes.get_joke(category=category) + await ctx.send(joke) + def setup(bot: Bot) -> None: """Load the Fun cog.""" diff --git a/poetry.lock b/poetry.lock index c3bea820..08d0de7e 100644 --- a/poetry.lock +++ b/poetry.lock @@ -236,8 +236,8 @@ six = ">=1.12" sortedcontainers = "*" [package.extras] -aioredis = ["aioredis"] lua = ["lupa"] +aioredis = ["aioredis"] [[package]] name = "filelock" @@ -604,6 +604,18 @@ category = "dev" optional = false python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*" +[[package]] +name = "pyjokes" +version = "0.6.0" +description = "One line jokes for programmers (jokes as a service)" +category = "main" +optional = false +python-versions = "*" + +[package.extras] +test = ["tox", "coverage", "pytest"] +doc = ["mkdocs"] + [[package]] name = "pyparsing" version = "3.0.6" @@ -835,7 +847,7 @@ multidict = ">=4.0" [metadata] lock-version = "1.1" python-versions = "^3.9" -content-hash = "7b443f1df31c4119783474342f05852ec35bcb4b21c07160ea6aa1ed9164fa34" +content-hash = "86ef4c274176e805702da51d96711698a09ca6e04c145b607258c34d04638b9e" [metadata.files] aiodns = [] @@ -891,6 +903,7 @@ pycodestyle = [] pycparser = [] pydocstyle = [] pyflakes = [] +pyjokes = [] pyparsing = [] pyreadline3 = [] python-dateutil = [] diff --git a/pyproject.toml b/pyproject.toml index 076f9676..729d67fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -22,6 +22,7 @@ coloredlogs = "~=15.0" colorama = { version = "~=0.4.3", markers = "sys_platform == 'win32'" } lxml = "~=4.9" emoji = "^1.6.1" +pyjokes = "0.6.0" [tool.poetry.dev-dependencies] flake8 = "~=3.8" -- cgit v1.2.3 From 6be0e91d1b7282febd39415fd33e6d3f6a0b0581 Mon Sep 17 00:00:00 2001 From: AbooMinister25 Date: Mon, 8 Aug 2022 18:09:24 -0400 Subject: Refactored methods _get_discord_message, _get_text_and_embed, and _convert_embed into bot/utils/messages.py --- bot/exts/fun/fun.py | 78 ++++++--------------------------------------------- bot/exts/fun/uwu.py | 8 +++--- bot/utils/messages.py | 72 ++++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 83 insertions(+), 75 deletions(-) (limited to 'bot/exts/fun/fun.py') diff --git a/bot/exts/fun/fun.py b/bot/exts/fun/fun.py index 9ec9b9ee..e7337cb6 100644 --- a/bot/exts/fun/fun.py +++ b/bot/exts/fun/fun.py @@ -3,16 +3,16 @@ import logging import random from collections.abc import Iterable from pathlib import Path -from typing import Callable, Literal, Optional, Union +from typing import Literal import pyjokes -from discord import Embed, Message +from discord import Embed from discord.ext import commands -from discord.ext.commands import BadArgument, Cog, Context, MessageConverter, clean_content +from discord.ext.commands import BadArgument, Cog, Context, clean_content from bot.bot import Bot from bot.constants import Client, Colours, Emojis -from bot.utils import helpers +from bot.utils import helpers, messages log = logging.getLogger(__name__) @@ -67,10 +67,10 @@ class Fun(Cog): return "".join( char.upper() if round(random.random()) else char.lower() for char in text ) - text, embed = await Fun._get_text_and_embed(ctx, text) + text, embed = await messages.get_text_and_embed(ctx, text) # Convert embed if it exists if embed is not None: - embed = Fun._convert_embed(conversion_func, embed) + embed = messages.convert_embed(conversion_func, embed) converted_text = conversion_func(text) converted_text = helpers.suppress_links(converted_text) # Don't put >>> if only embed present @@ -116,10 +116,10 @@ class Fun(Cog): """Encrypts the given string using the Caesar Cipher.""" return "".join(caesar_cipher(text, offset)) - text, embed = await Fun._get_text_and_embed(ctx, msg) + text, embed = await messages.get_text_and_embed(ctx, msg) if embed is not None: - embed = Fun._convert_embed(conversion_func, embed) + embed = messages.convert_embed(conversion_func, embed) converted_text = conversion_func(text) @@ -150,68 +150,6 @@ class Fun(Cog): """ await self._caesar_cipher(ctx, offset, msg, left_shift=True) - @staticmethod - async def _get_text_and_embed(ctx: Context, text: str) -> tuple[str, Optional[Embed]]: - """ - Attempts to extract the text and embed from a possible link to a discord Message. - - Does not retrieve the text and embed from the Message if it is in a channel the user does - not have read permissions in. - - Returns a tuple of: - str: If `text` is a valid discord Message, the contents of the message, else `text`. - Optional[Embed]: The embed if found in the valid Message, else None - """ - embed = None - - msg = await Fun._get_discord_message(ctx, text) - # Ensure the user has read permissions for the channel the message is in - if isinstance(msg, Message): - permissions = msg.channel.permissions_for(ctx.author) - if permissions.read_messages: - text = msg.clean_content - # Take first embed because we can't send multiple embeds - if msg.embeds: - embed = msg.embeds[0] - - return (text, embed) - - @staticmethod - async def _get_discord_message(ctx: Context, text: str) -> Union[Message, str]: - """ - Attempts to convert a given `text` to a discord Message object and return it. - - Conversion will succeed if given a discord Message ID or link. - Returns `text` if the conversion fails. - """ - try: - text = await MessageConverter().convert(ctx, text) - except commands.BadArgument: - log.debug(f"Input '{text:.20}...' is not a valid Discord Message") - return text - - @staticmethod - def _convert_embed(func: Callable[[str, ], str], embed: Embed) -> Embed: - """ - Converts the text in an embed using a given conversion function, then return the embed. - - Only modifies the following fields: title, description, footer, fields - """ - embed_dict = embed.to_dict() - - embed_dict["title"] = func(embed_dict.get("title", "")) - embed_dict["description"] = func(embed_dict.get("description", "")) - - if "footer" in embed_dict: - embed_dict["footer"]["text"] = func(embed_dict["footer"].get("text", "")) - - if "fields" in embed_dict: - for field in embed_dict["fields"]: - field["name"] = func(field.get("name", "")) - field["value"] = func(field.get("value", "")) - - return Embed.from_dict(embed_dict) - @commands.command() async def joke(self, ctx: commands.Context, category: Literal["neutral", "chuck", "all"] = "all") -> None: """Retrieves a joke of the specified `category` from the pyjokes api.""" diff --git a/bot/exts/fun/uwu.py b/bot/exts/fun/uwu.py index ed8925e5..60a5834d 100644 --- a/bot/exts/fun/uwu.py +++ b/bot/exts/fun/uwu.py @@ -9,7 +9,7 @@ from discord.ext import commands from discord.ext.commands import Cog, Context, clean_content from bot.bot import Bot -from bot.utils import helpers +from bot.utils import helpers, messages if t.TYPE_CHECKING: from bot.exts.fun.fun import Fun # pragma: no cover @@ -185,14 +185,14 @@ class Uwu(Cog): if fun_cog: # Grabs the text from the embed for uwuification if embeds: - embed = fun_cog._convert_embed(self._uwuify, embeds[0]) + embed = messages.convert_embed(self._uwuify, embeds[0]) else: # Parse potential message links in text - text, embed = await fun_cog._get_text_and_embed(ctx, text) + text, embed = await messages.get_text_and_embed(ctx, text) # If an embed is found, grab and uwuify its text if embed: - embed = fun_cog._convert_embed(self._uwuify, embed) + embed = messages.convert_embed(self._uwuify, embed) else: embed = None diff --git a/bot/utils/messages.py b/bot/utils/messages.py index a6c035f9..ccc8b61c 100644 --- a/bot/utils/messages.py +++ b/bot/utils/messages.py @@ -1,5 +1,12 @@ +import logging import re -from typing import Optional +from typing import Callable, Optional, Union + +from discord import Embed, Message +from discord.ext import commands +from discord.ext.commands import Context, MessageConverter + +log = logging.getLogger(__name__) def sub_clyde(username: Optional[str]) -> Optional[str]: @@ -17,3 +24,66 @@ def sub_clyde(username: Optional[str]) -> Optional[str]: return re.sub(r"(clyd)(e)", replace_e, username, flags=re.I) else: return username # Empty string or None + + +async def get_discord_message(ctx: Context, text: str) -> Union[Message, str]: + """ + Attempts to convert a given `text` to a discord Message object and return it. + + Conversion will succeed if given a discord Message ID or link. + Returns `text` if the conversion fails. + """ + try: + text = await MessageConverter().convert(ctx, text) + except commands.BadArgument: + log.debug(f"Input '{text:.20}...' is not a valid Discord Message") + + return text + + +async def get_text_and_embed(ctx: Context, text: str) -> tuple[str, Optional[Embed]]: + """ + Attempts to extract the text and embed from a possible link to a discord Message. + + Does not retrieve the text and embed from the Message if it is in a channel the user does + not have read permissions in. + + Returns a tuple of: + str: If `text` is a valid discord Message, the contents of the message, else `text`. + Optional[Embed]: The embed if found in the valid Message, else None + """ + embed: Optional[Embed] = None + + msg = await get_discord_message(ctx, text) + # Ensure the user has read permissions for the channel the message is in + if isinstance(msg, Message): + permissions = msg.channel.permissions_for(ctx.author) + if permissions.read_messages: + text = msg.clean_content + # Take first embed because we can't send multiple embeds + if msg.embeds: + embed = msg.embeds[0] + + return (text, embed) + + +def convert_embed(func: Callable[[str, ], str], embed: Embed) -> Embed: + """ + Converts the text in an embed using a given conversion function, then return the embed. + + Only modifies the following fields: title, description, footer, fields + """ + embed_dict = embed.to_dict() + + embed_dict["title"] = func(embed_dict.get("title", "")) + embed_dict["description"] = func(embed_dict.get("description", "")) + + if "footer" in embed_dict: + embed_dict["footer"]["text"] = func(embed_dict["footer"].get("text", "")) + + if "fields" in embed_dict: + for field in embed_dict["fields"]: + field["name"] = func(field.get("name", "")) + field["value"] = func(field.get("value", "")) + + return Embed.from_dict(embed_dict) -- cgit v1.2.3