Example #1
0
async def attempt_unbanish(ctx: Context, coginfo: CogInfo,
                           template_engine: TemplateEngine) -> str:
    """Undo one or more banishes."""
    if coginfo.bot:
        bot: MrFreeze = coginfo.bot

    mentions = ctx.message.mentions
    victims = [
        u for u in mentions
        if not u.guild_permissions.administrator and u != bot.user
    ]
    success_list: List[Member] = list()
    fails_list: List[Member] = list()
    success_string = ""
    fails_string = ""
    error_string = ""
    http_exception = False
    forbidden_exception = False
    other_exception = False

    for victim in victims:
        error = await mute_db.carry_out_unbanish(bot, victim, logger)
        if isinstance(error, Exception):
            fails_list.append(victim)
            if isinstance(error, discord.HTTPException):
                http_exception = True
            elif isinstance(error, discord.Forbidden):
                forbidden_exception = True
            else:
                other_exception = True

        else:
            success_list.append(victim)

        success_string = default.mentions_list(success_list)
        fails_string = default.mentions_list(fails_list)
        error_string = get_error_string(http_exception, forbidden_exception,
                                        other_exception)

    template = get_mute_response_type(success_list, fails_list, undo=True)
    logger.debug(f"attempt_banish(): Setting template to {template}")

    response_template = template_engine.get_template(ctx.invoked_with,
                                                     template)
    if response_template:
        response = response_template.substitute(
            author=ctx.author.mention,
            victims=success_string,
            fails=fails_string,
            errors=error_string,
        )
        return f"{ctx.author.mention} {response}"
    else:
        return f"{ctx.author.mention} Something went wrong, I'm literally at a loss for words."
Example #2
0
    async def _kick(self, ctx: Context, *args: str) -> None:
        """Force a user to leave the server temporarily."""
        success_list = list()
        fail_list = list()
        mods_list = list()
        mentions = ctx.message.mentions
        forbidden_error = False
        http_error = False
        reason = self.extract_reason(" ".join(args))
        verb = ctx.invoked_with

        # If they tried to kick a mod christmas is cancelled.
        mods_list = [
            user for user in mentions if user.guild_permissions.administrator
        ]
        ment_mods = default.mentions_list(mods_list)

        tried_to_kick_mod = False
        if len(mods_list) > 0:
            tried_to_kick_mod = True

        # Start the kicking.
        if len(mentions) > 0 and not tried_to_kick_mod:
            for victim in mentions:
                try:
                    if reason is None:
                        await ctx.guild.kick(victim)
                        status = f"{WHITE_B}{victim.name}#{victim.discriminator}{CYAN} was "
                        status += f"{RED_B}{verb}ed from {ctx.guild.name} {CYAN}by {GREEN_B}"
                        status += f"{ctx.author.name}#{ctx.author.discriminator}"
                    else:
                        await ctx.guild.kick(victim, reason=reason)
                        status = f"{WHITE_B}{victim.name}#{victim.discriminator}{CYAN} was "
                        status += f"{RED_B}{verb}ed from {ctx.guild.name} {CYAN}by {GREEN_B}"
                        status += f"{ctx.author.name}#{ctx.author.discriminator}{CYAN}."
                        status += f"\n{WHITE_B}Reason given: {WHITE}{reason}{RESET}"
                    success_list.append(victim)
                    self.logger.info(status)

                except discord.Forbidden:
                    fail_list.append(victim)
                    forbidden_error = True

                    status = f"{RED_B}ERROR {CYAN}I was not allowed to {RED_B}!{verb} "
                    status += f"{WHITE_B}{victim.name}#{victim.discriminator}"
                    status += f"{CYAN} in {RED_B}{ctx.guild.name}{CYAN}.{RESET}"
                    self.logger.info(status)

                except discord.HTTPException:
                    fail_list.append(victim)
                    http_error = True

                    status = f"{RED_B}ERROR {CYAN}I couldn't {RED_B}!{verb} {WHITE_B}"
                    status += f"{victim.name}#{victim.discriminator}{CYAN} in {RED_B}"
                    status += f"{ctx.guild.name} {CYAN}due to an HTTP Exception.{RESET}"
                    self.logger.info(status)

        # This will convert the lists into mentions suitable for text display:
        # user1, user2 and user 3
        ment_success = default.mentions_list(success_list)
        ment_fail = default.mentions_list(fail_list)

        # Preparation of replystrings.
        # Errors are added further down.

        # Had at least one success and no fails.
        if (len(success_list) > 0) and (len(fail_list) == 0):

            # Singular
            if len(success_list) == 1:
                replystr = f"{ctx.author.mention} The smud who goes by the name of {ment_success} "
                replystr += f"has been {verb}ed from the server, never to be seen again!"

            # Plural
            else:
                replystr = f"{ctx.author.mention} The smuds who go by the names of {ment_success} "
                replystr += f"have been {verb}ed from the server, never to be seen again!"

        # Had no successes and at least one fail.
        elif (len(success_list) == 0) and (len(fail_list) > 0):

            # Singular
            if len(fail_list) == 1:
                replystr = f"{ctx.author.mention} So... it seems I wasn't able to {verb} "
                replystr += f"{ment_fail} due to: "

            # Plural
            else:
                replystr = f"{ctx.author.mention} So... it seems I wasn't able to "
                replystr += f"{verb} any of {ment_fail}.\nThis was due to: "

        # Had at least one success and at least one fail.
        elif (len(success_list) > 0) and (len(fail_list) > 0):
            # Singular and plural don't matter here.
            replystr = f"{ctx.author.mention} The request was executed with mixed results."
            replystr += f"\n{verb.capitalize()}ed: {ment_success}\n"
            replystr += f"Not {verb.capitalize()}ed: {ment_fail}\nThis was due to: "

        # Had no mentions whatsoever.
        elif len(mentions) == 0:
            # Singular and plural don't matter here.
            replystr = f"{ctx.author.mention} You forgot to mention anyone "
            replystr += f"you doofus. Who exactly am I meant to {verb}??"

        # Now we're adding in the error codes if there are any.
        if forbidden_error and http_error:
            replystr += "Insufficient privilegies and HTTP exception."
        elif not forbidden_error and http_error:
            replystr += "HTTP exception."
        elif forbidden_error and not http_error:
            replystr += "Insufficient privilegies."

        # Finally, a special message to people who tried to kick a mod.
        if tried_to_kick_mod:
            if (len(mods_list) == 1) and ctx.author in mods_list:
                replystr = f"{ctx.author.mention} You can't {verb} yourself, silly."
            elif (len(mods_list)) == 1:
                replystr = f"{ctx.author.mention} Unfortunately you can't {verb} "
                replystr += f"{ment_mods}, because they're a mod."
            else:
                replystr = f"{ctx.author.mention} Unfortunately you can't {verb} "
                replystr += f"{ment_mods}, because they're mods."

        await ctx.send(replystr)
Example #3
0
    async def _unban(self, ctx: Context, *args: str) -> None:
        """Unpermanent removal from sight of a previously banned user."""
        forbidden_error = False
        http_error = False
        banlist = list()

        # This is a shortcut to invoke the banlist command with !unban list.
        if args == ("list", ):
            showbans = True
        else:
            showbans = False

        try:
            banlist = await ctx.guild.bans()
        except discord.Forbidden:
            forbidden_error = True
        except discord.HTTPException:
            http_error = True

        # We will assume that all args that are digits are ids
        # and all args of the form characters#fourdigits are
        # user names.
        usr_names = re.findall(r"(?<=\s)\S+#\d{4}(?=\s)",
                               (" " + ctx.message.content + " "))
        usr_ids = re.findall(r"(?<=\s)\d+(?=\s)",
                             (" " + ctx.message.content + " "))

        success_list = list()
        fail_list = list()
        found_anyone = False
        for ban_entry in banlist:
            user = ban_entry.user
            user_str = f"{user.name}#{user.discriminator}"

            # Below is an easy and expandable way to add criterias for unban.
            # Every if-statement corresponds to one criteria.
            #
            # For example we could easily add this as an extra criteria
            # if we wanted to. This would match any username, not requiring
            # the #identifier

            if user_str in usr_names:
                entry_hit = True
            elif (str(user.id) in usr_ids):
                entry_hit = True
            else:
                entry_hit = False

            # If any of the above resulted in a hit we'll try to remove the ban
            if entry_hit:
                found_anyone = True
                try:
                    await ctx.guild.unban(user)
                    success_list.append(user)

                except discord.Forbidden:
                    forbidden_error = True
                    fail_list.append(user)

                except discord.HTTPException:
                    http_error = True
                    fail_list.append(user)

        any_error = False
        if forbidden_error or http_error:
            any_error = True

        if forbidden_error and http_error:
            error_str = 'a mix of insufficient privilegies and HTTP issues'
        elif forbidden_error:
            error_str = 'insufficient privilegies'
        elif http_error:
            error_str = 'HTTP issues'
        else:
            error_str = 'unknown error'

        # Now all we need is a reply string.
        ment_success = default.mentions_list(success_list)
        ment_fail = default.mentions_list(fail_list)

        if showbans:
            # This is just a shortcut to invoke the listban command.
            await ctx.invoke(self.bot.get_command('listban'))

        elif not found_anyone and not any_error:
            # No banned users were found in the message.
            replystr = f"{ctx.author.mention} I wasn't able to spot any banned usernames "
            replystr += "or IDs in that message of yours."

        elif any_error and len(fail_list) == 0:
            # Encountered an error during listing,
            # no unban attempts have been made.
            replystr = f"{ctx.author.mention} Due to {error_str} I wasn't able to "
            replystr += "retrieve the list of banned users. Without that list I can't "
            replystr += "even try to unban them."

        elif len(success_list) == 1 and len(fail_list) == 0:
            # Singular success, no fails.
            replystr = f"{ctx.author.mention} Smuddy McSmudface, I mean {ment_success}, "
            replystr += "has been unbanned... for some reason. :shrug:"

        elif len(success_list) > 1 and len(fail_list) == 0:
            # Plural success, no fails.
            replystr = f"{ctx.author.mention} The users known as {ment_success} "
            replystr += "have been unbanned... but why?"

        elif len(success_list) > 0 and len(fail_list) > 0:
            # Mixed results.
            replystr = f"{ctx.author.mention} The unbanning was a success! Partially anyway...\n"
            replystr += f"Unbanned user(s): {ment_success}\n"
            replystr += f"Still banned user(s): {ment_fail}\n"
            replystr += f"Failure was caused by {error_str}."

        elif len(success_list) == 0 and len(fail_list) == 1:
            # No success, singular fail.
            replystr = f"{ctx.author.mention} I wasn't able to unban "
            replystr += f"{ment_fail} due to {error_str}."

        elif len(success_list) == 0 and len(fail_list) > 1:
            # No success, plural fails.
            ment_fail = ment_fail.replace(' and ', ' or ')
            replystr = f"{ctx.author.mention} I wasn't able to unban "
            replystr += f"{ment_fail} due to {error_str}."

        else:
            replystr = f"{ctx.author.mention} Quick someone call <@!154516898434908160>! "
            replystr += "I don't know what's going on!!!\n"

        if not showbans:
            await ctx.send(replystr)
Example #4
0
    async def _ban(self, ctx: Context, *args: str) -> None:
        """Remove a user from our sight - permanently."""
        # This function simply bans a user from the server in which it's issued
        reason = self.extract_reason(" ".join(args))
        forbidden_error = False
        http_error = False
        success_list = list()
        fail_list = list()

        if "list" in args:
            # This is just a shortcut to invoke the listban command.
            banlist = True
        else:
            banlist = False

        if ctx.invoked_with == "ban":
            do_purge = False
        else:
            do_purge = True

        mod_role = discord.utils.get(ctx.guild.roles, name="Administration")
        mods_list = [
            user for user in ctx.message.mentions if mod_role in user.roles
        ]

        for victim in [
                user for user in ctx.message.mentions if user not in mods_list
        ]:
            try:
                if not do_purge:
                    await ctx.guild.ban(victim,
                                        reason=reason,
                                        delete_message_days=0)
                else:
                    await ctx.guild.ban(victim,
                                        reason=reason,
                                        delete_message_days=7)

                success_list.append(victim)

            except discord.Forbidden:
                forbidden_error = True
                fail_list.append(victim)

            except discord.HTTPException:
                http_error = True
                fail_list.append(victim)

        # And now we compile a response.
        ment_success = default.mentions_list(success_list)
        ment_fail = default.mentions_list(fail_list)

        # Error list:
        if forbidden_error and not http_error:
            error_str = 'Insufficient privilegies.'
        elif not forbidden_error and http_error:
            error_str = 'HTTP issues.'
        else:
            error_str = 'A mix of insufficient privilegies and HTTP issues.'

        if banlist:
            # This is just a shortcut to invoke the listban command.
            await ctx.invoke(self.bot.get_command('listban'))

        elif len(ctx.message.mentions) == 0:
            # No mentions
            replystr = "Sure, I'll go right ahead and ban... wait who should I ban? "
            replystr += f"You didn't mention anyone? Freeze in hell {ctx.author.mention}!"

        elif len(ctx.message.mentions) == len(mods_list):
            # No mentions that weren't mods
            replystr = f"{ctx.author.mention} Every single person on that list of yours "
            replystr += "is a mod. This is mutiny!"

        elif (len(success_list) == 1) and (len(fail_list) == 0):
            # Singular success
            replystr = f"{ctx.author.mention} The smuddy little smud {ment_success} won't "
            replystr += "bother us no more, if you know what I mean... :hammer:"

        elif (len(success_list) > 1) and (len(fail_list) == 0):
            # Plural success
            ban_hammer = (":hammer:" * len(success_list))
            replystr = f"{ctx.author.mention} Those smuddy little smuds {ment_success} "
            replystr += f"won't bother us no more. Because they're all BANNED! {ban_hammer}"

        elif (len(success_list) > 0) and (len(fail_list) > 0):
            # Mixed results
            error_str = error_str.lower().replace('.',
                                                  '').replace('http', 'HTTP')
            replystr = f"{ctx.author.mention} My powers are disapating, due to {error_str} "
            replystr += "I wasn't able to ban all of the users requested."
            replystr += f"\nBanned: {ment_success}\nNot banned: {ment_fail}"

        elif (len(success_list) == 0) and (len(fail_list) == 1):
            # Singular fail
            error_str = error_str.lower().replace('.',
                                                  '').replace('http', 'HTTP')
            replystr = f"{ctx.author.mention} The smuddy little smud {ment_fail}... will "
            replystr += "actually keep bothering us. I wasn't able to ban them due "
            replystr += f"to {error_str}."

        elif (len(success_list) == 0) and (len(fail_list) > 1):
            # Plural fail
            ment_fail = ment_fail.replace(' and ', ' or ')
            replystr = f"{ctx.author.mention} I'm deeply ashamed to say that my systems "
            replystr += f"are malfunctioning and I wasn't able to ban {ment_fail}.\n"
            replystr += f"This seems to be due to: {error_str}"

        if not banlist:
            await ctx.send(replystr)
Example #5
0
async def banish(ctx: Context, coginfo: CogInfo,
                 template_engine: TemplateEngine, args: Tuple[str,
                                                              ...]) -> None:
    """Carry out one or more banishes."""
    if coginfo.bot:
        bot: MrFreeze = coginfo.bot
    else:
        raise InsufficientCogInfo()

    # Parse targetted users
    bot_mentioned = bot.user in ctx.message.mentions
    self_mentioned = ctx.author in ctx.message.mentions
    mentions = ctx.message.mentions
    mods = [
        u for u in mentions
        if u.guild_permissions.administrator and u != bot.user
    ]
    users = [
        u for u in mentions
        if not u.guild_permissions.administrator and u != bot.user
    ]

    if bot_mentioned or self_mentioned or mods:
        # Illegal banish, prepare silly response.
        if bot_mentioned and len(mentions) == 1:
            logger.debug("Setting template to MuteResponseType.FREEZE")
            template = MuteResponseType.FREEZE
            fails_list = [bot.user]
        elif bot_mentioned and self_mentioned and len(mentions) == 2:
            logger.debug("Setting template to MuteResponseType.FREEZE_SELF")
            template = MuteResponseType.FREEZE_SELF
            fails_list = [bot.user, ctx.author]
        elif bot_mentioned:
            logger.debug("Setting template to MuteResponseType.FREEZE_OTHERS")
            template = MuteResponseType.FREEZE_OTHERS
            fails_list = mods + users
        elif self_mentioned and len(mentions) == 1:
            logger.debug("Setting template to MuteResponseType.SELF")
            template = MuteResponseType.SELF
            fails_list = mods
        elif mods and len(mentions) == 1:
            logger.debug("Setting template to MuteResponseType.MOD")
            template = MuteResponseType.MOD
            fails_list = mods
        elif mods:
            logger.debug("Setting template to MuteResponseType.MODS")
            template = MuteResponseType.MODS
            fails_list = mods
        else:
            logger.warn("Setting template to MuteResponseType.INVALID")
            logger.warn(
                f"bot_mentioned={bot_mentioned}, self_mentioned={self_mentioned}"
            )
            logger.warn(f"{len(mentions)} mentions={mentions}")
            logger.warn(f"{len(mods)} mods={mods}")
            logger.warn(f"{len(users)} users={users}")
            template = MuteResponseType.INVALID
            fails_list = mentions

        banish_template = template_engine.get_template(ctx.invoked_with,
                                                       template)
        mention_fails = default.mentions_list(fails_list)
        if banish_template:
            msg = banish_template.substitute(author=ctx.author.mention,
                                             fails=mention_fails)
            await ctx.send(msg)

    else:
        # Legal banish, attempt banish.
        msg = await attempt_banish(ctx, coginfo, template_engine, users, args)
        await ctx.send(msg)
Example #6
0
async def attempt_banish(ctx: Context, coginfo: CogInfo,
                         template_engine: TemplateEngine,
                         victims: List[Member], args: Tuple[str, ...]) -> str:
    """Attempt to carry banish some people, then return an appropriate response."""
    if coginfo.bot:
        bot: MrFreeze = coginfo.bot

    success_list: List[Member] = list()
    fails_list: List[Member] = list()
    success_string = ""
    fails_string = ""
    error_string = ""
    http_exception = False
    forbidden_exception = False
    other_exception = False
    duration, end_date = get_unbanish_duration(ctx, template_engine, args)

    for victim in victims:
        error = await mute_db.carry_out_banish(bot, victim, logger, end_date)
        if isinstance(error, Exception):
            fails_list.append(victim)
            if isinstance(error, discord.HTTPException):
                http_exception = True
            elif isinstance(error, discord.Forbidden):
                forbidden_exception = True
            else:
                other_exception = True

        else:
            success_list.append(victim)

        success_string = default.mentions_list(success_list)
        fails_string = default.mentions_list(fails_list)
        error_string = get_error_string(http_exception, forbidden_exception,
                                        other_exception)

    template = get_mute_response_type(success_list, fails_list)
    logger.debug(f"attempt_banish(): Setting template to {template}")

    timestamp_template = template_engine.get_template(
        ctx.invoked_with, MuteResponseType.TIMESTAMP)
    if timestamp_template:
        timestamp = timestamp_template.substitute(
            duration=time.parse_timedelta(duration))
    else:
        logger.warn(
            f"template_engine.get_template({ctx.invoked_with}, TIMESTAMP) returned None!"
        )
        timestamp = ""

    response_template = template_engine.get_template(ctx.invoked_with,
                                                     template)
    if response_template:
        response = response_template.substitute(author=ctx.author.mention,
                                                victims=success_string,
                                                fails=fails_string,
                                                errors=error_string,
                                                timestamp=timestamp)
        return f"{ctx.author.mention} {response}"
    else:
        return f"{ctx.author.mention} Something went wrong, I'm literally at a loss for words."
async def run_command(ctx: Context, coginfo: CogInfo,
                      template_engine: TemplateEngine,
                      error: Exception) -> None:
    """
    Trigger on unauthorized banish, i.e. when a non-administrator try to banish people.

    When _banish() encounters an error this method is automatically triggered. If the error
    is an instance of discord.ext.commands.CheckFailure the user will be punished accordingly,
    if not the error is raised again.

    There are four relevant templates that can be used when sending the response.
    USER_NONE     User invoked mute with no arguments
    USER_SELF     User tried muting themselves
    USER_USER     User tried muting other user(s)
    USER_MIXED    User tried musing themselves and other user(s)
    """
    if coginfo.bot and coginfo.default_self_mute_time and coginfo.logger:
        bot: MrFreeze = coginfo.bot
        default_self_mute_time: int = coginfo.default_self_mute_time
        logger: Logger = coginfo.logger
    else:
        raise InsufficientCogInfo

    if not isinstance(error, CheckFailure):
        # Only run this on Check Failure.
        return

    mentions = ctx.message.mentions
    author = ctx.author
    server = ctx.guild

    none = (len(mentions) == 0)
    selfmute = (len(mentions) == 1 and author in mentions)
    mix = (not selfmute and author in mentions)
    user = (not selfmute and not mix and len(mentions) > 0)
    fails = default.mentions_list(
        [mention for mention in mentions if mention != author])

    if none:
        template = MuteResponseType.USER_NONE
    elif selfmute:
        template = MuteResponseType.USER_SELF
    elif user:
        template = MuteResponseType.USER_USER
    elif mix:
        template = MuteResponseType.USER_MIXED

    self_mute_time: int = bot.get_self_mute_time(
        server) or default_self_mute_time
    duration = datetime.timedelta(minutes=float(self_mute_time))
    end_date = datetime.datetime.now() + duration
    duration = time.parse_timedelta(duration)

    # Carry out the banish with resulting end date
    banish_error = await mute_db.carry_out_banish(bot, author, logger,
                                                  end_date)
    error_msg = "unspecified error"

    if isinstance(banish_error, Exception):
        if isinstance(banish_error, discord.Forbidden):
            error_msg = "**a lack of privilegies**"
        elif isinstance(banish_error, discord.HTTPException):
            error_msg = "**an HTTP exception**"
        else:
            error_msg = "**an unknown error**"
        template = MuteResponseType.USER_FAIL

    banish_template = template_engine.get_template(ctx.invoked_with, template)
    if banish_template:
        reply = banish_template.substitute(author=author.mention,
                                           fails=fails,
                                           errors=error_msg,
                                           timestamp=duration)
        await ctx.send(reply)
    else:
        reply = "I couldn't find an appropriate response, but anyway... you're not "
        reply += f"allowed to do that! Bad {ctx.author.mention}!"
        await ctx.send(reply)
Example #8
0
async def unbanish_loop(server: Guild, coginfo: CogInfo) -> None:
    """Check for people to unbanish every self.banish_interval seconds."""
    if coginfo.bot and coginfo.logger and coginfo.default_mute_interval:
        bot: MrFreeze = coginfo.bot
        logger: Logger = coginfo.logger
        default_mute_interval: int = coginfo.default_mute_interval
    else:
        raise Exception(
            "Failed to create unbanish loop, insufficient cog info.")

    while not bot.is_closed():
        # Wait the time specified by the server (or the default time) before running
        interval = bot.settings.get_mute_interval(
            server) or default_mute_interval
        logger.debug(f"Running unbanish loop for {server.name} in {interval}.")
        await asyncio.sleep(interval)
        logger.debug(f"Running unbanish loop for {server.name}.")

        # Fetch mute role/channel, which might fail.
        try:
            mute_role = await bot.get_mute_role(server)
            mute_channel = await bot.get_mute_channel(server, silent=True)
        except Exception:
            logger.warning(
                f"{server.name} Unbanish loop failed to fetch mute role or channel."
            )
            continue
        unmuted = list()

        # Create a list of mutes which are due for unmuting
        current_time = datetime.datetime.now()
        mutes = mute_db.mdb_fetch(bot, server)
        timed_mutes = [i for i in mutes if i.until is not None]
        due_mutes = [i for i in timed_mutes if i.until < current_time]
        logger.debug(
            f"{server.name} mutes: {len(mutes)}/{len(timed_mutes)}/{len(due_mutes)}"
        )

        for mute in due_mutes:
            logger.debug(f"{mute} is due for unbanish!")

            # Refresh member to make sure we have their latest roles.
            try:
                member = await server.fetch_member(mute.member.id)
                logger.debug(
                    f"Refreshed {member}, they have {len(member.roles)} roles."
                )
            except Exception as e:
                logger.error(f"Failed to refresh muted member: {e}")
                continue  # Will try again next unbanish loop

            # Calculate how late we were in unbanishing
            diff = bot.parse_timedelta(current_time - mute.until)
            if diff == "":
                diff = "now"
            else:
                diff = f"{diff} ago"

            # Remove from database
            mute_db.mdb_del(bot, member, logger)

            if mute_role in member.roles:
                logger.debug(f"{member} has the mute role! Removing it.")
                try:
                    await member.remove_roles(mute_role)
                    logger.debug(
                        f"{member} should no longer have the mute role.")
                    # Members are only considered unmuted if they had the antarctica role
                    unmuted.append(member)

                    log = f"Auto-unmuted {CYAN_B}{member.name}#"
                    log += f"{member.discriminator} @ {server.name}."
                    log += f"{YELLOW} (due {diff}){RESET}"
                    logger.info(log)

                except Exception as e:
                    log = f"Failed to remove mute role of {YELLOW}"
                    log += f"{member.name}#{member.discriminator}"
                    log += f"{CYAN_B} @ {MAGENTA} {server.name}:"
                    log += f"\n{RED}==> {RESET}{e}"
                    logger.error(log)
            else:
                log = f"User {YELLOW}{member.name}#{member.discriminator}"
                log += f"{CYAN_B} @ {MAGENTA} {server.name}{CYAN} "
                log += f"due for unmute but does not have a mute role!{RESET}"
                logger.warning(log)

        # Time for some great regrets
        if len(unmuted) > 0:
            unmuted_str = default.mentions_list(unmuted)

            if len(unmuted_str) == 1:
                msg = "It's with great regret that I must inform you all that "
                msg += f"{unmuted_str}'s exile has come to an end."
            else:
                msg = "It's with great regret that I must inform you all that the exile of "
                msg += f"{unmuted_str} has come to an end."

            await mute_channel.send(msg)