diff options
author | Kyomotoi <[email protected]> | 2021-07-08 22:09:00 +0800 |
---|---|---|
committer | Kyomotoi <[email protected]> | 2021-07-08 22:09:00 +0800 |
commit | be2747e4d4b820ca0f1f988d3b77a628da26fe7b (patch) | |
tree | e1a59dd79ecd973a7d704568dcdc018f1f1b651a /ATRI/utils/limit.py | |
parent | a4e1b9d1581d756ef79ad063d1c0bd6b2fd13c1d (diff) | |
download | ATRI-be2747e4d4b820ca0f1f988d3b77a628da26fe7b.tar.gz ATRI-be2747e4d4b820ca0f1f988d3b77a628da26fe7b.tar.bz2 ATRI-be2747e4d4b820ca0f1f988d3b77a628da26fe7b.zip |
🔖♻️🐛🔧🔥📝 更新版本:YHN-001-A03
🔖 更新版本至:YHN-001-A03
✨ 新增插件:
- 涩图
- 闲聊(文爱
♻️ 重构:
- Service
- 所有插件
🐛 修复部分小bug
🔧 暂时移除部分设置
🔥 删除:
- 插件:nsfw、wife。日后加回
- 插件 essential 中部分内容
📝 更新README
Diffstat (limited to 'ATRI/utils/limit.py')
-rw-r--r-- | ATRI/utils/limit.py | 161 |
1 files changed, 130 insertions, 31 deletions
diff --git a/ATRI/utils/limit.py b/ATRI/utils/limit.py index bdd1d84..4b3caef 100644 --- a/ATRI/utils/limit.py +++ b/ATRI/utils/limit.py @@ -1,39 +1,138 @@ -import datetime -from random import choice +import time +import pytz +import functools +from threading import RLock +from collections import defaultdict, deque +from datetime import datetime, timedelta -from ATRI.service import Service as sv -from .list import count_list, del_list_aim -from .apscheduler import scheduler, DateTrigger +class LimitBucket: + """ + 限制某功能运行中某段在一定速率下 + """ + + def __init__(self, capacity, fill_rate, is_lock: bool = False) -> None: + """ + :param capacity: 容量总数 + :param fill_rate: 重新装填速率(单位:秒) + """ + self._capacity = float(capacity) + self._tokens = float(capacity) + self._fill_rate = float(fill_rate) + self._last_time = time() + self._is_lock = is_lock + self._lock = RLock() + + def _get_cur_tokens(self): + if self._tokens < self._capacity: + now = time() + delta = self._fill_rate * (now - self._last_time) + self._tokens = min(self._capacity, self._tokens + delta) + self._last_time = now + return self._tokens + + def get_cur_tokens(self): + if self._is_lock: + with self._lock: + return self._get_cur_tokens() + else: + return self._get_cur_tokens() + + def _consume(self, tokens) -> bool: + if tokens <= self.get_cur_tokens(): + self._tokens -= tokens + return True + return False + + def consume(self, tokens): + if self._is_lock: + with self._lock: + return self._consume(tokens) + else: + return self._consume(tokens) -exciting_user_temp = [] -exciting_user = [] +class RateLimiting: + """ + 限制该功能全体速率 + """ + + def __init__(self, max_calls, period=1.0): + if period <= 0: + raise ValueError('Rate limiting period should be > 0') + if max_calls <= 0: + raise ValueError('Rate limiting number of calls should be > 0') -def del_list(user: str) -> None: - global exciting_user - exciting_user = del_list_aim(exciting_user, user) + self.calls = deque() + self.period = period + self.max_calls = max_calls -def is_too_exciting( - user: int, times: int, seconds: float = 0, hours: float = 0, days: float = 0 -) -> bool: - global exciting_user + def __call__(self, func): + @functools.wraps(func) + def wrapped(*args, **kwargs): + with self: + return func(*args, **kwargs) + return wrapped - if user in exciting_user: - return False - else: - if count_list(exciting_user_temp, user) == times: - delta = datetime.timedelta(seconds=seconds, hours=hours, days=days) - trigger = DateTrigger(run_date=datetime.datetime.now() + delta) - - scheduler.add_job( - func=del_list, - trigger=trigger, - args=(user,), - misfire_grace_time=1, - ) - return False - else: - exciting_user_temp.append(user) - return True + def __enter__(self): + if len(self.calls) >= self.max_calls: + time.sleep(self.period - self._timespan) + return self + + def __exit__(self, exc_type, exc_val, exc_tb): + self.calls.append(time.time()) + while self._timespan >= self.period: + self.calls.popleft() + + @property + def _timespan(self): + return self.calls[-1] - self.calls[0] + + +class FreqLimiter: + """ + Copy from: https://github.com/Ice-Cirno/HoshinoBot/blob/master/hoshino/util/__init__.py + """ + + def __init__(self, default_cd_seconds): + self.next_time = defaultdict(float) + self.default_cd = default_cd_seconds + + def check(self, key) -> bool: + return bool(time.time() >= self.next_time[key]) + + def start_cd(self, key, cd_time=0): + self.next_time[key] = time.time() + (cd_time if cd_time > 0 else self.default_cd) + + def left_time(self, key) -> float: + return self.next_time[key] - time.time() + + +class DailyLimiter: + """ + Copy from: https://github.com/Ice-Cirno/HoshinoBot/blob/master/hoshino/util/__init__.py + """ + tz = pytz.timezone('Asia/Shanghai') + + def __init__(self, max_num): + self.today = -1 + self.count = defaultdict(int) + self.max = max_num + + def check(self, key) -> bool: + now = datetime.now(self.tz) + day = (now - timedelta(hours=6)).day + if day != self.today: + self.today = day + self.count.clear() + return bool(self.count[key] < self.max) + + def get_num(self, key): + return self.count[key] + + def increase(self, key, num=1): + self.count[key] += num + + def reset(self, key): + self.count[key] = 0 |