示例#1
0
    async def pug(self, ctx: commands.Context, *args):
        self.logger.debug(
            f'{ctx.author}: {ctx.prefix}{ctx.invoked_with} {ctx.args[2:]}')
        random_teams: bool = False
        map_arg: str = None
        team1_captain_arg: discord.Member = None
        team2_captain_arg: discord.Member = None
        for arg in args:
            if arg == 'random':
                random_teams = True
                self.logger.debug('Random Teams Enabled')
            elif arg in current_map_pool:
                map_arg = arg
                self.logger.debug(f'Force Selected Map = {map_arg}')
            else:
                member: discord.Member = await commands.MemberConverter(
                ).convert(ctx, arg)
                if member in ctx.author.voice.channel.members:
                    if team1_captain_arg is None:
                        team1_captain_arg = member
                        self.logger.debug(
                            f'Forced Team 1 Captain = {team1_captain_arg}')
                    elif team2_captain_arg is None and member is not team1_captain_arg:
                        team2_captain_arg = member
                        self.logger.debug(
                            f'Forced Team 2 Captain = {team2_captain_arg}')
                    else:
                        if member is team1_captain_arg:
                            raise commands.CommandError(
                                message=
                                f'One user cannot be captain of 2 teams.')
                        else:
                            raise commands.CommandError(
                                message=f'You can only set 2 captains.')
                else:
                    raise commands.CommandError(
                        message=f'Invalid Argument: `{arg}`')

        if not self.pug.enabled:
            self.logger.info('Pug called from queue as pug is disabled')
            if len(self.bot.queue_captains) > 0:
                team1_captain_arg = self.bot.queue_captains.pop(0)
                self.logger.debug(
                    f'Forced Team 1 Captain = {team1_captain_arg}')
            if len(self.bot.queue_captains) > 0:
                team2_captain_arg = self.bot.queue_captains.pop(0)
                self.logger.debug(
                    f'Forced Team 2 Captain = {team2_captain_arg}')

        # TODO: Refactor this mess
        db = Database('sqlite:///main.sqlite')
        await db.connect()
        csgo_server = self.bot.servers[0]
        for server in self.bot.servers:
            if server.available:
                server.available = False
                csgo_server = server
                break
        channel_original = ctx.author.voice.channel
        players: List[discord.Member] = ctx.author.voice.channel.members.copy()
        players = players[:self.bot.match_size]
        if self.bot.dev:
            players = [ctx.author] * 10
            self.logger.info(
                'Filling list of players with the message author because bot is in dev mode'
            )

        if random_teams:
            shuffle(players)
            team1 = players[:len(players) // 2]
            team2 = players[len(players) // 2:]
            team1_captain = team1[0]
            team2_captain = team2[0]
            message_text = 'Random Teams'
            message = await ctx.send(message_text)
            embed = self.player_veto_embed(players_text='Random Teams',
                                           team1=team1,
                                           team1_captain=team1_captain,
                                           team2=team2,
                                           team2_captain=team2_captain)
            await message.edit(content=message_text, embed=embed)
            self.logger.debug(f'Random Team1: {team1}')
            self.logger.debug(f'Random Team2: {team2}')
        else:
            emojis = emoji_bank.copy()
            del emojis[len(players) - 2:len(emojis)]
            emojis_selected = []
            team1 = []
            team2 = []
            if team1_captain_arg is not None:
                team1_captain = team1_captain_arg
            else:
                team1_captain = players[randint(0, len(players) - 1)]
            self.logger.debug(f'team1_captain = {team1_captain}')
            team1.append(team1_captain)
            players.remove(team1_captain)

            if team2_captain_arg is not None:
                team2_captain = team2_captain_arg
            else:
                team2_captain = players[randint(0, len(players) - 1)]
            self.logger.debug(f'team2_captain = {team1_captain}')
            team2.append(team2_captain)
            players.remove(team2_captain)

            current_team_player_select = 1

            current_captain = team1_captain
            player_veto_count = 0

            message = await ctx.send(
                f'{self.bot.match_size} man time\nLoading player selection...')
            for emoji in emojis:
                await message.add_reaction(emoji)

            emoji_remove = []

            player_veto = []
            if self.bot.match_size == 2:
                player_veto = [1, 1]
            for i in range(self.bot.match_size - 2):
                if i == 0 or i == self.bot.match_size - 3:
                    player_veto.append(1)
                elif i % 2 == 0:
                    player_veto.append(2)
            self.logger.debug(f'player_veto = {player_veto}')

            while len(players) > 0:
                message_text = ''
                players_text = ''

                if current_team_player_select == 1:
                    message_text += f'<@{team1_captain.id}>'
                    current_captain = team1_captain
                elif current_team_player_select == 2:
                    message_text += f'<@{team2_captain.id}>'
                    current_captain = team2_captain
                self.logger.debug(
                    f'current_captain (captain currently selected) = {current_captain}'
                )

                message_text += f' select {player_veto[player_veto_count]}\n'
                message_text += 'You have 60 seconds to choose your player(s)\n'

                i = 0
                for player in players:
                    players_text += f'{emojis[i]} - <@{player.id}>\n'
                    i += 1
                embed = self.player_veto_embed(players_text=players_text,
                                               team1=team1,
                                               team1_captain=team1_captain,
                                               team2=team2,
                                               team2_captain=team2_captain)
                await message.edit(content=message_text, embed=embed)
                if len(emoji_remove) > 0:
                    for emoji in emoji_remove:
                        await message.clear_reaction(emoji)
                    emoji_remove = []

                selected_players = 0
                seconds = 0
                while True:
                    await asyncio.sleep(1)
                    message = await ctx.fetch_message(message.id)

                    for reaction in message.reactions:
                        users = await reaction.users().flatten()
                        if current_captain in users and selected_players < player_veto[
                                player_veto_count] and not (
                                    reaction.emoji in emojis_selected):
                            index = emojis.index(reaction.emoji)
                            if current_team_player_select == 1:
                                team1.append(players[index])
                            if current_team_player_select == 2:
                                team2.append(players[index])
                            self.logger.debug(
                                f'{current_captain} selected {players[index]}')
                            emojis_selected.append(reaction.emoji)
                            emoji_remove.append(reaction.emoji)
                            del emojis[index]
                            del players[index]
                            selected_players += 1

                    seconds += 1

                    if seconds % 60 == 0:
                        for _ in range(0, player_veto[player_veto_count]):
                            index = randint(0, len(players) - 1)
                            self.logger.debug(
                                f'{current_captain} selected {players[index]}')
                            if current_team_player_select == 1:
                                team1.append(players[index])
                            if current_team_player_select == 2:
                                team2.append(players[index])
                            emojis_selected.append(emojis[index])
                            del emojis[index]
                            del players[index]
                            selected_players += 1

                    if selected_players == player_veto[player_veto_count]:
                        if current_team_player_select == 1:
                            current_team_player_select = 2
                        elif current_team_player_select == 2:
                            current_team_player_select = 1
                        break

                player_veto_count += 1

        if map_arg is None:
            message_text = 'Map Veto Loading'
        else:
            message_text = f'Map is `{map_arg}`'
        players_text = 'None'
        embed = self.player_veto_embed(players_text=players_text,
                                       team1=team1,
                                       team1_captain=team1_captain,
                                       team2=team2,
                                       team2_captain=team2_captain)
        await message.edit(content=message_text, embed=embed)
        await message.clear_reactions()

        if map_arg is not None:
            chosen_map_embed = await self.get_chosen_map_embed(map_arg)
            await ctx.send(embed=chosen_map_embed)

        team1_steamIDs = {}
        team2_steamIDs = {}
        spectator_steamIDs = {}

        if ctx.author.voice.channel.category is None:
            team1_channel = await ctx.guild.create_voice_channel(
                name=f'team_{team1_captain.display_name}',
                user_limit=int(self.bot.match_size / 2) + 1)
            team2_channel = await ctx.guild.create_voice_channel(
                name=f'team_{team2_captain.display_name}',
                user_limit=int(self.bot.match_size / 2) + 1)
        else:
            team1_channel = await ctx.author.voice.channel.category.create_voice_channel(
                name=f'team_{team1_captain.display_name}',
                user_limit=int(self.bot.match_size / 2) + 1)
            team2_channel = await ctx.author.voice.channel.category.create_voice_channel(
                name=f'team_{team2_captain.display_name}',
                user_limit=int(self.bot.match_size / 2) + 1)

        for player in team1:
            await player.move_to(channel=team1_channel,
                                 reason=f'You are on {team1_captain}\'s Team')
            data = await db.fetch_one(
                'SELECT steam_id FROM users WHERE discord_id = :player',
                {"player": str(player.id)})
            team1_steamIDs[data[0]] = unidecode(player.display_name)
        self.logger.debug(f'Moved all team1 players to {team1_channel}')

        for player in team2:
            await player.move_to(channel=team2_channel,
                                 reason=f'You are on {team2_captain}\'s Team')
            data = await db.fetch_one(
                'SELECT steam_id FROM users WHERE discord_id = :player',
                {"player": str(player.id)})
            team2_steamIDs[data[0]] = unidecode(player.display_name)
        self.logger.debug(f'Moved all team2 players to {team2_channel}')

        if len(self.bot.spectators) > 0:
            for spec in self.bot.spectators:
                data = await db.fetch_one(
                    'SELECT steam_id FROM users WHERE discord_id = :spectator',
                    {"spectator": str(spec.id)})
                spectator_steamIDs[data[0]] = unidecode(spec.display_name)
            self.logger.info('Added Spectators')

        if map_arg is None:
            map_list = await self.map_veto(ctx, team1_captain, team2_captain)
        else:
            map_list = [map_arg]

        bot_ip = self.bot.web_server.IP
        if self.bot.bot_IP != '':
            bot_ip = self.bot.bot_IP

        team1_country = 'IE'
        team2_country = 'IE'

        team1_flags = []
        team2_flags = []

        team1_flag_request = ''
        team2_flag_request = ''

        for player in team1_steamIDs:
            team1_flag_request += SteamID(player).__str__() + ','
        team1_flag_request = team1_flag_request[:-1]

        for player in team2_steamIDs:
            team2_flag_request += SteamID(player).__str__() + ','
        team2_flag_request = team2_flag_request[:-1]

        self.logger.info('Making request to the Steam API to get player flags')
        session = aiohttp.ClientSession()
        async with session.get(
                f'https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/'
                f'?key={self.bot.steam_web_api_key}'
                f'&steamids={team1_flag_request}') as resp:
            player_info = await resp.json()
            for player in player_info['response']['players']:
                if 'loccountrycode' in player:
                    team1_flags.append(player['loccountrycode'])
            await session.close()

        session = aiohttp.ClientSession()
        async with session.get(
                f'https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v2/'
                f'?key={self.bot.steam_web_api_key}'
                f'&steamids={team2_flag_request}') as resp:
            player_info = await resp.json()
            for player in player_info['response']['players']:
                if 'loccountrycode' in player:
                    team2_flags.append(player['loccountrycode'])
            await session.close()

        # TODO: Add check for EU/CIS flag
        if len(team1_flags) > 0:
            team1_country = Counter(team1_flags).most_common(1)[0][0]
        if len(team2_flags) > 0:
            team2_country = Counter(team2_flags).most_common(1)[0][0]

        team1_name = f'team_{unidecode(team1_captain.display_name)}'
        team2_name = f'team_{unidecode(team2_captain.display_name)}'

        match_id = f'PUG_{datetime.now().strftime("%Y-%m-%d-%H-%M-%S")}'

        match_config = {
            'matchid': match_id,
            'num_maps': 1,
            'maplist': map_list,
            'skip_veto': True,
            'veto_first': 'team1',
            'side_type': 'always_knife',
            'players_per_team': int(self.bot.match_size / 2),
            'min_players_to_ready': 1,
            'spectators': {
                'players': spectator_steamIDs,
            },
            'team1': {
                'name': team1_name,
                'tag': 'team1',
                'flag': team1_country,
                'players': team1_steamIDs
            },
            'team2': {
                'name': team2_name,
                'tag': 'team2',
                'flag': team2_country,
                'players': team2_steamIDs
            },
            'cvars': {
                'get5_event_api_url':
                f'http://{bot_ip}:{self.bot.web_server.port}/',
                'get5_print_damage': 1,
            }
        }

        self.logger.debug(f'Match Config =\n {pprint.pformat(match_config)}')

        with open(f'./matches/{match_id}.json', 'w') as outfile:
            json.dump(match_config, outfile, ensure_ascii=False, indent=4)

        await ctx.send(
            'If you are coaching, once you join the server, type .coach')
        loading_map_message = await ctx.send('Server is being configured')
        await asyncio.sleep(0.5)
        get5_trigger = valve.rcon.execute(
            (csgo_server.server_address, csgo_server.server_port),
            csgo_server.RCON_password, 'exec triggers/get5')
        self.logger.debug(
            f'Executing get5_trigger (something for Yannicks Server) \n {get5_trigger}'
        )
        await asyncio.sleep(10)
        await loading_map_message.delete()
        load_match = valve.rcon.execute(
            (csgo_server.server_address, csgo_server.server_port),
            csgo_server.RCON_password,
            f'get5_loadmatch_url "{bot_ip}:{self.bot.web_server.port}/{match_id}"'
        )
        self.logger.debug(f'Load Match via URL\n {load_match}')
        await asyncio.sleep(5)
        connect_embed = await self.connect_embed(csgo_server)
        if self.bot.connect_dm:
            for player in team1 + team2 + self.bot.spectators:
                try:
                    await player.send(embed=connect_embed)
                except (discord.HTTPException, discord.Forbidden):
                    await ctx.send(
                        f'Unable to PM <@{player.id}> the server details.')
                    self.logger.warning(f'{player} was not sent the IP via DM')
        else:
            await ctx.send(embed=connect_embed)
        score_embed = discord.Embed()
        score_embed.add_field(name='0', value=team1_name, inline=True)
        score_embed.add_field(name='0', value=team2_name, inline=True)
        score_message = await ctx.send('Match in Progress', embed=score_embed)

        csgo_server.get_context(
            ctx=ctx,
            channels=[channel_original, team1_channel, team2_channel],
            players=team1 + team2,
            score_message=score_message)
        csgo_server.set_team_names([team1_name, team2_name])
        self.bot.web_server.add_server(csgo_server)

        if not self.pug.enabled:
            self.queue_check.start()
            self.logger.info('Queue Starting Back')
示例#2
0
    async def create(self, ctx, backup_id=None, name=None, *, description=None):
        """
        Turn a private backup into a PUBLIC template.


        __Arguments__

        **backup_id**: The id of the backup that you want to turn into a template
        **name**: A name for the template
        **description**: A description for the template


        __Examples__

        ```{c.prefix}template create oj1xky11871fzrbu start-template A good start for new servers```
        """

        backup_id = backup_id or await helpers.ask_question(
            ctx,
            "Please respond with the **id of the backup** that you want to turn into a template."
        )
        name = name or await helpers.ask_question(ctx, "Please respond with your desired **name for template**.")
        description = description or await helpers.ask_question(
            ctx,
            "Please respond with a **meaningful description** for the template."
        )

        name = name.lower().replace(" ", "_")
        backup = await ctx.db.backups.find_one(backup_id)
        if backup is None or backup.get("creator") != ctx.author.id:
            raise cmd.CommandError(f"You have **no backup** with the id `{backup_id}`.")

        already_exists = (await ctx.db.templates.find_one(name)) is not None
        if already_exists:
            raise cmd.CommandError(
                f"There is **already a template with that name**, please choose another one."
            )

        if len(description) < 30:
            raise cmd.CommandError("The template description must be **at least 30 characters** long.")

        backup["backup"]["members"] = []

        warning = await ctx.send(**ctx.em(
            "Are you sure you want to turn this backup into a template?\n\n"
            "Templates must not be a copy of your server, they are for **public use** and must be generic. "
            "Use `x!backup load` if you just want to load or clone your server.",
            type="warning"
        ))
        await warning.add_reaction("✅")
        await warning.add_reaction("❌")
        try:
            reaction, user = await self.bot.wait_for(
                "reaction_add",
                check=lambda r, u: r.message.id == warning.id and u.id == ctx.author.id,
                timeout=60
            )
        except TimeoutError:
            await warning.delete()
            raise cmd.CommandError(
                "Please make sure to **click the ✅ reaction** in order to create a template."
            )

        if str(reaction.emoji) != "✅":
            await warning.delete()
            return

        template = {
            "_id": name,
            "creator": backup["creator"],
            "used": 0,
            "featured": False,
            "approved": False,
            "original": backup_id,
            "description": description,
            "template": backup["backup"]
        }
        await ctx.db.templates.insert_one(template)
        await ctx.send(**ctx.em("Successfully **created template**.\n"
                                f"The template **will not be available** until a moderator approves it.\n"
                                f"Please join the [support server](https://discord.club/discord) and enable direct "
                                f"messages to get updates about your template.",
                                type="success"))
        await self.approval_webhook.send(embed=self._template_info(template))
示例#3
0
        if empire is None:
            raise commands.CommandError(f"Target does not have an empire.")

        if (last_battle := empire.get("last_battle")) is None:
            time_since_attack = BattleValues.COOLDOWN

        else:
            time_since_attack = (dt.datetime.utcnow() -
                                 last_battle).total_seconds()

        if time_since_attack < BattleValues.COOLDOWN:
            delta = dt.timedelta(seconds=int(BattleValues.COOLDOWN -
                                             time_since_attack))

            raise commands.CommandError(
                f"Target is recovering from a previous battle. Try again in `{delta}`"
            )

        return user


class Range(commands.Converter):
    def __init__(self, min_: int, max_: int = None):
        self.min_ = min_
        self.max_ = max_

    async def convert(self, ctx: commands.Context, argument: str) -> int:
        try:
            val = int(argument)

            if (self.max_ is not None and val > self.max_) or val < self.min_:
示例#4
0
    async def cloudahk_call(self, ctx, code, lang='ahk'):
        '''Call to CloudAHK to run "code" written in "lang". Replies to invoking user with stdout/runtime of code. '''

        token = '{0}:{1}'.format(CLOUDAHK_USER, CLOUDAHK_PASS)

        encoded = b64encode(bytes(token, 'utf-8')).decode('utf-8')
        headers = {'Authorization': 'Basic ' + encoded}

        # remove first line with backticks and highlighting lang
        if re.match('^```.*\n', code):
            code = code[code.find('\n') + 1:]

        # strip backticks on both sides
        code = code.strip('`').strip()

        url = f'{CLOUDAHK_URL}/{lang}/run'

        # call cloudahk with 20 in timeout
        async with self.bot.aiohttp.post(url,
                                         data=code,
                                         headers=headers,
                                         timeout=20) as resp:
            if resp.status == 200:
                result = await resp.json()
            else:
                raise commands.CommandError('Something went wrong.')

        stdout, time = result['stdout'].strip(), result['time']

        file = None
        stdout = stdout.replace('\r', '')

        if time is None:
            resp = 'Program ran for too long and was aborted.'
        else:
            stdout_len = len(stdout)
            display_time = f'Runtime: `{time:.2f}` seconds'

            if stdout_len < 1800 and stdout.count('\n') < 20:
                # upload as plaintext
                stdout = stdout.replace('``', '`\u200b`')

                resp = '```autoit\n{0}\n```{1}'.format(
                    stdout if stdout else 'No output.', display_time)

            elif stdout_len < DISCORD_UPLOAD_LIMIT:
                fp = io.BytesIO(bytes(stdout.encode('utf-8')))
                file = discord.File(fp, 'output.txt')
                resp = f'Output dumped to file.\n{display_time}'

            else:
                raise commands.CommandError('Output greater than 8 MB.')

        # logging for security purposes and checking for abuse
        filename = 'ahk_eval/{0}_{1}_{2}_{3}'.format(ctx.guild.id,
                                                     ctx.author.id,
                                                     ctx.message.id, lang)
        with open(filename, 'w', encoding='utf-8-sig') as f:
            f.write(
                '{0}\n\nLANG: {1}\n\nCODE:\n{2}\n\nPROCESSING TIME: {3}\n\nSTDOUT:\n{4}\n'
                .format(ctx.stamp, lang, code, time, stdout))

        # because of how the api works, if the person has deleted their message then
        # the api won't take the request and returns an error, so we catch it and don't
        # reply. This ensures the output gets sent even if they delete their message.
        try:
            await ctx.reply(content=resp, file=file)
        except discord.HTTPException:
            await ctx.send(content=resp, file=file)
示例#5
0
from cogs.ahk.ids import *
from cogs.mixins import AceMixin
from config import CLOUDAHK_PASS, CLOUDAHK_URL, CLOUDAHK_USER
from utils.docs_parser import parse_docs
from utils.html2markdown import HTML2Markdown
from utils.pager import Pager
from utils.string import po
from utils.time import pretty_timedelta

log = logging.getLogger(__name__)

AHK_COLOR = 0x95CD95
RSS_URL = 'https://www.autohotkey.com/boards/feed'

DOCS_FORMAT = 'https://autohotkey.com/docs/{}'
DOCS_NO_MATCH = commands.CommandError(
    'Sorry, couldn\'t find an entry similar to that.')

SUGGESTION_PREFIX = 'suggestion:'
UPVOTE_EMOJI = '\N{Thumbs Up Sign}'
DOWNVOTE_EMOJI = '\N{Thumbs Down Sign}'

INACTIVITY_LIMIT = timedelta(weeks=4)


class DocsPagePager(Pager):
    async def craft_page(self, e, page, entries):
        e.title = self.header.get('page')
        e.url = DOCS_FORMAT.format(self.header.get('link'))
        e.color = AHK_COLOR

        e.description = '\n'.join('[`{}`]({})'.format(
示例#6
0
async def queue_running(ctx: commands.Context):
    if not ctx.bot.cogs['CSGO'].queue_check.is_running():
        raise commands.CommandError(message='Queue not running.')
    return True
示例#7
0
 async def convert(self, ctx, argument):
     with contextlib.suppress(commands.MessageNotFound):
         message = await commands.MessageConverter().convert(ctx, argument)
         return message.jump_url
     raise commands.CommandError(
         f"I can't find {argument}. Is this even a real message?")
示例#8
0
    async def promote(self, ctx, *memberNames):
        '''
        Promotes the given user(s) (Admin+) (Portables only).
        Arguments: member(s)
        Members can be either one name or one or more mentions.
        '''
        addCommand()
        await ctx.channel.trigger_typing()

        msg = ctx.message
        user = ctx.author
        roles = user.roles
        guild = ctx.guild

        if len(msg.mentions) < 1 and not memberNames:
            raise commands.CommandError(
                message=f'Required argument missing: `user`.')

        members = msg.mentions
        if not members:
            if memberNames:
                memberName = ''
                for part in memberNames:
                    memberName += part + ' '
                memberName = memberName.strip()
                memberName = pattern.sub('', memberName).upper()
                member = discord.utils.find(lambda m: isName(memberName, m),
                                            guild.members)
                if not member:
                    raise commands.CommandError(
                        message=f'Could not find user: `{memberName}`.')
                members.append(member)
        isLeader = False
        for r in roles:
            if r.id == config['leaderRole'] or user.id == config['owner']:
                isLeader = True
                break
        txt = ""
        for r in guild.roles:
            if r.id == config['smileyRole']:
                smileyRole = r
            if r.id == config['rankRole']:
                rankRole = r
            if r.id == config['editorRole']:
                editorRole = r
            if r.id == config['modRole']:
                modRole = r
            if r.id == config['adminRole']:
                adminRole = r
        for m in members:
            name = m.display_name
            if m.top_role < smileyRole:
                await m.add_roles(smileyRole)
                txt += f'**{name}** has been promoted to **Smiley**.\n'
            elif m.top_role < rankRole:
                await m.add_roles(rankRole, editorRole)
                txt += f'**{name}** has been promoted to **Editor**.\n'
            elif m.top_role < modRole:
                await m.add_roles(modRole)
                txt += f'**{name}** has been promoted to **Moderator**.\n'
            elif m.top_role < adminRole:
                if isLeader:
                    await m.add_roles(adminRole)
                    txt += f'**{name}** has been promoted to **Admin**.\n'
                else:
                    raise commands.CommandError(
                        message=f'Missing permissions: `Admin`.')
            else:
                raise commands.CommandError(
                    message=f'Missing permissions: `Admin`.')
        if txt:
            await ctx.send(txt)
示例#9
0
    async def derank(self, ctx, *memberNames):
        '''
        Deranks the given user(s) (Admin+) (Portables only).
        Arguments: members
        Members can be either one name or one or more mentions.
        '''
        addCommand()
        await ctx.channel.trigger_typing()

        msg = ctx.message
        user = ctx.author
        roles = user.roles
        guild = ctx.guild

        if len(msg.mentions) < 1 and not memberNames:
            raise commands.CommandError(
                message=f'Required argument missing: `user`.')

        members = msg.mentions
        if not members:
            if memberNames:
                memberName = ''
                for part in memberNames:
                    memberName += part + ' '
                memberName = memberName.strip()
                memberName = pattern.sub('', memberName).upper()
                member = discord.utils.find(lambda m: isName(memberName, m),
                                            guild.members)
                if not member:
                    raise commands.CommandError(
                        message=f'Could not find user: `{memberName}`.')
                members.append(member)

        isLeader = False
        for r in roles:
            if r.id == config['leaderRole'] or user.id == config['owner']:
                isLeader = True
                break

        txt = ""
        for r in guild.roles:
            if r.id == config['smileyRole']:
                smileyRole = r
            if r.id == config['vetRole']:
                vetRole = r
            if r.id == config['rankRole']:
                rankRole = r
            if r.id == config['editorRole']:
                editorRole = r
            if r.id == config['modRole']:
                modRole = r
            if r.id == config['adminRole']:
                adminRole = r
            if r.id == config['leaderRole']:
                leaderRole = r
        for m in members:
            roles = m.roles[
                1:]  #get all except first role, because first role is always @everyone
            name = m.display_name
            if m.top_role >= leaderRole:
                await ctx.send(
                    f'Sorry, I do not have sufficient permissions to derank **{name}**.'
                )
                raise commands.CommandError(
                    message=f'Insufficient permissions.')
            elif m.top_role >= adminRole:
                if isLeader:
                    try:
                        await m.remove_roles(adminRole, modRole, editorRole,
                                             rankRole)
                        txt += f'{name} has been deranked.\n'
                    except discord.Forbidden:
                        raise commands.CommandError(
                            message=f'Missing permissions: `manage_roles`.')
                else:
                    raise commands.CommandError(
                        message=f'Missing permissions: `Admin`.')
            elif m.top_role >= modRole:
                try:
                    await m.remove_roles(modRole, editorRole, rankRole)
                    txt += f'**{name}** has been deranked.\n'
                except discord.Forbidden:
                    raise commands.CommandError(
                        message=f'Missing permissions: `manage_roles`.')
            elif m.top_role >= rankRole:
                try:
                    await m.remove_roles(editorRole, rankRole)
                    txt += f'**{name}** has been deranked.\n'
                except discord.Forbidden:
                    raise commands.CommandError(
                        message=f'Missing permissions: `manage_roles`.')
            elif m.top_role >= smileyRole:
                try:
                    if vetRole in roles:
                        await m.remove_roles(smileyRole, vetRole)
                    else:
                        await m.remove_roles(smileyRole)
                    txt += f'**{name}** has been deranked.\n'
                except discord.Forbidden:
                    raise commands.CommandError(
                        message=f'Missing permissions: `manage_roles`.')
            else:
                raise commands.CommandError(
                    message=f'Error: `{name}` cannot be deranked any further.')
        if txt:
            await ctx.send(txt)
示例#10
0
    async def mute(self, ctx, member='', duration='', reason='N/A'):
        '''
        Assigns a role 'Muted' to given member. (Admin+)
        Arguments:
        member: name, nickname, id, mention
        duration: [number][unit], where unit in {d, h, m} (optional)
        reason: string (optional)
        '''
        addCommand()
        await ctx.channel.trigger_typing()

        msg = ctx.message
        guild = ctx.guild

        if not member:
            raise commands.CommandError(
                message=f'Required argument missing: `member`.')

        temp = None
        if msg.mentions:
            temp = msg.mentions[0]
        if not temp:
            if is_int(member):
                temp = await guild.fetch_member(int(member))
        if not temp:
            temp = discord.utils.get(guild.members, display_name=member)
        if not temp:
            temp = discord.utils.get(guild.members, name=member)

        if not temp:
            raise commands.CommandError(
                message=f'Could not find member: `{member}`.')
        member = temp

        mute_role = discord.utils.find(lambda r: 'MUTE' in r.name.upper(),
                                       guild.roles)
        if not mute_role:
            try:
                mute_role = await guild.create_role(
                    name='Muted', permissions=discord.Permissions.none())
            except discord.Forbidden:
                raise commands.CommandError(
                    message=f'Missing permissions: `create_role`.')

        if mute_role in member.roles:
            raise commands.CommandError(
                message=f'Error: {member.mention} is already muted.')

        try:
            if not reason == 'N/A':
                await member.add_roles(mute_role, reason=reason)
            else:
                await member.add_roles(mute_role)
        except discord.Forbidden:
            raise commands.CommandError(
                message=f'Missing permissions: `role_management`.')

        await ctx.send(f'{member.mention} has been **muted**.')

        if duration:
            # format: [num][unit] where unit in {d, h, m}
            temp = duration.replace(' ', '')
            units = ['d', 'h', 'm']
            input = []
            num = ''
            for char in temp:
                if not is_int(char) and not char.lower() in units:
                    raise commands.CommandError(
                        message=f'Invalid argument: `duration`.')
                elif is_int(char):
                    num += char
                elif char.lower() in units:
                    if not num:
                        raise commands.CommandError(
                            message=f'Invalid argument: `duration`.')
                    input.append((int(num), char.lower()))
                    num = ''
            days = 0
            hours = 0
            minutes = 0
            for i in input:
                num = i[0]
                unit = i[1]
                if unit == 'd':
                    days += num
                elif unit == 'h':
                    hours += num
                elif unit == 'm':
                    minutes += num
            if days * 24 * 60 + hours * 60 + minutes <= 0:
                raise commands.CommandError(
                    message=f'Invalid argument: `duration`.')
            elif days * 24 * 60 + hours * 60 + minutes > 60 * 24 * 366:
                raise commands.CommandError(
                    message=f'Invalid argument: `duration`.')
            duration = timedelta(days=days, hours=hours, minutes=minutes)

            end = str(datetime.utcnow().replace(second=0, microsecond=0) +
                      duration)

            await Mute.create(guild_id=ctx.guild.id,
                              user_id=member.id,
                              expiration=end,
                              reason=reason)

            await ctx.send(f'Mute expiration date set to: `{end}`.')

        for c in guild.text_channels:
            send = c.permissions_for(member).send_messages
            if not send:
                continue
            overwritten = False
            for o in c.overwrites:
                try:
                    obj = o[0]
                except:
                    obj = o
                if obj == mute_role:
                    overwritten = True
                    break
            if overwritten:
                o = c.overwrites_for(mute_role)
                if o.send_messages:
                    try:
                        await c.set_permissions(mute_role, send_messages=False)
                    except discord.Forbidden:
                        raise commands.CommandError(
                            message=
                            f'Missing permissions: `channel_permission_overwrites`.'
                        )
            else:
                try:
                    await c.set_permissions(mute_role, send_messages=False)
                except discord.Forbidden:
                    raise commands.CommandError(
                        message=
                        f'Missing permissions: `channel_permission_overwrites`.'
                    )
            send = c.permissions_for(member).send_messages
            if send:
                try:
                    await c.set_permissions(member, send_messages=False)
                except discord.Forbidden:
                    raise commands.CommandError(
                        message=
                        f'Missing permissions: `channel_permission_overwrites`.'
                    )
示例#11
0
    async def unmute(self, ctx, member):
        '''
        Unmute a member. (Admin+)
        member: name, nickname, id, mention
        '''
        addCommand()
        await ctx.channel.trigger_typing()

        msg = ctx.message
        guild = ctx.guild

        temp = None
        if msg.mentions:
            temp = msg.mentions[0]
        if not temp:
            if is_int(member):
                temp = await guild.fetch_member(int(member))
        if not temp:
            temp = discord.utils.get(guild.members, display_name=member)
        if not temp:
            temp = discord.utils.get(guild.members, name=member)

        if not temp:
            raise commands.CommandError(
                message=f'Could not find member: `{member}`.')
        member = temp

        mute_role = discord.utils.find(lambda r: 'MUTE' in r.name.upper(),
                                       guild.roles)
        if not mute_role:
            raise commands.CommandError(message=f'Missing role: `muted`.')

        if not mute_role in member.roles:
            raise commands.CommandError(
                message=f'Error: `{member.display_name}` is not muted.')
        else:
            try:
                await member.remove_roles(mute_role)
            except discord.Forbidden:
                raise commands.CommandError(
                    message=f'Missing permissions: `role_management`.')

        mute = await Mute.query.where(Mute.guild_id == ctx.guild.id
                                      ).where(Mute.user_id == member.id
                                              ).gino.first()
        if mute:
            await mute.delete()

        await ctx.send(f'{member.mention} has been **unmuted**.')

        for c in guild.text_channels:
            send = c.permissions_for(member).send_messages
            if send:
                continue
            if member in c.overwrites:
                overwrite = c.overwrites[member]
                if not overwrite.pair()[1].send_messages:
                    try:
                        await c.set_permissions(member, send_messages=None)
                        c = guild.get_channel(c.id)
                        if member in c.overwrites:
                            overwrite = c.overwrites[member]
                            if overwrite[1].is_empty():
                                await c.set_permissions(member, overwrite=None)
                    except discord.Forbidden:
                        raise commands.CommandError(
                            message=
                            f'Missing permissions: `channel_permission_overwrites`.'
                        )
示例#12
0
import discord
import logging
import asyncio

from discord.ext import commands

from cogs.mixins import AceMixin
from utils.string import po
from utils.configtable import ConfigTable, ConfigTableRecord

log = logging.getLogger(__name__)

WELCOME_NOT_SET_UP_ERROR = commands.CommandError(
    'You don\'t seem to have set up a welcome message yet, do `welcome` to see available commands.'
)


class WelcomeRecord(ConfigTableRecord):
    @property
    def channel(self):
        if self.channel_id is None:
            return None

        guild = self._config.bot.get_guild(self.guild_id)
        if guild is None:
            return None

        return guild.get_channel(self.channel_id)


class Welcome(AceMixin, commands.Cog):
示例#13
0
    async def lyrics(self, ctx, *query):
        """Get song lyrics"""
        raw = requests.get(
            "https://api.genius.com/search?q={}&access_token={}".format(
                query, primebot.conf['genius_api_key']))
        raw = raw.json()
        titles = []
        i = 0
        j = 1
        s = ""
        reactions = ['1⃣', '2⃣', '3⃣', '4⃣', '5⃣', '6⃣', '7⃣', '8⃣', '9⃣']
        if not raw['response']['hits']:
            raise commands.CommandError("Song not Found")
        try:
            if raw['error'] == "invalid_token":
                raise commands.CommandError("Invalid Genius API Key")
        except KeyError:
            pass
        while i < 9:
            titles.append(raw['response']['hits'][i]['result']['full_title'])
            i += 1
        for title in titles:
            s += str(j) + " " + title + "\n"
            j += 1
        react_message = await ctx.send(s)
        for reaction in reactions[:len(titles)]:
            await react_message.add_reaction(reaction)

        # iterate over reactions
        try:

            def check(rctn, user):
                return user.id == ctx.author.id and str(rctn) in reactions

            rctn, user = await self.bot.wait_for("reaction_add",
                                                 check=check,
                                                 timeout=30)

            cache_msg = discord.utils.get(self.bot.cached_messages,
                                          id=react_message.id)
            for reaction in cache_msg.reactions:
                users = await reaction.users().flatten()
                for user in users:
                    if user == ctx.message.author:
                        selected_music = str(reaction)
        except asyncio.TimeoutError:
            pass

        # replace emoji with int
        reaction = str(selected_music)
        reaction = reaction.replace('1⃣', '1')
        reaction = reaction.replace('2⃣', '2')
        reaction = reaction.replace('3⃣', '3')
        reaction = reaction.replace('4⃣', '4')
        reaction = reaction.replace('5⃣', '5')
        reaction = reaction.replace('6⃣', '6')
        reaction = reaction.replace('7⃣', '7')
        reaction = reaction.replace('8⃣', '8')
        reaction = reaction.replace('9⃣', '9')

        lyric_url = raw['response']['hits'][int(
            str(reaction))]['result']['url']
        song_title = raw['response']['hits'][int(
            str(reaction))]['result']['full_title']

        lyrics = scrape_song_lyrics(lyric_url)
        emb = discord.Embed(title=f"{song_title}",
                            description=f"{lyrics}",
                            color=0xa3a3ff,
                            url=lyric_url)
        await ctx.send(embed=emb)
示例#14
0
    async def pug(self, ctx: commands.Context, *args):
        random_teams: bool = False
        map_arg: str = None
        team1_captain_arg: discord.Member = None
        team2_captain_arg: discord.Member = None
        for arg in args:
            if arg == 'random':
                random_teams = True
            elif arg.startswith('de_'):
                map_arg = arg
                if map_arg not in current_map_pool:
                    raise commands.CommandError(message=f'`{map_arg}` is not in Map Pool')
            else:
                member: discord.Member = await commands.MemberConverter().convert(ctx, arg)
                if member in ctx.author.voice.channel.members:
                    if team1_captain_arg is None:
                        team1_captain_arg = member
                    elif team2_captain_arg is None and member is not team1_captain_arg:
                        team2_captain_arg = member
                    else:
                        if member is team1_captain_arg:
                            raise commands.CommandError(message=f'One user cannot be captain of 2 teams.')
                        else:
                            raise commands.CommandError(message=f'You can only set 2 captains.')
                else:
                    raise commands.CommandError(message=f'Invalid Argument: `{arg}`')

        # TODO: Refactor this mess
        db = Database('sqlite:///main.sqlite')
        await db.connect()
        csgo_server = self.bot.servers[0]
        for server in self.bot.servers:
            if server.available:
                server.available = False
                csgo_server = server
                break
        channel_original = ctx.author.voice.channel
        players: List[discord.Member] = ctx.author.voice.channel.members.copy()
        players = players[0: 13]
        if self.bot.dev:
            players = [ctx.author] * 10

        if random_teams:
            shuffle(players)
            team1 = players[:len(players) // 2]
            team2 = players[len(players) // 2:]
            team1_captain = team1[0]
            team2_captain = team2[0]
            message_text = 'Random Teams'
            message = await ctx.send(message_text)
            embed = self.player_veto_embed(message_text=message_text, players_text='Random Teams', team1=team1,
                                           team1_captain=team1_captain, team2=team2, team2_captain=team2_captain)
            await message.edit(content=message_text, embed=embed)
        else:
            emojis = emoji_bank.copy()
            del emojis[len(players) - 2:len(emojis)]
            emojis_selected = []
            team1 = []
            team2 = []
            if team1_captain_arg is not None:
                team1_captain = team1_captain_arg
            else:
                team1_captain = players[randint(0, len(players) - 1)]
            team1.append(team1_captain)
            players.remove(team1_captain)

            if team2_captain_arg is not None:
                team2_captain = team2_captain_arg
            else:
                team2_captain = players[randint(0, len(players) - 1)]
            team2.append(team2_captain)
            players.remove(team2_captain)

            current_team_player_select = 1

            current_captain = team1_captain
            player_veto_count = 0

            message = await ctx.send('10 man time\nLoading player selection...')
            for emoji in emojis:
                await message.add_reaction(emoji)

            emoji_remove = []

            while len(players) > 0:
                message_text = ''
                players_text = ''

                if current_team_player_select == 1:
                    message_text += f'<@{team1_captain.id}>'
                    current_captain = team1_captain
                elif current_team_player_select == 2:
                    message_text += f'<@{team2_captain.id}>'
                    current_captain = team2_captain

                message_text += f' select {player_veto[player_veto_count]}\n'
                message_text += 'You have 60 seconds to choose your player(s)\n'

                i = 0
                for player in players:
                    players_text += f'{emojis[i]} - <@{player.id}>\n'
                    i += 1
                embed = self.player_veto_embed(message_text=message_text, players_text=players_text, team1=team1,
                                               team1_captain=team1_captain, team2=team2, team2_captain=team2_captain)
                await message.edit(content=message_text, embed=embed)
                if len(emoji_remove) > 0:
                    for emoji in emoji_remove:
                        await message.clear_reaction(emoji)
                    emoji_remove = []

                selected_players = 0
                seconds = 0
                while True:
                    await asyncio.sleep(1)
                    message = await ctx.fetch_message(message.id)

                    for reaction in message.reactions:
                        users = await reaction.users().flatten()
                        if current_captain in users and selected_players < player_veto[player_veto_count] and not (
                                reaction.emoji in emojis_selected):
                            index = emojis.index(reaction.emoji)
                            if current_team_player_select == 1:
                                team1.append(players[index])
                            if current_team_player_select == 2:
                                team2.append(players[index])
                            emojis_selected.append(reaction.emoji)
                            emoji_remove.append(reaction.emoji)
                            del emojis[index]
                            del players[index]
                            selected_players += 1

                    seconds += 1

                    if seconds % 60 == 0:
                        for _ in range(0, player_veto[player_veto_count]):
                            index = randint(0, len(players) - 1)
                            if current_team_player_select == 1:
                                team1.append(players[index])
                            if current_team_player_select == 2:
                                team2.append(players[index])
                            emojis_selected.append(emojis[index])
                            del emojis[index]
                            del players[index]
                            selected_players += 1

                    if selected_players == player_veto[player_veto_count]:
                        if current_team_player_select == 1:
                            current_team_player_select = 2
                        elif current_team_player_select == 2:
                            current_team_player_select = 1
                        break

                player_veto_count += 1

        if map_arg is None:
            message_text = 'Map Veto Loading'
        else:
            message_text = f'Map is `{map_arg}`'
        players_text = 'None'
        embed = self.player_veto_embed(message_text=message_text, players_text=players_text, team1=team1,
                                       team1_captain=team1_captain, team2=team2, team2_captain=team2_captain)
        await message.edit(content=message_text, embed=embed)
        await message.clear_reactions()

        if map_arg is not None:
            chosen_map_embed = await self.get_chosen_map_embed(map_arg)
            await ctx.send(embed=chosen_map_embed)

        team1_steamIDs = []
        team2_steamIDs = []

        if ctx.author.voice.channel.category is None:
            team1_channel = await ctx.guild.create_voice_channel(name=f'team_{team1_captain.display_name}',
                                                                 user_limit=7)
            team2_channel = await ctx.guild.create_voice_channel(name=f'team_{team2_captain.display_name}',
                                                                 user_limit=7)
        else:
            team1_channel = await ctx.author.voice.channel.category.create_voice_channel(
                name=f'team_{team1_captain.display_name}', user_limit=7)
            team2_channel = await ctx.author.voice.channel.category.create_voice_channel(
                name=f'team_{team2_captain.display_name}', user_limit=7)

        for player in team1:
            await player.move_to(channel=team1_channel, reason=f'You are on {team1_captain}\'s Team')
            data = await db.fetch_one('SELECT steam_id FROM users WHERE discord_id = :player',
                                      {"player": str(player.id)})
            team1_steamIDs.append(data[0])

        for player in team2:
            await player.move_to(channel=team2_channel, reason=f'You are on {team2_captain}\'s Team')
            data = await db.fetch_one('SELECT steam_id FROM users WHERE discord_id = :player',
                                      {"player": str(player.id)})
            team2_steamIDs.append(data[0])

        if map_arg is None:
            map_list = await self.map_veto(ctx, team1_captain, team2_captain)
        else:
            map_list = map_arg

        bot_ip = self.bot.web_server.IP
        if self.bot.bot_IP != "":
            bot_ip = self.bot.bot_IP

        team1_country = 'IE'
        team2_country = 'IE'
        session = aiohttp.ClientSession()
        async with session.get(f'http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/'
                               f'?key={self.bot.steam_web_api_key}'
                               f'&steamids={SteamID(team1_steamIDs[0]).as_64},{SteamID(team2_steamIDs[0]).as_64}') as resp:
            captain_info = await resp.json()
            if 'loccountrycode' in captain_info['response']['players'][0]:
                team1_country = captain_info['response']['players'][0]['loccountrycode']
            if 'loccountrycode' in captain_info['response']['players'][1]:
                team2_country = captain_info['response']['players'][1]['loccountrycode']
        await session.close()

        match_config = {
            'matchid': f'PUG-{date.today().strftime("%d-%B-%Y")}',
            'num_maps': 1,
            'maplist': map_list,
            'skip_veto': True,
            'veto_first': 'team1',
            'side_type': 'always_knife',
            'players_per_team': len(team2),
            'min_players_to_ready': 1,
            'team1': {
                'name': f'team_{team1_captain.display_name}',
                'tag': 'team1',
                'flag': team1_country,
                'players': team1_steamIDs
            },
            'team2': {
                'name': f'team_{team2_captain.display_name}',
                'tag': 'team2',
                'flag': team2_country,
                'players': team2_steamIDs
            },
            'cvars': {
                'get5_event_api_url': f'http://{bot_ip}:{self.bot.web_server.port}/',
                'get5_print_damage': 1
            }
        }

        with open('./match_config.json', 'w') as outfile:
            json.dump(match_config, outfile, ensure_ascii=False, indent=4)

        await ctx.send('If you are coaching, once you join the server, type .coach')
        loading_map_message = await ctx.send('Server is being configured')
        await asyncio.sleep(0.3)
        valve.rcon.execute((csgo_server.server_address, csgo_server.server_port), csgo_server.RCON_password,
                           'exec triggers/get5')
        await asyncio.sleep(10)
        await loading_map_message.delete()
        valve.rcon.execute((csgo_server.server_address, csgo_server.server_port), csgo_server.RCON_password,
                           f'get5_loadmatch_url "{bot_ip}:{self.bot.web_server.port}/match"')

        await asyncio.sleep(5)
        connect_embed = await self.connect_embed(csgo_server)
        await ctx.send(embed=connect_embed)
        score_embed = discord.Embed()
        score_embed.add_field(name='0', value=f'team_{team1_captain.display_name}', inline=True)
        score_embed.add_field(name='0', value=f'team_{team2_captain.display_name}', inline=True)
        score_message = await ctx.send('Match in Progress', embed=score_embed)

        csgo_server.get_context(ctx=ctx, channels=[channel_original, team1_channel, team2_channel],
                                players=team1 + team2, score_message=score_message)
        csgo_server.set_team_names(['team1', 'team2'])
        self.bot.web_server.add_server(csgo_server)

        if not self.pug.enabled:
            self.queue_check.start()
示例#15
0
文件: roles.py 项目: ozzhates/AceBot
    async def spawn(self, ctx):
        '''Spawn role selectors.'''

        await ctx.message.delete()

        conf = await self.config.get_entry(ctx.guild.id)

        selectors = await self.db.fetch(
            '''SELECT rs.*
			FROM role_selector as rs
			JOIN unnest($1::INTEGER[]) WITH ORDINALITY t(id, ord) USING (id)
			WHERE id=ANY($1::INTEGER[])
			ORDER BY t.ord
			''', conf.selectors)

        if not selectors:
            raise commands.CommandError(
                'No selectors configured. Do `roles editor` to set one up.')

        if any(not selector.get('roles') for selector in selectors):
            raise commands.CommandError(
                'You have empty selectors. Delete these or add roles to them before spawning.'
            )

        if conf.message_ids:
            channel = ctx.guild.get_channel(conf.channel_id)
            if channel:
                for message_id in conf.message_ids:
                    try:
                        msg = await channel.fetch_message(message_id)
                        if msg:
                            await msg.delete()
                    except discord.HTTPException:
                        pass

        msgs = list()

        async def delete_all():
            for m in msgs:
                try:
                    await m.delete()
                except discord.HTTPException:
                    pass

        for selector in selectors:

            # https://stackoverflow.com/questions/866465/order-by-the-in-value-list
            roles = await self.db.fetch(
                '''
				SELECT re.* 
				FROM role_entry as re 
				JOIN unnest($1::INTEGER[]) WITH ORDINALITY t(id, ord) USING (id) 
				WHERE id=ANY($1::INTEGER[])
				ORDER BY t.ord
				''', selector.get('roles'))

            if not roles:
                continue

            e = discord.Embed(
                description=selector.get('description')
                or 'Click the reactions to give or remove roles.')

            icon = selector.get('icon')

            e.set_author(name=selector.get('title') or 'Role Selector',
                         icon_url=icon if icon else ctx.guild.icon_url)

            for role in roles:
                e.add_field(name='{} {}'.format(role.get('emoji'),
                                                role.get('name')),
                            value=role.get('description'),
                            inline=selector.get('inline'))

            msg = await ctx.send(embed=e)

            msgs.append(msg)

            try:
                for role in roles:
                    emoj = role.get('emoji')
                    await msg.add_reaction(emoj)
            except discord.HTTPException:
                await delete_all()
                raise commands.CommandError(
                    'Failed adding the emoji {}.\nIf the emoji has been deleted, change it in the editor.'
                    .format(emoj))

        await conf.update(channel_id=ctx.channel.id,
                          message_ids=list(msg.id for msg in msgs))
示例#16
0
class Money(commands.Cog, command_attrs=(dict(cooldown_after_parsing=True))):
    def __init__(self, bot):
        self.bot = bot

    @commands.cooldown(1, 60 * 60 * 24, commands.BucketType.user)
    @commands.command(name="daily")
    async def daily(self, ctx):
        """ Get some free coins! """

        daily_money = random.randint(250, 2_500)

        await self.bot.pool.execute(BankSQL.ADD_MONEY, ctx.author.id,
                                    daily_money)

        await ctx.send(f"You gained **${daily_money}**!")

    @commands.command(name="balance", aliases=["bal", "money"])
    async def balance(self, ctx, target: DiscordUser(author_ok=True) = None):
        """ Show the bank balances of the user, or supply an optional target user. """

        target = target if target is not None else ctx.author

        bal = await utils.bank.get_user_bal(ctx.bot.pool, target)

        await ctx.send(
            f":moneybag: **{target.display_name}** has **${bal['money']:,}**.")

    @commands.cooldown(1, 60 * 60, commands.BucketType.user)
    @commands.command(name="steal")
    async def steal_coins(self, ctx, target: DiscordUser()):
        """ Attempt to steal from another user. """

        if random.randint(0, 2) != 0:
            return await ctx.send(
                f"You stole nothing from **{target.display_name}**")

        bals = await utils.bank.get_users_bals(ctx.bot.pool,
                                               author=ctx.author,
                                               target=target)

        author_bal = bals["author"]
        target_bal = bals["target"]

        max_amount = random.randint(
            1, int(min(author_bal["money"], target_bal["money"]) * 0.05))

        stolen_amount = min(10_000, max_amount)

        await self.bot.pool.execute(BankSQL.ADD_MONEY, ctx.author.id,
                                    stolen_amount)
        await self.bot.pool.execute(BankSQL.SUB_MONEY, target.id,
                                    stolen_amount)

        await ctx.send(
            f"You stole **${stolen_amount:,}** from **{target.display_name}**")

    @commands.cooldown(1, 30, commands.BucketType.user)
    @commands.command(name="gift", aliases=["give"])
    async def gift(self, ctx, target: DiscordUser(),
                   amount: Range(1, 1_000_000)):
        """ Gift some money to another user. """

        bal = await utils.bank.get_author_bal(ctx)

        if bal["money"] < amount:
            raise commands.CommandError("You do not have enough money.")

        await self.bot.pool.execute(BankSQL.SUB_MONEY, ctx.author.id, amount)
        await self.bot.pool.execute(BankSQL.ADD_MONEY, target.id, amount)

        await ctx.send(f"You gave **${amount:,}** to **{target.display_name}**"
                       )
示例#17
0
    async def _play(self, ctx: commands.Context, *, search: str):
        """Plays a song
        
        Can either find a song via its Youtube URL or its keywords
        """

        if not ctx.voice_state.voice:
            await ctx.invoke(self._join)

        httpFlag = False

        if (search.__contains__("https://")):
            httpFlag = True
        else:
            search = "ytsearch10:" + search

        async with ctx.typing():
            try:
                if not httpFlag:
                    searchList = await YTDLSource.find_source(
                        ctx, search, loop=self.bot.loop)
                else:
                    source = await YTDLSource.create_source(ctx,
                                                            search,
                                                            loop=self.bot.loop)

            except YTDLError as e:
                await ctx.send(
                    'An error occurred while processing this request: {}'.
                    format(str(e)))
                await ctx.voice_state.stop()
                del self.voice_states[ctx.guild.id]
            else:
                if not httpFlag:
                    embed = (discord.Embed(
                        title='These are the results I found:',
                        color=discord.Color.blurple()))
                    i = 0
                    for entry in searchList:
                        if i == 5:
                            break
                        else:
                            try:
                                embed.add_field(name='Song #%d' % (i + 1),
                                                value=str(entry['title']),
                                                inline=False)
                                i += 1
                            except KeyError:
                                pass

                    sent = await ctx.send(
                        embed=embed)  #creates a message object called "sent"

                    i = 0
                    reactionEmotes = ['1️⃣', '2️⃣', '3️⃣', '4️⃣', '5️⃣', '❌']
                    for i in range(0, 6):
                        await sent.add_reaction(reactionEmotes[i])

                    def check(reaction, user):
                        if user == ctx.author:
                            if str(reaction.emoji) == '1️⃣' or str(
                                    reaction.emoji
                            ) == '2️⃣' or str(reaction.emoji) == '3️⃣' or str(
                                    reaction.emoji) == '4️⃣' or str(
                                        reaction.emoji) == '5️⃣' or str(
                                            reaction.emoji) == '❌':
                                return True
                            else:
                                return False
                        else:
                            return False

                    def convertToIdx(emote):
                        if str(emote) == '1️⃣':
                            return 0
                        elif str(emote) == '2️⃣':
                            return 1
                        elif str(emote) == '3️⃣':
                            return 2
                        elif str(emote) == '4️⃣':
                            return 3
                        elif str(emote) == '5️⃣':
                            return 4
                        elif str(emote) == '❌':
                            return -1
                        else:
                            return -2

                    try:
                        result = await self.bot.wait_for('reaction_add',
                                                         timeout=30.0,
                                                         check=check)
                        opt = convertToIdx(result[0])
                        if opt < 0:
                            await sent.delete()
                            if ctx.voice_state.current == None:
                                await ctx.voice_state.stop()
                                del self.voice_states[ctx.guild.id]
                            if (opt == -2):
                                raise commands.CommandError(
                                    'Invalid reaction!')

                    except asyncio.TimeoutError:
                        await ctx.voice_state.stop()
                        del self.voice_states[ctx.guild.id]
                    else:
                        if (opt >= 0):
                            await sent.delete()
                            urlPost = searchList[opt]['url']
                            ytURL = "https://www.youtube.com/watch?v=" + urlPost
                            source = await YTDLSource.create_source(
                                ctx, ytURL, loop=self.bot.loop)
                            song = Song(source)
                            await ctx.voice_state.songs.put(song)
                            await ctx.send('Enqueued {}'.format(str(source)))

                else:
                    song = Song(source)
                    await ctx.voice_state.songs.put(song)
                    await ctx.send('Enqueued {}'.format(str(source)))
示例#18
0
async def match_size_check(ctx: commands.Context):
    if ctx.author.voice is not None and (len(ctx.author.voice.channel.members) < ctx.bot.match_size and not ctx.bot.dev):
        raise commands.CommandError(message=f'There must be {ctx.bot.match_size} members connected to the voice channel')
    return True
示例#19
0
async def voice_channel(ctx: commands.Context):
    if ctx.author.voice is None:
        raise commands.CommandError(message='You must be in a voice channel.')
    return True
示例#20
0
 async def ensure_call(self, ctx):
     if (not ctx.author.voice
             or ctx.author.voice.channel != ctx.voice_client.channel):
         await ctx.send("You are not in the same voice channel as the bot.")
         raise commands.CommandError(
             "Author not connected to the correct voice channel.")
示例#21
0
 async def think_command(self, ctx: disextc.Context, text_id: str):
     if self._synapse_0 is None or self._synapse_1 is None:
         raise disextc.CommandError(f"I have no mind.")
     results = await self.think(text_id=text_id)
     await ctx.send(f'think results: ```{results}```')
示例#22
0
 async def ensure_voice_state(self, ctx: commands.Context):
     if not ctx.author.voice or not ctx.author.voice.channel:
         raise commands.CommandError(
             'You are not connected to any voice channel.')
示例#23
0
    async def get_url(self, ctx, thing):  # sourcery no-metrics
        if ctx.message.reference:
            if ctx.message.reference.cached_message:
                if (ctx.message.reference.cached_message.embeds
                        and ctx.message.reference.cached_message.embeds[0].type
                        == "image"):
                    url = ctx.message.reference.cached_message.embeds[
                        0].thumbnail.proxy_url
                    url = url.replace("cdn.discordapp.com",
                                      "media.discordapp.net")
                    return url
                elif (ctx.message.reference.cached_message.embeds
                      and ctx.message.reference.cached_message.embeds[0].type
                      == "rich"):
                    url = ctx.message.reference.cached_message.embeds[
                        0].image.proxy_url
                    url = url.replace("cdn.discordapp.com",
                                      "media.discordapp.net")
                    return url
                elif (ctx.message.reference.cached_message.attachments and
                      ctx.message.reference.cached_message.attachments[0].width
                      and ctx.message.reference.cached_message.attachments[0].
                      height):
                    url = ctx.message.reference.cached_message.attachments[
                        0].proxy_url
                    url = url.replace("cdn.discordapp.com",
                                      "media.discordapp.net")
                    return url
            else:
                message = await self.bot.get_channel(
                    ctx.message.reference.channel_id
                ).fetch_message(ctx.message.reference.message_id)
                if message.embeds and message.embeds[0].type == "image":
                    url = message.embeds[0].thumbnail.proxy_url
                    url = url.replace("cdn.discordapp.com",
                                      "media.discordapp.net")
                    return url
                elif (message.attachments and message.attachments[0].width
                      and message.attachments[0].height):
                    url = message.attachments[0].proxy_url
                    url = url.replace("cdn.discordapp.com",
                                      "media.discordapp.net")
                    return url

        if (ctx.message.attachments and ctx.message.attachments[0].width
                and ctx.message.attachments[0].height):
            return ctx.message.attachments[0].proxy_url.replace(
                "cdn.discordapp.com", "media.discordapp.net")

        if thing is None:
            url = str(ctx.author.avatar_url_as(format="png"))
        elif isinstance(thing, (discord.PartialEmoji, discord.Emoji)):
            url = str(thing.url)
        elif isinstance(thing, (discord.Member, discord.User)):
            url = str(thing.avatar_url_as(format="png"))
        else:
            thing = str(thing).strip("<>")
            if (thing.startswith("http") or thing.startswith("https")
                    or thing.startswith("www")):
                url = thing
            else:
                url = await emoji_to_url(thing)
        async with self.bot.session.get(url) as resp:
            if resp.status != 200:
                raise commands.CommandError("Invalid Picture")
        url = url.replace("cdn.discordapp.com", "media.discordapp.net")
        return url
示例#24
0
    async def create(self, ctx, backup_id, name, *, description):
        """
        Turn a private backup into a PUBLIC template.


        backup_id   ::      The id of the backup that you want to turn into a template

        name        ::      A name for the template

        description ::      A description for the template
        """
        name = name.lower().replace(" ", "_")
        backup = await ctx.db.backups.find_one(backup_id)
        if backup is None or backup.get("creator") != ctx.author.id:
            raise cmd.CommandError(
                f"You have **no backup** with the id `{backup_id}`.")

        already_exists = (await ctx.db.templates.find_one(name)) is not None
        if already_exists:
            raise cmd.CommandError(
                f"There is **already a template with that name**, please choose another one."
            )

        backup["backup"]["members"] = []

        warning = await ctx.send(**ctx.em(
            "Are you sure you want to turn this backup into a template? **All templates are public!**",
            type="warning"))
        await warning.add_reaction("✅")
        await warning.add_reaction("❌")
        try:
            reaction, user = await self.bot.wait_for(
                "reaction_add",
                check=lambda r, u: r.message.id == warning.id and u.id == ctx.
                author.id,
                timeout=60)
        except TimeoutError:
            await warning.delete()
            raise cmd.CommandError(
                "Please make sure to **click the ✅ reaction** in order to create a template."
            )

        if str(reaction.emoji) != "✅":
            await warning.delete()
            return

        template = {
            "_id": name,
            "creator": backup["creator"],
            "used": 0,
            "featured": False,
            "approved": False,
            "original": backup_id,
            "description": description,
            "template": backup["backup"]
        }
        await ctx.db.templates.insert_one(template)
        await ctx.send(**ctx.em(
            "Successfully **created template**.\n"
            f"The template **will not be available** until a moderator approves it.\n"
            f"Please join the [support server](https://discord.club/discord) and enable direct "
            f"messages to get updates about your template.",
            type="success"))
        await self.approval_webhook.send(embed=self._template_info(template))
示例#25
0
    async def play(self, ctx, *, url=None, showembed=True):
        if (url == None):
            url = "https://youtu.be/dQw4w9WgXcQ"
            embed = discord.Embed(color=0xc061cb)
            embed.add_field(name="p.play [ url / search / spotify url ]  ",
                            value="```-loop number``` to loop the song",
                            inline=False)
            embed.add_field(name="p.play <>  -loop number",
                            value="loops the song in queue",
                            inline=False)
            embed.add_field(name="p.queue",
                            value="shows the current queue",
                            inline=False)
            embed.add_field(name="p.move [ from ] [ to ]",
                            value="moves song index : from → to",
                            inline=False)
            embed.add_field(name="p.remove [ index ]",
                            value="removes the last song by default ",
                            inline=False)
            embed.add_field(name="p.nowplaying / p.np",
                            value="shows now playing song",
                            inline=False)
            embed.add_field(name="p.clearqueue / p.cq",
                            value="clears the queue",
                            inline=False)
            embed.add_field(name="p.lofi",
                            value="plays lofigirl's lofi stream",
                            inline=False)
            embed.add_field(name="p.pod",
                            value="Show Podcast help section",
                            inline=True)
            embed.set_footer(text="nub now get rollllled :D")
            await ctx.send(embed=embed)

        url_split = url.split("-loop")
        try:
            loopcount = int(url_split[1])
        except:
            loopcount = 0

        url = url_split[0]

        if (loopcount > 0):
            await ctx.send("> looping the song".format(loopcount))
            progbar = await ctx.send("```[==========] 0%```")
            for i in range(int(loopcount)):
                prog = int((i / loopcount) * 100)
                await progbar.edit(
                    content=
                    f"```[{'#'*int(prog/10)} {'='*int(10-(prog/10))}] {prog}%```"
                )
                await ctx.invoke(self.client.get_command('play'),
                                 url=url,
                                 showembed=False)
            await progbar.edit(content=f"```[{'#'*10}] Done```")
            return
        try:
            if ("/playlist?list=" in url):
                await ctx.send("> Parsing playlists", delete_after=5.0)
                ytplaylist = basicYTPlaylist(url)
                progbar = await ctx.send("```[==========] 0%```")
                i = 0
                for song in ytplaylist:
                    tot = len(ytplaylist)
                    prog = int((i / tot) * 100)
                    i += 1
                    await progbar.edit(
                        content=
                        f"```[{'#'*int(prog/10)} {'='*int(10-(prog/10))}] {prog}%```"
                    )
                    await ctx.invoke(self.client.get_command('play'),
                                     url=song,
                                     showembed=False)
                await ctx.send("> Added {} songs to queue".format(
                    len(ytplaylist)))
                await progbar.edit(content=f"``` [{'#'*10}] Done```")
                return
        except Exception as e:
            await ctx.send(f"> Something somewhere went wrong \n ||{e}||",
                           delete_after=5.0)
            return

        try:
            timegx = r"[\?&]t=\d*"
            seekamt = int(re.findall(timegx, url)[0][1:].replace("t=", ''))
            await ctx.send(
                f"> Seeking based on url {seconds_to_hhmmss(seekamt)} hours")
        except IndexError:
            seekamt = 0

        url, whrc = handle_spotify(url)
        if (whrc == "SP"):
            await ctx.send("> Fetching from spotify", delete_after=5.0)
        client = ctx.guild.voice_client
        state = self.get_state(ctx.guild)  # get the guild's state

        if client and client.channel:
            try:
                video = Video(url, ctx.author, src=whrc)
            except youtube_dl.DownloadError as e:
                logging.warn(f"Error downloading video: {e}")
                await ctx.send("> Something went wrong in getting the video")
                return

            await ctx.message.add_reaction(Emotes.PACPLAY)
            state.playlist.append(video)
            if (showembed):
                message = await ctx.send("> Added to queue",
                                         embed=video.get_embed())
        else:
            if ctx.author.voice is not None and ctx.author.voice.channel is not None:
                channel = ctx.author.voice.channel
                try:
                    video = Video(url, ctx.author, whrc)
                except youtube_dl.DownloadError as e:
                    await ctx.send("> Something went wrong!! \n```{e}```")
                    return
                client = await channel.connect()
                self._play_song(client, state, video, seekamt)
                await ctx.message.add_reaction(Emotes.PACPLAY)
                message = await ctx.send("",
                                         embed=video.get_embed(),
                                         components=[
                                             Button(style=ButtonStyle.URL,
                                                    label="Youtube",
                                                    url=video.video_url)
                                         ])
                logging.info(f"Now playing '{video.title}'")
            else:
                await ctx.message.add_reaction(Emotes.PACNO)
                raise commands.CommandError(
                    "> I might be your music bot but I can't get your lazy ass to join voice channel :/"
                )
示例#26
0
文件: roles.py 项目: ozzhates/AceBot
    async def editor(self, ctx):
        '''Editor for selectors and roles.'''

        # ignore command input from user while editor is open
        self.set_editing(ctx)

        conf = await self.config.get_entry(ctx.guild.id)

        slcs = await self.db.fetch(
            '''
			SELECT rs.*
			FROM role_selector as rs
			JOIN unnest($1::INTEGER[]) WITH ORDINALITY t(id, ord) USING (id)
			WHERE id=ANY($1::INTEGER[])
			ORDER BY t.ord
			''', conf.selectors)

        selectors = list()

        for slc in slcs:
            roles = await self.db.fetch(
                '''
				SELECT re.* 
				FROM role_entry as re 
				JOIN unnest($1::INTEGER[]) WITH ORDINALITY t(id, ord) USING (id) 
				WHERE id=ANY($1::INTEGER[])
				ORDER BY t.ord
				''', slc.get('roles'))

            selector = Selector.from_record(
                slc, list(Role.from_record(role) for role in roles))
            selectors.append(selector)

        head = RoleHead(conf, selectors)

        # so converters can access the head for data integrity tests...
        ctx.head = head

        msg = await ctx.send(embed=discord.Embed(
            description='Please wait while reactions are being added...'))

        self.messages[ctx.guild.id] = msg

        for emoji in EMBED_EMOJIS:
            await msg.add_reaction(emoji)

        def pred(reaction, user):
            return reaction.message.id == msg.id and user.id == ctx.author.id

        async def close():
            self.unset_editing(ctx)
            try:
                await msg.delete()
                self.messages.pop(ctx.guild.id)
            except discord.HTTPException:
                pass

        while True:
            await msg.edit(embed=head.embed())

            try:
                reaction, user = await self.bot.wait_for('reaction_add',
                                                         check=pred,
                                                         timeout=300.0)
            except asyncio.TimeoutError:
                await close()
                raise commands.CommandError(
                    'Role editor closed after 5 minutes of inactivity.')
            else:
                await msg.remove_reaction(reaction.emoji, user)

                reac = str(reaction)

                if reac == ADD_SEL_EMOJI:
                    if len(head.selectors) > 7:
                        await ctx.send(embed=discord.Embed(
                            description='No more than 8 selectors, sorry!'),
                                       delete_after=6)
                        continue

                    selector_data = await self._multiprompt(
                        ctx, msg, NEW_SEL_PREDS)
                    if selector_data is None:
                        continue

                    selector = Selector(selector_data[0], None, list())
                    selector.set_dirty()

                    new_pos = 0 if not head.selectors else head.selector_pos + 1
                    head.add_selector(new_pos, selector)

                    head.selector_pos = new_pos
                    head.role_pos = None

                if reac == ABORT_EMOJI:
                    await close()
                    raise commands.CommandError(
                        'Editing aborted, no changes saved.')

                if reac == SAVE_EMOJI:
                    await head.store(ctx)
                    await close()
                    await ctx.send(
                        'New role selectors saved. Do `roles spawn` to see!')
                    break

                # rest of the actions assume at least one item (selector) is present
                if not head.selectors:
                    continue

                if reac == ADD_ROLE_EMOJI:
                    if len(head.selector.roles) > 24:
                        await ctx.send(embed=discord.Embed(
                            description=
                            'No more than 25 roles in one selector, sorry!'),
                                       delete_after=6)
                        continue

                    role_data = await self._multiprompt(
                        ctx, msg, NEW_ROLE_PREDS)
                    if role_data is None:
                        continue

                    role = Role(*role_data)

                    new_pos = 0 if head.role_pos is None else head.role_pos + 1
                    head.selector.add_role(new_pos, role)

                    head.role_pos = new_pos

                if reac == DOWN_EMOJI:
                    head.down()
                if reac == UP_EMOJI:
                    head.up()

                if reac in (MOVEUP_EMOJI, MOVEDOWN_EMOJI):
                    direction = -1 if reac == MOVEUP_EMOJI else 1

                    if head.role_pos is None:
                        head.move_selector(direction)
                    else:
                        head.move_role(direction)

                if reac == DEL_EMOJI:
                    if head.role_pos is None:

                        if len(head.selector.roles):
                            p = ctx.prompt(
                                'Delete selector?',
                                'The selector you\'re trying to delete has {} roles inside it.'
                                .format(len(head.selector.roles)))

                            if not await p:
                                continue

                        head.selectors.pop(head.selector_pos)
                        if head.selector_pos > head.selector_max:
                            head.selector_pos = head.selector_max
                        head.role_pos = None

                    else:
                        head.selector.roles.pop(head.role_pos)
                        if len(head.selector.roles) == 0:
                            head.role_pos = None
                        elif head.role_pos > head.role_max:
                            head.role_pos = head.role_max

                if reac == EDIT_EMOJI:
                    await self._edit_item(
                        ctx, msg, head.selector if head.role_pos is None else
                        head.selector.roles[head.role_pos])
示例#27
0
async def audio_playing(ctx):
    client = ctx.guild.voice_client
    if client and client.channel and client.source:
        return True
    else:
        raise commands.CommandError("Not currently playing any audio.")
示例#28
0
文件: roles.py 项目: ozzhates/AceBot
    async def _edit_item(self, ctx, msg, item):
        if isinstance(item, Selector):
            questions = dict(
                title=selector_title_converter,
                description=selector_desc_converter,
                inline=SelectorInlineConverter(),
            )
        elif isinstance(item, Role):
            questions = dict(
                name=role_title_converter,
                description=role_desc_converter,
                emoji=SelectorEmojiConverter(),
            )
        else:
            raise TypeError('Unknown item type: ' + str(type(item)))

        opts = {emoji: q for emoji, q in zip(EMBED_EMOJIS, questions.keys())}

        opt_string = '\n'.join('{} {}'.format(key, value)
                               for key, value in opts.items())

        e = discord.Embed(description='What would you like to edit?\n\n' +
                          opt_string)

        e.set_footer(text=ABORT_EMOJI + ' to abort.')

        await msg.edit(embed=e)

        def reac_pred(reaction, user):
            return reaction.message.id == msg.id and user.id == ctx.author.id

        while True:
            try:
                reaction, user = await self.bot.wait_for('reaction_add',
                                                         check=reac_pred,
                                                         timeout=300.0)
            except asyncio.TimeoutError:
                return
            else:
                await msg.remove_reaction(reaction.emoji, user)

                reac = str(reaction)

                if reac == ABORT_EMOJI:
                    return

                elif reac in opts.keys():
                    attr = opts[reac]
                    conv = questions[attr]
                    break

                else:
                    continue

        e.description = 'Please input a new value for \'{}\'.'.format(attr)
        e.set_footer(text='Send \'exit\' to abort.')
        await msg.edit(embed=e)

        def msg_pred(message):
            return message.channel.id == msg.channel.id and message.author.id == ctx.author.id

        while True:
            try:
                message = await self.bot.wait_for('message',
                                                  check=msg_pred,
                                                  timeout=60.0)
            except asyncio.TimeoutError:
                return

            await message.delete()

            if message.content.lower() == 'exit':
                return

            try:
                value = await conv.convert(ctx, message.content)
            except commands.CommandError as exc:
                if not msg.embeds:
                    try:
                        await msg.delete()
                    except discord.HTTPException:
                        pass
                    raise commands.CommandError(
                        'Embed seems to have been removed, aborting.')

                e = msg.embeds[0]
                e.set_footer(text='NOTE: ' + str(exc) + ' ' + RETRY_MSG)
                await msg.edit(embed=e)
                continue

            setattr(item, attr, value)
            item.set_dirty()
            return
示例#29
0
def author_is_server_owner(ctx):
	if ctx.author.id != ctx.guild.owner.id:
		raise commands.CommandError(f"You do not have access to this command.")

	return True
示例#30
0
    async def trivia(self,
                     ctx,
                     category: Optional[CategoryConverter] = None,
                     *,
                     difficulty: DifficultyConverter = None):
        '''Trivia time! Optionally specify a difficulty or category and difficulty as arguments. Valid difficulties are `easy`, `medium` and `hard`. Valid categories can be listed with `trivia categories`.'''

        raise commands.CommandError(
            'The trivia API is down currently. Sorry about that!')

        diff = difficulty

        if diff is None:
            diff = choice(list(Difficulty))

        params = dict(
            amount=1,
            encode='url3986',
            difficulty=diff.name.lower(),
        )

        # if we have a category ID, insert it into the query params for the question request
        if category is not None:
            params['category'] = choice(
                category) if category is list else category

        try:
            async with ctx.http.get(API_URL, params=params) as resp:
                if resp.status != 200:
                    self.trivia.reset_cooldown(ctx)
                    raise REQUEST_FAILED

                res = await resp.json()
        except asyncio.TimeoutError:
            self.trivia.reset_cooldown(ctx)
            raise REQUEST_FAILED

        result = res['results'][0]

        question_type = result['type']
        category = unquote(result['category'])
        correct_answer = unquote(result['correct_answer'])

        question = unquote(result['question'])
        question_hash = hash(question)

        if question_type == 'multiple':
            options = list(
                unquote(option) for option in result['incorrect_answers'])
            correct_pos = randrange(0, len(options) + 1)
            correct_emoji = MULTIPLE_MAP[correct_pos]
            options.insert(correct_pos, correct_answer)

        elif question_type == 'boolean':
            options = ('True', 'False')
            correct_emoji = BOOLEAN_MAP[int(correct_answer == 'False')]

        else:
            self.trivia.reset_cooldown(ctx)
            raise ValueError('Unknown question type: {}'.format(question_type))

        option_emojis = BOOLEAN_MAP if question_type == 'boolean' else MULTIPLE_MAP

        question_string = '{}\n\n{}\n'.format(
            question,
            '\n'.join('{} {}'.format(emoji, option)
                      for emoji, option in zip(option_emojis, options)))

        e = discord.Embed(
            title='Trivia time!',
            description='**Category**: {}\n**Difficulty**: {}'.format(
                category, diff.name.lower()),
            color=DIFFICULTY_COLORS[diff])
        e.add_field(name='Question', value=question_string, inline=False)
        e.set_footer(
            text=
            'Answer by pressing a reaction after all options have appeared.')

        msg = await ctx.send(embed=e)

        for emoji in option_emojis:
            await msg.add_reaction(emoji)

        now = datetime.utcnow()

        def check(reaction, user):
            return reaction.message.id == msg.id and user == ctx.author and str(
                reaction) in option_emojis

        try:
            reaction, user = await self.bot.wait_for('reaction_add',
                                                     check=check,
                                                     timeout=QUESTION_TIMEOUT)

            answered_at = datetime.utcnow()
            score = self._calculate_score(SCORE_POT[diff], answered_at - now)

            if str(reaction) == correct_emoji:
                # apply penalty if category was specified
                if 'category' in params:
                    score = int(score / CATEGORY_PENALTY)

                current_score = await self._on_correct(ctx, answered_at,
                                                       question_hash, score)

                e = discord.Embed(
                    title='{}  {}'.format(CORRECT_EMOJI,
                                          choice(CORRECT_MESSAGES)),
                    description='You gained {} points.'.format(score),
                    color=discord.Color.green())

                e.set_footer(text=FOOTER_FORMAT.format(current_score))
                await ctx.send(embed=e)
            else:
                score = int(score / PENALTY_DIV)
                current_score = await self._on_wrong(ctx, answered_at,
                                                     question_hash, score)

                e = discord.Embed(
                    title='{}  {}'.format(WRONG_EMOJI, choice(WRONG_MESSAGES)),
                    description='You lost {} points.'.format(score),
                    color=discord.Color.red())

                e.set_footer(text=FOOTER_FORMAT.format(current_score))

                if question_type == 'multiple':
                    e.description += '\nThe correct answer is ***`{}`***'.format(
                        correct_answer)

                await ctx.send(embed=e)

        except asyncio.TimeoutError:
            score = int(SCORE_POT[diff] / 4)
            answered_at = datetime.utcnow()

            try:
                await msg.clear_reactions()
            except discord.HTTPException:
                pass

            await ctx.send(
                'Question timed out and you lost {} points. Answer within {} seconds next time!'
                .format(score, int(QUESTION_TIMEOUT)))

            await self._on_wrong(ctx, answered_at, question_hash, score)