koishi-plugin-bilibili-notify 3.0.1-alpha.0 → 3.0.1-alpha.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.
- package/lib/comRegister.d.ts +1 -1
- package/lib/comRegister.js +195 -95
- package/lib/index.d.ts +1 -0
- package/lib/index.js +6 -5
- package/package.json +1 -1
- package/readme.md +2 -0
package/lib/comRegister.d.ts
CHANGED
|
@@ -35,7 +35,7 @@ declare class ComRegister {
|
|
|
35
35
|
updateSubNotifier(): void;
|
|
36
36
|
checkIfLoginInfoIsLoaded(): Promise<unknown>;
|
|
37
37
|
subUserInBili(mid: string): Promise<{
|
|
38
|
-
|
|
38
|
+
code: number;
|
|
39
39
|
msg: string;
|
|
40
40
|
}>;
|
|
41
41
|
loadSubFromConfig(subs: ComRegister.Config["sub"]): Promise<void>;
|
package/lib/comRegister.js
CHANGED
|
@@ -950,6 +950,7 @@ class ComRegister {
|
|
|
950
950
|
.replace("-name", masterInfo.username)
|
|
951
951
|
.replace("-time", await this.ctx.gi.getTimeDifference(liveTime))
|
|
952
952
|
.replace("-watched", watched)
|
|
953
|
+
.replace("\\n", "\n")
|
|
953
954
|
.replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`)
|
|
954
955
|
: null;
|
|
955
956
|
// 发送直播通知卡片
|
|
@@ -1028,6 +1029,7 @@ class ComRegister {
|
|
|
1028
1029
|
.replace("-name", masterInfo.username)
|
|
1029
1030
|
.replace("-time", await this.ctx.gi.getTimeDifference(liveTime))
|
|
1030
1031
|
.replace("-follower", follower)
|
|
1032
|
+
.replace("\\n", "\n")
|
|
1031
1033
|
.replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`)
|
|
1032
1034
|
: null;
|
|
1033
1035
|
// 推送开播通知
|
|
@@ -1070,6 +1072,7 @@ class ComRegister {
|
|
|
1070
1072
|
.replace("-name", masterInfo.username)
|
|
1071
1073
|
.replace("-time", await this.ctx.gi.getTimeDifference(liveTime))
|
|
1072
1074
|
.replace("-follower_change", followerChange)
|
|
1075
|
+
.replace("\\n", "\n")
|
|
1073
1076
|
: null;
|
|
1074
1077
|
// 推送通知卡片
|
|
1075
1078
|
await sendLiveNotifyCard(type_1.LiveType.StopBroadcast, followerChange, liveEndMsg);
|
|
@@ -1098,6 +1101,7 @@ class ComRegister {
|
|
|
1098
1101
|
.replace("-name", masterInfo.username)
|
|
1099
1102
|
.replace("-time", await this.ctx.gi.getTimeDifference(liveTime))
|
|
1100
1103
|
.replace("-watched", watched)
|
|
1104
|
+
.replace("\\n", "\n")
|
|
1101
1105
|
.replace("-link", `https://live.bilibili.com/${liveRoomInfo.short_id === 0 ? liveRoomInfo.room_id : liveRoomInfo.short_id}`)
|
|
1102
1106
|
: null;
|
|
1103
1107
|
// 发送直播通知卡片
|
|
@@ -1181,136 +1185,232 @@ class ComRegister {
|
|
|
1181
1185
|
}
|
|
1182
1186
|
}
|
|
1183
1187
|
else if (createGroupData.code !== 0) {
|
|
1184
|
-
console.log(createGroupData);
|
|
1185
1188
|
// 创建分组失败
|
|
1186
|
-
return
|
|
1189
|
+
return { code: createGroupData.code, msg: createGroupData.message };
|
|
1187
1190
|
}
|
|
1188
1191
|
// 创建成功,保存到数据库
|
|
1189
1192
|
this.ctx.database.set("loginBili", 1, {
|
|
1190
1193
|
dynamic_group_id: this.loginDBData.dynamic_group_id,
|
|
1191
1194
|
});
|
|
1192
1195
|
// 创建成功
|
|
1193
|
-
return
|
|
1196
|
+
return { code: createGroupData.code, msg: createGroupData.message };
|
|
1194
1197
|
}
|
|
1195
|
-
return
|
|
1198
|
+
return { code: 0, msg: "分组已存在" };
|
|
1196
1199
|
};
|
|
1197
1200
|
// 判断分组是否准备好
|
|
1198
|
-
const
|
|
1201
|
+
const resp = await checkGroupIsReady();
|
|
1199
1202
|
// 判断是否创建成功
|
|
1200
|
-
if (
|
|
1203
|
+
if (resp.code !== 0) {
|
|
1201
1204
|
// 创建分组失败
|
|
1202
|
-
return
|
|
1205
|
+
return resp;
|
|
1203
1206
|
}
|
|
1204
|
-
//
|
|
1205
|
-
const
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
//
|
|
1216
|
-
|
|
1207
|
+
// 获取分组详情
|
|
1208
|
+
const getGroupDetailData = async () => {
|
|
1209
|
+
// 获取分组明细
|
|
1210
|
+
const relationGroupDetailData = await this.ctx.ba.getRelationGroupDetail(this.loginDBData.dynamic_group_id);
|
|
1211
|
+
// 判断分组信息是否获取成功
|
|
1212
|
+
if (relationGroupDetailData.code !== 0) {
|
|
1213
|
+
if (relationGroupDetailData.code === 22104) {
|
|
1214
|
+
// 将原先的分组id置空
|
|
1215
|
+
this.loginDBData.dynamic_group_id = null;
|
|
1216
|
+
// 分组不存在
|
|
1217
|
+
const resp = await checkGroupIsReady();
|
|
1218
|
+
// 判断是否创建成功
|
|
1219
|
+
if (resp.code !== 0) {
|
|
1220
|
+
// 创建分组失败
|
|
1221
|
+
return { ...resp, data: undefined };
|
|
1222
|
+
}
|
|
1223
|
+
// 再次获取分组明细
|
|
1224
|
+
return getGroupDetailData();
|
|
1217
1225
|
}
|
|
1218
|
-
|
|
1226
|
+
// 获取分组明细失败
|
|
1227
|
+
return {
|
|
1228
|
+
code: relationGroupDetailData.code,
|
|
1229
|
+
msg: relationGroupDetailData.message,
|
|
1230
|
+
data: undefined,
|
|
1231
|
+
};
|
|
1219
1232
|
}
|
|
1220
|
-
|
|
1221
|
-
|
|
1233
|
+
return {
|
|
1234
|
+
code: 0,
|
|
1235
|
+
msg: "获取分组明细成功",
|
|
1236
|
+
data: relationGroupDetailData.data,
|
|
1237
|
+
};
|
|
1238
|
+
};
|
|
1239
|
+
// 获取分组明细
|
|
1240
|
+
const { code, msg, data } = await getGroupDetailData();
|
|
1241
|
+
// 判断获取分组明细是否成功
|
|
1242
|
+
if (code !== 0) {
|
|
1243
|
+
return { code, msg };
|
|
1222
1244
|
}
|
|
1223
|
-
|
|
1245
|
+
// 判断是否已经订阅该对象
|
|
1246
|
+
for (const user of data) {
|
|
1224
1247
|
if (user.mid === mid) {
|
|
1225
1248
|
// 已关注订阅对象
|
|
1226
|
-
return {
|
|
1249
|
+
return { code: 0, msg: "订阅对象已存在于分组中" };
|
|
1227
1250
|
}
|
|
1228
1251
|
}
|
|
1229
1252
|
// 订阅对象
|
|
1230
|
-
const subUserData = await this.ctx.ba.follow(mid);
|
|
1231
|
-
//
|
|
1232
|
-
|
|
1233
|
-
|
|
1253
|
+
const subUserData = (await this.ctx.ba.follow(mid));
|
|
1254
|
+
// 模式匹配
|
|
1255
|
+
const subUserMatchPattern = {
|
|
1256
|
+
[-101]: () => {
|
|
1234
1257
|
return {
|
|
1235
|
-
|
|
1258
|
+
code: subUserData.code,
|
|
1236
1259
|
msg: "账号未登录,请使用指令bili login登录后再进行订阅操作",
|
|
1237
1260
|
};
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1261
|
+
},
|
|
1262
|
+
[-102]: () => {
|
|
1263
|
+
return {
|
|
1264
|
+
code: subUserData.code,
|
|
1265
|
+
msg: "账号被封停,无法进行订阅操作",
|
|
1266
|
+
};
|
|
1267
|
+
},
|
|
1268
|
+
[22002]: () => {
|
|
1269
|
+
return {
|
|
1270
|
+
code: subUserData.code,
|
|
1271
|
+
msg: "因对方隐私设置,无法进行订阅操作",
|
|
1272
|
+
};
|
|
1273
|
+
},
|
|
1274
|
+
[22003]: () => {
|
|
1275
|
+
return {
|
|
1276
|
+
code: subUserData.code,
|
|
1277
|
+
msg: "你已将对方拉黑,无法进行订阅操作",
|
|
1278
|
+
};
|
|
1279
|
+
},
|
|
1280
|
+
[22013]: () => {
|
|
1281
|
+
return {
|
|
1282
|
+
code: subUserData.code,
|
|
1283
|
+
msg: "账号已注销,无法进行订阅操作",
|
|
1284
|
+
};
|
|
1285
|
+
},
|
|
1286
|
+
[40061]: () => {
|
|
1247
1287
|
return {
|
|
1248
|
-
|
|
1288
|
+
code: subUserData.code,
|
|
1249
1289
|
msg: "账号不存在,请检查uid输入是否正确或用户是否存在",
|
|
1250
1290
|
};
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1291
|
+
},
|
|
1292
|
+
[22001]: () => {
|
|
1293
|
+
return {
|
|
1294
|
+
code: 0,
|
|
1295
|
+
msg: "订阅对象为自己,无需添加到分组",
|
|
1296
|
+
};
|
|
1297
|
+
},
|
|
1298
|
+
// 已订阅该对象
|
|
1299
|
+
[22014]: async () => {
|
|
1256
1300
|
// 把订阅对象添加到分组中
|
|
1257
1301
|
const copyUserToGroupData = await this.ctx.ba.copyUserToGroup(mid, this.loginDBData.dynamic_group_id);
|
|
1258
1302
|
// 判断是否添加成功
|
|
1259
1303
|
if (copyUserToGroupData.code !== 0) {
|
|
1260
1304
|
// 添加失败
|
|
1261
|
-
return {
|
|
1305
|
+
return {
|
|
1306
|
+
code: copyUserToGroupData.code,
|
|
1307
|
+
msg: "添加订阅对象到分组失败,请稍后重试",
|
|
1308
|
+
};
|
|
1262
1309
|
}
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1310
|
+
// 添加成功
|
|
1311
|
+
return { code: 0, msg: "订阅对象添加成功" };
|
|
1312
|
+
},
|
|
1313
|
+
// 订阅成功
|
|
1314
|
+
[0]: async () => {
|
|
1315
|
+
// 把订阅对象添加到分组中
|
|
1316
|
+
const copyUserToGroupData = await this.ctx.ba.copyUserToGroup(mid, this.loginDBData.dynamic_group_id);
|
|
1317
|
+
// 判断是否添加成功
|
|
1318
|
+
if (copyUserToGroupData.code !== 0) {
|
|
1319
|
+
// 添加失败
|
|
1320
|
+
return {
|
|
1321
|
+
code: copyUserToGroupData.code,
|
|
1322
|
+
msg: "添加订阅对象到分组失败,请稍后重试",
|
|
1323
|
+
};
|
|
1324
|
+
}
|
|
1325
|
+
// 添加成功
|
|
1326
|
+
return { code: 0, msg: "订阅对象添加成功" };
|
|
1327
|
+
},
|
|
1328
|
+
};
|
|
1329
|
+
// 获取函数
|
|
1330
|
+
const func = subUserMatchPattern[subUserData.code];
|
|
1331
|
+
// 执行函数并返回
|
|
1332
|
+
return await func();
|
|
1267
1333
|
}
|
|
1268
1334
|
async loadSubFromConfig(subs) {
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
//
|
|
1285
|
-
sub.
|
|
1286
|
-
|
|
1287
|
-
this.logger.warn(`UID:${sub.uid} 用户没有开通直播间,无法订阅直播!`);
|
|
1288
|
-
}
|
|
1289
|
-
// 判断是否订阅直播
|
|
1290
|
-
if (sub.live) {
|
|
1291
|
-
// 启动直播监测
|
|
1292
|
-
await this.liveDetectWithListener(data.live_room.roomid, sub.target, sub.card);
|
|
1293
|
-
}
|
|
1294
|
-
}
|
|
1295
|
-
// 在B站中订阅该对象
|
|
1296
|
-
const subInfo = await this.subUserInBili(sub.uid);
|
|
1297
|
-
// 判断订阅是否成功
|
|
1298
|
-
if (!subInfo.flag)
|
|
1299
|
-
this.logger.warn(subInfo.msg);
|
|
1300
|
-
// 将该订阅添加到sm中
|
|
1301
|
-
this.subManager.push({
|
|
1302
|
-
id: +sub.uid,
|
|
1303
|
-
uid: sub.uid,
|
|
1304
|
-
uname: data.name,
|
|
1305
|
-
roomId: sub.live ? data.live_room.roomid : "",
|
|
1306
|
-
target: sub.target,
|
|
1307
|
-
platform: "",
|
|
1308
|
-
live: sub.live,
|
|
1309
|
-
dynamic: sub.dynamic,
|
|
1310
|
-
card: sub.card,
|
|
1335
|
+
// 定义一个AbortController
|
|
1336
|
+
const controller = new AbortController();
|
|
1337
|
+
const { signal } = controller;
|
|
1338
|
+
// 设置超时
|
|
1339
|
+
signal.addEventListener("abort", () => {
|
|
1340
|
+
this.logger.info(`${signal.reason},订阅未完全加载!`);
|
|
1341
|
+
});
|
|
1342
|
+
await Promise.all(subs.map(async (sub) => {
|
|
1343
|
+
let timer;
|
|
1344
|
+
// 创建一个定时器
|
|
1345
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
1346
|
+
timer = this.ctx.setTimeout(() => {
|
|
1347
|
+
// 取消订阅加载
|
|
1348
|
+
if (signal.aborted)
|
|
1349
|
+
return;
|
|
1350
|
+
// 终止
|
|
1351
|
+
controller.abort(`加载订阅UID:${sub.uid}超时`);
|
|
1352
|
+
}, 10 * 1000);
|
|
1311
1353
|
});
|
|
1312
|
-
|
|
1313
|
-
|
|
1354
|
+
await Promise.race([
|
|
1355
|
+
(async () => {
|
|
1356
|
+
// logger
|
|
1357
|
+
this.logger.info(`加载订阅UID:${sub.uid}中...`);
|
|
1358
|
+
// 定义Data
|
|
1359
|
+
const data = await (0, utils_1.withRetry)(async () => await this.ctx.ba.getUserInfo(sub.uid))
|
|
1360
|
+
.then((content) => content.data)
|
|
1361
|
+
.catch((e) => {
|
|
1362
|
+
this.logger.error(`loadSubFromConfig() getUserInfo() 发生了错误,错误为:${e.message}`);
|
|
1363
|
+
// logger
|
|
1364
|
+
this.logger.info(`加载订阅UID:${sub.uid}失败!`);
|
|
1365
|
+
});
|
|
1366
|
+
// 判断是否需要订阅直播
|
|
1367
|
+
if (sub.live) {
|
|
1368
|
+
// 检查roomid是否存在
|
|
1369
|
+
if (!data.live_room) {
|
|
1370
|
+
// 用户没有开通直播间,无法订阅直播
|
|
1371
|
+
sub.live = false;
|
|
1372
|
+
// 发送提示
|
|
1373
|
+
this.logger.warn(`UID:${sub.uid} 用户没有开通直播间,无法订阅直播!`);
|
|
1374
|
+
}
|
|
1375
|
+
// 判断是否订阅直播
|
|
1376
|
+
if (sub.live) {
|
|
1377
|
+
// 启动直播监测
|
|
1378
|
+
await this.liveDetectWithListener(data.live_room.roomid, sub.target, sub.card);
|
|
1379
|
+
}
|
|
1380
|
+
}
|
|
1381
|
+
// 在B站中订阅该对象
|
|
1382
|
+
const subInfo = await this.subUserInBili(sub.uid);
|
|
1383
|
+
// 判断订阅是否成功
|
|
1384
|
+
if (subInfo.code !== 0) {
|
|
1385
|
+
// 订阅失败,直接返回
|
|
1386
|
+
this.logger.error(`UID:${sub.uid} 订阅失败,错误信息:${subInfo.msg}`);
|
|
1387
|
+
return;
|
|
1388
|
+
}
|
|
1389
|
+
// 判断是否超时
|
|
1390
|
+
if (signal.aborted) {
|
|
1391
|
+
// 订阅加载超时,取消订阅加载
|
|
1392
|
+
return;
|
|
1393
|
+
}
|
|
1394
|
+
// 将该订阅添加到sm中
|
|
1395
|
+
this.subManager.push({
|
|
1396
|
+
id: +sub.uid,
|
|
1397
|
+
uid: sub.uid,
|
|
1398
|
+
uname: data.name,
|
|
1399
|
+
roomId: sub.live ? data.live_room.roomid : "",
|
|
1400
|
+
target: sub.target,
|
|
1401
|
+
platform: "",
|
|
1402
|
+
live: sub.live,
|
|
1403
|
+
dynamic: sub.dynamic,
|
|
1404
|
+
card: sub.card,
|
|
1405
|
+
});
|
|
1406
|
+
// 清除定时器
|
|
1407
|
+
timer();
|
|
1408
|
+
// logger
|
|
1409
|
+
this.logger.info(`UID:${sub.uid}订阅加载完毕!`);
|
|
1410
|
+
})(),
|
|
1411
|
+
timeoutPromise,
|
|
1412
|
+
]);
|
|
1413
|
+
}));
|
|
1314
1414
|
}
|
|
1315
1415
|
checkIfDynamicDetectIsNeeded() {
|
|
1316
1416
|
// 检查是否有订阅对象需要动态监测
|
|
@@ -1320,7 +1420,7 @@ class ComRegister {
|
|
|
1320
1420
|
enableDynamicDetect() {
|
|
1321
1421
|
// 开始动态监测
|
|
1322
1422
|
if (this.config.dynamicDebugMode) {
|
|
1323
|
-
this.dynamicDispose = this.ctx.setInterval(this.debug_dynamicDetect(),
|
|
1423
|
+
this.dynamicDispose = this.ctx.setInterval(this.debug_dynamicDetect(), this.config.dynamicLoopTime * 1000);
|
|
1324
1424
|
}
|
|
1325
1425
|
else {
|
|
1326
1426
|
this.dynamicDispose = this.ctx.setInterval(this.dynamicDetect(), this.config.dynamicLoopTime * 1000);
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -277,6 +277,7 @@ exports.Config = koishi_1.Schema.object({
|
|
|
277
277
|
.description("设置请求头User-Agen,请求出现-352时可以尝试修改,UA获取方法可参考:https://blog.csdn.net/qq_44503987/article/details/104929111"),
|
|
278
278
|
subTitle: koishi_1.Schema.object({}).description("订阅配置"),
|
|
279
279
|
sub: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
280
|
+
name: koishi_1.Schema.string().description("订阅用户昵称,只是给你自己看的(相当于备注),可填可不填"),
|
|
280
281
|
uid: koishi_1.Schema.string().required().description("订阅用户UID"),
|
|
281
282
|
dynamic: koishi_1.Schema.boolean().default(false).description("是否订阅用户动态"),
|
|
282
283
|
live: koishi_1.Schema.boolean().default(false).description("是否订阅用户直播"),
|
|
@@ -370,14 +371,14 @@ exports.Config = koishi_1.Schema.object({
|
|
|
370
371
|
.default(1)
|
|
371
372
|
.description("设定间隔多长时间推送一次直播状态,单位为小时,默认为一小时"),
|
|
372
373
|
customLiveStart: koishi_1.Schema.string()
|
|
373
|
-
.default("-name开播啦,当前粉丝数:-follower
|
|
374
|
-
.description("自定义开播提示语,-name代表UP昵称,-follower代表当前粉丝数,-link代表直播间链接(如果使用的是QQ
|
|
374
|
+
.default("-name开播啦,当前粉丝数:-follower\\n-link")
|
|
375
|
+
.description("自定义开播提示语,-name代表UP昵称,-follower代表当前粉丝数,-link代表直播间链接(如果使用的是QQ官方机器人,请不要使用),\\n为换行。例如-name开播啦,会发送为xxxUP开播啦"),
|
|
375
376
|
customLive: koishi_1.Schema.string()
|
|
376
|
-
.default("-name正在直播,目前已播-time,累计看过人数:-watched
|
|
377
|
-
.description("自定义直播中提示语,-name代表UP昵称,-time代表开播时长,-watched代表累计看过人数,-link代表直播间链接(如果使用的是QQ
|
|
377
|
+
.default("-name正在直播,目前已播-time,累计看过人数:-watched\\n-link")
|
|
378
|
+
.description("自定义直播中提示语,-name代表UP昵称,-time代表开播时长,-watched代表累计看过人数,-link代表直播间链接(如果使用的是QQ官方机器人,请不要使用),\\n为换行。例如-name正在直播,会发送为xxxUP正在直播xxx"),
|
|
378
379
|
customLiveEnd: koishi_1.Schema.string()
|
|
379
380
|
.default("-name下播啦,本次直播了-time,粉丝数变化-follower_change")
|
|
380
|
-
.description("自定义下播提示语,-name代表UP昵称,-follower_change代表本场直播粉丝数变,-time
|
|
381
|
+
.description("自定义下播提示语,-name代表UP昵称,-follower_change代表本场直播粉丝数变,-time代表开播时长,\\n为换行。例如-name下播啦,本次直播了-time,会发送为xxxUP下播啦,直播时长为xx小时xx分钟xx秒"),
|
|
381
382
|
followerDisplay: koishi_1.Schema.boolean()
|
|
382
383
|
.default(true)
|
|
383
384
|
.description("粉丝数变化和看过本场直播的人数是否显示在推送卡片中"),
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -214,6 +214,8 @@
|
|
|
214
214
|
- ver 3.0.0-beta.2 新增:配置项 `sub.card`,能更改每个订阅的推送卡片样式,未更改的样式与全局样式保持一致
|
|
215
215
|
- ver 3.0.0 优化:配置项 `sub.card` 添加实验性标识
|
|
216
216
|
- ver 3.0.1-alpha.0 优化:动态推送逻辑
|
|
217
|
+
- ver 3.0.1-alpha.1 新增:直播提示语换行符,配置项 `sub.name` 为可选项; 修复:debug模式下动态监测5S一次; 优化:加载订阅
|
|
218
|
+
- ver 3.0.1-alpha.2 优化:改进错误提示,改进订阅加载方式
|
|
217
219
|
|
|
218
220
|
## 交流群
|
|
219
221
|
|