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:
2026-09-03 00:44:38 +08:00
co-authored by Claude
parent 25bc30611f
commit 9371a28e35
2389 changed files with 5100 additions and 10127 deletions
@@ -1,34 +1,14 @@
"""群聊学习 —— 入口(元数据 + 配置注册 + Web 挂载)。"""
from nonebot import require
from nonebot.plugin import PluginMetadata
require("nonebot_plugin_orm")
import asyncio
import random
import time
from nonebot import on_message, require, logger, get_adapter
from nonebot.adapters.onebot.v11 import (
GroupMessageEvent,
GROUP,
Message,
ActionFailed,
Adapter,
)
from nonebot.params import Arg
from nonebot.plugin import PluginMetadata
from nonebot.rule import Rule
from nonebot.typing import T_State
from nonebot_plugin_orm import get_session
from sqlalchemy import insert
from .handler import LearningChat
from .models import ChatMessage
from .config import config_manager, NICKNAME
from . import web_api
from . import web_hub
from hexi.web_plugin_registry import register_web_plugin
require("nonebot_plugin_apscheduler")
from nonebot_plugin_apscheduler import scheduler
from .config import NICKNAME, config_manager, register_config as _register_learning_config # noqa: E402
from . import web_api # noqa: E402
from . import web_hub # noqa: E402
from hexi.web_plugin_registry import register_web_plugin # noqa: E402
# 注册到统一 Web 管理台(/hub):hub 启动时挂载 /api/learning_chat
register_web_plugin(
@@ -39,6 +19,9 @@ register_web_plugin(
module_name=__name__,
)
# 统一配置标准:把顶层 ChatConfig 字段暴露给 /hub 插件配置页
_register_learning_config()
__plugin_meta__ = PluginMetadata(
name="群聊学习",
description="学习群友们的发言、复读以及主动发言",
@@ -49,89 +32,4 @@ __plugin_meta__ = PluginMetadata(
extra={"author": "惜月"},
)
async def ChatRule(event: GroupMessageEvent, state: T_State) -> bool:
if answers := await LearningChat(event).answer():
state["answers"] = answers
return True
return False
learning_chat = on_message(
priority=99,
block=False,
rule=Rule(ChatRule),
permission=GROUP,
state={
"pm_name": "群聊学习",
"pm_description": "(被动技能)bot会学习群友们的发言",
"pm_usage": "群聊学习",
"pm_priority": 1,
},
)
@learning_chat.handle()
async def _(event: GroupMessageEvent, answers=Arg("answers")):
for answer in answers:
try:
logger.info(
"群聊学习", f'{NICKNAME}将向群<m>{event.group_id}</m>回复<m>"{answer}"</m>'
)
msg = await learning_chat.send(Message(answer))
async with get_session() as session:
session.add(ChatMessage(
group_id=event.group_id,
user_id=event.self_id,
message_id=msg["message_id"],
message=answer,
raw_message=answer,
time=int(time.time()),
plain_text=Message(answer).extract_plain_text(),
))
await session.commit()
await asyncio.sleep(random.random() + 0.5)
except ActionFailed:
logger.info(
"群聊学习",
f'{NICKNAME}向群<m>{event.group_id}</m>的回复<m>"{answer}"</m>发送<r>失败,可能处于风控中</r>',
)
@scheduler.scheduled_job("interval", minutes=3, misfire_grace_time=5)
async def speak_up():
if not config_manager.config.total_enable:
return
try:
bots = get_adapter(Adapter).bots
if len(bots) == 0:
return
bot = list(bots.values())[0]
except ValueError:
return
if not (speak := await LearningChat.speak(int(bot.self_id))):
return
group_id, messages = speak
for msg in messages:
try:
logger.info("群聊学习", f'{NICKNAME}向群<m>{group_id}</m>主动发言<m>"{msg}"</m>')
send_result = await bot.send_group_msg(
group_id=group_id, message=Message(msg)
)
async with get_session() as session:
session.add(ChatMessage(
group_id=group_id,
user_id=int(bot.self_id),
message_id=send_result["message_id"],
message=msg,
raw_message=msg,
time=int(time.time()),
plain_text=Message(msg).extract_plain_text(),
))
await session.commit()
await asyncio.sleep(random.randint(2, 4))
except ActionFailed:
logger.info(
"群聊学习",
f'{NICKNAME}向群<m>{group_id}</m>主动发言<m>"{msg}"</m><r>发送失败,可能处于风控中</r>',
)
from .handlers import learning_chat # noqa: E402,F401