diff options
| -rw-r--r-- | bot/exts/evergreen/emoji.py (renamed from bot/exts/evergreen/emoji_count.py) | 67 | ||||
| -rw-r--r-- | bot/utils/time.py | 84 | 
2 files changed, 131 insertions, 20 deletions
| diff --git a/bot/exts/evergreen/emoji_count.py b/bot/exts/evergreen/emoji.py index cc43e9ab..99f71218 100644 --- a/bot/exts/evergreen/emoji_count.py +++ b/bot/exts/evergreen/emoji.py @@ -1,49 +1,51 @@ -import datetime  import logging  import random +import textwrap  from collections import defaultdict -from typing import List, Tuple +from datetime import datetime +from typing import List, Optional, Tuple -import discord +from discord import Color, Embed, Emoji  from discord.ext import commands  from bot.constants import Colours, ERROR_REPLIES  from bot.utils.pagination import LinePaginator +from bot.utils.time import time_since  log = logging.getLogger(__name__) -class EmojiCount(commands.Cog): -    """Command that give random emoji based on category.""" +class Emojis(commands.Cog): +    """A collection of commands related to emojis in the server."""      def __init__(self, bot: commands.Bot):          self.bot = bot      @staticmethod -    def embed_builder(emoji: dict) -> Tuple[discord.Embed, List[str]]: +    def embed_builder(emoji: dict) -> Tuple[Embed, List[str]]:          """Generates an embed with the emoji names and count.""" -        embed = discord.Embed( +        embed = Embed(              color=Colours.orange,              title="Emoji Count", -            timestamp=datetime.datetime.utcnow() +            timestamp=datetime.utcnow()          )          msg = []          if len(emoji) == 1:              for category_name, category_emojis in emoji.items():                  if len(category_emojis) == 1: -                    msg.append(f"There is **{len(category_emojis)}** emoji in **{category_name}** category") +                    msg.append(f"There is **{len(category_emojis)}** emoji in the **{category_name}** category.")                  else: -                    msg.append(f"There are **{len(category_emojis)}** emojis in **{category_name}** category") +                    msg.append(f"There are **{len(category_emojis)}** emojis in the **{category_name}** category.")                  embed.set_thumbnail(url=random.choice(category_emojis).url)          else:              for category_name, category_emojis in emoji.items():                  emoji_choice = random.choice(category_emojis)                  if len(category_emojis) > 1: -                    emoji_info = f"There are **{len(category_emojis)}** emojis in **{category_name}** category" +                    emoji_info = f"There are **{len(category_emojis)}** emojis in the **{category_name}** category."                  else: -                    emoji_info = f"There is **{len(category_emojis)}** emoji in **{category_name}** category" +                    emoji_info = f"There is **{len(category_emojis)}** emoji in the **{category_name}** category."                  if emoji_choice.animated:                      msg.append(f'<a:{emoji_choice.name}:{emoji_choice.id}> {emoji_info}')                  else: @@ -51,9 +53,9 @@ class EmojiCount(commands.Cog):          return embed, msg      @staticmethod -    def generate_invalid_embed(emojis: list) -> Tuple[discord.Embed, List[str]]: -        """Generates error embed.""" -        embed = discord.Embed( +    def generate_invalid_embed(emojis: list) -> Tuple[Embed, List[str]]: +        """Generates error embed for invalid emoji categories.""" +        embed = Embed(              color=Colours.soft_red,              title=random.choice(ERROR_REPLIES)          ) @@ -64,11 +66,19 @@ class EmojiCount(commands.Cog):              emoji_dict[emoji.name.split("_")[0]].append(emoji)          error_comp = ', '.join(emoji_dict) -        msg.append(f"These are the valid categories\n```{error_comp}```") +        msg.append(f"These are the valid emoji categories:\n```{error_comp}```")          return embed, msg -    @commands.command(name="emojicount", aliases=["ec", "emojis"]) -    async def emoji_count(self, ctx: commands.Context, *, category_query: str = None) -> None: +    @commands.group(name="emoji", invoke_without_command=True) +    async def emoji_group(self, ctx: commands.Context, emoji: Optional[Emoji]) -> None: +        """A group of commands related to emojis.""" +        if emoji is not None: +            await ctx.invoke(self.info_command, emoji) +        else: +            await ctx.send_help(ctx.command) + +    @emoji_group.command(name="count", aliases=("c",)) +    async def count_command(self, ctx: commands.Context, *, category_query: str = None) -> None:          """Returns embed with emoji category and info given by the user."""          emoji_dict = defaultdict(list) @@ -91,7 +101,24 @@ class EmojiCount(commands.Cog):              embed, msg = self.embed_builder(emoji_dict)          await LinePaginator.paginate(lines=msg, ctx=ctx, embed=embed) +    @emoji_group.command(name="info", aliases=("i",)) +    async def info_command(self, ctx: commands.Context, emoji: Emoji) -> None: +        """Returns relevant information about a Discord Emoji.""" +        emoji_information = Embed( +            title=f"Emoji Information: {emoji.name}", +            description=textwrap.dedent(f""" +                **Name:** {emoji.name} +                **Created:** {time_since(emoji.created_at, precision="hours")} +                **Date:** {datetime.strftime(emoji.created_at, "%d/%m/%Y")} +                **ID:** {emoji.id} +            """), +            color=Color.blurple(), +            url=str(emoji.url), +        ).set_thumbnail(url=emoji.url) + +        await ctx.send(embed=emoji_information) +  def setup(bot: commands.Bot) -> None: -    """Emoji Count Cog load.""" -    bot.add_cog(EmojiCount(bot)) +    """Add the Emojis cog into the bot.""" +    bot.add_cog(Emojis(bot)) diff --git a/bot/utils/time.py b/bot/utils/time.py new file mode 100644 index 00000000..3c57e126 --- /dev/null +++ b/bot/utils/time.py @@ -0,0 +1,84 @@ +import datetime + +from dateutil.relativedelta import relativedelta + + +# All these functions are from https://github.com/python-discord/bot/blob/master/bot/utils/time.py +def _stringify_time_unit(value: int, unit: str) -> str: +    """ +    Returns a string to represent a value and time unit, ensuring that it uses the right plural form of the unit. + +    >>> _stringify_time_unit(1, "seconds") +    "1 second" +    >>> _stringify_time_unit(24, "hours") +    "24 hours" +    >>> _stringify_time_unit(0, "minutes") +    "less than a minute" +    """ +    if unit == "seconds" and value == 0: +        return "0 seconds" +    elif value == 1: +        return f"{value} {unit[:-1]}" +    elif value == 0: +        return f"less than a {unit[:-1]}" +    else: +        return f"{value} {unit}" + + +def humanize_delta(delta: relativedelta, precision: str = "seconds", max_units: int = 6) -> str: +    """ +    Returns a human-readable version of the relativedelta. + +    precision specifies the smallest unit of time to include (e.g. "seconds", "minutes"). +    max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours). +    """ +    if max_units <= 0: +        raise ValueError("max_units must be positive") + +    units = ( +        ("years", delta.years), +        ("months", delta.months), +        ("days", delta.days), +        ("hours", delta.hours), +        ("minutes", delta.minutes), +        ("seconds", delta.seconds), +    ) + +    # Add the time units that are >0, but stop at accuracy or max_units. +    time_strings = [] +    unit_count = 0 +    for unit, value in units: +        if value: +            time_strings.append(_stringify_time_unit(value, unit)) +            unit_count += 1 + +        if unit == precision or unit_count >= max_units: +            break + +    # Add the 'and' between the last two units, if necessary +    if len(time_strings) > 1: +        time_strings[-1] = f"{time_strings[-2]} and {time_strings[-1]}" +        del time_strings[-2] + +    # If nothing has been found, just make the value 0 precision, e.g. `0 days`. +    if not time_strings: +        humanized = _stringify_time_unit(0, precision) +    else: +        humanized = ", ".join(time_strings) + +    return humanized + + +def time_since(past_datetime: datetime.datetime, precision: str = "seconds", max_units: int = 6) -> str: +    """ +    Takes a datetime and returns a human-readable string that describes how long ago that datetime was. + +    precision specifies the smallest unit of time to include (e.g. "seconds", "minutes"). +    max_units specifies the maximum number of units of time to include (e.g. 1 may include days but not hours). +    """ +    now = datetime.datetime.utcnow() +    delta = abs(relativedelta(now, past_datetime)) + +    humanized = humanize_delta(delta, precision, max_units) + +    return f"{humanized} ago" | 
