summaryrefslogtreecommitdiff
path: root/ATRI/utils/limit.py
diff options
context:
space:
mode:
authorKyomotoi <[email protected]>2021-07-08 22:09:00 +0800
committerKyomotoi <[email protected]>2021-07-08 22:09:00 +0800
commitbe2747e4d4b820ca0f1f988d3b77a628da26fe7b (patch)
treee1a59dd79ecd973a7d704568dcdc018f1f1b651a /ATRI/utils/limit.py
parenta4e1b9d1581d756ef79ad063d1c0bd6b2fd13c1d (diff)
downloadATRI-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.py161
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