@sophiacyl/dsh-plugin-waves 0.0.0-stage → 0.1.0-beta.2

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 (140) hide show
  1. package/DSH-GO-VERIFICATION.md +57 -0
  2. package/ICON.png +0 -0
  3. package/LICENSE +674 -0
  4. package/README.md +251 -2
  5. package/cordis.patch.yml +8 -0
  6. package/install.ps1 +0 -0
  7. package/install.sh +209 -0
  8. package/lib/client.js +1511 -0
  9. package/lib/host/aliashint.js +70 -0
  10. package/lib/host/attachments.js +25 -0
  11. package/lib/host/command.js +34 -0
  12. package/lib/host/consts.js +16 -0
  13. package/lib/host/dataguide.js +35 -0
  14. package/lib/host/imagebridge.js +41 -0
  15. package/lib/host/native-result.js +91 -0
  16. package/lib/host/native.js +191 -0
  17. package/lib/host/proxy.js +59 -0
  18. package/lib/host/results.js +67 -0
  19. package/lib/host/service.js +263 -0
  20. package/lib/host/tool.js +185 -0
  21. package/lib/host/troubleshoot.js +31 -0
  22. package/lib/host/worldbook.js +80 -0
  23. package/lib/index.js +74 -0
  24. package/locale/en.json +6 -0
  25. package/locale/zh.json +6 -0
  26. package/package.json +136 -4
  27. package/service/ICON.png +0 -0
  28. package/service/ai/AI_DATA.md +221 -0
  29. package/service/knowledge/additional/manual.jsonl +5 -0
  30. package/service/knowledge/wiki/baike.jsonl +1231 -0
  31. package/service/knowledge/wiki/chengjiu.jsonl +36 -0
  32. package/service/knowledge/wiki/gonglve.jsonl +383 -0
  33. package/service/knowledge/wiki/huodong.jsonl +427 -0
  34. package/service/knowledge/wiki/renwu.jsonl +412 -0
  35. package/service/knowledge/wiki/tujian.jsonl +2889 -0
  36. package/service/knowledge/wiki/zixun.jsonl +47 -0
  37. package/service/plugins/__init__.py +0 -0
  38. package/service/pyproject.toml +40 -0
  39. package/service/rover/__init__.py +0 -0
  40. package/service/rover/ai_core/__init__.py +0 -0
  41. package/service/rover/ai_core/models.py +50 -0
  42. package/service/rover/ai_core/rag/__init__.py +5 -0
  43. package/service/rover/ai_core/rag/knowledge.py +23 -0
  44. package/service/rover/ai_core/register.py +86 -0
  45. package/service/rover/ai_core/resource.py +6 -0
  46. package/service/rover/ai_core/skills/__init__.py +0 -0
  47. package/service/rover/ai_core/skills/operations.py +8 -0
  48. package/service/rover/api.py +720 -0
  49. package/service/rover/api_render.py +161 -0
  50. package/service/rover/app_life.py +154 -0
  51. package/service/rover/aps.py +47 -0
  52. package/service/rover/avatar.py +85 -0
  53. package/service/rover/bot.py +133 -0
  54. package/service/rover/builtin.py +152 -0
  55. package/service/rover/cards/__init__.py +26 -0
  56. package/service/rover/cards/_shared.py +129 -0
  57. package/service/rover/cards/char_list.py +258 -0
  58. package/service/rover/cards/char_panel.py +1353 -0
  59. package/service/rover/cards/echo_list.py +206 -0
  60. package/service/rover/cards/matrix_roles.py +25 -0
  61. package/service/rover/cards/rank_boards.py +714 -0
  62. package/service/rover/cards/rank_total.py +664 -0
  63. package/service/rover/compat.py +215 -0
  64. package/service/rover/config.py +163 -0
  65. package/service/rover/custom_panel.py +220 -0
  66. package/service/rover/data_store.py +31 -0
  67. package/service/rover/gss.py +19 -0
  68. package/service/rover/handler.py +191 -0
  69. package/service/rover/help/__init__.py +0 -0
  70. package/service/rover/help/draw_new_plugin_help.py +332 -0
  71. package/service/rover/help/model.py +16 -0
  72. package/service/rover/help/utils.py +7 -0
  73. package/service/rover/hooks.py +420 -0
  74. package/service/rover/html_actions.py +236 -0
  75. package/service/rover/i18n.py +19 -0
  76. package/service/rover/knowledge.py +235 -0
  77. package/service/rover/logger.py +102 -0
  78. package/service/rover/main.py +33 -0
  79. package/service/rover/media.py +222 -0
  80. package/service/rover/message_models.py +23 -0
  81. package/service/rover/models.py +92 -0
  82. package/service/rover/native_media.py +64 -0
  83. package/service/rover/ocr.py +79 -0
  84. package/service/rover/pool.py +43 -0
  85. package/service/rover/restart.py +36 -0
  86. package/service/rover/segment.py +143 -0
  87. package/service/rover/server.py +131 -0
  88. package/service/rover/status/__init__.py +0 -0
  89. package/service/rover/status/models.py +10 -0
  90. package/service/rover/status/plugin_status.py +17 -0
  91. package/service/rover/subscribe.py +148 -0
  92. package/service/rover/sv.py +403 -0
  93. package/service/rover/templates/char_list.html +182 -0
  94. package/service/rover/templates/char_panel.html +465 -0
  95. package/service/rover/templates/echo_list.html +221 -0
  96. package/service/rover/templates/rank_all_total.html +193 -0
  97. package/service/rover/templates/rank_board.html +107 -0
  98. package/service/rover/templates/rank_level_total.html +57 -0
  99. package/service/rover/templates/rank_matrix_total.html +66 -0
  100. package/service/rover/templates/rank_phantom_total.html +180 -0
  101. package/service/rover/templates/rank_slash_total.html +38 -0
  102. package/service/rover/trigger.py +98 -0
  103. package/service/rover/utils/__init__.py +0 -0
  104. package/service/rover/utils/boardcast/__init__.py +0 -0
  105. package/service/rover/utils/boardcast/models.py +15 -0
  106. package/service/rover/utils/cookie_manager/__init__.py +0 -0
  107. package/service/rover/utils/cookie_manager/qrlogin.py +24 -0
  108. package/service/rover/utils/database/__init__.py +0 -0
  109. package/service/rover/utils/database/base_models.py +1771 -0
  110. package/service/rover/utils/database/models.py +80 -0
  111. package/service/rover/utils/database/startup.py +60 -0
  112. package/service/rover/utils/default_bg/bg.jpg +0 -0
  113. package/service/rover/utils/download_resource/__init__.py +0 -0
  114. package/service/rover/utils/download_resource/download_core.py +164 -0
  115. package/service/rover/utils/download_resource/download_file.py +46 -0
  116. package/service/rover/utils/fonts/__init__.py +0 -0
  117. package/service/rover/utils/fonts/fonts.py +33 -0
  118. package/service/rover/utils/image/__init__.py +0 -0
  119. package/service/rover/utils/image/convert.py +51 -0
  120. package/service/rover/utils/image/image_tools.py +30 -0
  121. package/service/rover/utils/image/utils.py +70 -0
  122. package/service/rover/utils/plugins_config/__init__.py +0 -0
  123. package/service/rover/utils/plugins_config/gs_config.py +506 -0
  124. package/service/rover/utils/plugins_config/models.py +182 -0
  125. package/service/rover/web_app.py +3 -0
  126. package/service/rover/webconsole/__init__.py +0 -0
  127. package/service/rover/webconsole/mount_app.py +26 -0
  128. package/service/tools/migrate.py +351 -0
  129. package/service/tools/regression.py +87 -0
  130. package/service/tools/smoke.py +63 -0
  131. package/service/tools/update_plugins.py +212 -0
  132. package/service/tools/verify_actions.py +104 -0
  133. package/service/tools/verify_card.py +82 -0
  134. package/service/tools/wwdata.py +665 -0
  135. package/tests/compat.test.js +181 -0
  136. package/tests/imagebridge.test.js +58 -0
  137. package/tests/media.test.js +68 -0
  138. package/tests/native.test.js +127 -0
  139. package/tests/runtime.test.js +69 -0
  140. package/tests/service.test.js +48 -0
@@ -0,0 +1,148 @@
1
+ from typing import Dict, Literal, Optional, Sequence, Union
2
+
3
+ from rover.models import Event
4
+ from rover.utils.database.models import Subscribe
5
+
6
+
7
+ class RoverSubscribeManager:
8
+ async def add_subscribe(
9
+ self,
10
+ subscribe_type: Literal["session", "single"],
11
+ task_name: str,
12
+ event: Event,
13
+ extra_message: Optional[str] = None,
14
+ uid: Optional[str] = None,
15
+ extra_data: Optional[str] = None,
16
+ ):
17
+ """📝简单介绍:
18
+
19
+ 该方法允许向数据库添加一个订阅信息的持久化保存
20
+
21
+ 注意`subscribe_type`参数必须为`session`或`single`
22
+
23
+ `session`模式下, 订阅都将在每个有效的session(group或direct)内独立存在 (公告推送)
24
+
25
+ `single`模式下, 同个session(group)可能同时存在多个订阅 (签到任务)
26
+
27
+ 🌱参数:
28
+
29
+ 🔹subscribe_type (`Literal['session', 'single']`):
30
+ 'session'模式: 同个group/user下只存在一条订阅
31
+ 'single'模式: 同个group下存在多条订阅, 同个user只存在一条订阅
32
+
33
+ 🔹task_name (`str`):
34
+ 订阅名称
35
+
36
+ 🔹event (`Event`):
37
+ 事件Event
38
+
39
+ 🔹extra_message (`Optional[str]`, 默认是 `None`):
40
+ 额外想要保存的信息, 例如推送信息或者数值阈值
41
+
42
+ 🚀使用范例:
43
+
44
+ `await gs_subscribe.add_subscribe('single', '签到', event)`
45
+ """
46
+ opt: Dict[str, Union[str, int, None]] = {
47
+ "bot_id": event.bot_id,
48
+ "task_name": task_name,
49
+ "uid": uid,
50
+ "WS_BOT_ID": event.WS_BOT_ID,
51
+ }
52
+ if subscribe_type == "session" and event.user_type == "group":
53
+ opt["group_id"] = event.group_id
54
+ opt["user_type"] = event.user_type
55
+ else:
56
+ opt["user_id"] = event.user_id
57
+
58
+ condi = await Subscribe.data_exist(
59
+ **opt,
60
+ )
61
+
62
+ if not condi:
63
+ await Subscribe.full_insert_data(
64
+ user_id=event.user_id,
65
+ bot_id=event.bot_id,
66
+ group_id=event.group_id,
67
+ task_name=task_name,
68
+ bot_self_id=event.bot_self_id,
69
+ user_type=event.user_type,
70
+ extra_message=extra_message,
71
+ WS_BOT_ID=event.WS_BOT_ID,
72
+ uid=uid,
73
+ extra_data=extra_data,
74
+ msg_id=event.msg_id,
75
+ )
76
+ else:
77
+ upd = {}
78
+ for i in [
79
+ "user_id",
80
+ "bot_id",
81
+ "group_id",
82
+ "bot_self_id",
83
+ "user_type",
84
+ "WS_BOT_ID",
85
+ "msg_id",
86
+ ]:
87
+ if i not in opt:
88
+ upd[i] = event.__getattribute__(i)
89
+
90
+ upd["extra_message"] = extra_message
91
+ upd["extra_data"] = extra_data
92
+
93
+ await Subscribe.update_data_by_data(
94
+ opt,
95
+ upd,
96
+ )
97
+
98
+ async def get_subscribe(
99
+ self,
100
+ task_name: str,
101
+ user_id: Optional[str] = None,
102
+ bot_id: Optional[str] = None,
103
+ user_type: Optional[str] = None,
104
+ uid: Optional[str] = None,
105
+ WS_BOT_ID: Optional[str] = None,
106
+ ):
107
+ params = {
108
+ "task_name": task_name,
109
+ }
110
+
111
+ if user_id and bot_id and user_type:
112
+ params["user_id"] = user_id
113
+ params["bot_id"] = bot_id
114
+ params["user_type"] = user_type
115
+
116
+ if uid:
117
+ params["uid"] = uid
118
+
119
+ if WS_BOT_ID:
120
+ params["WS_BOT_ID"] = WS_BOT_ID
121
+
122
+ all_data: Optional[Sequence[Subscribe]] = await Subscribe.select_rows(distinct=False, **params)
123
+ return all_data
124
+
125
+ async def delete_subscribe(
126
+ self,
127
+ subscribe_type: Literal["session", "single"],
128
+ task_name: str,
129
+ event: Event,
130
+ uid: Optional[str] = None,
131
+ WS_BOT_ID: Optional[str] = None,
132
+ ):
133
+ params = {
134
+ "task_name": task_name,
135
+ }
136
+ if uid:
137
+ params["uid"] = uid
138
+
139
+ if WS_BOT_ID:
140
+ params["WS_BOT_ID"] = WS_BOT_ID
141
+
142
+ if subscribe_type == "session" and event.user_type == "group":
143
+ await Subscribe.delete_row(group_id=event.group_id, **params)
144
+ else:
145
+ await Subscribe.delete_row(user_id=event.user_id, **params)
146
+
147
+
148
+ gs_subscribe = RoverSubscribeManager()
@@ -0,0 +1,403 @@
1
+ """功能注册。插件用 Plugins/SV 声明命令, 触发器按前缀展开后进 SL 供分发器遍历。"""
2
+
3
+ from __future__ import annotations
4
+
5
+ import traceback
6
+ from copy import deepcopy
7
+ from functools import wraps
8
+ from pathlib import Path
9
+ from typing import Callable, Dict, List, Literal, Optional, Set, Tuple, Union
10
+
11
+ from rover.logger import logger
12
+ from rover.models import Event
13
+ from rover.trigger import Trigger, TriggerType
14
+
15
+ _SERVICE_ROOT = Path(__file__).resolve().parents[1]
16
+ _PLUGINS_DIR = _SERVICE_ROOT / "plugins"
17
+
18
+ # file/meta/message 匹配不读 prefix, 展开只会产生等价副本导致重复执行
19
+ _PREFIXLESS_TYPES = frozenset({"file", "meta", "message"})
20
+
21
+
22
+ class SVList:
23
+ def __init__(self):
24
+ self.lst: Dict[str, SV] = {}
25
+ self.plugins: Dict[str, Plugins] = {}
26
+ self.detail_lst: Dict[Plugins, List[SV]] = {}
27
+
28
+ @property
29
+ def get_lst(self):
30
+ return self.lst
31
+
32
+
33
+ SL = SVList()
34
+
35
+
36
+ def _configured_prefix() -> Optional[List[str]]:
37
+ """命令前缀由本地配置统一决定; 逗号分隔多个, 空表示不需要前缀。"""
38
+ import re
39
+
40
+ from rover.config import core_config
41
+
42
+ value = core_config.get_config("command_prefix")
43
+ if not isinstance(value, str):
44
+ return None
45
+ return [p for p in (x.strip() for x in re.split(r"[,,]", value)) if p]
46
+
47
+
48
+ def _sv_overrides(sv_name: str) -> Dict:
49
+ from rover.config import core_config
50
+
51
+ table = core_config.get_config("sv")
52
+ if isinstance(table, dict):
53
+ one = table.get(sv_name)
54
+ if isinstance(one, dict):
55
+ return one
56
+ return {}
57
+
58
+
59
+ def _caller_plugin_name() -> str:
60
+ """插件名取 plugins 下一级目录名, 与上游的注册名一致。"""
61
+ for frame in reversed(traceback.extract_stack()[:-1]):
62
+ try:
63
+ rel = Path(frame.filename).resolve().relative_to(_PLUGINS_DIR)
64
+ except (ValueError, OSError):
65
+ continue
66
+ if rel.parts:
67
+ return rel.parts[0]
68
+ return "XutheringWavesUID"
69
+
70
+
71
+ def modify_func(func):
72
+ @wraps(func)
73
+ async def wrapper(bot, event: Event):
74
+ try:
75
+ return await func(bot, event)
76
+ except Exception as e:
77
+ logger.error(f"执行命令 {event.command} 时出现错误")
78
+ logger.exception(e)
79
+
80
+ return wrapper
81
+
82
+
83
+ class Plugins:
84
+ def __new__(cls, *args, **kwargs):
85
+ name = args[0] if len(args) >= 1 else kwargs.get("name")
86
+ if name is None:
87
+ raise ValueError("Plugins.name is None!")
88
+ if name in SL.plugins and not kwargs.get("force"):
89
+ return SL.plugins[name]
90
+ _plugin = super().__new__(cls)
91
+ _plugin.__init__(*args, **kwargs)
92
+ SL.plugins[name] = _plugin
93
+ return _plugin
94
+
95
+ def __hash__(self) -> int:
96
+ return hash(f"{self.name}{self.priority}{self.pm}{self.area}")
97
+
98
+ def __eq__(self, other):
99
+ if isinstance(other, Plugins):
100
+ return (self.name, self.pm, self.priority) == (other.name, other.pm, other.priority)
101
+ return False
102
+
103
+ def __init__(
104
+ self,
105
+ name: str = "",
106
+ pm: int = 6,
107
+ priority: int = 5,
108
+ enabled: bool = True,
109
+ area: Literal["GROUP", "DIRECT", "ALL", "SV"] = "SV",
110
+ black_list: List = [],
111
+ white_list: List = [],
112
+ sv: Dict = {},
113
+ prefix: Union[List[str], str] = [],
114
+ force_prefix: List[str] = [],
115
+ disable_force_prefix: bool = False,
116
+ allow_empty_prefix: Optional[bool] = None,
117
+ force: bool = False,
118
+ alias: List[str] = [],
119
+ ):
120
+ if isinstance(prefix, str):
121
+ prefix = [prefix]
122
+ configured = _configured_prefix()
123
+ if configured is not None:
124
+ force_prefix = configured
125
+ prefix = []
126
+ disable_force_prefix = False
127
+ allow_empty_prefix = not configured
128
+ if allow_empty_prefix is None:
129
+ _pf = [p for p in prefix + force_prefix if p != ""]
130
+ allow_empty_prefix = not _pf
131
+
132
+ self.name = name
133
+ self.priority = priority
134
+ self.enabled = enabled
135
+ self.pm = pm
136
+ self.black_list = black_list
137
+ self.area = area
138
+ self.white_list = white_list
139
+ self.sv: Dict[str, SV] = {}
140
+ self.prefix = prefix
141
+ self.allow_empty_prefix = allow_empty_prefix
142
+ self.force_prefix = force_prefix
143
+ self.disable_force_prefix = disable_force_prefix
144
+ self.alias = alias
145
+
146
+ def set(self, is_lazy: bool = True, **kwargs):
147
+ for var, value in kwargs.items():
148
+ setattr(self, var, value)
149
+
150
+ def enable(self):
151
+ self.set(enabled=True)
152
+
153
+ def disable(self):
154
+ self.set(enabled=False)
155
+
156
+
157
+ class SV:
158
+ is_initialized = False
159
+
160
+ def __new__(cls, *args, **kwargs):
161
+ name = args[0] if len(args) >= 1 else kwargs.get("name")
162
+ if name is None:
163
+ raise ValueError("SV.name is None!")
164
+ if name in SL.lst:
165
+ return SL.lst[name]
166
+ _sv = super().__new__(cls)
167
+ SL.lst[name] = _sv
168
+ return _sv
169
+
170
+ def __init__(
171
+ self,
172
+ name: str = "",
173
+ pm: int = 6,
174
+ priority: int = 5,
175
+ enabled: bool = True,
176
+ area: Literal["GROUP", "DIRECT", "ALL"] = "ALL",
177
+ black_list: List = [],
178
+ white_list: List = [],
179
+ ):
180
+ if self.is_initialized:
181
+ return
182
+ self.name = name
183
+ self.TL: Dict[str, Dict[str, Trigger]] = {}
184
+ self.is_initialized = True
185
+ self.self_plugin_name = plugins_name = _caller_plugin_name()
186
+
187
+ if plugins_name in SL.plugins:
188
+ plugins = SL.plugins[plugins_name]
189
+ else:
190
+ plugins = Plugins(name=plugins_name)
191
+
192
+ self.plugins = plugins
193
+ self.priority = priority
194
+ self.enabled = enabled
195
+ self.pm = pm
196
+ self.black_list = black_list
197
+ self.area = area
198
+ self.white_list = white_list
199
+
200
+ for key, value in _sv_overrides(name).items():
201
+ if hasattr(self, key):
202
+ setattr(self, key, value)
203
+
204
+ plugins.sv[name] = self
205
+ SL.detail_lst.setdefault(plugins, [])
206
+ if self not in SL.detail_lst[plugins]:
207
+ SL.detail_lst[plugins].append(self)
208
+
209
+ def set(self, is_lazy: bool = True, **kwargs):
210
+ for var, value in kwargs.items():
211
+ setattr(self, var, value)
212
+
213
+ def enable(self):
214
+ self.set(enabled=True)
215
+
216
+ def disable(self):
217
+ self.set(enabled=False)
218
+
219
+ def _on(
220
+ self,
221
+ type: TriggerType,
222
+ keyword: Union[str, Tuple[str, ...]],
223
+ block: bool = False,
224
+ to_me: bool = False,
225
+ prefix: bool = True,
226
+ to_ai: str = "",
227
+ ):
228
+ def deco(func: Callable) -> Callable:
229
+ keyword_list = (keyword,) if isinstance(keyword, str) else keyword
230
+
231
+ _pp = deepcopy(self.plugins.prefix)
232
+ if "" in _pp:
233
+ _pp.remove("")
234
+ if not self.plugins.disable_force_prefix:
235
+ _pp.extend(self.plugins.force_prefix)
236
+ if self.plugins.allow_empty_prefix:
237
+ _pp.append("")
238
+ _pp = list(set(_pp))
239
+
240
+ for _k in keyword_list:
241
+ if _k in self.TL:
242
+ continue
243
+ if type not in self.TL:
244
+ self.TL[type] = {}
245
+ if prefix and _pp and type not in _PREFIXLESS_TYPES:
246
+ for _p in _pp:
247
+ self.TL[type][_p + _k] = Trigger(
248
+ type, _k, modify_func(func), _p, block, to_me
249
+ )
250
+ else:
251
+ self.TL[type][_k] = Trigger(type, _k, modify_func(func), "", block, to_me)
252
+
253
+ if to_ai.strip():
254
+ AI_COMMANDS.append(
255
+ {
256
+ "sv": self.name,
257
+ "plugin": self.self_plugin_name,
258
+ "type": type,
259
+ "keyword": list(keyword_list),
260
+ "desc": to_ai.strip(),
261
+ }
262
+ )
263
+
264
+ @wraps(func)
265
+ async def wrapper(bot, msg) -> Optional[Callable]:
266
+ return await func(bot, msg)
267
+
268
+ return wrapper
269
+
270
+ return deco
271
+
272
+ def on_fullmatch(
273
+ self,
274
+ keyword: Union[str, Tuple[str, ...]],
275
+ block: bool = False,
276
+ to_me: bool = False,
277
+ prefix: bool = True,
278
+ to_ai: str = "",
279
+ ) -> Callable:
280
+ return self._on("fullmatch", keyword, block, to_me, prefix, to_ai=to_ai)
281
+
282
+ def on_prefix(
283
+ self,
284
+ keyword: Union[str, Tuple[str, ...]],
285
+ block: bool = False,
286
+ to_me: bool = False,
287
+ prefix: bool = True,
288
+ to_ai: str = "",
289
+ ) -> Callable:
290
+ return self._on("prefix", keyword, block, to_me, prefix, to_ai=to_ai)
291
+
292
+ def on_suffix(
293
+ self,
294
+ keyword: Union[str, Tuple[str, ...]],
295
+ block: bool = False,
296
+ to_me: bool = False,
297
+ prefix: bool = True,
298
+ to_ai: str = "",
299
+ ) -> Callable:
300
+ return self._on("suffix", keyword, block, to_me, prefix, to_ai=to_ai)
301
+
302
+ def on_keyword(
303
+ self,
304
+ keyword: Union[str, Tuple[str, ...]],
305
+ block: bool = False,
306
+ to_me: bool = False,
307
+ prefix: bool = True,
308
+ to_ai: str = "",
309
+ ) -> Callable:
310
+ return self._on("keyword", keyword, block, to_me, prefix, to_ai=to_ai)
311
+
312
+ def on_command(
313
+ self,
314
+ keyword: Union[str, Tuple[str, ...]],
315
+ block: bool = False,
316
+ to_me: bool = False,
317
+ prefix: bool = True,
318
+ to_ai: str = "",
319
+ ) -> Callable:
320
+ return self._on("command", keyword, block, to_me, prefix, to_ai=to_ai)
321
+
322
+ def on_file(
323
+ self,
324
+ file_type: Union[str, Tuple[str, ...]],
325
+ block: bool = False,
326
+ to_me: bool = False,
327
+ prefix: bool = False,
328
+ to_ai: str = "",
329
+ ) -> Callable:
330
+ return self._on("file", file_type, block, to_me, prefix, to_ai=to_ai)
331
+
332
+ def on_regex(
333
+ self,
334
+ pattern: Union[str, Tuple[str, ...]],
335
+ block: bool = False,
336
+ to_me: bool = False,
337
+ prefix: bool = True,
338
+ to_ai: str = "",
339
+ ) -> Callable:
340
+ return self._on("regex", pattern, block, to_me, prefix, to_ai=to_ai)
341
+
342
+ def on_message(
343
+ self,
344
+ block: bool = False,
345
+ to_me: bool = False,
346
+ to_ai: str = "",
347
+ ) -> Callable:
348
+ return self._on("message", "message", block, to_me, prefix=False, to_ai=to_ai)
349
+
350
+ def on_meta(
351
+ self,
352
+ event_name: Union[str, Tuple[str, ...]],
353
+ block: bool = False,
354
+ ) -> Callable:
355
+ return self._on("meta", event_name, block=block, to_me=False, prefix=False)
356
+
357
+
358
+ AI_COMMANDS: List[Dict] = []
359
+
360
+
361
+ def get_plugin_prefixs(plugin_name: str) -> List[str]:
362
+ plugin = SL.plugins.get(plugin_name)
363
+ if plugin is None:
364
+ raise ValueError(f"插件{plugin_name}不存在!")
365
+ return plugin.prefix
366
+
367
+
368
+ def get_plugin_prefix(plugin_name: str) -> str:
369
+ return get_plugin_prefixs(plugin_name)[0]
370
+
371
+
372
+ def get_plugin_force_prefixs(plugin_name: str) -> List[str]:
373
+ plugin = SL.plugins.get(plugin_name)
374
+ if plugin is None:
375
+ raise ValueError(f"插件{plugin_name}不存在!")
376
+ return plugin.force_prefix
377
+
378
+
379
+ def get_plugin_force_prefix(plugin_name: str) -> str:
380
+ return get_plugin_force_prefixs(plugin_name)[0]
381
+
382
+
383
+ def get_plugin_available_prefix(plugin_name: str) -> str:
384
+ plugin = SL.plugins.get(plugin_name)
385
+ if plugin is None:
386
+ raise ValueError(f"插件{plugin_name}不存在!")
387
+ if not plugin.disable_force_prefix and plugin.force_prefix:
388
+ return plugin.force_prefix[0]
389
+ if plugin.disable_force_prefix and plugin.prefix:
390
+ return plugin.prefix[0]
391
+ return ""
392
+
393
+
394
+ def all_prefixes() -> List[str]:
395
+ out: List[str] = []
396
+ for plugin in SL.plugins.values():
397
+ if not plugin.disable_force_prefix:
398
+ out.extend(plugin.force_prefix)
399
+ out.extend(p for p in plugin.prefix if p)
400
+ # 去重但保序: 配置里写在前面的就排在前面, 卡片上生成的命令才不会每次重启换一个前缀
401
+ seen: Set[str] = set()
402
+ ordered = [p for p in out if not (p in seen or seen.add(p))]
403
+ return sorted(ordered, key=len, reverse=True)