2025-07-14 22:35:29 +08:00
|
|
|
|
from dataclasses import dataclass
|
2025-07-15 17:13:33 +08:00
|
|
|
|
from datetime import datetime
|
2024-02-04 04:18:54 +08:00
|
|
|
|
import os
|
2024-12-10 19:49:11 +08:00
|
|
|
|
from pathlib import Path
|
2024-02-25 03:18:34 +08:00
|
|
|
|
import time
|
2025-07-15 17:13:33 +08:00
|
|
|
|
from typing import ClassVar
|
2024-02-04 04:18:54 +08:00
|
|
|
|
|
|
|
|
|
|
import httpx
|
2025-07-14 22:35:29 +08:00
|
|
|
|
from nonebot_plugin_uninfo import Uninfo
|
2024-03-18 16:10:44 +08:00
|
|
|
|
import pypinyin
|
2024-02-04 04:18:54 +08:00
|
|
|
|
|
2024-08-29 22:01:34 +08:00
|
|
|
|
from zhenxun.configs.config import Config
|
2024-12-10 19:49:11 +08:00
|
|
|
|
from zhenxun.services.log import logger
|
2024-02-04 04:18:54 +08:00
|
|
|
|
|
2025-07-15 17:13:33 +08:00
|
|
|
|
from .limiters import CountLimiter, FreqLimiter, UserBlockLimiter # noqa: F401
|
|
|
|
|
|
|
2024-02-04 04:18:54 +08:00
|
|
|
|
|
2025-07-14 22:35:29 +08:00
|
|
|
|
@dataclass
|
|
|
|
|
|
class EntityIDs:
|
|
|
|
|
|
user_id: str
|
|
|
|
|
|
"""用户id"""
|
|
|
|
|
|
group_id: str | None
|
|
|
|
|
|
"""群组id"""
|
|
|
|
|
|
channel_id: str | None
|
|
|
|
|
|
"""频道id"""
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-02-04 04:18:54 +08:00
|
|
|
|
class ResourceDirManager:
|
|
|
|
|
|
"""
|
|
|
|
|
|
临时文件管理器
|
|
|
|
|
|
"""
|
|
|
|
|
|
|
2025-07-14 22:35:29 +08:00
|
|
|
|
temp_path: ClassVar[set[Path]] = set()
|
2024-02-04 04:18:54 +08:00
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2025-07-14 22:35:29 +08:00
|
|
|
|
def __tree_append(cls, path: Path, deep: int = 1, current: int = 0):
|
|
|
|
|
|
"""递归添加文件夹"""
|
|
|
|
|
|
if current >= deep and deep != -1:
|
|
|
|
|
|
return
|
|
|
|
|
|
path = path.resolve() # 标准化路径
|
2024-02-04 04:18:54 +08:00
|
|
|
|
for f in os.listdir(path):
|
2025-07-14 22:35:29 +08:00
|
|
|
|
file = (path / f).resolve() # 标准化子路径
|
2024-02-04 04:18:54 +08:00
|
|
|
|
if file.is_dir():
|
|
|
|
|
|
if file not in cls.temp_path:
|
2025-07-14 22:35:29 +08:00
|
|
|
|
cls.temp_path.add(file)
|
|
|
|
|
|
logger.debug(f"添加临时文件夹: {file}")
|
|
|
|
|
|
cls.__tree_append(file, deep, current + 1)
|
2024-02-04 04:18:54 +08:00
|
|
|
|
|
|
|
|
|
|
@classmethod
|
2025-07-14 22:35:29 +08:00
|
|
|
|
def add_temp_dir(cls, path: str | Path, tree: bool = False, deep: int = 1):
|
2024-02-04 04:18:54 +08:00
|
|
|
|
"""添加临时清理文件夹,这些文件夹会被自动清理
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
path: 文件夹路径
|
|
|
|
|
|
tree: 是否递归添加文件夹
|
2025-07-14 22:35:29 +08:00
|
|
|
|
deep: 深度, -1 为无限深度
|
2024-02-04 04:18:54 +08:00
|
|
|
|
"""
|
|
|
|
|
|
if isinstance(path, str):
|
|
|
|
|
|
path = Path(path)
|
|
|
|
|
|
if path not in cls.temp_path:
|
2025-07-14 22:35:29 +08:00
|
|
|
|
cls.temp_path.add(path)
|
2024-02-04 04:18:54 +08:00
|
|
|
|
logger.debug(f"添加临时文件夹: {path}")
|
|
|
|
|
|
if tree:
|
2025-07-14 22:35:29 +08:00
|
|
|
|
cls.__tree_append(path, deep)
|
2024-02-04 04:18:54 +08:00
|
|
|
|
|
|
|
|
|
|
|
2024-03-18 16:10:44 +08:00
|
|
|
|
def cn2py(word: str) -> str:
|
|
|
|
|
|
"""将字符串转化为拼音
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
word: 文本
|
|
|
|
|
|
"""
|
2024-08-29 22:01:34 +08:00
|
|
|
|
return "".join("".join(i) for i in pypinyin.pinyin(word, style=pypinyin.NORMAL))
|
2024-03-18 16:10:44 +08:00
|
|
|
|
|
|
|
|
|
|
|
2024-02-04 04:18:54 +08:00
|
|
|
|
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
|
2024-08-29 22:01:34 +08:00
|
|
|
|
except Exception:
|
2024-02-04 04:18:54 +08:00
|
|
|
|
logger.error("获取用户头像错误", "Util", target=uid)
|
|
|
|
|
|
return None
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_group_avatar(gid: int | str) -> bytes | None:
|
|
|
|
|
|
"""快捷获取用群头像
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
2024-05-04 13:48:12 +08:00
|
|
|
|
gid: 群号
|
2024-02-04 04:18:54 +08:00
|
|
|
|
"""
|
|
|
|
|
|
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
|
2024-08-29 22:01:34 +08:00
|
|
|
|
except Exception:
|
2024-02-04 04:18:54 +08:00
|
|
|
|
logger.error("获取群头像错误", "Util", target=gid)
|
|
|
|
|
|
return None
|
2024-05-20 22:03:11 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
2024-11-05 15:47:59 +08:00
|
|
|
|
.replace("i.pixiv.re", nginx_url)
|
2024-05-20 22:03:11 +08:00
|
|
|
|
.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
|
2024-05-23 13:58:53 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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
|
2025-01-09 10:29:49 +08:00
|
|
|
|
|
|
|
|
|
|
|
2025-07-14 22:35:29 +08:00
|
|
|
|
def get_entity_ids(session: Uninfo) -> EntityIDs:
|
|
|
|
|
|
"""获取用户id,群组id,频道id
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
session: Uninfo
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
EntityIDs: 用户id,群组id,频道id
|
|
|
|
|
|
"""
|
|
|
|
|
|
user_id = session.user.id
|
|
|
|
|
|
group_id = None
|
|
|
|
|
|
channel_id = None
|
|
|
|
|
|
if session.group:
|
|
|
|
|
|
if session.group.parent:
|
|
|
|
|
|
group_id = session.group.parent.id
|
|
|
|
|
|
channel_id = session.group.id
|
|
|
|
|
|
else:
|
|
|
|
|
|
group_id = session.group.id
|
|
|
|
|
|
return EntityIDs(user_id=user_id, group_id=group_id, channel_id=channel_id)
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-01-09 10:29:49 +08:00
|
|
|
|
def is_number(text: str) -> bool:
|
|
|
|
|
|
"""是否为数字
|
|
|
|
|
|
|
|
|
|
|
|
参数:
|
|
|
|
|
|
text: 文本
|
|
|
|
|
|
|
|
|
|
|
|
返回:
|
|
|
|
|
|
bool: 是否为数字
|
|
|
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
|
|
|
float(text)
|
|
|
|
|
|
return True
|
|
|
|
|
|
except ValueError:
|
|
|
|
|
|
return False
|