def tempmute(self, event, user, duration=None, reason=None): if not duration and reason: duration = parse_duration(reason.split(' ')[0], safe=True) if duration: if ' ' in reason: reason = reason.split(' ', 1)[-1] else: reason = None elif duration: duration = parse_duration(duration) member = event.guild.get_member(user) if member: self.can_act_on(event, member.id) if not event.config.mute_role: raise CommandFail('mute is not setup on this server') if event.config.mute_role in member.roles: raise CommandFail(u'{} is already muted'.format(member.user)) # If we have a duration set, this is a tempmute if duration: # Create the infraction Infraction.tempmute(self, event, member, reason, duration) self.queue_infractions() self.confirm_action(event, maybe_string( reason, u':ok_hand: {u} is now muted for {t} (`{o}`)', u':ok_hand: {u} is now muted for {t}', u=member.user, t=humanize.naturaldelta(duration - datetime.utcnow()), )) else: existed = False # If the user is already muted check if we can take this from a temp # to perma mute. if event.config.mute_role in member.roles: existed = Infraction.clear_active(event, member.id, [Infraction.Types.TEMPMUTE]) # The user is 100% muted and not tempmuted at this point, so lets bail if not existed: raise CommandFail(u'{} is already muted'.format(member.user)) Infraction.mute(self, event, member, reason) existed = u' [was temp-muted]' if existed else '' self.confirm_action(event, maybe_string( reason, u':ok_hand: {u} is now muted (`{o}`)' + existed, u':ok_hand: {u} is now muted' + existed, u=member.user, )) else: raise CommandFail('invalid user')
def tempmute(self, event, user, duration, reason=None): member = event.guild.get_member(user) if member: self.can_act_on(event, member.id) if not event.config.mute_role: raise CommandFail('mute is not setup on this server') if event.config.mute_role in member.roles: raise CommandFail(u'{} is already muted'.format(member.user)) expire_dt = parse_duration(duration) # Reset the infraction task so we make sure it runs after this new infraction self.inf_task.set_next_schedule(expire_dt) # Create the infraction Infraction.tempmute(self, event, member, reason, expire_dt) if event.config.confirm_actions: event.msg.reply( maybe_string( reason, u':ok_hand: {u} is now muted for {t} (`{o}`)', u':ok_hand: {u} is now muted for {t}', u=member.user, t=humanize.naturaldelta(expire_dt - datetime.utcnow()), )) else: raise CommandFail('invalid user')
def command_recover(self, event, duration, pool=4, mode=None): if mode == 'global': channels = list(self.state.channels.values()) else: channels = list(event.guild.channels.values()) start_at = parse_duration(duration, negative=True) pool = Pool(pool) total = len(channels) count = 0 msg = event.msg.reply('Recovery Status: 0/{}'.format(total)) def updater(): last = count while True: if last != count: last = count msg.edit('Recovery Status: {}/{}'.format(count, total)) gevent.sleep(5) u = self.spawn(updater) try: for channel in channels: pool.wait_available() r = Recovery(self.log, channel, start_at) pool.spawn(r.run) count += 1 finally: u.kill() msg.edit('RECOVERY COMPLETED')
def temprole(self, event, user, role, duration, reason=None): member = event.guild.get_member(user) if not member: raise CommandFail('invalid user') self.can_act_on(event, member.id) role_id = role if isinstance(role, (int, long)) else event.config.role_aliases.get(role.lower()) if not role_id or role_id not in event.guild.roles: raise CommandFail('invalid or unknown role') if role_id in member.roles: raise CommandFail(u'{} is already in that role'.format(member.user)) expire_dt = parse_duration(duration) Infraction.temprole(self, event, member, role_id, reason, expire_dt) self.queue_infractions() self.confirm_action(event, maybe_string( reason, u':ok_hand: {u} is now in the {r} role for {t} (`{o}`)', u':ok_hand: {u} is now in the {r} role for {t}', r=event.guild.roles[role_id].name, u=member.user, t=humanize.naturaldelta(expire_dt - datetime.utcnow()), ))
def cmd_remind(self, event, duration, content=None): if Reminder.count_for_user(event.author.id) > 15: raise CommandFail('You can only have 15 reminders going at once!') remind_at = parse_duration(duration) if remind_at > (datetime.utcnow() + timedelta(seconds=5 * YEAR_IN_SEC)): raise CommandFail('That\'s too far in the future... I\'ll forget!') if event.msg.message_reference.message_id: referenced_msg: MessageReference = event.channel.get_message( event.msg.message_reference.message_id) content = 'https://discord.com/channels/{}/{}/{}'.format( self.state.channels.get(referenced_msg.channel_id).guild_id, referenced_msg.channel_id, referenced_msg.id) elif not content: raise CommandFail( 'You need to provide content for the reminder, or reply to a message!' ) r = Reminder.create(message_id=event.msg.id, remind_at=remind_at, content=content) self.reminder_task.set_next_schedule(r.remind_at) raise CommandSuccess( 'I\'ll remind you at <t:{0}:f> (<t:{0}:R>)'.format( int(r.remind_at.replace(tzinfo=pytz.UTC).timestamp())))
def infraction_duration(self, event, infraction, duration): try: inf = Infraction.get(id=infraction) except Infraction.DoesNotExist: raise CommandFail('invalid infraction (try `!infractions recent`)') if inf.actor_id != event.author.id and event.user_level < CommandLevels.ADMIN: raise CommandFail('only administrators can modify the duration of infractions created by other moderators') if not inf.active: raise CommandFail('that infraction is not active and cannot be updated') expires_dt = parse_duration(duration, inf.created_at) converted = False if inf.type_ in [Infraction.Types.MUTE.index, Infraction.Types.BAN.index]: inf.type_ = Infraction.Types.TEMPMUTE if inf.type_ == Infraction.Types.MUTE.index else Infraction.Types.TEMPBAN converted = True elif inf.type_ not in [Infraction.Types.TEMPMUTE.index, Infraction.Types.TEMPBAN.index, Infraction.Types.TEMPROLE.index]: raise CommandFail('cannot set the duration for that type of infraction') inf.expires_at = expires_dt inf.save() self.queue_infractions() if converted: raise CommandSuccess('ok, I\'ve made that infraction temporary, it will now expire on {}'.format( inf.expires_at.isoformat() )) else: raise CommandSuccess('ok, I\'ve updated that infractions duration, it will now expire on {}'.format( inf.expires_at.isoformat() ))
def archive_extend(self, event, archive_id, duration): try: archive = MessageArchive.get(archive_id=archive_id) except MessageArchive.DoesNotExist: raise CommandFail('invalid message archive id') archive.expires_at = parse_duration(duration) MessageArchive.update(expires_at=parse_duration(duration)).where( (MessageArchive.archive_id == archive_id)).execute() raise CommandSuccess( 'duration of archive {} has been extended (<{}>)'.format( archive_id, archive.url, ))
def tempban(self, event, duration, user, reason=None): member = event.guild.get_member(user) if member: self.can_act_on(event, member.id) expires_dt = parse_duration(duration) if event.config.limit_temp.duration_limit_level: if event.user_level <= event.config.limit_temp.duration_limit_level: if expires_dt > parse_duration( event.config.limit_temp.maximum_limited_duration): raise CommandFail( 'You cannot temp ban users for longer than ' + event.config.limit_temp.maximum_limited_duration) if event.config.notify_action_on: if event.config.notify_action_on.bans: try: event.guild.get_member(user.id).user.open_dm( ).send_message( 'You have been **Temporarily Banned** in the guild **{}** for **{}** for `{}`' .format( event.guild.name, humanize.naturaldelta(expires_dt - datetime.utcnow()), reason or 'no reason specified.')) except: pass else: pass else: pass Infraction.tempban(self, event, member, reason, expires_dt) self.queue_infractions() self.confirm_action( event, maybe_string( reason, u':ok_hand: temp-banned {u} for {t} (`{o}`)', u':ok_hand: temp-banned {u} for {t}', u=member.user, t=humanize.naturaldelta(expires_dt - datetime.utcnow()), )) else: raise CommandFail('invalid user')
def tempban(self, event, duration, user, reason=None): member = event.guild.get_member(user) if member: self.can_act_on(event, member.id) expires_dt = parse_duration(duration) Infraction.tempban(self, event, member, reason, expires_dt) self.queue_infractions() self.confirm_action(event, maybe_string( reason, u':ok_hand: temp-banned {u} for {t} (`{o}`)', u':ok_hand: temp-banned {u} for {t}', u=member.user, t=humanize.naturaldelta(expires_dt - datetime.utcnow()), )) else: raise CommandFail('invalid user')
def command_recover(self, event, duration, pool=4, mode=None): channels = [] if mode == 'global': chlist = self.bot.client.state.channels else: chlist = event.guild.channels for gch in chlist: if self.bot.client.state.channels[ gch].type == 0 or self.bot.client.state.channels[ gch].type == 5: if self.bot.client.state.channels[gch].get_permissions( self.bot.client.state.me).can( Permissions.VIEW_CHANNEL): channels.append(self.bot.client.state.channels[gch]) start_at = parse_duration(duration, negative=True) pool = Pool(pool) total = len(channels) msg = event.msg.reply('Recovery Status: 0/{}'.format(total)) recoveries = [] def updater(): last = len(recoveries) while True: if last != len(recoveries): last = len(recoveries) msg.edit('Recovery Status: {}/{}'.format( len(recoveries), total)) gevent.sleep(5) u = self.spawn(updater) try: for channel in channels: pool.wait_available() r = Recovery(self.log, channel, start_at) pool.spawn(r.run) recoveries.append(r) finally: pool.join() u.kill() msg.edit('RECOVERY COMPLETED ({} total messages)'.format( sum([i._recovered for i in recoveries])))
def cmd_remind(self, event, duration, content): if Reminder.count_for_user(event.author.id) > 15: raise CommandFail('You can only have 15 reminders going at once!') remind_at = parse_duration(duration) if remind_at > (datetime.utcnow() + timedelta(seconds=5 * YEAR_IN_SEC)): raise CommandSuccess('Thats too far in the future, I\'ll forget!') r = Reminder.create(message_id=event.msg.id, remind_at=remind_at, content=content) self.reminder_task.set_next_schedule(r.remind_at) raise CommandSuccess('I\'ll remind you at {} ({})'.format( r.remind_at.isoformat(), humanize.naturaldelta(r.remind_at - datetime.utcnow()), ))
def command_recover(self, event, duration, pool=4, mode=None): channels = [] if mode == 'global': chlist = list(self.state.channels.values()) else: chlist = list(event.guild.channels.values()) for gch in chlist: if self.state.channels[gch.id].type is ChannelType.GUILD_TEXT: if self.state.channels[gch.id].get_permissions( self.state.me.id).can( Permissions.VIEW_CHANNEL, Permissions.READ_MESSAGE_HISTORY): channels.append(self.state.channels[gch.id]) start_at = parse_duration(duration, negative=True) pool = Pool(pool) total = len(channels) msg = event.msg.reply('Recovery Status: 0/{}'.format(total)) recoveries = [] def updater(): last = len(recoveries) while True: if last != len(recoveries): last = len(recoveries) msg.edit('Recovery Status: {}/{}'.format( len(recoveries), total)) gevent.sleep(2) u = self.spawn(updater) try: for channel in channels: pool.wait_available() r = Recovery(self.log, channel, start_at) pool.spawn(r.run) recoveries.append(r) finally: pool.join() u.kill() msg.edit(':ok_hand: Recovery Completed ({} total messages)'.format( sum([i._recovered for i in recoveries])))
def tempban(self, event, duration, user, reason=None): member = event.guild.get_member(user) if member: expires_dt = parse_duration(duration) self.inf_task.set_next_schedule(expires_dt) Infraction.tempban(self, event, member, reason, expires_dt) if event.config.confirm_actions: event.msg.reply( maybe_string( reason, u':ok_hand: temp-banned {u} for {t} (`{o}`)', u':ok_hand: temp-banned {u} for {t}', u=member.user, t=humanize.naturaldelta(expires_dt - datetime.utcnow()), )) else: raise CommandFail('invalid user')
def cmd_remind(self, event, duration, content): if Reminder.count_for_user(event.author.id) > 30: return event.msg.reply( ':warning: you can only have 15 reminders going at once!') remind_at = parse_duration(duration) if remind_at > (datetime.utcnow() + timedelta(seconds=5 * YEAR_IN_SEC)): return event.msg.reply( ':warning: thats too far in the future, I\'ll forget!') r = Reminder.create(message_id=event.msg.id, remind_at=remind_at, content=content) self.reminder_task.set_next_schedule(r.remind_at) event.msg.reply(':ok_hand: I\'ll remind you at {} ({}) #{}'.format( r.remind_at.isoformat(), humanize_duration(r.remind_at - datetime.utcnow()), r.id))
def command_recover(self, event, duration, pool=4, mode=None): if mode == 'global': channels = list(self.state.channels.values()) else: channels = list(event.guild.channels.values()) start_at = parse_duration(duration, negative=True) pool = Pool(pool) total = len(channels) msg = event.msg.reply('Recovery Status: 0/{}'.format(total)) recoveries = [] def updater(): last = len(recoveries) while True: if last != len(recoveries): last = len(recoveries) msg.edit('Recovery Status: {}/{}'.format( len(recoveries), total)) gevent.sleep(5) u = self.spawn(updater) try: for channel in channels: pool.wait_available() r = Recovery(self.log, channel, start_at) pool.spawn(r.run) recoveries.append(r) finally: pool.join() u.kill() msg.edit('RECOVERY COMPLETED ({} total messages)'.format( sum([i._recovered for i in recoveries])))
def test_basic_durations(self): dt = parse_duration('1w2d3h4m5s') self.assertTrue(dt < (datetime.utcnow() + timedelta(days=10))) self.assertTrue(dt > (datetime.utcnow() + timedelta(days=7)))
def test_invalid_duration(self): self.assertEquals(parse_duration('mmmmm', safe=True), None)
def test_source_durations(self): origin = datetime.utcnow() + timedelta(days=17) dt = parse_duration('1w2d3h4m5s', source=origin) compare = (origin - datetime.utcnow()) + datetime.utcnow() self.assertTrue(dt < (compare + timedelta(days=10))) self.assertTrue(dt > (compare + timedelta(days=7)))