ErisPulse 2.7.0.dev3__tar.gz → 2.7.0.dev5__tar.gz
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.
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/PKG-INFO +1 -1
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/pyproject.toml +1 -1
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/cli.py +17 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/create.py +185 -188
- erispulse-2.7.0.dev5/src/ErisPulse/CLI/commands/doctor.py +127 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/init.py +1 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/list.py +29 -9
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/run.py +2 -1
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/types.py +3 -2
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/console.py +2 -2
- erispulse-2.7.0.dev5/src/ErisPulse/CLI/constants.py +46 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/__init__.py +82 -2
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/en.py +33 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/ja.py +33 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/ru.py +33 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/zh_cn.py +33 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/zh_tw.py +33 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/display.py +4 -2
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/package_manager.py +229 -38
- erispulse-2.7.0.dev5/src/ErisPulse/CLI/utils/scaffold_text.py +338 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/__init__.py +2 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/adapter.py +23 -14
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/config_schema.py +93 -18
- erispulse-2.7.0.dev5/src/ErisPulse/Core/Bases/converter.py +80 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/send_rules.py +3 -3
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/command.py +60 -25
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/adapter.py +33 -2
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/client.py +4 -3
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/config.py +23 -16
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/constants.py +66 -1
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/en.py +28 -7
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/ja.py +28 -7
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/ru.py +28 -7
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/zh_cn.py +28 -7
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/zh_tw.py +28 -7
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/lifecycle.py +61 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/logger.py +31 -7
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/master.py +1 -1
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/router.py +183 -20
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/storage.py +30 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/adapter.py +3 -2
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/module.py +3 -2
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/adapter.py +3 -2
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/module.py +10 -7
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/__init__.py +5 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/config_schema.py +2 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/frame_config.py +51 -1
- erispulse-2.7.0.dev5/src/ErisPulse/runtime/memory.py +121 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/sdk.py +106 -29
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/.gitignore +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/LICENSE +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/README.pypi.md +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/base.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/install.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/language.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/self_update.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/hints.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/registry.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/file_watcher.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/client.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/errors.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/i18n_schema.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/kv_builder.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/manager.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/module.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/router.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/send_builder.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/storage.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/websocket.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/base.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/message.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/message_builder.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/meta.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/notice.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/request.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/session_type.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/wrapper.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/error.css +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/error.html +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/root.css +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/root.html +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/constants.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/module.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/__main__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/bases/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/bases/finder.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/bases/__init__.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/bases/loader.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/strategy.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/strict.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/py.typed +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/context.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/diagnostics.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/exceptions.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/hints.py +0 -0
- {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/tasks.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ErisPulse
|
|
3
|
-
Version: 2.7.0.
|
|
3
|
+
Version: 2.7.0.dev5
|
|
4
4
|
Summary: Event-driven modular async bot framework — write once, deploy to any platform | 事件驱动的模块化异步机器人框架,一次编写,多平台部署
|
|
5
5
|
Project-URL: Homepage, https://github.com/ErisPulse/ErisPulse
|
|
6
6
|
Project-URL: Documentation, https://www.erisdev.com#docs
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "ErisPulse"
|
|
7
|
-
version = "2.7.0-dev.
|
|
7
|
+
version = "2.7.0-dev.5"
|
|
8
8
|
description = "Event-driven modular async bot framework — write once, deploy to any platform | 事件驱动的模块化异步机器人框架,一次编写,多平台部署"
|
|
9
9
|
readme = "README.pypi.md"
|
|
10
10
|
requires-python = ">=3.10"
|
|
@@ -60,6 +60,19 @@ class CLI:
|
|
|
60
60
|
default=0,
|
|
61
61
|
help=i18n.t("cli.parser.verbose_help"),
|
|
62
62
|
)
|
|
63
|
+
parser.add_argument(
|
|
64
|
+
"--no-color",
|
|
65
|
+
action="store_true",
|
|
66
|
+
default=False,
|
|
67
|
+
help=i18n.t("cli.parser.no_color_help"),
|
|
68
|
+
)
|
|
69
|
+
parser.add_argument(
|
|
70
|
+
"--yes",
|
|
71
|
+
"-y",
|
|
72
|
+
action="store_true",
|
|
73
|
+
default=False,
|
|
74
|
+
help=i18n.t("cli.parser.yes_help"),
|
|
75
|
+
)
|
|
63
76
|
|
|
64
77
|
# 子命令
|
|
65
78
|
subparsers = parser.add_subparsers(
|
|
@@ -286,6 +299,10 @@ class CLI:
|
|
|
286
299
|
args, unknown = self.parser.parse_known_args()
|
|
287
300
|
args._unknown_args = unknown
|
|
288
301
|
|
|
302
|
+
# --no-color:禁用 Rich 控制台着色(CI / 日志采集场景)
|
|
303
|
+
if getattr(args, "no_color", False):
|
|
304
|
+
console.no_color = True
|
|
305
|
+
|
|
289
306
|
print_banner()
|
|
290
307
|
|
|
291
308
|
# 前几次启动时提醒用户确认语言(检测错误时用户仍可看懂多语言提示)
|
|
@@ -16,6 +16,7 @@ from ..base import Command
|
|
|
16
16
|
from ..console import console
|
|
17
17
|
from ..i18n import i18n
|
|
18
18
|
from ..utils.display import prompt_validated, section_header
|
|
19
|
+
from ..utils.scaffold_text import ScaffoldText
|
|
19
20
|
|
|
20
21
|
_LICENSE_TEMPLATE = """MIT License
|
|
21
22
|
|
|
@@ -65,42 +66,57 @@ _MODULE_INIT = """from .Core import Main
|
|
|
65
66
|
_MODULE_CORE = """from dataclasses import dataclass, field
|
|
66
67
|
from ErisPulse.Core.Bases import BaseConfig, BaseI18n, BaseModule, I18nKey
|
|
67
68
|
from ErisPulse.Core.Event import command, message, notice
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
@dataclass
|
|
71
|
-
class {name}Config(BaseConfig):
|
|
72
|
-
\"\"\"{name} 模块配置\"\"\"
|
|
73
|
-
enabled: bool = field(
|
|
74
|
-
default=True,
|
|
75
|
-
metadata={{
|
|
76
|
-
\"description\": \"是否启用模块\",
|
|
77
|
-
}},
|
|
78
|
-
)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
class {name}I18n(BaseI18n):
|
|
82
|
-
\"\"\"{name} 模块翻译键声明
|
|
83
|
-
|
|
84
|
-
声明的翻译键会被框架自动注册到 i18n 系统。
|
|
85
|
-
键路径自动生成为: <模块名>.<属性名>
|
|
86
|
-
\"\"\"
|
|
87
|
-
# 示例:取消注释并修改以启用
|
|
88
|
-
# welcome: I18nKey = I18nKey(
|
|
89
|
-
# default="Welcome to {name}!",
|
|
90
|
-
# zh_CN="欢迎使用 {name}!",
|
|
91
|
-
# en="Welcome to {name}!",
|
|
92
|
-
# )
|
|
69
|
+
from ErisPulse.Core.i18n import i18n
|
|
93
70
|
|
|
94
71
|
|
|
95
72
|
class Main(BaseModule):
|
|
96
73
|
\"\"\"
|
|
97
|
-
{
|
|
98
|
-
|
|
99
|
-
继承自BaseModule基类,实现了标准化的模块生命周期管理和事件处理
|
|
74
|
+
{text[module.doc]}
|
|
100
75
|
\"\"\"
|
|
101
76
|
|
|
102
|
-
|
|
103
|
-
|
|
77
|
+
# {text[module.config_hint]}
|
|
78
|
+
@dataclass
|
|
79
|
+
class ConfigClass(BaseConfig):
|
|
80
|
+
\"\"\"{text[module.config_doc]}\"\"\"
|
|
81
|
+
|
|
82
|
+
enabled: bool = field(
|
|
83
|
+
default=True,
|
|
84
|
+
metadata={{
|
|
85
|
+
\"description\": {{\"i18n\": \"module.{name}.enabled\", \"default\": \"Enable module\"}},
|
|
86
|
+
}},
|
|
87
|
+
)
|
|
88
|
+
|
|
89
|
+
# {text[module.i18n_hint]}
|
|
90
|
+
class I18nClass(BaseI18n):
|
|
91
|
+
\"\"\"{name} translation keys\"\"\"
|
|
92
|
+
|
|
93
|
+
enabled: I18nKey = I18nKey(
|
|
94
|
+
key=\"module.{name}.enabled\",
|
|
95
|
+
default=\"Enable module\",
|
|
96
|
+
zh_CN=\"是否启用模块\",
|
|
97
|
+
en=\"Enable module\",
|
|
98
|
+
ja=\"モジュールを有効にする\",
|
|
99
|
+
ru=\"Включить модуль\",
|
|
100
|
+
zh_TW=\"啟用模組\",
|
|
101
|
+
)
|
|
102
|
+
hello_help: I18nKey = I18nKey(
|
|
103
|
+
key=\"module.{name}.command.hello.help\",
|
|
104
|
+
default=\"Send a greeting message\",
|
|
105
|
+
zh_CN=\"发送问候消息\",
|
|
106
|
+
en=\"Send a greeting message\",
|
|
107
|
+
ja=\"挨拶メッセージを送信\",
|
|
108
|
+
ru=\"Отправить приветствие\",
|
|
109
|
+
zh_TW=\"發送問候訊息\",
|
|
110
|
+
)
|
|
111
|
+
hello_reply: I18nKey = I18nKey(
|
|
112
|
+
key=\"module.{name}.command.hello.reply\",
|
|
113
|
+
default=\"Hello from {name}!\",
|
|
114
|
+
zh_CN=\"来自 {name} 的问候!\",
|
|
115
|
+
en=\"Hello from {name}!\",
|
|
116
|
+
ja=\"{name} からの挨拶です!\",
|
|
117
|
+
ru=\"Привет от {name}!\",
|
|
118
|
+
zh_TW=\"來自 {name} 的問候!\",
|
|
119
|
+
)
|
|
104
120
|
|
|
105
121
|
def __init__(self, sdk=None):
|
|
106
122
|
from ErisPulse import sdk as _sdk
|
|
@@ -110,7 +126,7 @@ class Main(BaseModule):
|
|
|
110
126
|
self.adapter = self.sdk.adapter
|
|
111
127
|
self.client = self.sdk.client
|
|
112
128
|
|
|
113
|
-
self.logger.info(\"{name}
|
|
129
|
+
self.logger.info((\"{text[module.log.init_done]}\").format(name=\"{name}\"))
|
|
114
130
|
|
|
115
131
|
@staticmethod
|
|
116
132
|
def get_load_strategy():
|
|
@@ -122,35 +138,39 @@ class Main(BaseModule):
|
|
|
122
138
|
|
|
123
139
|
async def on_load(self, event: dict) -> bool:
|
|
124
140
|
\"\"\"
|
|
125
|
-
|
|
141
|
+
{text[module.on_load_doc]}
|
|
126
142
|
|
|
127
|
-
:param event:
|
|
128
|
-
:return:
|
|
143
|
+
:param event: event data
|
|
144
|
+
:return: processing result
|
|
129
145
|
\"\"\"
|
|
130
146
|
await self._register_commands()
|
|
131
147
|
await self._register_message_handlers()
|
|
132
|
-
self.logger.info(
|
|
148
|
+
self.logger.info((\"{text[module.log.loaded]}\").format(event=event))
|
|
133
149
|
return True
|
|
134
150
|
|
|
135
151
|
async def on_unload(self, event: dict) -> bool:
|
|
136
152
|
\"\"\"
|
|
137
|
-
|
|
153
|
+
{text[module.on_unload_doc]}
|
|
138
154
|
|
|
139
|
-
:param event:
|
|
140
|
-
:return:
|
|
155
|
+
:param event: event data
|
|
156
|
+
:return: processing result
|
|
141
157
|
\"\"\"
|
|
142
|
-
self.logger.info(
|
|
158
|
+
self.logger.info((\"{text[module.log.unloaded]}\").format(event=event))
|
|
143
159
|
return True
|
|
144
160
|
|
|
161
|
+
def on_config_update(self, old_config, new_config):
|
|
162
|
+
\"\"\"{text[module.config_updated_doc]}\"\"\"
|
|
163
|
+
self.logger.info(\"{text[module.log.config_updated]}\")
|
|
164
|
+
|
|
145
165
|
async def _register_commands(self):
|
|
146
|
-
@command(\"hello\", help
|
|
166
|
+
@command(\"hello\", help=i18n.t(\"module.{name}.command.hello.help\"))
|
|
147
167
|
async def hello_command(event):
|
|
148
|
-
await event.reply(\"
|
|
168
|
+
await event.reply(i18n.t(\"module.{name}.command.hello.reply\"))
|
|
149
169
|
|
|
150
170
|
async def _register_message_handlers(self):
|
|
151
171
|
@message.on_private_message()
|
|
152
172
|
async def private_message_handler(event):
|
|
153
|
-
self.logger.info(
|
|
173
|
+
self.logger.info((\"{text[module.log.private_message]}\").format(content=event.get_text()))
|
|
154
174
|
|
|
155
175
|
@message.on_group_message()
|
|
156
176
|
async def group_message_handler(event):
|
|
@@ -158,7 +178,7 @@ class Main(BaseModule):
|
|
|
158
178
|
|
|
159
179
|
@notice.on_friend_add()
|
|
160
180
|
async def friend_add_handler(event):
|
|
161
|
-
self.logger.info(
|
|
181
|
+
self.logger.info((\"{text[module.log.friend_add]}\").format(nickname=event.get_user_nickname()))
|
|
162
182
|
"""
|
|
163
183
|
|
|
164
184
|
_ADAPTER_PYPROJECT = """[project]
|
|
@@ -193,89 +213,79 @@ _ADAPTER_CORE = """import asyncio
|
|
|
193
213
|
import json
|
|
194
214
|
from dataclasses import dataclass, field
|
|
195
215
|
from ErisPulse.Core import BaseAdapter
|
|
196
|
-
from ErisPulse.Core.Bases import BaseConfig,
|
|
216
|
+
from ErisPulse.Core.Bases import BaseConfig, BaseI18n, I18nKey
|
|
197
217
|
from ErisPulse.Core import router
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
@dataclass
|
|
201
|
-
class {name}Config(BaseConfig):
|
|
202
|
-
\"\"\"{name} 适配器配置\"\"\"
|
|
203
|
-
endpoint: str = field(
|
|
204
|
-
default="https://api.example.com",
|
|
205
|
-
metadata={{
|
|
206
|
-
"description": {{"i18n": "adapter.{name}.endpoint", "default": "平台 API 地址"}},
|
|
207
|
-
"required": False,
|
|
208
|
-
"ui": {{"widget": "text", "group": "connection", "order": 1}},
|
|
209
|
-
}},
|
|
210
|
-
)
|
|
211
|
-
token: str = field(
|
|
212
|
-
default="",
|
|
213
|
-
metadata={{
|
|
214
|
-
"description": {{"i18n": "adapter.{name}.token", "default": "平台 Token"}},
|
|
215
|
-
"required": True,
|
|
216
|
-
"secret": True,
|
|
217
|
-
"ui": {{"widget": "password", "group": "basic", "order": 2}},
|
|
218
|
-
}},
|
|
219
|
-
)
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
class {name}I18n(BaseI18n):
|
|
223
|
-
\"\"\"{name} 适配器翻译键声明
|
|
224
|
-
|
|
225
|
-
声明的翻译键会被框架自动注册到 i18n 系统。
|
|
226
|
-
这里集中管理适配器用到的所有翻译键(含配置描述引用的键)。
|
|
227
|
-
\"\"\"
|
|
228
|
-
# 注册配置描述中引用的 i18n 键的多语言译文
|
|
229
|
-
endpoint: I18nKey = I18nKey(
|
|
230
|
-
default="Platform API Endpoint",
|
|
231
|
-
zh_CN="平台 API 地址",
|
|
232
|
-
en="Platform API Endpoint",
|
|
233
|
-
ja="APIアドレス",
|
|
234
|
-
ru="API адрес",
|
|
235
|
-
zh_TW="API 位址",
|
|
236
|
-
)
|
|
237
|
-
token: I18nKey = I18nKey(
|
|
238
|
-
default="Platform Token",
|
|
239
|
-
zh_CN="平台 Token",
|
|
240
|
-
en="Platform Token",
|
|
241
|
-
ja="トークン",
|
|
242
|
-
ru="Токен",
|
|
243
|
-
zh_TW="權杖",
|
|
244
|
-
)
|
|
218
|
+
from ErisPulse.Core.i18n import i18n
|
|
245
219
|
|
|
246
220
|
|
|
247
221
|
class {name}(BaseAdapter):
|
|
248
222
|
\"\"\"
|
|
249
|
-
{
|
|
250
|
-
|
|
251
|
-
继承自 BaseAdapter 基类,使用声明式配置管理(ConfigClass),
|
|
252
|
-
实现了 SendDSL 风格的链式调用接口和 Bot 状态追踪。
|
|
223
|
+
{text[adapter.doc]}
|
|
253
224
|
\"\"\"
|
|
254
225
|
|
|
255
|
-
|
|
256
|
-
|
|
226
|
+
# {text[adapter.config_hint]}
|
|
227
|
+
@dataclass
|
|
228
|
+
class ConfigClass(BaseConfig):
|
|
229
|
+
\"\"\"{text[adapter.config_doc]}\"\"\"
|
|
230
|
+
|
|
231
|
+
endpoint: str = field(
|
|
232
|
+
default="https://api.example.com",
|
|
233
|
+
metadata={{
|
|
234
|
+
"description": {{"i18n": "adapter.{name}.endpoint", "default": "Platform API Endpoint"}},
|
|
235
|
+
"required": False,
|
|
236
|
+
"ui": {{"widget": "text", "group": "connection", "order": 1}},
|
|
237
|
+
}},
|
|
238
|
+
)
|
|
239
|
+
token: str = field(
|
|
240
|
+
default="",
|
|
241
|
+
metadata={{
|
|
242
|
+
"description": {{"i18n": "adapter.{name}.token", "default": "Platform Token"}},
|
|
243
|
+
"required": True,
|
|
244
|
+
"secret": True,
|
|
245
|
+
"ui": {{"widget": "password", "group": "basic", "order": 2}},
|
|
246
|
+
}},
|
|
247
|
+
)
|
|
248
|
+
|
|
249
|
+
# {text[adapter.i18n_hint]}
|
|
250
|
+
class I18nClass(BaseI18n):
|
|
251
|
+
\"\"\"{name} translation keys\"\"\"
|
|
252
|
+
|
|
253
|
+
endpoint: I18nKey = I18nKey(
|
|
254
|
+
key="adapter.{name}.endpoint",
|
|
255
|
+
default="Platform API Endpoint",
|
|
256
|
+
zh_CN="平台 API 地址",
|
|
257
|
+
en="Platform API Endpoint",
|
|
258
|
+
ja="APIアドレス",
|
|
259
|
+
ru="API адрес",
|
|
260
|
+
zh_TW="API 位址",
|
|
261
|
+
)
|
|
262
|
+
token: I18nKey = I18nKey(
|
|
263
|
+
key="adapter.{name}.token",
|
|
264
|
+
default="Platform Token",
|
|
265
|
+
zh_CN="平台 Token",
|
|
266
|
+
en="Platform Token",
|
|
267
|
+
ja="トークン",
|
|
268
|
+
ru="Токен",
|
|
269
|
+
zh_TW="權杖",
|
|
270
|
+
)
|
|
257
271
|
|
|
258
272
|
def __init__(self, sdk=None):
|
|
259
|
-
super().__init__()
|
|
273
|
+
super().__init__(sdk=sdk)
|
|
274
|
+
self.logger = self._get_logger()
|
|
260
275
|
self.converter = self._setup_converter()
|
|
261
276
|
self.convert = self.converter.convert
|
|
262
277
|
|
|
278
|
+
def on_config_update(self, old_config, new_config):
|
|
279
|
+
\"\"\"Called when adapter config hot-reloads\"\"\"
|
|
280
|
+
self.logger.info("{text[adapter.log.config_updated]}")
|
|
281
|
+
|
|
263
282
|
def _setup_converter(self):
|
|
264
283
|
from .Converter import {converter_name}
|
|
265
284
|
return {converter_name}()
|
|
266
285
|
|
|
267
286
|
class Send(BaseAdapter.Send):
|
|
268
287
|
\"\"\"
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
At / AtAll / Reply / Using / To 由框架基类内置处理。
|
|
272
|
-
标准发送方法(Text/Image/Voice/Video/File)已从 SendDSL 基类继承,
|
|
273
|
-
默认委托给 Raw_ob12,无需重复实现。
|
|
274
|
-
使用 self._apply_modifiers(message) 合并修饰器到消息段。
|
|
275
|
-
使用 self.send_context 获取发送上下文 (target_type, target_id, account_id)。
|
|
276
|
-
|
|
277
|
-
支持链式调用:
|
|
278
|
-
Send.To("group", "123").At("456").Reply("789").Text("hi")
|
|
288
|
+
{text[adapter.dsl.send_doc]}
|
|
279
289
|
\"\"\"
|
|
280
290
|
|
|
281
291
|
def Raw_ob12(self, message, **kwargs):
|
|
@@ -289,22 +299,18 @@ class {name}(BaseAdapter):
|
|
|
289
299
|
)
|
|
290
300
|
return asyncio.create_task(_do_send())
|
|
291
301
|
|
|
292
|
-
#
|
|
293
|
-
#
|
|
302
|
+
# {text[adapter.dsl.send_std_methods_hint]}
|
|
303
|
+
# {text[adapter.dsl.send_override_hint]}
|
|
294
304
|
# def Text(self, text: str):
|
|
295
305
|
# return self.Raw_ob12([{{"type": "text", "data": {{"text": text}}}}])
|
|
296
306
|
|
|
297
|
-
#
|
|
307
|
+
# {text[adapter.dsl.send_extra_methods_hint]}
|
|
298
308
|
# def Sticker(self, sticker_id: str):
|
|
299
309
|
# return self.Raw_ob12([{{"type": "sticker", "data": {{"id": sticker_id}}}}])
|
|
300
310
|
|
|
301
311
|
class Request(BaseAdapter.Request):
|
|
302
312
|
\"\"\"
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
适配器应重写 accept / reject 实现平台特定的请求处理逻辑。
|
|
306
|
-
如果平台不支持请求操作,可不实现此内部类。
|
|
307
|
-
基类默认返回 retcode=10002(不支持的操作)。
|
|
313
|
+
{text[adapter.dsl.request_doc]}
|
|
308
314
|
\"\"\"
|
|
309
315
|
|
|
310
316
|
async def _do_accept(self, **kwargs):
|
|
@@ -341,36 +347,32 @@ class {name}(BaseAdapter):
|
|
|
341
347
|
|
|
342
348
|
class Api(BaseAdapter.Api):
|
|
343
349
|
\"\"\"
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
提供跨平台的 OneBot12 标准动作(信息查询/群管理/消息管理/文件操作)。
|
|
347
|
-
默认实现委托给 call_api,适配器可覆盖单个方法映射到平台原生 API。
|
|
348
|
-
平台扩展动作通过 call("prefix.action", **params) 调用。
|
|
350
|
+
{text[adapter.dsl.api_doc]}
|
|
349
351
|
\"\"\"
|
|
350
352
|
|
|
351
|
-
#
|
|
352
|
-
#
|
|
353
|
+
# {text[adapter.dsl.api_std_methods_hint]}
|
|
354
|
+
# {text[adapter.dsl.api_override_hint]}
|
|
353
355
|
# async def get_user_info(self, user_id: str) -> dict:
|
|
354
356
|
# raw = await self._adapter._request("GET", f"/users/{{user_id}}")
|
|
355
357
|
# return self._adapter.make_response(data={{...}}, raw=raw)
|
|
356
358
|
|
|
357
359
|
async def start(self):
|
|
358
|
-
\"\"\"
|
|
360
|
+
\"\"\"Start the adapter\"\"\"
|
|
359
361
|
cfg = self.cfg
|
|
360
|
-
self.logger.info(
|
|
362
|
+
self.logger.info("{text[adapter.log.starting]}")
|
|
361
363
|
|
|
362
364
|
router.register_websocket(
|
|
363
365
|
module_name="{entry_key}",
|
|
364
366
|
path="/ws",
|
|
365
367
|
handler=self._ws_handler,
|
|
366
368
|
)
|
|
367
|
-
self.logger.info("
|
|
369
|
+
self.logger.info("{text[adapter.log.ws_registered]}")
|
|
368
370
|
|
|
369
371
|
async def _ws_handler(self, websocket):
|
|
370
372
|
bot_id = self._get_bot_id()
|
|
371
373
|
|
|
372
374
|
await self.emit_meta("connect", bot_id, user_name="{name}")
|
|
373
|
-
self.logger.info(
|
|
375
|
+
self.logger.info("{text[adapter.log.bot_connected]}")
|
|
374
376
|
|
|
375
377
|
try:
|
|
376
378
|
while True:
|
|
@@ -380,18 +382,18 @@ class {name}(BaseAdapter):
|
|
|
380
382
|
if event:
|
|
381
383
|
await self.adapter.emit(event)
|
|
382
384
|
except Exception as e:
|
|
383
|
-
self.logger.warning(
|
|
385
|
+
self.logger.warning("{text[adapter.log.connection_lost]}")
|
|
384
386
|
finally:
|
|
385
387
|
await self.emit_meta("disconnect", bot_id)
|
|
386
|
-
self.logger.info(
|
|
388
|
+
self.logger.info("{text[adapter.log.bot_disconnected]}")
|
|
387
389
|
|
|
388
390
|
async def shutdown(self):
|
|
389
|
-
\"\"\"
|
|
391
|
+
\"\"\"Shut down the adapter\"\"\"
|
|
390
392
|
router.unregister_websocket("{entry_key}", "/ws")
|
|
391
|
-
self.logger.info("
|
|
393
|
+
self.logger.info("{text[adapter.log.shutdown]}")
|
|
392
394
|
|
|
393
395
|
async def call_api(self, endpoint: str, **params):
|
|
394
|
-
\"\"\"
|
|
396
|
+
\"\"\"Call platform API\"\"\"
|
|
395
397
|
from ErisPulse.Core import client
|
|
396
398
|
|
|
397
399
|
cfg = self.cfg
|
|
@@ -413,77 +415,51 @@ class {name}(BaseAdapter):
|
|
|
413
415
|
raw=result,
|
|
414
416
|
)
|
|
415
417
|
except Exception as e:
|
|
416
|
-
self.logger.error(
|
|
418
|
+
self.logger.error(("{text[adapter.log.api_call_failed]}").format(error=e))
|
|
417
419
|
return self.make_error(message=str(e))
|
|
418
420
|
"""
|
|
419
421
|
|
|
420
422
|
_ADAPTER_CONVERTER = """\"\"\"
|
|
421
|
-
{
|
|
422
|
-
|
|
423
|
-
将平台原生事件转换为 OneBot12 标准格式(正向转换)。
|
|
424
|
-
反向转换(发送方向)由适配器的 Send.Raw_ob12() 处理。
|
|
423
|
+
{text[adapter.converter_doc]}
|
|
425
424
|
\"\"\"
|
|
426
425
|
|
|
427
|
-
import
|
|
428
|
-
import uuid
|
|
426
|
+
from ErisPulse.Core.Bases import BaseConverter
|
|
429
427
|
|
|
430
428
|
|
|
431
|
-
class {converter_name}:
|
|
429
|
+
class {converter_name}(BaseConverter):
|
|
432
430
|
\"\"\"
|
|
433
|
-
{
|
|
431
|
+
{converter_name} converter class
|
|
434
432
|
|
|
435
|
-
|
|
436
|
-
|
|
433
|
+
Converts platform-specific event formats to the ErisPulse standard OneBot12 format.
|
|
434
|
+
Inherits BaseConverter to reuse common field construction (build_base_event)
|
|
435
|
+
and message-segment helpers (text/at/image).
|
|
437
436
|
\"\"\"
|
|
438
437
|
|
|
439
|
-
def
|
|
438
|
+
def __init__(self):
|
|
439
|
+
super().__init__(platform="{entry_key}")
|
|
440
|
+
|
|
441
|
+
def convert(self, raw_event: dict) -> dict | None:
|
|
440
442
|
\"\"\"
|
|
441
|
-
|
|
443
|
+
Convert a platform-native event to the OneBot12 standard format
|
|
442
444
|
|
|
443
|
-
:param raw_event:
|
|
444
|
-
:return: OneBot12
|
|
445
|
+
:param raw_event: raw platform event data
|
|
446
|
+
:return: OneBot12 event dict, or None if unrecognized
|
|
445
447
|
\"\"\"
|
|
446
448
|
if not isinstance(raw_event, dict):
|
|
447
449
|
return None
|
|
448
450
|
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
event = {{
|
|
453
|
-
"id": str(event_id),
|
|
454
|
-
"time": int(timestamp),
|
|
455
|
-
"type": self._convert_type(raw_event),
|
|
456
|
-
"detail_type": self._convert_detail_type(raw_event),
|
|
457
|
-
"platform": "{entry_key}",
|
|
458
|
-
"self": {{
|
|
459
|
-
"platform": "{entry_key}",
|
|
460
|
-
"user_id": str(raw_event.get("self_id", "")),
|
|
461
|
-
}},
|
|
462
|
-
"{entry_key}_raw": raw_event,
|
|
463
|
-
"{entry_key}_raw_type": raw_event.get("type", ""),
|
|
464
|
-
}}
|
|
465
|
-
|
|
466
|
-
if event["type"] == "message":
|
|
467
|
-
event["user_id"] = str(raw_event.get("sender_id", ""))
|
|
468
|
-
event["message"] = self._convert_message_segments(raw_event.get("content", ""))
|
|
469
|
-
event["alt_message"] = raw_event.get("content", "")
|
|
451
|
+
event_type = raw_event.get("type", "")
|
|
452
|
+
base = self.build_base_event(raw_event, event_type)
|
|
470
453
|
|
|
471
|
-
|
|
454
|
+
if event_type == "message":
|
|
455
|
+
base["type"] = "message"
|
|
456
|
+
base["detail_type"] = "private" if raw_event.get("is_private") else "group"
|
|
457
|
+
base["user_id"] = str(raw_event.get("sender_id", ""))
|
|
458
|
+
base["message"] = [self.text(raw_event.get("content", ""))]
|
|
459
|
+
base["alt_message"] = raw_event.get("content", "")
|
|
460
|
+
return base
|
|
472
461
|
|
|
473
|
-
|
|
474
|
-
event_type = raw_event.get("type", "")
|
|
475
|
-
type_map = {{
|
|
476
|
-
"chat": "message",
|
|
477
|
-
}}
|
|
478
|
-
return type_map.get(event_type, "unknown")
|
|
479
|
-
|
|
480
|
-
def _convert_detail_type(self, raw_event: dict) -> str:
|
|
481
|
-
return "private" if raw_event.get("is_private") else "group"
|
|
482
|
-
|
|
483
|
-
def _convert_message_segments(self, content) -> list:
|
|
484
|
-
if isinstance(content, str) and content:
|
|
485
|
-
return [{{"type": "text", "data": {{"text": content}}}}]
|
|
486
|
-
return []
|
|
462
|
+
return None
|
|
487
463
|
"""
|
|
488
464
|
|
|
489
465
|
_README_MODULE = """# {name}
|
|
@@ -493,7 +469,7 @@ _README_MODULE = """# {name}
|
|
|
493
469
|
## 安装
|
|
494
470
|
|
|
495
471
|
```bash
|
|
496
|
-
epsdk install {name}
|
|
472
|
+
epsdk install ErisPulse-{name}
|
|
497
473
|
```
|
|
498
474
|
|
|
499
475
|
## 使用
|
|
@@ -517,7 +493,7 @@ _README_ADAPTER = """# {name}
|
|
|
517
493
|
## 安装
|
|
518
494
|
|
|
519
495
|
```bash
|
|
520
|
-
epsdk install {name}
|
|
496
|
+
epsdk install ErisPulse-{name}
|
|
521
497
|
```
|
|
522
498
|
|
|
523
499
|
## 使用
|
|
@@ -585,6 +561,20 @@ def _validate_name(name: str) -> bool:
|
|
|
585
561
|
return all(c.isalnum() or c == "_" for c in name)
|
|
586
562
|
|
|
587
563
|
|
|
564
|
+
def _scaffold_text(name: str) -> dict:
|
|
565
|
+
"""
|
|
566
|
+
构建当前语言的脚手架文案映射,并预填充 {name} 占位符
|
|
567
|
+
|
|
568
|
+
:param name: [str] 模块/适配器名称
|
|
569
|
+
:return: [dict] ScaffoldText.all() 的文案字典(含占位符替换)
|
|
570
|
+
"""
|
|
571
|
+
st = ScaffoldText()
|
|
572
|
+
return {
|
|
573
|
+
key: (value.replace("{name}", name) if value else value)
|
|
574
|
+
for key, value in st.all().items()
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
|
|
588
578
|
class CreateCommand(Command):
|
|
589
579
|
"""
|
|
590
580
|
create 命令
|
|
@@ -724,7 +714,8 @@ class CreateCommand(Command):
|
|
|
724
714
|
|
|
725
715
|
(pkg_dir / "__init__.py").write_text(_MODULE_INIT, encoding="utf-8")
|
|
726
716
|
(pkg_dir / "Core.py").write_text(
|
|
727
|
-
_MODULE_CORE.format(name=name
|
|
717
|
+
_MODULE_CORE.format(name=name, text=_scaffold_text(name)),
|
|
718
|
+
encoding="utf-8",
|
|
728
719
|
)
|
|
729
720
|
(project_dir / "pyproject.toml").write_text(
|
|
730
721
|
_MODULE_PYPROJECT.format(
|
|
@@ -822,13 +813,19 @@ class CreateCommand(Command):
|
|
|
822
813
|
)
|
|
823
814
|
(pkg_dir / "Core.py").write_text(
|
|
824
815
|
_ADAPTER_CORE.format(
|
|
825
|
-
name=name,
|
|
816
|
+
name=name,
|
|
817
|
+
converter_name=converter_name,
|
|
818
|
+
entry_key=entry_key,
|
|
819
|
+
text=_scaffold_text(name),
|
|
826
820
|
),
|
|
827
821
|
encoding="utf-8",
|
|
828
822
|
)
|
|
829
823
|
(pkg_dir / "Converter.py").write_text(
|
|
830
824
|
_ADAPTER_CONVERTER.format(
|
|
831
|
-
name=name,
|
|
825
|
+
name=name,
|
|
826
|
+
converter_name=converter_name,
|
|
827
|
+
entry_key=entry_key,
|
|
828
|
+
text=_scaffold_text(name),
|
|
832
829
|
),
|
|
833
830
|
encoding="utf-8",
|
|
834
831
|
)
|