@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,180 @@
1
+ <!DOCTYPE html>
2
+ <html lang="zh-CN">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <title>{{ header.title_name }}</title>
6
+ <style>
7
+ @import url('{{ font_css_url }}');
8
+
9
+ * { box-sizing: border-box; }
10
+ body { margin: 0; background: #0f1115; }
11
+
12
+ .container {
13
+ position: relative;
14
+ width: {{ card_w }}px;
15
+ padding-bottom: 40px;
16
+ background-image: url('{{ bg }}');
17
+ background-size: cover;
18
+ background-position: center;
19
+ font-family: 'Source Han Sans CN', 'Noto Sans CJK JP', 'Noto Sans CJK KR', sans-serif;
20
+ font-weight: 700;
21
+ color: #fff;
22
+ overflow: hidden;
23
+ }
24
+
25
+ /* 文本按 PIL 锚点定位: lm 左中 / mm 中中 / rm 右中 */
26
+ .t { position: absolute; white-space: nowrap; line-height: 1; transform: translateY(-50%); }
27
+ .t.mm { transform: translate(-50%, -50%); }
28
+
29
+ /* ---------------- 头部: 标题底图 + 立绘, 底部渐隐 ---------------- */
30
+ .header { position: relative; width: 100%; height: {{ header_h }}px; }
31
+ .header .art {
32
+ position: absolute; inset: 0; overflow: hidden;
33
+ -webkit-mask-image: {{ header.mask }}; mask-image: {{ header.mask }};
34
+ }
35
+ .header .art img { position: absolute; }
36
+ .header .title-bg { left: {{ header.title_dx }}px; top: 0; width: 1050px; height: 500px; }
37
+ .header .logo { left: 50px; top: 65px; width: 200px; height: 80px; }
38
+ .header .time { left: 170px; top: 205px; font-size: 20px; color: {{ grey }}; }
39
+ .header .title { left: 240px; top: 265px; font-size: 30px; color: #000; }
40
+ .header .ver {
41
+ position: absolute; top: 255px; width: 100px; height: 30px;
42
+ border-radius: 6px; background: rgba(0, 79, 152, .9);
43
+ font-size: 24px; line-height: 30px; text-align: center;
44
+ }
45
+ .header .stat-v { top: 335px; font-size: 44px; }
46
+ .header .stat-l { top: 375px; font-size: 20px; color: {{ gold }}; }
47
+
48
+ /* ---------------- 上榜条件 ---------------- */
49
+ .cond { position: relative; width: 100%; height: {{ cond.height }}px; }
50
+ .cond .panel {
51
+ position: absolute; left: 20px; top: 20px;
52
+ width: {{ card_w - 40 }}px; height: {{ cond.height - 35 }}px;
53
+ border-radius: 8px; background: rgba(36, 36, 41, .9);
54
+ }
55
+ .cond .accent {
56
+ position: absolute; left: 20px; top: 20px;
57
+ width: {{ card_w - 40 }}px; height: 7px; background: rgb(203, 161, 95);
58
+ }
59
+ .cond .label { left: 40px; top: 60px; font-size: 24px; color: {{ grey }}; }
60
+ .cond .line { left: 185px; font-size: 20px; color: {{ gold }}; }
61
+ .cond .notes { right: 40px; top: {{ cond.height - 30 }}px; font-size: 16px; color: {{ gold }}; }
62
+
63
+ /* ---------------- 排行行 ---------------- */
64
+ .row {
65
+ position: relative; width: {{ card_w }}px; height: {{ geo.item_h }}px;
66
+ background-image: url('{{ bar }}'); background-size: 100% 100%;
67
+ overflow: hidden;
68
+ }
69
+ .row .glow { position: absolute; left: {{ geo.user_x - 20 }}px; right: 70px; top: 10px; bottom: 10px; border-radius: 14px; }
70
+ .row.t1 .glow { background: linear-gradient(90deg, rgba(255, 196, 60, .18), rgba(255, 196, 60, 0) 42%); }
71
+ .row.t2 .glow { background: linear-gradient(90deg, rgba(208, 216, 226, .16), rgba(208, 216, 226, 0) 42%); }
72
+ .row.t3 .glow { background: linear-gradient(90deg, rgba(214, 150, 92, .16), rgba(214, 150, 92, 0) 42%); }
73
+
74
+ .row .medal { position: absolute; left: {{ geo.rank_x }}px; top: {{ geo.medal_y }}px; width: {{ geo.medal_size }}px; height: {{ geo.medal_size }}px; }
75
+ .row .box {
76
+ position: absolute; top: {{ geo.box_y }}px; height: {{ geo.box_h }}px;
77
+ border-radius: 8px; background: rgba(54, 54, 54, .9); text-align: center;
78
+ line-height: {{ geo.box_h }}px;
79
+ }
80
+ .row .avatar { position: absolute; }
81
+ .row .name {
82
+ left: {{ geo.user_x }}px; top: {{ geo.name_y }}px; font-size: 22px;
83
+ max-width: {{ geo.name_max }}px; overflow: hidden; text-overflow: ellipsis;
84
+ }
85
+ .row .uid { left: {{ geo.user_x }}px; top: {{ geo.uid_y }}px; font-size: 18px; }
86
+ .row .badge { position: absolute; left: {{ geo.badge_x }}px; top: {{ geo.badge_y }}px; width: 208px; height: 39px; }
87
+ .row .badge .plate { position: absolute; left: 4px; top: 5px; width: 201px; height: 31px; border-radius: 6px; background: rgba(54, 54, 54, .6); }
88
+ .row .badge .bg { position: absolute; left: 104px; top: 19px; transform: translate(-50%, -50%); }
89
+ .row .badge .txt { position: absolute; left: 104px; top: 20px; transform: translate(-50%, -50%); font-size: 18px; white-space: nowrap; }
90
+
91
+ .row .thumb { position: absolute; left: {{ geo.thumb_x }}px; top: {{ geo.icon_y }}px; width: {{ geo.icon_size }}px; height: {{ geo.icon_size }}px; }
92
+ .row .fetter { position: absolute; left: {{ geo.thumb_x + geo.fetter_dx }}px; top: {{ geo.fetter_y }}px; width: {{ geo.fetter_size }}px; height: {{ geo.fetter_size }}px; }
93
+ .row .pip { position: absolute; top: {{ geo.pip_y }}px; width: {{ geo.pip_size }}px; height: {{ geo.pip_size }}px; }
94
+
95
+ .row .hl { position: absolute; height: 26px; border-radius: 6px; background: rgba(234, 183, 4, .18); }
96
+ .row .pname { max-width: {{ geo.name_col_max }}px; overflow: hidden; text-overflow: ellipsis; }
97
+ .row .pval, .row .pscore { font-size: 16px; }
98
+
99
+ .row .emblem { position: absolute; left: {{ geo.emblem_x }}px; top: {{ geo.emblem_y }}px; width: {{ geo.emblem_size }}px; height: {{ geo.emblem_size }}px; }
100
+ .row .score-v { left: {{ geo.score_x }}px; top: {{ geo.score_y }}px; font-size: 40px; }
101
+ .row .score-l { left: {{ geo.score_label_x }}px; top: {{ geo.score_label_y }}px; font-size: 16px; color: {{ gold }}; }
102
+ .hot { position: absolute; border-radius: 10px; }
103
+
104
+ .icon-img { background-size: 100% 100%; background-repeat: no-repeat; }
105
+ {% for a in assets %}.{{ a.cls }} { background-image: url('{{ a.uri }}'); }
106
+ {% endfor %}
107
+ </style>
108
+ </head>
109
+ <body>
110
+ <div class="container">
111
+ <div class="header">
112
+ <div class="art">
113
+ <img class="title-bg" src="{{ header.title_bg }}">
114
+ <img class="logo" src="{{ header.logo }}">
115
+ <div class="t time">{{ header.time }}</div>
116
+ <div class="t title">{{ header.title_name }}</div>
117
+ <div class="ver" style="left: {{ header.version_x }}px">{{ header.version }}</div>
118
+ {% for s in header.stats %}
119
+ <div class="t mm stat-v" style="left: {{ s.x }}px">{{ s.value }}</div>
120
+ <div class="t mm stat-l" style="left: {{ s.x }}px">{{ s.label }}</div>
121
+ {% endfor %}
122
+ {% if header.pile %}
123
+ <img class="pile" src="{{ header.pile.uri }}"
124
+ style="left: {{ header.pile.x }}px; top: {{ header.pile.y }}px; width: {{ header.pile.w }}px; height: {{ header.pile.h }}px">
125
+ {% endif %}
126
+ </div>
127
+ </div>
128
+
129
+ <div class="cond">
130
+ <div class="panel"></div>
131
+ <div class="accent"></div>
132
+ <div class="t label">上榜条件</div>
133
+ <div class="t line" style="top: 50px">{{ cond.line1 }}</div>
134
+ <div class="t line" style="top: 85px">{{ cond.line2 }}</div>
135
+ <div class="t notes">{{ cond.notes }}</div>
136
+ </div>
137
+
138
+ {% for row in rows %}
139
+ <div class="row{% if row.rank <= 3 %} t{{ row.rank }}{% endif %}">
140
+ <div class="glow"></div>
141
+ {% if row.medal %}
142
+ <div class="medal icon-img {{ row.medal }}"></div>
143
+ {% else %}
144
+ <div class="box" style="left: {{ geo.box_cx - row.box.w // 2 }}px; width: {{ row.box.w }}px; font-size: {{ row.box.size }}px">{{ row.box.text }}</div>
145
+ {% endif %}
146
+ {% if row.avatar %}
147
+ <img class="avatar" src="{{ row.avatar.uri }}"
148
+ style="left: {{ row.avatar.x }}px; top: {{ row.avatar.y }}px; width: {{ row.avatar.w }}px; height: {{ row.avatar.h }}px">
149
+ {% endif %}
150
+ <div class="t name">{{ row.name }}</div>
151
+ <div class="t uid" style="color: {{ row.uid_color }}">{{ row.uid }}</div>
152
+ {% if row.badge %}
153
+ <div class="badge">
154
+ {% if row.badge.cls %}
155
+ <div class="bg icon-img {{ row.badge.cls }}" style="width: {{ row.badge.w }}px; height: {{ row.badge.h }}px"></div>
156
+ {% else %}<div class="plate"></div>{% endif %}
157
+ <div class="txt">{{ row.badge.name }}</div>
158
+ </div>
159
+ {% endif %}
160
+ {% if row.icon %}<div class="thumb icon-img {{ row.icon }}"></div>{% endif %}
161
+ {% if row.fetter %}<div class="fetter icon-img {{ row.fetter }}"></div>{% endif %}
162
+ {% for x in row.pips %}<img class="pip" src="{{ pip }}" style="left: {{ x }}px">{% endfor %}
163
+
164
+ {% for p in row.props %}
165
+ {% if p.highlight %}<div class="hl" style="left: {{ p.highlight.left }}px; top: {{ p.y - 13 }}px; width: {{ p.highlight.w }}px"></div>{% endif %}
166
+ <div class="t pname" style="left: {{ p.name_x }}px; top: {{ p.y }}px; font-size: {{ p.name_size }}px; color: {{ p.name_color }}">{{ p.name }}</div>
167
+ <div class="t pval" style="right: {{ p.value_right }}px; top: {{ p.y }}px; color: {{ p.value_color }}">{{ p.value }}</div>
168
+ <div class="t pscore" style="right: {{ p.score_right }}px; top: {{ p.y }}px; color: {{ p.score_color }}">{{ p.score }}</div>
169
+ {% endfor %}
170
+
171
+ {% if row.emblem %}<div class="emblem icon-img {{ row.emblem }}"></div>{% endif %}
172
+ <div class="t mm score-v">{{ row.score }}</div>
173
+ <div class="t mm score-l">单声骸分数</div>
174
+ <div class="hot" style="left: {{ geo.emblem_x }}px; top: {{ geo.emblem_y }}px; width: {{ geo.width - geo.emblem_x - 30 }}px; height: {{ geo.emblem_size }}px" data-ww-cmd="{{ cmd_damage }}"></div>
175
+ <div class="hot" style="left: {{ geo.thumb_x }}px; top: {{ geo.icon_y }}px; width: {{ geo.icon_size }}px; height: {{ geo.icon_size }}px" data-ww-cmd="{{ cmd_score }}"></div>
176
+ </div>
177
+ {% endfor %}
178
+ </div>
179
+ </body>
180
+ </html>
@@ -0,0 +1,38 @@
1
+ {% extends "rank_board.html" %}
2
+
3
+ {% block style %}
4
+ /* ---------------- 行内: 限定金数 / 两个半场 / 总分 ---------------- */
5
+ .row .gold { left: 210px; top: 40px; font-size: 18px; }
6
+ .row .score { left: 1140px; top: 55px; font-size: 44px; }
7
+
8
+ /* 挂了命令的格子会被交互桥改成 position:relative, 这里按更高优先级钉回绝对定位 */
9
+ .row .ch { position: absolute; top: 20px; width: 45px; height: 45px; overflow: hidden; }
10
+ .row .ch .pic { position: absolute; inset: 0; background-size: 100% 100%; }
11
+ .row .ch .chain { position: absolute; left: 30px; top: 30px; width: 20px; height: 20px; }
12
+ .row .ch .chain span { position: absolute; left: 8px; top: 8px; transform: translate(-50%, -50%); font-size: 12px; }
13
+
14
+ .row .buff { top: 15px; width: 50px; height: 50px; }
15
+ .row .buff .plate { position: absolute; inset: 0; border-radius: 5px; background: rgba(0, 0, 0, .8); }
16
+ .row .buff .strip { position: absolute; left: 0; top: 45px; width: 50px; height: 5px; }
17
+ .row .buff .pic { position: absolute; inset: 0; background-size: 100% 100%; }
18
+ .row .half-score { top: 80px; font-size: 20px; }
19
+ {% endblock %}
20
+
21
+ {% block row %}
22
+ <div class="t gold">{{ row.gold }}</div>
23
+ <div class="t mm score" style="color: {{ row.score_color }}">{{ row.score }}</div>
24
+ {% for half in row.halves %}
25
+ {% for c in half.chars %}
26
+ <div class="ch icon-img" style="left: {{ c.x }}px"{% if c.cmd %} data-ww-cmd="{{ c.cmd }}"{% endif %}>
27
+ <div class="pic {{ c.cls }}"></div>
28
+ {% if c.chain != -1 %}<div class="chain" style="background: {{ c.chain_color }}"><span>{{ c.chain }}</span></div>{% endif %}
29
+ </div>
30
+ {% endfor %}
31
+ <div class="buff icon-img" style="left: {{ half.buff_x }}px">
32
+ <div class="plate"></div>
33
+ <div class="strip" style="background: {{ half.buff_color }}"></div>
34
+ {% if half.buff %}<div class="pic {{ half.buff }}"></div>{% endif %}
35
+ </div>
36
+ <div class="t mm half-score" style="left: {{ half.score_x }}px; color: {{ half.score_color }}">{{ half.score }}</div>
37
+ {% endfor %}
38
+ {% endblock %}
@@ -0,0 +1,98 @@
1
+ import re
2
+ from typing import Any, Awaitable, Callable, Literal
3
+
4
+ from rover.models import Event
5
+
6
+ TriggerType = Literal[
7
+ "prefix",
8
+ "suffix",
9
+ "keyword",
10
+ "fullmatch",
11
+ "command",
12
+ "file",
13
+ "regex",
14
+ "message",
15
+ "meta",
16
+ ]
17
+
18
+
19
+ class Trigger:
20
+ def __init__(
21
+ self,
22
+ type: TriggerType,
23
+ keyword: str,
24
+ func: Callable,
25
+ prefix: str = "",
26
+ block: bool = False,
27
+ to_me: bool = False,
28
+ ):
29
+ self.type = type
30
+ self.prefix = prefix
31
+ self.keyword = keyword
32
+ self.func: Callable[[Any, Event], Awaitable[Any]] = func
33
+ self.block = block
34
+ self.to_me = to_me
35
+
36
+ def check_command(self, ev: Event) -> bool:
37
+ msg = ev.raw_text
38
+ if self.to_me and not ev.is_tome:
39
+ return False
40
+ if self.type == "file":
41
+ return self._check_file(self.keyword, ev)
42
+ if self.type == "meta":
43
+ return self._check_meta(self.keyword, ev)
44
+ return getattr(self, f"_check_{self.type}")(self.keyword, msg)
45
+
46
+ def _check_prefix(self, prefix: str, msg: str) -> bool:
47
+ return msg.startswith(self.prefix + prefix) and not self._check_fullmatch(prefix, msg)
48
+
49
+ def _check_command(self, command: str, msg: str) -> bool:
50
+ return msg.startswith(self.prefix + command)
51
+
52
+ def _check_suffix(self, suffix: str, msg: str) -> bool:
53
+ return (
54
+ msg.startswith(self.prefix)
55
+ and msg.endswith(suffix)
56
+ and not self._check_fullmatch(suffix, msg)
57
+ )
58
+
59
+ def _check_keyword(self, keyword: str, msg: str) -> bool:
60
+ return keyword in msg and msg.startswith(self.prefix)
61
+
62
+ def _check_fullmatch(self, keyword: str, msg: str) -> bool:
63
+ return msg == f"{self.prefix}{keyword}" and msg.startswith(self.prefix)
64
+
65
+ def _check_file(self, file_type: str, ev: Event) -> bool:
66
+ if ev.file and ev.file_name:
67
+ return ev.file_name.split(".")[-1] == file_type
68
+ return False
69
+
70
+ def _check_meta(self, event_name: str, ev: Event) -> bool:
71
+ return ev.meta_event_type == event_name
72
+
73
+ def _check_regex(self, pattern: str, msg: str) -> bool:
74
+ if msg.startswith(self.prefix):
75
+ _msg = msg.replace(self.prefix, "", 1)
76
+ return bool(re.findall(pattern, _msg))
77
+ return False
78
+
79
+ def _check_message(self, keyword: str, msg: str) -> bool:
80
+ return True
81
+
82
+ async def get_command(self, msg: Event) -> Event:
83
+ if self.type != "regex":
84
+ msg.command = self.keyword
85
+ msg.text = msg.raw_text.replace(self.keyword, "", 1)
86
+ if self.prefix:
87
+ msg.text = msg.text.replace(self.prefix, "", 1)
88
+ else:
89
+ if self.prefix:
90
+ msg.text = msg.text.replace(self.prefix, "", 1)
91
+ command_group = re.search(self.keyword, msg.text)
92
+ if command_group:
93
+ msg.regex_dict = command_group.groupdict()
94
+ msg.regex_group = command_group.groups()
95
+ msg.command = "|".join([i if i is not None else "" for i in list(msg.regex_group)])
96
+ text_list = re.split(self.keyword, msg.raw_text)
97
+ msg.text = "|".join([i if i is not None else "" for i in text_list])
98
+ return msg
File without changes
File without changes
@@ -0,0 +1,15 @@
1
+ """广播消息结构。"""
2
+
3
+ from typing import Dict, List, TypedDict
4
+
5
+ from rover.models import Message
6
+
7
+
8
+ class BoardCastMsg(TypedDict):
9
+ bot_id: str
10
+ messages: List[Message]
11
+
12
+
13
+ class BoardCastMsgDict(TypedDict):
14
+ private_msg_dict: Dict[str, List[BoardCastMsg]]
15
+ group_msg_dict: Dict[str, BoardCastMsg]
File without changes
@@ -0,0 +1,24 @@
1
+ """扫码登录二维码。"""
2
+
3
+ import io
4
+ from pathlib import Path
5
+
6
+
7
+ async def get_qrcode_base64(url: str, path: Path, bot_id: str) -> bytes:
8
+ # qrcode(+约2.7MB, 连带 PIL) 仅扫码登录时用到, 按需导入避免常驻启动内存。
9
+ import qrcode
10
+ from qrcode.constants import ERROR_CORRECT_L
11
+
12
+ qr = qrcode.QRCode( # type: ignore
13
+ version=1,
14
+ error_correction=ERROR_CORRECT_L,
15
+ box_size=10,
16
+ border=4,
17
+ )
18
+ qr.add_data(url)
19
+ qr.make(fit=True)
20
+ img = qr.make_image(fill_color=(255, 134, 36), back_color="white")
21
+
22
+ img_byte = io.BytesIO()
23
+ img.save(img_byte, format="PNG") # type: ignore
24
+ return img_byte.getvalue()
File without changes