def export_kazhelp_vars(self): return { 'check_interval': format_timedelta(self.cog_config.check_interval), 'min_post_interval': format_timedelta(self.cog_config.min_post_interval), 'max_posts_per_interval': str(self.cog_config.max_posts_per_interval) }
async def saylater(self, ctx: commands.Context, channel: discord.Channel, *, args: str): """!kazhelp description: | Schedule a message for the bot to send in-channel later. Can also set up recurring messages (static messages only). Messages can also be pinned. Recurring messages can repeat up to {{renew_limit}} times, and cannot repeat more often than every {{renew_interval_min}}s. TIP: You should double-check the time in the response message to make sure your timespec was interpreted correctly. parameters: - name: channel description: The channel to post the message in. - name: args description: "Same as {{!reminder}}. You can also include the word `pin` before the colon." examples: - command: ".saylater #community-programs at 12:00: Welcome to our AMA with philosopher Aristotle!" description: Single message at noon UTC. - command: ".saylater #announcements at 12:00 every 1 hour limit 24: Attention, citizens. For the duration of gremlin season, all citizens must be on the lookout for crown-stealing gremlins. Any sightings or incidents must be reported to your nearest moderator immediately." description: Recurring message every hour starting at noon UTC. - command: ".saylater #general at 15:00 pin: Karaoke hour for the next hour! Check out #karaoke for more info." description: Single message at 15:00 UTC, auto-pinned. """ reminder = self.make_reminder(ctx, args, channel) self.add_reminder(reminder) # set message if not reminder.renew_data: reply = "Got it! " \ "I'll post that in {channel} {pin} at {remind_time} UTC (in {delta}).".format( delta=format_timedelta(reminder.remind_time - reminder.timestamp), **reminder.str_dict() ) else: if not reminder.renew_data.limit_time: replyf = "Got it! I'll post that in {channel} {pin} at {remind_time} UTC " \ "(in {delta}), then every {interval} up to {limit} times." else: replyf = "Got it! I'll post that in {channel} {pin} at {remind_time} UTC " \ "(in {delta}), then every {interval} until {limit_time} " \ "or up to {limit} times." reply = replyf.format(delta=format_timedelta(reminder.remind_time - reminder.timestamp), **reminder.str_dict(), **reminder.renew_data.str_dict()) await self.send_message(ctx.message.channel, reply)
def format_list(reminders: List[ReminderData]) -> str: """ Format the input list for sending to a user. """ items = [] for reminder in reminders: build_str = [] if reminder.channel_id: build_str.append( "In {channel} at {remind_time} UTC (in {delta})") else: build_str.append("At {remind_time} UTC (in {delta})") if reminder.renew_data: renew_data = reminder.renew_data build_str.append(" and every {interval}") if renew_data.limit_time: build_str.append(" until {limit_time} up to") build_str.append(" {limit} times") build_str.append(": {message}") items.append(''.join(build_str).format( delta=format_timedelta(reminder.remind_time - datetime.utcnow()), **reminder.str_dict(), **(reminder.renew_data.str_dict() if reminder.renew_data else {}))) return format_list(items) if items else 'None'
async def list(self, ctx: commands.Context): """!kazhelp brief: List all configured sticky messages. description: | List all configured sticky messages. """ sorted_data = sorted(self.cog_state.messages.values(), key=lambda v: v.channel.name) if sorted_data: es = EmbedSplitter(title="Channel Sticky Messages") else: es = EmbedSplitter(title="Channel Sticky Messages", description="None.") for data in sorted_data: try: jump_url = ' *([link]({}))*'.format( get_jump_url(await data.get_posted_message(self.bot))) except (AttributeError, discord.NotFound): jump_url = '' delay_string = ' (delay: {})'.format(format_timedelta( data.delay)) if data.delay else '' es.add_field(name="#{}{}".format(data.channel.name, delay_string), value="{}{}".format( natural_truncate(data.message, 512), jump_url), inline=False) await self.send_message(ctx.message.channel, embed=es)
async def delay(self, ctx: commands.Context, channel: discord.Channel, *, delay: int): """!kazhelp brief: Set delay for updating sticky message. description: | Set a non-default delay for updating the sticky message in a channel. If a sticky message update is already scheduled, the delay will not be updated until the next event that would trigger a delayed update. parameters: - name: channel type: channel description: Channel to change - name: delay type: int description: Delay before updating the sticky message (seconds) examples: - command: ".sticky delay #meta 300" description: Set the sticky to update after 300 seconds in #meta. """ sticky_config = self.cog_state.messages try: sticky_config[channel.id].delay = timedelta(seconds=delay) except KeyError: raise commands.BadArgument("channel", channel) self.cog_state.set('messages', sticky_config) await self.send_message( ctx.message.channel, "Set delay for sticky in #{} to {}".format( channel, format_timedelta(sticky_config[channel.id].delay)))
def export_kazhelp_vars(self): interval_s = format_timedelta( timedelta(seconds=self.cog_config.renew_interval_min)) return { "max_per_user": f'{self.cog_config.max_per_user:d}', "renew_limit": f'{self.cog_config.renew_limit:d}', "renew_interval_min": interval_s }
def str_dict(self) -> Dict[str, str]: return { 'interval': format_timedelta(self.interval) if self.interval else '', 'limit': '{:d}'.format(self.limit) if self.limit else '', 'limit_time': format_datetime(self.limit_time) if self.limit_time else '' }
async def reminder_error(self, exc, ctx): if isinstance(exc, commands.BadArgument): if exc.args[0] == 'timespec': logger.warning("Passed unknown timespec: {}".format( exc.args[1])) await self.send_message( ctx.message.channel, ctx.message.author.mention + " Sorry, I don't understand the timespec '{}'".format( exc.args[1])) elif exc.args[0] == 'range': logger.warning("Passed timespec outside range: {}".format( exc.args[1])) await self.send_message( ctx.message.channel, ctx.message.author.mention + " Sorry, that's too far in the future! I'll forget by then." ) elif exc.args[0] == 'past': logger.warning("Passed timespec in the past: {}".format( exc.args[1])) await self.send_message( ctx.message.channel, ctx.message.author.mention + " Oops! You can't set a reminder in the past.") elif exc.args[0] == 'message': logger.warning("Invalid syntax: {}".format(exc.args[1])) await self.send_message( ctx.message.channel, ctx.message.author.mention + " You need to specify a message for your reminder! " "Separate it from the timespec with a colon *and* space: " "`.reminder in 10 minutes: message`") elif exc.args[0] == 'renew_interval_neg' or exc.args[ 0] == 'renew_interval_min': logger.warning("Passed invalid renew interval: {}".format( exc.args[1])) await self.send_message( ctx.message.channel, ctx.message.author.mention + " Sorry, I can't repeat reminders more often than {}. Don't want to spam you!" .format( format_timedelta( timedelta( seconds=self.cog_config.renew_interval_min)))) elif isinstance( exc, commands.UserInputError) and exc.args[0] == 'max_per_user': logger.warning("Cannot add reminder: user {} at limit".format( ctx.message.author)) await self.send_message( ctx.message.channel, ctx.message.author.mention + "Sorry, you already have too many future reminders! " "The limit is {:d} per person.".format( self.cog_config.max_per_user)) else: core_cog = self.bot.get_cog("CoreCog") await core_cog.on_command_error(exc, ctx, force=True ) # Other errors can bubble up
async def saylater_remove(self, ctx: commands.Context, index: int): """!kazhelp description: | Remove a scheduled message. WARNING: This command cannot be undone. parameters: - name: index type: int description: The number of the reminder to remove. See the {{!saylater list}} command for the numbered list. examples: - command: .saylater rem 4 description: Removes message number 4. """ if index <= 0: await self.send_message( ctx.message.channel, ctx.message.author.mention + " Oops, that scheduled message doesn't exist!") return try: reminder = self.saylaters[index - 1] except IndexError: await self.send_message( ctx.message.channel, ctx.message.author.mention + " Oops, that message doesn't exist! You only have {:d} messages scheduled." .format(len(self.saylaters))) return desc = "Removed scheduled message "\ "in {channel} for {remind_time} UTC (in {delta}): {message}".format( delta=format_timedelta(reminder.remind_time - datetime.utcnow()), **reminder.str_dict() ) self.remove_reminder(reminder) await self.send_message(ctx.message.channel, ctx.message.author.mention + " " + desc)
async def reminder_remove(self, ctx: commands.Context, index: int): """!kazhelp description: | Remove a reminder. WARNING: This command cannot be undone. parameters: - name: index type: int description: The number of the reminder to remove. See the {{!reminder list}} command for the numbered list. examples: - command: .reminder rem 4 description: Removes reminder number 4. """ if index <= 0: await self.send_message( ctx.message.channel, ctx.message.author.mention + " Oops, that reminder doesn't exist!") return try: reminder = self.get_matching(user_id=ctx.message.author.id)[index - 1] except IndexError: await self.send_message( ctx.message.channel, ctx.message.author.mention + " Oops, that reminder doesn't exist! You only have {:d} reminders." .format(self.get_count(user_id=ctx.message.author.id))) return desc = "Removed reminder for {remind_time} UTC (in {delta}): {message}".format( delta=format_timedelta(reminder.remind_time - datetime.utcnow()), **reminder.str_dict()) self.remove_reminder(reminder) await self.send_message(ctx.message.channel, ctx.message.author.mention + " " + desc)
async def list(self, ctx: commands.Context): """ Lists all future reminders you've requested. """ logger.info("reminder list: {}".format(message_log_str(ctx.message))) items = [] filtered = filter(lambda r: r.user_id == ctx.message.author.id, self.reminders) sorted_reminders = sorted(filtered, key=lambda r: r.remind_time) for reminder in sorted_reminders: items.append("At {} UTC (in {}): {}".format( format_datetime(reminder.remind_time), format_timedelta(reminder.remind_time - datetime.utcnow()), reminder.message )) if items: reminder_list = format_list(items) else: reminder_list = 'None' await self.bot.send_message(ctx.message.author, "**Your reminders**\n" + reminder_list) try: await self.bot.delete_message(ctx.message) except discord.Forbidden: pass # no permission or in a PM; oh well, this is not critical
async def reminder(self, ctx: commands.Context, *, args: str): """!kazhelp description: | Sends you a personal reminder by PM at some point in the future. This function can also set up recurring reminders. Each user can have a maximum of {{max_per_user}} reminders. Recurring reminders can repeat up to {{renew_limit}} times, and cannot repeat more often than every {{renew_interval_min}}. TIP: Make sure you've enabled "Allow direct messages from server members" for the server the bot is on. TIP: You should double-check the reminder time in the confirmation PM, to make sure your timespec was interpreted correctly. parameters: - name: args description: "Multi-part argument consists of `<timespec> [\\"every\\" <intervalspec> [\\"limit\\" <limit>|\\"until\\" <limit_timespec>]: <message>`." - name: timespec type: timespec description: | A time in the future to send you a reminder, followed by a colon and a space. This can be an absolute date and time `2018-03-07 12:00:00`, a relative time `in 2h 30m` (the "in" **and** the spaces are important), or combinations of the two (`tomorrow at 1pm`). If giving an absolute time, you can specify a time zone (e.g. `1pm UTC-5` or `13:05 EST`); if none specified, default is UTC. - name: intervalspec type: timespec optional: true description: | How often the reminder should repeat after the `timespec`. Can take any relative time specification accepted by `timespec`, e.g., `every 1 hour`, `every 4h 30m`, etc. - name: limit type: int optional: true description: How many times the reminder will repeat. Only one of `limit` or `limitspec` may be used. - name: limitspec type: timespec optional: true description: The latest time at which the reminder will repeat. Accepts the same values as `timespec`. Only one of `limit` or `limitspec` may be used. - name: message type: string description: The message you want to be reminded with. examples: - command: ".remind on 24 december at 4:50pm: Grandma's Christmas call" description: Date and time. Assumes nearest future date. Time is interpreted as UTC. - command: ".remind in 2 hours: Feed the dog" description: Relative time. - command: ".remind tomorrow at 8am PST: start spotlight" description: Relative date, absolute time, time zone specified. - command: ".remind in 2 hours every 1 hour limit 8: drink water, you dehydrated prune" description: Reminder starting in 2 hours, repeating every 1 hour, 8 times total. - command: ".remind 22:00 EDT every 1 hour until 08:00 EDT: Remember to sleep" description: Reminder every hour between 10PM tonight and 8AM tomorrow. """ if self.get_count( user_id=ctx.message.author.id) >= self.cog_config.max_per_user: raise commands.UserInputError('max_per_user') reminder = self.make_reminder(ctx, args) if reminder.pin: raise commands.BadArgument("message", "Personal reminders cannot be pinned.") self.add_reminder(reminder) # set message if not reminder.renew_data: reply = "Got it! I'll remind you by PM at {remind_time} UTC (in {delta}).".format( delta=format_timedelta(reminder.remind_time - reminder.timestamp), **reminder.str_dict()) else: if not reminder.renew_data.limit_time: replyf = "Got it! I'll remind you by PM at {remind_time} UTC (in {delta}), " \ "then every {interval} up to {limit} times." else: replyf = "Got it! I'll remind you by PM at {remind_time} UTC (in {delta}), " \ "then every {interval} until {limit_time} or up to {limit} times." reply = replyf.format(delta=format_timedelta(reminder.remind_time - reminder.timestamp), **reminder.str_dict(), **reminder.renew_data.str_dict()) await self.send_message(ctx.message.channel, reply)
def export_kazhelp_vars(self): return { 'mod_channel': '#' + self.cog_config.channel_mod.name, 'check_interval': format_timedelta(self.cog_config.ban_check_interval), 'temp_expires': self.cog_config.ban_temp_expires }
async def reminder(self, ctx: commands.Context, *, args: str): """ Sends you a personal reminder by PM at some point in the future. TIP: Make sure you've enabled "Allow direct messages from server members" for the server the bot is on. TIP: You should double-check the reminder time in the confirmation PM, to make sure your timespec was interpreted correctly. **Usage:** `.remind <timespec>: <message>` **Arguments:** * `<timespec>: `: A time in the future to send you a reminder, followed by a colon and a space. This can be an absolute date and time `2018-03-07 12:00:00`, a relative time `in 2h 30m` (the space between hours and minutes, or other different units, is important), or combinations of the two (`tomorrow at 1pm`). Times are in UTC+0000, unless you specify your time zone (e.g. `12:00:00 UTC-5`). * `<message>`: The message to include with the reminder. **Examples:** .remind in 2 hours: Feed the dog .remind on 24 december at 4:50pm: Grandma's christmas call .remind tomorrow at 8am: Start Spotlight """ logger.info("reminder: {}".format(message_log_str(ctx.message))) # check existing count n = reduce(lambda c, r: c+1 if r.user_id == ctx.message.author.id else c, self.reminders, 0) if n >= self.MAX_PER_USER: logger.warning("Cannot add reminder: user {} at limit".format(ctx.message.author)) await self.bot.say(("Oops! You already have too many future reminders! " "The limit is {:d} per person.").format(self.MAX_PER_USER)) return try: timespec_s, msg = re.split(r':\s+|,', args, maxsplit=1) except ValueError: raise commands.BadArgument("message") timestamp = datetime.utcnow() # first one allows "10 minutes" as a future input, second is a fallback timespec = dt_parse('in ' + timespec_s, future=True) or dt_parse(timespec_s, future=True) if timespec is None: raise commands.BadArgument("timespec", timespec_s[:64]) elif timespec <= timestamp: raise commands.BadArgument("past") reminder = ReminderData( user_id=ctx.message.author.id, timestamp=timestamp, remind_time=timespec, msg=msg ) reminder.start_timer(self.bot.loop, self.on_reminder_expired) self.reminders.append(reminder) self._save_reminders() logger.info("Set reminder: {!r}".format(reminder)) await self.bot.say("Got it! I'll remind you by PM at {} UTC (in {!s}).".format( format_datetime(reminder.remind_time), format_timedelta(reminder.remind_time - datetime.utcnow()) ))
def __repr__(self): return '<InfoMessageData channel=#{} message={} delay={}, posted_message={}>'.format( self.channel.name, self.message[:50], format_timedelta(self.delay) if self.delay else 'None', self.posted_message_id)
def export_kazhelp_vars(self): return {'default_delay': format_timedelta(self.cog_config.delay)}