koishi-plugin-bilibili-notify 1.0.13 → 1.0.14
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/biliAPI.d.ts +1 -0
- package/lib/biliAPI.js +70 -17
- package/lib/comRegister.d.ts +2 -0
- package/lib/comRegister.js +84 -43
- package/lib/index.d.ts +1 -0
- package/lib/index.js +6 -1
- package/package.json +1 -1
- package/readme.md +1 -0
package/lib/biliAPI.d.ts
CHANGED
package/lib/biliAPI.js
CHANGED
|
@@ -25,6 +25,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
25
25
|
client;
|
|
26
26
|
loginData;
|
|
27
27
|
loginNotifier;
|
|
28
|
+
refreshCookieTimer;
|
|
28
29
|
constructor(ctx) {
|
|
29
30
|
super(ctx, 'biliAPI');
|
|
30
31
|
}
|
|
@@ -35,6 +36,52 @@ class BiliAPI extends koishi_1.Service {
|
|
|
35
36
|
this.loadCookiesFromDatabase();
|
|
36
37
|
// this.logger.info('BiliAPI已被注册到Context中')
|
|
37
38
|
}
|
|
39
|
+
/* async test_refresh_token() {
|
|
40
|
+
const publicKey = await crypto.subtle.importKey(
|
|
41
|
+
"jwk",
|
|
42
|
+
{
|
|
43
|
+
kty: "RSA",
|
|
44
|
+
n: "y4HdjgJHBlbaBN04VERG4qNBIFHP6a3GozCl75AihQloSWCXC5HDNgyinEnhaQ_4-gaMud_GF50elYXLlCToR9se9Z8z433U3KjM-3Yx7ptKkmQNAMggQwAVKgq3zYAoidNEWuxpkY_mAitTSRLnsJW-NCTa0bqBFF6Wm1MxgfE",
|
|
45
|
+
e: "AQAB",
|
|
46
|
+
},
|
|
47
|
+
{ name: "RSA-OAEP", hash: "SHA-256" },
|
|
48
|
+
true,
|
|
49
|
+
["encrypt"],
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
async function getCorrespondPath(timestamp) {
|
|
53
|
+
const data = new TextEncoder().encode(`refresh_${timestamp}`);
|
|
54
|
+
const encrypted = new Uint8Array(await crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data))
|
|
55
|
+
return encrypted.reduce((str, c) => str + c.toString(16).padStart(2, "0"), "")
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const ts = Date.now()
|
|
59
|
+
const correspondPath = await getCorrespondPath(ts)
|
|
60
|
+
const { data } = await this.client.get(`https://www.bilibili.com/correspond/1/${correspondPath}`)
|
|
61
|
+
// 创建一个虚拟的DOM元素
|
|
62
|
+
const { document } = new JSDOM(data).window;
|
|
63
|
+
// 提取标签name为1-name的内容
|
|
64
|
+
const targetElement = document.getElementById('1-name');
|
|
65
|
+
const refresh_csrf = targetElement ? targetElement.textContent : null;
|
|
66
|
+
// 获取csrf
|
|
67
|
+
let csrf: string
|
|
68
|
+
const cookies = JSON.parse(this.getCookies())
|
|
69
|
+
cookies.forEach(cookie => {
|
|
70
|
+
if (cookie.key === 'bili_jct') csrf = cookie.value
|
|
71
|
+
})
|
|
72
|
+
// 读取数据库获取cookies
|
|
73
|
+
const database = (await this.ctx.database.get('loginBili', 1))[0]
|
|
74
|
+
// 获取refreshToken
|
|
75
|
+
const refresh_token = this.ctx.wbi.decrypt(database.bili_refresh_token)
|
|
76
|
+
// 发送请求
|
|
77
|
+
const { data: refreshData } = await this.client.post('https://passport.bilibili.com/x/passport-login/web/cookie/refresh', {
|
|
78
|
+
csrf,
|
|
79
|
+
refresh_csrf,
|
|
80
|
+
source: 'main_web',
|
|
81
|
+
refresh_token
|
|
82
|
+
})
|
|
83
|
+
console.log(refreshData);
|
|
84
|
+
} */
|
|
38
85
|
async getServerUTCTime() {
|
|
39
86
|
try {
|
|
40
87
|
const { data } = await this.client.get(GET_SERVER_UTC_TIME);
|
|
@@ -204,12 +251,28 @@ class BiliAPI extends koishi_1.Service {
|
|
|
204
251
|
// restart plugin check
|
|
205
252
|
this.checkIfTokenNeedRefresh(decryptedRefreshToken, csrf);
|
|
206
253
|
// Open scheduled tasks and check if token need refresh
|
|
207
|
-
this.ctx.setInterval(() => {
|
|
254
|
+
this.refreshCookieTimer = this.ctx.setInterval(() => {
|
|
208
255
|
this.checkIfTokenNeedRefresh(decryptedRefreshToken, csrf);
|
|
209
256
|
}, 43200000);
|
|
210
257
|
}
|
|
211
258
|
async checkIfTokenNeedRefresh(refreshToken, csrf, times = 0) {
|
|
259
|
+
// 定义数据
|
|
212
260
|
let data;
|
|
261
|
+
// 定义方法
|
|
262
|
+
const notifyAndError = (info) => {
|
|
263
|
+
// 设置控制台通知
|
|
264
|
+
this.loginNotifier = this.ctx.notifier.create({
|
|
265
|
+
type: 'warning',
|
|
266
|
+
content: info
|
|
267
|
+
});
|
|
268
|
+
// 重置为未登录状态
|
|
269
|
+
this.createNewClient();
|
|
270
|
+
// 关闭定时器
|
|
271
|
+
this.refreshCookieTimer();
|
|
272
|
+
// 抛出错误
|
|
273
|
+
throw new Error(info);
|
|
274
|
+
};
|
|
275
|
+
// 尝试获取Cookieinfo
|
|
213
276
|
try {
|
|
214
277
|
const { data: cookieData } = await this.getCookieInfo(refreshToken);
|
|
215
278
|
data = cookieData;
|
|
@@ -227,17 +290,21 @@ class BiliAPI extends koishi_1.Service {
|
|
|
227
290
|
// 不需要刷新,直接返回
|
|
228
291
|
if (!data.refresh)
|
|
229
292
|
return;
|
|
293
|
+
// 定义Key
|
|
230
294
|
const publicKey = await crypto.subtle.importKey("jwk", {
|
|
231
295
|
kty: "RSA",
|
|
232
296
|
n: "y4HdjgJHBlbaBN04VERG4qNBIFHP6a3GozCl75AihQloSWCXC5HDNgyinEnhaQ_4-gaMud_GF50elYXLlCToR9se9Z8z433U3KjM-3Yx7ptKkmQNAMggQwAVKgq3zYAoidNEWuxpkY_mAitTSRLnsJW-NCTa0bqBFF6Wm1MxgfE",
|
|
233
297
|
e: "AQAB",
|
|
234
298
|
}, { name: "RSA-OAEP", hash: "SHA-256" }, true, ["encrypt"]);
|
|
299
|
+
// 定义获取CorrespondPath方法
|
|
235
300
|
async function getCorrespondPath(timestamp) {
|
|
236
301
|
const data = new TextEncoder().encode(`refresh_${timestamp}`);
|
|
237
302
|
const encrypted = new Uint8Array(await crypto.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data));
|
|
238
303
|
return encrypted.reduce((str, c) => str + c.toString(16).padStart(2, "0"), "");
|
|
239
304
|
}
|
|
240
|
-
|
|
305
|
+
// 获取CorrespondPath
|
|
306
|
+
const ts = Date.now();
|
|
307
|
+
const correspondPath = await getCorrespondPath(ts);
|
|
241
308
|
// 获取refresh_csrf
|
|
242
309
|
const { data: refreshCsrfHtml } = await this.client.get(`https://www.bilibili.com/correspond/1/${correspondPath}`);
|
|
243
310
|
// 创建一个虚拟的DOM元素
|
|
@@ -247,19 +314,11 @@ class BiliAPI extends koishi_1.Service {
|
|
|
247
314
|
const refresh_csrf = targetElement ? targetElement.textContent : null;
|
|
248
315
|
// 发送刷新请求
|
|
249
316
|
const { data: refreshData } = await this.client.post('https://passport.bilibili.com/x/passport-login/web/cookie/refresh', {
|
|
250
|
-
csrf
|
|
317
|
+
csrf,
|
|
251
318
|
refresh_csrf,
|
|
252
319
|
source: 'main_web',
|
|
253
320
|
refresh_token: refreshToken
|
|
254
321
|
});
|
|
255
|
-
const notifyAndError = (info) => {
|
|
256
|
-
// 设置控制台通知
|
|
257
|
-
this.loginNotifier = this.ctx.notifier.create({
|
|
258
|
-
type: 'warning',
|
|
259
|
-
content: info
|
|
260
|
-
});
|
|
261
|
-
throw new Error(info);
|
|
262
|
-
};
|
|
263
322
|
// 检查是否有其他问题
|
|
264
323
|
switch (refreshData.code) {
|
|
265
324
|
// 账号未登录
|
|
@@ -283,12 +342,6 @@ class BiliAPI extends koishi_1.Service {
|
|
|
283
342
|
}]);
|
|
284
343
|
// Get new csrf from cookies
|
|
285
344
|
let newCsrf;
|
|
286
|
-
/* this.jar.store.getAllCookies((err, c) => {
|
|
287
|
-
if (err) throw err;
|
|
288
|
-
c.forEach(cookie => {
|
|
289
|
-
if (cookie.key === 'bili_jct') newCsrf = cookie.value
|
|
290
|
-
});
|
|
291
|
-
}) */
|
|
292
345
|
this.jar.serializeSync().cookies.forEach(cookie => {
|
|
293
346
|
if (cookie.key === 'bili_jct')
|
|
294
347
|
newCsrf = cookie.value;
|
package/lib/comRegister.d.ts
CHANGED
|
@@ -20,6 +20,7 @@ declare class ComRegister {
|
|
|
20
20
|
qqBot: Bot<Context>;
|
|
21
21
|
qqguildBot: Bot<Context>;
|
|
22
22
|
oneBot: Bot<Context>;
|
|
23
|
+
redBot: Bot<Context>;
|
|
23
24
|
constructor(ctx: Context, config: ComRegister.Config);
|
|
24
25
|
dynamicDetect(ctx: Context, bot: Bot<Context>, guildId: string, uid: string): () => Promise<void>;
|
|
25
26
|
liveDetect(ctx: Context, bot: Bot<Context>, guildId: string, roomId: string): () => Promise<string[]>;
|
|
@@ -33,6 +34,7 @@ declare class ComRegister {
|
|
|
33
34
|
declare namespace ComRegister {
|
|
34
35
|
interface Config {
|
|
35
36
|
unlockSubLimits: boolean;
|
|
37
|
+
liveStartAtAll: boolean;
|
|
36
38
|
pushTime: number;
|
|
37
39
|
liveLoopTime: number;
|
|
38
40
|
dynamicLoopTime: number;
|
package/lib/comRegister.js
CHANGED
|
@@ -27,15 +27,20 @@ class ComRegister {
|
|
|
27
27
|
qqguildBot;
|
|
28
28
|
// OneBot机器人
|
|
29
29
|
oneBot;
|
|
30
|
+
// Red机器人
|
|
31
|
+
redBot;
|
|
30
32
|
constructor(ctx, config) {
|
|
31
33
|
this.logger = ctx.logger('commandRegister');
|
|
32
34
|
this.config = config;
|
|
33
|
-
//
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
35
|
+
// 拿到各类机器人
|
|
36
|
+
ctx.bots.forEach(bot => {
|
|
37
|
+
switch (bot.platform) {
|
|
38
|
+
case 'qq': this.qqBot = bot;
|
|
39
|
+
case 'qqguild': this.qqguildBot = bot;
|
|
40
|
+
case 'onebot': this.oneBot = bot;
|
|
41
|
+
case ' red': this.redBot = bot;
|
|
42
|
+
}
|
|
43
|
+
});
|
|
39
44
|
// 从数据库获取订阅
|
|
40
45
|
this.getSubFromDatabase(ctx);
|
|
41
46
|
/* const testCom = ctx.command('test', { hidden: true, permissions: ['authority:5'] })
|
|
@@ -45,7 +50,7 @@ class ComRegister {
|
|
|
45
50
|
.action(async () => {
|
|
46
51
|
this.logger.info('调用test cookies指令')
|
|
47
52
|
// await ctx.biliAPI.loadCookiesFromDatabase()
|
|
48
|
-
console.log(ctx.biliAPI.getCookies());
|
|
53
|
+
console.log(JSON.parse(ctx.biliAPI.getCookies()));
|
|
49
54
|
})
|
|
50
55
|
|
|
51
56
|
testCom
|
|
@@ -120,6 +125,14 @@ class ComRegister {
|
|
|
120
125
|
.example('test utc')
|
|
121
126
|
.action(async ({ session }) => {
|
|
122
127
|
session.send((await ctx.biliAPI.getServerUTCTime()).toString())
|
|
128
|
+
})
|
|
129
|
+
|
|
130
|
+
testCom
|
|
131
|
+
.subcommand('.refresh')
|
|
132
|
+
.usage('测试cookie刷新方法')
|
|
133
|
+
.example('test refresh')
|
|
134
|
+
.action(async ({ session }) => {
|
|
135
|
+
ctx.biliAPI.test_refresh_token()
|
|
123
136
|
}) */
|
|
124
137
|
const biliCom = ctx.command('bili', 'bili-notify插件相关指令', { permissions: ['authority:3'] });
|
|
125
138
|
biliCom.subcommand('.login', '登录B站之后才可以进行之后的操作')
|
|
@@ -154,43 +167,55 @@ class ComRegister {
|
|
|
154
167
|
});
|
|
155
168
|
// 检查之前是否存在登录定时器
|
|
156
169
|
this.loginTimer && this.loginTimer();
|
|
170
|
+
// 设置flag
|
|
171
|
+
let flag = true;
|
|
157
172
|
// 发起登录请求检查登录状态
|
|
158
173
|
this.loginTimer = ctx.setInterval(async () => {
|
|
159
|
-
let loginContent;
|
|
160
174
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
175
|
+
// 判断上一个循环是否完成
|
|
176
|
+
if (!flag)
|
|
177
|
+
return;
|
|
178
|
+
flag = false;
|
|
179
|
+
// 获取登录信息
|
|
180
|
+
let loginContent;
|
|
181
|
+
try {
|
|
182
|
+
loginContent = await ctx.biliAPI.getLoginStatus(content.data.qrcode_key);
|
|
183
|
+
}
|
|
184
|
+
catch (e) {
|
|
185
|
+
this.logger.error(e);
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
if (loginContent.code !== 0) {
|
|
189
|
+
this.loginTimer();
|
|
190
|
+
return await session.send('登录失败请联系管理员解决');
|
|
191
|
+
}
|
|
192
|
+
if (loginContent.data.code === 86038) {
|
|
193
|
+
this.loginTimer();
|
|
194
|
+
return await session.send('二维码已失效,请重新登录');
|
|
195
|
+
}
|
|
196
|
+
if (loginContent.data.code === 0) { // 登录成功
|
|
197
|
+
const encryptedCookies = ctx.wbi.encrypt(ctx.biliAPI.getCookies());
|
|
198
|
+
const encryptedRefreshToken = ctx.wbi.encrypt(loginContent.data.refresh_token);
|
|
199
|
+
await ctx.database.upsert('loginBili', [{
|
|
200
|
+
id: 1,
|
|
201
|
+
bili_cookies: encryptedCookies,
|
|
202
|
+
bili_refresh_token: encryptedRefreshToken
|
|
203
|
+
}]);
|
|
204
|
+
// 销毁定时器
|
|
205
|
+
this.loginTimer();
|
|
206
|
+
// 订阅之前的订阅
|
|
207
|
+
await this.getSubFromDatabase(ctx);
|
|
208
|
+
// 清除控制台通知
|
|
209
|
+
ctx.biliAPI.disposeNotifier();
|
|
210
|
+
// 发送成功登录推送
|
|
211
|
+
await session.send('登录成功');
|
|
212
|
+
// bili show
|
|
213
|
+
await session.execute('bili show');
|
|
214
|
+
return;
|
|
215
|
+
}
|
|
174
216
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
const encryptedRefreshToken = ctx.wbi.encrypt(loginContent.data.refresh_token);
|
|
178
|
-
await ctx.database.upsert('loginBili', [{
|
|
179
|
-
id: 1,
|
|
180
|
-
bili_cookies: encryptedCookies,
|
|
181
|
-
bili_refresh_token: encryptedRefreshToken
|
|
182
|
-
}]);
|
|
183
|
-
// 销毁定时器
|
|
184
|
-
this.loginTimer();
|
|
185
|
-
// 订阅之前的订阅
|
|
186
|
-
await this.getSubFromDatabase(ctx);
|
|
187
|
-
// 清除控制台通知
|
|
188
|
-
ctx.biliAPI.disposeNotifier();
|
|
189
|
-
// 发送成功登录推送
|
|
190
|
-
await session.send('登录成功');
|
|
191
|
-
// bili show
|
|
192
|
-
await session.execute('bili show');
|
|
193
|
-
return;
|
|
217
|
+
finally {
|
|
218
|
+
flag = true;
|
|
194
219
|
}
|
|
195
220
|
}, 1000);
|
|
196
221
|
});
|
|
@@ -315,11 +340,12 @@ class ComRegister {
|
|
|
315
340
|
if (!liveMsg && !dynamicMsg) {
|
|
316
341
|
return '您未订阅该UP的任何消息';
|
|
317
342
|
}
|
|
318
|
-
//
|
|
319
|
-
if (!guildId) { //
|
|
343
|
+
// 设置频道号
|
|
344
|
+
if (!guildId) { // 没有输入频道号,默认当前聊天环境
|
|
320
345
|
switch (session.event.platform) {
|
|
321
|
-
case '
|
|
346
|
+
case 'red':
|
|
322
347
|
case 'onebot':
|
|
348
|
+
case 'qqguild':
|
|
323
349
|
guildId = session.event.channel.id;
|
|
324
350
|
break;
|
|
325
351
|
case 'qq':
|
|
@@ -413,6 +439,9 @@ class ComRegister {
|
|
|
413
439
|
case 'onebot':
|
|
414
440
|
bot = this.oneBot;
|
|
415
441
|
break;
|
|
442
|
+
case 'red':
|
|
443
|
+
bot = this.redBot;
|
|
444
|
+
break;
|
|
416
445
|
default: return '非法调用';
|
|
417
446
|
}
|
|
418
447
|
// 开始循环检测
|
|
@@ -451,6 +480,9 @@ class ComRegister {
|
|
|
451
480
|
case 'onebot':
|
|
452
481
|
bot = this.oneBot;
|
|
453
482
|
break;
|
|
483
|
+
case 'red':
|
|
484
|
+
bot = this.redBot;
|
|
485
|
+
break;
|
|
454
486
|
default: return '非法调用';
|
|
455
487
|
}
|
|
456
488
|
// 开始循环检测
|
|
@@ -753,6 +785,11 @@ class ComRegister {
|
|
|
753
785
|
uData = userData;
|
|
754
786
|
// 发送直播通知卡片
|
|
755
787
|
sendLiveNotifyCard(data, uData, LiveType.StartBroadcasting);
|
|
788
|
+
// 判断是否需要@全体成员
|
|
789
|
+
if (this.config.liveStartAtAll) {
|
|
790
|
+
// 发送@全体成员通知
|
|
791
|
+
bot.sendMessage(guildId, (0, jsx_runtime_1.jsx)("at", { type: "all" }));
|
|
792
|
+
}
|
|
756
793
|
}
|
|
757
794
|
else { // 还在直播
|
|
758
795
|
if (this.config.pushTime > 0) {
|
|
@@ -881,6 +918,9 @@ class ComRegister {
|
|
|
881
918
|
case 'onebot':
|
|
882
919
|
bot = this.oneBot;
|
|
883
920
|
break;
|
|
921
|
+
case 'red':
|
|
922
|
+
bot = this.redBot;
|
|
923
|
+
break;
|
|
884
924
|
default: {
|
|
885
925
|
// 本条数据被篡改,删除该条订阅
|
|
886
926
|
ctx.database.remove('bilibili', { id: sub.id });
|
|
@@ -1040,6 +1080,7 @@ class ComRegister {
|
|
|
1040
1080
|
(function (ComRegister) {
|
|
1041
1081
|
ComRegister.Config = koishi_1.Schema.object({
|
|
1042
1082
|
unlockSubLimits: koishi_1.Schema.boolean().required(),
|
|
1083
|
+
liveStartAtAll: koishi_1.Schema.boolean().required(),
|
|
1043
1084
|
pushTime: koishi_1.Schema.number().required(),
|
|
1044
1085
|
liveLoopTime: koishi_1.Schema.number().default(10),
|
|
1045
1086
|
dynamicLoopTime: koishi_1.Schema.number().default(60),
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -49,6 +49,10 @@ exports.Config = koishi_1.Schema.object({
|
|
|
49
49
|
unlockSubLimits: koishi_1.Schema.boolean()
|
|
50
50
|
.default(false)
|
|
51
51
|
.description('解锁3个订阅限制,默认只允许订阅3位UP主。订阅过多用户可能有导致IP暂时被封禁的风险'),
|
|
52
|
+
liveStartAtAll: koishi_1.Schema.boolean()
|
|
53
|
+
.default(false)
|
|
54
|
+
.experimental()
|
|
55
|
+
.description('直播开始时艾特全体成员,默认关闭'),
|
|
52
56
|
pushTime: koishi_1.Schema.number()
|
|
53
57
|
.min(0)
|
|
54
58
|
.max(12)
|
|
@@ -61,7 +65,7 @@ exports.Config = koishi_1.Schema.object({
|
|
|
61
65
|
.role('slider')
|
|
62
66
|
.step(1)
|
|
63
67
|
.default(5)
|
|
64
|
-
.description('设定每次检查动态的数量。若订阅的UP主经常在短时间内连着发多条动态可以将该值提高,若订阅的UP
|
|
68
|
+
.description('设定每次检查动态的数量。若订阅的UP主经常在短时间内连着发多条动态可以将该值提高,若订阅的UP主有置顶动态,在计算该值时应+1。默认值为5条'),
|
|
65
69
|
dynamicLoopTime: koishi_1.Schema.union(['1分钟', '2分钟', '3分钟', '5分钟'])
|
|
66
70
|
.role('')
|
|
67
71
|
.default('2分钟')
|
|
@@ -163,6 +167,7 @@ function apply(ctx, config) {
|
|
|
163
167
|
// ctx.plugin(Authority)
|
|
164
168
|
ctx.plugin(comRegister_1.default, {
|
|
165
169
|
unlockSubLimits: config.unlockSubLimits,
|
|
170
|
+
liveStartAtAll: config.liveStartAtAll,
|
|
166
171
|
pushTime: config.pushTime,
|
|
167
172
|
dynamicCheckNumber: config.dynamicCheckNumber,
|
|
168
173
|
dynamicLoopTime
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -89,6 +89,7 @@
|
|
|
89
89
|
- ver 1.0.11 修复了render渲染模式下,动态重复推送的问题,修复了没有订阅时,控制台空白提示的问题。优化了视频动态缩略图显示不全的问题,优化了部分逻辑。增强容错和增加错误提示
|
|
90
90
|
- ver 1.0.12 提供用户选择动态推送卡片字体增大的选项
|
|
91
91
|
- ver 1.0.13 修复了直播通知卡片连续发三次的bug,修复了多次调用指令 `bili login` 产生的bug
|
|
92
|
+
- ver 1.0.14 修复了获取二维码,二维码失效后会多次发生提示的bug,新增对red的支持,新增开播艾特全体成员功能,优化了部分逻辑
|
|
92
93
|
|
|
93
94
|
## 感谢
|
|
94
95
|
|