Example #1
0
    async def repo(self, ctx: context.Context, *, repo):
        async with ctx.typing():
            data = await self.repo_request(repo)

        if data is None:
            embed = discord.Embed(title="Error", color=discord.Color.red())
            embed.description = f'An error occurred while searching for that repo'
            await ctx.message.delete(delay=5)
            await ctx.send(embed=embed, delete_after=5)
            return

        if not isinstance(data, list):
            data = [data]

        if len(data) == 0:
            embed = discord.Embed(title=":(\nYour command ran into a problem",
                                  color=discord.Color.red())
            embed.description = f"Sorry, I couldn't find a repo by that name."
            await ctx.message.delete(delay=5)
            await ctx.send(embed=embed, delete_after=5)
            return

        menus = MenuPages(source=ReposSource(data, key=lambda t: 1,
                                             per_page=1),
                          clear_reactions_after=True)
        await menus.start(ctx)
Example #2
0
    async def reset(self, ctx: context.Context):
        """Reset all points (mod only)
        """

        async with ctx.typing():
            members_reset = await ctx.settings.reset_trivia_points()

        if not members_reset:
            raise commands.BadArgument("There were no points to reset!")

        await ctx.message.delete(delay=5)
        await ctx.send_success(
            title="Done!",
            description=f"I reset {members_reset} users' points.",
            delete_after=5)
Example #3
0
    async def jumbo(self, ctx: context.Context,
                    emoji: typing.Union[discord.Emoji, discord.PartialEmoji,
                                        str]):
        """Post large version of a given emoji

        Example usage
        -------------
        !jumbo :ntwerk:

        Parameters
        ----------
        emoji : typing.Union[discord.Emoji, discord.PartialEmoji]
            "Emoji to post"
        """

        # non-mod users will be ratelimited
        bot_chan = self.bot.settings.guild().channel_botspam
        if not self.bot.settings.permissions.hasAtLeast(
                ctx.guild, ctx.author, 5) and ctx.channel.id != bot_chan:
            if await self.ratelimit(ctx.message):
                raise commands.BadArgument("This command is on cooldown.")

        # is this a regular Unicode emoji?
        if isinstance(emoji, str):
            # yes, read the bytes from our JSON file and turn it into an image
            async with ctx.typing():
                emoji_url_file = self.emojis.get(emoji)
                if emoji_url_file is None:
                    raise commands.BadArgument(
                        "Couldn't find a suitable emoji.")

            im = Image.open(BytesIO(base64.b64decode(emoji_url_file)))
            image_container = BytesIO()
            im.save(image_container, 'png')
            image_container.seek(0)
            _file = discord.File(image_container, filename="image.png")
            await ctx.message.reply(file=_file, mention_author=False)
        else:
            # no, this is a custom emoji. send its URL
            await ctx.message.reply(emoji.url, mention_author=False)
Example #4
0
    async def jumbo(self, ctx: context.Context,
                    emoji: typing.Union[discord.Emoji, discord.PartialEmoji,
                                        str]):
        """Post large version of a given emoji

        Example usage
        -------------
        !jumbo :ntwerk:

        Parameters
        ----------
        emoji : typing.Union[discord.Emoji, discord.PartialEmoji]
            "Emoji to post"
        """

        bot_chan = ctx.settings.guild().channel_offtopic
        if not ctx.settings.permissions.hasAtLeast(
                ctx.guild, ctx.author, 2) and ctx.channel.id != bot_chan:
            if await self.ratelimit(ctx.message):
                raise commands.BadArgument("This command is on cooldown.")

        if isinstance(emoji, str):
            async with ctx.typing():
                emoji_url_file = self.emojis.get(emoji)
                if emoji_url_file is None:
                    raise commands.BadArgument(
                        "Couldn't find a suitable emoji.")

            im = Image.open(BytesIO(base64.b64decode(emoji_url_file)))
            image_container = BytesIO()
            im.save(image_container, 'png')
            image_container.seek(0)
            _file = discord.File(image_container, filename="image.png")
            await ctx.message.reply(file=_file, mention_author=False)

        else:
            await ctx.message.reply(emoji.url, mention_author=False)
Example #5
0
    async def batchraid(self, ctx: context.Context, *, phrases: str) -> None:
        """Add a list of (newline-separated) phrases to the raid filter.

        Example usage
        --------------
        !raid <phrase>

        Parameters
        ----------
        phrases : str
            "Phrases to add, separated with enter"
        """

        async with ctx.typing():
            phrases = list(set(phrases.split("\n")))
            phrases = [phrase.strip() for phrase in phrases]

            phrases_contenders = set(phrases)
            phrases_already_in_db = set(
                [phrase.word for phrase in ctx.settings.guild().raid_phrases])

            duplicate_count = len(
                phrases_already_in_db
                & phrases_contenders)  # count how many duplicates we have
            new_phrases = list(phrases_contenders - phrases_already_in_db)

        if not new_phrases:
            raise commands.BadArgument(
                "All the phrases you supplied are already in the database.")

        phrases_prompt_string = "\n".join(
            [f"**{i+1}**. {phrase}" for i, phrase in enumerate(new_phrases)])
        if len(phrases_prompt_string) > 3900:
            phrases_prompt_string = phrases_prompt_string[:3500] + "\n... (and some more)"

        embed = Embed(
            title="Confirm raidphrase batch",
            color=discord.Color.dark_orange(),
            description=
            f"{phrases_prompt_string}\n\nShould we add these {len(new_phrases)} phrases?"
        )

        if duplicate_count > 0:
            embed.set_footer(
                text=
                f"Note: we found {duplicate_count} duplicates in your list.")

        message = await ctx.send(embed=embed)

        prompt_data = context.PromptDataReaction(message=message,
                                                 reactions=['✅', '❌'],
                                                 timeout=120,
                                                 delete_after=True)
        response, _ = await ctx.prompt_reaction(info=prompt_data)

        if response == '✅':
            async with ctx.typing():
                for phrase in new_phrases:
                    await ctx.settings.add_raid_phrase(phrase)

            await ctx.send_success(
                f"Added {len(new_phrases)} phrases to the raid filter.",
                delete_after=5)
        else:
            await ctx.send_warning("Cancelled.", delete_after=5)

        await ctx.message.delete(delay=5)
Example #6
0
    async def ban(self,
                  ctx: context.Context,
                  user: permissions.ModsAndAboveExternal,
                  *,
                  reason: str = "No reason."):
        """Ban a user (mod only)

        Example usage
        --------------
        !ban <@user/ID> <reason (optional)>

        Parameters
        ----------
        user : typing.Union[discord.Member, int]
            "The user to be banned, doesn't have to be part of the guild"
        reason : str, optional
            "Reason for ban, by default 'No reason.'"
        """

        reason = discord.utils.escape_markdown(reason)
        reason = discord.utils.escape_mentions(reason)

        member_is_external = ctx.guild.get_member(user.id) is None

        if self.ban_list_cache is None:
            self.ban_list_cache = {
                user.id
                for _, user in await ctx.guild.bans()
            }
        # if the ID given is of a user who isn't in the guild, try to fetch the profile
        if member_is_external:
            async with ctx.typing():
                # previous_bans = [user for _, user in await ctx.guild.bans()]

                if user.id in self.ban_list_cache:
                    raise commands.BadArgument("That user is already banned!")

        self.ban_list_cache.add(user.id)
        log = await self.add_ban_case(ctx, user, reason)

        if not member_is_external:
            try:
                await user.send(f"You were banned from {ctx.guild.name}",
                                embed=log)
            except Exception:
                pass

        if not member_is_external:
            await user.ban(reason=reason)
        else:
            # hackban for user not currently in guild
            await ctx.guild.ban(discord.Object(id=user.id))

        await ctx.message.reply(embed=log, delete_after=10)
        await ctx.message.delete(delay=10)

        public_chan = ctx.guild.get_channel(
            ctx.settings.guild().channel_public)
        if public_chan:
            log.remove_author()
            log.set_thumbnail(url=user.avatar_url)
            await public_chan.send(embed=log)
Example #7
0
    async def editreason(self, ctx: context.Context,
                         user: permissions.ModsAndAboveExternal, case_id: int,
                         *, new_reason: str) -> None:
        """Edit case reason and the embed in #public-mod-logs. (mod only)

        Example usage
        --------------
        !editreason <@user/ID> <case ID> <reason>

        Parameters
        ----------
        user : discord.Member
            "User to edit case of"
        case_id : int
            "The ID of the case for which we want to edit reason"
        new_reason : str
            "New reason"

        """

        # retrieve user's case with given ID
        cases = await ctx.settings.get_case(user.id, case_id)
        case = cases.cases.filter(_id=case_id).first()

        new_reason = discord.utils.escape_markdown(new_reason)
        new_reason = discord.utils.escape_mentions(new_reason)

        # sanity checks
        if case is None:
            raise commands.BadArgument(
                message=f"{user} has no case with ID {case_id}")

        old_reason = case.reason
        case.reason = new_reason
        case.date = datetime.datetime.now()
        cases.save()

        dmed = True
        log = await logging.prepare_editreason_log(ctx.author, user, case,
                                                   old_reason)
        if isinstance(user, discord.Member):
            try:
                await user.send(f"Your case was updated in {ctx.guild.name}.",
                                embed=log)
            except Exception:
                dmed = False

        public_chan = ctx.guild.get_channel(
            ctx.settings.guild().channel_public)

        found = False
        async with ctx.typing():
            async for message in public_chan.history(limit=200):
                if message.author.id != ctx.me.id:
                    continue
                if len(message.embeds) == 0:
                    continue
                embed = message.embeds[0]
                # print(embed.footer.text)
                if embed.footer.text == discord.Embed.Empty:
                    continue
                if len(embed.footer.text.split(" ")) < 2:
                    continue

                if f"#{case_id}" == embed.footer.text.split(" ")[1]:
                    for i, field in enumerate(embed.fields):
                        if field.name == "Reason":
                            embed.set_field_at(i,
                                               name="Reason",
                                               value=new_reason)
                            await message.edit(embed=embed)
                            found = True
        if found:
            await ctx.message.reply(
                f"We updated the case and edited the embed in {public_chan.mention}.",
                embed=log,
                delete_after=10)
        else:
            await ctx.message.reply(
                f"We updated the case but weren't able to find a corresponding message in {public_chan.mention}!",
                embed=log,
                delete_after=10)
            log.remove_author()
            log.set_thumbnail(url=user.avatar_url)
            await public_chan.send(user.mention if not dmed else "", embed=log)

        await ctx.message.delete(delay=10)