@hieuzest/koishi-plugin-mahjongpub 0.1.4 → 0.1.6
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/lib/api.d.ts +28 -4
- package/lib/api.js +43 -5
- package/lib/index.d.ts +10 -1
- package/lib/index.js +118 -15
- package/lib/locales/zh-CN.json +1 -1
- package/package.json +1 -1
package/lib/api.d.ts
CHANGED
|
@@ -1,7 +1,17 @@
|
|
|
1
|
-
import { Context, Quester } from 'koishi';
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import { Context, Dict, Quester } from 'koishi';
|
|
2
|
+
export interface Team {
|
|
3
|
+
pw?: string;
|
|
4
|
+
tid: string;
|
|
5
|
+
cid?: string;
|
|
6
|
+
name: string;
|
|
7
|
+
players: string[];
|
|
8
|
+
qq?: string[];
|
|
9
|
+
}
|
|
10
|
+
export declare class TeamAdmin implements Team {
|
|
11
|
+
pw: string;
|
|
12
|
+
options: {
|
|
13
|
+
endpoint: string;
|
|
14
|
+
};
|
|
5
15
|
tid: string;
|
|
6
16
|
cid: string;
|
|
7
17
|
name: string;
|
|
@@ -16,3 +26,17 @@ export declare class Team {
|
|
|
16
26
|
read(): Promise<void>;
|
|
17
27
|
write(): Promise<any>;
|
|
18
28
|
}
|
|
29
|
+
export declare class ContestAdmin {
|
|
30
|
+
cid: string;
|
|
31
|
+
pw: string;
|
|
32
|
+
options: {
|
|
33
|
+
endpoint: string;
|
|
34
|
+
};
|
|
35
|
+
name: string;
|
|
36
|
+
teams: Dict<Team>;
|
|
37
|
+
http: Quester;
|
|
38
|
+
constructor(ctx: Context, cid: string, pw: string, options: {
|
|
39
|
+
endpoint: string;
|
|
40
|
+
});
|
|
41
|
+
read(): Promise<void>;
|
|
42
|
+
}
|
package/lib/api.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.
|
|
4
|
-
class
|
|
3
|
+
exports.ContestAdmin = exports.TeamAdmin = void 0;
|
|
4
|
+
class TeamAdmin {
|
|
5
5
|
pw;
|
|
6
6
|
options;
|
|
7
7
|
tid;
|
|
@@ -52,9 +52,47 @@ class Team {
|
|
|
52
52
|
return (await this.http.post('/team_post.php', form, { responseType: 'json' }))?.msg;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
|
-
exports.
|
|
56
|
-
|
|
57
|
-
|
|
55
|
+
exports.TeamAdmin = TeamAdmin;
|
|
56
|
+
class ContestAdmin {
|
|
57
|
+
cid;
|
|
58
|
+
pw;
|
|
59
|
+
options;
|
|
60
|
+
name;
|
|
61
|
+
teams;
|
|
62
|
+
http;
|
|
63
|
+
constructor(ctx, cid, pw, options) {
|
|
64
|
+
this.cid = cid;
|
|
65
|
+
this.pw = pw;
|
|
66
|
+
this.options = options;
|
|
67
|
+
this.http = ctx.http.extend({
|
|
68
|
+
endpoint: options.endpoint,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
async read() {
|
|
72
|
+
const payload = {
|
|
73
|
+
posttype: 'login',
|
|
74
|
+
cid: this.cid,
|
|
75
|
+
c_pw: this.pw,
|
|
76
|
+
};
|
|
77
|
+
const form = new FormData();
|
|
78
|
+
Object.entries(payload).forEach(([key, value]) => form.append(key, value));
|
|
79
|
+
const data = await this.http.post('/api/admin_post.php', form, {
|
|
80
|
+
responseType: 'json',
|
|
81
|
+
});
|
|
82
|
+
if (!data?.c_admin?.cid)
|
|
83
|
+
throw new Error('failed to read contest info');
|
|
84
|
+
this.name = data.c_admin.c_name;
|
|
85
|
+
this.teams = Object.fromEntries(data.c_team.map(team => [team.tid, {
|
|
86
|
+
tid: team.tid,
|
|
87
|
+
cid: this.cid,
|
|
88
|
+
name: team.t_name,
|
|
89
|
+
qq: team.qq.split(/\s+/),
|
|
90
|
+
players: [...(team.t_player?.split(/\s+/) ?? []), ...(team.t_sub?.split(/\s+/) ?? [])],
|
|
91
|
+
pw: team.t_pw,
|
|
92
|
+
}]));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
exports.ContestAdmin = ContestAdmin;
|
|
58
96
|
// namespace Contest {
|
|
59
97
|
// interface Payload {
|
|
60
98
|
// CS_EBD?: string
|
package/lib/index.d.ts
CHANGED
|
@@ -1,18 +1,27 @@
|
|
|
1
|
-
import { Context, Schema } from 'koishi';
|
|
1
|
+
import { Context, Dict, Schema } from 'koishi';
|
|
2
|
+
import { ContestAdmin, TeamAdmin } from './api';
|
|
2
3
|
declare module 'koishi' {
|
|
3
4
|
interface User {
|
|
4
5
|
'mahjongpub/bind-team': string;
|
|
6
|
+
'mahjongpub/bind-teams': Dict<string>;
|
|
5
7
|
}
|
|
6
8
|
interface Channel {
|
|
7
9
|
'mahjongpub/bind-team': string;
|
|
10
|
+
'mahjongpub/bind-contest': string;
|
|
11
|
+
'mahjongpub/bind-contestpw': string;
|
|
8
12
|
}
|
|
9
13
|
}
|
|
10
14
|
export declare class MahjongPub {
|
|
15
|
+
private ctx;
|
|
16
|
+
private config;
|
|
11
17
|
constructor(ctx: Context, config: MahjongPub.Config);
|
|
18
|
+
getTeam(pw: string): TeamAdmin;
|
|
19
|
+
getContest(cid: string, pw: string): ContestAdmin;
|
|
12
20
|
}
|
|
13
21
|
export declare namespace MahjongPub {
|
|
14
22
|
const inject: {
|
|
15
23
|
required: string[];
|
|
24
|
+
optional: string[];
|
|
16
25
|
};
|
|
17
26
|
interface Config {
|
|
18
27
|
informNotbind: boolean;
|
package/lib/index.js
CHANGED
|
@@ -10,13 +10,20 @@ function parsePlatform(target) {
|
|
|
10
10
|
return [platform, id];
|
|
11
11
|
}
|
|
12
12
|
class MahjongPub {
|
|
13
|
+
ctx;
|
|
14
|
+
config;
|
|
13
15
|
constructor(ctx, config) {
|
|
16
|
+
this.ctx = ctx;
|
|
17
|
+
this.config = config;
|
|
14
18
|
ctx.i18n.define('zh-CN', require('./locales/zh-CN'));
|
|
15
19
|
ctx.model.extend('user', {
|
|
16
20
|
'mahjongpub/bind-team': 'string',
|
|
21
|
+
'mahjongpub/bind-teams': 'json',
|
|
17
22
|
});
|
|
18
23
|
ctx.model.extend('channel', {
|
|
19
24
|
'mahjongpub/bind-team': 'string',
|
|
25
|
+
'mahjongpub/bind-contest': 'string',
|
|
26
|
+
'mahjongpub/bind-contestpw': 'string',
|
|
20
27
|
});
|
|
21
28
|
ctx.command('mahjongpub.team.bind <pw:string>')
|
|
22
29
|
.alias('!绑定', '!绑定')
|
|
@@ -27,7 +34,7 @@ class MahjongPub {
|
|
|
27
34
|
.action(async ({ session, options }, pw) => {
|
|
28
35
|
if (pw?.length !== 20)
|
|
29
36
|
return;
|
|
30
|
-
const team = new api_1.
|
|
37
|
+
const team = new api_1.TeamAdmin(ctx, pw, config);
|
|
31
38
|
try {
|
|
32
39
|
await team.read();
|
|
33
40
|
}
|
|
@@ -52,7 +59,7 @@ class MahjongPub {
|
|
|
52
59
|
.channelFields(['mahjongpub/bind-team'])
|
|
53
60
|
.action(async ({ session, options }) => {
|
|
54
61
|
const pw = options.channel ? (await session.getChannel(parsePlatform(options.channel)[1], ['mahjongpub/bind-team']))['mahjongpub/bind-team']
|
|
55
|
-
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'];
|
|
62
|
+
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'] ?? session.user['mahjongpub/bind-team'];
|
|
56
63
|
if (!pw)
|
|
57
64
|
return config.informNotbind ? session.text('.notbind') : '';
|
|
58
65
|
if (session.isDirect)
|
|
@@ -70,20 +77,79 @@ class MahjongPub {
|
|
|
70
77
|
return config.informNotbind ? session.text('.notbind') : '';
|
|
71
78
|
return session.text('.output', [pw]);
|
|
72
79
|
});
|
|
73
|
-
ctx.command('mahjongpub.team.stats')
|
|
80
|
+
ctx.command('mahjongpub.team.stats [user:user]')
|
|
74
81
|
.alias('!信息', '!信息', '!概况', '!概况')
|
|
75
82
|
.option('channel', '-c <channel:channel>')
|
|
83
|
+
.option('team', '-t <team:string>')
|
|
84
|
+
.userFields(['mahjongpub/bind-team', 'mahjongpub/bind-teams'])
|
|
85
|
+
.channelFields(['mahjongpub/bind-team', 'mahjongpub/bind-contest', 'mahjongpub/bind-contestpw'])
|
|
86
|
+
.action(async ({ session, options }, user) => {
|
|
87
|
+
let pw = options.channel ? (await session.getChannel(parsePlatform(options.channel)[1], ['mahjongpub/bind-team']))['mahjongpub/bind-team']
|
|
88
|
+
: session.isDirect ? session.user['mahjongpub/bind-team']
|
|
89
|
+
: session.channel['mahjongpub/bind-contest']
|
|
90
|
+
? (user ? session.user['mahjongpub/bind-teams'][session.channel['mahjongpub/bind-contest']] : session.user['mahjongpub/bind-team'])
|
|
91
|
+
: session.channel['mahjongpub/bind-team'] ?? session.user['mahjongpub/bind-team'];
|
|
92
|
+
if (options.team) {
|
|
93
|
+
const [cid, cpw] = [session.channel['mahjongpub/bind-contest'], session.channel['mahjongpub/bind-contestpw']];
|
|
94
|
+
if (!cid || !cpw)
|
|
95
|
+
return session.text('.notbind');
|
|
96
|
+
const contest = new api_1.ContestAdmin(ctx, cid, cpw, config);
|
|
97
|
+
await contest.read();
|
|
98
|
+
pw = (contest.teams[options.team] ?? Object.values(contest.teams).find(t => t.name === options.team))?.pw;
|
|
99
|
+
}
|
|
100
|
+
if (!pw)
|
|
101
|
+
return config.informNotbind ? session.text('.notbind') : '';
|
|
102
|
+
const team = new api_1.TeamAdmin(ctx, pw, config);
|
|
103
|
+
try {
|
|
104
|
+
await team.read();
|
|
105
|
+
return session.text('.output', team);
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
ctx.logger.warn(e);
|
|
109
|
+
return session.text('.failed');
|
|
110
|
+
}
|
|
111
|
+
});
|
|
112
|
+
ctx.command('mahjongpub.team.desc [desc:rawtext]')
|
|
113
|
+
.alias('!简介', '!简介')
|
|
114
|
+
.option('channel', '-c <channel:channel>')
|
|
76
115
|
.userFields(['mahjongpub/bind-team'])
|
|
77
116
|
.channelFields(['mahjongpub/bind-team'])
|
|
78
|
-
.action(async ({ session, options }) => {
|
|
117
|
+
.action(async ({ session, options }, desc) => {
|
|
79
118
|
const pw = options.channel ? (await session.getChannel(parsePlatform(options.channel)[1], ['mahjongpub/bind-team']))['mahjongpub/bind-team']
|
|
80
|
-
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'];
|
|
119
|
+
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'] ?? session.user['mahjongpub/bind-team'];
|
|
81
120
|
if (!pw)
|
|
82
121
|
return config.informNotbind ? session.text('.notbind') : '';
|
|
83
|
-
const team = new api_1.
|
|
122
|
+
const team = new api_1.TeamAdmin(ctx, pw, config);
|
|
84
123
|
try {
|
|
85
124
|
await team.read();
|
|
86
|
-
|
|
125
|
+
if (!desc)
|
|
126
|
+
return session.text('.output', team);
|
|
127
|
+
team.description = desc;
|
|
128
|
+
return await team.write();
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
ctx.logger.warn(e);
|
|
132
|
+
return session.text('.failed');
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
ctx.command('mahjongpub.team.logo [img:img]')
|
|
136
|
+
.alias('!头像', '!头像')
|
|
137
|
+
.option('channel', '-c <channel:channel>')
|
|
138
|
+
.userFields(['mahjongpub/bind-team'])
|
|
139
|
+
.channelFields(['mahjongpub/bind-team'])
|
|
140
|
+
.action(async ({ session, options }, img) => {
|
|
141
|
+
const pw = options.channel ? (await session.getChannel(parsePlatform(options.channel)[1], ['mahjongpub/bind-team']))['mahjongpub/bind-team']
|
|
142
|
+
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'] ?? session.user['mahjongpub/bind-team'];
|
|
143
|
+
if (!pw)
|
|
144
|
+
return config.informNotbind ? session.text('.notbind') : '';
|
|
145
|
+
const team = new api_1.TeamAdmin(ctx, pw, config);
|
|
146
|
+
try {
|
|
147
|
+
await team.read();
|
|
148
|
+
if (!img)
|
|
149
|
+
return koishi_1.h.image(team.img);
|
|
150
|
+
const url = await ctx.assets.upload('https://koishi.chat/logo.png', `${session.id}-${session.timestamp}.png`);
|
|
151
|
+
team.img = url;
|
|
152
|
+
return await team.write();
|
|
87
153
|
}
|
|
88
154
|
catch (e) {
|
|
89
155
|
ctx.logger.warn(e);
|
|
@@ -97,10 +163,10 @@ class MahjongPub {
|
|
|
97
163
|
.channelFields(['mahjongpub/bind-team'])
|
|
98
164
|
.action(async ({ session, options }, ...users) => {
|
|
99
165
|
const pw = options.channel ? (await session.getChannel(parsePlatform(options.channel)[1], ['mahjongpub/bind-team']))['mahjongpub/bind-team']
|
|
100
|
-
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'];
|
|
166
|
+
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'] ?? session.user['mahjongpub/bind-team'];
|
|
101
167
|
if (!pw)
|
|
102
168
|
return config.informNotbind ? session.text('.notbind') : '';
|
|
103
|
-
const team = new api_1.
|
|
169
|
+
const team = new api_1.TeamAdmin(ctx, pw, config);
|
|
104
170
|
try {
|
|
105
171
|
await team.read();
|
|
106
172
|
team.players.push(...users);
|
|
@@ -119,10 +185,10 @@ class MahjongPub {
|
|
|
119
185
|
.channelFields(['mahjongpub/bind-team'])
|
|
120
186
|
.action(async ({ session, options }, ...indices) => {
|
|
121
187
|
const pw = options.channel ? (await session.getChannel(parsePlatform(options.channel)[1], ['mahjongpub/bind-team']))['mahjongpub/bind-team']
|
|
122
|
-
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'];
|
|
188
|
+
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'] ?? session.user['mahjongpub/bind-team'];
|
|
123
189
|
if (!pw)
|
|
124
190
|
return config.informNotbind ? session.text('.notbind') : '';
|
|
125
|
-
const team = new api_1.
|
|
191
|
+
const team = new api_1.TeamAdmin(ctx, pw, config);
|
|
126
192
|
try {
|
|
127
193
|
await team.read();
|
|
128
194
|
team.players = team.players.filter((_, i) => !indices.includes(i + 1));
|
|
@@ -137,16 +203,25 @@ class MahjongPub {
|
|
|
137
203
|
ctx.command('mahjongpub.team.swap <...indices:natural>')
|
|
138
204
|
.alias('!交换', '!交换')
|
|
139
205
|
.option('channel', '-c <channel:channel>')
|
|
206
|
+
.option('team', '-t <team:string>')
|
|
140
207
|
.userFields(['mahjongpub/bind-team'])
|
|
141
|
-
.channelFields(['mahjongpub/bind-team'])
|
|
208
|
+
.channelFields(['mahjongpub/bind-team', 'mahjongpub/bind-contest', 'mahjongpub/bind-contestpw'])
|
|
142
209
|
.action(async ({ session, options }, ...indices) => {
|
|
143
|
-
|
|
144
|
-
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'];
|
|
210
|
+
let pw = options.channel ? (await session.getChannel(parsePlatform(options.channel)[1], ['mahjongpub/bind-team']))['mahjongpub/bind-team']
|
|
211
|
+
: session.isDirect ? session.user['mahjongpub/bind-team'] : session.channel['mahjongpub/bind-team'] ?? session.user['mahjongpub/bind-team'];
|
|
212
|
+
if (options.team) {
|
|
213
|
+
const [cid, cpw] = [session.channel['mahjongpub/bind-contest'], session.channel['mahjongpub/bind-contestpw']];
|
|
214
|
+
if (!cid || !cpw)
|
|
215
|
+
return session.text('.notbind');
|
|
216
|
+
const contest = new api_1.ContestAdmin(ctx, cid, cpw, config);
|
|
217
|
+
await contest.read();
|
|
218
|
+
pw = (contest.teams[options.team] ?? Object.values(contest.teams).find(t => t.name === options.team))?.pw;
|
|
219
|
+
}
|
|
145
220
|
if (!pw)
|
|
146
221
|
return config.informNotbind ? session.text('.notbind') : '';
|
|
147
222
|
if (indices.length % 2 !== 0)
|
|
148
223
|
return session.text('.invalid');
|
|
149
|
-
const team = new api_1.
|
|
224
|
+
const team = new api_1.TeamAdmin(ctx, pw, config);
|
|
150
225
|
try {
|
|
151
226
|
await team.read();
|
|
152
227
|
for (let i = 0; i < indices.length; i += 2) {
|
|
@@ -161,12 +236,40 @@ class MahjongPub {
|
|
|
161
236
|
return session.text('.failed');
|
|
162
237
|
}
|
|
163
238
|
});
|
|
239
|
+
ctx.command('mahjongpub.contest.bind <cid:string> <pw:string>')
|
|
240
|
+
// .alias('!绑定', '!绑定')
|
|
241
|
+
.option('channel', '-c <channel:channel>')
|
|
242
|
+
.channelFields(['mahjongpub/bind-contest', 'mahjongpub/bind-contestpw'])
|
|
243
|
+
.action(async ({ session, options }, cid, pw) => {
|
|
244
|
+
const contest = new api_1.ContestAdmin(ctx, cid, pw, config);
|
|
245
|
+
try {
|
|
246
|
+
await contest.read();
|
|
247
|
+
}
|
|
248
|
+
catch (e) {
|
|
249
|
+
ctx.logger.warn(e);
|
|
250
|
+
return session.text('.failed');
|
|
251
|
+
}
|
|
252
|
+
if (options.channel)
|
|
253
|
+
ctx.database.setChannel(...parsePlatform(options.channel), { 'mahjongpub/bind-contest': cid, 'mahjongpub/bind-contestpw': pw });
|
|
254
|
+
else {
|
|
255
|
+
session.channel['mahjongpub/bind-contest'] = cid;
|
|
256
|
+
session.channel['mahjongpub/bind-contestpw'] = pw;
|
|
257
|
+
}
|
|
258
|
+
return session.text('.success', contest);
|
|
259
|
+
});
|
|
260
|
+
}
|
|
261
|
+
getTeam(pw) {
|
|
262
|
+
return new api_1.TeamAdmin(this.ctx, pw, this.config);
|
|
263
|
+
}
|
|
264
|
+
getContest(cid, pw) {
|
|
265
|
+
return new api_1.ContestAdmin(this.ctx, cid, pw, this.config);
|
|
164
266
|
}
|
|
165
267
|
}
|
|
166
268
|
exports.MahjongPub = MahjongPub;
|
|
167
269
|
(function (MahjongPub) {
|
|
168
270
|
MahjongPub.inject = {
|
|
169
271
|
required: ['database'],
|
|
272
|
+
optional: ['assets'],
|
|
170
273
|
};
|
|
171
274
|
MahjongPub.Config = koishi_1.Schema.object({
|
|
172
275
|
informNotbind: koishi_1.Schema.boolean().default(false),
|
package/lib/locales/zh-CN.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"commands":{"mahjongpub.team.bind":{"description":"绑定队伍","messages":{"failed":"绑定失败","success":"成功: [{cid}:{tid}] {name}"}},"mahjongpub.team.unbind":{"description":"解除绑定","messages":{"notbind":"未绑定","success":"成功"}},"mahjongpub.team.password":{"description":"查看密码","messages":{"notbind":"未绑定","output":"密码: {0}"}},"mahjongpub.team.stats":{"description":"查看队伍信息","messages":{"notbind":"未绑定","failed":"失败","output":"- [{cid}:{tid}] {name}\n{players.map((p, i) => '' + (i+1) + ': ' + p).join('\\n')}\n"}},"mahjongpub.team.add":{"description":"添加队员","messages":{"notbind":"未绑定","failed":"失败","success":"成功"}},"mahjongpub.team.remove":{"description":"删除队员","messages":{"notbind":"未绑定","failed":"失败","success":"成功"}},"mahjongpub.team.swap":{"description":"交换队员","messages":{"notbind":"未绑定","failed":"失败","success":"成功"}}}}
|
|
1
|
+
{"commands":{"mahjongpub.team.bind":{"description":"绑定队伍","messages":{"failed":"绑定失败","success":"成功: [{cid}:{tid}] {name}"}},"mahjongpub.team.unbind":{"description":"解除绑定","messages":{"notbind":"未绑定","success":"成功"}},"mahjongpub.team.password":{"description":"查看密码","messages":{"notbind":"未绑定","output":"密码: {0}"}},"mahjongpub.team.stats":{"description":"查看队伍信息","messages":{"notbind":"未绑定","failed":"失败","output":"- [{cid}:{tid}] {name}\n{players.map((p, i) => '' + (i+1) + ': ' + p).join('\\n')}\n"}},"mahjongpub.team.desc":{"description":"队伍简介","messages":{"notbind":"未绑定","failed":"失败","output":"- [{cid}:{tid}] {name}\n{description}\n"}},"mahjongpub.team.logo":{"description":"队伍头像","messages":{"notbind":"未绑定","failed":"失败"}},"mahjongpub.team.add":{"description":"添加队员","messages":{"notbind":"未绑定","failed":"失败","success":"成功"}},"mahjongpub.team.remove":{"description":"删除队员","messages":{"notbind":"未绑定","failed":"失败","success":"成功"}},"mahjongpub.team.swap":{"description":"交换队员","messages":{"notbind":"未绑定","failed":"失败","success":"成功"}},"mahjongpub.contest.bind":{"description":"绑定比赛","messages":{"failed":"绑定失败","success":"成功: [{cid}] {name}"}}}}
|