Lib.utils.QQDataCacher

QQ数据缓存

  1"""
  2QQ数据缓存
  3"""
  4import time
  5import threading
  6
  7from ..core import OnebotAPI, ConfigManager
  8from . import Logger
  9
 10NotFetched = type("NotFetched", (), {"__getattr__": lambda _, __: NotFetched,
 11                                     "__repr__": lambda _: "NotFetched",
 12                                     "__bool__": lambda _: False})
 13api = OnebotAPI.api
 14logger = Logger.get_logger()
 15
 16if ConfigManager.GlobalConfig().qq_data_cache.enable:
 17    expire_time = ConfigManager.GlobalConfig().qq_data_cache.expire_time
 18else:
 19    expire_time = 0
 20
 21
 22class QQDataItem:
 23    """
 24    QQ数据缓存类
 25    """
 26
 27    def __init__(self):
 28        self._data = NotFetched  # 数据
 29        self.last_update = time.time()  # 最后刷新时间
 30        self.last_use = -1  # 最后被使用时间(数据被使用)
 31
 32    def refresh_cache(self):
 33        """
 34        刷新缓存
 35        Returns:
 36            None
 37        """
 38        self.last_update = time.time()
 39
 40
 41class UserData(QQDataItem):
 42    """
 43    QQ用户数据缓存类
 44    """
 45
 46    def __init__(
 47            self,
 48            user_id: int,
 49            nickname: str = NotFetched,
 50            sex: str = NotFetched,
 51            age: int = NotFetched,
 52            is_friend: bool = NotFetched,
 53            remark: str | None = NotFetched  # 此值仅在是好友的时候会存在
 54    ):
 55        super().__init__()
 56        self._user_id = user_id
 57        self._data = {
 58            "user_id": user_id,
 59            "nickname": nickname,
 60            "sex": sex,
 61            "age": age,
 62            "is_friend": is_friend,
 63            "remark": remark
 64        }
 65
 66    def refresh_cache(self):
 67        """
 68        刷新缓存
 69        Returns:
 70            None
 71        """
 72        if int(self._user_id) <= 0:
 73            logger.warn(f"获取用户{self._user_id}缓存信息失败: user_id小于等于0")
 74            return
 75        try:
 76            data = api.get_stranger_info(self._user_id)
 77            for k in data:
 78                self._data[k] = data[k]
 79            self._data["is_friend"] = NotFetched
 80            self._data["remark"] = NotFetched
 81        except Exception as e:
 82            logger.warn(f"获取用户{self._user_id}缓存信息失败: {repr(e)}")
 83            return
 84
 85    def __getattr__(self, item):
 86        if item == "_data" or item == "data":
 87            return self._data
 88
 89        if item in ["remark", "is_friend"] and self._data.get(item) != NotFetched:
 90            try:
 91                res = api.get_friend_list()
 92                for friend in res:
 93                    if friend["user_id"] == self._user_id:
 94                        self._data["remark"] = friend["remark"]
 95                        self._data["is_friend"] = True
 96                        break
 97                else:
 98                    self._data["is_friend"] = False
 99                    self._data["remark"] = None
100            except Exception as e:
101                logger.warn(f"获取用户{self._user_id}是否为好友失败: {repr(e)}")
102                return None
103
104        if self._data.get(item) == NotFetched or time.time() - self.last_update > expire_time:
105            self.refresh_cache()
106
107        if self._data.get(item) == NotFetched:
108            return None
109
110        if item in self._data:
111            self.last_use = time.time()
112
113        return self._data.get(item)
114
115    def get_nickname(self) -> str:
116        """
117        获取昵称(如果有备注名优先返回备注名)
118        Returns:
119            昵称
120        """
121        return self.remark or self.nickname
122
123    def __repr__(self):
124        return f"UserData(user_id={self._user_id})"
125
126
127class GroupMemberData(QQDataItem):
128    """
129    QQ群成员数据缓存类
130    """
131
132    def __init__(
133            self,
134            group_id: int,
135            user_id: int,
136            nickname: str = NotFetched,
137            card: str = NotFetched,
138            sex: str = NotFetched,
139            age: int = NotFetched,
140            area: str = NotFetched,
141            join_time: int = NotFetched,
142            last_sent_time: int = NotFetched,
143            level: str = NotFetched,
144            role: str = NotFetched,
145            unfriendly: bool = NotFetched,
146            title: str = NotFetched,
147            title_expire_time: int = NotFetched,
148            card_changeable: bool = NotFetched,
149    ):
150        super().__init__()
151        self._group_id = group_id
152        self._user_id = user_id
153        self._data = {
154            "group_id": group_id,
155            "user_id": user_id,
156            "nickname": nickname,
157            "card": card,
158            "sex": sex,
159            "age": age,
160            "area": area,
161            "join_time": join_time,
162            "last_sent_time": last_sent_time,
163            "level": level,
164            "role": role,
165            "unfriendly": unfriendly,
166            "title": title,
167            "title_expire_time": title_expire_time,
168            "card_changeable": card_changeable,
169        }
170
171    def refresh_cache(self):
172        """
173        刷新缓存
174        Returns:
175            None
176        """
177        if int(self._group_id) <= 0 or int(self._user_id) <= 0:
178            logger.warn(f"获取群{self._group_id}中成员{self._user_id}缓存信息失败: group_id或user_id小于等于0")
179            return
180        try:
181            data = api.get_group_member_info(self._group_id, self._user_id, no_cache=True)
182            for k in data:
183                self._data[k] = data[k]
184        except Exception as e:
185            logger.warn(f"获取群{self._group_id}中成员{self._user_id}缓存信息失败: {repr(e)}")
186            user_data = get_user_info(self._user_id)
187            self._data["nickname"] = user_data.nickname if user_data.nickname else NotFetched
188            self._data["sex"] = user_data.sex if user_data.sex else NotFetched
189            self._data["age"] = user_data.age if user_data.age else NotFetched
190        super().refresh_cache()
191
192    def __getattr__(self, item):
193        if item == "_data" or item == "data":
194            return self._data
195
196        if self._data.get(item) == NotFetched or time.time() - self.last_update > expire_time:
197            self.refresh_cache()
198
199        if self._data.get(item) == NotFetched:
200            return None
201
202        if item in self._data:
203            self.last_use = time.time()
204
205        return self._data.get(item)
206
207    def __repr__(self):
208        return f"GroupMemberData(group_id={self._group_id}, user_id={self._user_id})"
209
210    def get_nickname(self):
211        """
212        获取群名片(如果有群名片优先返回群名片)
213        Returns:
214            群名片
215        """
216        return self.card or self.nickname
217
218
219class GroupData(QQDataItem):
220    """
221    QQ群数据缓存类
222    """
223
224    def __init__(
225            self,
226            group_id: int,
227            group_name: str = NotFetched,
228            member_count: int = NotFetched,
229            max_member_count: int = NotFetched
230    ):
231        super().__init__()
232        self._group_id = group_id
233        self._data = {
234            "group_id": group_id,
235            "group_name": group_name,
236            "member_count": member_count,
237            "max_member_count": max_member_count,
238            "group_member_list": NotFetched
239        }
240
241    def refresh_cache(self):
242        """
243        刷新缓存
244        Returns:
245            None
246        """
247        if int(self._group_id) <= 0:
248            logger.warn(f"获取群{self._group_id}缓存信息失败: group_id小于等于0")
249            return
250        try:
251            data = api.get_group_info(group_id=self._group_id, no_cache=True)
252            for k in data:
253                self._data[k] = data[k]
254            self._data["group_member_list"] = NotFetched
255        except Exception as e:
256            logger.warn(f"获取群{self._group_id}缓存信息失败: {repr(e)}")
257            return
258        super().refresh_cache()
259
260    def __getattr__(self, item):
261        if item == "_data" or item == "data":
262            return self._data
263
264        if item == "group_member_list" and self._data.get(item) == NotFetched:
265            try:
266                res = api.get_group_member_list(self._group_id)
267                member_list = [GroupMemberData(**{k: (v if v is not None else NotFetched)
268                                                  for k, v in member.items()})
269                               for member in res]
270                self._data[item] = member_list
271            except Exception as e:
272                logger.warn(f"获取群{self._group_id}成员列表信息失败: {repr(e)}")
273                return
274
275        if self._data.get(item) == NotFetched or time.time() - self.last_update > expire_time:
276            self.refresh_cache()
277
278        if self._data.get(item) == NotFetched:
279            return None
280
281        if item in self._data:
282            self.last_use = time.time()
283
284        return self._data.get(item)
285
286    def __repr__(self):
287        return f"GroupData(group_id={self._group_id})"
288
289
290group_info = {}
291group_member_info = {}
292user_info = {}
293
294group_info_lock = threading.Lock()
295group_member_info_lock = threading.Lock()
296user_info_lock = threading.Lock()
297
298max_cache_size = ConfigManager.GlobalConfig().qq_data_cache.max_cache_size
299expire_time = expire_time
300
301
302def get_user_info(user_id: int, *args, **kwargs) -> UserData:
303    """
304    获取用户信息
305    Args:
306        user_id: 用户ID
307    Returns:
308        None
309    """
310    with user_info_lock:
311        if user_id not in user_info:
312            data = UserData(user_id, *args, **kwargs)
313            user_info[user_id] = data
314
315        data = user_info[user_id]
316        return data
317
318
319def get_group_info(group_id: int, *args, **kwargs) -> GroupData:
320    """
321    获取群信息
322    Args:
323        group_id: 群号
324    Returns:
325        None
326    """
327    with group_info_lock:
328        if group_id not in group_info:
329            data = GroupData(group_id, *args, **kwargs)
330            group_info[group_id] = data
331
332        data = group_info[group_id]
333        return data
334
335
336def get_group_member_info(group_id: int, user_id: int, *args, **kwargs) -> GroupMemberData:
337    """
338    获取群成员信息
339    Args:
340        group_id: 群号
341        user_id: 用户ID
342    Returns:
343        None
344    """
345    with group_member_info_lock:
346        if group_id not in group_member_info:
347            group_member_info[group_id] = {}
348
349        if user_id not in group_member_info[group_id]:
350            data = GroupMemberData(group_id, user_id, *args, **kwargs)
351            group_member_info[group_id][user_id] = data
352
353        data = group_member_info[group_id][user_id]
354        return data
355
356
357def garbage_collection():
358    """
359    垃圾回收
360    Returns:
361        None
362    """
363    counter = 0
364
365    with group_member_info_lock:
366        for k in list(group_member_info.keys()):
367            group_member_items = list(zip(group_member_info[k].keys(), group_member_info[k].values()))
368            max_last_use_time = max([item[1].last_use for item in group_member_items])
369
370            if max_last_use_time < time.time() - expire_time * 2:
371                del group_member_info[k]
372                counter += 1
373                continue
374
375            group_member_items.sort(key=lambda x: x[1].last_use)
376            if len(group_member_items) > max_cache_size * (2 / 3):
377                for user_id, _ in group_member_items[:int(max_cache_size * (1 / 3))]:
378                    del group_member_info[k][user_id]
379                    counter += 1
380            del group_member_items, max_last_use_time
381
382    with group_info_lock:
383        group_items = list(zip(group_info.keys(), group_info.values()))
384        group_items.sort(key=lambda x: x[1].last_use)
385        if len(group_items) > max_cache_size * (2 / 3):
386            for group_id, _ in group_items[:int(max_cache_size * (1 / 3))]:
387                del group_info[group_id]
388                counter += 1
389        del group_items
390
391    with user_info_lock:
392        user_items = list(zip(user_info.keys(), user_info.values()))
393        user_items.sort(key=lambda x: x[1].last_use)
394        if len(user_items) > max_cache_size * (2 / 3):
395            for user_id, _ in user_items[:int(max_cache_size * (1 / 3))]:
396                del user_info[user_id]
397                counter += 1
398        del user_items
399
400    return counter
401
402
403def scheduled_garbage_collection():
404    """
405    定时垃圾回收
406    Returns:
407        None
408    """
409    t = 0
410    while True:
411        time.sleep(60)
412        t += 1
413        if (
414                t > 4 or (
415                t > 1 and (
416                len(group_info) > max_cache_size or
417                len(user_info) > max_cache_size or
418                len(group_member_info) > max_cache_size
419        )
420        )
421        ):
422            t = 0
423            logger.debug("QQ数据缓存清理开始...")
424            try:
425                counter = garbage_collection()
426                logger.debug(f"QQ数据缓存清理完成,共清理了 {counter} 项信息。")
427            except Exception as e:
428                logger.warn(f"QQ数据缓存清理时出现异常: {repr(e)}")
429
430
431# 启动垃圾回收线程
432threading.Thread(target=scheduled_garbage_collection, daemon=True).start()
class NotFetched:
logger = <RootLogger root (INFO)>
class QQDataItem:
23class QQDataItem:
24    """
25    QQ数据缓存类
26    """
27
28    def __init__(self):
29        self._data = NotFetched  # 数据
30        self.last_update = time.time()  # 最后刷新时间
31        self.last_use = -1  # 最后被使用时间(数据被使用)
32
33    def refresh_cache(self):
34        """
35        刷新缓存
36        Returns:
37            None
38        """
39        self.last_update = time.time()

QQ数据缓存类

last_update: float
last_use: float
def refresh_cache(self):
33    def refresh_cache(self):
34        """
35        刷新缓存
36        Returns:
37            None
38        """
39        self.last_update = time.time()

刷新缓存

Returns:

None

class UserData(QQDataItem):
 42class UserData(QQDataItem):
 43    """
 44    QQ用户数据缓存类
 45    """
 46
 47    def __init__(
 48            self,
 49            user_id: int,
 50            nickname: str = NotFetched,
 51            sex: str = NotFetched,
 52            age: int = NotFetched,
 53            is_friend: bool = NotFetched,
 54            remark: str | None = NotFetched  # 此值仅在是好友的时候会存在
 55    ):
 56        super().__init__()
 57        self._user_id = user_id
 58        self._data = {
 59            "user_id": user_id,
 60            "nickname": nickname,
 61            "sex": sex,
 62            "age": age,
 63            "is_friend": is_friend,
 64            "remark": remark
 65        }
 66
 67    def refresh_cache(self):
 68        """
 69        刷新缓存
 70        Returns:
 71            None
 72        """
 73        if int(self._user_id) <= 0:
 74            logger.warn(f"获取用户{self._user_id}缓存信息失败: user_id小于等于0")
 75            return
 76        try:
 77            data = api.get_stranger_info(self._user_id)
 78            for k in data:
 79                self._data[k] = data[k]
 80            self._data["is_friend"] = NotFetched
 81            self._data["remark"] = NotFetched
 82        except Exception as e:
 83            logger.warn(f"获取用户{self._user_id}缓存信息失败: {repr(e)}")
 84            return
 85
 86    def __getattr__(self, item):
 87        if item == "_data" or item == "data":
 88            return self._data
 89
 90        if item in ["remark", "is_friend"] and self._data.get(item) != NotFetched:
 91            try:
 92                res = api.get_friend_list()
 93                for friend in res:
 94                    if friend["user_id"] == self._user_id:
 95                        self._data["remark"] = friend["remark"]
 96                        self._data["is_friend"] = True
 97                        break
 98                else:
 99                    self._data["is_friend"] = False
100                    self._data["remark"] = None
101            except Exception as e:
102                logger.warn(f"获取用户{self._user_id}是否为好友失败: {repr(e)}")
103                return None
104
105        if self._data.get(item) == NotFetched or time.time() - self.last_update > expire_time:
106            self.refresh_cache()
107
108        if self._data.get(item) == NotFetched:
109            return None
110
111        if item in self._data:
112            self.last_use = time.time()
113
114        return self._data.get(item)
115
116    def get_nickname(self) -> str:
117        """
118        获取昵称(如果有备注名优先返回备注名)
119        Returns:
120            昵称
121        """
122        return self.remark or self.nickname
123
124    def __repr__(self):
125        return f"UserData(user_id={self._user_id})"

QQ用户数据缓存类

UserData( user_id: int, nickname: str = <class 'NotFetched'>, sex: str = <class 'NotFetched'>, age: int = <class 'NotFetched'>, is_friend: bool = <class 'NotFetched'>, remark: str | None = <class 'NotFetched'>)
47    def __init__(
48            self,
49            user_id: int,
50            nickname: str = NotFetched,
51            sex: str = NotFetched,
52            age: int = NotFetched,
53            is_friend: bool = NotFetched,
54            remark: str | None = NotFetched  # 此值仅在是好友的时候会存在
55    ):
56        super().__init__()
57        self._user_id = user_id
58        self._data = {
59            "user_id": user_id,
60            "nickname": nickname,
61            "sex": sex,
62            "age": age,
63            "is_friend": is_friend,
64            "remark": remark
65        }
def refresh_cache(self) -> None:
67    def refresh_cache(self):
68        """
69        刷新缓存
70        Returns:
71            None
72        """
73        if int(self._user_id) <= 0:
74            logger.warn(f"获取用户{self._user_id}缓存信息失败: user_id小于等于0")
75            return
76        try:
77            data = api.get_stranger_info(self._user_id)
78            for k in data:
79                self._data[k] = data[k]
80            self._data["is_friend"] = NotFetched
81            self._data["remark"] = NotFetched
82        except Exception as e:
83            logger.warn(f"获取用户{self._user_id}缓存信息失败: {repr(e)}")
84            return

刷新缓存

Returns:

None

def get_nickname(self) -> str:
116    def get_nickname(self) -> str:
117        """
118        获取昵称(如果有备注名优先返回备注名)
119        Returns:
120            昵称
121        """
122        return self.remark or self.nickname

获取昵称(如果有备注名优先返回备注名)

Returns:

昵称

Inherited Members
QQDataItem
last_update
last_use
class GroupMemberData(QQDataItem):
128class GroupMemberData(QQDataItem):
129    """
130    QQ群成员数据缓存类
131    """
132
133    def __init__(
134            self,
135            group_id: int,
136            user_id: int,
137            nickname: str = NotFetched,
138            card: str = NotFetched,
139            sex: str = NotFetched,
140            age: int = NotFetched,
141            area: str = NotFetched,
142            join_time: int = NotFetched,
143            last_sent_time: int = NotFetched,
144            level: str = NotFetched,
145            role: str = NotFetched,
146            unfriendly: bool = NotFetched,
147            title: str = NotFetched,
148            title_expire_time: int = NotFetched,
149            card_changeable: bool = NotFetched,
150    ):
151        super().__init__()
152        self._group_id = group_id
153        self._user_id = user_id
154        self._data = {
155            "group_id": group_id,
156            "user_id": user_id,
157            "nickname": nickname,
158            "card": card,
159            "sex": sex,
160            "age": age,
161            "area": area,
162            "join_time": join_time,
163            "last_sent_time": last_sent_time,
164            "level": level,
165            "role": role,
166            "unfriendly": unfriendly,
167            "title": title,
168            "title_expire_time": title_expire_time,
169            "card_changeable": card_changeable,
170        }
171
172    def refresh_cache(self):
173        """
174        刷新缓存
175        Returns:
176            None
177        """
178        if int(self._group_id) <= 0 or int(self._user_id) <= 0:
179            logger.warn(f"获取群{self._group_id}中成员{self._user_id}缓存信息失败: group_id或user_id小于等于0")
180            return
181        try:
182            data = api.get_group_member_info(self._group_id, self._user_id, no_cache=True)
183            for k in data:
184                self._data[k] = data[k]
185        except Exception as e:
186            logger.warn(f"获取群{self._group_id}中成员{self._user_id}缓存信息失败: {repr(e)}")
187            user_data = get_user_info(self._user_id)
188            self._data["nickname"] = user_data.nickname if user_data.nickname else NotFetched
189            self._data["sex"] = user_data.sex if user_data.sex else NotFetched
190            self._data["age"] = user_data.age if user_data.age else NotFetched
191        super().refresh_cache()
192
193    def __getattr__(self, item):
194        if item == "_data" or item == "data":
195            return self._data
196
197        if self._data.get(item) == NotFetched or time.time() - self.last_update > expire_time:
198            self.refresh_cache()
199
200        if self._data.get(item) == NotFetched:
201            return None
202
203        if item in self._data:
204            self.last_use = time.time()
205
206        return self._data.get(item)
207
208    def __repr__(self):
209        return f"GroupMemberData(group_id={self._group_id}, user_id={self._user_id})"
210
211    def get_nickname(self):
212        """
213        获取群名片(如果有群名片优先返回群名片)
214        Returns:
215            群名片
216        """
217        return self.card or self.nickname

QQ群成员数据缓存类

GroupMemberData( group_id: int, user_id: int, nickname: str = <class 'NotFetched'>, card: str = <class 'NotFetched'>, sex: str = <class 'NotFetched'>, age: int = <class 'NotFetched'>, area: str = <class 'NotFetched'>, join_time: int = <class 'NotFetched'>, last_sent_time: int = <class 'NotFetched'>, level: str = <class 'NotFetched'>, role: str = <class 'NotFetched'>, unfriendly: bool = <class 'NotFetched'>, title: str = <class 'NotFetched'>, title_expire_time: int = <class 'NotFetched'>, card_changeable: bool = <class 'NotFetched'>)
133    def __init__(
134            self,
135            group_id: int,
136            user_id: int,
137            nickname: str = NotFetched,
138            card: str = NotFetched,
139            sex: str = NotFetched,
140            age: int = NotFetched,
141            area: str = NotFetched,
142            join_time: int = NotFetched,
143            last_sent_time: int = NotFetched,
144            level: str = NotFetched,
145            role: str = NotFetched,
146            unfriendly: bool = NotFetched,
147            title: str = NotFetched,
148            title_expire_time: int = NotFetched,
149            card_changeable: bool = NotFetched,
150    ):
151        super().__init__()
152        self._group_id = group_id
153        self._user_id = user_id
154        self._data = {
155            "group_id": group_id,
156            "user_id": user_id,
157            "nickname": nickname,
158            "card": card,
159            "sex": sex,
160            "age": age,
161            "area": area,
162            "join_time": join_time,
163            "last_sent_time": last_sent_time,
164            "level": level,
165            "role": role,
166            "unfriendly": unfriendly,
167            "title": title,
168            "title_expire_time": title_expire_time,
169            "card_changeable": card_changeable,
170        }
def refresh_cache(self) -> None:
172    def refresh_cache(self):
173        """
174        刷新缓存
175        Returns:
176            None
177        """
178        if int(self._group_id) <= 0 or int(self._user_id) <= 0:
179            logger.warn(f"获取群{self._group_id}中成员{self._user_id}缓存信息失败: group_id或user_id小于等于0")
180            return
181        try:
182            data = api.get_group_member_info(self._group_id, self._user_id, no_cache=True)
183            for k in data:
184                self._data[k] = data[k]
185        except Exception as e:
186            logger.warn(f"获取群{self._group_id}中成员{self._user_id}缓存信息失败: {repr(e)}")
187            user_data = get_user_info(self._user_id)
188            self._data["nickname"] = user_data.nickname if user_data.nickname else NotFetched
189            self._data["sex"] = user_data.sex if user_data.sex else NotFetched
190            self._data["age"] = user_data.age if user_data.age else NotFetched
191        super().refresh_cache()

刷新缓存

Returns:

None

def get_nickname(self) -> str:
211    def get_nickname(self):
212        """
213        获取群名片(如果有群名片优先返回群名片)
214        Returns:
215            群名片
216        """
217        return self.card or self.nickname

获取群名片(如果有群名片优先返回群名片)

Returns:

群名片

Inherited Members
QQDataItem
last_update
last_use
class GroupData(QQDataItem):
220class GroupData(QQDataItem):
221    """
222    QQ群数据缓存类
223    """
224
225    def __init__(
226            self,
227            group_id: int,
228            group_name: str = NotFetched,
229            member_count: int = NotFetched,
230            max_member_count: int = NotFetched
231    ):
232        super().__init__()
233        self._group_id = group_id
234        self._data = {
235            "group_id": group_id,
236            "group_name": group_name,
237            "member_count": member_count,
238            "max_member_count": max_member_count,
239            "group_member_list": NotFetched
240        }
241
242    def refresh_cache(self):
243        """
244        刷新缓存
245        Returns:
246            None
247        """
248        if int(self._group_id) <= 0:
249            logger.warn(f"获取群{self._group_id}缓存信息失败: group_id小于等于0")
250            return
251        try:
252            data = api.get_group_info(group_id=self._group_id, no_cache=True)
253            for k in data:
254                self._data[k] = data[k]
255            self._data["group_member_list"] = NotFetched
256        except Exception as e:
257            logger.warn(f"获取群{self._group_id}缓存信息失败: {repr(e)}")
258            return
259        super().refresh_cache()
260
261    def __getattr__(self, item):
262        if item == "_data" or item == "data":
263            return self._data
264
265        if item == "group_member_list" and self._data.get(item) == NotFetched:
266            try:
267                res = api.get_group_member_list(self._group_id)
268                member_list = [GroupMemberData(**{k: (v if v is not None else NotFetched)
269                                                  for k, v in member.items()})
270                               for member in res]
271                self._data[item] = member_list
272            except Exception as e:
273                logger.warn(f"获取群{self._group_id}成员列表信息失败: {repr(e)}")
274                return
275
276        if self._data.get(item) == NotFetched or time.time() - self.last_update > expire_time:
277            self.refresh_cache()
278
279        if self._data.get(item) == NotFetched:
280            return None
281
282        if item in self._data:
283            self.last_use = time.time()
284
285        return self._data.get(item)
286
287    def __repr__(self):
288        return f"GroupData(group_id={self._group_id})"

QQ群数据缓存类

GroupData( group_id: int, group_name: str = <class 'NotFetched'>, member_count: int = <class 'NotFetched'>, max_member_count: int = <class 'NotFetched'>)
225    def __init__(
226            self,
227            group_id: int,
228            group_name: str = NotFetched,
229            member_count: int = NotFetched,
230            max_member_count: int = NotFetched
231    ):
232        super().__init__()
233        self._group_id = group_id
234        self._data = {
235            "group_id": group_id,
236            "group_name": group_name,
237            "member_count": member_count,
238            "max_member_count": max_member_count,
239            "group_member_list": NotFetched
240        }
def refresh_cache(self) -> None:
242    def refresh_cache(self):
243        """
244        刷新缓存
245        Returns:
246            None
247        """
248        if int(self._group_id) <= 0:
249            logger.warn(f"获取群{self._group_id}缓存信息失败: group_id小于等于0")
250            return
251        try:
252            data = api.get_group_info(group_id=self._group_id, no_cache=True)
253            for k in data:
254                self._data[k] = data[k]
255            self._data["group_member_list"] = NotFetched
256        except Exception as e:
257            logger.warn(f"获取群{self._group_id}缓存信息失败: {repr(e)}")
258            return
259        super().refresh_cache()

刷新缓存

Returns:

None

Inherited Members
QQDataItem
last_update
last_use
group_info: dict = {}
group_member_info: dict = {}
user_info: dict = {}
group_info_lock = <unlocked _thread.lock object>
group_member_info_lock = <unlocked _thread.lock object>
user_info_lock = <unlocked _thread.lock object>
max_cache_size: int = 500
expire_time: int = 300
def get_user_info(user_id: int, *args, **kwargs) -> UserData:
303def get_user_info(user_id: int, *args, **kwargs) -> UserData:
304    """
305    获取用户信息
306    Args:
307        user_id: 用户ID
308    Returns:
309        None
310    """
311    with user_info_lock:
312        if user_id not in user_info:
313            data = UserData(user_id, *args, **kwargs)
314            user_info[user_id] = data
315
316        data = user_info[user_id]
317        return data

获取用户信息

Arguments:
  • user_id: 用户ID
Returns:

None

def get_group_info(group_id: int, *args, **kwargs) -> GroupData:
320def get_group_info(group_id: int, *args, **kwargs) -> GroupData:
321    """
322    获取群信息
323    Args:
324        group_id: 群号
325    Returns:
326        None
327    """
328    with group_info_lock:
329        if group_id not in group_info:
330            data = GroupData(group_id, *args, **kwargs)
331            group_info[group_id] = data
332
333        data = group_info[group_id]
334        return data

获取群信息

Arguments:
  • group_id: 群号
Returns:

None

def get_group_member_info( group_id: int, user_id: int, *args, **kwargs) -> GroupMemberData:
337def get_group_member_info(group_id: int, user_id: int, *args, **kwargs) -> GroupMemberData:
338    """
339    获取群成员信息
340    Args:
341        group_id: 群号
342        user_id: 用户ID
343    Returns:
344        None
345    """
346    with group_member_info_lock:
347        if group_id not in group_member_info:
348            group_member_info[group_id] = {}
349
350        if user_id not in group_member_info[group_id]:
351            data = GroupMemberData(group_id, user_id, *args, **kwargs)
352            group_member_info[group_id][user_id] = data
353
354        data = group_member_info[group_id][user_id]
355        return data

获取群成员信息

Arguments:
  • group_id: 群号
  • user_id: 用户ID
Returns:

None

def garbage_collection() -> int:
358def garbage_collection():
359    """
360    垃圾回收
361    Returns:
362        None
363    """
364    counter = 0
365
366    with group_member_info_lock:
367        for k in list(group_member_info.keys()):
368            group_member_items = list(zip(group_member_info[k].keys(), group_member_info[k].values()))
369            max_last_use_time = max([item[1].last_use for item in group_member_items])
370
371            if max_last_use_time < time.time() - expire_time * 2:
372                del group_member_info[k]
373                counter += 1
374                continue
375
376            group_member_items.sort(key=lambda x: x[1].last_use)
377            if len(group_member_items) > max_cache_size * (2 / 3):
378                for user_id, _ in group_member_items[:int(max_cache_size * (1 / 3))]:
379                    del group_member_info[k][user_id]
380                    counter += 1
381            del group_member_items, max_last_use_time
382
383    with group_info_lock:
384        group_items = list(zip(group_info.keys(), group_info.values()))
385        group_items.sort(key=lambda x: x[1].last_use)
386        if len(group_items) > max_cache_size * (2 / 3):
387            for group_id, _ in group_items[:int(max_cache_size * (1 / 3))]:
388                del group_info[group_id]
389                counter += 1
390        del group_items
391
392    with user_info_lock:
393        user_items = list(zip(user_info.keys(), user_info.values()))
394        user_items.sort(key=lambda x: x[1].last_use)
395        if len(user_items) > max_cache_size * (2 / 3):
396            for user_id, _ in user_items[:int(max_cache_size * (1 / 3))]:
397                del user_info[user_id]
398                counter += 1
399        del user_items
400
401    return counter

垃圾回收

Returns:

None

def scheduled_garbage_collection() -> None:
404def scheduled_garbage_collection():
405    """
406    定时垃圾回收
407    Returns:
408        None
409    """
410    t = 0
411    while True:
412        time.sleep(60)
413        t += 1
414        if (
415                t > 4 or (
416                t > 1 and (
417                len(group_info) > max_cache_size or
418                len(user_info) > max_cache_size or
419                len(group_member_info) > max_cache_size
420        )
421        )
422        ):
423            t = 0
424            logger.debug("QQ数据缓存清理开始...")
425            try:
426                counter = garbage_collection()
427                logger.debug(f"QQ数据缓存清理完成,共清理了 {counter} 项信息。")
428            except Exception as e:
429                logger.warn(f"QQ数据缓存清理时出现异常: {repr(e)}")

定时垃圾回收

Returns:

None