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 parent
25bc30611f
commit
9371a28e35
2389 files changed
+5100
-10127
No files matched your search
@@ -1,14 +1,13 @@
|
||||
import os
|
||||
import re
|
||||
"""otto 活字印刷 —— 入口(元数据 + 依赖)。"""
|
||||
|
||||
from nonebot import on_startswith, require
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from __future__ import annotations
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from nonebot import get_driver, require
|
||||
from nonebot.plugin import PluginMetadata
|
||||
|
||||
require("nonebot_plugin_alconna")
|
||||
from nonebot_plugin_alconna import UniMessage
|
||||
|
||||
from .HuoZiYinShua.huoZiYinShua import huoZiYinShua
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="otto活字印刷",
|
||||
@@ -17,32 +16,40 @@ __plugin_meta__ = PluginMetadata(
|
||||
type="application",
|
||||
)
|
||||
|
||||
self_path = os.path.dirname(__file__)
|
||||
settings = {
|
||||
"sourceDirectory": os.path.join(self_path, "HuoZiYinShua", "sources", ""),
|
||||
"ysddSourceDirectory": os.path.join(self_path, "HuoZiYinShua", "ysddSources", ""),
|
||||
"dictFile": os.path.join(self_path, "HuoZiYinShua", "dictionary.json"),
|
||||
"ysddTableFile": os.path.join(self_path, "HuoZiYinShua", "ysddTable.json"),
|
||||
}
|
||||
_PLUGIN_ROOT = Path(__file__).resolve().parent
|
||||
# 生成音频统一落到 hexi/data/temp/otto(与 video_analysis 的临时目录约定一致)。
|
||||
# 协议端(NapCat)通常与 bot 后端不在同一台机器,无法直接访问本地文件,
|
||||
# 所以先落盘再通过 FastAPI 路由以 http URL 提供给 NapCat 下载。
|
||||
_TEMP_ROOT = _PLUGIN_ROOT.parent.parent / "data" / "temp" / "otto"
|
||||
|
||||
HZYS = huoZiYinShua(settings)
|
||||
|
||||
otto = on_startswith("otto")
|
||||
_driver = get_driver()
|
||||
|
||||
|
||||
@otto.handle()
|
||||
async def otto_handle(ev: MessageEvent):
|
||||
txt = ev.message.extract_plain_text().removeprefix("otto")
|
||||
pattern_reverse = re.compile(r"-reverse\s*$")
|
||||
@_driver.on_startup
|
||||
async def _register_media_route() -> None:
|
||||
"""挂载 /api/otto/{filename},供协议端跨机器下载生成的语音。"""
|
||||
from fastapi import HTTPException
|
||||
from fastapi.responses import FileResponse
|
||||
from nonebot import get_app
|
||||
|
||||
if re.search(pattern_reverse, txt):
|
||||
# 结尾匹配到 -reverse
|
||||
reverse = True
|
||||
txt = re.sub(pattern_reverse, "", txt)
|
||||
txt = ",,," + txt # 加一段空白
|
||||
else:
|
||||
reverse = False
|
||||
txt = txt + ",,," # 加一段空白
|
||||
app = get_app()
|
||||
|
||||
b64audio = HZYS.export(txt, inYsddMode=True, reverse=reverse)
|
||||
await UniMessage.voice(url=f"base64://{b64audio.decode()}").send()
|
||||
def serve_otto_file(filename: str):
|
||||
root = _TEMP_ROOT.resolve()
|
||||
target = (root / filename).resolve()
|
||||
if root != target.parent and root not in target.parents:
|
||||
raise HTTPException(status_code=403, detail="Forbidden")
|
||||
if not target.is_file():
|
||||
raise HTTPException(status_code=404, detail="Not Found")
|
||||
return FileResponse(target, media_type="audio/mpeg")
|
||||
|
||||
if not any(getattr(r, "path", None) == "/api/otto/{filename}" for r in app.routes):
|
||||
app.add_api_route(
|
||||
"/api/otto/{filename}",
|
||||
serve_otto_file,
|
||||
methods=["GET"],
|
||||
name="otto_media",
|
||||
)
|
||||
|
||||
|
||||
from .handlers import otto # noqa: E402,F401
|
||||
@@ -0,0 +1,3 @@
|
||||
"""otto 活字印刷 —— Trigger 层。"""
|
||||
|
||||
from .otto import otto # noqa: F401
|
||||
@@ -0,0 +1,83 @@
|
||||
"""otto 活字印刷 —— 触发器(Trigger)层。"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import base64
|
||||
import os
|
||||
import re
|
||||
import socket
|
||||
import time
|
||||
from pathlib import Path
|
||||
|
||||
from nonebot import get_driver, on_startswith, require
|
||||
from nonebot.adapters.onebot.v11 import MessageEvent
|
||||
from nonebot.plugin import PluginMetadata
|
||||
from nonebot_plugin_alconna import UniMessage
|
||||
|
||||
from .. import _TEMP_ROOT
|
||||
from ..services.synthesis import synthesize
|
||||
|
||||
require("nonebot_plugin_alconna")
|
||||
|
||||
__plugin_meta__ = PluginMetadata(
|
||||
name="otto活字印刷",
|
||||
description="鬼畜音源活字印刷(otto语音合成)",
|
||||
usage="otto 一些内容 生成otto语音\notto 一些内容 -reverse 生成并倒放",
|
||||
type="application",
|
||||
)
|
||||
|
||||
otto = on_startswith("otto")
|
||||
|
||||
|
||||
async def _remove_later(path: Path) -> None:
|
||||
"""发送完成后延迟清理临时音频,避免 NapCat 还在下载/转码时被删。"""
|
||||
await asyncio.sleep(300)
|
||||
path.unlink(missing_ok=True)
|
||||
|
||||
|
||||
def _public_base() -> str:
|
||||
"""构造对外可达的 bot HTTP 地址,供协议端跨机器下载音频。
|
||||
|
||||
优先读 .env 的 OTTO_PUBLIC_BASE_URL(例如 http://192.168.2.100:39697
|
||||
或公网地址);未配置时回退为 HOST:PORT,且 0.0.0.0 自动替换成本机可路由 IP。
|
||||
"""
|
||||
base = os.getenv("OTTO_PUBLIC_BASE_URL", "").strip().rstrip("/")
|
||||
if base:
|
||||
return base
|
||||
cfg = get_driver().config
|
||||
host = str(getattr(cfg, "host", None) or os.getenv("HOST", "0.0.0.0"))
|
||||
port = str(getattr(cfg, "port", None) or os.getenv("PORT", "39697"))
|
||||
if host in ("0.0.0.0", "::", ""):
|
||||
try:
|
||||
# 用 UDP connect 探测本机对外(路由) IP,不真正发包
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
|
||||
s.connect(("8.8.8.8", 80))
|
||||
host = s.getsockname()[0]
|
||||
s.close()
|
||||
except OSError:
|
||||
host = "127.0.0.1"
|
||||
return f"http://{host}:{port}"
|
||||
|
||||
|
||||
@otto.handle()
|
||||
async def otto_handle(ev: MessageEvent):
|
||||
txt = ev.message.extract_plain_text().removeprefix("otto")
|
||||
pattern_reverse = re.compile(r"-reverse\s*$")
|
||||
if re.search(pattern_reverse, txt):
|
||||
reverse = True
|
||||
txt = re.sub(pattern_reverse, "", txt)
|
||||
txt = ",,," + txt
|
||||
else:
|
||||
reverse = False
|
||||
txt = txt + ",,,"
|
||||
b64audio = await synthesize(txt, reverse)
|
||||
audio_bytes = base64.b64decode(b64audio)
|
||||
_TEMP_ROOT.mkdir(parents=True, exist_ok=True)
|
||||
mp3_path = _TEMP_ROOT / f"otto-{time.time_ns()}.mp3"
|
||||
mp3_path.write_bytes(audio_bytes)
|
||||
url = f"{_public_base()}/api/otto/{mp3_path.name}"
|
||||
try:
|
||||
await UniMessage.voice(url=url).send()
|
||||
finally:
|
||||
asyncio.create_task(_remove_later(mp3_path))
|
||||
File renamed without changes.
+5
-5
@@ -11,7 +11,7 @@ from pathlib import Path
|
||||
from io import BytesIO
|
||||
import base64
|
||||
|
||||
|
||||
from nonebot.log import logger
|
||||
|
||||
|
||||
#--------------------------------------------
|
||||
@@ -69,7 +69,7 @@ def _modifyPitchAndSpeed(data, pitchMultiple, speedMultiple):
|
||||
return data
|
||||
|
||||
elif (pitchMultiple > 2 or speedMultiple < 0.5):
|
||||
print("过于极端的音调和速度参数可能导致输出结果与预期不符,故不作改动")
|
||||
logger.warning("过于极端的音调和速度参数可能导致输出结果与预期不符,故不作改动")
|
||||
return data
|
||||
|
||||
else:
|
||||
@@ -120,7 +120,7 @@ class huoZiYinShua:
|
||||
|
||||
except Exception as e:
|
||||
self.__configSucceed = False
|
||||
print(e)
|
||||
logger.warning(e)
|
||||
|
||||
|
||||
|
||||
@@ -207,7 +207,7 @@ class huoZiYinShua:
|
||||
#如果出现错误
|
||||
except Exception as e:
|
||||
if word != ",":
|
||||
print(e) #显示错误信息
|
||||
logger.warning(e) #显示错误信息
|
||||
#加入缺失素材列表
|
||||
if word not in missingPinyin:
|
||||
missingPinyin.append(word)
|
||||
@@ -225,7 +225,7 @@ class huoZiYinShua:
|
||||
norm)))
|
||||
#如果出现错误
|
||||
except Exception as e:
|
||||
print(e) #显示错误信息
|
||||
logger.warning(e) #显示错误信息
|
||||
#加入缺失素材列表
|
||||
if self.__ysddTable[pronunciations[i][0]] not in missingPinyin:
|
||||
missingPinyin.append(self.__ysddTable[pronunciations[i][0]])
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
Loaded 100 of 2389 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user