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()
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)
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), )
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) ]
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)
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}')
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}**.' )
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)))
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}
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})"
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)
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)
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})"
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
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)
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
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}." )
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)
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)
def human_delta(self): return time.human_timedelta(self.created_at)
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)
def human_delta(self): return time.human_timedelta(self.time)
def get_uptime(self, *, brief=False): return human_timedelta(self.uptime, accuracy=None, brief=brief, suffix=False)
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')
async def uptime(self, ctx): ago = human_timedelta(self.bot.uptime) await ctx.send(f"Startup at {self.bot.uptime} : {ago}")
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)