ErisPulse 2.7.1.dev2__tar.gz → 2.7.1.dev4__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.1.dev2 → erispulse-2.7.1.dev4}/PKG-INFO +1 -1
  2. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/pyproject.toml +1 -1
  3. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/adapter.py +70 -55
  4. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/config_schema.py +9 -10
  5. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/converter.py +3 -1
  6. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/errors.py +0 -5
  7. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/i18n_schema.py +2 -2
  8. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/kv_builder.py +9 -8
  9. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/module.py +5 -6
  10. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/send_builder.py +9 -4
  11. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/send_rules.py +5 -5
  12. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/storage.py +3 -1
  13. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/base.py +4 -4
  14. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/command.py +21 -13
  15. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/session_type.py +6 -6
  16. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/wrapper.py +120 -34
  17. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/adapter.py +47 -39
  18. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/client.py +1 -1
  19. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/constants.py +26 -3
  20. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/i18n/locales/en.py +117 -0
  21. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/i18n/locales/ja.py +117 -0
  22. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/i18n/locales/ru.py +117 -0
  23. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/i18n/locales/zh_cn.py +117 -0
  24. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/i18n/locales/zh_tw.py +117 -0
  25. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/master.py +2 -1
  26. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/module.py +2 -2
  27. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/router.py +168 -257
  28. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/storage.py +33 -68
  29. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/finders/bases/finder.py +2 -2
  30. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/loaders/module.py +12 -5
  31. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/runtime/exceptions.py +2 -1
  32. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/runtime/frame_config.py +6 -0
  33. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/runtime/memory.py +13 -4
  34. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/sdk.py +221 -40
  35. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/.gitignore +0 -0
  36. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/LICENSE +0 -0
  37. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/README.pypi.md +0 -0
  38. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/__init__.py +0 -0
  39. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/base.py +0 -0
  40. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/cli.py +0 -0
  41. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/__init__.py +0 -0
  42. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/create.py +0 -0
  43. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/doctor.py +0 -0
  44. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/init.py +0 -0
  45. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/install.py +0 -0
  46. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/language.py +0 -0
  47. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/list.py +0 -0
  48. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/list_remote.py +0 -0
  49. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/run.py +0 -0
  50. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/self_update.py +0 -0
  51. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/types.py +0 -0
  52. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/uninstall.py +0 -0
  53. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/commands/upgrade.py +0 -0
  54. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/console.py +0 -0
  55. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/constants.py +0 -0
  56. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/hints.py +0 -0
  57. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/i18n/__init__.py +0 -0
  58. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/i18n/locales/__init__.py +0 -0
  59. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/i18n/locales/en.py +0 -0
  60. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/i18n/locales/ja.py +0 -0
  61. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/i18n/locales/ru.py +0 -0
  62. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/i18n/locales/zh_cn.py +0 -0
  63. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/i18n/locales/zh_tw.py +0 -0
  64. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/registry.py +0 -0
  65. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/utils/__init__.py +0 -0
  66. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/utils/display.py +0 -0
  67. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/utils/file_watcher.py +0 -0
  68. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/utils/package_manager.py +0 -0
  69. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/CLI/utils/scaffold_text.py +0 -0
  70. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/__init__.py +0 -0
  71. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/client.py +0 -0
  72. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/manager.py +0 -0
  73. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/router.py +0 -0
  74. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Bases/websocket.py +0 -0
  75. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/__init__.py +0 -0
  76. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/message.py +0 -0
  77. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/message_builder.py +0 -0
  78. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/meta.py +0 -0
  79. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/notice.py +0 -0
  80. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/Event/request.py +0 -0
  81. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/__init__.py +0 -0
  82. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/assets/__init__.py +0 -0
  83. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/assets/error.css +0 -0
  84. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/assets/error.html +0 -0
  85. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/assets/root.css +0 -0
  86. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/assets/root.html +0 -0
  87. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/config.py +0 -0
  88. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/i18n/__init__.py +0 -0
  89. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/i18n/constants.py +0 -0
  90. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/i18n/locales/__init__.py +0 -0
  91. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/lifecycle.py +0 -0
  92. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/Core/logger.py +0 -0
  93. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/__init__.py +0 -0
  94. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/__main__.py +0 -0
  95. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/finders/__init__.py +0 -0
  96. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/finders/adapter.py +0 -0
  97. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/finders/bases/__init__.py +0 -0
  98. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/finders/module.py +0 -0
  99. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/loaders/__init__.py +0 -0
  100. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/loaders/adapter.py +0 -0
  101. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/loaders/bases/__init__.py +0 -0
  102. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/loaders/bases/loader.py +0 -0
  103. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/loaders/strategy.py +0 -0
  104. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/loaders/strict.py +0 -0
  105. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/py.typed +0 -0
  106. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/runtime/__init__.py +0 -0
  107. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/runtime/config_schema.py +0 -0
  108. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/runtime/context.py +0 -0
  109. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/runtime/diagnostics.py +0 -0
  110. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/src/ErisPulse/runtime/hints.py +0 -0
  111. {erispulse-2.7.1.dev2 → erispulse-2.7.1.dev4}/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.1.dev2
3
+ Version: 2.7.1.dev4
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.1-dev.2"
7
+ version = "2.7.1-dev.4"
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"
@@ -19,6 +19,7 @@ if TYPE_CHECKING:
19
19
  # typing.Self 仅在 3.11+ 提供,3.10 需 typing_extensions 兜底
20
20
  from typing_extensions import Self
21
21
 
22
+ from ..config import config as config_mgr
22
23
  from ..constants import (
23
24
  DEFAULT_SEND_METHOD,
24
25
  DEFAULT_SEND_TARGET_TYPE,
@@ -28,6 +29,9 @@ from ..constants import (
28
29
  STATUS_FAILED,
29
30
  STATUS_OK,
30
31
  )
32
+ from ..i18n import i18n
33
+ from ..lifecycle import lifecycle
34
+ from ..logger import logger
31
35
 
32
36
  _CHAIN_MODIFIER_NAMES = frozenset({
33
37
  "At", "AtAll", "Reply", "To", "Using", "Account",
@@ -137,6 +141,7 @@ def _wrap_send_method(method_name: str, original_method: Callable, send_dsl: "Se
137
141
  "bot_id": send_dsl._account_id or "",
138
142
  }
139
143
 
144
+ # 循环依赖:Core/adapter.py 顶层导入本模块(BaseAdapter)
140
145
  from ..adapter import _msg_logger
141
146
 
142
147
  target_type = send_dsl._target_type or ""
@@ -158,7 +163,6 @@ def _wrap_send_method(method_name: str, original_method: Callable, send_dsl: "Se
158
163
 
159
164
  # 预判是否有生命周期监听者:无监听时跳过 Task 创建与 emit 调度,
160
165
  # 避免每条发送消息都无条件 spawn 两个后台任务
161
- from ..lifecycle import lifecycle
162
166
 
163
167
  _has_sending_hooks = lifecycle.has_handlers("message.sending")
164
168
  _has_sent_hooks = lifecycle.has_handlers("message.sent")
@@ -309,10 +313,13 @@ class SendDSL:
309
313
  return resolved
310
314
  return attr
311
315
 
312
- from ..logger import logger
313
316
 
314
317
  logger.warning(
315
- f"平台 {self._adapter.__class__.__name__} 未实现 {name} 发送方法"
318
+ i18n.t(
319
+ "core.adapter.send_method_not_implemented",
320
+ platform=self._adapter.__class__.__name__,
321
+ name=name,
322
+ )
316
323
  )
317
324
 
318
325
  raise AttributeError(
@@ -428,11 +435,12 @@ class SendDSL:
428
435
  :param kwargs: 其他参数
429
436
  :return: asyncio.Task
430
437
  """
431
- from ..logger import logger
432
438
 
433
439
  logger.error(
434
- f"平台 {self._adapter.__class__.__name__} 未实现 Raw_ob12 方法,"
435
- f"消息未被发送。适配器必须实现此方法以支持 OneBot12 消息段发送。"
440
+ i18n.t(
441
+ "core.adapter.raw_ob12_not_implemented",
442
+ platform=self._adapter.__class__.__name__,
443
+ )
436
444
  )
437
445
 
438
446
  async def _not_impl():
@@ -441,7 +449,10 @@ class SendDSL:
441
449
  "retcode": RETCODE_NOT_IMPLEMENTED,
442
450
  "data": None,
443
451
  "message_id": "",
444
- "message": f"平台 {self._adapter.__class__.__name__} 未实现 Raw_ob12 方法",
452
+ "message": i18n.t(
453
+ "core.adapter.raw_ob12_not_implemented_short",
454
+ platform=self._adapter.__class__.__name__,
455
+ ),
445
456
  }
446
457
 
447
458
  try:
@@ -923,19 +934,26 @@ class RequestDSL:
923
934
  :param action: 操作名称(accept/reject)
924
935
  :return: 标准错误响应字典
925
936
  """
926
- from ..logger import logger
927
937
 
928
938
  platform_name = self._adapter.__class__.__name__
929
939
  logger.warning(
930
- f"平台 {platform_name} 未实现 Request.{action}() 方法,"
931
- f"请求 {self._request_id} 未被处理。"
940
+ i18n.t(
941
+ "core.adapter.request_not_implemented",
942
+ platform=platform_name,
943
+ action=action,
944
+ request_id=self._request_id,
945
+ )
932
946
  )
933
947
  return {
934
948
  "status": STATUS_FAILED,
935
949
  "retcode": RETCODE_NOT_IMPLEMENTED,
936
950
  "data": None,
937
951
  "message_id": "",
938
- "message": f"平台 {platform_name} 未实现请求操作 ({action})",
952
+ "message": i18n.t(
953
+ "core.adapter.request_op_not_implemented",
954
+ platform=platform_name,
955
+ action=action,
956
+ ),
939
957
  }
940
958
 
941
959
  def _create_task(self, coro) -> Awaitable[Any]:
@@ -1321,7 +1339,6 @@ class BaseAdapter(ABC):
1321
1339
  {!--< /tips >!--}
1322
1340
  """
1323
1341
 
1324
- ...
1325
1342
 
1326
1343
  class Api(ApiDSL):
1327
1344
  """
@@ -1337,7 +1354,6 @@ class BaseAdapter(ABC):
1337
1354
  {!--< /tips >!--}
1338
1355
  """
1339
1356
 
1340
- ...
1341
1357
 
1342
1358
  class Send(SendDSL):
1343
1359
  """
@@ -1373,9 +1389,8 @@ class BaseAdapter(ABC):
1373
1389
  }
1374
1390
 
1375
1391
  async def _send_example():
1376
- from ..logger import logger
1377
1392
 
1378
- logger.info(f"发送示例消息: {text}")
1393
+ logger.info(i18n.t("core.adapter.send_example", text=text))
1379
1394
  return mock_response
1380
1395
 
1381
1396
  return asyncio.create_task(_send_example())
@@ -1420,18 +1435,22 @@ class BaseAdapter(ABC):
1420
1435
  """
1421
1436
 
1422
1437
  async def _send_raw():
1423
- from ..logger import logger
1424
1438
 
1425
1439
  logger.error(
1426
- f"适配器 {self._adapter.__class__.__name__} 未实现 Raw_ob12 方法,"
1427
- f"消息未被发送。适配器必须实现此方法以支持 OneBot12 消息段发送。"
1440
+ i18n.t(
1441
+ "core.adapter.raw_ob12_not_implemented",
1442
+ platform=self._adapter.__class__.__name__,
1443
+ )
1428
1444
  )
1429
1445
  return {
1430
1446
  "status": STATUS_FAILED,
1431
1447
  "retcode": RETCODE_NOT_IMPLEMENTED,
1432
1448
  "data": None,
1433
1449
  "message_id": "",
1434
- "message": f"适配器 {self._adapter.__class__.__name__} 未实现 Raw_ob12 方法",
1450
+ "message": i18n.t(
1451
+ "core.adapter.raw_ob12_not_implemented_short",
1452
+ platform=self._adapter.__class__.__name__,
1453
+ ),
1435
1454
  }
1436
1455
 
1437
1456
  try:
@@ -1509,7 +1528,8 @@ class BaseAdapter(ABC):
1509
1528
  :return: API调用结果
1510
1529
  :raises NotImplementedError: 必须由子类实现
1511
1530
  """
1512
- raise NotImplementedError("适配器必须实现call_api方法")
1531
+
1532
+ raise NotImplementedError(i18n.t("core.adapter.must_implement_call_api"))
1513
1533
 
1514
1534
  @abstractmethod
1515
1535
  async def start(self) -> None:
@@ -1518,7 +1538,8 @@ class BaseAdapter(ABC):
1518
1538
 
1519
1539
  :raises NotImplementedError: 必须由子类实现
1520
1540
  """
1521
- raise NotImplementedError("适配器必须实现start方法")
1541
+
1542
+ raise NotImplementedError(i18n.t("core.adapter.must_implement_start"))
1522
1543
 
1523
1544
  @abstractmethod
1524
1545
  async def shutdown(self) -> None:
@@ -1527,7 +1548,8 @@ class BaseAdapter(ABC):
1527
1548
 
1528
1549
  :raises NotImplementedError: 必须由子类实现
1529
1550
  """
1530
- raise NotImplementedError("适配器必须实现shutdown方法")
1551
+
1552
+ raise NotImplementedError(i18n.t("core.adapter.must_implement_shutdown"))
1531
1553
 
1532
1554
  @property
1533
1555
  def cfg(self):
@@ -1546,11 +1568,9 @@ class BaseAdapter(ABC):
1546
1568
  {!--< /tips >!--}
1547
1569
  """
1548
1570
  if self.ConfigClass is None:
1549
- raise AttributeError(
1550
- "未声明 ConfigClass,请设置 MyAdapter.ConfigClass = MyConfig"
1551
- )
1552
1571
 
1553
- from ..config import config as config_mgr
1572
+ raise AttributeError(i18n.t("core.adapter.config_class_not_declared"))
1573
+
1554
1574
  from .config_schema import dict_to_dataclass
1555
1575
 
1556
1576
  # 适配器的 EventMixin 在 AdapterManager.register() 注入 _platform 后注册
@@ -1570,7 +1590,6 @@ class BaseAdapter(ABC):
1570
1590
  if value is not None:
1571
1591
  from dataclasses import asdict
1572
1592
 
1573
- from ..config import config as config_mgr
1574
1593
 
1575
1594
  try:
1576
1595
  config_mgr.setConfig(self._get_config_key(), asdict(value))
@@ -1601,11 +1620,9 @@ class BaseAdapter(ABC):
1601
1620
  :raises AttributeError: 未声明 AccountConfigClass 时抛出
1602
1621
  """
1603
1622
  if self.AccountConfigClass is None:
1604
- raise AttributeError(
1605
- "未声明 AccountConfigClass,请设置 MyAdapter.AccountConfigClass = MyBotConfig"
1606
- )
1607
1623
 
1608
- from ..config import config as config_mgr
1624
+ raise AttributeError(i18n.t("core.adapter.account_config_not_declared"))
1625
+
1609
1626
  from .config_schema import dict_to_dataclass, validate_config
1610
1627
 
1611
1628
  key = f"{self._get_config_key()}.accounts"
@@ -1623,7 +1640,7 @@ class BaseAdapter(ABC):
1623
1640
  errors = validate_config(instance)
1624
1641
  if errors:
1625
1642
  self._get_logger().error(
1626
- f"账户 {name} 配置校验失败: {', '.join(errors)}"
1643
+ i18n.t("core.adapter.account_config_invalid", name=name, error=", ".join(errors))
1627
1644
  )
1628
1645
  continue
1629
1646
  accounts[name] = instance
@@ -1637,7 +1654,6 @@ class BaseAdapter(ABC):
1637
1654
  if value is not None:
1638
1655
  from dataclasses import asdict
1639
1656
 
1640
- from ..config import config as config_mgr
1641
1657
 
1642
1658
  key = f"{self._get_config_key()}.accounts"
1643
1659
  try:
@@ -1674,17 +1690,18 @@ class BaseAdapter(ABC):
1674
1690
  return self.__class__.__name__
1675
1691
 
1676
1692
  def _get_logger(self):
1677
- """获取 logger,兼容 sdk 未注入的场景"""
1693
+ """
1694
+ {!--< internal-use >!--}
1695
+ 获取 logger,兼容 sdk 未注入的场景
1696
+
1697
+ 此处刻意使用函数内导入:模块顶层已有 ``logger``,但函数内动态导入可让测试
1698
+ 通过 ``patch("ErisPulse.Core.logger.logger")`` 拦截日志输出。
1699
+ """
1678
1700
  if hasattr(self, "logger"):
1679
1701
  return self.logger
1680
- try:
1681
- from ..logger import logger
1682
-
1683
- return logger
1684
- except ImportError:
1685
- import logging
1702
+ from ..logger import logger
1686
1703
 
1687
- return logging.getLogger(self.__class__.__name__)
1704
+ return logger
1688
1705
 
1689
1706
  def _ensure_config_exists(self):
1690
1707
  """
@@ -1700,7 +1717,6 @@ class BaseAdapter(ABC):
1700
1717
 
1701
1718
  if self.ConfigClass is None:
1702
1719
  return
1703
- from ..config import config as config_mgr
1704
1720
  from .config_schema import (
1705
1721
  dataclass_to_defaults_dict,
1706
1722
  dataclass_to_toml_with_comments,
@@ -1713,7 +1729,7 @@ class BaseAdapter(ABC):
1713
1729
  data = dataclass_to_defaults_dict(self.ConfigClass)
1714
1730
  toml_str = dataclass_to_toml_with_comments(self.ConfigClass)
1715
1731
  config_mgr.setConfig(key, data, immediate=True)
1716
- self._get_logger().info(f"已生成 {key} 默认配置模板:\n{toml_str}")
1732
+ self._get_logger().info(i18n.t("core.adapter.config_template_generated", key=key, toml=toml_str))
1717
1733
 
1718
1734
  def _ensure_i18n_registered(self):
1719
1735
  """
@@ -1741,7 +1757,7 @@ class BaseAdapter(ABC):
1741
1757
  except Exception:
1742
1758
  # i18n 注册失败不应中断适配器初始化
1743
1759
  self._get_logger().debug(
1744
- f"{self.__class__.__name__}.I18nClass.register() 失败",
1760
+ i18n.t("core.adapter.i18n_register_failed", adapter=self.__class__.__name__),
1745
1761
  exc_info=True,
1746
1762
  )
1747
1763
 
@@ -1768,7 +1784,7 @@ class BaseAdapter(ABC):
1768
1784
  register_event_mixin(platform, self.EventMixin)
1769
1785
  except Exception:
1770
1786
  self._get_logger().debug(
1771
- f"{self.__class__.__name__}.EventMixin 注册失败",
1787
+ i18n.t("core.adapter.eventmixin_register_failed", adapter=self.__class__.__name__),
1772
1788
  exc_info=True,
1773
1789
  )
1774
1790
 
@@ -1781,7 +1797,6 @@ class BaseAdapter(ABC):
1781
1797
  """
1782
1798
  if self.AccountConfigClass is None:
1783
1799
  return
1784
- from ..config import config as config_mgr
1785
1800
  from .config_schema import dataclass_to_defaults_dict
1786
1801
 
1787
1802
  key = f"{self._get_config_key()}.accounts"
@@ -1791,7 +1806,7 @@ class BaseAdapter(ABC):
1791
1806
  default_account = dataclass_to_defaults_dict(self.AccountConfigClass)
1792
1807
  data = {"default": default_account}
1793
1808
  config_mgr.setConfig(key, data, immediate=True)
1794
- self._get_logger().info(f"已生成 {key} 默认账户配置")
1809
+ self._get_logger().info(i18n.t("core.adapter.account_config_generated", key=key))
1795
1810
 
1796
1811
  def _resolve_account(self, account_id: str | None = None) -> tuple:
1797
1812
  """
@@ -1836,7 +1851,10 @@ class BaseAdapter(ABC):
1836
1851
  if cfg.enabled:
1837
1852
  return name, cfg
1838
1853
 
1839
- raise ValueError(f"未找到可用账户 (account_id={account_id})")
1854
+
1855
+ raise ValueError(
1856
+ i18n.t("core.adapter.account_not_found", account_id=account_id)
1857
+ )
1840
1858
 
1841
1859
  async def emit_meta(self, detail_type: str, bot_id: str, **extra_info):
1842
1860
  """
@@ -1847,8 +1865,9 @@ class BaseAdapter(ABC):
1847
1865
  :param extra_info: 扩展字段(user_name, nickname, avatar 等)
1848
1866
  """
1849
1867
  if not self._platform:
1850
- raise RuntimeError("平台名未注入,请确保适配器已注册后再使用 emit_meta")
1868
+ raise RuntimeError(i18n.t("core.adapter.platform_not_injected"))
1851
1869
 
1870
+ # 循环依赖:Core/adapter.py 顶层导入本模块(BaseAdapter)
1852
1871
  from ..adapter import adapter
1853
1872
 
1854
1873
  await adapter.emit(
@@ -1926,13 +1945,9 @@ class BaseAdapter(ABC):
1926
1945
  :param old_config: 变更前的配置实例
1927
1946
  :param new_config: 变更后的配置实例
1928
1947
  """
1929
- ...
1930
1948
 
1931
1949
  async def emit(self, *args, **kwargs):
1932
- raise NotImplementedError(
1933
- "适配器的 emit 方法已被弃用。请使用 adapter.emit() 通过 AdapterManager 提交事件。"
1934
- "如果你是适配器开发者,请查看 ErisPulse 文档进行更新。"
1935
- )
1950
+ raise NotImplementedError(i18n.t("core.adapter.emit_deprecated"))
1936
1951
 
1937
1952
  def send(
1938
1953
  self, target_type: str, target_id: str, message: Any, **kwargs: Any
@@ -1961,7 +1976,7 @@ class BaseAdapter(ABC):
1961
1976
  method = getattr(self.Send.To(target_type, target_id), method_name, None) # pyright: ignore[reportArgumentType]
1962
1977
  if not method:
1963
1978
  raise AttributeError(
1964
- f"未找到 {method_name} 方法,请确保已在 Send 类中定义"
1979
+ i18n.t("core.adapter.send_method_missing", name=method_name)
1965
1980
  )
1966
1981
  return await method(message, **kwargs)
1967
1982
 
@@ -21,6 +21,8 @@ from collections.abc import Mapping
21
21
  from dataclasses import MISSING, dataclass, field, fields
22
22
  from typing import Any, ClassVar
23
23
 
24
+ from ..i18n import i18n
25
+
24
26
  # ---------------------------------------------------------------------------
25
27
  # 内部辅助函数
26
28
  # ---------------------------------------------------------------------------
@@ -303,7 +305,7 @@ class I18nConfig:
303
305
  language: str = field(
304
306
  default="auto",
305
307
  metadata={
306
- "description": "显示语言 (auto=自动检测, zh-CN, zh-TW, en, ja, ru)",
308
+ "description": {"i18n": "core.config.i18n_language_description", "default": "显示语言 (auto=自动检测, zh-CN, zh-TW, en, ja, ru)"},
307
309
  "required": False,
308
310
  "ui": {
309
311
  "widget": "select",
@@ -373,7 +375,7 @@ def dataclass_to_toml_with_comments(
373
375
  required = meta.get("required", False) if meta else False
374
376
 
375
377
  if description:
376
- suffix = "(必填)" if required else ""
378
+ suffix = i18n.t("core.config.required_suffix") if required else ""
377
379
  lines.append(f"# {description}{suffix}")
378
380
 
379
381
  # secret 字段不把真实值写入模板文件,避免配置文件泄露敏感信息
@@ -466,7 +468,6 @@ def _notify_instance_config_update(
466
468
  instance.on_config_update(old_config, new_config)
467
469
  except Exception as e:
468
470
  try:
469
- from ErisPulse.Core.i18n import i18n
470
471
  from ErisPulse.Core.logger import logger
471
472
 
472
473
  params = dict(log_params)
@@ -504,7 +505,7 @@ def validate_config(instance) -> list[str]:
504
505
  # 1. required 非空(需要 metadata 声明)
505
506
  if meta.get("required", False) and _is_empty(value):
506
507
  desc_text = _resolve_description_text(meta) or f.name
507
- errors.append(f"{f.name}({desc_text})不能为空")
508
+ errors.append(i18n.t("core.config.field_required_empty", field=f.name, desc=desc_text))
508
509
  continue # 已为空,类型/范围检查无意义
509
510
 
510
511
  # 跳过空值后续检查
@@ -519,7 +520,7 @@ def validate_config(instance) -> list[str]:
519
520
  if expected is not None and not isinstance(value, expected):
520
521
  # bool 是 int 的子类:声明 int 但实际为 bool 视为类型不符
521
522
  errors.append(
522
- f"{f.name} 类型应为 {type_name},实际为 {type(value).__name__}"
523
+ i18n.t("core.config.field_type_mismatch", field=f.name, expected=type_name, actual=type(value).__name__)
523
524
  )
524
525
 
525
526
  # 3. 枚举选项(options 可在 ui 子表或 metadata 顶层)
@@ -529,7 +530,7 @@ def validate_config(instance) -> list[str]:
529
530
  o.get("value") if isinstance(o, dict) else o for o in options
530
531
  ]
531
532
  if value not in plain_opts:
532
- errors.append(f"{f.name} 的值 '{value}' 不在允许的选项中")
533
+ errors.append(i18n.t("core.config.field_option_invalid", field=f.name, value=value))
533
534
 
534
535
  # 4. 数值范围
535
536
  if isinstance(value, (int, float)) and not isinstance(value, bool):
@@ -540,9 +541,9 @@ def validate_config(instance) -> list[str]:
540
541
  if max_val is None:
541
542
  max_val = meta.get("max")
542
543
  if min_val is not None and value < min_val:
543
- errors.append(f"{f.name} 的值 {value} 小于最小值 {min_val}")
544
+ errors.append(i18n.t("core.config.field_below_min", field=f.name, value=value, min=min_val))
544
545
  if max_val is not None and value > max_val:
545
- errors.append(f"{f.name} 的值 {value} 大于最大值 {max_val}")
546
+ errors.append(i18n.t("core.config.field_above_max", field=f.name, value=value, max=max_val))
546
547
 
547
548
  return errors
548
549
 
@@ -636,7 +637,6 @@ def register_config_i18n(
636
637
  :param domain: i18n 域标识,默认 "config"
637
638
  :return: 注册的翻译条目数
638
639
  """
639
- from ErisPulse.Core.i18n import i18n
640
640
 
641
641
  count = 0
642
642
  for f in fields(config_class):
@@ -706,7 +706,6 @@ def resolve_config_schema(config_class: type, resolve_i18n: bool = True) -> dict
706
706
  if not resolve_i18n:
707
707
  return schema
708
708
 
709
- from ErisPulse.Core.i18n import i18n
710
709
 
711
710
  for field_schema in schema["fields"].values():
712
711
  # description
@@ -72,8 +72,10 @@ class BaseConverter:
72
72
  :param raw_event: 平台原始事件数据
73
73
  :return: OneBot12 标准格式事件字典;无法识别时返回 None
74
74
  """
75
+ from ..i18n import i18n
76
+
75
77
  raise NotImplementedError(
76
- f"{self.__class__.__name__} 必须实现 convert() 方法"
78
+ i18n.t("core.converter.must_implement_convert", name=self.__class__.__name__)
77
79
  )
78
80
 
79
81
 
@@ -21,7 +21,6 @@ class ErisPulseError(Exception):
21
21
  所有 ErisPulse 自定义异常的基类。
22
22
  """
23
23
 
24
- ...
25
24
 
26
25
 
27
26
  class ClientError(ErisPulseError):
@@ -39,7 +38,6 @@ class ClientError(ErisPulseError):
39
38
  ... print(f"请求失败: {e}")
40
39
  """
41
40
 
42
- ...
43
41
 
44
42
 
45
43
 
@@ -56,7 +54,6 @@ class ClientConnectionError(ClientError):
56
54
  ... print("无法连接到服务器")
57
55
  """
58
56
 
59
- ...
60
57
 
61
58
 
62
59
  class ClientTimeoutError(ClientError):
@@ -72,7 +69,6 @@ class ClientTimeoutError(ClientError):
72
69
  ... print("请求超时")
73
70
  """
74
71
 
75
- ...
76
72
 
77
73
 
78
74
  class HTTPStatusError(ClientError):
@@ -104,7 +100,6 @@ class WebSocketError(ErisPulseError):
104
100
  WebSocket 连接、通信相关的异常。
105
101
  """
106
102
 
107
- ...
108
103
 
109
104
 
110
105
  class WebSocketDisconnect(WebSocketError):
@@ -15,6 +15,7 @@ ErisPulse i18n 键声明 Schema 模块
15
15
  {!--< /tips >!--}
16
16
  """
17
17
 
18
+ from ..i18n import i18n
18
19
 
19
20
  class I18nKey:
20
21
  """
@@ -73,7 +74,7 @@ class I18nKey:
73
74
  ru: str | None = None,
74
75
  ):
75
76
  if not default:
76
- raise ValueError("default 是必填项(语言无关的兜底文本)")
77
+ raise ValueError(i18n.t("core.i18n_schema.default_required"))
77
78
 
78
79
  self._key = key
79
80
  self.default = default
@@ -214,7 +215,6 @@ class BaseI18n:
214
215
  >>> MyKeys.register(prefix="myapp.", domain="myapp")
215
216
  2 # zh-CN + en
216
217
  """
217
- from ..i18n import i18n
218
218
 
219
219
  count = 0
220
220
  for attr_name, i18n_key in cls._collect_keys().items():
@@ -24,6 +24,7 @@ ErisPulse KV 查询构建器
24
24
  import json
25
25
  from typing import Any
26
26
 
27
+ from ..i18n import i18n
27
28
  from .storage import BaseQueryBuilder
28
29
 
29
30
  _TABLE_PREFIX = "__erispulse_sql__"
@@ -197,18 +198,18 @@ class KVQueryBuilder(BaseQueryBuilder):
197
198
  return self._exec_update()
198
199
  if self._operation == "delete":
199
200
  return self._exec_delete()
200
- raise ValueError(f"Unknown operation: {self._operation}")
201
+ raise ValueError(i18n.t("core.kvbuilder.unknown_operation", op=self._operation))
201
202
 
202
203
  def _exec_insert(self) -> int:
203
204
  if not isinstance(self._data, dict):
204
- raise ValueError("Insert 操作需要字典数据")
205
+ raise ValueError(i18n.t("core.kvbuilder.insert_needs_dict"))
205
206
  row_id = self._get_next_id()
206
207
  self._storage.set(self._row_key(row_id), json.dumps(self._data, ensure_ascii=False))
207
208
  return 1
208
209
 
209
210
  def _exec_insert_multi(self) -> int:
210
211
  if not isinstance(self._data, list):
211
- raise ValueError("InsertMulti 操作需要列表数据")
212
+ raise ValueError(i18n.t("core.kvbuilder.insert_multi_needs_list"))
212
213
  count = 0
213
214
  for row in self._data:
214
215
  row_id = self._get_next_id()
@@ -241,7 +242,7 @@ class KVQueryBuilder(BaseQueryBuilder):
241
242
 
242
243
  def _exec_update(self) -> int:
243
244
  if not isinstance(self._data, dict):
244
- raise ValueError("Update 操作需要字典数据")
245
+ raise ValueError(i18n.t("core.kvbuilder.update_needs_dict"))
245
246
  count = 0
246
247
  for row_id, row in self._scan_rows():
247
248
  if self._match_row(row):
@@ -304,13 +305,13 @@ class KVQueryBuilder(BaseQueryBuilder):
304
305
  """异步执行查询"""
305
306
  if self._operation == "insert":
306
307
  if not isinstance(self._data, dict):
307
- raise ValueError("Insert 操作需要字典数据")
308
+ raise ValueError(i18n.t("core.kvbuilder.insert_needs_dict"))
308
309
  row_id = self._get_next_id()
309
310
  await self._storage.aset(self._row_key(row_id), json.dumps(self._data, ensure_ascii=False))
310
311
  return 1
311
312
  if self._operation == "insert_multi":
312
313
  if not isinstance(self._data, list):
313
- raise ValueError("InsertMulti 操作需要列表数据")
314
+ raise ValueError(i18n.t("core.kvbuilder.insert_multi_needs_list"))
314
315
  count = 0
315
316
  for row in self._data:
316
317
  row_id = self._get_next_id()
@@ -321,7 +322,7 @@ class KVQueryBuilder(BaseQueryBuilder):
321
322
  return await self._aexec_select()
322
323
  if self._operation == "update":
323
324
  if not isinstance(self._data, dict):
324
- raise ValueError("Update 操作需要字典数据")
325
+ raise ValueError(i18n.t("core.kvbuilder.update_needs_dict"))
325
326
  count = 0
326
327
  for row_id, row in await self._ascan_rows():
327
328
  if self._match_row(row):
@@ -336,7 +337,7 @@ class KVQueryBuilder(BaseQueryBuilder):
336
337
  await self._storage.adelete(self._row_key(row_id))
337
338
  count += 1
338
339
  return count
339
- raise ValueError(f"Unknown operation: {self._operation}")
340
+ raise ValueError(i18n.t("core.kvbuilder.unknown_operation", op=self._operation))
340
341
 
341
342
  async def _aexec_select(self) -> list[tuple]:
342
343
  rows = await self._ascan_rows()
@@ -8,7 +8,10 @@ from abc import ABC, abstractmethod
8
8
  from typing import Any, TypedDict
9
9
 
10
10
  from ...loaders.strategy import ModuleLoadStrategy
11
+ from ..config import config as config_mgr
11
12
  from ..constants import DEFAULT_LAZY_LOADING_ENABLED, DEFAULT_MODULE_PRIORITY
13
+ from ..i18n import i18n
14
+ from ..logger import logger
12
15
 
13
16
 
14
17
  class ModuleEvent(TypedDict):
@@ -154,7 +157,6 @@ class BaseModule(ABC):
154
157
 
155
158
  if self.ConfigClass is None:
156
159
  return
157
- from ..config import config as config_mgr
158
160
  from .config_schema import (
159
161
  dataclass_to_defaults_dict,
160
162
  )
@@ -167,8 +169,7 @@ class BaseModule(ABC):
167
169
  config_mgr.setConfig(key, data, immediate=True)
168
170
  # 懒加载 logger(模块可能未注入 sdk)
169
171
  try:
170
- from ..logger import logger
171
- logger.info(f"已生成 {key} 默认配置模板")
172
+ logger.info(i18n.t("core.module.config_template_generated", key=key))
172
173
  except ImportError:
173
174
  pass
174
175
 
@@ -212,9 +213,8 @@ class BaseModule(ABC):
212
213
  """
213
214
  if self.ConfigClass is None:
214
215
  raise AttributeError(
215
- "未声明 ConfigClass,请设置 MyModule.ConfigClass = MyConfig"
216
+ i18n.t("core.module.config_class_not_declared")
216
217
  )
217
- from ..config import config as config_mgr
218
218
  from .config_schema import dict_to_dataclass
219
219
 
220
220
  data = config_mgr.getConfig(self._get_config_key())
@@ -230,7 +230,6 @@ class BaseModule(ABC):
230
230
  if value is not None:
231
231
  from dataclasses import asdict
232
232
 
233
- from ..config import config as config_mgr
234
233
 
235
234
  try:
236
235
  config_mgr.setConfig(self._get_config_key(), asdict(value))