Ejemplo n.º 1
0
    async def convert(self, ctx: commands.Context, arg: str) -> None:
        link_pattern = "^https://discord.com/channels/[0-9]+/[0-9]+/[0-9]+"
        special_id_pattern = "^[0-9]+-[0-9]*[0-9]$"
        normal_id_pattern = "^[0-9][0-9]+[0-9]$"

        channel_id: int = None
        message_id: int = None
        message: discord.Message = None

        if arg == "^":
            try:
                message = (await ctx.channel.history(limit=2).flatten())[1]
            except discord.Forbidden:
                raise discord.Forbidden(
                    "I can't read the messae history of this channel, "
                    "so I don't know what message you want me to force.")
        elif re.match(normal_id_pattern, arg):
            channel_id = ctx.channel.id
            message_id = int(arg)
        elif re.match(link_pattern, arg):
            split = arg.split("/")
            channel_id = int(split[-2])
            message_id = int(split[-1])
        elif re.match(special_id_pattern, arg):
            split = arg.split("-")
            channel_id = int(split[0])
            message_id = int(split[1])
        else:
            raise discord.InvalidArgument(
                f"The argument, `{arg}`, does not appear to be "
                "a message link.")

        if not message:
            channel = ctx.guild.get_channel(channel_id)
            if not channel:
                raise discord.InvalidArgument(
                    "I couldn't find a channel with the id "
                    f"`{channel_id}`. Please make sure the message "
                    "link is valid, and is in this server.")
            try:
                message = await channel.fetch_message(message_id)
            except discord.NotFound:
                raise discord.InvalidArgument(
                    "I couldn't find a message that matches "
                    f"the link `{arg}`. Please make sure the "
                    "message link is valid.")
            except discord.Forbidden:
                raise discord.Forbidden(
                    "I don't have permission to read message history "
                    f"in {channel.mention}, so I can't fetch the message "
                    f"`{arg}`")
        return message
async def test_handle_raised_forbidden():
    # Given
    clientside_error_message = "The bot has insufficient permissions to do this."

    message_provider = MagicMock(MessageProvider)
    message_provider.bot_missing_access.return_value = clientside_error_message

    route_handler = _route_handler(message_provider=message_provider)

    event = DiscordEvent(command=DiscordCommand(command_name="ping", ))

    mock_route = AsyncMock(Route)
    mock_route.call.side_effect = discord.Forbidden(
        MagicMock(aiohttp.ClientResponse), "Missing Access.")

    route_definition = RouteDefinition(
        route=mock_route,
        command="ping",
    )

    # When
    response = await route_handler.handle(event, route_definition)

    # Then
    assert response.is_ephemeral
    assert response.content == clientside_error_message
Ejemplo n.º 3
0
 async def get_meme_image(self,
                          image_type: str,
                          url: str = None,
                          text: str = None,
                          timeout: int = 300,
                          **args):
     """
     Basic get_image function using aiohttp
     Returns a Discord File
     """
     if text:
         text = quote(text)
     url = '{}/{}{}{}'.format(
         memer, image_type.lower(),
         '?avatar1={}'.format(url) if url else '',
         '{}text={}'.format('&' if url else '?', text) if text else '')
     for p, v in args.items():
         url += '&{}={}'.format(p, v)
     async with self.session.get(url,
                                 headers=self.dankmemer_header,
                                 timeout=timeout) as response:
         if response.status != 200:
             raise discord.Forbidden(
                 'You are not allowed to access this resource.')
         ext = response.content_type.split('/')[-1]
         img = BytesIO(await response.read())
         await response.release()
     return discord.File(img, filename="image.{}".format(ext))
Ejemplo n.º 4
0
    async def cleanup(self, ctx, num_messages: int, *users):
        try:
            to_clean = [(await commands.MemberConverter().convert(ctx, user)) for user in users]
        except commands.BadArgument:
            try:
                to_clean = [(await commands.RoleConverter().convert(ctx, user)) for user in users]
            except commands.BadArgument:
                print(users)
                if users[0] in ['bots', 'humans']:
                    to_clean = []
                    members = ctx.channel.members
                    if users[0] == 'bots':
                        for m in members:
                            if m.bot:
                                to_clean.append(m)
                    if users[0] == 'humans':
                        for m in members:
                            if not m.bot:
                                to_clean.append(m)
                else:
                    raise commands.BadArgument("That was not a correct mention(s), role(s) or `bots` or `humans`")

        if num_messages > 100:
            num_messages = 100

        def check(m):
            return m.author in to_clean
        try:
            deleted = await ctx.channel.purge(limit=num_messages, check=check)
        except discord.Forbidden:
            raise discord.Forbidden("I don't have the required `manage_messages` permission to run this command!")

        send = await ctx.send(f"Deleted {len(deleted)} messages from `{[n.name for n in to_clean]}`.")
        await asyncio.sleep(5)
        await send.delete()
Ejemplo n.º 5
0
 async def get_webhook(self, ctx: commands.Context,
                       channel: discord.TextChannel):
     link = self.cache.get(channel.id)
     if link:
         return link
     if channel.permissions_for(ctx.me).manage_webhooks:
         webhook_list = [
             w for w in (await channel.webhooks())
             if w.type == discord.WebhookType.incoming
         ]
         if webhook_list:
             webhook = webhook_list[0]
         else:
             creation_reason = f"Webhook creation requested by {ctx.author} ({ctx.author.id}) for the `{ctx.command.qualified_name}` command."
             webhook = await channel.create_webhook(
                 name=f"{ctx.me.name} Webhook",
                 reason=creation_reason,
                 avatar=await ctx.me.avatar_url.read(),
             )
         self.cache[channel.id] = webhook.url
         return webhook.url
     else:
         raise discord.Forbidden(
             "Missing Permissions",
             f"I need permissions to `manage_webhooks` in #{channel.name}.",
         )
Ejemplo n.º 6
0
 async def purge(self, ctx, num: int):
     try:
         await ctx.message.delete()
         await ctx.channel.purge(limit=num)
         resp = await ctx.send(f"Succesfully deleted {num} messages.")
         await asyncio.sleep(5)
         await resp.delete()
     except discord.Forbidden():
         await ctx.send(
             "I don't have the proper permissions to delete messages.")
Ejemplo n.º 7
0
 async def purge(self, ctx, num_messages: int=None):
     if num_messages is None:
         num_messages = 100
     if num_messages > 100:
         num_messages = 100
     try:
         deleted = await ctx.channel.purge(limit=num_messages)
     except discord.Forbidden:
         raise discord.Forbidden("I don't have `manage_messages` permission to run this command!")
     msg = await ctx.send(f"Purged {len(deleted)} messages from this channel!")
     await asyncio.sleep(5)
     await msg.delete()
Ejemplo n.º 8
0
 async def get_image_ksoft(self, tag: str, nsfw: bool = True):
     params = f'?tag={tag}&nsfw={nsfw}'
     url = ksoft_api + params
     async with self.session.get(url,
                                 headers=self.ksoft_headers,
                                 timeout=20) as response:
         if response.status != 200:
             raise discord.Forbidden(
                 'You are not allowed to access this resource.')
         ext = response.content_type.split('/')[-1]
         img = await response.read()
         await response.release()
     img = json.loads(img)
     return img['url']
Ejemplo n.º 9
0
    async def get_webhook(
        self,
        *,
        channel: discord.TextChannel = None,
        me: discord.Member = None,
        author: discord.Member = None,
        reason: str = None,
        ctx: commands.Context = None,
    ):
        if ctx:
            channel = channel or ctx.channel
            me = me or ctx.me
            author = author or ctx.author
            reason = (reason
                      or f"For the {ctx.command.qualified_name} command", )

        link = self.cache.get(channel.id)
        if link:
            return link
        if channel.permissions_for(me).manage_webhooks:
            webhook_list = [
                w for w in (await channel.webhooks())
                if w.type == discord.WebhookType.incoming
            ]
            if webhook_list:
                webhook = webhook_list[0]
            else:
                creation_reason = f"Webhook creation requested by {author} ({author.id})"
                if reason:
                    creation_reason += f" Reason: {reason}"
                webhook = await channel.create_webhook(
                    name=f"{me.name} Webhook",
                    reason=creation_reason,
                    avatar=await me.avatar_url.read(),
                )
            self.cache[channel.id] = webhook.url
            return webhook.url
        else:
            raise discord.Forbidden(
                "Missing Permissions",
                f"I need permissions to `manage_webhooks` in #{channel.name}.",
            )
Ejemplo n.º 10
0
    async def get_from_cdn(url) -> bytes:
        """
        A method that downloads an image from a url.

        Parameters:
            url (str): The url of the image.

        Returns:
            (bytes): A bytes object of the downloaded image.
        """

        async with ClientSession() as session:
            async with session.get(url) as resp:
                if resp.status == 200:
                    return await resp.read()
                elif resp.status == 404:
                    raise discord.NotFound(resp, 'Not Found')
                elif resp.status == 403:
                    raise discord.Forbidden(resp, 'Forbidden')
                else:
                    raise discord.HTTPException(resp, 'Failed')
Ejemplo n.º 11
0
 async def ban(self, ctx, user, *, reason=None):
     mod_log = await self.bot.db.config.find_one({"_id": ctx.guild.id})
     if user.startswith("<@") and user.endswith(">"):
         user = ctx.guild.get_member(int(user.lstrip("<@").strip(">")))
     else:
         user = ctx.guild.get_member(int(user))
     try:
         await user.ban(reason=reason)
     except discord.Forbidden():
         await ctx.send(
             "I don't have the proper permissions to ban this member")
     await ctx.send(f"**{user} was banned**")
     if mod_log["mod_log"]:
         channel = self.bot.get_channel(mod_log["mod_log"])
         embed = discord.Embed(
             title="Member banned!",
             color=discord.Color.red(),
             description=f"{user} was banned by {ctx.author}")
         embed.add_field(name="Reason", value=reason)
         embed.set_image(url=user.avatar_url)
         await channel.send(embed=embed)
Ejemplo n.º 12
0
 async def _process_message(self, rift, message, dest):
     send = message.channel == rift.source
     destination = rift.destination if send else rift.source
     me = self.bot.user if destination.is_private else destination.server.me
     author = message.author
     sauthor = self.bot.user if destination.is_private else destination.server.get_member(author.id)
     perms = destination.permissions_for(sauthor)
     isowner = author.id == self.bot.settings.owner
     if send and (sauthor is None or not isowner and not
                  perms.send_messages):
         raise discord.Forbidden(403, "Forbidden")
     content = message.content
     embed = None
     if not isowner or not send:
         content = "{}: {}".format(author, content)
     if not isowner and not destination.permissions_for(
             sauthor).mention_everyone:
         content = escape(content, mass_mentions=True)
     if message.attachments and (isowner or
                                 destination.permissions_for(
                                     sauthor).attach_files):
         if destination.permissions_for(me).embed_links:
             attach = message.attachments[0]
             embed = discord.Embed(description="{}\n**[{}]({})**".format(
                 xbytes(attach["size"]), attach["filename"], attach["url"]),
                                   colour=randint(0, 0xFFFFFF))
             embed.set_image(url=message.attachments[0]["url"])
             if len(message.attachments) > 1:
                 rest = message.attachments[1:]
                 embed.set_footer(" | ".join("**[{}]({})** ({})".format(
                     a["filename"], a["url"], xbytes(a["size"]))
                                             for a in rest))
         else:
             content += "\n\n" + "\n".join("{} ({})".format(a["url"], xbytes(a["size"])) for a in
                                         message.attachments)
     if isinstance(dest, discord.Message):
         return await self.bot.edit_message(dest, content, embed=embed)
     else:
         return await self.bot.send_message(dest, content, embed=embed)
Ejemplo n.º 13
0
class Webhook(commands.Cog):
    """Webhook utility commands."""

    __author__ = "PhenoM4n4n"

    def __init__(self, bot):
        self.bot = bot
        self.cache = {}
        self.session = aiohttp.ClientSession()

    def cog_unload(self):
        self.bot.loop.create_task(self.session.close())

    async def red_delete_data_for_user(self, **kwargs):
        return

    @staticmethod
    async def delete_quietly(ctx: commands.Context):
        if ctx.channel.permissions_for(ctx.me).manage_messages:
            try:
                await ctx.message.delete()
            except discord.HTTPException:
                pass

    @commands.guild_only()
    @commands.group()
    async def webhook(self, ctx):
        """Webhook related commands."""

    @commands.bot_has_permissions(manage_webhooks=True)
    @commands.admin_or_permissions(manage_webhooks=True)
    @webhook.command()
    async def create(
        self,
        ctx: commands.Context,
        channel: discord.TextChannel = None,
        *,
        webhook_name: str = None,
    ):
        """Creates a webhook in the channel specified with the name specified.

        If no channel is specified then it will default to the current channel."""
        channel = channel or ctx.channel
        webhook_name = webhook_name or f"{ctx.bot.user.name} Webhook"
        creation_reason = f"Webhook creation requested by {ctx.author} ({ctx.author.id})"
        await channel.create_webhook(name=webhook_name, reason=creation_reason)
        await ctx.tick()

    @commands.admin_or_permissions(manage_webhooks=True)
    @webhook.command()
    async def send(self, ctx: commands.Context, webhook_link: str, *,
                   message: str):
        """Sends a message to the specified webhook using your avatar and display name."""
        await self.delete_quietly(ctx)
        try:
            await self.webhook_link_send(webhook_link,
                                         ctx.author.display_name,
                                         ctx.author.avatar_url,
                                         content=message)
        except InvalidWebhook:
            await ctx.send("You need to provide a valid webhook link.")

    @commands.bot_has_permissions(manage_webhooks=True)
    @webhook.command()
    async def say(self, ctx: commands.Context, *, message: str):
        """Sends a message to the channel as a webhook with your avatar and display name."""
        await self.delete_quietly(ctx)
        await self.send_to_channel(
            ctx.channel,
            ctx.me,
            ctx.author,
            ctx=ctx,
            content=message,
            avatar_url=ctx.author.avatar_url,
            username=ctx.author.display_name,
        )

    @commands.admin_or_permissions(manage_webhooks=True)
    @commands.bot_has_permissions(manage_webhooks=True)
    @webhook.command()
    async def sudo(self, ctx: commands.Context, member: discord.Member, *,
                   message: str):
        """Sends a message to the channel as a webhook with the specified member's avatar and display name."""
        await self.delete_quietly(ctx)
        await self.send_to_channel(
            ctx.channel,
            ctx.me,
            ctx.author,
            ctx=ctx,
            content=message,
            avatar_url=member.avatar_url,
            username=member.display_name,
        )

    @commands.admin_or_permissions(manage_webhooks=True, manage_guild=True)
    @commands.bot_has_permissions(manage_webhooks=True)
    @webhook.command(hidden=True)
    async def loudsudo(self, ctx: commands.Context, member: discord.Member, *,
                       message: str):
        """Sends a message to the channel as a webhook with the specified member's avatar and display name."""
        await self.send_to_channel(
            ctx.channel,
            ctx.me,
            ctx.author,
            ctx=ctx,
            content=message,
            avatar_url=member.avatar_url,
            username=member.display_name,
            allowed_mentions=discord.AllowedMentions(everyone=False,
                                                     roles=False,
                                                     users=True),
        )

    @commands.admin_or_permissions(manage_webhooks=True, manage_guild=True)
    @commands.bot_has_permissions(manage_webhooks=True)
    @webhook.command(hidden=True)
    async def clyde(self, ctx: commands.Context, *, message: str):
        """Sends a message to the channel as a webhook with Clyde's avatar and name."""
        await self.delete_quietly(ctx)
        await self.send_to_channel(
            ctx.channel,
            ctx.me,
            ctx.author,
            ctx=ctx,
            content=message,
            avatar_url=
            "https://discordapp.com/assets/f78426a064bc9dd24847519259bc42af.png",
            username="******",
            allowed_mentions=discord.AllowedMentions(everyone=False,
                                                     roles=False,
                                                     users=True),
        )

    @commands.max_concurrency(1, commands.BucketType.guild)
    @commands.has_permissions(manage_webhooks=True)
    @commands.bot_has_permissions(manage_webhooks=True)
    @webhook.command()
    async def clear(self, ctx):
        """Delete all webhooks in the server."""
        webhooks = await ctx.guild.webhooks()
        if not webhooks:
            await ctx.send("There are no webhooks in this server.")
            return

        msg = await ctx.send(
            "This will delete all webhooks in the server. Are you sure you want to do this?"
        )
        start_adding_reactions(msg, ReactionPredicate.YES_OR_NO_EMOJIS)
        pred = ReactionPredicate.yes_or_no(msg, ctx.author)
        try:
            await ctx.bot.wait_for("reaction_add", check=pred, timeout=60)
        except asyncio.TimeoutError:
            await ctx.send("Action Cancelled.")
            return

        if pred.result is False:
            return await ctx.send("Action Cancelled.")
        msg = await ctx.send("Deleting webhooks..")
        count = 0
        async with ctx.typing():
            for webhook in webhooks:
                try:
                    await webhook.delete(
                        reason=
                        f"Guild Webhook Deletion requested by {ctx.author} ({ctx.author.id})"
                    )
                except discord.InvalidArgument:
                    pass
                else:
                    count += 1
        try:
            await msg.edit(content=f"{count} webhooks deleted.")
        except discord.NotFound:
            await ctx.send(f"{count} webhooks deleted.")

    @commands.mod_or_permissions(ban_members=True)
    @webhook.command()
    async def perms(self, ctx):
        """Show all members in the server that have `manage_webhook` permissions."""
        await ctx.trigger_typing()
        members = []
        strings = []
        roles = []
        for role in ctx.guild.roles:
            if role.permissions.is_superset(discord.Permissions(
                    536870912)) or role.permissions.is_superset(
                        discord.Permissions(8)):
                roles.append(role)
                for member in role.members:
                    if member not in members:
                        members.append(member)
                        string = (
                            f"[{member.mention} - {member}](https://www.youtube.com/watch?v=dQw4w9WgXcQ&ab_channel=RickAstleyVEVO 'This user is a bot')"
                            if member.bot else f"{member.mention} - {member}")
                        strings.append(string)
        if not members:
            await ctx.send(
                "No one here has `manage_webhook` permissions other than the owner."
            )
        strings = "\n".join(strings)
        if len(strings) > 2000:
            embeds = []
            for page in pagify(strings):
                embed = discord.Embed(
                    color=await ctx.embed_color(),
                    title="Users with `manage_webhook` Permissions",
                    description=page,
                )
                if roles:
                    embed.add_field(
                        name="Roles:",
                        value=humanize_list([role.mention for role in roles]),
                        inline=False,
                    )
                embeds.append(embed)
            await menu(ctx, embeds, DEFAULT_CONTROLS)
        else:
            embed = discord.Embed(
                color=await ctx.embed_color(),
                title="Users with `manage_webhook` Permissions",
                description=strings,
            )
            if roles:
                embed.add_field(
                    name="Roles:",
                    value=humanize_list([role.mention for role in roles]),
                    inline=False,
                )
            emoji = self.bot.get_emoji(736038541364297738) or "❌"
            await menu(ctx, [embed], {emoji: close_menu})

    @commands.max_concurrency(1, commands.BucketType.channel)
    @commands.admin_or_permissions(manage_webhooks=True)
    @webhook.command()
    async def session(self, ctx: commands.Context, webhook_link: str):
        """Initiate a session within this channel sending messages to a specified webhook link."""
        if ctx.channel.permissions_for(ctx.me).manage_messages:
            try:
                await ctx.message.delete()
            except discord.NotFound:
                pass
        e = discord.Embed(
            color=0x49FC95,
            title="Webhook Session Initiated",
            description=f"Session Created by `{ctx.author}`.",
        )
        initial_result = await self.webhook_link_send(
            webhook_link,
            "Webhook Session",
            "https://imgur.com/BMeddyn.png",
            embed=e)
        if initial_result is not True:
            return await ctx.send(initial_result)
        await ctx.send(
            "I will send all messages in this channel to the webhook until "
            "the session is closed by saying 'close' or there are 2 minutes of inactivity.",
            embed=e,
        )
        while True:
            try:
                result = await self.bot.wait_for(
                    "message_without_command",
                    check=lambda x: x.channel == ctx.channel and not x.author.
                    bot and x.content,
                    timeout=120,
                )
            except asyncio.TimeoutError:
                return await ctx.send("Session closed.")
            if result.content.lower() == "close":
                return await ctx.send("Session closed.")
            send_result = await self.webhook_link_send(
                webhook_link,
                result.author.display_name,
                result.author.avatar_url,
                content=result.content,
            )
            if send_result is not True:
                return await ctx.send(
                    "The webhook was deleted so this session has been closed.")

    @commands.cooldown(5, 10, commands.BucketType.guild)
    @commands.admin_or_permissions(manage_webhooks=True)
    @webhook.command(name="edit")
    async def webhook_edit(self, ctx: commands.Context,
                           message: discord.Message, *, content: str):
        """Edit a message sent by a webhook."""
        if not message.webhook_id:
            raise commands.BadArgument
        if not message.channel.permissions_for(ctx.me).manage_webhooks:
            return await ctx.send(
                f"I need `Manage Webhook` permission in {message.channel}.")
        webhooks = await message.channel.webhooks()
        webhook = None
        for chan_webhook in webhooks:
            if (chan_webhook.type == discord.WebhookType.incoming
                    and chan_webhook.id == message.webhook_id):
                webhook = chan_webhook
                break
        if not webhook:
            raise commands.BadArgument
        await webhook.edit_message(message.id, content=content)
        await self.delete_quietly(ctx)

    async def webhook_link_send(
        self,
        link: str,
        username: str,
        avatar_url: str,
        *,
        allowed_mentions: discord.AllowedMentions = discord.AllowedMentions(
            users=False, everyone=False, roles=False),
        **kwargs,
    ):
        try:
            async with aiohttp.ClientSession() as session:
                webhook = discord.Webhook.from_url(
                    link, adapter=discord.AsyncWebhookAdapter(session))
                await webhook.send(
                    username=username,
                    avatar_url=avatar_url,
                    allowed_mentions=allowed_mentions,
                    **kwargs,
                )
                return True
        except (discord.InvalidArgument, discord.NotFound):
            raise InvalidWebhook("You need to provide a valid webhook link.")

    async def get_webhook(
        self,
        *,
        channel: discord.TextChannel = None,
        me: discord.Member = None,
        author: discord.Member = None,
        reason: str = None,
        ctx: commands.Context = None,
    ) -> discord.Webhook:
        if ctx:
            channel = channel or ctx.channel
            me = me or ctx.me
            author = author or ctx.author
            reason = (reason
                      or f"For the {ctx.command.qualified_name} command", )

        if webhook := self.cache.get(channel.id):
            return webhook
        if me and not channel.permissions_for(me).manage_webhooks:
            raise discord.Forbidden(
                FakeResponse(),
                f"I need permissions to `manage_webhooks` in #{channel.name}.",
            )
        chan_hooks = await channel.webhooks()
        webhook_list = [
            w for w in chan_hooks if w.type == discord.WebhookType.incoming
        ]
        if webhook_list:
            webhook = webhook_list[0]
        else:
            creation_reason = f"Webhook creation requested by {author} ({author.id})"
            if reason:
                creation_reason += f" Reason: {reason}"
            if len(chan_hooks) == 10:
                await chan_hooks[-1].delete()
            webhook = await channel.create_webhook(
                name=f"{me.name} Webhook",
                reason=creation_reason,
                avatar=await me.avatar_url.read(),
            )
        self.cache[channel.id] = webhook
        return webhook
Ejemplo n.º 14
0
        me: discord.Member = None,
        author: discord.Member = None,
        reason: str = None,
        ctx: commands.Context = None,
    ) -> discord.Webhook:
        if ctx:
            channel = channel or ctx.channel
            me = me or ctx.me
            author = author or ctx.author
            reason = (reason or f"For the {ctx.command.qualified_name} command",)

        if webhook := self.channel_cache.get(channel.id):
            return webhook
        if me and not channel.permissions_for(me).manage_webhooks:
            raise discord.Forbidden(
                FakeResponse(),
                f"I need permissions to `manage_webhooks` in #{channel}.",
            )
        chan_hooks = await channel.webhooks()
        webhook_list = [w for w in chan_hooks if await self.webhook_check(w)]
        if webhook_list:
            webhook = webhook_list[0]
        else:
            if len(chan_hooks) == 10:
                # delete_hook = chan_hooks[-1]
                # await delete_hook.delete()
                return  # can't delete follower type webhooks
            creation_reason = (
                f"Webhook creation requested by {author} ({author.id})" if author else ""
            )
            if reason:
                creation_reason += f" Reason: {reason}"
Ejemplo n.º 15
0
 def test_forbidden(self):
     message = AsyncMock()
     message.delete.side_effect = discord.Forbidden(Mock(), "")
     self.assertFalse(self._await(delete_message(message)))
     message.delete.assert_awaited_once()