Esempio n. 1
0
 def str_dict(self) -> Dict[str, str]:
     return {
         'user': user_mention(self.user_id) if self.user_id else '',
         'channel':
         channel_mention(self.channel_id) if self.channel_id else 'PM',
         'timestamp': format_datetime(self.timestamp),
         'remind_time': format_datetime(self.remind_time),
         'pin': 'pinned' if self.pin else '',
         'message': self.message
     }
Esempio n. 2
0
    def format_quote(self, quote: Quote, show_saved=True):
        s_fmt = "[{0}] <#{1}> <{2}> {3}" if self.cog_config.show_channel else "[{0}] <{2}> {3}"

        if self.cog_config.datetime_format == 'seconds':
            timestamp_str = format_datetime(quote.timestamp, seconds=True)
        elif self.cog_config.datetime_format == 'datetime':
            timestamp_str = format_datetime(quote.timestamp, seconds=False)
        elif self.cog_config.datetime_format == 'date':
            timestamp_str = format_date(quote.timestamp)
        else:
            raise RuntimeError("Invalid date_format??")

        s = s_fmt.format(timestamp_str, quote.channel_id, quote.author.mention,
                         quote.message)
        if show_saved:
            s += "\n*(saved by {})*".format(quote.saved_by.name)
        return s
Esempio n. 3
0
    async def task_reminder_expired(self, reminder: ReminderData):
        logger.info("Reminder has expired: {!r}".format(reminder))

        # determine the destination (user or channel)
        if reminder.channel_id:
            dest = discord.Object(id=reminder.channel_id)
            message = f"{reminder.message}\n\n*({user_mention(reminder.user_id)})*"
        else:  # user reminder
            # can't use discord.Object - send_message would interpret it as a channel, not a user
            dest = discord.utils.get(self.bot.get_all_members(),
                                     id=reminder.user_id)
            message = "**Reminder** At {} UTC, you asked me to send you a reminder: {}".format(
                format_datetime(reminder.timestamp), reminder.message)

        posted_messages = await self.send_message(dest, message)

        if reminder.pin:
            try:
                await self.bot.pin_message(posted_messages[0])
            except discord.HTTPException as e:
                logger.exception(
                    "Error pinning message; skipping: {!r}".format(reminder))
                await self.send_output(
                    "Error trying to pin reminder/saylater message: {!r}.".
                    format(reminder))

        # stop scheduled retries and remove the reminder
        try:
            for instance in self.scheduler.get_instances(
                    self.task_reminder_expired):
                if instance.args[0] is reminder:
                    instance.cancel()
                    break
        except asyncio.InvalidStateError:
            pass

        try:
            self.reminders.remove(reminder)
        except ValueError:
            logger.warning(
                "task_reminder_expired: Reminder not in list of reminders - "
                "already removed? {!r}".format(reminder))

        # set up recurring reminder
        if reminder.renew_data and reminder.renew_data.interval:
            reminder.remind_time += reminder.renew_data.interval
            reminder.renew_data.limit -= 1
            if reminder.renew_data.limit <= 0:
                logger.debug("Recurring reminder has reached recurrence limit")
            elif reminder.renew_data.limit_time and \
                    reminder.remind_time > reminder.renew_data.limit_time:
                logger.debug("Recurring reminder has reached time limit")
            else:
                logger.debug("Setting up recurrence")
                self.add_reminder(reminder)

        self._save_reminders()
Esempio n. 4
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 ''
     }
Esempio n. 5
0
    async def kaztime(self, ctx: commands.Context):
        """!kazhelp

        description: "Gets the current KazTron Time (UTC, GMT). Can be used to help convert
            community programme schedules to your timezone."
        parameters: []
        examples:
            - command: .kaztime
              description: Shows the current time.
        """
        await self.send_message(ctx.message.channel, ctx.message.author.mention +
            "Current KazTime: {} UTC".format(format_datetime(datetime.utcnow(), seconds=True)))
Esempio n. 6
0
    async def send_check_in_list(self, dest: discord.Channel,
                                 check_ins: Pagination,
                                 member: discord.Member):

        es = EmbedSplitter(auto_truncate=True,
                           title="Check-in list",
                           description=member.mention,
                           colour=self.EMBED_COLOR)
        es.set_footer(text="Page {:d}/{:d}".format(check_ins.page +
                                                   1, check_ins.total_pages))

        for check_in in check_ins:  # type: model.CheckIn
            f_name = format_datetime(check_in.timestamp)
            f_message = '{}\n*{:d} {}* – {}\n\\_\\_\\_'.format(
                member.mention, check_in.word_count,
                self.PROJECT_UNIT_MAP[check_in.project_type], check_in.message)
            es.add_field(name=f_name, value=f_message, inline=False)

        for em in es.finalize():
            await self.bot.send_message(dest, embed=em)
Esempio n. 7
0
    async def show_badges(self, dest: discord.Channel, badges: Pagination,
                          member: discord.Member):
        es = EmbedSplitter(auto_truncate=True,
                           title="Badges",
                           description="{} - {:d} badges".format(
                               member.mention, len(badges)),
                           colour=self.EMBED_COLOUR)
        es.set_footer(text="Page {:d}/{:d} (total {:d} badges)".format(
            badges.page + 1, badges.total_pages, len(badges)))
        for b in badges:  # type: model.Badge
            es.add_field_no_break(name=format_datetime(b.timestamp),
                                  value=self._get_badge(b.badge))
            es.add_field_no_break(name="From",
                                  value=user_mention(b.from_user.discord_id))
            es.add_field(name="For",
                         value=b.reason + '\n' + r'\_' * 16,
                         inline=False)

        for em in es.finalize():
            await self.bot.send_message(dest, embed=em)
Esempio n. 8
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
Esempio n. 9
0
 async def on_reminder_expired(self, reminder: ReminderData):
     logger.info("Reminder has expired: {!r}".format(reminder))
     user = discord.utils.get(self.bot.get_all_members(), id=reminder.user_id)
     try:
         await self.bot.send_message(
             user,
             "**Reminder** At {} UTC, you asked me to send you a reminder: {}".format(
                 format_datetime(reminder.timestamp),
                 reminder.message
             )
         )
     except discord.errors.DiscordException as e:
         logger.error("Error sending reminder: {}".format(exc_log_str(e)))
         reminder.remind_time += 30  # try again a little later
         reminder.start_timer(self.bot.loop, self.on_reminder_expired)
     else:
         try:
             self.reminders.remove(reminder)
         except ValueError:
             logger.warning("on_reminder_expired: Reminder not in list of reminders - "
                            "already removed? {!r}".format(reminder))
     self._save_reminders()
Esempio n. 10
0
    async def check_in_delta(self,
                             ctx: commands.Context,
                             *,
                             datespec: NaturalDateConverter = None):
        """!kazhelp
        description: "Get a report of wordcount changes in a given check-in week, compared to
            previous check-in."
        parameters:
            - name: datespec
              type: datespec
              optional: true
              default: 'last week ("7 days ago")'
              description: A date in any unambiguous format (2018-03-14, March 14 2018,
                  14 March 2018, today, 1 month ago, etc.). The report will be for the check-in week
                  that includes this date.
        examples:
            - command: .checkin delta
              description: Get a report for last week.
            - command: .checkin delta 2018-04-18
              description: Get a report for the week that includes 18 April 2018.
        """
        if not datespec:
            datespec = datetime.utcnow() - timedelta(days=7)

        start, end = self.c.get_check_in_week(datespec)
        week_str = "the week from {} to {}".format(format_datetime(start),
                                                   format_datetime(end))
        try:
            report, ci_map = self.c.generate_check_in_deltas(datespec)
        except orm.exc.NoResultFound:
            await self.bot.say("No check-ins for {}.".format(week_str))
            return

        # sort descending by diff value
        s_users = list(report.keys())
        s_users.sort(key=lambda u: report[u]
                     if report.get(u, None) else float('-inf'),
                     reverse=True)

        # Prepare display
        delta_strings = []
        for u in s_users:
            if report[u] is not None:
                unit = self.PROJECT_UNIT_MAP[ci_map[u].project_type] if ci_map.get(u, None) \
                    else 'words'
                delta_strings.append(
                    "{0} ({1:+d} {3} - *total {2:d} {3}*)".format(
                        u.mention, report[u],
                        ci_map[u].word_count if ci_map.get(u, None) else 0,
                        unit))
            else:
                delta_strings.append("{0} (no check-in)".format(u.mention))
        delta_list_str = '\n'.join(delta_strings)

        # Prepare the overall embed
        es = EmbedSplitter(title="User Progress Report",
                           colour=solarized.cyan,
                           description="Report for " + week_str + '\n\n',
                           timestamp=datetime.utcnow(),
                           repeat_header=True,
                           auto_truncate=True)
        es.set_footer(text="Generated: ")
        es.add_field(name="_", value=delta_list_str, inline=False)
        await self.send_message(ctx.message.channel, embed=es)
Esempio n. 11
0
    async def check_in_report(self,
                              ctx: commands.Context,
                              *,
                              datespec: NaturalDateConverter = None):
        """!kazhelp
        description: "Get a report of who has or has not checked in in a given week."
        parameters:
            - name: datespec
              type: datespec
              optional: true
              default: 'last week ("7 days ago")'
              description: A date in any unambiguous format (2018-03-14, March 14 2018,
                  14 March 2018, today, 1 month ago, etc.). The report will be for the check-in week
                  that includes this date.
        examples:
            - command: .checkin report
              description: Get a report for last week.
            - command: .checkin report 2018-04-18
              description: Get a report for the week that includes 18 April 2018.
        """
        if not datespec:
            datespec = datetime.utcnow() - timedelta(days=7)

        start, end = self.c.get_check_in_week(datespec)
        week_str = "the week from {} to {}".format(format_datetime(start),
                                                   format_datetime(end))
        try:
            ci, nci = self.c.generate_check_in_report(
                datespec)  # checked in, not checked in
        except orm.exc.NoResultFound:
            await self.bot.say("No check-ins for {}.".format(week_str))
            return

        #
        # determine sorting order of each list
        #

        # checked in: by name
        ci_users = list(ci.keys())
        ci_users.sort(
            key=lambda u: u.nick.lower() if u.nick else u.name.lower())

        # not checked in: by last checkin date pre-reporting week
        nci_users = list(nci.keys())
        epoch = datetime(1970, 1, 1)
        nci_users.sort(key=lambda u: nci[u].timestamp
                       if nci.get(u, None) else epoch,
                       reverse=True)

        #
        # Prepare display
        #

        # format strings for display
        ci_list_str = '\n'.join("{0} ({1} - *{2:d} {3}*)".format(
            u.mention, format_datetime(ci[u].timestamp), ci[u].word_count,
            self.PROJECT_UNIT_MAP[ci[u].project_type]) for u in ci_users)
        nci_list_str = '\n'.join("{0} (last: {1})".format(
            u.mention,
            format_date(nci[u].timestamp) if nci.get(u, None) else 'Never')
                                 for u in nci_users)

        # Prepare the overall embed
        es = EmbedSplitter(title="Check-In Report",
                           colour=solarized.green,
                           description="Report for " + week_str,
                           timestamp=datetime.utcnow(),
                           repeat_header=True,
                           auto_truncate=True)
        es.set_footer(text="Generated: ")
        if len(ci_list_str) < Limits.EMBED_FIELD_VALUE:
            es.add_field(name="Checked in",
                         value=ci_list_str or 'Nobody',
                         inline=False)
        else:
            es.add_field(name="Checked in",
                         value="{:d} users (list too long)".format(
                             len(ci_users)),
                         inline=False)

        es.add_field(name="Did NOT check in", value=nci_list_str, inline=False)
        await self.send_message(ctx.message.channel, embed=es)
Esempio n. 12
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())
        ))