Ejemplo n.º 1
0
    async def helper_role_remove(self, ctx, role: RoleConv, *args):
        logger.info(
            "Removing automatically managed helper role '%s' for guild '%s' (%d)",
            role.name,
            ctx.guild.name,
            ctx.guild.id,
        )

        if len(args) == 1:
            if args[0] == "-h":
                helper_role = self.bot.sql.roles.get_pingable_role_from_original(
                    ctx.guild, role)
                if not helper_role:
                    role = None
                else:
                    role = discord.utils.get(ctx.guild.roles,
                                             id=helper_role.id)
            else:
                raise CommandFailed("Unknown argument")
        channels = self.bot.sql.roles.get_channels_from_role(ctx.guild, role)
        if not channels or not role:
            raise CommandFailed(
                "Role was not pingable or did not exist to begin with")
        await self.role_unpingable(ctx, role, *channels)
        await self.role_unjoinable(ctx, role)
        await role.delete()
Ejemplo n.º 2
0
    async def perform_mute(self, ctx, member: MemberConv, minutes: int, reason: str = None):
        logger.info(
            "Muting user '%s' (%d) for %d minutes", member.name, member.id, minutes
        )

        if minutes <= 0:
            # Since muting prevents members from responding or petitioning staff,
            # a timed release is mandatory. Otherwise they might be forgotten
            # and muted forever.
            raise CommandFailed()

        roles = self.bot.sql.settings.get_special_roles(ctx.guild)
        if roles.mute is None:
            raise CommandFailed(content="No configured mute role")

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

        minutes = max(minutes, 0)
        reason = self.build_reason(ctx, "Muted", minutes, reason, past=True)

        await self.bot.punish.mute(ctx.guild, member, reason)

        # If a delayed event, schedule a Navi task
        if minutes:
            await self.remove_roles(
                ctx, member, minutes, PunishAction.RELIEVE_MUTE, reason
            )
Ejemplo n.º 3
0
    async def perform_unjail(self, ctx, member, minutes, reason):
        roles = self.bot.sql.settings.get_special_roles(ctx.guild)
        if roles.jail is None:
            raise CommandFailed(content="No configured jail role")

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

        if roles.jail_role not in member.roles:
            raise CommandFailed(content="User is not jailed")

        if member != ctx.author:
            if member.top_role == ctx.author.top_role:
                raise CommandFailed(
                    content=
                    "You can not unjail a user with the same role as you")

        minutes = max(minutes, 0)
        reason = self.build_reason(ctx, "Released", minutes, reason, past=True)

        if minutes:
            await self.remove_roles(ctx, member, minutes,
                                    PunishAction.RELIEVE_JAIL, reason)
        else:
            await self.bot.punish.unjail(ctx.guild, member, reason)
Ejemplo n.º 4
0
    async def perform_jail(self, ctx, member, minutes, reason):
        roles = self.bot.sql.settings.get_special_roles(ctx.guild)
        if roles.jail is None:
            raise CommandFailed(content="No configured jail role")

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

        if roles.jail_role in member.roles:
            raise CommandFailed(content="User is already jailed")

        # Check that users top role is not the same as the requesters top role.
        if member != ctx.author:
            if member.top_role == ctx.author.top_role:
                raise CommandFailed(
                    content="You can not jail a user with the same role as you"
                )

        minutes = max(minutes, 0)
        reason = self.build_reason(ctx, "Jailed", minutes, reason)

        await self.bot.punish.jail(ctx.guild, member, reason)

        # If a delayed event, schedule a Navi task
        if minutes:
            await self.remove_roles(ctx, member, minutes,
                                    PunishAction.RELIEVE_JAIL, reason)
Ejemplo n.º 5
0
    async def log_send(self, ctx, path: str, content: str, *attributes: str):
        """
        Manually send a journal event to test logging channels.
        The content must be a single argument, wrapped in quotes if
        it has spaces, and you can specify a number of journal attributes
        in the form KEY=VALUE.
        """

        if path == "/":
            raise CommandFailed(content="Cannot broadcast on /")

        journal_attributes = {}
        for attribute in attributes:
            try:
                key, value = attribute.split("=")
                journal_attributes[key] = value
            except ValueError:
                raise CommandFailed(
                    content="All attributes must be in the form KEY=VALUE"
                )

        logger.info(
            "Sending manual journal event: '%s' (attrs: %s)",
            content,
            journal_attributes,
        )
        self.bot.get_broadcaster(path).send(
            "", ctx.guild, content, **journal_attributes
        )
Ejemplo n.º 6
0
async def check_hashsums(*hashsums):
    if not hashsums:
        raise CommandFailed()

    if not all(
            map(lambda h: len(h) == 40 and HEXADECIMAL_REGEX.match(h),
                hashsums)):
        raise CommandFailed(content="SHA1 hashes are 40 hex digits long.")
Ejemplo n.º 7
0
    async def remind_me(self, ctx, when: str, *, message: str):
        """ Request the bot remind you in the given time. """

        timestamp = dateparser.parse(when)
        now = datetime.now()
        if timestamp is None:
            embed = discord.Embed(colour=discord.Colour.red())
            embed.description = (
                f"Unknown date specification: `{escape_backticks(when)}`")
            raise CommandFailed(embed=embed)

        if now > timestamp:
            # First, try to see if a naive time specification put it in the past
            new_timestamp = dateparser.parse(f"in {when}")
            if new_timestamp is None or now > new_timestamp:
                time_since = fancy_timedelta(now - timestamp)
                embed = discord.Embed(colour=discord.Colour.red())
                embed.description = f"Specified date was in the past: {time_since} ago"
                raise CommandFailed(embed=embed)

            # Was successful, replace it
            timestamp = new_timestamp

        # Check time
        assert timestamp > now
        duration = timestamp - now
        time_since = fancy_timedelta(duration)
        if duration > MAX_REMINDER_DURATION:
            embed = discord.Embed(colour=discord.Colour.red())
            embed.description = f"Specified date is too far away: {time_since}"
            raise CommandFailed(embed=embed)

        logger.info(
            "Creating self-reminder SendMessageTask for '%s' (%d): %r",
            ctx.author.name,
            ctx.author.id,
            message,
        )

        # Create navi task
        embed = discord.Embed(colour=discord.Colour.dark_teal())
        embed.set_author(name=f"Reminder made {time_since} ago")
        embed.description = f"You asked to be reminded of:\n\n{message}"
        embed.timestamp = now
        self.bot.add_tasks(
            SendMessageTask(
                self.bot,
                None,
                ctx.author,
                timestamp,
                None,
                ctx.author,
                embed=embed,
                metadata={
                    "type": "reminder",
                    "message": message
                },
            ))
Ejemplo n.º 8
0
    async def unload(self, ctx, cogname: str):
        """ Unloads the named cog. """

        logger.info("Cog unload requested: %s", cogname)

        if cogname in Reloader.MANDATORY_COGS:
            logger.info("Cog cannot be unloaded because it is mandatory")
            embed = discord.Embed(colour=discord.Colour.red())
            embed.set_author(name="Cannot unload")
            embed.description = "Cog cannot be unloaded because it is mandatory"

            content = f"Unable to unload cog {cogname} because it is mandatory"
            self.journal.send(
                "unload/fail",
                ctx.guild,
                content,
                icon="cog",
                cogname=cogname,
                reason="mandatory",
            )
            raise CommandFailed(embed=embed)

        try:
            self.unload_cog(cogname)
        except Exception as error:
            logger.error("Unloading cog %s failed", cogname, exc_info=error)
            if isinstance(error, KeyError):
                # For no such cog errors
                (error, ) = error.args
            embed = discord.Embed(colour=discord.Colour.red(),
                                  description=f"```\n{error}\n```")
            embed.set_author(name="Unload failed")

            content = f"Error while trying to unload cog {cogname}"
            self.journal.send(
                "unload/fail",
                ctx.guild,
                content,
                icon="cog",
                cogname=cogname,
                reason="error",
                error=error,
            )
            raise CommandFailed(embed=embed)
        else:
            logger.info("Unloaded cog: %s", cogname)
            embed = discord.Embed(colour=discord.Colour.green(),
                                  description=f"```\n{cogname}\n```")
            embed.set_author(name="Unloaded")

            content = f"Successfully unloaded cog {cogname}"
            self.journal.send("unload",
                              ctx.guild,
                              content,
                              icon="cog",
                              cogname=cogname)
            await ctx.send(embed=embed)
Ejemplo n.º 9
0
    async def pinghelpers(self, ctx):
        """Pings helpers if used in the respective channel"""

        cooldown_time = self.bot.config.helper_ping_cooldown

        logger.info(
            "User '%s' (%d) is pinging the helper role in channel '%s' in guild '%s' (%d)",
            ctx.author,
            ctx.author.mention,
            ctx.channel,
            ctx.guild,
            ctx.guild.id,
        )
        pingable_channels = self.bot.sql.roles.get_pingable_role_channels(
            ctx.guild)
        # this will return an empty list if there is nothing.
        channel_role = [(channel, role) for channel, role in pingable_channels
                        if channel == ctx.channel]

        if not channel_role:
            embed = discord.Embed(colour=discord.Colour.red())
            embed.set_author(name="Failed to ping helper role.")
            embed.description = f"There is no helper role set for this channel."
            raise CommandFailed(embed=embed)

        channel_user = (ctx.channel.id, ctx.author.id)
        cooldown = self.cooldowns.get(channel_user)

        if mod_perm(ctx) or not cooldown or cooldown <= datetime.now():
            self.cooldowns[channel_user] = datetime.now() + timedelta(
                seconds=cooldown_time)

            # This will loop over the dictionary and remove expired entries.
            key_list = list(self.cooldowns.keys())
            for k in key_list:
                if self.cooldowns[k] < datetime.now():
                    del self.cooldowns[k]

            # channel[0] will be the first tuple in the list. there will only be one, since the
            # channel's id is a primary key (tb_pingable_role_channel in roles.py). channel[0][1] is the role.
            await ctx.send(
                f"{channel_role[0][1].mention}, {ctx.author.mention} needs help."
            )

        elif cooldown > datetime.now():
            # convert deltatime into string: Hh, Mm, Ss
            time_remaining = cooldown - datetime.now()

            embed = discord.Embed(colour=discord.Colour.red())
            embed.set_author(name="Failed to ping helper role.")
            embed.description = f"You can ping the helper role for this channel again in {fancy_timedelta(time_remaining)}"
            raise CommandFailed(embed=embed)
Ejemplo n.º 10
0
    async def role_unpingable(self, ctx, role: RoleConv,
                              *channels: TextChannelConv):
        logger.info(
            "Making role '%s' not pingable in guild '%s' (%d), channel(s) [%s]",
            role.name,
            ctx.guild.name,
            ctx.guild.id,
            self.str_channels(channels),
        )

        if not channels:
            raise CommandFailed()

        # See role_pingable for an explanation
        channel_role = zip(
            *self.bot.sql.roles.get_pingable_role_channels(ctx.guild))
        pingable_channels = next(channel_role, set())

        exempt_channels = []

        with self.bot.sql.transaction():
            for channel in channels:
                if channel in pingable_channels:
                    self.bot.sql.roles.remove_pingable_role_channel(
                        ctx.guild, channel, role)
                else:
                    exempt_channels.append(channel)

        if exempt_channels:
            embed = discord.Embed(colour=discord.Colour.dark_grey())
            embed.set_author(
                name="Failed to make role unpingable in channels: ")
            descr = StringBuilder(sep=", ")
            for channel in exempt_channels:
                descr.write(channel.mention)
            embed.description = str(descr)
            await ctx.send(embed=embed)
            if set(exempt_channels) == set(channels):
                raise CommandFailed()

        # Send journal event
        content = f"Role was set as not pingable in channels: {self.str_channels(channels)}, except {self.str_channels(exempt_channels)}"
        self.journal.send(
            "pingable/remove",
            ctx.guild,
            content,
            icon="role",
            role=role,
            channels=channels,
        )
Ejemplo n.º 11
0
    async def check_role(self, ctx, role):
        embed = discord.Embed(colour=discord.Colour.red())
        if role.is_default():
            embed.description = "@everyone role cannot be assigned for this purpose"
            raise CommandFailed(embed=embed)

        special_roles = self.bot.sql.settings.get_special_roles(ctx.guild)
        if role in special_roles:
            embed.description = "Cannot assign the same role for multiple purposes"
            raise CommandFailed(embed=embed)

        embed = permissions.elevated_role_embed(ctx.guild, role, "warning")
        if embed is not None:
            await ctx.send(embed=embed)
Ejemplo n.º 12
0
async def delete_filter(bot, filters, location, text):
    logger.info("Removing %r from server filter for '%s' (%d)", text,
                location.name, location.id)

    try:
        with bot.sql.transaction():
            if bot.sql.filter.delete_filter(location, text):
                filters[location].pop(text, None)
                logger.debug("Succesfully removed filter")
            else:
                logger.debug("Filter was not present, deletion failed")
                raise CommandFailed()
    except Exception as error:
        logger.error("Error deleting filter", exc_info=error)
        raise CommandFailed()
Ejemplo n.º 13
0
    async def perform_focus(self, ctx, member, minutes, reason):
        roles = self.bot.sql.settings.get_special_roles(ctx.guild)
        if roles.focus is None:
            raise CommandFailed(content="No configured focus role")

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

        if roles.focus_role in member.roles:
            raise CommandFailed(content="User is already focused")

        minutes = max(minutes, 0)
        reason = self.build_reason(ctx, "Focus", minutes, reason)

        await self.bot.punish.focus(ctx.guild, member, reason)
Ejemplo n.º 14
0
async def add_filter(cog, filters, location, level, text):
    logger.info(
        "Adding %r to server filter '%s' for '%s' (%d)",
        text,
        level.value,
        location.name,
        location.id,
    )

    try:
        with cog.bot.sql.transaction():
            if text in filters[location]:
                cog.bot.sql.filter.update_filter(location, level, text)
            else:
                cog.bot.sql.filter.add_filter(location, level, text)
    except Exception as error:
        logger.error("Error adding filter", exc_info=error)
        raise CommandFailed()
    else:
        filter = Filter(text)
        filters[location][text] = (filter, level)

    if isinstance(location, discord.Guild):
        logger.debug("Checking all members against new guild text filter")
        cog.bot.loop.create_task(
            check_all_members_on_filter(cog, location, filter))
Ejemplo n.º 15
0
    async def add_alt(self, ctx, first_user: UserConv, second_user: UserConv):
        """ Add a suspected alternate account for a user. """

        logger.info(
            "Adding suspected alternate account pair for '%s' (%d) and '%s' (%d)",
            first_user.name,
            first_user.id,
            second_user.name,
            second_user.id,
        )

        if first_user == second_user:
            embed = discord.Embed(colour=discord.Colour.red())
            embed.description = "Both users are the same person!"
            raise CommandFailed(embed=embed)

        with self.bot.sql.transaction():
            self.bot.sql.alias.add_possible_alt(ctx.guild, first_user,
                                                second_user)

        content = f"Added {first_user.mention} and {second_user.mention} as possible alt accounts."
        self.journal.send(
            "alt/add",
            ctx.guild,
            content,
            icon="item_add",
            users=[first_user, second_user],
        )
Ejemplo n.º 16
0
    async def guestify(self, ctx):
        """
        In the event that the bot is not properly assigning guest roles to users,
        this command can be used to manually apply it to all roleless users.
        """

        roles = self.bot.sql.settings.get_special_roles(ctx.guild)
        if roles.guest is None:
            prefix = self.bot.prefix(ctx.guild)
            embed = discord.Embed(colour=discord.Colour.red())
            embed.description = (
                f"No guest role set.\nYou can assign one using `{prefix}guest <role>`."
            )
            raise CommandFailed(embed=embed)

        tasks = []
        for member in ctx.guild.members:
            if member.top_role == ctx.guild.default_role:
                tasks.append(
                    member.add_roles(roles.guest,
                                     reason="Manually assigning guest role",
                                     atomic=True))
        await asyncio.gather(*tasks)

        embed = discord.Embed(colour=discord.Colour.dark_teal())
        if tasks:
            embed.description = f"Added the {roles.guest.mention} role to `{len(tasks)}` member{plural(len(tasks))}."
        else:
            embed.description = "No roleless members found."
        await ctx.send(embed=embed)
Ejemplo n.º 17
0
    async def unmute(
        self, ctx, member: MemberConv, minutes: int = 0, *, reason: str = None
    ):
        """
        Unmutes the user, with an optional delay in minutes.
        Requires a mute role to be configured.
        Set 'minutes' to 0 to unmute immediately.
        """

        logger.info(
            "Unmuting user '%s' (%d) in %d minutes", member.name, member.id, minutes
        )

        roles = self.bot.sql.settings.get_special_roles(ctx.guild)
        if roles.mute is None:
            raise CommandFailed(content="No configured mute role")

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

        minutes = max(minutes, 0)
        reason = self.build_reason(ctx, "Unmuted", minutes, reason, past=True)

        if minutes:
            await self.remove_roles(
                ctx, member, minutes, PunishAction.RELIEVE_MUTE, reason
            )
        else:
            await self.bot.punish.unjail(ctx.guild, member, reason)
Ejemplo n.º 18
0
    async def cleanup_user_cancel(self, ctx, code: str):
        """ Cancels a deletion code. """

        try:
            print(self.delete_codes, code)
            guild, user, flag = self.delete_codes[code]

            # Check if the guilds match
            if guild != ctx.guild:
                raise KeyError
        except KeyError:
            embed = discord.Embed(colour=discord.Colour.red())
            embed.title = "Complete user message purge"
            embed.description = (
                "Invalid code provided, no deletion queue exists with that value."
            )
            raise CommandFailed(embed=embed)

        # Actually perform the deletion
        flag.set()
        del self.delete_codes[code]

        # Send result
        embed = discord.Embed(colour=discord.Colour.dark_teal())
        embed.title = "Complete user message purge"
        embed.description = f"Deletion associated with {user.mention} cancelled."
        await ctx.send(embed=embed)
Ejemplo n.º 19
0
    async def log_remove(self, ctx, path: str, channel: TextChannelConv):
        """
        Removes a journal logger for the given path from the channel.
        """

        logger.info(
            "Removing journal logger for channel #%s (%d) from path '%s'",
            channel.name,
            channel.id,
            path,
        )

        listener = self.router.get(path, channel=channel)
        if listener is None:
            # No listener found
            raise CommandFailed(
                content=f"No output on `{path}` found for {channel.mention}"
            )

        self.router.unregister(listener)

        with self.bot.sql.transaction():
            self.bot.sql.journal.delete_journal_output(ctx.guild, channel, path)

        await channel.send(content=self.log_updated_message(channel))
        content = f"Removed journal logger to {channel.mention} for `{path}`"
        self.journal.send(
            "channel/remove",
            ctx.guild,
            content,
            icon="journal",
            channel=channel,
            path=path,
        )
Ejemplo n.º 20
0
    async def log_dm_remove(self, ctx, path: str):
        """
        Removes a DM journal logger for the given path.
        """

        logger.info(
            "Removing journal logger for user '%s' (%d) from path '%s'",
            ctx.author.name,
            ctx.author.id,
            path,
        )

        user = self.bot.get_user(ctx.author.id)
        listener = self.router.get(path, user=user)
        if listener is None:
            # No listener found
            raise CommandFailed(
                content=f"No output on `{path}` found for {user_discrim(ctx.author)}"
            )

        self.router.unregister(listener)

        with self.bot.sql.transaction():
            self.bot.sql.journal.delete_journal_output(ctx.guild, user, path)

        await ctx.send(content=self.log_updated_message(user))
        content = f"Removed journal logger to {user_discrim(ctx.author)} for `{path}`"
        self.journal.send(
            "user/remove",
            ctx.guild,
            content,
            icon="journal",
            user=ctx.author,
            path=path,
        )
Ejemplo n.º 21
0
    async def channel_set(self, ctx, *channels: TextChannelConv):
        """ Overwrites the channel(s) in the restricted role channel list to exactly this. """

        logger.info(
            "Setting channels to be used for role commands in guild '%s' (%d): [%s]",
            ctx.guild.name,
            ctx.guild.id,
            ", ".join(channel.name for channel in channels),
        )

        if not channels:
            raise CommandFailed()

        # Write new channel list to database
        with self.bot.sql.transaction():
            self.bot.sql.roles.remove_all_role_command_channels(ctx.guild)
            for channel in channels:
                self.bot.sql.roles.add_role_command_channel(ctx.guild, channel)

        # Send response
        embed = discord.Embed(colour=discord.Colour.dark_teal())
        embed.set_author(name="Set channels to be used for adding roles")
        descr = StringBuilder(sep=", ")
        for channel in channels:
            descr.write(channel.mention)
        embed.description = str(descr)
        await ctx.send(embed=embed)

        # Send journal event
        self.channel_journal(ctx.guild)
Ejemplo n.º 22
0
    async def prune(self, ctx, days: int = 7):
        """
        Prunes users that have not used the !agree command for at least the given number of days.
        Defaults to seven days.
        """

        pruned_members = await self.prune_member(ctx, days)

        # Check if prune_members is None as if it is there is not member role set
        # If there is no member role set pruning members makes no sense
        if pruned_members is None:
            error_message = (
                "The server has no member role set, so pruning will have no effect"
            )
            embed = discord.Embed(
                description=error_message, colour=discord.Colour.red()
            )
            raise CommandFailed(embed=embed)

        content = f"Pruned {pruned_members} members"
        embed = discord.Embed(description=content, colour=discord.Colour.dark_teal())
        await ctx.send(embed=embed)

        self.journal.send(
            "prune",
            ctx.guild,
            content,
            icon="snip",
            cause=ctx.author,
            members=pruned_members,
        )
Ejemplo n.º 23
0
    async def filter_immunity_remove(self, ctx, *members: discord.Member):
        """
        Removes a set of users from the server filter immunity list.
        """

        if not members:
            raise CommandFailed()

        member_names = ", ".join(
            (f"'{member.name}' ({member.id})" for member in members))
        logger.info(
            "Removing members to guild '%s' (%d) filter immunity list: %s",
            ctx.guild.name,
            ctx.guild.id,
            member_names,
        )

        with self.bot.sql.transaction():
            for member in members:
                logger.debug(
                    "Removing member to filter immune: %s (%d)",
                    member.display_name,
                    member.id,
                )
                self.bot.sql.filter.remove_filter_immune_user(
                    ctx.guild, member)

        for member in members:
            content = f"Removed {member.name}#{member.discriminator} from filter immunity list"
            self.journal.send("immunity/remove",
                              ctx.guild,
                              content,
                              icon="person")
Ejemplo n.º 24
0
    async def role_unjoinable(self, ctx, *roles: RoleConv):
        """ Allows a moderator to remove roles from the self-assignable group. """

        logger.info(
            "Removing joinable roles for guild '%s' (%d): [%s]",
            ctx.guild.name,
            ctx.guild.id,
            ", ".join(role.name for role in roles),
        )

        if not roles:
            raise CommandFailed()

        # Remove roles from database
        with self.bot.sql.transaction():
            for role in roles:
                self.bot.sql.roles.remove_assignable_role(ctx.guild, role)

        # Send response
        embed = discord.Embed(colour=discord.Colour.dark_teal())
        embed.set_author(name="Made roles not joinable")
        descr = StringBuilder(sep=", ")
        for role in roles:
            descr.write(role.mention)
        embed.description = str(descr)
        await ctx.send(embed=embed)

        # Send journal event
        content = f"Roles were set as not joinable: {self.str_roles(roles)}"
        self.journal.send("joinable/remove",
                          ctx.guild,
                          content,
                          icon="role",
                          roles=roles)
Ejemplo n.º 25
0
    def get_flags(flags):
        recursive = True
        for flag in flags:
            if flag == "-exact":
                recursive = False
            else:
                raise CommandFailed(content=f"No such flag: `{flag}`")

        return recursive
Ejemplo n.º 26
0
    async def log_rename(
        self,
        ctx,
        old_channel: TextChannelConv,
        new_channel: TextChannelConv,
        path: str,
        *flags: str,
    ):
        """
        Moves a journal logger from one channel to another.
        Accepts the optional flags:
            -exact, Don't recursively accept journal events from children.
        """

        logger.info(
            "Moving journal logger from channel #%s (%d) to #%s (%d) for path '%s'",
            old_channel.name,
            old_channel.id,
            new_channel.name,
            new_channel.id,
            path,
        )

        recursive = self.get_flags(flags)

        listener = self.router.get(path, channel=old_channel)
        if listener is None:
            # No listener found at old channel
            raise CommandFailed(
                content=f"No output on `{path}` found for {old_channel.mention}"
            )

        listener.channel = new_channel

        logger.debug("Updating database for moved channel output")
        with self.bot.sql.transaction():
            self.bot.sql.journal.delete_journal_output(ctx.guild, old_channel, path)
            self.bot.sql.journal.add_journal_output(
                ctx.guild, new_channel, path, recursive
            )

        await asyncio.gather(
            old_channel.send(content=self.log_updated_message(old_channel)),
            new_channel.send(content=self.log_updated_message(new_channel)),
        )

        content = f"Moved journal logger from {old_channel.mention} to {new_channel.mention} for `{path}`"
        self.journal.send(
            "channel/move",
            ctx.guild,
            content,
            icon="journal",
            old_channel=old_channel,
            new_channel=new_channel,
            path=path,
            recursive=recursive,
        )
Ejemplo n.º 27
0
    async def role_joinable(self, ctx, *roles: RoleConv):
        """ Allows a moderator to add roles to the self-assignable group. """

        logger.info(
            "Adding joinable roles for guild '%s' (%d): [%s]",
            ctx.guild.name,
            ctx.guild.id,
            ", ".join(role.name for role in roles),
        )

        if not roles:
            raise CommandFailed()

        # Get special roles
        special_roles = self.bot.sql.settings.get_special_roles(ctx.guild)

        # Ensure none of the roles grant any permissions
        for role in roles:
            embed = permissions.elevated_role_embed(ctx.guild, role, "error")
            if embed is not None:
                raise ManualCheckFailure(embed=embed)

            for attr in ("member", "guest", "mute", "jail"):
                if role == getattr(special_roles, attr):
                    embed = discord.Embed(colour=discord.Colour.red())
                    embed.set_author(name="Cannot add role as assignable")
                    embed.description = (
                        f"{role.mention} cannot be self-assignable, "
                        f"it is already used as the **{attr}** role!")
                    raise ManualCheckFailure(embed=embed)

        # Get roles that are already assignable
        assignable_roles = self.bot.sql.roles.get_assignable_roles(ctx.guild)

        # Add roles to database
        with self.bot.sql.transaction():
            for role in roles:
                if role not in assignable_roles:
                    self.bot.sql.roles.add_assignable_role(ctx.guild, role)

        # Send response
        embed = discord.Embed(colour=discord.Colour.dark_teal())
        embed.set_author(name="Made roles joinable")
        descr = StringBuilder(sep=", ")
        for role in roles:
            descr.write(role.mention)
        embed.description = str(descr)
        await ctx.send(embed=embed)

        # Send journal event
        content = f"Roles were set as joinable: {self.str_roles(roles)}"
        self.journal.send("joinable/add",
                          ctx.guild,
                          content,
                          icon="role",
                          roles=roles)
Ejemplo n.º 28
0
    def check_roles(self, ctx, roles):
        if not roles:
            raise CommandFailed()

        assignable_roles = self.bot.sql.roles.get_assignable_roles(ctx.guild)
        for role in roles:
            if role not in assignable_roles:
                embed = discord.Embed(colour=discord.Colour.red())
                embed.set_author(name="Role not assignable")
                embed.description = f"The role {role.mention} cannot be self-assigned"
                raise CommandFailed(embed=embed)

            if role >= ctx.me.top_role:
                embed = discord.Embed(colour=discord.Colour.red())
                embed.set_author(name="Error assigning roles")
                embed.description = (
                    f"Cannot assign {role.mention}, which is above me in the hierarchy"
                )
                raise ManualCheckFailure(embed=embed)
Ejemplo n.º 29
0
    async def check_count(self, ctx, count):
        embed = discord.Embed(colour=discord.Colour.red())
        max_count = self.bot.sql.settings.get_max_delete_messages(ctx.guild)
        if count < 1:
            embed.description = f"Invalid message count: {count}"
            raise CommandFailed(embed=embed)

        if is_discord_id(count):
            prefix = self.bot.prefix(ctx.guild)
            embed.description = (
                "This looks like a Discord ID. If you want to delete all "
                f"messages up to a message ID, use `{prefix}cleanupid`.")
            raise CommandFailed(embed=embed)

        if count > max_count:
            embed.description = (
                "Count too high. Maximum configured for this guild is "
                f"`{max_count}`.")
            raise CommandFailed(embed=embed)
Ejemplo n.º 30
0
    async def sha1sum(self, ctx, *urls: str):
        """ Gives the SHA1 hashes of any files attached to the message. """

        # Check all URLs
        links = []
        for url in urls:
            match = URL_REGEX.match(url)
            if match is None:
                raise CommandFailed(content=f"Not a valid url: {url}")
            links.append(match[1])
        links.extend(attach.url for attach in ctx.message.attachments)

        # Get list of "names"
        names = list(urls)
        names.extend(attach.filename for attach in ctx.message.attachments)

        # Send error if no URLS
        if not links:
            raise CommandFailed(content="No URLs listed or files attached.")

        # Download and check files
        contents = []
        content = StringBuilder("Hashes:\n```")
        buffers = await download_links(links)
        for i, binio in enumerate(buffers):
            if binio is None:
                hashsum = SHA1_ERROR_MESSAGE
            else:
                hashsum = sha1(binio.getbuffer()).hexdigest()

            content.writeln(f"{hashsum} {names[i]}")
            if len(content) > 1920:
                contents.append(content)
                if i < len(buffers) - 1:
                    content.clear()
                    content.writeln("```")

        if len(content) > 4:
            content.writeln("```")
            contents.append(content)

        for content in contents:
            await ctx.send(content=str(content))