diff options
| author | 2020-09-21 18:43:54 +1000 | |
|---|---|---|
| committer | 2020-09-21 18:43:54 +1000 | |
| commit | bb02341c959b434cd8d2b6d40f0726a3b1388fb0 (patch) | |
| tree | 179c12922ec2be15072380f144c2dadfe86615fc /bot/exts/evergreen/fun.py | |
| parent | Prevent uwu output from pinging globally allowed roles. (diff) | |
| parent | Merge pull request #445 from python-discord/fix/issue-pr-command (diff) | |
Merge branch 'master' into clean_uwu
Diffstat (limited to 'bot/exts/evergreen/fun.py')
| -rw-r--r-- | bot/exts/evergreen/fun.py | 117 | 
1 files changed, 109 insertions, 8 deletions
| diff --git a/bot/exts/evergreen/fun.py b/bot/exts/evergreen/fun.py index 5d4743e4..1668982b 100644 --- a/bot/exts/evergreen/fun.py +++ b/bot/exts/evergreen/fun.py @@ -1,14 +1,16 @@  import functools +import json  import logging  import random -from typing import Callable, Tuple, Union +from pathlib import Path +from typing import Callable, Iterable, Tuple, Union  from discord import Embed, Message  from discord.ext import commands  from discord.ext.commands import Bot, Cog, Context, MessageConverter, clean_content  from bot import utils -from bot.constants import Emojis +from bot.constants import Colours, Emojis  log = logging.getLogger(__name__) @@ -26,12 +28,35 @@ UWU_WORDS = {  } +def caesar_cipher(text: str, offset: int) -> Iterable[str]: +    """ +    Implements a lazy Caesar Cipher algorithm. + +    Encrypts a `text` given a specific integer `offset`. The sign +    of the `offset` dictates the direction in which it shifts to, +    with a negative value shifting to the left, and a positive +    value shifting to the right. +    """ +    for char in text: +        if not char.isascii() or not char.isalpha() or char.isspace(): +            yield char +            continue + +        case_start = 65 if char.isupper() else 97 +        true_offset = (ord(char) - case_start + offset) % 26 + +        yield chr(case_start + true_offset) + +  class Fun(Cog):      """A collection of general commands for fun."""      def __init__(self, bot: Bot) -> None:          self.bot = bot +        with Path("bot/resources/evergreen/caesar_info.json").open("r", encoding="UTF-8") as f: +            self._caesar_cipher_embed = json.load(f) +      @commands.command()      async def roll(self, ctx: Context, num_rolls: int = 1) -> None:          """Outputs a number of random dice emotes (up to 6).""" @@ -79,23 +104,99 @@ class Fun(Cog):              converted_text = f">>> {converted_text.lstrip('> ')}"          await ctx.send(content=converted_text, embed=embed) +    @commands.group(name="caesarcipher", aliases=("caesar", "cc",)) +    async def caesarcipher_group(self, ctx: Context) -> None: +        """ +        Translates a message using the Caesar Cipher. + +        See `decrypt`, `encrypt`, and `info` subcommands. +        """ +        if ctx.invoked_subcommand is None: +            await ctx.invoke(self.bot.get_command("help"), "caesarcipher") + +    @caesarcipher_group.command(name="info") +    async def caesarcipher_info(self, ctx: Context) -> None: +        """Information about the Caesar Cipher.""" +        embed = Embed.from_dict(self._caesar_cipher_embed) +        embed.colour = Colours.dark_green + +        await ctx.send(embed=embed) + +    @staticmethod +    async def _caesar_cipher(ctx: Context, offset: int, msg: str, left_shift: bool = False) -> None: +        """ +        Given a positive integer `offset`, translates and sends the given `msg`. + +        Performs a right shift by default unless `left_shift` is specified as `True`. + +        Also accepts a valid Discord Message ID or link. +        """ +        if offset < 0: +            await ctx.send(":no_entry: Cannot use a negative offset.") +            return + +        if left_shift: +            offset = -offset + +        def conversion_func(text: str) -> str: +            """Encrypts the given string using the Caesar Cipher.""" +            return "".join(caesar_cipher(text, offset)) + +        text, embed = await Fun._get_text_and_embed(ctx, msg) + +        if embed is not None: +            embed = Fun._convert_embed(conversion_func, embed) + +        converted_text = conversion_func(text) + +        if converted_text: +            converted_text = f">>> {converted_text.lstrip('> ')}" + +        await ctx.send(content=converted_text, embed=embed) + +    @caesarcipher_group.command(name="encrypt", aliases=("rightshift", "rshift", "enc",)) +    async def caesarcipher_encrypt(self, ctx: Context, offset: int, *, msg: str) -> None: +        """ +        Given a positive integer `offset`, encrypt the given `msg`. + +        Performs a right shift of the letters in the message. + +        Also accepts a valid Discord Message ID or link. +        """ +        await self._caesar_cipher(ctx, offset, msg, left_shift=False) + +    @caesarcipher_group.command(name="decrypt", aliases=("leftshift", "lshift", "dec",)) +    async def caesarcipher_decrypt(self, ctx: Context, offset: int, *, msg: str) -> None: +        """ +        Given a positive integer `offset`, decrypt the given `msg`. + +        Performs a left shift of the letters in the message. + +        Also accepts a valid Discord Message ID or link. +        """ +        await self._caesar_cipher(ctx, offset, msg, left_shift=True) +      @staticmethod      async def _get_text_and_embed(ctx: Context, text: str) -> Tuple[str, Union[Embed, None]]:          """          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`.              Union[Embed, None]: The embed if found in the valid Message, else None          """          embed = None -        # message = await Fun._get_discord_message(ctx, text) -        # if isinstance(message, Message): -        #     text = message.content -        #     # Take first embed because we can't send multiple embeds -        #     if message.embeds: -        #         embed = message.embeds[0] +        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) and ctx.author.permissions_in(msg.channel).read_messages: +            text = msg.content +            # Take first embed because we can't send multiple embeds +            if msg.embeds: +                embed = msg.embeds[0]          return (text, embed) | 
