feat: 故障推送 / 公需课自动重学 / 插件解耦,发版 v0.0.8

体检修复:启动日志不再打印 AUTH_TOKEN、密钥可清空、推送失败不占整段冷却、目录名与文案统一成 plugins/。
This commit is contained in:
study-tool
2026-11-01 17:47:22 +08:00
parent 491fd08c1e
commit aa48145557
114 changed files with 7779 additions and 3302 deletions
+73
View File
@@ -0,0 +1,73 @@
"""系统设置(大模型 + 学习方式)读写。"""
from study_tool import database
# ------------------------------------------------------------------ settings
SETTINGS_NAME = "llm"
DEFAULT_LLM = {
"base_url": "https://api.openai.com/v1",
"api_key": "",
"model": "gpt-4o-mini",
"temperature": 0.2,
"timeout": 120,
"prompt": "",
}
# 学习方式:fast = 只调 study/log 接口,不开浏览器;normal = 真播视频(可多路并发)
DEFAULT_STUDY = {
"mode": "fast",
"parallel": 2,
}
# 故障推送:渠道插件(plugins/*/plugin.json 里 kind=notify)读这段配置
DEFAULT_NOTIFY = {
"enabled": True,
"base_url": "https://corn.hanhan.ltd",
"app_key": "",
"app_secret": "",
"channel_name": "",
"on_error": True, # 运行 / 课程异常
"on_login": True, # 登录失败 / 会话失效且无法续期
"on_progress": True, # 学习进度异常
"on_retry": True, # 同一原因反复重试
"retry_threshold": 3, # 重试几次算故障
"cooldown_min": 10, # 同一账号同一类型的推送冷却
}
def load_settings():
data = database.get_setting(SETTINGS_NAME) or {}
llm = dict(DEFAULT_LLM)
study = dict(DEFAULT_STUDY)
notify = dict(DEFAULT_NOTIFY)
if isinstance(data, dict):
raw = data.get("llm")
if isinstance(raw, dict):
llm.update({k: v for k, v in raw.items() if k in DEFAULT_LLM and v is not None})
st = data.get("study")
if isinstance(st, dict):
study.update({k: v for k, v in st.items() if v is not None})
nt = data.get("notify")
if isinstance(nt, dict):
notify.update({k: v for k, v in nt.items() if k in DEFAULT_NOTIFY and v is not None})
study["mode"] = study.get("mode") if study.get("mode") in ("fast", "normal") else "fast"
try:
study["parallel"] = max(1, min(int(study.get("parallel") or 2), 5))
except (TypeError, ValueError):
study["parallel"] = 2
notify["enabled"] = bool(notify.get("enabled"))
for flag in ("on_error", "on_login", "on_progress", "on_retry"):
notify[flag] = bool(notify.get(flag))
try:
notify["retry_threshold"] = max(2, min(int(notify.get("retry_threshold") or 3), 10))
except (TypeError, ValueError):
notify["retry_threshold"] = 3
try:
notify["cooldown_min"] = max(1, min(int(notify.get("cooldown_min") or 10), 1440))
except (TypeError, ValueError):
notify["cooldown_min"] = 10
return {"llm": llm, "study": study, "notify": notify}
def save_settings(data):
database.set_setting(SETTINGS_NAME, {"llm": data.get("llm") or {},
"study": data.get("study") or {},
"notify": data.get("notify") or {}})