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.
Files changed (38) hide show
  1. pallas_plugin_protocol/__init__.py +93 -0
  2. pallas_plugin_protocol/backends/__init__.py +55 -0
  3. pallas_plugin_protocol/backends/napcat.py +54 -0
  4. pallas_plugin_protocol/backends/protocol.py +46 -0
  5. pallas_plugin_protocol/backends/snowluma.py +72 -0
  6. pallas_plugin_protocol/config.py +624 -0
  7. pallas_plugin_protocol/config_manager.py +285 -0
  8. pallas_plugin_protocol/contract.py +69 -0
  9. pallas_plugin_protocol/docker_cli.py +120 -0
  10. pallas_plugin_protocol/docker_onebot_host.py +104 -0
  11. pallas_plugin_protocol/importer.py +246 -0
  12. pallas_plugin_protocol/launch_manager.py +855 -0
  13. pallas_plugin_protocol/linux_docker.py +233 -0
  14. pallas_plugin_protocol/platform/__init__.py +19 -0
  15. pallas_plugin_protocol/platform/base.py +46 -0
  16. pallas_plugin_protocol/platform/posix.py +97 -0
  17. pallas_plugin_protocol/platform/windows.py +152 -0
  18. pallas_plugin_protocol/runtime/__init__.py +30 -0
  19. pallas_plugin_protocol/runtime/installer.py +949 -0
  20. pallas_plugin_protocol/runtime/snowluma_installer.py +542 -0
  21. pallas_plugin_protocol/runtime/tag_paths.py +22 -0
  22. pallas_plugin_protocol/service.py +2193 -0
  23. pallas_plugin_protocol/snowluma_config.py +265 -0
  24. pallas_plugin_protocol/snowluma_docker.py +168 -0
  25. pallas_plugin_protocol/web/__init__.py +3 -0
  26. pallas_plugin_protocol/web/pages.py +4114 -0
  27. pallas_plugin_protocol/web/routes.py +941 -0
  28. pallas_plugin_protocol/web/static/pallas_ui/InterVariable.woff2 +0 -0
  29. pallas_plugin_protocol/web/static/pallas_ui/favicon.png +0 -0
  30. pallas_plugin_protocol/web/static/pallas_ui/fonts.css +7 -0
  31. pallas_plugin_protocol/web/static/pallas_ui/shell.css +2133 -0
  32. pallas_plugin_protocol-4.0.1.dist-info/METADATA +761 -0
  33. pallas_plugin_protocol-4.0.1.dist-info/RECORD +38 -0
  34. pallas_plugin_protocol-4.0.1.dist-info/WHEEL +4 -0
  35. pallas_plugin_protocol-4.0.1.dist-info/licenses/LICENSE +661 -0
  36. pallas_plugin_relogin_bot/__init__.py +306 -0
  37. pallas_plugin_relogin_bot/service.py +353 -0
  38. 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"]