aboutsummaryrefslogtreecommitdiffstats
path: root/bot/utils/converters.py
blob: 7227a406547ccea89fe2c4b26753a8cd46df993a (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
from datetime import datetime
from typing import Union

import discord
from discord.ext import commands


class WrappedMessageConverter(commands.MessageConverter):
    """A converter that handles embed-suppressed links like <http://example.com>."""

    async def convert(self, ctx: commands.Context, argument: str) -> discord.Message:
        """Wrap the commands.MessageConverter to handle <> delimited message links."""
        # It's possible to wrap a message in [<>] as well, and it's supported because its easy
        if argument.startswith("[") and argument.endswith("]"):
            argument = argument[1:-1]
        if argument.startswith("<") and argument.endswith(">"):
            argument = argument[1:-1]

        return await super().convert(ctx, argument)


class CoordinateConverter(commands.Converter):
    """Converter for Coordinates."""

    @staticmethod
    async def convert(ctx: commands.Context, coordinate: str) -> tuple[int, int]:
        """Take in a coordinate string and turn it into an (x, y) tuple."""
        if len(coordinate) not in (2, 3):
            raise commands.BadArgument("Invalid co-ordinate provided.")

        coordinate = coordinate.lower()
        if coordinate[0].isalpha():
            digit = coordinate[1:]
            letter = coordinate[0]
        else:
            digit = coordinate[:-1]
            letter = coordinate[-1]

        if not digit.isdecimal():
            raise commands.BadArgument

        x = ord(letter) - ord("a")
        y = int(digit) - 1

        if (not 0 <= x <= 9) or (not 0 <= y <= 9):
            raise commands.BadArgument
        return x, y


SourceType = Union[commands.Command, commands.Cog]


class SourceConverter(commands.Converter):
    """Convert an argument into a command or cog."""

    @staticmethod
    async def convert(ctx: commands.Context, argument: str) -> SourceType:
        """Convert argument into source object."""
        cog = ctx.bot.get_cog(argument)
        if cog:
            return cog

        cmd = ctx.bot.get_command(argument)
        if cmd:
            return cmd

        raise commands.BadArgument(
            f"Unable to convert `{argument}` to valid command or Cog."
        )


class DateConverter(commands.Converter):
    """Parse SOL or earth date (in format YYYY-MM-DD) into `int` or `datetime`. When invalid input, raise error."""

    @staticmethod
    async def convert(ctx: commands.Context, argument: str) -> Union[int, datetime]:
        """Parse date (SOL or earth) into `datetime` or `int`. When invalid value, raise error."""
        if argument.isdecimal():
            return int(argument)
        try:
            date = datetime.strptime(argument, "%Y-%m-%d")
        except ValueError:
            raise commands.BadArgument(
                f"Can't convert `{argument}` to `datetime` in format `YYYY-MM-DD` or `int` in SOL."
            )
        return date


class Subreddit(commands.Converter):
    """Forces a string to begin with "r/" and checks if it's a valid subreddit."""

    @staticmethod
    async def convert(ctx: commands.Context, sub: str) -> str:
        """
        Force sub to begin with "r/" and check if it's a valid subreddit.

        If sub is a valid subreddit, return it prepended with "r/"
        """
        sub = sub.lower()

        if not sub.startswith("r/"):
            sub = f"r/{sub}"

        resp = await ctx.bot.http_session.get(
            "https://www.reddit.com/subreddits/search.json",
            params={"q": sub}
        )

        json = await resp.json()
        if not json["data"]["children"]:
            raise commands.BadArgument(
                f"The subreddit `{sub}` either doesn't exist, or it has no posts."
            )

        return sub