2023-11-18 10:37:51 +00:00
|
|
|
|
""""配置文件"""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
import os
|
2023-11-25 01:08:51 +00:00
|
|
|
|
import platform
|
2023-11-18 10:37:51 +00:00
|
|
|
|
from hashlib import md5
|
|
|
|
|
from json import JSONDecodeError
|
2023-11-13 13:18:59 +00:00
|
|
|
|
from pathlib import Path
|
2023-11-13 14:31:39 +00:00
|
|
|
|
from typing import Dict, List, Optional, Union
|
2023-11-13 13:18:59 +00:00
|
|
|
|
|
2023-12-04 14:21:41 +00:00
|
|
|
|
import orjson
|
|
|
|
|
import yaml # pylint: disable=wrong-import-order
|
|
|
|
|
from pydantic import BaseModel, ValidationError, field_validator
|
2023-11-25 01:08:51 +00:00
|
|
|
|
|
|
|
|
|
from .logger import log
|
2023-11-13 13:18:59 +00:00
|
|
|
|
|
2023-12-11 02:32:47 +00:00
|
|
|
|
ROOT_PATH = Path(__file__).parent.parent.absolute()
|
2023-11-13 13:18:59 +00:00
|
|
|
|
|
|
|
|
|
DATA_PATH = ROOT_PATH / "data"
|
2023-11-22 04:02:10 +00:00
|
|
|
|
"""数据保存目录"""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
|
2023-12-04 14:21:41 +00:00
|
|
|
|
CONFIG_TYPE = "json" if os.path.isfile(DATA_PATH / "config.json") else "yaml"
|
|
|
|
|
"""数据文件类型"""
|
|
|
|
|
|
|
|
|
|
CONFIG_PATH = DATA_PATH / f"config.{CONFIG_TYPE}" if os.getenv("MIUITASK_CONFIG_PATH") is None else Path(os.getenv("MIUITASK_CONFIG_PATH"))
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""数据文件默认路径"""
|
|
|
|
|
|
2023-11-22 04:02:10 +00:00
|
|
|
|
os.makedirs(DATA_PATH, exist_ok=True)
|
|
|
|
|
|
2023-11-13 13:18:59 +00:00
|
|
|
|
|
|
|
|
|
def md5_crypto(passwd: str) -> str:
|
2023-11-18 10:37:51 +00:00
|
|
|
|
"""MD5加密"""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
return md5(passwd.encode('utf8')).hexdigest().upper()
|
|
|
|
|
|
2023-11-22 04:02:10 +00:00
|
|
|
|
|
2023-12-07 14:54:25 +00:00
|
|
|
|
def cookies_to_dict(cookies: str):
|
2023-11-18 10:37:51 +00:00
|
|
|
|
"""将cookies字符串转换为字典"""
|
2023-11-13 14:31:39 +00:00
|
|
|
|
cookies_dict = {}
|
2023-12-07 14:54:25 +00:00
|
|
|
|
if not cookies or "=" not in cookies:
|
|
|
|
|
return cookies_dict
|
2023-11-13 14:31:39 +00:00
|
|
|
|
for cookie in cookies.split(';'):
|
|
|
|
|
key, value = cookie.strip().split('=', 1) # 分割键和值
|
|
|
|
|
cookies_dict[key] = value
|
|
|
|
|
return cookies_dict
|
2023-11-13 13:18:59 +00:00
|
|
|
|
|
2023-11-25 01:08:51 +00:00
|
|
|
|
def get_platform() -> str:
|
|
|
|
|
"""获取当前运行平台"""
|
|
|
|
|
if os.path.exists('/.dockerenv'):
|
|
|
|
|
if os.environ.get('QL_DIR') and os.environ.get('QL_BRANCH'):
|
|
|
|
|
return "qinglong"
|
|
|
|
|
else:
|
|
|
|
|
return "docker"
|
|
|
|
|
return platform.system().lower()
|
|
|
|
|
|
2023-11-22 04:02:10 +00:00
|
|
|
|
|
2023-11-13 13:18:59 +00:00
|
|
|
|
class Account(BaseModel):
|
2023-11-18 10:37:51 +00:00
|
|
|
|
"""账号处理器"""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
uid: str = "100000"
|
|
|
|
|
"""账户ID 非账户用户名或手机号"""
|
|
|
|
|
password: str = ""
|
|
|
|
|
"""账户密码或其MD5哈希"""
|
2023-11-13 14:31:39 +00:00
|
|
|
|
cookies: Union[dict, str] = {}
|
|
|
|
|
"""账户登录后的cookies"""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
user_agent: str = 'Mozilla/5.0 (Linux; Android 13) AppleWebKit/537.36 (KHTML, like Gecko) Version/4.0 Chrome/116.0.0.0 Safari/537.36'
|
|
|
|
|
"""登录社区时所用浏览器的 User-Agent"""
|
|
|
|
|
|
2023-11-18 10:37:51 +00:00
|
|
|
|
CheckIn: bool = False
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""社区成长值签到,启用功能意味着你愿意自行承担相关风险"""
|
2023-11-18 10:37:51 +00:00
|
|
|
|
BrowseUserPage: bool = False
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""社区浏览个人主页10秒,启用功能意味着你愿意自行承担相关风险"""
|
2023-11-18 10:37:51 +00:00
|
|
|
|
BrowsePost: bool = False
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""社区浏览帖子10秒,启用功能意味着你愿意自行承担相关风险"""
|
2023-11-18 10:37:51 +00:00
|
|
|
|
ThumbUp: bool = False
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""点赞帖子,启用功能意味着你愿意自行承担相关风险"""
|
2023-11-18 10:37:51 +00:00
|
|
|
|
BrowseSpecialPage: bool = False
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""社区在活动期间可能会出现限时的“浏览指定专题页”任务,启用功能意味着你愿意自行承担相关风险"""
|
2023-11-18 10:37:51 +00:00
|
|
|
|
BoardFollow: bool = False
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""社区可能会出现限时的“加入圈子”任务,启用功能意味着你愿意自行承担相关风险"""
|
2023-11-19 12:19:54 +00:00
|
|
|
|
CarrotPull: bool = False
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""社区拔萝卜,启用功能意味着你愿意自行承担相关风险"""
|
|
|
|
|
|
2023-11-22 04:02:10 +00:00
|
|
|
|
@field_validator("password")
|
|
|
|
|
@classmethod
|
|
|
|
|
def _password(cls, value: Optional[str]): # pylint: disable=no-self-argument
|
2023-11-18 10:37:51 +00:00
|
|
|
|
if len(value) == 32:
|
|
|
|
|
return value
|
|
|
|
|
return md5_crypto(value)
|
|
|
|
|
|
2023-11-22 04:02:10 +00:00
|
|
|
|
@field_validator("cookies")
|
|
|
|
|
@classmethod
|
|
|
|
|
def _cookies(cls, value: Union[dict, str]): # pylint: disable=no-self-argument
|
2023-11-18 10:37:51 +00:00
|
|
|
|
if isinstance(value, str):
|
|
|
|
|
return cookies_to_dict(value)
|
|
|
|
|
return value
|
2023-11-13 13:18:59 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class OnePush(BaseModel):
|
2023-11-18 10:37:51 +00:00
|
|
|
|
"""推送配置"""
|
2023-11-13 14:31:39 +00:00
|
|
|
|
notifier: Union[str, bool] = ""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""是否开启消息推送"""
|
|
|
|
|
params: Dict = {
|
|
|
|
|
"title": "",
|
|
|
|
|
"markdown": False,
|
|
|
|
|
"token": "",
|
|
|
|
|
"userid": ""
|
|
|
|
|
}
|
|
|
|
|
"""推送参数"""
|
|
|
|
|
|
2023-11-22 04:02:10 +00:00
|
|
|
|
|
2023-11-15 13:07:29 +00:00
|
|
|
|
class Preference(BaseModel):
|
2023-11-18 10:37:51 +00:00
|
|
|
|
"""偏好设置"""
|
2023-11-15 13:07:29 +00:00
|
|
|
|
geetest_url: str = ""
|
|
|
|
|
"""极验验证URL"""
|
|
|
|
|
geetest_params: Dict = {}
|
|
|
|
|
"""极验自定义params参数"""
|
|
|
|
|
geetest_data: Dict = {}
|
|
|
|
|
"""极验自定义data参数"""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
|
|
|
|
|
class Config(BaseModel):
|
2023-11-18 10:37:51 +00:00
|
|
|
|
"""插件数据"""
|
2023-11-15 13:07:29 +00:00
|
|
|
|
preference: Preference = Preference()
|
2023-11-13 13:18:59 +00:00
|
|
|
|
"""偏好设置"""
|
2023-11-15 13:07:29 +00:00
|
|
|
|
accounts: List[Account] = [Account()]
|
|
|
|
|
"""账号设置"""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
ONEPUSH: OnePush = OnePush()
|
|
|
|
|
"""消息推送"""
|
|
|
|
|
|
|
|
|
|
def write_plugin_data(data: Config = None):
|
|
|
|
|
"""
|
|
|
|
|
写入插件数据文件
|
|
|
|
|
|
|
|
|
|
:param data: 配置对象
|
|
|
|
|
"""
|
|
|
|
|
try:
|
|
|
|
|
if data is None:
|
|
|
|
|
data = ConfigManager.data_obj
|
|
|
|
|
try:
|
2023-12-04 14:21:41 +00:00
|
|
|
|
if CONFIG_TYPE == "json":
|
|
|
|
|
str_data = orjson.dumps(data.model_dump(), option=orjson.OPT_PASSTHROUGH_DATETIME | orjson.OPT_SERIALIZE_NUMPY | orjson.OPT_INDENT_2)
|
|
|
|
|
with open(CONFIG_PATH, "wb") as file:
|
|
|
|
|
file.write(str_data)
|
|
|
|
|
else:
|
|
|
|
|
str_data = yaml.dump(data.model_dump(), indent=4, allow_unicode=True, sort_keys=False)
|
|
|
|
|
with open(CONFIG_PATH, "w", encoding="utf-8") as file:
|
|
|
|
|
file.write(str_data)
|
|
|
|
|
return True
|
2023-11-13 13:18:59 +00:00
|
|
|
|
except (AttributeError, TypeError, ValueError):
|
|
|
|
|
log.exception("数据对象序列化失败,可能是数据类型错误")
|
|
|
|
|
return False
|
|
|
|
|
except OSError:
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class ConfigManager:
|
2023-11-18 10:37:51 +00:00
|
|
|
|
"""配置管理器"""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
data_obj = Config()
|
|
|
|
|
"""加载出的插件数据对象"""
|
2023-11-25 01:08:51 +00:00
|
|
|
|
platform = get_platform()
|
|
|
|
|
"""运行平台"""
|
2023-11-13 13:18:59 +00:00
|
|
|
|
|
|
|
|
|
@classmethod
|
|
|
|
|
def load_config(cls):
|
|
|
|
|
"""
|
|
|
|
|
加载插件数据文件
|
|
|
|
|
"""
|
|
|
|
|
if os.path.exists(DATA_PATH) and os.path.isfile(CONFIG_PATH):
|
|
|
|
|
try:
|
2023-11-18 10:37:51 +00:00
|
|
|
|
with open(CONFIG_PATH, 'r', encoding="utf-8") as file:
|
2023-12-04 14:21:41 +00:00
|
|
|
|
if CONFIG_TYPE == "json":
|
|
|
|
|
data = orjson.loads(file.read())
|
|
|
|
|
else:
|
|
|
|
|
data = yaml.safe_load(file)
|
2023-11-13 13:18:59 +00:00
|
|
|
|
new_model = Config.model_validate(data)
|
|
|
|
|
for attr in new_model.model_fields:
|
2023-11-22 04:02:10 +00:00
|
|
|
|
# ConfigManager.data_obj.__setattr__(attr, new_model.__getattribute__(attr))
|
2023-11-18 10:37:51 +00:00
|
|
|
|
setattr(ConfigManager.data_obj, attr, getattr(new_model, attr))
|
2023-11-13 13:18:59 +00:00
|
|
|
|
write_plugin_data(ConfigManager.data_obj) # 同步配置
|
|
|
|
|
except (ValidationError, JSONDecodeError):
|
|
|
|
|
log.exception(f"读取数据文件失败,请检查数据文件 {CONFIG_PATH} 格式是否正确")
|
|
|
|
|
raise
|
|
|
|
|
except Exception:
|
|
|
|
|
log.exception(
|
|
|
|
|
f"读取数据文件失败,请检查数据文件 {CONFIG_PATH} 是否存在且有权限读取和写入")
|
|
|
|
|
raise
|
|
|
|
|
else:
|
|
|
|
|
try:
|
|
|
|
|
if not os.path.exists(DATA_PATH):
|
|
|
|
|
os.mkdir(DATA_PATH)
|
|
|
|
|
write_plugin_data()
|
|
|
|
|
except (AttributeError, TypeError, ValueError, PermissionError):
|
|
|
|
|
log.exception(f"创建数据文件失败,请检查是否有权限读取和写入 {CONFIG_PATH}")
|
|
|
|
|
raise
|
|
|
|
|
log.info(f"数据文件 {CONFIG_PATH} 不存在,已创建默认数据文件。")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ConfigManager.load_config()
|