refactor: restructure plugins per MTSS standard, consolidate assets to res/
Per docs/plugin-audit-report.md (plugins normalized to Trigger(handlers) → Service(services) → Model(repository/models) + utils): - Split monolithic __init__.py into handlers/services/utils across dailywife, deer_pipe, dice, galgame_card, helldivers_tools, huoziyinshua, learning_chat, makeaquote, mc_server_status, ncm_saying, picfinder_take, picstatus, random_jm_code, regif, steam_info, video_analysis, group_tools - Move static assets under res/: deer_pipe font/img, makeaquote font, helldivers img/templates, huoziyinshua HuoZiYinShua - Add config.py + register_config_items to ncm_saying, random_jm_code, group_tools; learning_chat unified config bridge - Remove deprecated: voice_trans plugin, bf_bot/test.py, dead code in dailywife/deer_pipe, empty dirs, debug scripts under helldivers temp - Disable brash_general_supercredits_tools (stub comment only) - bot.py: optional stdout/stderr redirect to log file for Web log viewer, force ANSI colorize on non-TTY sinks - Move runtime data (jm_code.json) out of plugin dir into hexi/data - Docs: plugin-audit-report.md; README reflects removed plugins Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""统一配置注册:把插件配置按文档标准接入 hexi.web_config(Web 可读可改)。
|
||||
|
||||
- temp 清理配置(env pydantic) → register_model_config
|
||||
- S3 存储配置(原硬编码在 storage/s3.py) → register_config_items(store=s3 模块)
|
||||
- S3 存储配置(原硬编码在 services/storage/s3.py) → register_config_items(store=s3 模块)
|
||||
- 群分组配置(data/list.json) → register_config_items(type=json, nosave)
|
||||
"""
|
||||
|
||||
@@ -10,7 +10,7 @@ from __future__ import annotations
|
||||
from hexi.config_standard import register_config_items, register_model_config
|
||||
|
||||
from . import cleanup, list_proc # noqa: F401
|
||||
from .storage import s3 as _s3mod
|
||||
from .services.storage import s3 as _s3mod
|
||||
|
||||
# 插件模块名 = plugin_id(与 NoneBot 模块名一致)
|
||||
_PLUGIN_ID = __package__
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""抖音视频/图文解析编排层"""
|
||||
|
||||
import os
|
||||
import re
|
||||
import urllib.parse
|
||||
from pathlib import Path
|
||||
@@ -9,19 +8,15 @@ from typing import Optional, Union
|
||||
import httpx
|
||||
from nonebot import logger
|
||||
|
||||
from ..fetchers.douyin_api import fetch_douyin_content
|
||||
from ..fetchers.douyin_ssr import MOBILE_UA, fetch_douyin_note_ssr
|
||||
from ..services.fetchers.douyin_api import fetch_douyin_content
|
||||
from ..services.fetchers.douyin_ssr import MOBILE_UA, fetch_douyin_note_ssr
|
||||
from ..models import DouyinFetchError
|
||||
from ..utils import parse_netscape_cookies
|
||||
from ..utils import get_data_dir, parse_netscape_cookies
|
||||
from .sender import PendingMedia, _as_paths
|
||||
|
||||
SHORT_LINK_PATTERN = re.compile(r"(v\.douyin\.com/[A-Za-z0-9_\-]+)")
|
||||
|
||||
|
||||
def _get_data_dir() -> str:
|
||||
return os.path.join(os.path.dirname(__file__), "..", "data")
|
||||
|
||||
|
||||
async def _resolve_short_link(url: str) -> Optional[str]:
|
||||
"""短链重定向:httpx 取 Location(最多 3 跳),失败返回 None"""
|
||||
try:
|
||||
@@ -83,7 +78,7 @@ async def parse_douyin(
|
||||
|
||||
try:
|
||||
is_img_post = False
|
||||
cookies_path = os.path.join(_get_data_dir(), "cookies.txt")
|
||||
cookies_path = get_data_dir() / "cookies.txt"
|
||||
cookies = parse_netscape_cookies(cookies_path)
|
||||
# 先试 SSR(秒级、免浏览器),失败一律回退 playwright 链路。
|
||||
# 2026-08-13 起抖音对 iesdouyin SSR 端点整体降级(登录态也拿不到
|
||||
|
||||
@@ -3,7 +3,6 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import html
|
||||
import os
|
||||
import re
|
||||
from typing import Optional
|
||||
|
||||
@@ -13,16 +12,13 @@ from nonebot.rule import to_me
|
||||
from nonebot_plugin_alconna import UniMessage
|
||||
from nonebot_plugin_alconna.uniseg import get_target
|
||||
|
||||
from ..fetchers.bilibili_content import fetch_bilibili_content, resolve_short_link
|
||||
from ..fetchers.rednote_content import fetch_rednote_content
|
||||
from ..services.fetchers.bilibili_content import fetch_bilibili_content, resolve_short_link
|
||||
from ..services.fetchers.rednote_content import fetch_rednote_content
|
||||
from .douyin import parse_douyin, process_douyin_res
|
||||
from .sender import PendingMedia, send_pending_media
|
||||
from .universal import handle_universal
|
||||
from ..list_proc import AUTO_LINK_KEYWORDS, get_group_auto_link, verify_user
|
||||
|
||||
BASE_DIR = os.path.dirname(os.path.abspath(__file__))
|
||||
FILE_PATH = os.path.join(BASE_DIR, "data", "list.json")
|
||||
|
||||
URL_PATTERN = re.compile(r"(https?://\S+)")
|
||||
XCX_PATTERN = r"QQ小程序(?:&#93;|]|\])"
|
||||
|
||||
|
||||
@@ -15,7 +15,7 @@ from typing import Optional, Union
|
||||
from nonebot import logger
|
||||
from nonebot_plugin_alconna import UniMessage
|
||||
|
||||
from ..storage.s3 import upload_with_plan
|
||||
from ..services.storage.s3 import upload_with_plan
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -6,7 +6,7 @@ from typing import Optional
|
||||
from nonebot import logger
|
||||
from nonebot_plugin_alconna import UniMessage
|
||||
|
||||
from ..fetchers.video_downloader import download_video
|
||||
from ..services.fetchers.video_downloader import download_video
|
||||
from .sender import PendingMedia, _as_paths
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
# video_analysis —— Service 层。
|
||||
+3
-3
@@ -21,10 +21,10 @@ from typing import Optional, Union
|
||||
|
||||
from nonebot import logger
|
||||
|
||||
from ..models import ContentFetchError
|
||||
from ..utils import parse_netscape_cookies, slugify
|
||||
from ...models import ContentFetchError
|
||||
from ...utils import get_data_dir, parse_netscape_cookies, slugify
|
||||
|
||||
DATA_DIR = Path(__file__).resolve().parent.parent / "data"
|
||||
DATA_DIR = get_data_dir()
|
||||
|
||||
BILI_UA = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||||
+2
-2
@@ -9,8 +9,8 @@ import httpx
|
||||
from nonebot import logger
|
||||
from playwright.async_api import async_playwright
|
||||
|
||||
from ..models import DouyinFetchError
|
||||
from ..utils import ensure_unique_path, get_temp_root
|
||||
from ...models import DouyinFetchError
|
||||
from ...utils import ensure_unique_path, get_temp_root
|
||||
from .douyin_parser import (
|
||||
ParsedDouyinContent,
|
||||
extract_trailing_digits,
|
||||
+2
-2
@@ -9,8 +9,8 @@ from typing import Dict, List, Optional
|
||||
|
||||
from nonebot import logger
|
||||
|
||||
from ..models import DouyinFetchError
|
||||
from ..utils import slugify
|
||||
from ...models import DouyinFetchError
|
||||
from ...utils import slugify
|
||||
|
||||
|
||||
# ============================= 数据结构 =============================
|
||||
+2
-2
@@ -19,8 +19,8 @@ from typing import List, Optional
|
||||
import httpx
|
||||
from nonebot import logger
|
||||
|
||||
from ..models import DouyinFetchError
|
||||
from ..utils import get_temp_root
|
||||
from ...models import DouyinFetchError
|
||||
from ...utils import get_temp_root
|
||||
from .douyin_api import _process_animated_note, _process_note_with_parsed
|
||||
from .douyin_parser import parse_douyin_response, parse_note_images, parse_ssr_page
|
||||
|
||||
+3
-3
@@ -27,10 +27,10 @@ from typing import Optional, Union
|
||||
|
||||
from nonebot import logger
|
||||
|
||||
from ..models import ContentFetchError
|
||||
from ..utils import get_temp_root, parse_netscape_cookies, slugify
|
||||
from ...models import ContentFetchError
|
||||
from ...utils import get_data_dir, get_temp_root, parse_netscape_cookies, slugify
|
||||
|
||||
DATA_DIR = Path(__file__).resolve().parent.parent / "data"
|
||||
DATA_DIR = get_data_dir()
|
||||
|
||||
REDNOTE_UA = (
|
||||
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 "
|
||||
+2
-7
@@ -14,7 +14,7 @@ from nonebot import logger
|
||||
from yt_dlp import YoutubeDL
|
||||
from yt_dlp.utils import DownloadError
|
||||
|
||||
from ..utils import get_temp_root, slugify, ensure_unique_path
|
||||
from ...utils import get_data_dir, get_temp_root, slugify, ensure_unique_path
|
||||
|
||||
|
||||
def detect_platform(url: str) -> str:
|
||||
@@ -49,11 +49,6 @@ def get_ffmpeg_path() -> str:
|
||||
return "ffmpeg"
|
||||
|
||||
|
||||
def _get_data_dir() -> Path:
|
||||
"""获取 data/ 目录路径"""
|
||||
return Path(__file__).resolve().parent.parent / "data"
|
||||
|
||||
|
||||
async def _retry_download(
|
||||
loop: asyncio.AbstractEventLoop,
|
||||
url: str,
|
||||
@@ -154,7 +149,7 @@ async def download_video(url: str) -> Optional[Path]:
|
||||
"ffmpeg_location": get_ffmpeg_path(),
|
||||
}
|
||||
|
||||
cookie_path = _get_data_dir() / "cookies.txt"
|
||||
cookie_path = get_data_dir() / "cookies.txt"
|
||||
|
||||
if platform in ("bilibili", "twitter", "youtube"):
|
||||
if not cookie_path.exists():
|
||||
@@ -5,6 +5,15 @@ from time import strftime, localtime
|
||||
from typing import List
|
||||
|
||||
|
||||
def get_data_dir() -> Path:
|
||||
"""插件 data 目录:.../nonebot_plugin_video_analysis/data
|
||||
|
||||
注意 utils.py 位于插件根目录,.parent 即插件根,因此这里写死回到
|
||||
插件自身 data/(cookies.txt / list.json / temp 都在此处)。
|
||||
"""
|
||||
return Path(__file__).resolve().parent / "data"
|
||||
|
||||
|
||||
def get_temp_root(sub: str = "") -> Path:
|
||||
"""插件媒体临时目录:data/temp[/sub] — 下载的媒体先进这里
|
||||
|
||||
|
||||
Reference in New Issue
Block a user