koishi-plugin-bilibili-notify 3.0.0-alpha.11 → 3.0.0-alpha.13
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 +5 -5
- package/lib/biliAPI.js +151 -128
- package/lib/comRegister.d.ts +7 -7
- package/lib/comRegister.js +619 -448
- package/lib/generateImg.d.ts +3 -2
- package/lib/generateImg.js +178 -133
- package/lib/index.d.ts +5 -5
- package/lib/index.js +146 -133
- package/package.json +3 -8
- package/readme.md +2 -0
package/lib/biliAPI.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Context, Schema, Service } from "koishi";
|
|
2
|
-
import { CookieJar } from
|
|
3
|
-
import { Notifier } from "@koishijs/plugin-notifier";
|
|
4
|
-
declare module
|
|
1
|
+
import { type Context, Schema, Service } from "koishi";
|
|
2
|
+
import { CookieJar } from "tough-cookie";
|
|
3
|
+
import type { Notifier } from "@koishijs/plugin-notifier";
|
|
4
|
+
declare module "koishi" {
|
|
5
5
|
interface Context {
|
|
6
6
|
ba: BiliAPI;
|
|
7
7
|
}
|
|
@@ -13,7 +13,7 @@ declare class BiliAPI extends Service {
|
|
|
13
13
|
apiConfig: BiliAPI.Config;
|
|
14
14
|
loginData: any;
|
|
15
15
|
loginNotifier: Notifier;
|
|
16
|
-
refreshCookieTimer:
|
|
16
|
+
refreshCookieTimer: () => void;
|
|
17
17
|
loginInfoIsLoaded: boolean;
|
|
18
18
|
constructor(ctx: Context, config: BiliAPI.Config);
|
|
19
19
|
protected start(): void | Promise<void>;
|
package/lib/biliAPI.js
CHANGED
|
@@ -9,12 +9,9 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
9
9
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
/* eslint-disable @typescript-eslint/ban-types */
|
|
13
|
-
/* eslint-disable @typescript-eslint/no-namespace */
|
|
14
|
-
/* eslint-disable @typescript-eslint/no-explicit-any */
|
|
15
12
|
const koishi_1 = require("koishi");
|
|
16
13
|
const md5_1 = __importDefault(require("md5"));
|
|
17
|
-
const
|
|
14
|
+
const node_crypto_1 = __importDefault(require("node:crypto"));
|
|
18
15
|
const axios_1 = __importDefault(require("axios"));
|
|
19
16
|
const tough_cookie_1 = require("tough-cookie");
|
|
20
17
|
const axios_cookiejar_support_1 = require("axios-cookiejar-support");
|
|
@@ -23,46 +20,48 @@ const luxon_1 = require("luxon");
|
|
|
23
20
|
const retry_1 = __importDefault(require("./utils/retry"));
|
|
24
21
|
const mixinKeyEncTab = [
|
|
25
22
|
46, 47, 18, 2, 53, 8, 23, 32, 15, 50, 10, 31, 58, 3, 45, 35, 27, 43, 5, 49,
|
|
26
|
-
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40,
|
|
27
|
-
|
|
28
|
-
|
|
23
|
+
33, 9, 42, 19, 29, 28, 14, 39, 12, 38, 41, 13, 37, 48, 7, 16, 24, 55, 40, 61,
|
|
24
|
+
26, 17, 0, 1, 60, 51, 30, 4, 22, 25, 54, 21, 56, 59, 6, 63, 57, 62, 11, 36,
|
|
25
|
+
20, 34, 44, 52,
|
|
29
26
|
];
|
|
30
27
|
// 在getUserInfo中检测到番剧出差的UID时,要传回的数据:
|
|
31
|
-
const bangumiTripData = {
|
|
32
|
-
const GET_USER_SPACE_DYNAMIC_LIST =
|
|
33
|
-
const GET_ALL_DYNAMIC_LIST =
|
|
34
|
-
const HAS_NEW_DYNAMIC =
|
|
35
|
-
const GET_COOKIES_INFO =
|
|
36
|
-
const GET_USER_INFO =
|
|
37
|
-
const GET_MYSELF_INFO =
|
|
38
|
-
const GET_LOGIN_QRCODE =
|
|
39
|
-
const GET_LOGIN_STATUS =
|
|
40
|
-
const GET_LIVE_ROOM_INFO =
|
|
41
|
-
const GET_MASTER_INFO =
|
|
42
|
-
const GET_TIME_NOW =
|
|
43
|
-
const GET_SERVER_UTC_TIME =
|
|
28
|
+
const bangumiTripData = { code: 0, data: { live_room: { roomid: 931774 } } };
|
|
29
|
+
const GET_USER_SPACE_DYNAMIC_LIST = "https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space";
|
|
30
|
+
const GET_ALL_DYNAMIC_LIST = "https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all";
|
|
31
|
+
const HAS_NEW_DYNAMIC = "https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/all/update";
|
|
32
|
+
const GET_COOKIES_INFO = "https://passport.bilibili.com/x/passport-login/web/cookie/info";
|
|
33
|
+
const GET_USER_INFO = "https://api.bilibili.com/x/space/wbi/acc/info";
|
|
34
|
+
const GET_MYSELF_INFO = "https://api.bilibili.com/x/member/web/account";
|
|
35
|
+
const GET_LOGIN_QRCODE = "https://passport.bilibili.com/x/passport-login/web/qrcode/generate";
|
|
36
|
+
const GET_LOGIN_STATUS = "https://passport.bilibili.com/x/passport-login/web/qrcode/poll";
|
|
37
|
+
const GET_LIVE_ROOM_INFO = "https://api.live.bilibili.com/room/v1/Room/get_info";
|
|
38
|
+
const GET_MASTER_INFO = "https://api.live.bilibili.com/live_user/v1/Master/info";
|
|
39
|
+
const GET_TIME_NOW = "https://api.bilibili.com/x/report/click/now";
|
|
40
|
+
const GET_SERVER_UTC_TIME = "https://interface.bilibili.com/serverdate.js";
|
|
44
41
|
// 最近更新UP
|
|
45
|
-
const GET_LATEST_UPDATED_UPS =
|
|
42
|
+
const GET_LATEST_UPDATED_UPS = "https://api.bilibili.com/x/polymer/web-dynamic/v1/portal";
|
|
46
43
|
// 操作
|
|
47
|
-
const MODIFY_RELATION =
|
|
48
|
-
const CREATE_GROUP =
|
|
49
|
-
const MODIFY_GROUP_MEMBER =
|
|
50
|
-
const GET_ALL_GROUP =
|
|
51
|
-
const COPY_USER_TO_GROUP =
|
|
52
|
-
const GET_RELATION_GROUP_DETAIL =
|
|
44
|
+
const MODIFY_RELATION = "https://api.bilibili.com/x/relation/modify";
|
|
45
|
+
const CREATE_GROUP = "https://api.bilibili.com/x/relation/tag/create";
|
|
46
|
+
const MODIFY_GROUP_MEMBER = "https://api.bilibili.com/x/relation/tags/addUsers";
|
|
47
|
+
const GET_ALL_GROUP = "https://api.bilibili.com/x/relation/tags";
|
|
48
|
+
const COPY_USER_TO_GROUP = "https://api.bilibili.com/x/relation/tags/copyUsers";
|
|
49
|
+
const GET_RELATION_GROUP_DETAIL = "https://api.bilibili.com/x/relation/tag";
|
|
53
50
|
// 直播
|
|
54
|
-
const GET_LIVE_ROOM_INFO_STREAM_KEY =
|
|
51
|
+
const GET_LIVE_ROOM_INFO_STREAM_KEY = "https://api.live.bilibili.com/xlive/web-room/v1/index/getDanmuInfo";
|
|
55
52
|
class BiliAPI extends koishi_1.Service {
|
|
56
|
-
static inject = [
|
|
53
|
+
static inject = ["database", "notifier"];
|
|
57
54
|
jar;
|
|
55
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
58
56
|
client;
|
|
59
57
|
apiConfig;
|
|
58
|
+
// biome-ignore lint/suspicious/noExplicitAny: <explanation>
|
|
60
59
|
loginData;
|
|
61
60
|
loginNotifier;
|
|
62
61
|
refreshCookieTimer;
|
|
63
62
|
loginInfoIsLoaded = false;
|
|
64
63
|
constructor(ctx, config) {
|
|
65
|
-
super(ctx,
|
|
64
|
+
super(ctx, "ba");
|
|
66
65
|
this.apiConfig = config;
|
|
67
66
|
}
|
|
68
67
|
start() {
|
|
@@ -79,7 +78,9 @@ class BiliAPI extends koishi_1.Service {
|
|
|
79
78
|
.slice(0, 32);
|
|
80
79
|
// 为请求参数进行 wbi 签名
|
|
81
80
|
encWbi(params, img_key, sub_key) {
|
|
82
|
-
const mixin_key = this.getMixinKey(img_key + sub_key)
|
|
81
|
+
const mixin_key = this.getMixinKey(img_key + sub_key);
|
|
82
|
+
const curr_time = Math.round(Date.now() / 1000);
|
|
83
|
+
const chr_filter = /[!'()*]/g;
|
|
83
84
|
Object.assign(params, { wts: curr_time }); // 添加 wts 字段
|
|
84
85
|
// 按照 key 重排参数
|
|
85
86
|
const query = Object.keys(params)
|
|
@@ -91,32 +92,36 @@ class BiliAPI extends koishi_1.Service {
|
|
|
91
92
|
})
|
|
92
93
|
.join("&");
|
|
93
94
|
const wbi_sign = (0, md5_1.default)(query + mixin_key); // 计算 w_rid
|
|
94
|
-
return query
|
|
95
|
+
return `${query}&w_rid=${wbi_sign}`;
|
|
95
96
|
}
|
|
96
97
|
async getWbi(params) {
|
|
97
98
|
const web_keys = await this.getWbiKeys();
|
|
98
|
-
const img_key = web_keys.img_key
|
|
99
|
+
const img_key = web_keys.img_key;
|
|
100
|
+
const sub_key = web_keys.sub_key;
|
|
99
101
|
const query = this.encWbi(params, img_key, sub_key);
|
|
100
102
|
return query;
|
|
101
103
|
}
|
|
102
104
|
encrypt(text) {
|
|
103
|
-
const iv =
|
|
104
|
-
const cipher =
|
|
105
|
+
const iv = node_crypto_1.default.randomBytes(16);
|
|
106
|
+
const cipher = node_crypto_1.default.createCipheriv("aes-256-cbc", Buffer.from(this.apiConfig.key), iv);
|
|
105
107
|
const encrypted = Buffer.concat([cipher.update(text), cipher.final()]);
|
|
106
|
-
return iv.toString(
|
|
108
|
+
return `${iv.toString("hex")}:${encrypted.toString("hex")}`;
|
|
107
109
|
}
|
|
108
110
|
decrypt(text) {
|
|
109
|
-
const textParts = text.split(
|
|
110
|
-
const iv = Buffer.from(textParts.shift(),
|
|
111
|
-
const encryptedText = Buffer.from(textParts.join(
|
|
112
|
-
const decipher =
|
|
113
|
-
const decrypted = Buffer.concat([
|
|
111
|
+
const textParts = text.split(":");
|
|
112
|
+
const iv = Buffer.from(textParts.shift(), "hex");
|
|
113
|
+
const encryptedText = Buffer.from(textParts.join(":"), "hex");
|
|
114
|
+
const decipher = node_crypto_1.default.createDecipheriv("aes-256-cbc", Buffer.from(this.apiConfig.key), iv);
|
|
115
|
+
const decrypted = Buffer.concat([
|
|
116
|
+
decipher.update(encryptedText),
|
|
117
|
+
decipher.final(),
|
|
118
|
+
]);
|
|
114
119
|
return decrypted.toString();
|
|
115
120
|
}
|
|
116
121
|
// BA API
|
|
117
122
|
async getTheUserWhoIsLiveStreaming() {
|
|
118
123
|
// 获取直播间信息流密钥
|
|
119
|
-
const { data: { live_users } } = await this.client.get(GET_LATEST_UPDATED_UPS);
|
|
124
|
+
const { data: { live_users }, } = await this.client.get(GET_LATEST_UPDATED_UPS);
|
|
120
125
|
// 返回data
|
|
121
126
|
return live_users;
|
|
122
127
|
}
|
|
@@ -134,9 +139,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
134
139
|
const timestamp = new Function(`return Date.UTC(${match[1]})`)();
|
|
135
140
|
return timestamp / 1000;
|
|
136
141
|
}
|
|
137
|
-
|
|
138
|
-
throw new Error('解析服务器时间失败!');
|
|
139
|
-
}
|
|
142
|
+
throw new Error("解析服务器时间失败!");
|
|
140
143
|
}
|
|
141
144
|
async getTimeNow() {
|
|
142
145
|
const { data } = await this.client.get(GET_TIME_NOW);
|
|
@@ -153,11 +156,11 @@ class BiliAPI extends koishi_1.Service {
|
|
|
153
156
|
const { data } = await this.client.post(MODIFY_GROUP_MEMBER, {
|
|
154
157
|
fids: mid,
|
|
155
158
|
tagids: 0,
|
|
156
|
-
csrf
|
|
159
|
+
csrf,
|
|
157
160
|
}, {
|
|
158
161
|
headers: {
|
|
159
|
-
|
|
160
|
-
}
|
|
162
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
163
|
+
},
|
|
161
164
|
});
|
|
162
165
|
return data;
|
|
163
166
|
}
|
|
@@ -168,11 +171,11 @@ class BiliAPI extends koishi_1.Service {
|
|
|
168
171
|
const { data } = await this.client.post(COPY_USER_TO_GROUP, {
|
|
169
172
|
fids: mid,
|
|
170
173
|
tagids: groupId,
|
|
171
|
-
csrf
|
|
174
|
+
csrf,
|
|
172
175
|
}, {
|
|
173
176
|
headers: {
|
|
174
|
-
|
|
175
|
-
}
|
|
177
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
178
|
+
},
|
|
176
179
|
});
|
|
177
180
|
return data;
|
|
178
181
|
}
|
|
@@ -183,17 +186,19 @@ class BiliAPI extends koishi_1.Service {
|
|
|
183
186
|
async createGroup(tag) {
|
|
184
187
|
const { data } = await this.client.post(CREATE_GROUP, {
|
|
185
188
|
tag,
|
|
186
|
-
csrf: this.getCSRF()
|
|
189
|
+
csrf: this.getCSRF(),
|
|
187
190
|
}, {
|
|
188
191
|
headers: {
|
|
189
|
-
|
|
190
|
-
}
|
|
192
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
193
|
+
},
|
|
191
194
|
});
|
|
192
195
|
return data;
|
|
193
196
|
}
|
|
194
197
|
async getAllDynamic(updateBaseline) {
|
|
195
198
|
let url = GET_ALL_DYNAMIC_LIST;
|
|
196
|
-
|
|
199
|
+
if (updateBaseline) {
|
|
200
|
+
url += `?update_baseline=${updateBaseline}`;
|
|
201
|
+
}
|
|
197
202
|
const { data } = await this.client.get(url);
|
|
198
203
|
return data;
|
|
199
204
|
}
|
|
@@ -206,11 +211,11 @@ class BiliAPI extends koishi_1.Service {
|
|
|
206
211
|
fid,
|
|
207
212
|
act: 1,
|
|
208
213
|
re_src: 11,
|
|
209
|
-
csrf: this.getCSRF()
|
|
214
|
+
csrf: this.getCSRF(),
|
|
210
215
|
}, {
|
|
211
216
|
headers: {
|
|
212
|
-
|
|
213
|
-
}
|
|
217
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
218
|
+
},
|
|
214
219
|
});
|
|
215
220
|
return data;
|
|
216
221
|
}
|
|
@@ -235,11 +240,11 @@ class BiliAPI extends koishi_1.Service {
|
|
|
235
240
|
}
|
|
236
241
|
// 获取最新的 img_key 和 sub_key
|
|
237
242
|
async getWbiKeys() {
|
|
238
|
-
const { data } = await this.client.get(
|
|
243
|
+
const { data } = await this.client.get("https://api.bilibili.com/x/web-interface/nav");
|
|
239
244
|
const { data: { wbi_img: { img_url, sub_url }, }, } = data;
|
|
240
245
|
return {
|
|
241
|
-
img_key: img_url.slice(img_url.lastIndexOf(
|
|
242
|
-
sub_key: sub_url.slice(sub_url.lastIndexOf(
|
|
246
|
+
img_key: img_url.slice(img_url.lastIndexOf("/") + 1, img_url.lastIndexOf(".")),
|
|
247
|
+
sub_key: sub_url.slice(sub_url.lastIndexOf("/") + 1, sub_url.lastIndexOf(".")),
|
|
243
248
|
};
|
|
244
249
|
}
|
|
245
250
|
async getMyselfInfo() {
|
|
@@ -262,15 +267,17 @@ class BiliAPI extends koishi_1.Service {
|
|
|
262
267
|
const { data } = await this.client.get(`${GET_MASTER_INFO}?uid=${mid}`);
|
|
263
268
|
return data;
|
|
264
269
|
}
|
|
265
|
-
disposeNotifier() {
|
|
266
|
-
this.loginNotifier
|
|
270
|
+
disposeNotifier() {
|
|
271
|
+
if (this.loginNotifier)
|
|
272
|
+
this.loginNotifier.dispose();
|
|
273
|
+
}
|
|
267
274
|
getRandomUserAgent() {
|
|
268
275
|
const userAgents = [
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
276
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
|
|
277
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/117.0.0.0 Safari/537.36",
|
|
278
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.0.0 Safari/537.36",
|
|
279
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36 Edg/123.0.0.0",
|
|
280
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/118.0.0.0 Safari/537.36",
|
|
274
281
|
];
|
|
275
282
|
const index = Math.floor(Math.random() * userAgents.length);
|
|
276
283
|
return userAgents[index];
|
|
@@ -280,16 +287,18 @@ class BiliAPI extends koishi_1.Service {
|
|
|
280
287
|
this.client = (0, axios_cookiejar_support_1.wrapper)(axios_1.default.create({
|
|
281
288
|
jar: this.jar,
|
|
282
289
|
headers: {
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
290
|
+
"Content-Type": "application/json",
|
|
291
|
+
"User-Agent": this.apiConfig.userAgent !==
|
|
292
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36"
|
|
293
|
+
? this.apiConfig.userAgent
|
|
294
|
+
: this.getRandomUserAgent(),
|
|
295
|
+
Origin: "https://www.bilibili.com",
|
|
296
|
+
Referer: "https://www.bilibili.com/",
|
|
297
|
+
},
|
|
289
298
|
}));
|
|
290
299
|
}
|
|
291
300
|
getTimeOfUTC8() {
|
|
292
|
-
return Math.floor(luxon_1.DateTime.now().setZone(
|
|
301
|
+
return Math.floor(luxon_1.DateTime.now().setZone("UTC+8").toSeconds());
|
|
293
302
|
}
|
|
294
303
|
getCookies() {
|
|
295
304
|
const cookies = JSON.stringify(this.jar.serializeSync().cookies);
|
|
@@ -301,8 +310,8 @@ class BiliAPI extends koishi_1.Service {
|
|
|
301
310
|
const cookies = this.jar.serializeSync().cookies;
|
|
302
311
|
// 将每个 cookie 对象转换为 "key=value" 形式,并用 "; " 连接起来
|
|
303
312
|
const cookieHeader = cookies
|
|
304
|
-
.map(cookie => `${cookie.key}=${cookie.value}`)
|
|
305
|
-
.join(
|
|
313
|
+
.map((cookie) => `${cookie.key}=${cookie.value}`)
|
|
314
|
+
.join("; ");
|
|
306
315
|
return cookieHeader;
|
|
307
316
|
}
|
|
308
317
|
catch (e) {
|
|
@@ -315,18 +324,19 @@ class BiliAPI extends koishi_1.Service {
|
|
|
315
324
|
}
|
|
316
325
|
async getLoginInfoFromDB() {
|
|
317
326
|
// 读取数据库获取cookies
|
|
318
|
-
const data = (await this.ctx.database.get(
|
|
327
|
+
const data = (await this.ctx.database.get("loginBili", 1))[0];
|
|
319
328
|
// 判断是否登录
|
|
320
|
-
if (data === undefined) {
|
|
329
|
+
if (data === undefined) {
|
|
330
|
+
// 没有数据则直接返回
|
|
321
331
|
// 未登录,在控制台提示
|
|
322
332
|
this.loginNotifier = this.ctx.notifier.create({
|
|
323
|
-
type:
|
|
324
|
-
content:
|
|
333
|
+
type: "warning",
|
|
334
|
+
content: "您尚未登录,将无法使用插件提供的指令",
|
|
325
335
|
});
|
|
326
336
|
// 返回空值
|
|
327
337
|
return {
|
|
328
338
|
cookies: null,
|
|
329
|
-
refresh_token: null
|
|
339
|
+
refresh_token: null,
|
|
330
340
|
};
|
|
331
341
|
}
|
|
332
342
|
// 尝试解密
|
|
@@ -340,27 +350,29 @@ class BiliAPI extends koishi_1.Service {
|
|
|
340
350
|
// 返回值
|
|
341
351
|
return {
|
|
342
352
|
cookies,
|
|
343
|
-
refresh_token: decryptedRefreshToken
|
|
353
|
+
refresh_token: decryptedRefreshToken,
|
|
344
354
|
};
|
|
345
355
|
}
|
|
346
356
|
catch (e) {
|
|
347
357
|
// 数据库被篡改,在控制台提示
|
|
348
358
|
this.loginNotifier = this.ctx.notifier.create({
|
|
349
|
-
type:
|
|
350
|
-
content:
|
|
359
|
+
type: "warning",
|
|
360
|
+
content: "数据库被篡改,请重新登录",
|
|
351
361
|
});
|
|
352
362
|
// 解密或解析失败,删除数据库登录信息
|
|
353
|
-
await this.ctx.database.remove(
|
|
363
|
+
await this.ctx.database.remove("loginBili", [1]);
|
|
354
364
|
// 返回空值
|
|
355
365
|
return {
|
|
356
366
|
cookies: null,
|
|
357
|
-
refresh_token: null
|
|
367
|
+
refresh_token: null,
|
|
358
368
|
};
|
|
359
369
|
}
|
|
360
370
|
}
|
|
361
371
|
getCSRF() {
|
|
362
372
|
// 获取csrf
|
|
363
|
-
return this.jar
|
|
373
|
+
return this.jar
|
|
374
|
+
.serializeSync()
|
|
375
|
+
.cookies.find((cookie) => cookie.key === "bili_jct").value;
|
|
364
376
|
}
|
|
365
377
|
async loadCookiesFromDatabase() {
|
|
366
378
|
// Get login info from db
|
|
@@ -372,10 +384,16 @@ class BiliAPI extends koishi_1.Service {
|
|
|
372
384
|
return;
|
|
373
385
|
}
|
|
374
386
|
// 定义CSRF Token
|
|
375
|
-
let csrf
|
|
376
|
-
|
|
387
|
+
let csrf;
|
|
388
|
+
let expires;
|
|
389
|
+
let domain;
|
|
390
|
+
let path;
|
|
391
|
+
let secure;
|
|
392
|
+
let httpOnly;
|
|
393
|
+
let sameSite;
|
|
394
|
+
for (const cookieData of cookies) {
|
|
377
395
|
// 获取key为bili_jct的值
|
|
378
|
-
if (cookieData.key ===
|
|
396
|
+
if (cookieData.key === "bili_jct") {
|
|
379
397
|
csrf = cookieData.value;
|
|
380
398
|
expires = new Date(cookieData.expires);
|
|
381
399
|
domain = cookieData.domain;
|
|
@@ -393,22 +411,22 @@ class BiliAPI extends koishi_1.Service {
|
|
|
393
411
|
path: cookieData.path,
|
|
394
412
|
secure: cookieData.secure,
|
|
395
413
|
httpOnly: cookieData.httpOnly,
|
|
396
|
-
sameSite: cookieData.sameSite
|
|
414
|
+
sameSite: cookieData.sameSite,
|
|
397
415
|
});
|
|
398
|
-
this.jar.setCookieSync(cookie, `http${cookie.secure ?
|
|
399
|
-
}
|
|
416
|
+
this.jar.setCookieSync(cookie, `http${cookie.secure ? "s" : ""}://${cookie.domain}${cookie.path}`, {});
|
|
417
|
+
}
|
|
400
418
|
// 对于某些 IP 地址,需要在 Cookie 中提供任意非空的 buvid3 字段
|
|
401
419
|
const buvid3Cookie = new tough_cookie_1.Cookie({
|
|
402
|
-
key:
|
|
403
|
-
value:
|
|
420
|
+
key: "buvid3",
|
|
421
|
+
value: "some_non_empty_value", // 设置任意非空值
|
|
404
422
|
expires, // 设置过期时间
|
|
405
423
|
domain, // 设置域名
|
|
406
424
|
path, // 设置路径
|
|
407
425
|
secure, // 设置是否为安全 cookie
|
|
408
426
|
httpOnly, // 设置是否为 HttpOnly cookie
|
|
409
|
-
sameSite // 设置 SameSite 属性
|
|
427
|
+
sameSite, // 设置 SameSite 属性
|
|
410
428
|
});
|
|
411
|
-
this.jar.setCookieSync(buvid3Cookie, `http${buvid3Cookie.secure ?
|
|
429
|
+
this.jar.setCookieSync(buvid3Cookie, `http${buvid3Cookie.secure ? "s" : ""}://${buvid3Cookie.domain}${buvid3Cookie.path}`, {});
|
|
412
430
|
// Login info is loaded
|
|
413
431
|
this.loginInfoIsLoaded = true;
|
|
414
432
|
// restart plugin check
|
|
@@ -422,15 +440,16 @@ class BiliAPI extends koishi_1.Service {
|
|
|
422
440
|
this.refreshCookieTimer();
|
|
423
441
|
// Open scheduled tasks and check if token need refresh
|
|
424
442
|
this.refreshCookieTimer = this.ctx.setInterval(async () => {
|
|
443
|
+
// 每12小时检测一次
|
|
425
444
|
// 从数据库获取登录信息
|
|
426
445
|
const { cookies, refresh_token } = await this.getLoginInfoFromDB();
|
|
427
446
|
// 判断是否有值
|
|
428
447
|
if (!cookies || !refresh_token)
|
|
429
448
|
return;
|
|
430
449
|
// 获取csrf
|
|
431
|
-
const csrf = cookies.find(cookie => {
|
|
450
|
+
const csrf = cookies.find((cookie) => {
|
|
432
451
|
// 判断key是否为bili_jct
|
|
433
|
-
if (cookie.key ===
|
|
452
|
+
if (cookie.key === "bili_jct")
|
|
434
453
|
return true;
|
|
435
454
|
}).value;
|
|
436
455
|
// 检查是否需要更新
|
|
@@ -442,8 +461,8 @@ class BiliAPI extends koishi_1.Service {
|
|
|
442
461
|
const notifyAndError = (info) => {
|
|
443
462
|
// 设置控制台通知
|
|
444
463
|
this.loginNotifier = this.ctx.notifier.create({
|
|
445
|
-
type:
|
|
446
|
-
content: info
|
|
464
|
+
type: "warning",
|
|
465
|
+
content: info,
|
|
447
466
|
});
|
|
448
467
|
// 重置为未登录状态
|
|
449
468
|
this.createNewClient();
|
|
@@ -470,7 +489,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
470
489
|
// 如果请求失败,有可能是404,直接刷新cookie
|
|
471
490
|
}
|
|
472
491
|
// 定义Key
|
|
473
|
-
const publicKey = await
|
|
492
|
+
const publicKey = await node_crypto_1.default.subtle.importKey("jwk", {
|
|
474
493
|
kty: "RSA",
|
|
475
494
|
n: "y4HdjgJHBlbaBN04VERG4qNBIFHP6a3GozCl75AihQloSWCXC5HDNgyinEnhaQ_4-gaMud_GF50elYXLlCToR9se9Z8z433U3KjM-3Yx7ptKkmQNAMggQwAVKgq3zYAoidNEWuxpkY_mAitTSRLnsJW-NCTa0bqBFF6Wm1MxgfE",
|
|
476
495
|
e: "AQAB",
|
|
@@ -478,7 +497,7 @@ class BiliAPI extends koishi_1.Service {
|
|
|
478
497
|
// 定义获取CorrespondPath方法
|
|
479
498
|
async function getCorrespondPath(timestamp) {
|
|
480
499
|
const data = new TextEncoder().encode(`refresh_${timestamp}`);
|
|
481
|
-
const encrypted = new Uint8Array(await
|
|
500
|
+
const encrypted = new Uint8Array(await node_crypto_1.default.subtle.encrypt({ name: "RSA-OAEP" }, publicKey, data));
|
|
482
501
|
return encrypted.reduce((str, c) => str + c.toString(16).padStart(2, "0"), "");
|
|
483
502
|
}
|
|
484
503
|
// 获取CorrespondPath
|
|
@@ -489,63 +508,67 @@ class BiliAPI extends koishi_1.Service {
|
|
|
489
508
|
// 创建一个虚拟的DOM元素
|
|
490
509
|
const { document } = new jsdom_1.JSDOM(refreshCsrfHtml).window;
|
|
491
510
|
// 提取标签name为1-name的内容
|
|
492
|
-
const targetElement = document.getElementById(
|
|
511
|
+
const targetElement = document.getElementById("1-name");
|
|
493
512
|
const refresh_csrf = targetElement ? targetElement.textContent : null;
|
|
494
513
|
// 发送刷新请求
|
|
495
|
-
const { data: refreshData } = await this.client.post(
|
|
514
|
+
const { data: refreshData } = await this.client.post("https://passport.bilibili.com/x/passport-login/web/cookie/refresh", {
|
|
496
515
|
csrf,
|
|
497
516
|
refresh_csrf,
|
|
498
|
-
source:
|
|
499
|
-
refresh_token: refreshToken
|
|
517
|
+
source: "main_web",
|
|
518
|
+
refresh_token: refreshToken,
|
|
500
519
|
}, {
|
|
501
520
|
headers: {
|
|
502
|
-
|
|
503
|
-
}
|
|
521
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
522
|
+
},
|
|
504
523
|
});
|
|
505
524
|
// 检查是否有其他问题
|
|
506
525
|
switch (refreshData.code) {
|
|
507
526
|
// 账号未登录
|
|
508
|
-
case -101:
|
|
527
|
+
case -101:
|
|
528
|
+
return this.createNewClient();
|
|
509
529
|
case -111: {
|
|
510
|
-
await this.ctx.database.remove(
|
|
511
|
-
notifyAndError(
|
|
530
|
+
await this.ctx.database.remove("loginBili", [1]);
|
|
531
|
+
notifyAndError("csrf 校验错误,请重新登录");
|
|
512
532
|
break;
|
|
513
533
|
}
|
|
514
534
|
case 86095: {
|
|
515
|
-
await this.ctx.database.remove(
|
|
516
|
-
notifyAndError(
|
|
535
|
+
await this.ctx.database.remove("loginBili", [1]);
|
|
536
|
+
notifyAndError("refresh_csrf 错误或 refresh_token 与 cookie 不匹配,请重新登录");
|
|
517
537
|
}
|
|
518
538
|
}
|
|
519
539
|
// 更新 新的cookies和refresh_token
|
|
520
540
|
const encryptedCookies = this.encrypt(this.getCookies());
|
|
521
541
|
const encryptedRefreshToken = this.encrypt(refreshData.data.refresh_token);
|
|
522
|
-
await this.ctx.database.upsert(
|
|
542
|
+
await this.ctx.database.upsert("loginBili", [
|
|
543
|
+
{
|
|
523
544
|
id: 1,
|
|
524
545
|
bili_cookies: encryptedCookies,
|
|
525
|
-
bili_refresh_token: encryptedRefreshToken
|
|
526
|
-
}
|
|
546
|
+
bili_refresh_token: encryptedRefreshToken,
|
|
547
|
+
},
|
|
548
|
+
]);
|
|
527
549
|
// Get new csrf from cookies
|
|
528
|
-
const newCsrf = this.jar.serializeSync().cookies.find(cookie => {
|
|
529
|
-
if (cookie.key ===
|
|
550
|
+
const newCsrf = this.jar.serializeSync().cookies.find((cookie) => {
|
|
551
|
+
if (cookie.key === "bili_jct")
|
|
530
552
|
return true;
|
|
531
553
|
}).value;
|
|
532
554
|
// Accept update
|
|
533
|
-
const { data: aceeptData } = await this.client.post(
|
|
555
|
+
const { data: aceeptData } = await this.client.post("https://passport.bilibili.com/x/passport-login/web/confirm/refresh", {
|
|
534
556
|
csrf: newCsrf,
|
|
535
|
-
refresh_token: refreshToken
|
|
557
|
+
refresh_token: refreshToken,
|
|
536
558
|
}, {
|
|
537
559
|
headers: {
|
|
538
|
-
|
|
539
|
-
}
|
|
560
|
+
"Content-Type": "application/x-www-form-urlencoded",
|
|
561
|
+
},
|
|
540
562
|
});
|
|
541
563
|
// 检查是否有其他问题
|
|
542
564
|
switch (aceeptData.code) {
|
|
543
565
|
case -111: {
|
|
544
|
-
await this.ctx.database.remove(
|
|
545
|
-
notifyAndError(
|
|
566
|
+
await this.ctx.database.remove("loginBili", [1]);
|
|
567
|
+
notifyAndError("csrf 校验失败,请重新登录");
|
|
546
568
|
break;
|
|
547
569
|
}
|
|
548
|
-
case -400:
|
|
570
|
+
case -400:
|
|
571
|
+
throw new Error("请求错误");
|
|
549
572
|
}
|
|
550
573
|
// 没有问题,cookies已更新完成
|
|
551
574
|
}
|
|
@@ -723,7 +746,7 @@ __decorate([
|
|
|
723
746
|
userAgent: koishi_1.Schema.string(),
|
|
724
747
|
key: koishi_1.Schema.string()
|
|
725
748
|
.pattern(/^[0-9a-f]{32}$/)
|
|
726
|
-
.required()
|
|
749
|
+
.required(),
|
|
727
750
|
});
|
|
728
751
|
})(BiliAPI || (BiliAPI = {}));
|
|
729
752
|
exports.default = BiliAPI;
|
package/lib/comRegister.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import { Bot, Context, FlatPick, Logger, Schema, Session } from "koishi";
|
|
2
|
-
import { Notifier } from "@koishijs/plugin-notifier";
|
|
3
|
-
import { LoginBili } from "./database";
|
|
1
|
+
import { type Bot, type Context, type FlatPick, type Logger, Schema, type Session } from "koishi";
|
|
2
|
+
import type { Notifier } from "@koishijs/plugin-notifier";
|
|
3
|
+
import type { LoginBili } from "./database";
|
|
4
4
|
declare enum LiveType {
|
|
5
5
|
NotLiveBroadcast = 0,
|
|
6
6
|
StartBroadcasting = 1,
|
|
@@ -34,7 +34,7 @@ declare class ComRegister {
|
|
|
34
34
|
qqRelatedBotList: Array<string>;
|
|
35
35
|
logger: Logger;
|
|
36
36
|
config: ComRegister.Config;
|
|
37
|
-
loginTimer:
|
|
37
|
+
loginTimer: () => void;
|
|
38
38
|
num: number;
|
|
39
39
|
rebootCount: number;
|
|
40
40
|
subNotifier: Notifier;
|
|
@@ -42,8 +42,8 @@ declare class ComRegister {
|
|
|
42
42
|
subManager: SubManager;
|
|
43
43
|
loginDBData: FlatPick<LoginBili, "dynamic_group_id">;
|
|
44
44
|
privateBot: Bot<Context>;
|
|
45
|
-
dynamicDispose:
|
|
46
|
-
liveDispose:
|
|
45
|
+
dynamicDispose: () => void;
|
|
46
|
+
liveDispose: () => void;
|
|
47
47
|
sendMsgFunc: (bot: Bot<Context, any>, channelId: string, content: any) => Promise<void>;
|
|
48
48
|
constructor(ctx: Context, config: ComRegister.Config);
|
|
49
49
|
init(config: ComRegister.Config): Promise<void>;
|
|
@@ -111,7 +111,7 @@ declare namespace ComRegister {
|
|
|
111
111
|
};
|
|
112
112
|
unlockSubLimits: boolean;
|
|
113
113
|
automaticResend: boolean;
|
|
114
|
-
liveDetectMode:
|
|
114
|
+
liveDetectMode: "API" | "WS";
|
|
115
115
|
restartPush: boolean;
|
|
116
116
|
pushTime: number;
|
|
117
117
|
liveLoopTime: number;
|