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.
Files changed (111) hide show
  1. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/PKG-INFO +1 -1
  2. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/pyproject.toml +1 -1
  3. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/cli.py +17 -0
  4. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/create.py +185 -188
  5. erispulse-2.7.0.dev5/src/ErisPulse/CLI/commands/doctor.py +127 -0
  6. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/init.py +1 -0
  7. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/list.py +29 -9
  8. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/run.py +2 -1
  9. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/types.py +3 -2
  10. {erispulse-2.7.0.dev3 → 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.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/__init__.py +82 -2
  13. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/en.py +33 -0
  14. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/ja.py +33 -0
  15. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/ru.py +33 -0
  16. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/zh_cn.py +33 -0
  17. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/zh_tw.py +33 -0
  18. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/display.py +4 -2
  19. {erispulse-2.7.0.dev3 → 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.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/__init__.py +2 -0
  22. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/adapter.py +23 -14
  23. {erispulse-2.7.0.dev3 → 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.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/send_rules.py +3 -3
  26. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/command.py +60 -25
  27. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/adapter.py +33 -2
  28. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/client.py +4 -3
  29. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/config.py +23 -16
  30. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/constants.py +66 -1
  31. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/en.py +28 -7
  32. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/ja.py +28 -7
  33. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/ru.py +28 -7
  34. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/zh_cn.py +28 -7
  35. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/zh_tw.py +28 -7
  36. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/lifecycle.py +61 -0
  37. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/logger.py +31 -7
  38. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/master.py +1 -1
  39. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/router.py +183 -20
  40. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/storage.py +30 -0
  41. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/adapter.py +3 -2
  42. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/module.py +3 -2
  43. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/adapter.py +3 -2
  44. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/module.py +10 -7
  45. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/__init__.py +5 -0
  46. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/config_schema.py +2 -0
  47. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/frame_config.py +51 -1
  48. erispulse-2.7.0.dev5/src/ErisPulse/runtime/memory.py +121 -0
  49. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/sdk.py +106 -29
  50. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/.gitignore +0 -0
  51. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/LICENSE +0 -0
  52. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/README.pypi.md +0 -0
  53. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/__init__.py +0 -0
  54. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/base.py +0 -0
  55. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/__init__.py +0 -0
  56. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/install.py +0 -0
  57. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/language.py +0 -0
  58. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
  59. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/self_update.py +0 -0
  60. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
  61. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
  62. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/hints.py +0 -0
  63. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/i18n/locales/__init__.py +0 -0
  64. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/registry.py +0 -0
  65. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/__init__.py +0 -0
  66. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/CLI/utils/file_watcher.py +0 -0
  67. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/client.py +0 -0
  68. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/errors.py +0 -0
  69. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/i18n_schema.py +0 -0
  70. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/kv_builder.py +0 -0
  71. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/manager.py +0 -0
  72. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/module.py +0 -0
  73. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/router.py +0 -0
  74. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/send_builder.py +0 -0
  75. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/storage.py +0 -0
  76. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Bases/websocket.py +0 -0
  77. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/__init__.py +0 -0
  78. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/base.py +0 -0
  79. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/message.py +0 -0
  80. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/message_builder.py +0 -0
  81. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/meta.py +0 -0
  82. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/notice.py +0 -0
  83. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/request.py +0 -0
  84. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/session_type.py +0 -0
  85. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/Event/wrapper.py +0 -0
  86. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/__init__.py +0 -0
  87. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/__init__.py +0 -0
  88. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/error.css +0 -0
  89. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/error.html +0 -0
  90. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/root.css +0 -0
  91. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/assets/root.html +0 -0
  92. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/__init__.py +0 -0
  93. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/constants.py +0 -0
  94. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/i18n/locales/__init__.py +0 -0
  95. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/Core/module.py +0 -0
  96. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/__init__.py +0 -0
  97. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/__main__.py +0 -0
  98. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/__init__.py +0 -0
  99. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/bases/__init__.py +0 -0
  100. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/finders/bases/finder.py +0 -0
  101. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/__init__.py +0 -0
  102. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/bases/__init__.py +0 -0
  103. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/bases/loader.py +0 -0
  104. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/strategy.py +0 -0
  105. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/loaders/strict.py +0 -0
  106. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/py.typed +0 -0
  107. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/context.py +0 -0
  108. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/diagnostics.py +0 -0
  109. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/exceptions.py +0 -0
  110. {erispulse-2.7.0.dev3 → erispulse-2.7.0.dev5}/src/ErisPulse/runtime/hints.py +0 -0
  111. {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.dev3
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.3"
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):
@@ -341,36 +347,32 @@ class {name}(BaseAdapter):
341
347
 
342
348
  class Api(BaseAdapter.Api):
343
349
  \"\"\"
344
- 标准 API 动作 DSL
345
-
346
- 提供跨平台的 OneBot12 标准动作(信息查询/群管理/消息管理/文件操作)。
347
- 默认实现委托给 call_api,适配器可覆盖单个方法映射到平台原生 API。
348
- 平台扩展动作通过 call("prefix.action", **params) 调用。
350
+ {text[adapter.dsl.api_doc]}
349
351
  \"\"\"
350
352
 
351
- # 标准方法(get_user_info / get_group_info / delete_message 等)已从基类继承,
352
- # 默认委托给 call_api。如需平台特定逻辑,可覆盖单个方法:
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(f"启动 {{cfg.endpoint}} 适配器")
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("WebSocket 路由已注册: /ws")
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(f"Bot {{bot_id}} 已连接")
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(f"连接断开: {{e}}")
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(f"Bot {{bot_id}} 已断开")
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
- \"\"\"调用平台 API\"\"\"
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(f"API 调用失败: {{e}}")
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
- {name} 事件转换器
422
-
423
- 将平台原生事件转换为 OneBot12 标准格式(正向转换)。
424
- 反向转换(发送方向)由适配器的 Send.Raw_ob12() 处理。
423
+ {text[adapter.converter_doc]}
425
424
  \"\"\"
426
425
 
427
- import time
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
- {name} 转换器类
431
+ {converter_name} converter class
434
432
 
435
- 负责将平台特定的事件格式转换为 ErisPulse 标准 OneBot12 格式。
436
- 所有转换后的事件必须包含 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).
437
436
  \"\"\"
438
437
 
439
- 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:
440
442
  \"\"\"
441
- 将平台原生事件转换为 OneBot12 标准格式
443
+ Convert a platform-native event to the OneBot12 standard format
442
444
 
443
- :param raw_event: 平台原始事件数据
444
- :return: OneBot12 标准格式的事件字典,无法识别时返回 None
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
- event_id = raw_event.get("event_id") or str(uuid.uuid4())
450
- timestamp = raw_event.get("timestamp") or int(time.time())
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
- 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
472
461
 
473
- def _convert_type(self, raw_event: dict) -> str:
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), encoding="utf-8"
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, 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),
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, 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),
832
829
  ),
833
830
  encoding="utf-8",
834
831
  )