pallas-plugin-protocol 4.0.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pallas_plugin_protocol/__init__.py +93 -0
- pallas_plugin_protocol/backends/__init__.py +55 -0
- pallas_plugin_protocol/backends/napcat.py +54 -0
- pallas_plugin_protocol/backends/protocol.py +46 -0
- pallas_plugin_protocol/backends/snowluma.py +72 -0
- pallas_plugin_protocol/config.py +624 -0
- pallas_plugin_protocol/config_manager.py +285 -0
- pallas_plugin_protocol/contract.py +69 -0
- pallas_plugin_protocol/docker_cli.py +120 -0
- pallas_plugin_protocol/docker_onebot_host.py +104 -0
- pallas_plugin_protocol/importer.py +246 -0
- pallas_plugin_protocol/launch_manager.py +855 -0
- pallas_plugin_protocol/linux_docker.py +233 -0
- pallas_plugin_protocol/platform/__init__.py +19 -0
- pallas_plugin_protocol/platform/base.py +46 -0
- pallas_plugin_protocol/platform/posix.py +97 -0
- pallas_plugin_protocol/platform/windows.py +152 -0
- pallas_plugin_protocol/runtime/__init__.py +30 -0
- pallas_plugin_protocol/runtime/installer.py +949 -0
- pallas_plugin_protocol/runtime/snowluma_installer.py +542 -0
- pallas_plugin_protocol/runtime/tag_paths.py +22 -0
- pallas_plugin_protocol/service.py +2193 -0
- pallas_plugin_protocol/snowluma_config.py +265 -0
- pallas_plugin_protocol/snowluma_docker.py +168 -0
- pallas_plugin_protocol/web/__init__.py +3 -0
- pallas_plugin_protocol/web/pages.py +4114 -0
- pallas_plugin_protocol/web/routes.py +941 -0
- pallas_plugin_protocol/web/static/pallas_ui/InterVariable.woff2 +0 -0
- pallas_plugin_protocol/web/static/pallas_ui/favicon.png +0 -0
- pallas_plugin_protocol/web/static/pallas_ui/fonts.css +7 -0
- pallas_plugin_protocol/web/static/pallas_ui/shell.css +2133 -0
- pallas_plugin_protocol-4.0.1.dist-info/METADATA +761 -0
- pallas_plugin_protocol-4.0.1.dist-info/RECORD +38 -0
- pallas_plugin_protocol-4.0.1.dist-info/WHEEL +4 -0
- pallas_plugin_protocol-4.0.1.dist-info/licenses/LICENSE +661 -0
- pallas_plugin_relogin_bot/__init__.py +306 -0
- pallas_plugin_relogin_bot/service.py +353 -0
- pallas_plugin_relogin_forward/__init__.py +122 -0
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# ruff: noqa: E501
|
|
2
|
+
import logging
|
|
3
|
+
|
|
4
|
+
from nonebot import get_app, get_driver, logger
|
|
5
|
+
from nonebot.plugin import PluginMetadata
|
|
6
|
+
|
|
7
|
+
from src.console.web import public_base_url
|
|
8
|
+
from src.console.webui.console_login import prime_shared_console_login
|
|
9
|
+
from src.features.cmd_perm.metadata_defaults import (
|
|
10
|
+
PLUGIN_EXTRA_VERSION,
|
|
11
|
+
PLUGIN_HOMEPAGE,
|
|
12
|
+
PLUGIN_MENU_TEMPLATE,
|
|
13
|
+
)
|
|
14
|
+
from src.features.cmd_perm.metadata_text import join_usage, usage_line
|
|
15
|
+
from src.foundation.paths import plugin_data_dir
|
|
16
|
+
|
|
17
|
+
from .config import Config, get_pallas_protocol_config, plugin_config, resolve_protocol_webui_base_path
|
|
18
|
+
from .service import PallasProtocolService
|
|
19
|
+
from .web import register_pallas_protocol_routes
|
|
20
|
+
|
|
21
|
+
__plugin_meta__ = PluginMetadata(
|
|
22
|
+
name="协议端管理",
|
|
23
|
+
description="NapCat/SnowLuma 协议端账号管理与 Web 控制台。",
|
|
24
|
+
usage=join_usage(
|
|
25
|
+
usage_line("/protocol/console", "协议端管理页"),
|
|
26
|
+
usage_line("X-Pallas-Protocol-Token / ?token=", "API 鉴权"),
|
|
27
|
+
),
|
|
28
|
+
type="application",
|
|
29
|
+
homepage=PLUGIN_HOMEPAGE,
|
|
30
|
+
supported_adapters={"~onebot.v11"},
|
|
31
|
+
extra={
|
|
32
|
+
"version": PLUGIN_EXTRA_VERSION,
|
|
33
|
+
"menu_template": PLUGIN_MENU_TEMPLATE,
|
|
34
|
+
"menu_data": [
|
|
35
|
+
{
|
|
36
|
+
"func": "协议端管理页",
|
|
37
|
+
"trigger_method": "http",
|
|
38
|
+
"help_audience": "maintainer",
|
|
39
|
+
"trigger_condition": "/protocol/console",
|
|
40
|
+
"brief_des": "管理协议账号与进程",
|
|
41
|
+
"detail_des": "可在页面执行创建账号、启动、停止、重启与日志查看。",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
"func": "协议端 API",
|
|
45
|
+
"trigger_method": "http",
|
|
46
|
+
"help_audience": "maintainer",
|
|
47
|
+
"trigger_condition": "/protocol/*",
|
|
48
|
+
"brief_des": "提供协议管理接口",
|
|
49
|
+
"detail_des": "提供账号、配置、协议端发行包下载与状态查询接口。",
|
|
50
|
+
},
|
|
51
|
+
],
|
|
52
|
+
},
|
|
53
|
+
)
|
|
54
|
+
|
|
55
|
+
app = get_app()
|
|
56
|
+
driver = get_driver()
|
|
57
|
+
manager = PallasProtocolService(plugin_data_dir("pallas_protocol"), get_pallas_protocol_config())
|
|
58
|
+
|
|
59
|
+
register_pallas_protocol_routes(app, manager=manager, plugin_config=plugin_config)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@driver.on_startup
|
|
63
|
+
async def _pallas_protocol_prime_shared_console_login() -> None:
|
|
64
|
+
prime_shared_console_login()
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@driver.on_startup
|
|
68
|
+
async def _startup() -> None:
|
|
69
|
+
if not plugin_config.pallas_protocol_enabled:
|
|
70
|
+
return
|
|
71
|
+
logging.getLogger("uvicorn.access").setLevel(logging.WARNING)
|
|
72
|
+
await manager.initialize()
|
|
73
|
+
if plugin_config.pallas_protocol_webui_enabled:
|
|
74
|
+
dconf = get_driver().config
|
|
75
|
+
base_u = public_base_url(
|
|
76
|
+
host=getattr(dconf, "host", None),
|
|
77
|
+
port=getattr(dconf, "port", None),
|
|
78
|
+
)
|
|
79
|
+
path = resolve_protocol_webui_base_path(plugin_config)
|
|
80
|
+
logger.info(f"Pallas-Bot 协议端 | WebUI={base_u}{path}/")
|
|
81
|
+
profile = manager.runtime_profile()
|
|
82
|
+
if bool(profile.get("follow_bot_lifecycle", True)):
|
|
83
|
+
await manager.start_all_enabled_accounts()
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
@driver.on_shutdown
|
|
87
|
+
async def _shutdown() -> None:
|
|
88
|
+
if not plugin_config.pallas_protocol_enabled:
|
|
89
|
+
return
|
|
90
|
+
profile = manager.runtime_profile()
|
|
91
|
+
if not bool(profile.get("follow_bot_lifecycle", True)):
|
|
92
|
+
return
|
|
93
|
+
await manager.stop_all_enabled_accounts()
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"""协议端运行时注册表:按账号 `protocol_backend` 字段分派。"""
|
|
2
|
+
|
|
3
|
+
# 新协议栈:实现 ProtocolRuntimeBackend 后在此 register;kind 与账号里存的字符串同形且小写匹配。
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from collections.abc import Callable
|
|
8
|
+
from typing import Any
|
|
9
|
+
|
|
10
|
+
from ..contract import DEFAULT_PROTOCOL_BACKEND
|
|
11
|
+
from .napcat import NapcatRuntimeBackend
|
|
12
|
+
from .protocol import ProtocolRuntimeBackend
|
|
13
|
+
from .snowluma import SnowlumaRuntimeBackend
|
|
14
|
+
|
|
15
|
+
ProtocolRuntimeBackendFactory = Callable[[Any], ProtocolRuntimeBackend]
|
|
16
|
+
|
|
17
|
+
_PROTOCOL_RUNTIME_FACTORIES: dict[str, ProtocolRuntimeBackendFactory] = {}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def register_protocol_runtime_backend(kind: str, factory: ProtocolRuntimeBackendFactory) -> None:
|
|
21
|
+
"""登记一种实现;factory 接收 PallasProtocolService,返回该后端的 RuntimeBackend 实例。应在插件加载阶段调用。"""
|
|
22
|
+
key = (kind or "").strip().lower()
|
|
23
|
+
if not key:
|
|
24
|
+
msg = "协议端 backend 注册名不能为空"
|
|
25
|
+
raise ValueError(msg)
|
|
26
|
+
_PROTOCOL_RUNTIME_FACTORIES[key] = factory
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def registered_protocol_runtime_backends() -> tuple[str, ...]:
|
|
30
|
+
return tuple(sorted(_PROTOCOL_RUNTIME_FACTORIES.keys()))
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def make_protocol_runtime_backend(service: Any, kind: str) -> ProtocolRuntimeBackend:
|
|
34
|
+
"""按 kind 取工厂;空串回退到 contract.DEFAULT_PROTOCOL_BACKEND。未登记则 ValueError。"""
|
|
35
|
+
raw = (kind or "").strip().lower() or DEFAULT_PROTOCOL_BACKEND
|
|
36
|
+
factory = _PROTOCOL_RUNTIME_FACTORIES.get(raw)
|
|
37
|
+
if factory is None:
|
|
38
|
+
known = ", ".join(registered_protocol_runtime_backends()) or "(empty)"
|
|
39
|
+
msg = f"未注册的协议端实现: {raw!r};已注册: {known}"
|
|
40
|
+
raise ValueError(msg)
|
|
41
|
+
return factory(service)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# 内置:与 DEFAULT_PROTOCOL_BACKEND 一致
|
|
45
|
+
register_protocol_runtime_backend("napcat", lambda s: NapcatRuntimeBackend(s))
|
|
46
|
+
register_protocol_runtime_backend("snowluma", lambda s: SnowlumaRuntimeBackend(s))
|
|
47
|
+
|
|
48
|
+
__all__ = [
|
|
49
|
+
"NapcatRuntimeBackend",
|
|
50
|
+
"SnowlumaRuntimeBackend",
|
|
51
|
+
"ProtocolRuntimeBackend",
|
|
52
|
+
"make_protocol_runtime_backend",
|
|
53
|
+
"register_protocol_runtime_backend",
|
|
54
|
+
"registered_protocol_runtime_backends",
|
|
55
|
+
]
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""NapCat 协议栈:委托现有 LaunchManager / AccountConfigManager。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
|
|
10
|
+
from ..service import PallasProtocolService
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class NapcatRuntimeBackend:
|
|
14
|
+
"""当前默认协议端实现,行为与抽出抽象前一致。"""
|
|
15
|
+
|
|
16
|
+
kind = "napcat"
|
|
17
|
+
|
|
18
|
+
def __init__(self, service: PallasProtocolService) -> None:
|
|
19
|
+
self._service = service
|
|
20
|
+
|
|
21
|
+
def apply_defaults(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
22
|
+
self._service._launch.apply_defaults(account, resolve_qq)
|
|
23
|
+
|
|
24
|
+
def prepare_dirs(self, account: dict) -> None:
|
|
25
|
+
self._service._launch.prepare_dirs(account)
|
|
26
|
+
|
|
27
|
+
def sync_all_configs(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
28
|
+
self._service._configs.sync_onebot(account, resolve_qq)
|
|
29
|
+
self._service._configs.sync_napcat_core(account, resolve_qq)
|
|
30
|
+
self._service._configs.sync_webui(account, resolve_qq)
|
|
31
|
+
|
|
32
|
+
def sync_onebot(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
33
|
+
self._service._configs.sync_onebot(account, resolve_qq)
|
|
34
|
+
|
|
35
|
+
def sync_webui(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
36
|
+
self._service._configs.sync_webui(account, resolve_qq)
|
|
37
|
+
|
|
38
|
+
def read_webui_into_account(self, account: dict) -> bool:
|
|
39
|
+
return self._service._configs.read_webui_into_account(account)
|
|
40
|
+
|
|
41
|
+
def get_account_configs(self, account: dict, resolve_qq: Callable[[dict], str]) -> dict[str, Any]:
|
|
42
|
+
return self._service._configs.get_account_configs(account, resolve_qq)
|
|
43
|
+
|
|
44
|
+
def update_account_configs(self, account: dict, payload: dict, resolve_qq: Callable[[dict], str]) -> dict[str, Any]:
|
|
45
|
+
return self._service._configs.update_account_configs(account, payload, resolve_qq)
|
|
46
|
+
|
|
47
|
+
def check_launch_issues(self, account: dict, resolve_qq: Callable[[dict], str]) -> list[str]:
|
|
48
|
+
return self._service._launch.check_launch_issues(account, resolve_qq)
|
|
49
|
+
|
|
50
|
+
def describe_account_data_paths(self, account: dict) -> dict[str, object]:
|
|
51
|
+
return self._service._launch.describe_account_data_paths(account)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
__all__ = ["NapcatRuntimeBackend"]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""协议端运行时抽象:新栈实现 ``ProtocolRuntimeBackend``,并用 ``register_protocol_runtime_backend`` 登记。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Protocol, runtime_checkable
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@runtime_checkable
|
|
12
|
+
class ProtocolRuntimeBackend(Protocol):
|
|
13
|
+
"""单账号协议栈的配置准备与校验"""
|
|
14
|
+
|
|
15
|
+
kind: str
|
|
16
|
+
|
|
17
|
+
def apply_defaults(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
18
|
+
"""补齐 command/args/program_dir/account_data_dir 等默认值。"""
|
|
19
|
+
...
|
|
20
|
+
|
|
21
|
+
def prepare_dirs(self, account: dict) -> None:
|
|
22
|
+
"""创建账号数据目录等。"""
|
|
23
|
+
...
|
|
24
|
+
|
|
25
|
+
def sync_all_configs(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
26
|
+
"""写入协议栈所需的全部配置文件。"""
|
|
27
|
+
...
|
|
28
|
+
|
|
29
|
+
def sync_onebot(self, account: dict, resolve_qq: Callable[[dict], str]) -> None: ...
|
|
30
|
+
|
|
31
|
+
def sync_webui(self, account: dict, resolve_qq: Callable[[dict], str]) -> None: ...
|
|
32
|
+
|
|
33
|
+
def read_webui_into_account(self, account: dict) -> bool: ...
|
|
34
|
+
|
|
35
|
+
def get_account_configs(self, account: dict, resolve_qq: Callable[[dict], str]) -> dict[str, Any]: ...
|
|
36
|
+
|
|
37
|
+
def update_account_configs(
|
|
38
|
+
self, account: dict, payload: dict, resolve_qq: Callable[[dict], str]
|
|
39
|
+
) -> dict[str, Any]: ...
|
|
40
|
+
|
|
41
|
+
def check_launch_issues(self, account: dict, resolve_qq: Callable[[dict], str]) -> list[str]: ...
|
|
42
|
+
|
|
43
|
+
def describe_account_data_paths(self, account: dict) -> dict[str, object]: ...
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
__all__ = ["ProtocolRuntimeBackend"]
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
"""SnowLuma 协议栈:扁平 OneBot、runtime.json;启动由 LaunchManager 的 snowluma 分支处理。"""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
from typing import TYPE_CHECKING, Any
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from collections.abc import Callable
|
|
9
|
+
|
|
10
|
+
from ..service import PallasProtocolService
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class SnowlumaRuntimeBackend:
|
|
14
|
+
kind = "snowluma"
|
|
15
|
+
|
|
16
|
+
def __init__(self, service: PallasProtocolService) -> None:
|
|
17
|
+
self._service = service
|
|
18
|
+
|
|
19
|
+
def apply_defaults(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
20
|
+
self._service._launch.apply_defaults(account, resolve_qq)
|
|
21
|
+
|
|
22
|
+
def prepare_dirs(self, account: dict) -> None:
|
|
23
|
+
self._service._launch.prepare_dirs(account)
|
|
24
|
+
|
|
25
|
+
def sync_all_configs(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
26
|
+
from ..snowluma_config import sync_snowluma_onebot, sync_snowluma_runtime_json
|
|
27
|
+
|
|
28
|
+
cfg = self._service._configs
|
|
29
|
+
wmin = int(getattr(self._service._config, "pallas_protocol_webui_port_min", 6099) or 6099)
|
|
30
|
+
pc = self._service._config
|
|
31
|
+
sync_snowluma_runtime_json(account, webui_port_fallback_min=wmin, plugin_config=pc)
|
|
32
|
+
sync_snowluma_onebot(cfg, account, resolve_qq, plugin_config=pc)
|
|
33
|
+
|
|
34
|
+
def sync_onebot(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
35
|
+
from ..snowluma_config import sync_snowluma_onebot
|
|
36
|
+
|
|
37
|
+
sync_snowluma_onebot(
|
|
38
|
+
self._service._configs,
|
|
39
|
+
account,
|
|
40
|
+
resolve_qq,
|
|
41
|
+
plugin_config=self._service._config,
|
|
42
|
+
)
|
|
43
|
+
|
|
44
|
+
def sync_webui(self, account: dict, resolve_qq: Callable[[dict], str]) -> None:
|
|
45
|
+
from ..snowluma_config import sync_snowluma_runtime_json
|
|
46
|
+
|
|
47
|
+
wmin = int(getattr(self._service._config, "pallas_protocol_webui_port_min", 6099) or 6099)
|
|
48
|
+
sync_snowluma_runtime_json(account, webui_port_fallback_min=wmin, plugin_config=self._service._config)
|
|
49
|
+
|
|
50
|
+
def read_webui_into_account(self, account: dict) -> bool:
|
|
51
|
+
from ..snowluma_config import read_snowluma_runtime_into_account
|
|
52
|
+
|
|
53
|
+
return read_snowluma_runtime_into_account(account)
|
|
54
|
+
|
|
55
|
+
def get_account_configs(self, account: dict, resolve_qq: Callable[[dict], str]) -> dict[str, Any]:
|
|
56
|
+
from ..snowluma_config import get_snowluma_account_configs
|
|
57
|
+
|
|
58
|
+
return get_snowluma_account_configs(self._service._configs, account, resolve_qq)
|
|
59
|
+
|
|
60
|
+
def update_account_configs(self, account: dict, payload: dict, resolve_qq: Callable[[dict], str]) -> dict[str, Any]:
|
|
61
|
+
from ..snowluma_config import update_snowluma_account_configs
|
|
62
|
+
|
|
63
|
+
return update_snowluma_account_configs(self._service._configs, account, payload, resolve_qq)
|
|
64
|
+
|
|
65
|
+
def check_launch_issues(self, account: dict, resolve_qq: Callable[[dict], str]) -> list[str]:
|
|
66
|
+
return self._service._launch.check_launch_issues(account, resolve_qq)
|
|
67
|
+
|
|
68
|
+
def describe_account_data_paths(self, account: dict) -> dict[str, object]:
|
|
69
|
+
return self._service._launch.describe_account_data_paths(account)
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
__all__ = ["SnowlumaRuntimeBackend"]
|