コード例 #1
0
ファイル: moderation.py プロジェクト: Jashooa/Aphid
    async def cases(self, ctx, member: converters.UserConverter):
        """View all mod cases for a member."""

        query = 'SELECT * FROM mod_cases WHERE user_id = $1 ORDER BY issued DESC;'
        records = await self.bot.pool.fetch(query, member.id)

        if len(records) == 0:
            return await ctx.send("None found.")

        entries = []
        for record in records:
            id = record['id']
            action = record['action']
            issued = time.human_timedelta(datetime.datetime.utcnow() -
                                          record['issued'],
                                          largest_only=True)
            duration = record['duration']
            duration = time.human_timedelta(
                duration) if duration is not None else None
            reason = record['reason']

            if duration is None:
                entries.append(
                    f'#{id} • *{action.capitalize()}* • {reason} • {issued} ago'
                )
            else:
                entries.append(
                    f'#{id} • *{action.capitalize()}* ({duration}) • {reason} • {issued} ago'
                )

        p = Pages(ctx, entries=entries)
        p.embed.set_author(name=f'{member} Cases', icon_url=member.avatar_url)
        p.embed.colour = discord.Colour.red()
        await p.paginate()
コード例 #2
0
ファイル: weather.py プロジェクト: iamsix/palbot
    async def sun(self, ctx, *, location: str = None):
        """Show sunrise/sunset for a <location>
           Can be invoked without location if you have done a `set location`"""
        key = self.bot.config.forecast_io_key
        loc = await self.locatamatron(ctx, location)
        if not loc:
            return

        url = "https://api.forecast.io/forecast/{}/{},{}"
        url = url.format(key, loc.latitude, loc.longitude)

        async with self.bot.session.get(url) as resp:
            data = await resp.json()

        tmz = pytz.timezone(data['timezone'])
        now = datetime.fromtimestamp(int(data['currently']['time']), tz=tmz)
        data = data['daily']['data'][0]

        sunriseobj = datetime.fromtimestamp(int(data['sunriseTime']), tz=tmz)
        sunsetobj = datetime.fromtimestamp(int(data['sunsetTime']), tz=tmz)
        sunlength = sunsetobj - sunriseobj

        til = human_timedelta(sunriseobj, source=now, suffix=True)
        sunrise = sunriseobj.strftime("%H:%M")
        sunrise = f"{sunrise} ({til})"

        til = human_timedelta(sunsetobj, source=now, suffix=True)
        sunset = sunsetobj.strftime("%H:%M")
        sunset = f"{sunset} ({til})"

        out = f"{loc.formatted_address} / Sunrise: {sunrise} / Sunset: {sunset} / Day Length: {sunlength}"
        await ctx.send(out)
コード例 #3
0
    async def reminder(
        self,
        ctx,
        *,
        when: time.UserFriendlyTime(commands.clean_content, default="\u2026"),
    ):
        """Reminds you of something after a certain amount of time.

        The input can be any direct date (e.g. YYYY-MM-DD) or a human
        readable offset. Examples:

        - "next thursday at 3pm do something funny"
        - "do the dishes tomorrow"
        - "in 3 days do the thing"
        - "2d unmute someone"

        Times are in UTC.
        """

        timer = await self.create_timer(
            when.dt,
            "reminder",
            ctx.author.id,
            ctx.channel.id,
            when.arg,
            connection=ctx.db,
            created=ctx.message.created_at,
            message_id=ctx.message.id,
        )
        delta = time.human_timedelta(when.dt, source=timer.created_at)
        await ctx.send(
            f"Alright {ctx.author.mention}, in {delta}: {when.arg}",
            allowed_mentions=discord.AllowedMentions(users=True),
        )
コード例 #4
0
 def cmd_stats(self):
     return [
         len(self.bot.guilds),
         sum(g.member_count for g in self.bot.guilds if not g.unavailable),
         self.ping(),
         human_timedelta(self.bot.last_message)
     ]
コード例 #5
0
    async def on_member_join(self, member):
        config = await self.get_mod_config(member.guild.id)
        if config is None:
            return
        if config.get('mute_role') is not None and member.id in (config.get('muted') or ()):
            color = 0xFAA935
            title = 'Muted Member Join'
            descr = 'User was previously muted!'

            try:
                await member.add_roles(discord.Object(id=config.get('mute_role')),
                                       reason='User was previously muted')
            except discord.Forbidden:
                pass
        else:
            color = 0x55dd55
            title = 'New Member Join'
            descr = discord.Embed.Empty

        if member.guild.get_channel(config.get('join_ch')) is not None:
            join_channel = member.guild.get_channel(config.get('join_ch'))
            e = discord.Embed(title=title,
                              color=color,
                              timestamp=datetime.utcnow(),
                              description=descr)
            e.set_author(icon_url=member.avatar_url, name=member)
            e.add_field(name='ID', value=member.id)
            e.add_field(name='Created', value=human_timedelta(member.created_at))

            await join_channel.send(embed=e)
コード例 #6
0
    async def selfmute(self, ctx, *, duration: ShortTime):
        """Temporarily mutes yourself for the specified duration.

        The duration must be in a short time form, e.g. 4h
        """
        query = '''SELECT mute_role
                   FROM guild_mod_config
                   WHERE id = $1'''
        config = await self.bot.pool.fetchrow(query, ctx.guild.id)

        if config is None or ctx.guild.get_role(config.get('mute_role')) is None:
            return await ctx.send(f'Unable to find mute role!\n'
                                  f'Please use `{ctx.prefix}createmute` to create the role with the appropriate permissions\n'
                                  f'or use `{ctx.prefix}config role mute` to set an existing role as your mute role')
        role = ctx.guild.get_role(config.get('mute_role'))
        if ctx.me.top_role < role:
            return await ctx.send(f'Unable to mute, please move my role above thwile Muted role')

        timer = self.bot.get_cog('Reminders')
        if timer is None:
            return await ctx.send('Sorry this command is not available at the moment')

        time = human_timedelta(duration.dt)
        if not await confirm_prompt(ctx, f'Are you sure you want to self-mute for **{time}**?'):
            return
        reason = f'Self-mute for {time}'
        await ctx.author.add_roles(role, reason=reason)
        await timer.create_timer(duration.dt, ctx.author, ctx.guild.id, ctx.author.id, reason, 'tempmute')
        await ctx.send(f'Ok, you are muted for {time}')
コード例 #7
0
ファイル: reminder.py プロジェクト: realSnosh/Vale.py
    async def on_reminder_complete(self, timer):
        user_id, channel_id, message = timer.args
        human_delta = human_timedelta(timer.created)

        if channel_id is None:
            user = self.bot.get_user(user_id)
            try:
                channel = await user.create_dm()
            except Exception:  # user was either gone or deleted
                return
        else:
            channel = self.bot.get_channel(channel_id)
            if channel is None:
                return

        is_private = isinstance(channel, discord.abc.PrivateChannel)
        destination_format = ('Direct Message' if is_private else
                              f'#{channel} in {channel.guild}!')

        embed = (discord.Embed(description=message,
                               colour=0x00ff00,
                               timestamp=timer.utc).set_author(
                                   name=f'Reminder for {destination_format}',
                                   icon_url=ALARM_CLOCK_URL).set_footer(
                                       text=f'From {human_delta}.'))

        try:
            await channel.send(f"<@{user_id}>", embed=embed)
        except discord.HTTPException:  # can't embed
            await channel.send(
                f'<@{user_id}> {human_delta} ago, you wanted to be reminded of **{message}**.'
            )
コード例 #8
0
ファイル: Reminders.py プロジェクト: NotSoPrivate/NotSoBot
 async def remind_list(self, ctx):
     sql = 'SELECT * FROM `reminders` WHERE user={0}'.format(ctx.author.id)
     q = await self.cursor.execute(sql)
     result = await q.fetchall()
     if not result:
         return await ctx.send(
             "\N{NO ENTRY} You don't have any reminders set!")
     try:
         entries = [
          f"**__{human_timedelta(datetime.fromtimestamp(x['time']))}__**" \
          f"\n{x['message'] or ''}" \
          for x in result
         ]
         p = Pages(ctx, entries=entries, per_page=8, show_zero=False)
         p.embed.title = 'Reminders'
         p.embed.color = self.bot.funcs.get_color()()
         p.embed.set_author(name=ctx.author.display_name,
                            icon_url=ctx.author.avatar_url_as())
         await p.paginate()
     except CannotPaginate:
         reminders = []
         count = 0
         for reminder in result:
             created_at = human_timedelta(
                 datetime.fromtimestamp(reminder['time']))
             if reminder['message'] is not None:
                 reminders.append('**{0}.** __{2}__: `{1}`'.format(
                     count, reminder['message'], created_at))
             else:
                 reminders.append('**{0}.** __{1}__'.format(
                     count, created_at))
             count += 1
         await self.truncate(
             ctx.channel, '**Reminders**\n{0}'.format('\n'.join(reminders)))
コード例 #9
0
 def parse_tweet(self, tweet):
     print(tweet)
     updated = datetime.strptime(tweet['created_at'],
                                 "%a %b %d %H:%M:%S +0000 %Y")
     ago = human_timedelta(updated, brief=True)
     author = tweet['user']['screen_name']
     text = html.unescape(tweet['full_text'].strip())
     return {'author': author, 'text': text, "ago": ago, "updated": updated}
コード例 #10
0
    def format_commit(commit):
        message, _, _ = commit.message.partition("\n")
        commit_tz = datetime.timezone(datetime.timedelta(minutes=commit.commit_time_offset))
        commit_time = datetime.datetime.fromtimestamp(commit.commit_time).astimezone(commit_tz)

        offset = human_timedelta(
            commit_time.astimezone(datetime.timezone.utc).replace(tzinfo=None),
            accuracy=1,
        )
        return f"[`{commit.hex[:6]}`](https://github.com/davidetacchini/overbot/commit/{commit.hex}) {message} ({offset})"
コード例 #11
0
    async def on_member_remove(self, member):
        config = await self.get_mod_config(member.guild.id)
        if config is None:
            return

        if member.guild.get_channel(config.get('leave_ch')) is not None:
            join_channel = member.guild.get_channel(config.get('leave_ch'))
            e = discord.Embed(title='Member leave',
                              color=0xff0000,
                              timestamp=datetime.utcnow())
            e.set_author(icon_url=member.avatar_url, name=member)
            e.add_field(name='ID', value=member.id)
            e.add_field(name='Created', value=human_timedelta(member.created_at))
            e.add_field(name='Last Joined', value=human_timedelta(member.joined_at))
            if self.bot.get_cog('Info') is not None:
                first_join = await self.bot.get_cog('Info').get_join_date(member)
                e.add_field(name='First Joined', value=human_timedelta(first_join))

            await join_channel.send(embed=e)
コード例 #12
0
ファイル: moderation.py プロジェクト: Jashooa/Aphid
    async def log_action(self,
                         guild: discord.Guild,
                         action: str,
                         member: discord.Member,
                         moderator: discord.Member,
                         issued: datetime.datetime,
                         *,
                         duration: time.ShortTime = None,
                         reason: str = None):
        query = 'INSERT INTO mod_cases (action, user_id, mod_id, issued, duration, reason) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id;'

        duration = duration.delta if duration is not None else duration

        record = await self.bot.pool.fetchrow(query, action, member.id,
                                              moderator.id, issued, duration,
                                              reason)

        embed = discord.Embed()

        if action == 'warn':
            embed.colour = discord.Colour.gold()
        elif action == 'mute' or action == 'tempmute':
            embed.colour = discord.Colour.orange()
        elif action == 'kick':
            embed.colour = discord.Colour.dark_orange()
        elif action == 'ban' or action == 'tempban':
            embed.colour = discord.Colour.red()
        elif action == 'unmute' or action == 'unban':
            embed.colour = discord.Colour.blue()

        embed.set_author(name=action.capitalize(), icon_url=member.avatar_url)
        embed.description = f'{member.mention} {member}'

        embed.add_field(name='Moderator',
                        value=f'{moderator.mention} {moderator}',
                        inline=False)

        if duration is not None:
            embed.add_field(name='Duration',
                            value=time.human_timedelta(duration),
                            inline=False)

        embed.add_field(name='Reason',
                        value=formatting.truncate(reason, 512)
                        if reason is not None else 'None')

        embed.set_footer(text=f'Case #{record["id"]} • ID: {member.id}')
        embed.timestamp = issued

        channel = discord.utils.get(guild.channels, name=self.log_channel)
        await channel.send(embed=embed)
コード例 #13
0
    def format_commit(self, commit):
        short, _, _ = commit.message.partition("\n")
        short_sha2 = commit.hex[0:6]
        commit_tz = datetime.timezone(
            datetime.timedelta(minutes=commit.commit_time_offset))
        commit_time = datetime.datetime.fromtimestamp(
            commit.commit_time).replace(tzinfo=commit_tz)

        # [`hash`](url) message (offset)
        offset = time.human_timedelta(
            commit_time.astimezone(datetime.timezone.utc).replace(tzinfo=None),
            accuracy=1,
        )
        return f"[`{short_sha2}`](https://github.com/AbstractUmbra/Akane/commit/{commit.hex}) {short} ({offset})"
コード例 #14
0
    async def on_reminder_complete(self, timer):
        user = self.bot.get_user(timer.get('user'))
        if user is None:
            # rip
            return

        cid = timer.get('channel')
        message_id = timer.get('message')
        message = timer.get('content')
        channel = self.bot.get_channel(cid)

        if cid is not None:
            if channel is not None:
                guild_id = channel.guild.id
                channel_id = channel.id

                member = channel.guild.get_member(user.id)
                if not member or not channel.permissions_for(
                        member).read_messages:
                    channel = user
                    message_id = None  # No jump url if user can't access channel
                elif member and not channel.permissions_for(
                        channel.guild.me).send_messages:
                    channel = user
            else:
                channel = user
                message_id = None

        else:
            channel = user
            guild_id = '@me'
            channel_id = (await user.create_dm()).id

        delta = human_timedelta(timer.get("start"))
        msg = f'{user.mention} {delta}: {message}'
        if message_id:
            url = f'https://discordapp.com/channels/{guild_id}/{channel_id}/{message_id}'
            view = discord.ui.View()
            view.add_item(
                discord.ui.Button(label='Go to original message', url=url))
        else:
            view = discord.utils.MISSING
        try:
            await channel.send(msg, view=view)
        except discord.HTTPException:
            pass
コード例 #15
0
ファイル: ServerInfo.py プロジェクト: baulml/botcogs
 async def serverinfo(self, ctx):
     guild = ctx.guild
     text_channels = len(guild.text_channels)
     voice_channels = len(guild.voice_channels)
     roles = ['@everyone']
     roles.extend([role.mention for role in guild.roles[1:]])
     roles = ", ".join(roles)
     e = discord.Embed(title='Server Info', color=bright_color())
     e.set_author(icon_url=guild.icon_url, name=guild.name)
     e.add_field(name='ID', value=guild.id)
     e.add_field(name='Owner', value=guild.owner)
     e.add_field(name='Region', value=guild.region)
     e.add_field(name='Members', value=guild.member_count)
     e.add_field(name='Channels',
                 value=f'{text_channels} Text | {voice_channels} Voice')
     e.add_field(name='Created', value=human_timedelta(guild.created_at))
     e.add_field(name='Roles', value=roles)
     await ctx.send(embed=e)
コード例 #16
0
    async def on_reminder_complete(self, timer):
        user = self.bot.get_user(timer.get('user'))
        if user is None:
            # rip
            return

        cid = timer.get('channel')
        message_id = timer.get('message')
        message = timer.get('content')
        channel = self.bot.get_channel(cid)

        if cid is not None:
            if channel is not None:
                guild_id = channel.guild.id
                channel_id = channel.id

                member = channel.guild.get_member(user.id)
                if not member or not channel.permissions_for(
                        member).read_messages:
                    channel = user
                    message_id = None  # No jump url if user can't access channel
                elif member and not channel.permissions_for(
                        channel.guild.me).send_messages:
                    channel = user
            else:
                channel = user
                message_id = None

        else:
            channel = user
            guild_id = '@me'
            channel_id = (await user.create_dm()).id

        delta = human_timedelta(timer.get("start"))
        if message_id:
            url = f'<https://discordapp.com/channels/{guild_id}/{channel_id}/{message_id}>'
            msg = f'{user.mention} {delta}: {message}\n\n{url}'
        else:
            msg = f'{user.mention} {delta}: {message}'

        try:
            await channel.send(msg)
        except discord.HTTPException:
            pass
コード例 #17
0
ファイル: reminder.py プロジェクト: firmannst123/iceteabot
    async def reminder(self, ctx, *,
                       when: time.UserFriendlyTime(commands.clean_content,
                                                   default='something')):
        """Reminds you of something after a certain amount of time.
        The input can be any direct date (e.g. YYYY-MM-DD) or a human
        readable offset. Examples:
        - "next thursday at 3pm do something funny"
        - "do the dishes tomorrow"
        - "in 3 days do the thing"
        - "2d unmute someone"
        Times are in UTC.
        """

        data = await self.create_reminder(ctx, when, "reminder_complete")
        if data:
            delta = time.human_timedelta(when.dt,
                                         source=ctx.message.created_at)
            await ctx.send(
                f"Alright {ctx.author.mention}, I'll remind you about {when.arg} in {delta}."
            )
コード例 #18
0
ファイル: Raids.py プロジェクト: NotSoPrivate/NotSoBot
	async def on_member_join(self, member):
		check = await self.has_raid(member.guild.id)
		if not check:
			return
		now = datetime.datetime.utcnow()
		created = (now - member.created_at).total_seconds() // 60
		was_kicked = False
		if check['mode'] == 2:
			was_kicked = self.punished.get(member.guild.id)
			if was_kicked is not None:
				try:
					was_kicked.remove(member.id)
				except KeyError:
					pass
				else:
					was_kicked = True
		if was_kicked:
			title = 'Member Re-Joined'
			colour = 0xdd5f53 # red
		else:
			title = 'Member Joined'
			colour = 0x53dda4 # green
			if created < 30:
				colour = 0xdda453 # yellow
		e = discord.Embed(title=title, colour=colour)
		e.timestamp = now
		e.set_footer(text='Created')
		e.set_author(name=str(member), icon_url=member.avatar_url or member.default_avatar_url)
		e.add_field(name='ID', value=member.id)
		e.add_field(name='Joined', value=member.joined_at)
		e.add_field(name='Created', value=human_timedelta(member.created_at))
		try:
			channel = member.guild.get_channel(check['channel'])
			assert channel
			await channel.send(embed=e)
		except (AssertionError, discord.Forbidden, discord.NotFound):
			await self.remove_raid(member.guild.id)
コード例 #19
0
ファイル: mod.py プロジェクト: IAmTomahawkx/xlydn
    async def action(self, ctx, strikes: int, *, action: ActionConverter):
        arg = None
        if action in (1, 4):
            resp = await ctx.ask(
                self.system.locale(
                    "Please respond with the duration this should last for"))
            arg = await timeutil.UserFriendlyTime(default="e",
                                                  now=True).convert(ctx, resp)
            self.value._value['temp_lengths'][str(strikes)] = (
                arg.dt - datetime.datetime.utcnow()).total_seconds()

        self.value._value['levels'][str(strikes)] = action
        self.value.save()
        r = self.system.locale(
            "`{0}` strikes will now be punished with a {1}").format(
                strikes, self.levels[action])
        if arg is not None:
            t = timeutil.human_timedelta(
                self.system.locale,
                arg.dt,
            )
            r += self.system.locale(", lasting ") + t

        await ctx.send(r)
コード例 #20
0
 def human_delta(self):
     return time.human_timedelta(self.created_at)
コード例 #21
0
    async def info(self, ctx, *, user: Union[discord.User, str] = None):
        """
        gives you advent of code info on the given person. if no target is given, will give you your own info
        """
        if isinstance(user, str):
            user = user.strip()
            if not user:
                user = ctx.author

        user = user or ctx.author
        if isinstance(user, discord.user.BaseUser):
            query = """
            SELECT * FROM users
            INNER JOIN guilds g on g.guild_id = $2
            INNER JOIN cookies c on c.guild_id = g.guild_id
            WHERE discord_id = $1
            """
            # do this in two queries due to variable row amounts
            data = await self.bot.db.fetchrow(query, user.id, ctx.guild.id)
            if not data:
                return await ctx.send(
                    f"Hmm, either {'you havent identified yourself' if user == ctx.author else f'{user.name} hasnt identified themselves'} "
                    f"(use the {ctx.prefix}iam command), the server owner has not set up a leaderboard (use the {ctx.prefix}leaderboard set command), "
                    f"or something screwed up internally. Probably the latter")

            board = await self.bot.get_board(data['board_id'], data['cookie'])
            langs = await self.bot.db.fetch(
                "SELECT * FROM langs WHERE id = $1", user.id)
            member: _board.Member = discord.utils.get(board.members,
                                                      id=data['aoc_id'])

        else:
            board_id = await self.bot.db.fetchrow(
                "SELECT board_id, cookie FROM guilds inner join cookies c on c.guild_id = guilds.guild_id where guilds.guild_id = $1",
                ctx.guild.id)
            if not board_id:
                return await ctx.send(
                    "Please ask someone with the manage server permission to set a board id"
                )

            board = await self.bot.get_board(board_id['board_id'],
                                             board_id['cookie'])
            member = discord.utils.get(board.members, name=user)
            if not member:
                return await ctx.send(
                    "That user doesnt appear to be in your server's leaderboard. Passing leaderboard names is case-sensitive"
                )
            langs = []

        rows = []

        for day in range(1, tz.get().day + 1):
            day_langs = [x['lang'] for x in langs if x['day'] == day]
            stars = member.completion_stats.get(day)
            if not stars:
                star_1 = "Incomplete"
                star_2 = "Incomplete"
            else:
                start = tz.day_start(day)
                star_1 = time.human_timedelta(
                    stars[1], source=start) if stars[1] else "Incomplete"
                star_2 = time.human_timedelta(
                    stars[2], source=start) if stars[2] else "Incomplete"

            rows.append((day, star_1, star_2, ", ".join(day_langs)))

        boards = []
        index = 0
        while index < len(rows):
            boards.append(
                tabulate.tabulate(rows[index:index + 5],
                                  headers=("Day #", "First star",
                                           "Second star", "Languages used"),
                                  tablefmt="simple",
                                  stralign="center").strip())
            index += 4

        menu = ext_menus.MenuPages(menus.InfoDataSource(boards),
                                   clear_reactions_after=True)
        await menu.start(ctx)
コード例 #22
0
 def human_delta(self):
     return time.human_timedelta(self.time)
コード例 #23
0
 def get_uptime(self, *, brief=False):
     return human_timedelta(self.uptime,
                            accuracy=None,
                            brief=brief,
                            suffix=False)
コード例 #24
0
ファイル: moderation.py プロジェクト: Jashooa/Aphid
        elif action == 'ban' or action == 'tempban':
            embed.colour = discord.Colour.red()
        elif action == 'unmute' or action == 'unban':
            embed.colour = discord.Colour.blue()

        embed.set_author(name=f'{action.capitalize()} • Update',
                         icon_url=member.avatar_url)
        embed.description = f'{member.mention} {member}'

        embed.add_field(name='Moderator',
                        value=f'{moderator.mention} {moderator}',
                        inline=False)

        if duration is not None:
            embed.add_field(name='Duration',
                            value=time.human_timedelta(duration),
                            inline=False)

        embed.add_field(name='Reason',
                        value=formatting.truncate(reason, 512)
                        if reason is not None else 'None')

        embed.set_footer(text=f'Case #{record["id"]} • ID: {member.id}')
        embed.timestamp = issued

        channel = discord.utils.get(ctx.guild.channels, name=self.log_channel)
        await channel.send(embed=embed)

    @case.command(name='pardon', description='Pardon a mod case')
    # @commands.bot_has_permissions(ban_members=True, manage_roles=True, embed_links=True)
    @commands.has_any_role('Queen', 'Inquiline')
コード例 #25
0
 async def uptime(self, ctx):
     ago = human_timedelta(self.bot.uptime)
     await ctx.send(f"Startup at {self.bot.uptime} : {ago}")
コード例 #26
0
ファイル: moderation.py プロジェクト: Jashooa/Aphid
class Moderation:
    """Server moderation commands."""
    def __init__(self, bot):
        self.bot = bot
        self.log_channel = 'mod-logs'
        self.mute_role = 'Muted'
        self.protected_roles = ['Queen', 'Inquiline', 'Alate']

    async def on_member_join(self, member):
        if member.guild is None:
            return

        if member.guild.id != self.bot.guild_id:
            return

        query = 'SELECT * FROM mod_tempactions WHERE user_id = $1;'
        record = await self.bot.pool.fetchrow(query, member.id)

        if record is not None:
            role = discord.utils.get(member.guild.roles, name=self.mute_role)
            await member.add_roles(role, reason='Tempmute Reapplication')

    async def log_action(self,
                         guild: discord.Guild,
                         action: str,
                         member: discord.Member,
                         moderator: discord.Member,
                         issued: datetime.datetime,
                         *,
                         duration: time.ShortTime = None,
                         reason: str = None):
        query = 'INSERT INTO mod_cases (action, user_id, mod_id, issued, duration, reason) VALUES ($1, $2, $3, $4, $5, $6) RETURNING id;'

        duration = duration.delta if duration is not None else duration

        record = await self.bot.pool.fetchrow(query, action, member.id,
                                              moderator.id, issued, duration,
                                              reason)

        embed = discord.Embed()

        if action == 'warn':
            embed.colour = discord.Colour.gold()
        elif action == 'mute' or action == 'tempmute':
            embed.colour = discord.Colour.orange()
        elif action == 'kick':
            embed.colour = discord.Colour.dark_orange()
        elif action == 'ban' or action == 'tempban':
            embed.colour = discord.Colour.red()
        elif action == 'unmute' or action == 'unban':
            embed.colour = discord.Colour.blue()

        embed.set_author(name=action.capitalize(), icon_url=member.avatar_url)
        embed.description = f'{member.mention} {member}'

        embed.add_field(name='Moderator',
                        value=f'{moderator.mention} {moderator}',
                        inline=False)

        if duration is not None:
            embed.add_field(name='Duration',
                            value=time.human_timedelta(duration),
                            inline=False)

        embed.add_field(name='Reason',
                        value=formatting.truncate(reason, 512)
                        if reason is not None else 'None')

        embed.set_footer(text=f'Case #{record["id"]} • ID: {member.id}')
        embed.timestamp = issued

        channel = discord.utils.get(guild.channels, name=self.log_channel)
        await channel.send(embed=embed)

    async def temp_action(self, action: str, member: discord.Member,
                          duration: time.ShortTime):
        timers = self.bot.get_cog('Timers')
        if timers is None:
            return

        query = 'SELECT * FROM mod_tempactions WHERE user_id = $1 AND action = $2'
        record = await self.bot.pool.fetchrow(query, member.id, action)

        if record is None:
            timer = await timers.create_timer(action, duration.datetime)

            query = 'INSERT INTO mod_tempactions (user_id, action, timer_id) VALUES ($1, $2, $3);'
            await self.bot.pool.execute(query, member.id, action, timer.id)
        else:
            await timers.update_timer(record['timer_id'], duration.datetime)

    async def on_tempmute_timer_complete(self, timer):
        query = 'SELECT * FROM mod_tempactions WHERE timer_id = ($1);'
        record = await self.bot.pool.fetchrow(query, timer.id)

        timers = self.bot.get_cog('Timers')
        if timers is None:
            return
        await timers.remove_timer(timer.id)

        if record is not None:
            guild = self.bot.get_guild(self.bot.guild_id)
            if guild is None:
                return

            member = guild.get_member(record['user_id'])
            if member is None:
                return

            reason = 'Tempmute Expiration'
            await self.log_action(guild,
                                  'unmute',
                                  member,
                                  self.bot.user,
                                  datetime.datetime.utcnow(),
                                  reason=reason)

            try:
                await member.send(
                    f'You have been unmuted in **{guild.name}**.\n**Reason:** {reason}'
                )
            except discord.errors.Forbidden:
                pass

            role = discord.utils.get(guild.roles, name=self.mute_role)
            await member.remove_roles(role, reason=reason)

    async def on_tempban_timer_complete(self, timer):
        query = 'SELECT * FROM mod_tempactions WHERE timer_id = ($1);'
        record = await self.bot.pool.fetchrow(query, timer.id)

        timers = self.bot.get_cog('Timers')
        if timers is None:
            return
        await timers.remove_timer(timer.id)

        if record is not None:
            guild = self.bot.get_guild(self.bot.guild_id)
            if guild is None:
                return

            user = await self.bot.get_user_info(record['user_id'])
            if user is None:
                return

            reason = 'Tempban Expiration'
            await self.log_action(guild,
                                  'unban',
                                  user,
                                  self.bot.user,
                                  datetime.datetime.utcnow(),
                                  reason=reason)

            await guild.unban(user, reason=reason)

    @commands.command()
    @commands.has_any_role('Queen', 'Inquiline', 'Alate')
    @commands.guild_only()
    async def warn(self, ctx, member: discord.Member, *, reason: str = None):
        """Warn a member."""

        try:
            await ctx.message.delete()
        except discord.errors.NotFound:
            pass

        if checks.has_any_role(member, *self.protected_roles):
            return await ctx.send(f'That member has a protected role.')

        if await self.bot.is_owner(member):
            return

        await self.log_action(ctx.guild,
                              'warn',
                              member,
                              ctx.author,
                              datetime.datetime.utcnow(),
                              reason=reason)

        try:
            await member.send(
                f'You have been warned in **{ctx.guild.name}**.\n**Reason:** {reason}'
            )
        except discord.errors.Forbidden:
            pass

        await ctx.send(
            f'{member.mention} has been warned.\n**Reason:** {reason}')

    @commands.command()
    @commands.bot_has_permissions(manage_roles=True)
    @commands.has_any_role('Queen', 'Inquiline', 'Alate')
    @commands.guild_only()
    async def tempmute(self,
                       ctx,
                       member: discord.Member,
                       duration: time.ShortTime,
                       *,
                       reason: str = None):
        """Temporarily mute a member."""

        try:
            await ctx.message.delete()
        except discord.errors.NotFound:
            pass

        if checks.has_any_role(member, *self.protected_roles):
            return await ctx.send(f'That member has a protected role.')

        if await self.bot.is_owner(member):
            return

        await self.temp_action('tempmute', member, duration)

        await self.log_action(ctx.guild,
                              'tempmute',
                              member,
                              ctx.author,
                              datetime.datetime.utcnow(),
                              duration=duration,
                              reason=reason)

        try:
            await member.send(
                f'You have been temporarily muted in **{ctx.guild.name}** for {time.human_timedelta(duration.delta)}.\n**Reason:** {reason}'
            )
        except discord.errors.Forbidden:
            pass

        role = discord.utils.get(ctx.guild.roles, name=self.mute_role)
        await member.add_roles(role, reason=reason)

        await ctx.send(
            f'{member.mention} has been temporarily muted for {time.human_timedelta(duration.delta)}.\n**Reason:** {reason}'
        )

    @commands.command()
    @commands.bot_has_permissions(manage_roles=True)
    @commands.has_any_role('Queen', 'Inquiline', 'Alate')
    @commands.guild_only()
    async def mute(self, ctx, member: discord.Member, *, reason: str = None):
        """Mute a member."""

        try:
            await ctx.message.delete()
        except discord.errors.NotFound:
            pass

        if checks.has_any_role(member, *self.protected_roles):
            return await ctx.send(f'That member has a protected role.')

        if await self.bot.is_owner(member):
            return

        query = 'SELECT timer_id FROM mod_tempactions WHERE user_id = $1 AND action = $2;'
        record = await self.bot.pool.fetchrow(query, member.id, 'tempmute')

        if record is not None:
            timer_id = record['timer_id']

            timers = self.bot.get_cog('Timers')
            if timers is None:
                return await ctx.send('Timers module is not loaded.')
            else:
                await timers.remove_timer(timer_id)

        await self.log_action(ctx.guild,
                              'mute',
                              member,
                              ctx.author,
                              datetime.datetime.utcnow(),
                              reason=reason)

        try:
            await member.send(
                f'You have been muted in **{ctx.guild.name}**.\n**Reason:** {reason}'
            )
        except discord.errors.Forbidden:
            pass

        role = discord.utils.get(ctx.guild.roles, name=self.mute_role)
        await member.add_roles(role, reason=reason)

        await ctx.send(
            f'{member.mention} has been muted.\n**Reason:** {reason}')

    @commands.command()
    @commands.bot_has_permissions(manage_roles=True)
    @commands.has_any_role('Queen', 'Inquiline', 'Alate')
    @commands.guild_only()
    async def unmute(self, ctx, member: discord.Member, *, reason: str = None):
        """Unmute a member."""

        try:
            await ctx.message.delete()
        except discord.errors.NotFound:
            pass

        if checks.has_any_role(member, *self.protected_roles):
            return await ctx.send(f'That member has a protected role.')

        if await self.bot.is_owner(member):
            return

        query = 'SELECT timer_id FROM mod_tempactions WHERE user_id = $1 AND action = $2;'
        record = await self.bot.pool.fetchrow(query, member.id, 'tempmute')

        if record is not None:
            timer_id = record['timer_id']

            timers = self.bot.get_cog('Timers')
            if timers is None:
                return await ctx.send('Timers module is not loaded.')
            else:
                await timers.remove_timer(timer_id)

        await self.log_action(ctx.guild,
                              'unmute',
                              member,
                              ctx.author,
                              datetime.datetime.utcnow(),
                              reason=reason)

        try:
            await member.send(
                f'You have been unmuted in **{ctx.guild.name}**.\n**Reason:** {reason}'
            )
        except discord.errors.Forbidden:
            pass

        role = discord.utils.get(ctx.guild.roles, name=self.mute_role)
        await member.remove_roles(role, reason=reason)

        await ctx.send(
            f'{member.mention} has been unmuted.\n**Reason:** {reason}')

    @commands.command(description='Kick a user')
    @commands.bot_has_permissions(kick_members=True)
    @commands.has_any_role('Queen', 'Inquiline')
    @commands.guild_only()
    async def kick(self, ctx, member: discord.Member, *, reason: str = None):
        """Kick a member."""

        try:
            await ctx.message.delete()
        except discord.errors.NotFound:
            pass

        if checks.has_any_role(member, *self.protected_roles):
            return await ctx.send(f'That member has a protected role.')

        if await self.bot.is_owner(member):
            return

        await self.log_action(ctx.guild,
                              'kick',
                              member,
                              ctx.author,
                              datetime.datetime.utcnow(),
                              reason=reason)

        try:
            await member.send(
                f'You have been kicked from **{ctx.guild.name}**.\n**Reason:** {reason}'
            )
        except discord.errors.Forbidden:
            pass

        await ctx.send(
            f'{member.mention} has been kicked.\n**Reason:** {reason}')

        await member.kick(reason=reason)

    @commands.command(description='Temporarily ban a user')
    @commands.bot_has_permissions(kick_members=True)
    @commands.has_any_role('Queen', 'Inquiline')
    @commands.guild_only()
    async def tempban(self,
                      ctx,
                      member: converters.UserConverter,
                      duration: time.ShortTime,
                      *,
                      reason: str = None):
        """Temporarily ban a member."""

        try:
            await ctx.message.delete()
        except discord.errors.NotFound:
            pass

        guild_member = ctx.guild.get_member(member.id)

        if guild_member is not None and checks.has_any_role(
                guild_member, *self.protected_roles):
            return await ctx.send(f'That member has a protected role.')

        if await self.bot.is_owner(member):
            return

        await self.temp_action('tempban', member, duration)

        await self.log_action(ctx.guild,
                              'tempban',
                              member,
                              ctx.author,
                              datetime.datetime.utcnow(),
                              duration=duration,
                              reason=reason)

        if guild_member is not None:
            try:
                await member.send(
                    f'You have been temporarily banned from **{ctx.guild.name}** for {time.human_timedelta(duration.delta)}.\n**Reason:** {reason}'
                )
            except discord.errors.Forbidden:
                pass

        await ctx.send(
            f'{member.mention} has been temporarily banned for {time.human_timedelta(duration.delta)}.\n**Reason:** {reason}'
        )

        await ctx.guild.ban(member, reason=reason, delete_message_days=0)

    @commands.command(description='Ban a user')
    @commands.bot_has_permissions(ban_members=True)
    @commands.has_any_role('Queen', 'Inquiline')
    @commands.guild_only()
    async def ban(self,
                  ctx,
                  member: converters.UserConverter,
                  *,
                  reason: str = None):
        """Ban a member."""

        try:
            await ctx.message.delete()
        except discord.errors.NotFound:
            pass

        guild_member = ctx.guild.get_member(member.id)

        if guild_member is not None and checks.has_any_role(
                guild_member, *self.protected_roles):
            return await ctx.send(f'That member has a protected role.')

        if await self.bot.is_owner(member):
            return

        query = 'SELECT timer_id FROM mod_tempactions WHERE user_id = $1 AND action = $2;'
        record = await self.bot.pool.fetchrow(query, member.id, 'tempban')

        if record is not None:
            timer_id = record['timer_id']

            timers = self.bot.get_cog('Timers')
            if timers is None:
                return await ctx.send('Timers module is not loaded.')
            else:
                await timers.remove_timer(timer_id)

        await self.log_action(ctx.guild,
                              'ban',
                              member,
                              ctx.author,
                              datetime.datetime.utcnow(),
                              reason=reason)

        if guild_member is not None:
            try:
                await member.send(
                    f'You have been banned from **{ctx.guild.name}**.\n**Reason:** {reason}'
                )
            except discord.errors.Forbidden:
                pass

        await ctx.send(
            f'{member.mention} has been banned.\n**Reason:** {reason}')

        await ctx.guild.ban(member, reason=reason, delete_message_days=0)

    @commands.command(description='Unban a user')
    @commands.bot_has_permissions(ban_members=True)
    @commands.has_any_role('Queen', 'Inquiline')
    @commands.guild_only()
    async def unban(self,
                    ctx,
                    member: converters.UserConverter,
                    *,
                    reason: str = None):
        """Unban a member."""

        try:
            await ctx.message.delete()
        except discord.errors.NotFound:
            pass

        guild_member = ctx.guild.get_member(member.id)

        if guild_member is not None and checks.has_any_role(
                guild_member, *self.protected_roles):
            return await ctx.send(f'That member has a protected role.')

        if await self.bot.is_owner(member):
            return

        if await ctx.guild.get_ban(member) is None:
            return await ctx.send(f'That member is not banned.')

        query = "SELECT timer_id FROM mod_tempactions WHERE user_id = $1 AND action = $2;"
        record = await self.bot.pool.fetchrow(query, member.id, 'tempban')

        if record is not None:
            timer_id = record['timer_id']

            timers = self.bot.get_cog('Timers')
            if timers is None:
                return await ctx.send('Timers module is not loaded.')
            else:
                await timers.remove_timer(timer_id)

        await self.log_action(ctx.guild,
                              'unban',
                              member,
                              ctx.author,
                              datetime.datetime.utcnow(),
                              reason=reason)

        await ctx.guild.unban(member, reason=reason)

    @commands.group(name='case',
                    description='View a mod case',
                    invoke_without_command=True)
    @commands.bot_has_permissions(embed_links=True)
    @commands.has_any_role('Queen', 'Inquiline', 'Alate')
    @commands.guild_only()
    async def case(self, ctx, case: int):
        """View a mod case."""

        query = 'SELECT * FROM mod_cases WHERE id = $1;'
        record = await self.bot.pool.fetchrow(query, case)

        if record is None:
            return await ctx.send(f'Case #{case} not found.')

        action = record['action']
        user_id = record['user_id']
        mod_id = record['mod_id']
        issued = record['issued']
        duration = record['duration']
        reason = record['reason']

        member = ctx.guild.get_member(user_id) or await self.bot.get_user_info(
            user_id)

        moderator = ctx.guild.get_member(mod_id)

        embed = discord.Embed()

        if action == 'warn':
            embed.colour = discord.Colour.gold()
        elif action == 'mute' or action == 'tempmute':
            embed.colour = discord.Colour.orange()
        elif action == 'kick':
            embed.colour = discord.Colour.dark_orange()
        elif action == 'ban' or action == 'tempban':
            embed.colour = discord.Colour.red()
        elif action == 'unmute' or action == 'unban':
            embed.colour = discord.Colour.blue()

        embed.set_author(name=f'{action.capitalize()}',
                         icon_url=member.avatar_url)
        embed.description = f'{member.mention} {member}'

        embed.add_field(name='Moderator',
                        value=f'{moderator.mention} {moderator}',
                        inline=False)

        if duration is not None:
            embed.add_field(name='Duration',
                            value=time.human_timedelta(duration),
                            inline=False)

        embed.add_field(name='Reason',
                        value=formatting.truncate(reason, 512)
                        if reason is not None else 'None')

        embed.set_footer(text=f'Case #{record["id"]} • ID: {member.id}')
        embed.timestamp = issued

        await ctx.send(embed=embed)