Exemple #1
0
    async def convert(self, ctx, argument):
        match = MESSAGE_ID_RE.match(argument) or MESSAGE_LINK_RE.match(
            argument)
        if not match:
            raise errors.BadArgument(
                "{} doesn't look like a message to me...".format(argument))

        msg_id = int(match.group("message_id"))
        channel_id = int(match.group("channel_id") or ctx.channel.id)
        channel = ctx.guild.get_channel(channel_id)
        if not channel:
            channel = ctx.bot.get_channel(channel_id)

        if not channel:
            raise errors.BadArgument("Channel {} not found".format(channel_id))

        author = channel.guild.get_member(ctx.author.id)

        if not channel.guild.me.permissions_in(channel).read_messages:
            raise errors.CheckFailure(
                "I don't have permission to view channel {0.mention}".format(
                    channel))
        if not author or not channel.permissions_for(author).read_messages:
            raise errors.CheckFailure(
                "You don't have permission to view channel {0.mention}".format(
                    channel))

        return (channel, msg_id)
Exemple #2
0
    async def convert(self, ctx, argument):
        match = MESSAGE_ID_RE.match(argument) or MESSAGE_LINK_RE.match(
            argument)
        if not match:
            cleaned = await clean_content().convert(
                ctx, f"{argument} doesn't look like a message to me…")
            raise errors.BadArgument(cleaned)

        msg_id = int(match['message_id'])
        channel_id = int(match['channel_id'] or ctx.channel.id)
        channel = ctx.guild.get_channel(channel_id)
        if not channel:
            channel = ctx.bot.get_channel(channel_id)

        if not channel:
            raise errors.BadArgument(f'Channel {channel_id} not found.')

        author = channel.guild.get_member(ctx.author.id)

        if not channel.guild.me.permissions_in(channel).read_messages:
            raise errors.CheckFailure(
                f"I don't have permission to view channel {channel.mention}.")
        if not author or not channel.permissions_for(author).read_messages:
            raise errors.CheckFailure(
                f"You don't have permission to view channel {channel.mention}."
            )

        return (channel, msg_id)
Exemple #3
0
    async def star(self, ctx, *, msg):
        """Create a star out of a string 1-25 characters long."""
        if (len(msg) > 25):
            raise errors.BadArgument("String must be less than 26 characters")
        elif (len(msg) == 0):
            raise errors.BadArgument("String must be at least 1 character")

        str = '```\n'

        mid = len(msg) - 1

        for i in range(len(msg) * 2 - 1):
            if (mid == i):
                str += msg[::-1] + msg[1:] + "\n"
            else:
                let = abs(mid - i)
                str += " " * (mid - let)
                str += msg[let]
                str += " " * (let - 1)
                str += msg[let]
                str += " " * (let - 1)
                str += msg[let]
                str += "\n"

        str += "```"
        await ctx.send(str)
Exemple #4
0
def resolve_color(value):
    """Resolve a custom or pre-defined color.

    This allows html style #RRGGBB and #AARRGGBB

    Returns (r, g, b) or (a, r, g, b)
    """
    if value.startswith('#'):
        value = value[1:]  # assumes no named color starts with #

    try:
        intval = int(value, 16)
    except ValueError:
        pass
    else:
        if intval >= (1 << 32):
            raise errors.BadArgument("Invalid color {} is too big!".format(value))
        if len(value) > 6:
            color = discord.Colour(intval)
            return (color.r, color.g, color.b, color._get_byte(3))
        return discord.Colour(intval).to_rgb()
    try:
        return getattr(discord.Colour, value)().to_rgb()
    except AttributeError:
        raise errors.BadArgument("Invalid color {}".format(value))
Exemple #5
0
async def fetch_image(url):
    """Fetch the given image."""
    url = str(url)
    # Workaround https://github.com/aio-libs/aiohttp/issues/3426            
    async with aiohttp.ClientSession(
            connector=aiohttp.TCPConnector(enable_cleanup_closed=True)) as sess:
        # proxy_url must be passed exactly - encoded=True
        # https://github.com/aio-libs/aiohttp/issues/3424#issuecomment-443760653
        async with sess.get(yarl.URL(url, encoded=True)) as resp:
            resp.raise_for_status()
            content_length = int(resp.headers.get('Content-Length', 50<<20))
            if content_length > 50<<20:
                raise errors.BadArgument("File too big")

            blocks = []
            readlen = 0
            tested_image = False
            # Read up to X bytes, raise otherwise
            while True:
                block = await resp.content.readany()
                if not block:
                    break
                blocks.append(block)
                readlen += len(block)
                if readlen >= 10<<10 and not tested_image:
                    try:
                        Image.open(io.BytesIO(b''.join(blocks)))
                    except OSError:
                        raise errors.BadArgument("This doesn't look like an image to me")
                    else:
                        tested_image = True
                if readlen > content_length:
                    raise errors.BadArgument("File too big")
            source_bytes = b''.join(blocks)
    return source_bytes
Exemple #6
0
 async def countdown(self, ctx, seconds: int = 3):
     """3... 2... 1... Go!"""
     if seconds > 10:
         raise errors.BadArgument("No more than 10 seconds, thanks")
     if seconds < 0:
         raise errors.BadArgument("No negative numbers, thanks")
     while seconds:
         await ctx.send("%d..." % seconds)
         await asyncio.sleep(1)
         seconds -= 1
     await ctx.send("Go!")
Exemple #7
0
    async def msg(self, ctx, msg: converters.MessageConverter):
        """Delete a specific message created by the bot.

        Use developer mode to be able to copy a message id in context menu."""
        if not msg:
            raise errors.BadArgument("Could not find a message by that id!")

        if msg.author != ctx.bot.user:
            raise errors.BadArgument("I didn't make that message!")

        await msg.delete()
        await ctx.send("Message {} deleted.".format(msg.id), delete_after=5)
    async def osu(self, ctx, *, account: StringOrMentionConverter = None):
        """Show a user's osu profile.
        Use + to give a raw account name. e.g.:
        osu +cookiezi
        osu @ppy
        """
        account = account or ctx.message.author

        mode = {
            'osu': OsuMode.osu,
            'taiko': OsuMode.taiko,
            'mania': OsuMode.mania,
            'ctb': OsuMode.ctb
        }[ctx.invoked_with]

        with ctx.typing():
            if account is None:
                raise errors.BadArgument("Invalid mention...!")

            if isinstance(account, discord.abc.User):
                osu_acct = await self._get_osu_account(ctx, account, mode)
            else:
                osu_acct = await self._lookup_acct(account, mode=mode)

            usrscore = await self.osuapi.get_user_best(osu_acct.user_id,
                                                       limit=100,
                                                       mode=mode)

        embed = discord.Embed()
        embed.title = osu_acct.username
        embed.url = "https://osu.ppy.sh/u/%s" % osu_acct.user_id
        embed.color = hash(str(osu_acct.user_id)) % (1 << 24)
        if isinstance(account, discord.abc.User):
            embed.set_author(
                name=str(account),
                icon_url=account.avatar_url_as(static_format="png"))
        embed.set_thumbnail(url="http://a.ppy.sh/%s?_=%s" %
                            (osu_acct.user_id, time.time()))

        if not usrscore:
            embed.description = "%s has never played %s" % (osu_acct.username,
                                                            ctx.invoked_with)
        else:
            embed.description = "#{0.pp_rank:,} ({0.pp_raw} pp)".format(
                osu_acct)
            fave_mod = collections.Counter(
                play.enabled_mods for play in usrscore).most_common()[0][0]
            bplay = usrscore[0]
            embed.add_field(name="Plays",
                            value="{:,}".format(osu_acct.playcount))
            embed.add_field(name="Hits",
                            value="{:,}".format(osu_acct.total_hits))
            embed.add_field(name="Acc",
                            value="{:.2f}".format(osu_acct.accuracy))
            embed.add_field(name="Best Play",
                            value="{:,}pp {:s}".format(bplay.pp,
                                                       bplay.enabled_mods))
            embed.add_field(name="Favorite Mod", value="{:l}".format(fave_mod))

        await ctx.send(embed=embed)
Exemple #9
0
    async def test_handle_input_error_handler_errors(self):
        """Should handle each error probably."""
        test_cases = ({
            "error": errors.MissingRequiredArgument(MagicMock()),
            "call_prepared": True
        }, {
            "error": errors.TooManyArguments(),
            "call_prepared": True
        }, {
            "error": errors.BadArgument(),
            "call_prepared": True
        }, {
            "error":
            errors.BadUnionArgument(MagicMock(), MagicMock(), MagicMock()),
            "call_prepared":
            True
        }, {
            "error": errors.ArgumentParsingError(),
            "call_prepared": False
        }, {
            "error": errors.UserInputError(),
            "call_prepared": True
        })

        for case in test_cases:
            with self.subTest(error=case["error"],
                              call_prepared=case["call_prepared"]):
                self.ctx.reset_mock()
                self.assertIsNone(await self.cog.handle_user_input_error(
                    self.ctx, case["error"]))
                self.ctx.send.assert_awaited_once()
                if case["call_prepared"]:
                    self.ctx.send_help.assert_awaited_once()
                else:
                    self.ctx.send_help.assert_not_awaited()
Exemple #10
0
 async def test_try_get_tag_convert_fail(self, tag_converter):
     """Converting tag should raise `BadArgument`."""
     self.ctx.reset_mock()
     self.ctx.invoked_with = "bar"
     tag_converter.convert = AsyncMock(side_effect=errors.BadArgument())
     self.assertIsNone(await self.cog.try_get_tag(self.ctx))
     self.ctx.invoke.assert_not_awaited()
Exemple #11
0
    async def blame(self, ctx, message_id: int):
        """Show who caused a command response to show up.

        message_id must be a message said by the bot. Note you
        must enable developer mode in order to get message ids.
        See Discord's documentation here: https://waa.ai/j06h
        """
        async with self.database.acquire() as conn:
            row = await conn.fetchrow(
                "SELECT blame.id, blame.message_id, blame.author_id, "
                "blame.channel_id, blame.server_id "
                "FROM blame where blame.id = $1", message_id)

        if not row:
            raise errors.BadArgument("No info for that message.")

        _, message_id, author_id, channel_id, server_id = row.values()

        usr = ctx.bot.get_user(author_id)
        srv = ctx.bot.get_guild(server_id)
        if usr:
            author_id = "%s (%s)" % (str(usr), author_id)
        if srv:
            server_id = "%s (%s)" % (srv, srv.id)

        await ctx.send("Server: %s\nChannel: <#%s>\nUser: %s\nMessage: %s" %
                       (server_id, channel_id, author_id, message_id))
Exemple #12
0
    async def setbattletag(self, ctx, tag: battle_tag):
        """Sets the Blizzard BattleTag associated with this Discord account.

        Tags are of the format <platform>/<region>/username#number, or username#number. For example: "pc/us/noob#0001".
        If platform and region are not specified, I will search. If the incorrect one is chosen, please specify the platform and region.
        """
        async with aiohttp.ClientSession() as client:
            if tag.is_complete:
                async with client.head(tag.profile_url) as resp:
                    if resp.status == 404:
                        raise errors.BadArgument(
                            "That battletag does not match a user")
                    elif resp.status != 200:
                        raise errors.CommandError(
                            "There was an unknown error setting your battletag"
                        )
                    else:
                        await self.attr.set_attributes(
                            ctx.message.author, blizzard_battletag=tag.tag)
                        await ctx.send(
                            "Updated your battletag to {}".format(tag))
            else:
                tag = await find_tag(client, tag)
                await self.attr.set_attributes(ctx.message.author,
                                               blizzard_battletag=tag.tag)
                await ctx.send("Updated your battletag to {}".format(tag))
Exemple #13
0
    async def overwatch(self, ctx, tag: user_or_name=None):
        """Get Overwatch stats for a given user.

        May supply a Discord mention, or a BattleTag.
        Tags are of the format <platform>/<region>/username#number, or username#number. For example: "pc/us/noob#0001".
        If platform and region are not specified, I will search. If the incorrect one is chosen, please specify the platform and region.
        """
        if ctx.invoked_with == "owc":
            return await self.competitive.invoke(ctx)

        if not tag:
            tag = ctx.message.author

        prof = await self._get_overwatch_profile(ctx, tag)

        if not prof.quick_play:
            raise errors.BadArgument(
                "Player has no quick play stats available.")

        content = """
{0.tag.tag_private} Level {0.level} - {0.quick_play.all_heroes.game.games_won} Wins
{1.game.time_played} played. {1.combat.eliminations} eliminations, {1.combat.deaths} deaths.
Average: {1.average.damage_done} damage, {1.average.eliminations} elims, {1.average.final_blows} final blows, {1.average.deaths} deaths, {1.average.healing_done} healing
""".format(prof, prof.quick_play.all_heroes)

        await ctx.send("```prolog{}```".format(content))
Exemple #14
0
    async def _get_osu_account(self, ctx, user, mode):
        osu_user_id = await self.attr.get_attribute(user, 'osu_id')

        if osu_user_id:
            return await self._lookup_acct(osu_user_id, mode=mode)

        if ctx.author.id != user.id:
            raise errors.BadArgument(
                "I don't know {}'s osu username!".format(user))

        presence_username = self._osu_presence_username_cache.get(user.id)

        clean_prefix = utils.clean_double_backtick(ctx.prefix)

        if presence_username:
            await ctx.send(
                "I don't know your osu username! I'm setting your osu username "
                "to {}, which rich presence showed you recently playing as. "
                "If this is wrong use ``{}setosu <username>``".format(
                    presence_username, clean_prefix))
            return await self._set_osu_username(user, presence_username)

        await ctx.send(
            "I don't know your osu username! I'm setting your osu username "
            "to {}, if this is wrong use ``{}setosu <username>``".format(
                user.name, clean_prefix))
        return await self._set_osu_username(user, user.name)
Exemple #15
0
def get_cog_or_cmd_callback(ctx, *cmd_name):
    if len(cmd_name) == 1:
        cog = ctx.bot.get_cog(cmd_name[0])
        if cog:
            return cog.__class__
    try:
        cmd = resolve_command(ctx.bot, *cmd_name)
    except NoSubCommands as e:
        raise errors.BadArgument("`{}` has no subcommands".format(
            e.cmd.qualified_name))
    except NoSuchSubCommand as e:
        raise errors.BadArgument("`{}` has no subcommand {}".format(
            e.cmd.qualified_name, e.sub))
    except NoSuchCommand as e:
        raise errors.BadArgument("No such command or cog `{}`".format(
            e.cmd_name))
    return cmd.callback
Exemple #16
0
    def return_time_info(
            self,
            pattern: Optional[str]) -> Tuple[datetime, timedelta, float, dict]:
        time_match = re.match(r'\d{1,4}[dhmsDHMS]', pattern)
        if time_match is None:
            raise errors.BadArgument('Введено некорректное время!')
        *num_str, time_letter = time_match.group(0)
        num = int(''.join(num_str))

        __td, __time_units = None, {
            'days': False,
            'hours': False,
            'minutes': False,
            'seconds': False
        }
        if time_letter in ['d', 'D']:
            __td = timedelta(days=num)
            __time_units['days'], __time_units['hours'], __time_units[
                'minutes'] = True, True, True
        elif time_letter in ['h', 'H']:
            __td = timedelta(hours=num)
            __time_units['hours'], __time_units['minutes'], __time_units[
                'seconds'] = True, True, True
            if len(num_str) > 3:
                __time_units['days'], __time_units['seconds'] = True, False
        elif time_letter in ['m', 'M']:
            __td = timedelta(minutes=num)
            __time_units['minutes'], __time_units['seconds'] = True, True
            if len(num_str) >= 3:
                __time_units['hours'], __time_units['seconds'] = True, False
        elif time_letter in ['s', 'S']:
            if num < 30:
                raise errors.BadArgument(
                    'Укажите значение больше, чем 30! **(Для секунд)**')
            if num < 3600:
                __time_units['minutes'] = True
            elif num < 86400:
                __time_units['hours'] = True
            elif num > 86400:
                __time_units['days'] = True
            __td = timedelta(seconds=num)
            __time_units['seconds'] = True

        __dt = datetime.now(self._gmt) + __td
        __seconds = __td.total_seconds()
        return __dt, __td, __seconds, __time_units
Exemple #17
0
    async def convert(self, ctx, argument):
        match = await self.find_match(ctx, argument)

        if not match:
            raise errors.BadArgument(
                """Guild "{}" not found, or you aren't a member""".format(
                    argument))
        return match
Exemple #18
0
 def return_number_of_winners(pattern: Optional[str]) -> int:
     winners_match = re.match(r'\d+[wW]', pattern)
     if winners_match is None:
         raise errors.BadArgument(
             'Введено некорректное кол-во победителей!')
     *winners, _ = winners_match.group(0)
     __champions = int(''.join(winners))
     return __champions
Exemple #19
0
 async def convert(self, ctx: "IceTeaContext", argument):
     guild = discord.utils.get(ctx.bot.guilds, name=argument)
     if guild is None:
         guild = discord.utils.get(ctx.bot.guilds, id=int(argument))
     if guild is None:
         raise errors.BadArgument(message="I am not in any guild with this ID")
     else:
         return guild
Exemple #20
0
    async def _lookup_acct(self, username, mode=OsuMode.osu):
        res = await self.osuapi.get_user(username, mode=mode)

        if len(res) == 0:
            raise errors.BadArgument(
                "There is no osu user by the name {}".format(username))

        return res[0]
Exemple #21
0
    async def convert(self, ctx, argument):
        channel, msg_id = await MessageId().convert(ctx, argument)

        msg = discord.utils.get(ctx.bot.cached_messages, id=msg_id)
        if msg is None:
            try:
                msg = await channel.fetch_message(msg_id)
            except discord.NotFound:
                raise errors.BadArgument(
                    f'Message {msg_id} not found in channel {channel.mention}.'
                )
            except discord.Forbidden:
                raise errors.CheckFailure(
                    f"I don't have permission to view channel {channel.mention}."
                )
        elif msg.channel.id != channel.id:
            raise errors.BadArgument('Message not found.')
        return msg
Exemple #22
0
def osu_map_url(value):
    match = re.match(
        r'https://osu.ppy.sh/(?:s/(?P<beatmapset>[0-9]+)|b/(?P<beatmap>0-9]+))/?.*',
        value)
    if match.group("beatmapset"):
        return dict(beatmapset=match.group("beatmapset"))
    elif match.group("beatmap"):
        return dict(beatmap=match.group("beatmap"))
    raise errors.BadArgument("Not recognized as a beatmap url!")
Exemple #23
0
 async def get_user(self, ctx, session, user_id):
     user = session.query(
         database.User).filter(database.User.discord_id == user_id).first()
     if user is None:
         await ctx.bot.whisper(
             f'Hello {ctx.message.author.name}, your profile doesn\'t exist yet. Please run `!user setup` to get started!'
         )
         raise errors.BadArgument('No profile')
     return user
Exemple #24
0
    async def convert(self, ctx, argument):
        channel, msg_id = await MessageIdConverter().convert(ctx, argument)

        msg = discord.utils.get(ctx.bot.cached_messages, id=msg_id)
        if msg is None:
            try:
                msg = await channel.fetch_message(msg_id)
            except discord.NotFound:
                raise errors.BadArgument(
                    "Message {0} not found in channel {1.mention}".format(
                        msg_id, channel))
            except discord.Forbidden:
                raise errors.CheckFailure(
                    "I don't have permission to view channel {0.mention}".
                    format(channel))
        elif msg.channel.id != channel.id:
            raise errors.BadArgument("Message not found")
        return msg
Exemple #25
0
    async def bc(self, ctx, *, args):
        args = args.strip(' `')

        for banned_word in ['open', 'read', 'write', 'getline']:
            if banned_word in args:
                raise errors.CheckFailure(f'Banned word found')
        try:
            await ctx.bot.say(str(cexprtk.evaluate_expression(args, {})))
        except:
            raise errors.BadArgument(f'Invalid expression')
Exemple #26
0
    async def convert(self, ctx, argument):
        if argument.startswith("http://") or argument.startswith("https://"):
            return argument

        member = await UserMemberConverter().convert(ctx, argument)
        if member:
            return member.avatar_url_as(format="png")

        raise errors.BadArgument(
            "{argument} isn't a member or url.".format(argument=argument))
Exemple #27
0
def silent_convert_member(ctx, name, optional=False):
    if name and name.lower() == 'motsy':
        name = 'Motoko'
    if name and name.lower() == '@me':
        return ctx.message.author

    if name:
        return AndreMemberConverter(ctx, name).convert()
    elif optional:
        return None
    else:
        raise errors.BadArgument('No member specified')
Exemple #28
0
    async def poll(self, ctx, *options: clean_content):
        """Run a poll with up to 11 options.

        Poll ends 30 seconds after the last response.
        """
        if len(options) > 11:
            raise errors.BadArgument("No more than 11 options")

        e = discord.Embed(title="Vote now!",
                          description="\n".join(
                              "%s %s" % (number_emoji(idx), text)
                              for idx, text in enumerate(options)))
        e.set_footer(text="Voting ends 30 seconds after the last response!")

        msg = await ctx.send(embed=e)
        for i in range(len(options)):
            await msg.add_reaction(number_emoji(i))

        while True:
            try:
                resp = await ctx.bot.wait_for(
                    "raw_reaction_add",
                    timeout=30,
                    check=lambda r: r.message_id == msg.id and is_number_emoji(
                        r.emoji.name))
            except asyncio.TimeoutError:
                break

        msg = await ctx.get_message(msg.id)

        max_val = 0
        results = []
        for reaction in msg.reactions:
            if isinstance(reaction.emoji, str) and is_number_emoji(
                    reaction.emoji):
                idx = emoji_number(reaction.emoji)
                if idx >= len(options):
                    continue
                if reaction.count > max_val:
                    max_val = reaction.count
                    results = [idx]
                elif reaction.count == max_val:
                    results.append(idx)

        if max_val == 1:
            await ctx.send("No one voted...")
        elif len(results) > 1:
            final = "all" if len(results) > 2 else "both"
            await ctx.send("Tie! %s %s won!" %
                           (joinand([options[idx] for idx in results]), final))
        else:
            await ctx.send("The best choice is clearly %s" %
                           options[results[0]])
Exemple #29
0
 async def convert(self, ctx, argument):
     if ctx.command.parent is not None:
         g_command = await ctx.bot.factory.Command.get(
             argument, connection=ctx.bot.rethinkdb, bot=ctx.bot)
         if g_command is not None:
             return g_command
         else:
             raise myerrors.NotRootCommand(argument)
     else:
         raise errors.BadArgument(
             message=
             f"{ctx.command.invoked_with} cannot be blocked, only parent commands can be blocked."
         )
Exemple #30
0
    async def find(self, ctx, *, username):
        """Find users with simple matching."""
        username = username.lower()

        if len(username) < 2:
            raise errors.BadArgument("Username must be at least 2 characters.")

        matches = [
            member for member in ctx.message.guild.members
            if username in member.name.lower()
        ]

        await _send_find_results(ctx, matches)