import os import time from collections import defaultdict from datetime import datetime from pathlib import Path from typing import Any import httpx import pypinyin import pytz from zhenxun.configs.config import Config from zhenxun.services.log import logger class ResourceDirManager: """ 临时文件管理器 """ temp_path = [] @classmethod def __tree_append(cls, path: Path): """递归添加文件夹 参数: path: 文件夹路径 """ for f in os.listdir(path): file = path / f if file.is_dir(): if file not in cls.temp_path: cls.temp_path.append(file) logger.debug(f"添加临时文件夹: {path}") cls.__tree_append(file) @classmethod def add_temp_dir(cls, path: str | Path, tree: bool = False): """添加临时清理文件夹,这些文件夹会被自动清理 参数: path: 文件夹路径 tree: 是否递归添加文件夹 """ if isinstance(path, str): path = Path(path) if path not in cls.temp_path: cls.temp_path.append(path) logger.debug(f"添加临时文件夹: {path}") if tree: cls.__tree_append(path) class CountLimiter: """ 每日调用命令次数限制 """ 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: day = datetime.now(self.tz).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 class UserBlockLimiter: """ 检测用户是否正在调用命令 """ def __init__(self): self.flag_data = defaultdict(bool) self.time = time.time() def set_true(self, key: Any): self.time = time.time() self.flag_data[key] = True def set_false(self, key: Any): self.flag_data[key] = False def check(self, key: Any) -> bool: if time.time() - self.time > 30: self.set_false(key) return not self.flag_data[key] class FreqLimiter: """ 命令冷却,检测用户是否处于冷却状态 """ def __init__(self, default_cd_seconds: int): self.next_time = defaultdict(float) self.default_cd = default_cd_seconds def check(self, key: Any) -> bool: return time.time() >= self.next_time[key] def start_cd(self, key: Any, cd_time: int = 0): self.next_time[key] = time.time() + ( cd_time if cd_time > 0 else self.default_cd ) def left_time(self, key: Any) -> float: return self.next_time[key] - time.time() def cn2py(word: str) -> str: """将字符串转化为拼音 参数: word: 文本 """ temp = "" for i in pypinyin.pinyin(word, style=pypinyin.NORMAL): temp += "".join(i) return temp async def get_user_avatar(uid: int | str) -> bytes | None: """快捷获取用户头像 参数: uid: 用户id """ url = f"http://q1.qlogo.cn/g?b=qq&nk={uid}&s=160" async with httpx.AsyncClient() as client: for _ in range(3): try: return (await client.get(url)).content except Exception as e: logger.error("获取用户头像错误", "Util", target=uid) return None async def get_group_avatar(gid: int | str) -> bytes | None: """快捷获取用群头像 参数: gid: 群号 """ url = f"http://p.qlogo.cn/gh/{gid}/{gid}/640/" async with httpx.AsyncClient() as client: for _ in range(3): try: return (await client.get(url)).content except Exception as e: logger.error("获取群头像错误", "Util", target=gid) return None def change_pixiv_image_links( url: str, size: str | None = None, nginx_url: str | None = None ) -> str: """根据配置改变图片大小和反代链接 参数: url: 图片原图链接 size: 模式 nginx_url: 反代 返回: str: url """ if size == "master": img_sp = url.rsplit(".", maxsplit=1) url = img_sp[0] img_type = img_sp[1] url = url.replace("original", "master") + f"_master1200.{img_type}" if not nginx_url: nginx_url = Config.get_config("pixiv", "PIXIV_NGINX_URL") if nginx_url: url = ( url.replace("i.pximg.net", nginx_url) .replace("i.pixiv.cat", nginx_url) .replace("_webp", "") ) return url def change_img_md5(path_file: str | Path) -> bool: """改变图片MD5 参数: path_file: 图片路径 返还: bool: 是否修改成功 """ try: with open(path_file, "a") as f: f.write(str(int(time.time() * 1000))) return True except Exception as e: logger.warning(f"改变图片MD5错误 Path:{path_file}", e=e) return False def is_valid_date(date_text: str, separator: str = "-") -> bool: """日期是否合法 参数: date_text: 日期 separator: 分隔符 返回: bool: 日期是否合法 """ try: datetime.strptime(date_text, f"%Y{separator}%m{separator}%d") return True except ValueError: return False