modify: ip音柱插件

This commit is contained in:
2026-09-04 14:51:12 +08:00
parent 5ab68d6002
commit 5ec90a5575
12 changed files with 256 additions and 6 deletions
+52
View File
@@ -0,0 +1,52 @@
"""Ip_sound_column Worker 插件
统一风格模板 — 基于 chongming_worker.plugin.Plugin 基类。
工作流:
1. 编辑 config/workers/ip_sound_column/*.toml 定义 RPC 端点
2. uv run gen_models config/workers/ip_sound_column src/plugins/ip_sound_column
3. 在 router/<操作>.py 中实现业务逻辑
4. 在 on_init / on_cleanup 中初始化 / 清理资源
"""
import logging
from chongming_worker.plugin import Plugin
from . import router # noqa: F401 触发 @rpc_method 注册
# from .routes import router # 可选: 自定义 FastAPI 路由(网关自动发现)
logger = logging.getLogger(__name__)
class IpSoundColumnPlugin(Plugin):
"""ip_sound_column 插件 — 统一生命周期。"""
type = "ip_sound_column"
db_name = "ip_sound_column.db" # 自动在 data_dir 下建 SQLite 库(不需要则置 None)
auto_bind = True # 首次初始化时自动 auto_bind_models(self.type)
async def on_init(self):
"""首次 create_workers 时调用一次:初始化资源、启动后台任务。
数据库已由基类 _bootstrap 自动初始化(self.db)。
"""
config = self.config
logger.info(
"[ip_sound_column] 初始化开始 (count=%s, ip=%s)",
config.get("count", 1),
config.get("ip", "unknown")
)
ip = config.get("ip", "unknown")
self.set_var("ip", ip)
logger.info("[ip_sound_column] 初始化完成")
async def on_cleanup(self):
"""cleanup 时调用:关闭自定义客户端等(后台任务与数据库由基类处理)。"""
logger.info("[ip_sound_column] 清理完成")
# 暴露给 app lifespan 的模块级入口(bound method,签名与旧写法完全一致)
_plugin = IpSoundColumnPlugin()
create_workers = _plugin.create_workers
cleanup = _plugin.cleanup
+41
View File
@@ -0,0 +1,41 @@
"""Ip_sound_column 插件全局运行时状态 — 统一容器
约定:
- 模块只做"声明默认值",不产生任何导入期副作用
- 所有值统一在 Plugin.on_init 中通过 set_global_var 显式写入
- set_global_var 仅允许写入 _ALLOWED 白名单,拼写错误会立即抛错
- require() 用于取"必须已初始化"的值,未初始化时抛清晰错误
"""
from typing import Any
from chongming_database import Database
# 允许写入的全局变量白名单(防止拼写错误静默创建无用的新变量)
_ALLOWED = frozenset({"ip"})
# 显式声明所有运行时状态(默认 None,运行时由 on_init 写入)
# db: Database | None = None
def set_global_var(name: str, value: Any) -> None:
"""白名单校验版 setter — 设置模块级运行时状态。"""
if name not in _ALLOWED:
raise KeyError(
f"global_var: 未知变量 '{name}'(允许: {sorted(_ALLOWED)})"
)
globals()[name] = value
def require(name: str) -> Any:
"""获取必须已初始化的全局变量,未初始化时抛清晰错误。"""
if name not in _ALLOWED:
raise KeyError(f"global_var: 未知变量 '{name}'(允许: {sorted(_ALLOWED)})")
value = globals().get(name)
if value is None:
raise RuntimeError(
f"global_var: '{name}' 未初始化 —— 请确认插件 create_workers 已执行且配置正确"
)
return value
__all__ = sorted(_ALLOWED) + ["set_global_var", "require"]
+14
View File
@@ -0,0 +1,14 @@
"""
模型导出 — ip_sound_column
自动生成,请勿手动修改
"""
from .pydantic import IpSoundColumnResponse
from .pydantic import TtsPostRequest
from .pydantic import TtsPostResponse
__all__ = [
"IpSoundColumnResponse",
"TtsPostRequest",
"TtsPostResponse",
]
+30
View File
@@ -0,0 +1,30 @@
"""
自动生成的 Pydantic 模型 — ip_sound_column
来源: config/workers/ip_sound_column/
"""
from __future__ import annotations
from pydantic import BaseModel, Field
class TtsPostRequest(BaseModel):
"""ip 音柱 tts 播放 — 请求体"""
command: str = Field(...)
volume: int = Field(...)
play_count: int = Field(...)
queue: bool = Field(...)
sync_response: bool = Field(...)
play_interval: int = Field(...)
tts_pitch: int = Field(...)
tts_speed: int = Field(...)
tts_voice_name: str = Field(...)
tts_text: str = Field(...)
class TtsPostResponse(BaseModel):
"""ip 音柱 tts 播放 — 响应体"""
command: str = Field(...)
result: int = Field(...)
# 所有响应模型的联合类型
IpSoundColumnResponse = TtsPostResponse
+5
View File
@@ -0,0 +1,5 @@
"""Ip_sound_column RPC 端点"""
from . import tts
__all__ = ['tts']
+41
View File
@@ -0,0 +1,41 @@
"""RPC 端点: ip_sound_column.ping
模型由 gen_models 生成(编辑 config/workers/ip_sound_column/ping.toml 后重新生成)。
"""
import httpx
from chongming_worker.decorators import rpc_method
from ..models.pydantic import TtsPostRequest, TtsPostResponse
from ..global_var import require
@rpc_method(
subject="ip_sound_column.tts",
method="POST",
summary="tts 操作",
response_model=TtsPostResponse,
)
async def tts(request: TtsPostRequest) -> TtsPostResponse:
"""业务逻辑实现。"""
ip_addr: str = require("ip")
if ip_addr == "unknown":
raise RuntimeError("ip_sound_column: 未初始化 ip 地址,请检查配置")
tts_url = f"http://{ip_addr}/v1/api"
try:
async with httpx.AsyncClient() as client:
resp = await client.post(
tts_url,
json=request.model_dump(),
headers={"Content-Type": "application/json"},
)
if resp.is_success:
return TtsPostResponse(**resp.json())
else:
raise RuntimeError(
f"ip_sound_column: tts 请求失败,状态码 {resp.status_code},响应内容: {resp.text}"
)
except httpx.RequestError as e:
raise RuntimeError(f"ip_sound_column: tts 请求异常: {str(e)}") from e