示例#1
0
    def get_conn(self, keyword: str, keyword_type: AutoReplyContentType,
                 channel_oid: ObjectId, case_insensitive: bool = True) -> \
            Optional[AutoReplyModuleModel]:
        ret: Optional[AutoReplyModuleModel] = \
            self.find_one_casted(
                {
                    AutoReplyModuleModel.KEY_KW_CONTENT: keyword,
                    AutoReplyModuleModel.KEY_KW_TYPE: keyword_type,
                    AutoReplyModuleModel.ChannelId.key: channel_oid,
                    AutoReplyModuleModel.Active.key: True
                },
                parse_cls=AutoReplyModuleModel,
                collation=case_insensitive_collation if case_insensitive else None)

        if ret:
            now = now_utc_aware()
            if ret.can_be_used(now):
                q_found = {AutoReplyModuleModel.Id.key: ret.id}
                u_query = {
                    "$set": {
                        AutoReplyModuleModel.LastUsed.key: now
                    },
                    "$inc": {
                        AutoReplyModuleModel.CalledCount.key: 1
                    }
                }

                self.update_one_async(q_found, u_query)
                return ret

        return None
示例#2
0
    def test_to_string(self):
        now = now_utc_aware()
        seoul_tzinfo = LocaleInfo.get_tzinfo("Asia/Taipei")

        actual_str = self.get_data().to_string(self.get_user_model())

        expected_str = [
            Timer.FUTURE.format(
                event=TestTimerListResult.TMR_3.title,
                diff=t_delta_str(
                    TestTimerListResult.TMR_3.get_target_time_diff(now)),
                time=localtime(TestTimerListResult.TMR_3.target_time,
                               seoul_tzinfo)),
            Timer.FUTURE.format(
                event=TestTimerListResult.TMR_4.title,
                diff=t_delta_str(
                    TestTimerListResult.TMR_4.get_target_time_diff(now)),
                time=localtime(TestTimerListResult.TMR_4.target_time,
                               seoul_tzinfo)), "",
            Timer.PAST_CONTINUE.format(
                event=TestTimerListResult.TMR_2.title,
                diff=t_delta_str(
                    TestTimerListResult.TMR_2.get_target_time_diff(now)),
                time=localtime(TestTimerListResult.TMR_2.target_time,
                               seoul_tzinfo)), "",
            Timer.PAST_DONE.format(
                event=TestTimerListResult.TMR_1.title,
                diff=t_delta_str(
                    TestTimerListResult.TMR_1.get_target_time_diff(now)),
                time=localtime(TestTimerListResult.TMR_1.target_time,
                               seoul_tzinfo))
        ]
        expected_str = "\n".join(expected_str)

        self.assertEqual(actual_str, expected_str)
示例#3
0
    def get_notify(self,
                   channel_oid: ObjectId,
                   within_secs: Optional[int] = None) -> List[TimerModel]:
        now = now_utc_aware()

        filter_ = {
            TimerModel.ChannelOid.key: channel_oid,
            TimerModel.TargetTime.key: {
                "$lt":
                now + timedelta(seconds=within_secs if within_secs else Bot.
                                Timer.MaxNotifyRangeSeconds),
                "$gt":
                now
            },
            TimerModel.Notified.key: False
        }

        ret = list(
            self.find_cursor_with_count(filter_, parse_cls=TimerModel).sort([
                (TimerModel.TargetTime.key, pymongo.ASCENDING)
            ]))

        self.update_many_async(filter_,
                               {"$set": {
                                   TimerModel.Notified.key: True
                               }})

        return ret
示例#4
0
 def _remove_update_ops_(self, remover_oid: ObjectId):
     return {
         "$set": {
             AutoReplyModuleModel.Active.key: False,
             AutoReplyModuleModel.RemovedAt.key: now_utc_aware(),
             AutoReplyModuleModel.RemoverOid.key: remover_oid
         }
     }
示例#5
0
 def record_message(self, channel_oid: ObjectId, user_root_oid: ObjectId,
                    message_type: MessageType, message_content: Any,
                    proc_time_secs: float):
     self.insert_one_data(ChannelOid=channel_oid,
                          UserRootOid=user_root_oid,
                          MessageType=message_type,
                          MessageContent=str(message_content)
                          [:Database.MessageStats.MaxContentCharacter],
                          ProcessTimeSecs=proc_time_secs,
                          Timestamp=now_utc_aware())
示例#6
0
 def test_dt_naive(self):
     self.assertTrue(is_tz_naive(datetime.now()))
     self.assertTrue(is_tz_naive(datetime.utcnow()))
     self.assertTrue(is_tz_naive(localtime().replace(tzinfo=None)))
     self.assertFalse(is_tz_naive(now_utc_aware()))
     self.assertTrue(is_tz_naive(datetime(2020, 5, 7)))
     self.assertTrue(is_tz_naive(datetime.min))
     self.assertFalse(is_tz_naive(
         datetime.min.replace(tzinfo=timezone.utc)))
     self.assertTrue(is_tz_naive(datetime.max))
     self.assertFalse(is_tz_naive(
         datetime.max.replace(tzinfo=timezone.utc)))
示例#7
0
    def __post_init__(self, cursor):
        now = now_utc_aware()

        for mdl in cursor:
            self.has_data = True

            if mdl.is_after(now):
                self.future.append(mdl)
            else:
                if mdl.countup:
                    self.past_continue.append(mdl)
                else:
                    self.past_done.append(mdl)
示例#8
0
    def test_locale_info(self):
        """Only testing if the info can be acquired without any exceptions. Not testing the correctness."""
        for locale in locales:
            with self.subTest(locale=locale):
                self.assertIsNotNone(locale.current_utc_hr_offset)

                pytzinfo = locale.to_tzinfo()
                now = now_utc_aware()
                self.assertIsNotNone(pytzinfo)
                self.assertIsNotNone(pytzinfo.utcoffset(now))
                self.assertIsNotNone(pytzinfo.dst(now))
                self.assertIsNotNone(pytzinfo.tzname(now))
                self.assertEqual(locale.pytz_code, pytzinfo.tzidentifier)
示例#9
0
def process_timer_notification(
        e: TextMessageEventObject) -> List[HandledMessageEvent]:
    within_secs = min(
        TimerManager.get_notify_within_secs(
            MessageRecordStatisticsManager.get_message_frequency(
                e.channel_oid, Bot.Timer.MessageFrequencyRangeMin)),
        Bot.Timer.MaxNotifyRangeSeconds)

    tmr_ntf = TimerManager.get_notify(e.channel_oid, within_secs)
    tmr_up = TimerManager.get_time_up(e.channel_oid)

    ret = []

    if tmr_up:
        ret.append(_("**{} timer(s) have timed up!**").format(len(tmr_up)))
        ret.append("")

        for tmr in tmr_up:
            ret.append(
                _("- {event} has timed up! (at {time})").format(
                    event=tmr.title,
                    time=localtime(tmr.target_time,
                                   e.user_model.config.tzinfo)))

    if tmr_ntf:
        if ret:
            ret.append("-------------")

        now = now_utc_aware()
        ret.append(
            _("**{} timer(s) will time up in less than {:.0f} minutes!**").
            format(len(tmr_ntf), within_secs / 60))
        ret.append("")

        for tmr in tmr_ntf:
            ret.append(
                _("- {event} will time up after [{diff}]! (at {time})").format(
                    event=tmr.title,
                    diff=t_delta_str(tmr.get_target_time_diff(now)),
                    time=localtime(tmr.target_time,
                                   e.user_model.config.tzinfo)))

    if ret and ret[-1] == "":
        ret = ret[:-1]

    if ret:
        return [HandledMessageEventText(content="\n".join(ret))]
    else:
        return []
示例#10
0
    def get(self, request, *args, **kwargs):
        root_oid = get_root_oid(request)
        if root_oid and now_utc_aware() - root_oid.generation_time < timedelta(
                days=Website.NewRegisterThresholdDays):
            messages.info(
                request,
                _('It seems that you haven\'t integrate your account. '
                  'Visit <a href="{}{}">this page</a> to know what to do to fully utilize this bot!'
                  ).format(
                      HostUrl,
                      reverse("page.doc.botcmd.cmd",
                              kwargs={"code": cmd_uintg.main_cmd_code})),
                extra_tags="safe")

        return render_template(request, _("Home Page"), "index.html")
示例#11
0
 def test_fill_all_none(self):
     tr = TimeRange()
     now = now_utc_aware()
     self.assertIsNone(tr.start)
     self.assertIsNone(tr.start_org)
     self.assertAlmostEquals(0, abs((tr.end - now).total_seconds()), 0)
     self.assertIsNone(tr.tzinfo_)
     self.assertFalse(tr.expandable)
     self.assertFalse(tr.expanded)
     self.assertTrue(tr.is_inf)
     self.assertEqual(math.inf, tr.hr_length_org)
     self.assertEqual(math.inf, tr.hr_length)
     self.assertEqual(f"- ~ {now.strftime('%m-%d')}", tr.expr_period_short)
     self.assertAlmostEquals(tr.end_time_seconds, time_to_seconds(now), 0)
     prd = tr.get_periods()
     self.assertListEqual([tr], prd)
示例#12
0
    def get(self, request, *args, **kwargs):
        u_data = RootUserManager.get_root_data_api_token(
            self.request.COOKIES[keys.Cookies.USER_TOKEN])
        excde_list = ExecodeManager.get_queued_execodes(u_data.model.id)

        return render_template(
            self.request, _("Account Home"), "account/main.html", {
                "root_data":
                u_data.model,
                "api_user_data":
                u_data.model_api,
                "execode_list":
                excde_list,
                "onplat_user_data_list":
                u_data.model_onplat_list,
                "reg_time_str":
                t_delta_str(now_utc_aware() - u_data.model.id.generation_time)
            })
示例#13
0
    def get_message_frequency(self,
                              channel_oid: ObjectId,
                              within_mins: Union[float, int] = 720) -> float:
        """Get message frequency in terms of seconds per message."""
        rct_msg_count = self.count_documents({
            MessageRecordModel.ChannelOid.key:
            channel_oid,
            OID_KEY: {
                "$gt":
                ObjectId.from_datetime(now_utc_aware() -
                                       timedelta(minutes=within_mins))
            }
        })

        if rct_msg_count > 0:
            return (within_mins * 60) / rct_msg_count
        else:
            return 0.0
示例#14
0
    def _delete_recent_module_(self, keyword):
        """Delete modules which is created and marked inactive within `Bot.AutoReply.DeleteDataMins` minutes."""
        now = now_utc_aware()

        self.delete_many(
            {
                OID_KEY: {
                    "$lt":
                    ObjectId.from_datetime(now),
                    "$gt":
                    ObjectId.from_datetime(now - timedelta(
                        minutes=Bot.AutoReply.DeleteDataMins))
                },
                AutoReplyModuleModel.KEY_KW_CONTENT: keyword,
                AutoReplyModuleModel.Active.key: False
            },
            collation=case_insensitive_collation
            if AutoReply.CaseInsensitive else None)
示例#15
0
    def data_days_collected(collection,
                            filter_,
                            *,
                            hr_range: Optional[int] = None,
                            start: Optional[datetime] = None,
                            end: Optional[datetime] = None):
        """
        Returns the count of days collected in data.

        Notice that this is different from ``days_collected`` in ``__init__()`` because
        this one connects to the database to calculate the actual days collected in the filtered dataset
        while the one in ``__init__()`` will not be checked and assume that it is true.

        ``hr_range`` will be ignored if both ``start`` and ``end`` is specified.
        """
        trange = TimeRange(range_hr=hr_range,
                           start=start,
                           end=end,
                           end_autofill_now=False)

        if trange.is_inf:
            oldest = collection.find_one(filter_,
                                         sort=[(OID_KEY, pymongo.ASCENDING)])

            if not oldest:
                return HourlyResult.DAYS_NONE

            now = now_utc_aware()

            if start:
                start = make_tz_aware(start)

            if start and start > now:
                return HourlyResult.DAYS_NONE

            if end:
                end = make_tz_aware(end)

            return max(
                ((end or now) -
                 ObjectId(oldest[OID_KEY]).generation_time).total_seconds() /
                86400, 0)
        else:
            return trange.hr_length / 24
示例#16
0
 def test_nfill_range_only(self):
     tr = TimeRange(range_hr=120, end_autofill_now=False)
     now = now_utc_aware()
     expected_start = now - timedelta(hours=120)
     self.assertAlmostEquals(
         0, abs((expected_start - tr.start).total_seconds()), 0)
     self.assertAlmostEquals(
         0, abs((expected_start - tr.start_org).total_seconds()), 0)
     self.assertIsNone(tr.end)
     self.assertIsNone(tr.tzinfo_)
     self.assertFalse(tr.expandable)
     self.assertFalse(tr.expanded)
     self.assertTrue(tr.is_inf)
     self.assertAlmostEquals(120, tr.hr_length_org, 0)
     self.assertAlmostEquals(120, tr.hr_length, 0)
     self.assertEqual(f"{expected_start.strftime('%m-%d')} ~ -",
                      tr.expr_period_short)
     self.assertAlmostEquals(tr.end_time_seconds, time_to_seconds(now), 0)
     prd = tr.get_periods()
     self.assertListEqual([tr], prd)
示例#17
0
    def __post_init__(self):
        msg_count = len(self.data)

        proc_secs = [msg.model.process_time_secs for msg in self.data]
        self.avg_processing_secs = sum(proc_secs) / msg_count

        counter = {mt: 0 for mt in MessageType}
        for entry in self.data:
            counter[MessageType.cast(entry.model.message_type)] += 1
        self.msg_composition = HandledMessagesComposition(counter)

        self.unique_sender_count = len(
            {data.model.user_root_oid
             for data in self.data})

        if self.data:
            self.message_frequency = \
                (now_utc_aware() - self.data[-1].model.id.generation_time).total_seconds() / msg_count
        else:
            self.message_frequency = 0.0
示例#18
0
    def get_current(
            self,
            user_oid: ObjectId,
            source_channel_oid: ObjectId,
            *,
            update_expiry: bool = True) -> Optional[RemoteControlEntryModel]:
        """
        Get the current activating remote control.
        Upon found, update the expiry time if `update_expiry` is set to `True`.

        :return: Current activating remote control. `None` if not found.
        """
        filter_ = {
            RemoteControlEntryModel.UserOid.key: user_oid,
            RemoteControlEntryModel.SourceChannelOid.key: source_channel_oid
        }
        now = now_utc_aware()

        ret = self.find_one_casted(filter_, parse_cls=RemoteControlEntryModel)

        # Entry not found
        if not ret:
            return None

        if update_expiry:
            # Update the object to be returned and the object in the database
            new_expiry = now + timedelta(
                seconds=Bot.RemoteControl.IdleDeactivateSeconds)

            ret.expiry_utc = new_expiry
            self.update_one_async(
                filter_,
                {"$set": {
                    RemoteControlEntryModel.ExpiryUtc.key: new_expiry
                }})

        # Ensure TTL - TTL may not work under millisecond scale
        if now < ret.expiry_utc:
            return RemoteControlEntryModel.cast_model(ret)
        else:
            return None
示例#19
0
 def test_fill_range_hr_0(self):
     tr = TimeRange(range_hr=0)
     now = now_utc_aware()
     expected_start_end = now
     self.assertAlmostEquals(
         0, abs((expected_start_end - tr.start).total_seconds()), 0)
     self.assertAlmostEquals(
         0, abs((expected_start_end - tr.start_org).total_seconds()), 0)
     self.assertAlmostEquals(0, abs((tr.end - now).total_seconds()), 0)
     self.assertIsNone(tr.tzinfo_)
     self.assertTrue(tr.expandable)
     self.assertFalse(tr.expanded)
     self.assertFalse(tr.is_inf)
     self.assertAlmostEquals(0, tr.hr_length_org, 0)
     self.assertAlmostEquals(0, tr.hr_length, 0)
     self.assertEqual(
         f"{expected_start_end.strftime('%m-%d')} ~ {now.strftime('%m-%d')}",
         tr.expr_period_short)
     self.assertAlmostEquals(tr.end_time_seconds, time_to_seconds(now), 0)
     prd = tr.get_periods()
     self.assertListEqual([tr], prd)
示例#20
0
    def get_time_up(self, channel_oid: ObjectId) -> List[TimerModel]:
        now = now_utc_aware()

        filter_ = {
            TimerModel.ChannelOid.key: channel_oid,
            TimerModel.TargetTime.key: {
                "$lt": now
            },
            TimerModel.NotifiedExpired.key: False
        }

        ret = list(
            self.find_cursor_with_count(filter_, parse_cls=TimerModel).sort([
                (TimerModel.TargetTime.key, pymongo.ASCENDING)
            ]))

        self.update_many_async(
            filter_, {"$set": {
                TimerModel.NotifiedExpired.key: True
            }})

        return ret
示例#21
0
    def test_nfill_start(self):
        start_dt = datetime(2020, 4, 4, 1, 1, 1, tzinfo=pytz.UTC)

        tr = TimeRange(start=start_dt, end_autofill_now=False)
        now = now_utc_aware()
        hr_diff = (now - start_dt).total_seconds() / 3600
        self.assertAlmostEquals(0, abs((start_dt - tr.start).total_seconds()),
                                0)
        self.assertAlmostEquals(0,
                                abs((start_dt - tr.start_org).total_seconds()),
                                0)
        self.assertIsNone(tr.end)
        self.assertIsNone(tr.tzinfo_)
        self.assertFalse(tr.expandable)
        self.assertFalse(tr.expanded)
        self.assertTrue(tr.is_inf)
        self.assertAlmostEquals(hr_diff, tr.hr_length_org, 0)
        self.assertAlmostEquals(hr_diff, tr.hr_length, 0)
        self.assertEqual(f"{start_dt.strftime('%m-%d')} ~ -",
                         tr.expr_period_short)
        self.assertAlmostEquals(tr.end_time_seconds, time_to_seconds(now), 0)
        prd = tr.get_periods()
        self.assertListEqual([tr], prd)
示例#22
0
    def to_string(self, user_model):
        now = now_utc_aware()
        tzinfo = user_model.config.tzinfo

        ret = []

        if self.future:
            for tmr in self.future:
                ret.append(
                    Timer.FUTURE.format(event=tmr.title,
                                        diff=t_delta_str(
                                            tmr.get_target_time_diff(now)),
                                        time=localtime(tmr.target_time,
                                                       tzinfo)))
            ret.append("")  # Separator

        if self.past_continue:
            for tmr in self.past_continue:
                ret.append(
                    Timer.PAST_CONTINUE.format(
                        event=tmr.title,
                        diff=t_delta_str(tmr.get_target_time_diff(now)),
                        time=localtime(tmr.target_time, tzinfo)))
            ret.append("")  # Separator

        if self.past_done:
            for tmr in self.past_done:
                ret.append(
                    Timer.PAST_DONE.format(event=tmr.title,
                                           time=localtime(
                                               tmr.target_time, tzinfo)))

        # Take out the final separator
        if ret and ret[-1] == "":
            ret = ret[:-1]

        return "\n".join(ret)
示例#23
0
def record_boot_dt():
    boot_dt_utc["dt"] = now_utc_aware()