ErisPulse 2.7.0.dev0__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.
Files changed (111) hide show
  1. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/PKG-INFO +1 -1
  2. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/pyproject.toml +1 -1
  3. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/cli.py +17 -0
  4. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/create.py +193 -181
  5. erispulse-2.7.0.dev5/src/ErisPulse/CLI/commands/doctor.py +127 -0
  6. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/init.py +1 -0
  7. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/list.py +29 -9
  8. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/run.py +2 -1
  9. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/types.py +3 -2
  10. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/console.py +2 -2
  11. erispulse-2.7.0.dev5/src/ErisPulse/CLI/constants.py +46 -0
  12. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/__init__.py +82 -2
  13. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/en.py +33 -0
  14. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/ja.py +33 -0
  15. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/ru.py +33 -0
  16. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/zh_cn.py +33 -0
  17. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/zh_tw.py +33 -0
  18. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/display.py +4 -2
  19. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/package_manager.py +229 -38
  20. erispulse-2.7.0.dev5/src/ErisPulse/CLI/utils/scaffold_text.py +338 -0
  21. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/__init__.py +4 -1
  22. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/adapter.py +363 -15
  23. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/config_schema.py +93 -18
  24. erispulse-2.7.0.dev5/src/ErisPulse/Core/Bases/converter.py +80 -0
  25. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/send_rules.py +3 -3
  26. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/command.py +60 -25
  27. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/session_type.py +28 -6
  28. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/wrapper.py +111 -4
  29. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/__init__.py +2 -0
  30. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/adapter.py +33 -2
  31. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/client.py +4 -3
  32. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/config.py +164 -26
  33. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/constants.py +66 -1
  34. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/en.py +43 -7
  35. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/ja.py +43 -7
  36. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/ru.py +43 -7
  37. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/zh_cn.py +43 -7
  38. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/zh_tw.py +43 -7
  39. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/lifecycle.py +61 -0
  40. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/logger.py +31 -7
  41. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/master.py +1 -1
  42. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/router.py +183 -20
  43. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/storage.py +30 -0
  44. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/adapter.py +3 -2
  45. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/module.py +3 -2
  46. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/adapter.py +9 -2
  47. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/module.py +22 -7
  48. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/__init__.py +15 -0
  49. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/config_schema.py +2 -0
  50. erispulse-2.7.0.dev5/src/ErisPulse/runtime/diagnostics.py +317 -0
  51. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/frame_config.py +51 -1
  52. erispulse-2.7.0.dev5/src/ErisPulse/runtime/memory.py +121 -0
  53. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/sdk.py +221 -31
  54. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/.gitignore +0 -0
  55. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/LICENSE +0 -0
  56. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/README.pypi.md +0 -0
  57. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/__init__.py +0 -0
  58. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/base.py +0 -0
  59. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/__init__.py +0 -0
  60. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/install.py +0 -0
  61. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/language.py +0 -0
  62. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
  63. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/self_update.py +0 -0
  64. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
  65. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
  66. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/hints.py +0 -0
  67. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/__init__.py +0 -0
  68. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/registry.py +0 -0
  69. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/__init__.py +0 -0
  70. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/file_watcher.py +0 -0
  71. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/client.py +0 -0
  72. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/errors.py +0 -0
  73. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/i18n_schema.py +0 -0
  74. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/kv_builder.py +0 -0
  75. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/manager.py +0 -0
  76. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/module.py +0 -0
  77. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/router.py +0 -0
  78. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/send_builder.py +0 -0
  79. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/storage.py +0 -0
  80. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/websocket.py +0 -0
  81. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/__init__.py +0 -0
  82. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/base.py +0 -0
  83. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/message.py +0 -0
  84. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/message_builder.py +0 -0
  85. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/meta.py +0 -0
  86. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/notice.py +0 -0
  87. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/request.py +0 -0
  88. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/__init__.py +0 -0
  89. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/error.css +0 -0
  90. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/error.html +0 -0
  91. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/root.css +0 -0
  92. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/root.html +0 -0
  93. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/__init__.py +0 -0
  94. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/constants.py +0 -0
  95. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/__init__.py +0 -0
  96. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/module.py +0 -0
  97. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/__init__.py +0 -0
  98. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/__main__.py +0 -0
  99. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/__init__.py +0 -0
  100. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/bases/__init__.py +0 -0
  101. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/bases/finder.py +0 -0
  102. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/__init__.py +0 -0
  103. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/bases/__init__.py +0 -0
  104. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/bases/loader.py +0 -0
  105. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/strategy.py +0 -0
  106. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/strict.py +0 -0
  107. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/py.typed +0 -0
  108. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/context.py +0 -0
  109. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/exceptions.py +0 -0
  110. {erispulse-2.7.0.dev0 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/hints.py +0 -0
  111. {erispulse-2.7.0.dev0 → 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.dev0
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-dev0"
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
- {name}模块
98
-
99
- 继承自BaseModule基类,实现了标准化的模块生命周期管理和事件处理
74
+ {text[module.doc]}
100
75
  \"\"\"
101
76
 
102
- ConfigClass = {name}Config
103
- I18nClass = {name}I18n
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(f\"模块已加载: {{event}}\")
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(f\"模块已卸载: {{event}}\")
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(\"Hello from {name}!\")
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(f\"收到私聊消息: {{event.get_text()}}\")
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(f\"新好友添加: {{event.get_user_nickname()}}\")
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, I18nKey, I18nKeys
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
- {name} 适配器
250
-
251
- 继承自 BaseAdapter 基类,使用声明式配置管理(ConfigClass),
252
- 实现了 SendDSL 风格的链式调用接口和 Bot 状态追踪。
223
+ {text[adapter.doc]}
253
224
  \"\"\"
254
225
 
255
- ConfigClass = {name}Config
256
- I18nClass = {name}I18n
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
- Send 消息发送 DSL
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
- # 标准方法 Text/Image/Voice/Video/File 已从基类继承,默认委托 Raw_ob12。
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
- # 可添加平台特有的发送方法(会被 event.supports() 识别):
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
- Request 请求操作 DSL
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):
@@ -339,23 +345,34 @@ class {name}(BaseAdapter):
339
345
  "message": result.get("message", ""),
340
346
  }}
341
347
 
348
+ class Api(BaseAdapter.Api):
349
+ \"\"\"
350
+ {text[adapter.dsl.api_doc]}
351
+ \"\"\"
352
+
353
+ # {text[adapter.dsl.api_std_methods_hint]}
354
+ # {text[adapter.dsl.api_override_hint]}
355
+ # async def get_user_info(self, user_id: str) -> dict:
356
+ # raw = await self._adapter._request("GET", f"/users/{{user_id}}")
357
+ # return self._adapter.make_response(data={{...}}, raw=raw)
358
+
342
359
  async def start(self):
343
- \"\"\"启动适配器\"\"\"
360
+ \"\"\"Start the adapter\"\"\"
344
361
  cfg = self.cfg
345
- self.logger.info(f"启动 {{cfg.endpoint}} 适配器")
362
+ self.logger.info("{text[adapter.log.starting]}")
346
363
 
347
364
  router.register_websocket(
348
365
  module_name="{entry_key}",
349
366
  path="/ws",
350
367
  handler=self._ws_handler,
351
368
  )
352
- self.logger.info("WebSocket 路由已注册: /ws")
369
+ self.logger.info("{text[adapter.log.ws_registered]}")
353
370
 
354
371
  async def _ws_handler(self, websocket):
355
372
  bot_id = self._get_bot_id()
356
373
 
357
374
  await self.emit_meta("connect", bot_id, user_name="{name}")
358
- self.logger.info(f"Bot {{bot_id}} 已连接")
375
+ self.logger.info("{text[adapter.log.bot_connected]}")
359
376
 
360
377
  try:
361
378
  while True:
@@ -365,18 +382,18 @@ class {name}(BaseAdapter):
365
382
  if event:
366
383
  await self.adapter.emit(event)
367
384
  except Exception as e:
368
- self.logger.warning(f"连接断开: {{e}}")
385
+ self.logger.warning("{text[adapter.log.connection_lost]}")
369
386
  finally:
370
387
  await self.emit_meta("disconnect", bot_id)
371
- self.logger.info(f"Bot {{bot_id}} 已断开")
388
+ self.logger.info("{text[adapter.log.bot_disconnected]}")
372
389
 
373
390
  async def shutdown(self):
374
- \"\"\"关闭适配器\"\"\"
391
+ \"\"\"Shut down the adapter\"\"\"
375
392
  router.unregister_websocket("{entry_key}", "/ws")
376
- self.logger.info("适配器已关闭")
393
+ self.logger.info("{text[adapter.log.shutdown]}")
377
394
 
378
395
  async def call_api(self, endpoint: str, **params):
379
- \"\"\"调用平台 API\"\"\"
396
+ \"\"\"Call platform API\"\"\"
380
397
  from ErisPulse.Core import client
381
398
 
382
399
  cfg = self.cfg
@@ -398,77 +415,51 @@ class {name}(BaseAdapter):
398
415
  raw=result,
399
416
  )
400
417
  except Exception as e:
401
- self.logger.error(f"API 调用失败: {{e}}")
418
+ self.logger.error(("{text[adapter.log.api_call_failed]}").format(error=e))
402
419
  return self.make_error(message=str(e))
403
420
  """
404
421
 
405
422
  _ADAPTER_CONVERTER = """\"\"\"
406
- {name} 事件转换器
407
-
408
- 将平台原生事件转换为 OneBot12 标准格式(正向转换)。
409
- 反向转换(发送方向)由适配器的 Send.Raw_ob12() 处理。
423
+ {text[adapter.converter_doc]}
410
424
  \"\"\"
411
425
 
412
- import time
413
- import uuid
426
+ from ErisPulse.Core.Bases import BaseConverter
414
427
 
415
428
 
416
- class {converter_name}:
429
+ class {converter_name}(BaseConverter):
417
430
  \"\"\"
418
- {name} 转换器类
431
+ {converter_name} converter class
419
432
 
420
- 负责将平台特定的事件格式转换为 ErisPulse 标准 OneBot12 格式。
421
- 所有转换后的事件必须包含 platform_raw 字段以保留原始数据。
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).
422
436
  \"\"\"
423
437
 
424
- def convert(self, raw_event: dict) -> dict:
438
+ def __init__(self):
439
+ super().__init__(platform="{entry_key}")
440
+
441
+ def convert(self, raw_event: dict) -> dict | None:
425
442
  \"\"\"
426
- 将平台原生事件转换为 OneBot12 标准格式
443
+ Convert a platform-native event to the OneBot12 standard format
427
444
 
428
- :param raw_event: 平台原始事件数据
429
- :return: OneBot12 标准格式的事件字典,无法识别时返回 None
445
+ :param raw_event: raw platform event data
446
+ :return: OneBot12 event dict, or None if unrecognized
430
447
  \"\"\"
431
448
  if not isinstance(raw_event, dict):
432
449
  return None
433
450
 
434
- event_id = raw_event.get("event_id") or str(uuid.uuid4())
435
- timestamp = raw_event.get("timestamp") or int(time.time())
436
-
437
- event = {{
438
- "id": str(event_id),
439
- "time": int(timestamp),
440
- "type": self._convert_type(raw_event),
441
- "detail_type": self._convert_detail_type(raw_event),
442
- "platform": "{entry_key}",
443
- "self": {{
444
- "platform": "{entry_key}",
445
- "user_id": str(raw_event.get("self_id", "")),
446
- }},
447
- "{entry_key}_raw": raw_event,
448
- "{entry_key}_raw_type": raw_event.get("type", ""),
449
- }}
450
-
451
- if event["type"] == "message":
452
- event["user_id"] = str(raw_event.get("sender_id", ""))
453
- event["message"] = self._convert_message_segments(raw_event.get("content", ""))
454
- event["alt_message"] = raw_event.get("content", "")
451
+ event_type = raw_event.get("type", "")
452
+ base = self.build_base_event(raw_event, event_type)
455
453
 
456
- return event
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
457
461
 
458
- def _convert_type(self, raw_event: dict) -> str:
459
- event_type = raw_event.get("type", "")
460
- type_map = {{
461
- "chat": "message",
462
- }}
463
- return type_map.get(event_type, "unknown")
464
-
465
- def _convert_detail_type(self, raw_event: dict) -> str:
466
- return "private" if raw_event.get("is_private") else "group"
467
-
468
- def _convert_message_segments(self, content) -> list:
469
- if isinstance(content, str) and content:
470
- return [{{"type": "text", "data": {{"text": content}}}}]
471
- return []
462
+ return None
472
463
  """
473
464
 
474
465
  _README_MODULE = """# {name}
@@ -478,7 +469,7 @@ _README_MODULE = """# {name}
478
469
  ## 安装
479
470
 
480
471
  ```bash
481
- epsdk install {name}
472
+ epsdk install ErisPulse-{name}
482
473
  ```
483
474
 
484
475
  ## 使用
@@ -502,7 +493,7 @@ _README_ADAPTER = """# {name}
502
493
  ## 安装
503
494
 
504
495
  ```bash
505
- epsdk install {name}
496
+ epsdk install ErisPulse-{name}
506
497
  ```
507
498
 
508
499
  ## 使用
@@ -570,6 +561,20 @@ def _validate_name(name: str) -> bool:
570
561
  return all(c.isalnum() or c == "_" for c in name)
571
562
 
572
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
+
573
578
  class CreateCommand(Command):
574
579
  """
575
580
  create 命令
@@ -709,7 +714,8 @@ class CreateCommand(Command):
709
714
 
710
715
  (pkg_dir / "__init__.py").write_text(_MODULE_INIT, encoding="utf-8")
711
716
  (pkg_dir / "Core.py").write_text(
712
- _MODULE_CORE.format(name=name), encoding="utf-8"
717
+ _MODULE_CORE.format(name=name, text=_scaffold_text(name)),
718
+ encoding="utf-8",
713
719
  )
714
720
  (project_dir / "pyproject.toml").write_text(
715
721
  _MODULE_PYPROJECT.format(
@@ -807,13 +813,19 @@ class CreateCommand(Command):
807
813
  )
808
814
  (pkg_dir / "Core.py").write_text(
809
815
  _ADAPTER_CORE.format(
810
- name=name, converter_name=converter_name, entry_key=entry_key
816
+ name=name,
817
+ converter_name=converter_name,
818
+ entry_key=entry_key,
819
+ text=_scaffold_text(name),
811
820
  ),
812
821
  encoding="utf-8",
813
822
  )
814
823
  (pkg_dir / "Converter.py").write_text(
815
824
  _ADAPTER_CONVERTER.format(
816
- name=name, converter_name=converter_name, entry_key=entry_key
825
+ name=name,
826
+ converter_name=converter_name,
827
+ entry_key=entry_key,
828
+ text=_scaffold_text(name),
817
829
  ),
818
830
  encoding="utf-8",
819
831
  )