Пример #1
0
    async def unban(self, ctx, user: UserConv, *, reason: str):
        """
        Unbans the id from the guild with a reason.
        If guild has moderation logging enabled, it is logged
        """

        try:
            embed = discord.Embed(description="Done! User Unbanned")
            embed.add_field(name="Reason", value=reason)

            mod = user_discrim(ctx.author)
            unbanned = user_discrim(user)
            clean_reason = escape_backticks(reason)
            content = f"{mod} unbanned {user.mention} ({unbanned}) with reason: `{clean_reason}`"

            await ctx.guild.unban(user, reason=f"{reason} - {mod}")
            await ctx.send(embed=embed)

            self.journal.send("member/unban",
                              ctx.guild,
                              content,
                              icon="unban",
                              user=user)

        except discord.errors.Forbidden:
            raise ManualCheckFailure(
                content="I don't have permission to unban this user")
Пример #2
0
    async def member_join(self, member):
        logger.info(
            "Member %s (%d) joined '%s' (%d) %s",
            user_discrim(member),
            member.id,
            member.guild.name,
            member.guild.id,
            "(BOT)" if member.bot else "",
        )

        if member.bot:
            # Since bots have to be manually invited by an admin,
            # let them handle all the roles and stuff.
            return

        welcome = self.bot.sql.welcome.get_welcome(member.guild)
        roles = self.bot.sql.settings.get_special_roles(member.guild)

        # Delay to let Discord API catch up
        # Without this, some users won't receive the guest role
        await asyncio.sleep(2)

        if welcome.welcome_message and welcome.channel:
            self.send_welcome_message(self.bot, member,
                                      welcome.welcome_message, welcome.channel)

        if roles.guest:
            logger.info("Adding role %s (%d) to new guest", roles.guest.name,
                        roles.guest.id)
            await member.add_roles(roles.guest, reason="New user joined")
Пример #3
0
    async def handle(self, _path, guild, _content, attributes):
        """
        Handle the incoming leave event, and output a kick or ban journal event as appropriate.
        """

        # We want to ignore two arguments
        # pylint: disable=arguments-differ

        leaver = attributes["member"]
        cause = attributes["cause"]

        logger.debug("Received member leave event: %s (%d)", leaver.name,
                     leaver.id)

        if cause.type == MemberLeaveType.KICKED:
            action = "kicked"
            path = "member/kick"
            icon = "kick"
        elif cause.type == MemberLeaveType.BANNED:
            action = "banned"
            path = "member/ban"
            icon = "ban"
        else:
            # We don't care about this event!
            return

        # Get formatted reason
        if cause.reason:
            reason = f"with reason: `{escape_backticks(cause.reason)}`"
        else:
            reason = ""

        # Build journal event content
        mod = user_discrim(cause.cause)
        leaver_discrim = user_discrim(leaver)

        logger.info("Sending journal event %s for %s (%d)", path, leaver.name,
                    leaver.id)
        content = f"{mod} {action} {leaver.mention} ({leaver_discrim}) {reason}"
        self.broadcaster.send(path, guild, content, icon=icon)
Пример #4
0
    async def cleanup_text(self,
                           ctx,
                           text: str,
                           count: int,
                           channel: discord.TextChannel = None):
        """ Deletes the last <count> messages with the given text. """

        await self.check_count(ctx, count)

        if channel is None:
            channel = ctx.channel

        # Deletes the messages with the text
        text = normalize_caseless(text)
        deleted = _Counter()

        def check(message):
            if deleted < count:
                if text in normalize_caseless(message.content):
                    deleted.incr()
                    return True
            return False

        messages = await channel.purge(limit=count * 2,
                                       check=check,
                                       before=ctx.message,
                                       bulk=True)

        # Send journal events
        text = escape_backticks(text)
        causer = user_discrim(ctx.author)
        content = f"{causer} deleted {len(messages)} messages in {channel.mention} matching `{text}`"
        self.journal.send(
            "text",
            ctx.guild,
            content,
            icon="delete",
            count=count,
            channel=channel,
            messags=messages,
            text=text,
            cause=ctx.author,
        )

        obj, file = self.dump_messages(messages)
        content = f"Cleanup by {causer} in {channel.mention} of `{text}` deleted these messages:"
        self.dump.send("text",
                       ctx.guild,
                       content,
                       icon="delete",
                       messages=obj,
                       file=file)
Пример #5
0
    async def avatar(self, ctx, *, name: str = None):
        """
        Displays the given user's avatar and its URL.
        If no argument is passed, the caller is checked instead.
        """

        user = await self.get_user(ctx, name)
        logger.info("Displaying avatar on '%s' (%d)", user.name, user.id)

        embed = discord.Embed(colour=discord.Colour.dark_teal())
        embed.set_author(name=user_discrim(user))
        embed.set_image(url=user.avatar_url)
        await ctx.send(content=user.avatar_url, embed=embed)
Пример #6
0
    async def uinfo(self, ctx, *, name: str = None):
        """
        Fetch information about a user, whether they are in the guild or not.
        If no argument is passed, the caller is checked instead.
        """

        user = await self.get_user(ctx, name)
        usernames, nicknames = self.bot.sql.alias.get_alias_names(
            ctx.guild, user)

        logger.info("Running uinfo on '%s' (%d)", user.name, user.id)

        # Status
        content = StringBuilder()
        if getattr(user, "status", None):
            status = ("do not disturb"
                      if user.status == discord.Status.dnd else user.status)
            content.writeln(f"{user.mention}, {status}")
        else:
            content.writeln(user.mention)

        embed = discord.Embed()
        embed.timestamp = user.created_at
        embed.set_author(name=user_discrim(user))
        embed.set_thumbnail(url=user.avatar_url)

        # User colour
        if hasattr(user, "colour"):
            embed.colour = user.colour

        embed.add_field(name="ID", value=f"`{user.id}`")
        self.uinfo_add_roles(embed, user)
        self.uinfo_add_activity(embed, user, content)

        embed.description = str(content)
        content.clear()

        self.uinfo_add_voice(embed, user)
        self.uinfo_add_aliases(embed, content, usernames, nicknames)

        # Guild join date
        if hasattr(user, "joined_at"):
            embed.add_field(name="Member for",
                            value=fancy_timedelta(user.joined_at))

        # Discord join date
        embed.add_field(name="Account age",
                        value=fancy_timedelta(user.created_at))

        # Send them
        await ctx.send(embed=embed)
Пример #7
0
    async def cleanup_user(self,
                           ctx,
                           user: discord.User,
                           count: int,
                           channel: discord.TextChannel = None):
        """ Deletes the last <count> messages created by the given user. """

        await self.check_count(ctx, count)

        if channel is None:
            channel = ctx.channel

        # Deletes the messages by the user
        deleted = _Counter()

        def check(message):
            if deleted < count:
                if user == message.author:
                    deleted.incr()
                    return True
            return False

        messages = await channel.purge(limit=count * 2,
                                       check=check,
                                       before=ctx.message,
                                       bulk=True)

        # Send journal events
        causer = user_discrim(ctx.author)
        content = f"{causer} deleted {len(messages)} messages in {channel.mention} by {user.mention}"
        self.journal.send(
            "user",
            ctx.guild,
            content,
            icon="delete",
            count=count,
            channel=channel,
            messages=messages,
            user=user,
            cause=ctx.author,
        )

        obj, file = self.dump_messages(messages)
        content = f"Cleanup by {causer} of {user.mention} in {channel.mention} deleted these messages:"
        self.dump.send("user",
                       ctx.guild,
                       content,
                       icon="delete",
                       messages=obj,
                       file=file)
Пример #8
0
    async def nick(self, ctx, member: MemberConv, nick: str = None):
        """ Changes or reset a member's nickname. """

        logger.info("Setting the nickname of user '%s' (%d) to %r",
                    member.name, member.id, nick)

        if member.top_role >= ctx.me.top_role:
            raise ManualCheckFailure(
                "I don't have permission to nick this user")

        mod = user_discrim(ctx.author)
        await member.edit(
            nick=nick,
            reason=f"{mod} {'un' if nick is None else ''}set nickname")
Пример #9
0
    async def softban(self, ctx, user: UserConv, *, reason: str):
        """
        Soft-bans the user from the guild with a reason.
        If guild has moderation logging enabled, it is logged

        Soft-ban is a kick that cleans up the chat
        """

        try:
            embed = discord.Embed(description="Done! User Soft-banned")
            embed.add_field(name="Reason", value=reason)

            mod = user_discrim(ctx.author)
            banned = user_discrim(user)
            clean_reason = escape_backticks(reason)
            content = f"{mod} soft-banned {user.mention} ({banned}) with reason: `{clean_reason}`"

            await ctx.guild.ban(user,
                                reason=f"{reason} - {mod}",
                                delete_message_days=1)
            await asyncio.sleep(0.1)
            await ctx.guild.unban(user, reason=f"{reason} - {mod}")
            await ctx.send(embed=embed)

            self.journal.send(
                "member/softban",
                ctx.guild,
                content,
                icon="soft",
                user=user,
                reason=reason,
                cause=ctx.author,
            )

        except discord.errors.Forbidden:
            raise ManualCheckFailure(
                content="I don't have permission to soft-ban this user")
Пример #10
0
def format_message(welcome_message, ctx):
    user = ctx.author
    channel = ctx.channel
    guild = ctx.guild

    return welcome_message.format(
        mention=user.mention,
        user=user.name,
        discrim=user.discriminator,
        user_discrim=user_discrim(user),
        user_id=user.id,
        channel=channel.mention,
        channel_name=channel.name,
        channel_id=channel.id,
        server=guild.name,
        server_id=guild.id,
        guild=guild.name,
        guild_id=guild.id,
    )
Пример #11
0
    async def member_leave(self, member):
        logger.info(
            "Member %s (%d) left '%s' (%d)",
            user_discrim(member),
            member.id,
            member.guild.name,
            member.guild.id,
        )

        welcome = self.bot.sql.welcome.get_welcome(member.guild)

        if welcome.goodbye_message and welcome.channel:
            self.send_welcome_message(self.bot, member,
                                      welcome.goodbye_message, welcome.channel)

        # Remove from recently_joined, they need to re-agree!
        try:
            self.recently_joined.remove(member)
        except ValueError:
            pass
Пример #12
0
    async def cleanup(self,
                      ctx,
                      count: int,
                      channel: discord.TextChannel = None):
        """ Deletes the last <count> messages, not including this command. """

        await self.check_count(ctx, count)

        if channel is None:
            channel = ctx.channel

        # Delete the messages
        messages = await channel.purge(limit=count,
                                       before=ctx.message,
                                       bulk=True)

        # Send journal events
        causer = user_discrim(ctx.author)
        content = f"{causer} deleted {len(messages)} messages in {channel.mention}"
        self.journal.send(
            "count",
            ctx.guild,
            content,
            icon="delete",
            count=count,
            channel=channel,
            messages=messages,
            cause=ctx.author,
        )

        obj, file = self.dump_messages(messages)
        content = f"Cleanup by {causer} in {channel.mention} deleted these messages:"
        self.dump.send("count",
                       ctx.guild,
                       content,
                       icon="delete",
                       messages=obj,
                       file=file)
Пример #13
0
    async def cleanup_id(self,
                         ctx,
                         message_id: int,
                         channel: discord.TextChannel = None):
        """ Deletes all messages from the passed message ID to the present. """

        if channel is None:
            channel = ctx.channel

        # Make sure it's an ID
        if not is_discord_id(message_id):
            embed = discord.Embed(colour=discord.Colour.red())
            embed.set_author(name="Won't delete to message ID")
            embed.description = (
                f"The given number `{message_id}` doesn't look like a Discord ID."
            )
            raise CommandFailed(embed=embed)

        # Make sure it's not actually a user ID
        try:
            user = await self.bot.fetch_user(message_id)
        except discord.NotFound:
            pass
        else:
            embed = discord.Embed(colour=discord.Colour.red())
            embed.description = (
                f"The passed ID is for user {user.mention}. Did you copy the message ID or the user ID?\n\n"
                f"Not deleting. If you'd like to delete this far, specify the message count directly instead."
            )
            raise CommandFailed(embed=embed)

        # Delete the messages before the message ID
        max_count = self.bot.sql.settings.get_max_delete_messages(ctx.guild)
        messages = await channel.purge(
            limit=max_count,
            check=lambda message: message.id >= message_id,
            before=ctx.message,
            bulk=True,
        )

        if len(messages) == max_count and messages[0].id != message_id:
            embed = discord.Embed(colour=discord.Colour.dark_teal())
            embed.description = (
                f"This guild only allows `{max_count}` messages to be deleted at a time. "
                f"Because of this limitation, message ID `{message_id}` was not actually deleted."
            )
            await ctx.send(embed=embed)

        # Send journal events
        causer = user_discrim(ctx.author)
        content = (f"{causer} deleted {len(messages)} messages in "
                   f"{channel.mention} until message ID {message_id}")
        self.journal.send(
            "id",
            ctx.guild,
            content,
            icon="delete",
            message_id=message_id,
            messages=messages,
            cause=ctx.author,
        )

        obj, file = self.dump_messages(messages)
        content = (f"Cleanup by {causer} until message ID {message_id} in "
                   f"{channel.mention} deleted these messages")
        self.dump.send("id",
                       ctx.guild,
                       content,
                       icon="delete",
                       messages=obj,
                       file=file)