koishi-plugin-cchess 1.0.6 → 1.0.8

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.
package/LICENSE-APACHE ADDED
@@ -0,0 +1,178 @@
1
+ # Apache License
2
+
3
+ Apache License
4
+ Version 2.0, January 2004
5
+ http://www.apache.org/licenses/
6
+
7
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
8
+
9
+ 1. Definitions.
10
+
11
+ "License" shall mean the terms and conditions for use, reproduction,
12
+ and distribution as defined by Sections 1 through 9 of this document.
13
+
14
+ "Licensor" shall mean the copyright owner or entity authorized by
15
+ the copyright owner that is granting the License.
16
+
17
+ "Legal Entity" shall mean the union of the acting entity and all
18
+ other entities that control, are controlled by, or are under common
19
+ control with that entity. For the purposes of this definition,
20
+ "control" means (i) the power, direct or indirect, to cause the
21
+ direction or management of such entity, whether by contract or
22
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
23
+ outstanding shares, or (iii) beneficial ownership of such entity.
24
+
25
+ "You" (or "Your") shall mean an individual or Legal Entity
26
+ exercising permissions granted by this License.
27
+
28
+ "Source" form shall mean the preferred form for making modifications,
29
+ including but not limited to software source code, documentation
30
+ source, and configuration files.
31
+
32
+ "Object" form shall mean any form resulting from mechanical
33
+ transformation or translation of a Source form, including but
34
+ not limited to compiled object code, generated documentation,
35
+ and conversions to other media types.
36
+
37
+ "Work" shall mean the work of authorship, whether in Source or
38
+ Object form, made available under the License, as indicated by a
39
+ copyright notice that is included in or attached to the work
40
+ (an example is provided in the Appendix below).
41
+
42
+ "Derivative Works" shall mean any work, whether in Source or Object
43
+ form, that is based on (or derived from) the Work and for which the
44
+ editorial revisions, annotations, elaborations, or other modifications
45
+ represent, as a whole, an original work of authorship. For the purposes
46
+ of this License, Derivative Works shall not include works that remain
47
+ separable from, or merely link (or bind by name) to the interfaces of,
48
+ the Work and Derivative Works thereof.
49
+
50
+ "Contribution" shall mean any work of authorship, including
51
+ the original version of the Work and any modifications or additions
52
+ to that Work or Derivative Works thereof, that is intentionally
53
+ submitted to Licensor for inclusion in the Work by the copyright owner
54
+ or by an individual or Legal Entity authorized to submit on behalf of
55
+ the copyright owner. For the purposes of this definition, "submitted"
56
+ means any form of electronic, verbal, or written communication sent
57
+ to the Licensor or its representatives, including but not limited to
58
+ communication on electronic mailing lists, source code control systems,
59
+ and issue tracking systems that are managed by, or on behalf of, the
60
+ Licensor for the purpose of discussing and improving the Work, but
61
+ excluding communication that is conspicuously marked or otherwise
62
+ designated in writing by the copyright owner as "Not a Contribution."
63
+
64
+ "Contributor" shall mean Licensor and any individual or Legal Entity
65
+ on behalf of whom a Contribution has been received by Licensor and
66
+ subsequently incorporated within the Work.
67
+
68
+ 2. Grant of Copyright License. Subject to the terms and conditions of
69
+ this License, each Contributor hereby grants to You a perpetual,
70
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
71
+ copyright license to reproduce, prepare Derivative Works of,
72
+ publicly display, publicly perform, sublicense, and distribute the
73
+ Work and such Derivative Works in Source or Object form.
74
+
75
+ 3. Grant of Patent License. Subject to the terms and conditions of
76
+ this License, each Contributor hereby grants to You a perpetual,
77
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
78
+ (except as stated in this section) patent license to make, have made,
79
+ use, offer to sell, sell, import, and otherwise transfer the Work,
80
+ where such license applies only to those patent claims licensable
81
+ by such Contributor that are necessarily infringed by their
82
+ Contribution(s) alone or by combination of their Contribution(s)
83
+ with the Work to which such Contribution(s) was submitted. If You
84
+ institute patent litigation against any entity (including a
85
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
86
+ or a Contribution incorporated within the Work constitutes direct
87
+ or contributory patent infringement, then any patent licenses
88
+ granted to You under this License for that Work shall terminate
89
+ as of the date such litigation is filed.
90
+
91
+ 4. Redistribution. You may reproduce and distribute copies of the
92
+ Work or Derivative Works thereof in any medium, with or without
93
+ modifications, and in Source or Object form, provided that You
94
+ meet the following conditions:
95
+
96
+ (a) You must give any other recipients of the Work or
97
+ Derivative Works a copy of this License; and
98
+
99
+ (b) You must cause any modified files to carry prominent notices
100
+ stating that You changed the files; and
101
+
102
+ (c) You must retain, in the Source form of any Derivative Works
103
+ that You distribute, all copyright, patent, trademark, and
104
+ attribution notices from the Source form of the Work,
105
+ excluding those notices that do not pertain to any part of
106
+ the Derivative Works; and
107
+
108
+ (d) If the Work includes a "NOTICE" text file as part of its
109
+ distribution, then any Derivative Works that You distribute must
110
+ include a readable copy of the attribution notices contained
111
+ within such NOTICE file, excluding those notices that do not
112
+ pertain to any part of the Derivative Works, in at least one
113
+ of the following places: within a NOTICE text file distributed
114
+ as part of the Derivative Works; within the Source form or
115
+ documentation, if provided along with the Derivative Works; or,
116
+ within a display generated by the Derivative Works, if and
117
+ wherever such third-party notices normally appear. The contents
118
+ of the NOTICE file are for informational purposes only and
119
+ do not modify the License. You may add Your own attribution
120
+ notices within Derivative Works that You distribute, alongside
121
+ or as an addendum to the NOTICE text from the Work, provided
122
+ that such additional attribution notices cannot be construed
123
+ as modifying the License.
124
+
125
+ You may add Your own copyright statement to Your modifications and
126
+ may provide additional or different license terms and conditions
127
+ for use, reproduction, or distribution of Your modifications, or
128
+ for any such Derivative Works as a whole, provided Your use,
129
+ reproduction, and distribution of the Work otherwise complies with
130
+ the conditions stated in this License.
131
+
132
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
133
+ any Contribution intentionally submitted for inclusion in the Work
134
+ by You to the Licensor shall be under the terms and conditions of
135
+ this License, without any additional terms or conditions.
136
+ Notwithstanding the above, nothing herein shall supersede or modify
137
+ the terms of any separate license agreement you may have executed
138
+ with Licensor regarding such Contributions.
139
+
140
+ 6. Trademarks. This License does not grant permission to use the trade
141
+ names, trademarks, service marks, or product names of the Licensor,
142
+ except as required for reasonable and customary use in describing the
143
+ origin of the Work and reproducing the content of the NOTICE file.
144
+
145
+ 7. Disclaimer of Warranty. Unless required by applicable law or
146
+ agreed to in writing, Licensor provides the Work (and each
147
+ Contributor provides its Contributions) on an "AS IS" BASIS,
148
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
149
+ implied, including, without limitation, any warranties or conditions
150
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
151
+ PARTICULAR PURPOSE. You are solely responsible for determining the
152
+ appropriateness of using or redistributing the Work and assume any
153
+ risks associated with Your exercise of permissions under this License.
154
+
155
+ 8. Limitation of Liability. In no event and under no legal theory,
156
+ whether in tort (including negligence), contract, or otherwise,
157
+ unless required by applicable law (such as deliberate and grossly
158
+ negligent acts) or agreed to in writing, shall any Contributor be
159
+ liable to You for damages, including any direct, indirect, special,
160
+ incidental, or consequential damages of any character arising as a
161
+ result of this License or out of the use or inability to use the
162
+ Work (including but not limited to damages for loss of goodwill,
163
+ work stoppage, computer failure or malfunction, or any and all
164
+ other commercial damages or losses), even if such Contributor
165
+ has been advised of the possibility of such damages.
166
+
167
+ 9. Accepting Warranty or Additional Liability. While redistributing
168
+ the Work or Derivative Works thereof, You may choose to offer,
169
+ and charge a fee for, acceptance of support, warranty, indemnity,
170
+ or other liability obligations and/or rights consistent with this
171
+ License. However, in accepting such obligations, You may act only
172
+ on Your own behalf and on Your sole responsibility, not on behalf
173
+ of any other Contributor, and only if You agree to indemnify,
174
+ defend, and hold each Contributor harmless for any liability
175
+ incurred by, or claims asserted against, such Contributor by reason
176
+ of your accepting any such warranty or additional liability.
177
+
178
+ END OF TERMS AND CONDITIONS
package/LICENSE-MIT ADDED
@@ -0,0 +1,25 @@
1
+ # MIT License
2
+
3
+ Permission is hereby granted, free of charge, to any
4
+ person obtaining a copy of this software and associated
5
+ documentation files (the "Software"), to deal in the
6
+ Software without restriction, including without
7
+ limitation the rights to use, copy, modify, merge,
8
+ publish, distribute, sublicense, and/or sell copies of
9
+ the Software, and to permit persons to whom the Software
10
+ is furnished to do so, subject to the following
11
+ conditions:
12
+
13
+ The above copyright notice and this permission notice
14
+ shall be included in all copies or substantial portions
15
+ of the Software.
16
+
17
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
18
+ ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED
19
+ TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A
20
+ PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT
21
+ SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
22
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
24
+ IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25
+ DEALINGS IN THE SOFTWARE.
package/lib/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { Context, Schema } from 'koishi';
2
2
  export declare const name = "cchess";
3
- export declare const usage = "## \u4F7F\u7528\n\n1. \u5B89\u88C5 `puppeteer` \u670D\u52A1\u3002\n2. \u4E3A\u5E38\u7528\u6307\u4EE4\u8BBE\u7F6E\u522B\u540D\uFF08\u5982\u300C\u52A0\u5165\u300D\u300C\u8BA4\u8F93\u300D\u300C\u6094\u68CB\u300D\uFF09\u3002\n\n## \u6307\u4EE4\n\n| \u6307\u4EE4 | \u8BF4\u660E |\n| --- | --- |\n| `cchess.\u52A0\u5165 [\u7EA2/\u9ED1]` | \u5165\u5EA7\u5E76\u9009\u62E9\u9635\u8425\uFF0C\u7701\u7565\u5219\u968F\u673A |\n| `cchess.\u9000\u51FA` | \u5F00\u5C40\u524D\u79BB\u5E2D |\n| `cchess.\u5F00\u59CB.\u4EBA\u4EBA\u5BF9\u6218` | \u4E0E\u597D\u53CB\u624B\u8C08\uFF08\u81F3\u5C11 2 \u4EBA\uFF09 |\n| `cchess.\u5F00\u59CB.\u4EBA\u673A\u5BF9\u6218` | \u6311\u6218\u76AE\u5361\u9C7C\uFF08\u81F3\u5C11 1 \u4EBA\uFF09 |\n| `cchess.\u79FB\u52A8 <\u7740\u6CD5>` | \u843D\u5B50\uFF0C\u4EA6\u53EF\u76F4\u63A5\u53D1\u9001\u7740\u6CD5 |\n| `cchess.\u6094\u68CB.\u8BF7\u6C42` | \u8BF7\u6C42\u6094\u68CB\uFF0C\u4EBA\u673A\u6A21\u5F0F\u4E0B\u7ACB\u5373\u751F\u6548 |\n| `cchess.\u8BA4\u8F93` | \u63A8\u67B0\u8BA4\u8D1F |\n| `cchess.\u7ED3\u675F` | \u5F3A\u5236\u6E05\u76D8 |\n| `cchess.\u7F16\u8F91\u68CB\u76D8.\u5BFC\u5165 <FEN>` | \u7531\u6307\u5B9A\u5C40\u9762\u5F00\u5C40 |\n| `cchess.\u7F16\u8F91\u68CB\u76D8.\u5BFC\u51FA` | \u5BFC\u51FA\u5F53\u524D\u5C40\u9762 |\n| `cchess.\u6392\u884C\u699C.\u603B\u80DC\u573A [\u4EBA\u6570]` | \u67E5\u770B\u80DC\u573A\u699C |\n| `cchess.\u67E5\u8BE2\u73A9\u5BB6\u8BB0\u5F55 [@\u67D0\u4EBA]` | \u67E5\u770B\u68CB\u58EB\u6218\u7EE9 |\n\n## Q&A\n\n1. \u5982\u4F55\u79FB\u52A8\u68CB\u5B50\uFF1F\n\n- \u7EB5\u7EBF\uFF08\u70AE\u4E8C\u5E73\u4E94/\u70AE8\u5E735\uFF09\u6216\u5B57\u6BCD\u5750\u6807\uFF08b2e2\uFF09\u3002\n- \u8BE6\u89C1 [\u4E2D\u56FD\u8C61\u68CB\u7740\u6CD5\u8868\u793A](https://www.xqbase.com/protocol/cchess_move.htm)\u3002\n\n2. \u4EBA\u673A\u592A\u5F3A/\u592A\u5F31\uFF1F\n\n- \u8C03\u6574\u300C\u5F15\u64CE\u601D\u8003\u6DF1\u5EA6\u300D\uFF0C\u6DF1\u5EA6\u8D8A\u9AD8\u68CB\u529B\u8D8A\u5F3A\uFF0C\u8017\u65F6\u4E5F\u8D8A\u957F\u3002\n\n## QQ \u7FA4\n\n- 956758505";
3
+ export declare const usage = "## \u4F7F\u7528\n\n`cchess.\u52A0\u5165` \u5165\u5EA7\uFF0C`cchess.\u5F00\u59CB.\u4EBA\u4EBA\u5BF9\u6218` \u6216 `cchess.\u5F00\u59CB.\u4EBA\u673A\u5BF9\u6218` \u5F00\u5C40\u3002\u7740\u6CD5\u53EF\u76F4\u63A5\u53D1\u9001\uFF0C\u5982\u70AE\u4E8C\u5E73\u4E94\u6216 `b2e2`\u3002\n\n## \u6307\u4EE4\n\n| \u6307\u4EE4 | \u8BF4\u660E |\n| --- | --- |\n| `cchess.\u52A0\u5165 [\u7EA2/\u9ED1]` | \u5165\u5EA7 |\n| `cchess.\u9000\u51FA` | \u5F00\u5C40\u524D\u79BB\u5E2D |\n| `cchess.\u5F00\u59CB.\u4EBA\u4EBA\u5BF9\u6218` | \u4EBA\u4EBA\u5BF9\u6218 |\n| `cchess.\u5F00\u59CB.\u4EBA\u673A\u5BF9\u6218` | \u4EBA\u673A\u5BF9\u6218\uFF08\u76AE\u5361\u9C7C\uFF09 |\n| `cchess.\u79FB\u52A8 <\u7740\u6CD5>` | \u843D\u5B50 |\n| `cchess.\u6094\u68CB.\u8BF7\u6C42` | \u6094\u68CB |\n| `cchess.\u8BA4\u8F93` | \u8BA4\u8F93 |\n| `cchess.\u7ED3\u675F` | \u5F3A\u5236\u6E05\u76D8 |\n| `cchess.\u7F16\u8F91\u68CB\u76D8.\u5BFC\u5165 <FEN>` | \u5BFC\u5165\u5C40\u9762 |\n| `cchess.\u7F16\u8F91\u68CB\u76D8.\u5BFC\u51FA` | \u5BFC\u51FA\u5C40\u9762 |\n| `cchess.\u6392\u884C\u699C.\u603B\u80DC\u573A [\u4EBA\u6570]` | \u80DC\u573A\u699C |\n| `cchess.\u67E5\u8BE2\u73A9\u5BB6\u8BB0\u5F55 [@\u67D0\u4EBA]` | \u67E5\u770B\u6218\u7EE9 |";
4
4
  export declare const inject: string[];
5
5
  export interface Config {
6
6
  boardSkin: string;
package/lib/index.js CHANGED
@@ -28,55 +28,39 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
28
28
  var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
29
 
30
30
  // src/index.ts
31
- var src_exports = {};
32
- __export(src_exports, {
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
33
  Config: () => Config,
34
34
  apply: () => apply,
35
35
  inject: () => inject,
36
36
  name: () => name,
37
37
  usage: () => usage
38
38
  });
39
- module.exports = __toCommonJS(src_exports);
39
+ module.exports = __toCommonJS(index_exports);
40
40
  var import_koishi = require("koishi");
41
41
  var path = __toESM(require("path"));
42
42
  var fs = __toESM(require("fs"));
43
43
  var name = "cchess";
44
44
  var usage = `## 使用
45
45
 
46
- 1. 安装 \`puppeteer\` 服务。
47
- 2. 为常用指令设置别名(如「加入」「认输」「悔棋」)。
46
+ \`cchess.加入\` 入座,\`cchess.开始.人人对战\` \`cchess.开始.人机对战\` 开局。着法可直接发送,如炮二平五或 \`b2e2\`。
48
47
 
49
48
  ## 指令
50
49
 
51
50
  | 指令 | 说明 |
52
51
  | --- | --- |
53
- | \`cchess.加入 [红/黑]\` | 入座并选择阵营,省略则随机 |
52
+ | \`cchess.加入 [红/黑]\` | 入座 |
54
53
  | \`cchess.退出\` | 开局前离席 |
55
- | \`cchess.开始.人人对战\` | 与好友手谈(至少 2 人) |
56
- | \`cchess.开始.人机对战\` | 挑战皮卡鱼(至少 1 人) |
57
- | \`cchess.移动 <着法>\` | 落子,亦可直接发送着法 |
58
- | \`cchess.悔棋.请求\` | 请求悔棋,人机模式下立即生效 |
59
- | \`cchess.认输\` | 推枰认负 |
54
+ | \`cchess.开始.人人对战\` | 人人对战 |
55
+ | \`cchess.开始.人机对战\` | 人机对战(皮卡鱼) |
56
+ | \`cchess.移动 <着法>\` | 落子 |
57
+ | \`cchess.悔棋.请求\` | 悔棋 |
58
+ | \`cchess.认输\` | 认输 |
60
59
  | \`cchess.结束\` | 强制清盘 |
61
- | \`cchess.编辑棋盘.导入 <FEN>\` | 由指定局面开局 |
62
- | \`cchess.编辑棋盘.导出\` | 导出当前局面 |
63
- | \`cchess.排行榜.总胜场 [人数]\` | 查看胜场榜 |
64
- | \`cchess.查询玩家记录 [@某人]\` | 查看棋士战绩 |
65
-
66
- ## Q&A
67
-
68
- 1. 如何移动棋子?
69
-
70
- - 纵线(炮二平五/炮8平5)或字母坐标(b2e2)。
71
- - 详见 [中国象棋着法表示](https://www.xqbase.com/protocol/cchess_move.htm)。
72
-
73
- 2. 人机太强/太弱?
74
-
75
- - 调整「引擎思考深度」,深度越高棋力越强,耗时也越长。
76
-
77
- ## QQ 群
78
-
79
- - 956758505`;
60
+ | \`cchess.编辑棋盘.导入 <FEN>\` | 导入局面 |
61
+ | \`cchess.编辑棋盘.导出\` | 导出局面 |
62
+ | \`cchess.排行榜.总胜场 [人数]\` | 胜场榜 |
63
+ | \`cchess.查询玩家记录 [@某人]\` | 查看战绩 |`;
80
64
  var inject = ["database", "puppeteer", "canvas"];
81
65
  var pieceSkins = ["棋弈无限图形棋子", "云库木制棋子", "刘炳森红黑牛角隶书棋子", "木制棋子", "本纹隶书棋子", "银灰将军体棋子", "棋弈无限红绿正棋子", "棋弈无限红绿棋子", "棋天大圣棋子", "棋者象棋棋子", "楷书玉石棋子", "水墨青瓷棋子", "牛皮纸华康金文棋子", "皮卡鱼棋子", "红蓝祥隶棋子", "红黑精典无阴影棋子", "红黑精典棋子", "行书玉石棋子", "象甲棋子", "金属棋子篆体棋子", "镌刻华彩棋子", "鹏飞红黑棋子", "鹏飞经典棋子", "鹏飞绿龙棋子", "龙腾四海棋子"];
82
66
  var boardSkins = ["棋弈无限红绿棋盘", "一鸣惊人棋盘", "三星堆棋盘", "云库木制棋盘", "云心鹤眼棋盘", "兰亭集序无字棋盘", "兰亭集序棋盘", "凤舞九天棋盘", "卯兔添福棋盘", "叱咤风云棋盘", "君临天下棋盘", "壁画古梦棋盘", "大闹天宫棋盘", "天山共色棋盘", "女神之约棋盘", "小吕飞刀棋盘", "山海绘卷棋盘", "护眼绿棋盘", "星河璀璨棋盘", "木制棋盘", "本纹隶书白棋盘", "本纹隶书黑棋盘", "桃花源记棋盘", "棋天大圣棋盘", "棋天无大圣棋盘", "武侠江湖棋盘", "水墨青瓷棋盘", "清悠茶道棋盘", "游园惊梦棋盘", "牛转乾坤棋盘", "玉石太极棋盘", "皓月无痕棋盘", "皮卡鱼棋盘", "盲棋神迹棋盘", "空城计棋盘", "竹波烟雨棋盘", "经典木棋盘棋盘", "绿色棋盘", "象甲2023棋盘", "象甲征程棋盘", "象甲重燃棋盘", "金牛降世棋盘", "金虎贺岁棋盘", "金虎贺岁(王天一签名版)棋盘", "鎏金岁月棋盘", "霸王别姬棋盘", "鸿门宴棋盘", "鹏飞红黑棋盘", "鹏飞经典棋盘", "鹏飞绿龙棋盘", "龙腾天坛棋盘"];
@@ -109,10 +93,10 @@ __name(isLine, "isLine");
109
93
  function panel(options) {
110
94
  const { icon = "♟️", title, at, body = [], tips = [], image } = options;
111
95
  const lines = [`${icon} ${title}`, RULE];
112
- if (at) lines.push(`👤 @${at}`);
96
+ if (at) lines.push(`@${at}`);
113
97
  lines.push(...body.filter(isLine));
114
98
  const validTips = tips.filter(isLine).filter(Boolean);
115
- if (validTips.length) lines.push(RULE, ...validTips.map((tip) => `💡 ${tip}`));
99
+ if (validTips.length) lines.push(RULE, ...validTips);
116
100
  if (image) lines.push(image);
117
101
  return lines.join("\n");
118
102
  }
@@ -242,21 +226,21 @@ function apply(ctx, config) {
242
226
  const gameRecord = await getGameRecord(channelId);
243
227
  if (gameRecord.isStarted) {
244
228
  return await sendMessage(session, panel({
245
- icon: "⚔️",
229
+ icon: "⚠️",
246
230
  title: "棋局已经开战",
247
231
  at: username,
248
- body: ["本局对弈正在进行,无法中途入场。"],
249
- tips: ["等本局落幕,下一盘再并肩落子吧"],
232
+ body: ["本局正在进行,无法中途加入。"],
233
+ tips: ["本局结束后可再加入。"],
250
234
  image: await renderBoard(channelId)
251
235
  }));
252
236
  }
253
237
  void checkEngine(channelId);
254
238
  if (choice?.includes("红") && choice.includes("黑")) {
255
239
  return await sendMessage(session, panel({
256
- icon: "🤔",
240
+ icon: "⚠️",
257
241
  title: "一次只能选一方",
258
242
  at: username,
259
- body: ["红黑不可兼得,请择一而战。"],
243
+ body: ["请只选择红方或黑方。"],
260
244
  tips: ["「cchess.加入 红」执红先行", "「cchess.加入 黑」执黑应招"]
261
245
  }));
262
246
  }
@@ -269,7 +253,7 @@ function apply(ctx, config) {
269
253
  if (selfRecords.length === 0) {
270
254
  await ctx.database.create("cchess_gaming_player_records", { channelId, userId, username, side: choice });
271
255
  return await sendMessage(session, panel({
272
- icon: "🎏",
256
+ icon: "",
273
257
  title: "入座成功",
274
258
  at: username,
275
259
  body: [field("阵营", withSideIcon(choice)), field("当前人数", `${playersNum + 1} 人`)],
@@ -278,7 +262,7 @@ function apply(ctx, config) {
278
262
  } else {
279
263
  await ctx.database.set("cchess_gaming_player_records", { channelId, userId }, { side: choice });
280
264
  return await sendMessage(session, panel({
281
- icon: "🔄",
265
+ icon: "",
282
266
  title: "换边成功",
283
267
  at: username,
284
268
  body: [field("新的阵营", withSideIcon(choice)), field("当前人数", `${playersNum} 人`)]
@@ -291,11 +275,11 @@ function apply(ctx, config) {
291
275
  const gameRecord = await getGameRecord(channelId);
292
276
  if (gameRecord.isStarted) {
293
277
  return await sendMessage(session, panel({
294
- icon: "🚩",
278
+ icon: "⚠️",
295
279
  title: "棋局已经开战",
296
280
  at: username,
297
- body: ["战鼓已响,临阵脱逃可不是棋士风范。"],
298
- tips: ["实在下不动了,可发送「cchess.认输」体面收官"]
281
+ body: ["对局进行中,无法退出。"],
282
+ tips: ["可发送「cchess.认输」结束本局"]
299
283
  }));
300
284
  }
301
285
  const selfRecords = await ctx.database.get("cchess_gaming_player_records", { channelId, userId });
@@ -306,10 +290,10 @@ function apply(ctx, config) {
306
290
  } else {
307
291
  await ctx.database.remove("cchess_gaming_player_records", { channelId, userId });
308
292
  return await sendMessage(session, panel({
309
- icon: "👋",
293
+ icon: "",
310
294
  title: "已离席",
311
295
  at: username,
312
- body: ["期待下次与您对坐手谈。", field("剩余人数", `${playersNum - 1} 人`)]
296
+ body: ["已退出本局。", field("剩余人数", `${playersNum - 1} 人`)]
313
297
  }));
314
298
  }
315
299
  });
@@ -331,7 +315,7 @@ function apply(ctx, config) {
331
315
  let assignNotice = "";
332
316
  if (playersNum < 2) {
333
317
  return await sendMessage(session, panel({
334
- icon: "🪑",
318
+ icon: "⚠️",
335
319
  title: "棋差一手,人差一位",
336
320
  at: username,
337
321
  body: ["人人对战至少需要 2 位棋手。", field("当前人数", `${playersNum} 人`)],
@@ -352,7 +336,7 @@ function apply(ctx, config) {
352
336
  await ctx.database.set("cchess_game_records", { channelId }, { isStarted: true });
353
337
  const sideString = convertTurnToString(gameRecord.turn);
354
338
  return await sendMessage(session, panel({
355
- icon: "🎉",
339
+ icon: "",
356
340
  title: "楚河汉界,对局开始",
357
341
  body: [
358
342
  ...assignNotice ? [assignNotice, ""] : [],
@@ -381,7 +365,7 @@ function apply(ctx, config) {
381
365
  const blackPlayers = playerRecords.filter((player) => player.side === "黑方");
382
366
  if (playersNum < 1 && !config.allowFreePieceMovementInHumanMachineMode) {
383
367
  return await sendMessage(session, panel({
384
- icon: "🪑",
368
+ icon: "⚠️",
385
369
  title: "尚无棋手入座",
386
370
  at: username,
387
371
  body: ["人机对战至少需要 1 位棋手。"],
@@ -412,7 +396,7 @@ function apply(ctx, config) {
412
396
  const sideString = convertTurnToString(gameRecord.turn);
413
397
  if (engineSide === sideString) await requestEngineMove(channelId);
414
398
  return await sendMessage(session, panel({
415
- icon: "🎉",
399
+ icon: "",
416
400
  title: "人机对局开始",
417
401
  body: [
418
402
  field("棋 手", withSideIcon(humanSide)),
@@ -430,7 +414,7 @@ function apply(ctx, config) {
430
414
  const gameRecord = await getGameRecord(channelId);
431
415
  if (!gameRecord.isStarted) {
432
416
  return await sendMessage(session, panel({
433
- icon: "🌱",
417
+ icon: "⚠️",
434
418
  title: "棋盘尚且空空",
435
419
  at: username,
436
420
  body: ["当前没有进行中的对局,无需收拾残局。"],
@@ -439,7 +423,7 @@ function apply(ctx, config) {
439
423
  }
440
424
  await endGame(channelId);
441
425
  return await sendMessage(session, panel({
442
- icon: "🧹",
426
+ icon: "",
443
427
  title: "对局已强制结束",
444
428
  at: username,
445
429
  body: ["棋盘已收,胜负不计。"],
@@ -596,7 +580,7 @@ function apply(ctx, config) {
596
580
  }
597
581
  if (gameRecord.moveList.length < 1) {
598
582
  return await sendMessage(session, panel({
599
- icon: "🀄",
583
+ icon: "⚠️",
600
584
  title: "棋局初开",
601
585
  at: username,
602
586
  body: ["一子未落,无悔可言。"]
@@ -607,7 +591,7 @@ function apply(ctx, config) {
607
591
  await undoMove(channelId);
608
592
  await undoMove(channelId);
609
593
  return await sendMessage(session, panel({
610
- icon: "↩️",
594
+ icon: "",
611
595
  title: "悔棋成功",
612
596
  at: username,
613
597
  body: ["棋子已归原位,皮卡鱼宽宏大量。"],
@@ -616,16 +600,16 @@ function apply(ctx, config) {
616
600
  }
617
601
  if (playerRecord[0].side === sideString) {
618
602
  return await sendMessage(session, panel({
619
- icon: "🤨",
603
+ icon: "⚠️",
620
604
  title: "此刻不可悔棋",
621
605
  at: username,
622
- body: ["轮到您落子,上一着是对方所走。"],
606
+ body: ["轮到你落子,上一着是对方所走。"],
623
607
  tips: ["落子之后,方有悔棋之说"]
624
608
  }));
625
609
  }
626
610
  await ctx.database.set("cchess_game_records", { channelId }, { isRegretRequest: true });
627
611
  return await sendMessage(session, panel({
628
- icon: "🙏",
612
+ icon: "",
629
613
  title: "悔棋请求已送出",
630
614
  at: username,
631
615
  body: [field("等待答复", withSideIcon(sideString))],
@@ -639,11 +623,11 @@ function apply(ctx, config) {
639
623
  await undoMove(channelId);
640
624
  await ctx.database.set("cchess_game_records", { channelId }, { isRegretRequest: false });
641
625
  return await sendMessage(session, panel({
642
- icon: "🤝",
626
+ icon: "",
643
627
  title: "悔棋成功",
644
628
  at: username,
645
- body: ["您应允了对方的请求,棋子已归原位。"],
646
- tips: ["棋风谦和,可敬可佩"],
629
+ body: ["已同意悔棋,棋子已归原位。"],
630
+ tips: ["对局继续。"],
647
631
  image: await renderBoard(channelId)
648
632
  }));
649
633
  });
@@ -653,7 +637,7 @@ function apply(ctx, config) {
653
637
  if (typeof decision === "string") return await sendMessage(session, decision);
654
638
  await ctx.database.set("cchess_game_records", { channelId }, { isRegretRequest: false });
655
639
  return await sendMessage(session, panel({
656
- icon: "",
640
+ icon: "⚠️",
657
641
  title: "悔棋被拒",
658
642
  at: username,
659
643
  body: ["落子无悔,棋局继续。"],
@@ -678,15 +662,15 @@ function apply(ctx, config) {
678
662
  const sideString = convertTurnToString(turn);
679
663
  if (playerRecord[0].side !== sideString) {
680
664
  return await sendMessage(session, panel({
681
- icon: "🕊️",
665
+ icon: "⚠️",
682
666
  title: "此刻不便认输",
683
667
  at: username,
684
- body: [field("您的阵营", withSideIcon(playerRecord[0].side)), field("当前轮走", withSideIcon(sideString))],
685
- tips: ["轮到您走棋时,方可推枰认负"]
668
+ body: [field("你的阵营", withSideIcon(playerRecord[0].side)), field("当前轮走", withSideIcon(sideString))],
669
+ tips: ["轮到你走棋时,才可以认输"]
686
670
  }));
687
671
  }
688
672
  const message = await settleVictory(channelId, turn === "w" ? "b" : "w", turn, "拱手认负", {
689
- icon: "🕊️",
673
+ icon: "",
690
674
  at: username,
691
675
  extra: [`${withSideIcon(sideString)} 推枰认负,风度翩翩。`]
692
676
  });
@@ -699,7 +683,7 @@ function apply(ctx, config) {
699
683
  const { username, userId } = session;
700
684
  await updateNameInPlayerRecord(userId, username);
701
685
  return await sendMessage(session, panel({
702
- icon: "☁️",
686
+ icon: "📋",
703
687
  title: "云库残局 · DTM 统计",
704
688
  at: username,
705
689
  body: ["DTM:距将死的步数统计。", "https://www.chessdb.cn/egtb_info_dtm.html"]
@@ -709,7 +693,7 @@ function apply(ctx, config) {
709
693
  const { username, userId } = session;
710
694
  await updateNameInPlayerRecord(userId, username);
711
695
  return await sendMessage(session, panel({
712
- icon: "☁️",
696
+ icon: "📋",
713
697
  title: "云库残局 · DTC 统计",
714
698
  at: username,
715
699
  body: ["DTC:距吃子的步数统计。", "https://www.chessdb.cn/egtb_info.html"]
@@ -724,7 +708,7 @@ function apply(ctx, config) {
724
708
  const gameRecord = await getGameRecord(channelId);
725
709
  if (gameRecord.isStarted) {
726
710
  return await sendMessage(session, panel({
727
- icon: "🔒",
711
+ icon: "⚠️",
728
712
  title: "棋局进行中",
729
713
  at: username,
730
714
  body: ["对弈期间不可改动棋盘。"],
@@ -734,7 +718,7 @@ function apply(ctx, config) {
734
718
  fen = fen?.trim();
735
719
  if (!fen || !isValidateFen(fen)) {
736
720
  return await sendMessage(session, panel({
737
- icon: "📜",
721
+ icon: "",
738
722
  title: "FEN 串无法解析",
739
723
  at: username,
740
724
  body: ["请检查局面串的格式是否完整。"],
@@ -760,7 +744,7 @@ function apply(ctx, config) {
760
744
  await parseFen(channelId, fen);
761
745
  const record = await getGameRecord(channelId);
762
746
  return await sendMessage(session, panel({
763
- icon: "🎨",
747
+ icon: "",
764
748
  title: "棋盘摆放完毕",
765
749
  at: username,
766
750
  body: [field("轮走方", withSideIcon(convertTurnToString(record.turn)))],
@@ -780,7 +764,7 @@ function apply(ctx, config) {
780
764
  }
781
765
  const fenWithFullMove = await getFenWithFullMove(channelId);
782
766
  return await sendMessage(session, panel({
783
- icon: "📜",
767
+ icon: "📋",
784
768
  title: "当前局面 FEN",
785
769
  at: username,
786
770
  body: [fenWithFullMove],
@@ -791,7 +775,7 @@ function apply(ctx, config) {
791
775
  const { username, userId } = session;
792
776
  await updateNameInPlayerRecord(userId, username);
793
777
  return await sendMessage(session, panel({
794
- icon: "📖",
778
+ icon: "📋",
795
779
  title: "FEN 串使用方法",
796
780
  at: username,
797
781
  body: ["中国象棋 FEN 格式规范:", "https://www.xqbase.com/protocol/cchess_fen.htm"]
@@ -860,7 +844,7 @@ function apply(ctx, config) {
860
844
  });
861
845
  function notStartedPanel(username) {
862
846
  return panel({
863
- icon: "🌱",
847
+ icon: "⚠️",
864
848
  title: "棋局尚未开始",
865
849
  at: username,
866
850
  body: ["当前频道还没有进行中的对局。"],
@@ -870,27 +854,27 @@ function apply(ctx, config) {
870
854
  __name(notStartedPanel, "notStartedPanel");
871
855
  function alreadyStartedPanel(username) {
872
856
  return panel({
873
- icon: "⚔️",
857
+ icon: "⚠️",
874
858
  title: "棋局已经开战",
875
859
  at: username,
876
- body: ["本局尚未分出胜负,不必重开。"],
877
- tips: ["「cchess.认输」体面收官", "「cchess.结束」强制清盘"]
860
+ body: ["本局尚未结束,不必重开。"],
861
+ tips: ["「cchess.认输」结束本局", "「cchess.结束」强制清盘"]
878
862
  });
879
863
  }
880
864
  __name(alreadyStartedPanel, "alreadyStartedPanel");
881
865
  function notJoinedPanel(username) {
882
866
  return panel({
883
- icon: "🪑",
884
- title: "您尚未入座",
867
+ icon: "⚠️",
868
+ title: "你尚未入座",
885
869
  at: username,
886
- body: ["先落座,才好落子。"],
870
+ body: ["请先加入对局。"],
887
871
  tips: ["发送「cchess.加入」加入对局"]
888
872
  });
889
873
  }
890
874
  __name(notJoinedPanel, "notJoinedPanel");
891
875
  function analyzingPanel(username) {
892
876
  return panel({
893
- icon: "🧠",
877
+ icon: "",
894
878
  title: "皮卡鱼正在推演",
895
879
  at: username,
896
880
  body: ["引擎正在计算局面,请稍候再试。"]
@@ -899,17 +883,17 @@ function apply(ctx, config) {
899
883
  __name(analyzingPanel, "analyzingPanel");
900
884
  function notYourTurnPanel(username, yourSide, currentSide) {
901
885
  return panel({
902
- icon: "",
903
- title: "尚未轮到您",
886
+ icon: "⚠️",
887
+ title: "尚未轮到你",
904
888
  at: username,
905
- body: [field("您的阵营", withSideIcon(yourSide)), field("当前轮走", withSideIcon(currentSide))],
906
- tips: ["稍安勿躁,静候对方落子"]
889
+ body: [field("你的阵营", withSideIcon(yourSide)), field("当前轮走", withSideIcon(currentSide))],
890
+ tips: ["请等待对方落子。"]
907
891
  });
908
892
  }
909
893
  __name(notYourTurnPanel, "notYourTurnPanel");
910
894
  function invalidMovePanel(username, reason) {
911
895
  return panel({
912
- icon: "🚫",
896
+ icon: "",
913
897
  title: reason,
914
898
  at: username,
915
899
  body: ["请确认着法是否合乎棋规。"],
@@ -919,7 +903,7 @@ function apply(ctx, config) {
919
903
  __name(invalidMovePanel, "invalidMovePanel");
920
904
  function engineUnavailablePanel(username) {
921
905
  return panel({
922
- icon: "🐟",
906
+ icon: "",
923
907
  title: "皮卡鱼尚未就绪",
924
908
  at: username,
925
909
  body: ["引擎启动失败,暂时无法进行人机对战。"],
@@ -929,10 +913,10 @@ function apply(ctx, config) {
929
913
  __name(engineUnavailablePanel, "engineUnavailablePanel");
930
914
  function invalidLeaderboardSizePanel(username) {
931
915
  return panel({
932
- icon: "🔢",
916
+ icon: "⚠️",
933
917
  title: "榜单人数有误",
934
918
  at: username,
935
- body: ["请输入大于等于 0 的整数。"],
919
+ body: ["请输入不小于 0 的整数。"],
936
920
  tips: ["例如:cchess.排行榜.总胜场 10"]
937
921
  });
938
922
  }
@@ -942,7 +926,7 @@ function apply(ctx, config) {
942
926
  }
943
927
  __name(renderBoard, "renderBoard");
944
928
  async function settleVictory(channelId, winSide, loseSide, title, options = {}) {
945
- const { icon = "🏆", at, extra = [], withBoard = false } = options;
929
+ const { icon = "", at, extra = [], withBoard = false } = options;
946
930
  const image = withBoard ? await renderBoard(channelId) : void 0;
947
931
  const winners = await ctx.database.get("cchess_gaming_player_records", {
948
932
  channelId,
@@ -972,7 +956,7 @@ function apply(ctx, config) {
972
956
  if (gameRecord.isAnalyzing || busyChannels.has(channelId)) return analyzingPanel(username);
973
957
  if (!gameRecord.isRegretRequest) {
974
958
  return panel({
975
- icon: "🍃",
959
+ icon: "⚠️",
976
960
  title: "暂无悔棋请求",
977
961
  at: username,
978
962
  body: [`当前没有待答复的请求,无从${action}。`]
@@ -983,7 +967,7 @@ function apply(ctx, config) {
983
967
  const sideString = convertTurnToString(gameRecord.turn);
984
968
  if (playerRecord[0].side !== sideString) {
985
969
  return panel({
986
- icon: "🙅",
970
+ icon: "⚠️",
987
971
  title: "无权表决",
988
972
  at: username,
989
973
  body: ["悔棋请求正由对方等待答复。", field("应答方", withSideIcon(sideString))]
@@ -1037,8 +1021,7 @@ function apply(ctx, config) {
1037
1021
  }
1038
1022
  __name(addPlayerCount, "addPlayerCount");
1039
1023
  async function getLeaderboard(session, sortField, title, icon, number) {
1040
- const players = await ctx.database.get("cchess_player_records", {});
1041
- const topPlayers = players.filter((player) => player[sortField] > 0).sort((a, b) => b[sortField] - a[sortField]).slice(0, number);
1024
+ const topPlayers = await ctx.database.select("cchess_player_records").where({ [sortField]: { $gt: 0 } }).orderBy(sortField, "desc").limit(number).execute();
1042
1025
  return await sendMessage(session, panel({
1043
1026
  icon,
1044
1027
  title,
@@ -1160,7 +1143,7 @@ function apply(ctx, config) {
1160
1143
  }
1161
1144
  __name(parseFen, "parseFen");
1162
1145
  function deepCopy(obj) {
1163
- return JSON.parse(JSON.stringify(obj));
1146
+ return structuredClone(obj);
1164
1147
  }
1165
1148
  __name(deepCopy, "deepCopy");
1166
1149
  async function gotoHistory(channelId, targetMove) {
@@ -1190,9 +1173,9 @@ function apply(ctx, config) {
1190
1173
  isHistoryMode = true;
1191
1174
  }
1192
1175
  let moveId = targetMove.moveId;
1193
- let idx = originalState.moveList.findIndex((m) => m.moveId == moveId);
1176
+ let idx = originalState.moveList.findIndex(((m) => m.moveId == moveId));
1194
1177
  moveList = originalState.moveList.slice(0, idx + 1);
1195
- idx = originalState.movesAfterLastEat.findIndex((m) => m.moveId == moveId);
1178
+ idx = originalState.movesAfterLastEat.findIndex(((m) => m.moveId == moveId));
1196
1179
  if (idx != -1) {
1197
1180
  movesAfterLastEat = originalState.movesAfterLastEat.slice(0, idx + 1);
1198
1181
  } else {
package/package.json CHANGED
@@ -1,13 +1,17 @@
1
1
  {
2
2
  "name": "koishi-plugin-cchess",
3
3
  "description": "Koishi 的中国象棋插件。支持人人/人机对战。",
4
- "version": "1.0.6",
4
+ "version": "1.0.8",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",
7
7
  "files": [
8
8
  "lib",
9
- "dist"
9
+ "LICENSE-APACHE",
10
+ "LICENSE-MIT"
10
11
  ],
12
+ "scripts": {
13
+ "yakumo:after:esbuild": "node scripts/copy-assets.mjs"
14
+ },
11
15
  "koishi": {
12
16
  "service": {
13
17
  "required": [
@@ -42,7 +46,7 @@
42
46
  "对战"
43
47
  ],
44
48
  "peerDependencies": {
45
- "koishi": "^4.18.9"
49
+ "koishi": "^4.18.11"
46
50
  },
47
51
  "devDependencies": {
48
52
  "@koishijs/canvas": "^0.2.0",
package/readme.md CHANGED
@@ -1,55 +1,35 @@
1
1
  koishi-plugin-cchess
2
2
  ====================
3
3
 
4
- [<img alt="github" src="https://img.shields.io/badge/github-araea/cchess-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/araea/koishi-plugin-cchess)
4
+ [<img alt="github" src="https://img.shields.io/badge/github-araea/koishi__plugin__cchess-8da0cb?style=for-the-badge&labelColor=555555&logo=github" height="20">](https://github.com/araea/koishi-plugin-cchess)
5
5
  [<img alt="npm" src="https://img.shields.io/npm/v/koishi-plugin-cchess.svg?style=for-the-badge&color=fc8d62&logo=npm" height="20">](https://www.npmjs.com/package/koishi-plugin-cchess)
6
6
 
7
- Koishi 的中国象棋插件。支持人人/人机对战。
7
+ Koishi 的中国象棋插件。
8
8
 
9
9
  ## 使用
10
10
 
11
- 1. 安装 `puppeteer` 服务。
12
- 2. 为常用指令设置别名(如「加入」「认输」「悔棋」)。
11
+ `cchess.加入` 入座,`cchess.开始.人人对战` `cchess.开始.人机对战` 开局。着法可直接发送,如炮二平五或 `b2e2`。
13
12
 
14
13
  ## 指令
15
14
 
16
15
  | 指令 | 说明 |
17
16
  | --- | --- |
18
- | `cchess.加入 [红/黑]` | 入座并选择阵营,省略则随机 |
17
+ | `cchess.加入 [红/黑]` | 入座 |
19
18
  | `cchess.退出` | 开局前离席 |
20
- | `cchess.开始.人人对战` | 与好友手谈(至少 2 人) |
21
- | `cchess.开始.人机对战` | 挑战皮卡鱼(至少 1 人) |
22
- | `cchess.移动 <着法>` | 落子,亦可直接发送着法 |
23
- | `cchess.悔棋.请求` | 请求悔棋,人机模式下立即生效 |
24
- | `cchess.认输` | 推枰认负 |
19
+ | `cchess.开始.人人对战` | 人人对战 |
20
+ | `cchess.开始.人机对战` | 人机对战(皮卡鱼) |
21
+ | `cchess.移动 <着法>` | 落子 |
22
+ | `cchess.悔棋.请求` | 悔棋 |
23
+ | `cchess.认输` | 认输 |
25
24
  | `cchess.结束` | 强制清盘 |
26
- | `cchess.编辑棋盘.导入 <FEN>` | 由指定局面开局 |
27
- | `cchess.编辑棋盘.导出` | 导出当前局面 |
28
- | `cchess.排行榜.总胜场 [人数]` | 查看胜场榜 |
29
- | `cchess.查询玩家记录 [@某人]` | 查看棋士战绩 |
30
-
31
- ## Q&A
32
-
33
- 1. 如何移动棋子?
34
-
35
- - 纵线(炮二平五/炮8平5)或字母坐标(b2e2)。
36
- - 详见 [中国象棋着法表示](https://www.xqbase.com/protocol/cchess_move.htm)。
37
-
38
- 2. 人机太强 / 太弱?
39
-
40
- - 调整「引擎思考深度」,深度越高棋力越强,耗时也越长。
41
-
42
- ## 致谢
43
-
44
- - [Koishi](https://koishi.chat/) - 机器人框架
45
- - [风满楼]() - 棋盘与棋子素材
46
- - [皮卡鱼](https://xiangqiai.com/#/) - 中国象棋 AI 引擎
47
- - [中国象棋云库](https://www.chessdb.cn/query/) - 中国象棋棋谱
48
- - [中国象棋应用规范](https://www.xqbase.com/protocol/cchess_intro.htm) - 中国象棋标准规范
25
+ | `cchess.编辑棋盘.导入 <FEN>` | 导入局面 |
26
+ | `cchess.编辑棋盘.导出` | 导出局面 |
27
+ | `cchess.排行榜.总胜场 [人数]` | 胜场榜 |
28
+ | `cchess.查询玩家记录 [@某人]` | 查看战绩 |
49
29
 
50
30
  ## QQ 群
51
31
 
52
- - 956758505
32
+ 956758505
53
33
 
54
34
  <br>
55
35