itsVale/Vale.py

View on GitHub
cogs/fun/fun.py

Summary

Maintainability
A
0 mins
Test Coverage
import contextlib
import logging
import random
import typing
from urllib.parse import quote

import aiohttp
import discord
from discord.ext import commands

logger = logging.getLogger(__name__)


class IdiotError(Exception):
    pass


api_endpoints = {
    "api_endpoints": {
        "Generators": ("achievement", "batslap", "beautiful", "blame", "crush", "facepalm", "pls", "respect", "slap",
                       "snapchat", "stepped", "superpunch", "tattoo", "thesearch", "triggered", "vault", "wanted")
    }
}

dev_endpoints = {
    "effects": ("brightness", "darkness", "greyscale", "invert", "invertGreyscale", "invertThreshold", "sepia",
                "silhouette", "threshold"),

    "generators": ("achievement", "batslap", "beautiful", "blame", "bobross", "challenger", "changemymind",
                   "coffee", "colour", "confused", "crush", "facepalm", "fanslap", "garbage", "girls", "hates",
                   "heavyfear", "hide", "ignore", "karen", "kirby", "missing", "painting", "pls", "religion",
                   "respect", "snapchat", "sniper", "steam", "stepped", "suggestion", "superpunch", "superspank",
                   "tattoo", "thesearch", "time", "tinder", "triggered", "vault", "vr", "waifuinsult", "wanted",
                   "wreckit", "zerotwopicture", "osu"),

    "greetings": ("anime_goodbye", "anime_welcome", "unified"),

    "overlays": ("approved", "discord", "rainbow", "rejected"),

    "profile": "card",

    "text": ("cursive", "mock", "owoify", "tinytext", "vaporwave"),
    }


def get_endpoint(endpoint):
    for key, values in api_endpoints.items():
        if endpoint.lower() in values:
            return key + "/" + endpoint.lower()

    raise IdiotError("The endpoint you are trying to make a request to is invalid.")


def get_dev_endpoint(endpoint):
    for key, values in dev_endpoints.items():
        if endpoint.lower() in values:
            return key + "/" + endpoint.lower()

    raise IdiotError("The endpoint you are trying to make a request to is invalid.")


class IdiotResponse:
    def __init__(self, **data):
        self.type = data.get('type')
        self.text = data.get('text')

        img_data = data.get('data')
        self.data = bytes(img_data) if img_data else None

    @classmethod
    def parse_result(cls, result, *, status_code):
        """Parses a response from the API."""

        status_codes = {
            "404": "Endpoint not found.",
            "403": "Either your API key is missing or an improper one was passed.",
            "400": "You are missing some required fields."
        }

        for key, value in status_codes.items():
            if status_code == int(key):
                raise IdiotError(value)

        return cls(**result)


class IdiotClient:
    def __init__(self, key, *, dev=False, **kwargs):
        self.api_key = key
        self.dev = dev
        self.base_url = 'https://dev.anidiots.guide/' if dev else 'https://api.anidiots.guide/'
        self.session = kwargs.get('session') or aiohttp.ClientSession(loop=kwargs.get('loop'))

        self.headers = {"Authorization" if dev else "token": self.api_key}

    async def _get_image(self, endpoint: str, **kwargs):
        """Makes a request to an endpoint of the API."""

        params = {k: v for k, v in kwargs.items() if v is not None}

        async with self.session.get(self.base_url + endpoint, headers=self.headers, params=params) as response:
            json_response = await response.json()
            status = response.status

        return IdiotResponse.parse_result(result=json_response, status_code=status)

    async def _get_text(self, endpoint: str, text: str, style=None):
        """Makes a request to a text endpoint of the API."""

        params = {'text': text}
        if style:
            params['style'] = style

        async with self.session.get(self.base_url + endpoint, headers=self.headers, params=params) as response:
            json_response = await response.json()
            status = response.status

        return IdiotResponse.parse_result(result=json_response, status_code=status)

    async def retrieve_greeting(self, type, version, bot, avatar, username, discriminator, guild_name, member_count, *, message=None):
        """This represents the base for Welcome/Goodbye messages."""

        result = await self._get_image(
            get_dev_endpoint('unified'),
            type=type,
            version=version,
            message=message,
            bot=bot,
            avatar=avatar,
            username=username,
            discriminator=discriminator,
            guildName=guild_name,
            memberCount=member_count
        )

        if not result.data:
            raise IdiotError('Failed to retrieve a Greeting Image.')

        return discord.File(result.data, 'greeting.png')


class Fun(commands.Cog, IdiotClient):
    def __init__(self, bot):
        super().__init__(bot.idiotic_api_key, dev=True, session=bot.session)

        self.bot = bot

    async def cog_command_error(self, ctx, error):
        if isinstance(error, IdiotError):
            await ctx.send(error)

    @commands.command(name='osu')
    async def _osu(self, ctx, theme: typing.Optional[int] = 3, *, user: str = None):
        """Returns an osu profile card for a given user.

        You can choose a theme by providing a number. Defaults to `darker`.
        `light`  => 1
        `dark`   => 2
        `darker` => 3

        If no user is provided, it will try to retrieve data using your nickname.

        Examples:
            {prefix}osu dark itsVale
            {prefix}osu itsVale
        """

        themes = ['light', 'dark', 'darker']

        if not 0 <= theme < 4:
            raise IdiotError('You must provide a theme as a integer.\n```\nlight  => 1\ndark  => 2\ndarker => 3```')

        user = user or ctx.author.display_name

        async with ctx.typing():
            try:
                result = await self._get_image(get_dev_endpoint('osu'), user=user, theme=themes[theme - 1])

                res = result.data
            except KeyError:
                return await ctx.send('Nothing found for the given username.')

            with contextlib.suppress(discord.HTTPException):
                await ctx.send(file=discord.File(res, 'osu.png'))

    @commands.command(name='lmgtfy', aliases=['google', 'search'])
    async def _lmgtfy(self, ctx, *args):
        """Use this command if someone is unable to google for stuff on his own."""

        if not args:
            return await ctx.send('This command requires arguments on what to google for.')

        query = quote(' '.join(args))
        await ctx.send(f'Someone who doesn\'t know how to google, huh? Let me show you how to do this. <http://lmgtfy.com/?iie=1&q={query}>')

    @commands.command(name='mock')
    async def _mock(self, ctx, *, text):
        """Who doesn't like the Spongebob mock meme?"""

        # Quality code
        await ctx.send(''.join([random.choice([char.upper(), char.lower()]) for char in text]))


def setup(bot):
    bot.add_cog(Fun(bot))