Beispiel #1
0
 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)
     }
Beispiel #2
0
    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)
Beispiel #3
0
    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'
Beispiel #4
0
    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)
Beispiel #5
0
    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)))
Beispiel #6
0
 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
     }
Beispiel #7
0
 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 ''
     }
Beispiel #8
0
 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
Beispiel #9
0
    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)
Beispiel #10
0
    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)
Beispiel #11
0
    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
Beispiel #12
0
    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)
Beispiel #13
0
 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
     }
Beispiel #14
0
    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())
        ))
Beispiel #15
0
 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)
Beispiel #16
0
 def export_kazhelp_vars(self):
     return {'default_delay': format_timedelta(self.cog_config.delay)}