koishi-plugin-bilibili-videolink-analysis 1.3.1 → 1.3.3
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/index.d.ts +3 -1
- package/lib/index.js +166 -21
- package/lib/utils.d.ts +14 -0
- package/package.json +26 -26
- package/src/index.ts +359 -354
- package/src/utils.ts +223 -18
package/src/index.ts
CHANGED
|
@@ -1,354 +1,359 @@
|
|
|
1
|
-
import { Schema, Logger, h, Context, Session } from "koishi";
|
|
2
|
-
import { } from "koishi-plugin-puppeteer";
|
|
3
|
-
import { BilibiliParser } from "./utils";
|
|
4
|
-
|
|
5
|
-
const logger = new Logger('bilibili-videolink-analysis');
|
|
6
|
-
|
|
7
|
-
export const name = 'bilibili-videolink-analysis';
|
|
8
|
-
export const inject = {
|
|
9
|
-
optional: ['puppeteer'],
|
|
10
|
-
// required: ['BiliBiliVideo']
|
|
11
|
-
}
|
|
12
|
-
export const usage = `
|
|
13
|
-
|
|
14
|
-
<h2>→ <a href="https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis" target="_blank">可以点击这里查看详细的文档说明✨</a></h2>
|
|
15
|
-
|
|
16
|
-
✨ 只需开启插件,就可以解析B站视频的链接啦~ ✨
|
|
17
|
-
|
|
18
|
-
向bot发送B站视频链接吧~
|
|
19
|
-
|
|
20
|
-
会返回视频信息与视频哦
|
|
21
|
-
|
|
22
|
-
---
|
|
23
|
-
|
|
24
|
-
#### ⚠️ **如果你使用不了本项目,请优先检查:** ⚠️
|
|
25
|
-
#### 若无注册的指令,请关开一下[command插件](/market?keyword=commands+email:shigma10826@gmail.com)(没有指令也不影响解析别人的链接)
|
|
26
|
-
#### 视频内容是否为B站的大会员专属视频/付费视频/充电专属视频
|
|
27
|
-
#### 接入方法是否支持获取网址链接/小程序卡片消息
|
|
28
|
-
#### 接入方法是否支持视频元素的发送
|
|
29
|
-
#### 发送视频超时/其他网络问题
|
|
30
|
-
#### 视频内容被平台屏蔽/其他平台因素
|
|
31
|
-
|
|
32
|
-
---
|
|
33
|
-
|
|
34
|
-
### 注意,点播功能需要使用 puppeteer 服务
|
|
35
|
-
|
|
36
|
-
点播功能是为了方便群友一起刷B站哦~
|
|
37
|
-
|
|
38
|
-
比如:搜索 “遠い空へ” 的第二页,并且结果以语音格式返回
|
|
39
|
-
|
|
40
|
-
示例:\`点播 遠い空へ -a -p 2\`
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
---
|
|
44
|
-
|
|
45
|
-
### 特别鸣谢 💖
|
|
46
|
-
|
|
47
|
-
特别鸣谢以下项目的支持:
|
|
48
|
-
|
|
49
|
-
- [@summonhim/koishi-plugin-bili-parser](/market?keyword=bili-parser)
|
|
50
|
-
|
|
51
|
-
---
|
|
52
|
-
|
|
53
|
-
`;
|
|
54
|
-
|
|
55
|
-
export interface Config {
|
|
56
|
-
demand: boolean;
|
|
57
|
-
timeout?: number;
|
|
58
|
-
point?: [number, number];
|
|
59
|
-
enable?: boolean;
|
|
60
|
-
enablebilianalysis: boolean;
|
|
61
|
-
videoParseMode: string[];
|
|
62
|
-
waitTip_Switch?: string | null;
|
|
63
|
-
videoParseComponents: string[];
|
|
64
|
-
BVnumberParsing: boolean;
|
|
65
|
-
MinimumTimeInterval: number;
|
|
66
|
-
Minimumduration: number;
|
|
67
|
-
Minimumduration_tip: 'return' | { tipcontent: string; tipanalysis: boolean } | null;
|
|
68
|
-
Maximumduration: number;
|
|
69
|
-
Maximumduration_tip: 'return' | { tipcontent: string; tipanalysis: boolean } | null;
|
|
70
|
-
parseLimit: number;
|
|
71
|
-
useNumeral: boolean;
|
|
72
|
-
showError: boolean;
|
|
73
|
-
bVideoIDPreference: "bv" | "av";
|
|
74
|
-
bVideo_area: string;
|
|
75
|
-
bVideoShowLink: boolean;
|
|
76
|
-
bVideoShowIntroductionTofixed: number;
|
|
77
|
-
isfigure: boolean;
|
|
78
|
-
filebuffer: boolean;
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
.
|
|
117
|
-
|
|
118
|
-
.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
Schema.const('
|
|
123
|
-
Schema.const('
|
|
124
|
-
|
|
125
|
-
.
|
|
126
|
-
|
|
127
|
-
.
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if (!config.videoParseMode.includes('
|
|
201
|
-
return next();
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
.
|
|
230
|
-
.
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
await page.
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
//
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
overlay
|
|
272
|
-
overlay.style.
|
|
273
|
-
overlay.style.
|
|
274
|
-
overlay.style.
|
|
275
|
-
overlay.style.
|
|
276
|
-
overlay.style.
|
|
277
|
-
overlay.style.
|
|
278
|
-
overlay.style.
|
|
279
|
-
overlay.
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
bilibiliParser.logInfo(
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
1
|
+
import { Schema, Logger, h, Context, Session } from "koishi";
|
|
2
|
+
import { } from "koishi-plugin-puppeteer";
|
|
3
|
+
import { BilibiliParser } from "./utils";
|
|
4
|
+
|
|
5
|
+
const logger = new Logger('bilibili-videolink-analysis');
|
|
6
|
+
|
|
7
|
+
export const name = 'bilibili-videolink-analysis';
|
|
8
|
+
export const inject = {
|
|
9
|
+
optional: ['puppeteer'],
|
|
10
|
+
// required: ['BiliBiliVideo']
|
|
11
|
+
}
|
|
12
|
+
export const usage = `
|
|
13
|
+
|
|
14
|
+
<h2>→ <a href="https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis" target="_blank">可以点击这里查看详细的文档说明✨</a></h2>
|
|
15
|
+
|
|
16
|
+
✨ 只需开启插件,就可以解析B站视频的链接啦~ ✨
|
|
17
|
+
|
|
18
|
+
向bot发送B站视频链接吧~
|
|
19
|
+
|
|
20
|
+
会返回视频信息与视频哦
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
#### ⚠️ **如果你使用不了本项目,请优先检查:** ⚠️
|
|
25
|
+
#### 若无注册的指令,请关开一下[command插件](/market?keyword=commands+email:shigma10826@gmail.com)(没有指令也不影响解析别人的链接)
|
|
26
|
+
#### 视频内容是否为B站的大会员专属视频/付费视频/充电专属视频
|
|
27
|
+
#### 接入方法是否支持获取网址链接/小程序卡片消息
|
|
28
|
+
#### 接入方法是否支持视频元素的发送
|
|
29
|
+
#### 发送视频超时/其他网络问题
|
|
30
|
+
#### 视频内容被平台屏蔽/其他平台因素
|
|
31
|
+
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
### 注意,点播功能需要使用 puppeteer 服务
|
|
35
|
+
|
|
36
|
+
点播功能是为了方便群友一起刷B站哦~
|
|
37
|
+
|
|
38
|
+
比如:搜索 “遠い空へ” 的第二页,并且结果以语音格式返回
|
|
39
|
+
|
|
40
|
+
示例:\`点播 遠い空へ -a -p 2\`
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
### 特别鸣谢 💖
|
|
46
|
+
|
|
47
|
+
特别鸣谢以下项目的支持:
|
|
48
|
+
|
|
49
|
+
- [@summonhim/koishi-plugin-bili-parser](/market?keyword=bili-parser)
|
|
50
|
+
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
`;
|
|
54
|
+
|
|
55
|
+
export interface Config {
|
|
56
|
+
demand: boolean;
|
|
57
|
+
timeout?: number;
|
|
58
|
+
point?: [number, number];
|
|
59
|
+
enable?: boolean;
|
|
60
|
+
enablebilianalysis: boolean;
|
|
61
|
+
videoParseMode: string[];
|
|
62
|
+
waitTip_Switch?: string | null;
|
|
63
|
+
videoParseComponents: string[];
|
|
64
|
+
BVnumberParsing: boolean;
|
|
65
|
+
MinimumTimeInterval: number;
|
|
66
|
+
Minimumduration: number;
|
|
67
|
+
Minimumduration_tip: 'return' | { tipcontent: string; tipanalysis: boolean } | null;
|
|
68
|
+
Maximumduration: number;
|
|
69
|
+
Maximumduration_tip: 'return' | { tipcontent: string; tipanalysis: boolean } | null;
|
|
70
|
+
parseLimit: number;
|
|
71
|
+
useNumeral: boolean;
|
|
72
|
+
showError: boolean;
|
|
73
|
+
bVideoIDPreference: "bv" | "av";
|
|
74
|
+
bVideo_area: string;
|
|
75
|
+
bVideoShowLink: boolean;
|
|
76
|
+
bVideoShowIntroductionTofixed: number;
|
|
77
|
+
isfigure: boolean;
|
|
78
|
+
filebuffer: boolean;
|
|
79
|
+
maxFileSizeMB: number;
|
|
80
|
+
middleware: boolean;
|
|
81
|
+
userAgent: string;
|
|
82
|
+
pageclose: boolean;
|
|
83
|
+
loggerinfo: boolean;
|
|
84
|
+
loggerinfofulljson: boolean;
|
|
85
|
+
bufferDelay: number;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export const Config = Schema.intersect([
|
|
89
|
+
Schema.object({
|
|
90
|
+
demand: Schema.boolean().default(true).description("开启点播指令功能<br>`其实点播登录不登录 都搜不准,登录只是写着玩的`"),
|
|
91
|
+
}).description('点播设置(需要puppeteer服务)'),
|
|
92
|
+
Schema.union([
|
|
93
|
+
Schema.object({
|
|
94
|
+
demand: Schema.const(false).required(),
|
|
95
|
+
}),
|
|
96
|
+
Schema.object({
|
|
97
|
+
demand: Schema.const(true),
|
|
98
|
+
timeout: Schema.number().role('slider').min(1).max(300).step(1).default(60).description('指定播放视频的输入时限。`单位 秒`'),
|
|
99
|
+
point: Schema.tuple([Number, Number]).description('序号标注位置。分别表示`距离顶部 距离左侧`的百分比').default([50, 50]),
|
|
100
|
+
enable: Schema.boolean().description('是否开启自动解析`选择对应视频 会自动解析视频内容`').default(true),
|
|
101
|
+
}),
|
|
102
|
+
]),
|
|
103
|
+
|
|
104
|
+
Schema.object({
|
|
105
|
+
enablebilianalysis: Schema.boolean().default(true).description("开启解析功能<br>`关闭后,解析功能将关闭`"),
|
|
106
|
+
}).description('视频解析 - 功能开关'),
|
|
107
|
+
Schema.union([
|
|
108
|
+
Schema.object({
|
|
109
|
+
enablebilianalysis: Schema.const(true),
|
|
110
|
+
waitTip_Switch: Schema.union([
|
|
111
|
+
Schema.const(null).description('不返回文字提示'),
|
|
112
|
+
Schema.string().description('返回文字提示(请在右侧填写文字内容)').default('正在解析B站链接...'),
|
|
113
|
+
]).description("是否返回等待提示。开启后,会发送`等待提示语`"),
|
|
114
|
+
videoParseMode: Schema.array(Schema.union([
|
|
115
|
+
Schema.const('link').description('解析链接'),
|
|
116
|
+
Schema.const('card').description('解析哔哩哔哩分享卡片'),
|
|
117
|
+
]))
|
|
118
|
+
.default(['link', 'card'])
|
|
119
|
+
.role('checkbox')
|
|
120
|
+
.description('选择解析来源'),
|
|
121
|
+
videoParseComponents: Schema.array(Schema.union([
|
|
122
|
+
Schema.const('log').description('记录日志'),
|
|
123
|
+
Schema.const('text').description('返回图文'),
|
|
124
|
+
Schema.const('link').description('返回视频直链'),
|
|
125
|
+
Schema.const('video').description('返回视频'),
|
|
126
|
+
]))
|
|
127
|
+
.default(['text', 'video'])
|
|
128
|
+
.role('checkbox')
|
|
129
|
+
.description('选择要返回的内容组件'),
|
|
130
|
+
BVnumberParsing: Schema.boolean().default(true).description("是否允许根据`独立的BV、AV号`解析视频 `开启后,可以通过视频的BV、AV号解析视频。` <br> [触发说明见README](https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis)"),
|
|
131
|
+
MinimumTimeInterval: Schema.number().default(180).description("若干`秒`内 不再处理相同链接 `防止多bot互相触发 导致的刷屏/性能浪费`").min(1),
|
|
132
|
+
Minimumduration: Schema.number().default(0).description("允许解析的视频最小时长(分钟)`低于这个时长 就不会发视频内容`").min(0),
|
|
133
|
+
Minimumduration_tip: Schema.union([
|
|
134
|
+
Schema.const('return').description('不返回文字提示'),
|
|
135
|
+
Schema.object({
|
|
136
|
+
tipcontent: Schema.string().default('视频太短啦!不看不看~').description("文字提示内容"),
|
|
137
|
+
tipanalysis: Schema.boolean().default(true).description("是否进行图文解析(不会返回视频链接)"),
|
|
138
|
+
}).description('返回文字提示'),
|
|
139
|
+
Schema.const(null),
|
|
140
|
+
]).description("对`过短视频`的文字提示内容").default(null),
|
|
141
|
+
Maximumduration: Schema.number().default(25).description("允许解析的视频最大时长(分钟)`超过这个时长 就不会发视频内容`").min(1),
|
|
142
|
+
Maximumduration_tip: Schema.union([
|
|
143
|
+
Schema.const('return').description('不返回文字提示'),
|
|
144
|
+
Schema.object({
|
|
145
|
+
tipcontent: Schema.string().default('视频太长啦!内容还是去B站看吧~').description("文字提示内容"),
|
|
146
|
+
tipanalysis: Schema.boolean().default(true).description("是否进行图文解析(不会返回视频链接)"),
|
|
147
|
+
}).description('返回文字提示'),
|
|
148
|
+
Schema.const(null),
|
|
149
|
+
]).description("对`过长视频`的文字提示内容").default(null),
|
|
150
|
+
parseLimit: Schema.number().default(3).description("单对话多链接解析上限").hidden(),
|
|
151
|
+
useNumeral: Schema.boolean().default(true).description("使用格式化数字").hidden(),
|
|
152
|
+
showError: Schema.boolean().default(false).description("当链接不正确时提醒发送者").hidden(),
|
|
153
|
+
bVideoIDPreference: Schema.union([
|
|
154
|
+
Schema.const("bv").description("BV 号"),
|
|
155
|
+
Schema.const("av").description("AV 号"),
|
|
156
|
+
]).default("bv").description("ID 偏好").hidden(),
|
|
157
|
+
bVideo_area: Schema.string().role('textarea', { rows: [8, 16] })
|
|
158
|
+
.default("${标题} ${tab} ${UP主}\n${简介}\n点赞:${点赞} ${tab} 投币:${投币}\n收藏:${收藏} ${tab} 转发:${转发}\n观看:${观看} ${tab} 弹幕:${弹幕}\n${~~~}\n${封面}")
|
|
159
|
+
.description(`图文解析的返回格式<br>
|
|
160
|
+
注意变量格式,以及变量名称。<br>比如 \`\${标题}\` 不可以变成\`\${标题123}\`,你可以直接删掉但是不能修改变量名称哦<br>
|
|
161
|
+
当然变量也不能无中生有,下面的默认值内容 就是所有变量了,你仅可以删去变量 或者修改变量之外的格式。<br>
|
|
162
|
+
· 特殊变量\`\${~~~}\`表示分割线,会把上下内容分为两个信息单独发送。\`\${tab}\`表示制表符。`),
|
|
163
|
+
bVideoShowLink: Schema.boolean().default(false).description("在末尾显示视频的链接地址 `开启可能会导致其他bot循环解析`"),
|
|
164
|
+
bVideoShowIntroductionTofixed: Schema.number().default(50).description("视频的`简介`最大的字符长度<br>超出部分会使用 `...` 代替"),
|
|
165
|
+
isfigure: Schema.boolean().default(false).description("是否开启合并转发 `仅支持 onebot 适配器` 其他平台开启 无效").experimental(),
|
|
166
|
+
filebuffer: Schema.boolean().default(true).description("是否将视频链接下载后再发送 (以解决部分onebot协议端的问题)<br>否则使用视频直链发送").experimental(),
|
|
167
|
+
maxFileSizeMB: Schema.number().default(50).description("文件缓冲最大大小(MB)<br>超过此大小的视频将使用直链发送,避免内存溢出<br>设置为0表示不限制").min(0).max(200),
|
|
168
|
+
bufferDelay: Schema.number().default(5).description("消息接收缓冲延迟(秒)<br>收到链接后等待指定时间,收集同时发送的多个链接后再逐个处理").min(0).max(30),
|
|
169
|
+
middleware: Schema.boolean().default(false).description("前置中间件模式"),
|
|
170
|
+
userAgent: Schema.string().description("所有 API 请求所用的 User-Agent").default("Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36"),
|
|
171
|
+
}),
|
|
172
|
+
Schema.object({
|
|
173
|
+
enablebilianalysis: Schema.const(false).required(),
|
|
174
|
+
}),
|
|
175
|
+
]),
|
|
176
|
+
|
|
177
|
+
Schema.object({
|
|
178
|
+
pageclose: Schema.boolean().default(true).description("自动`page.close()`<br>非开发者请勿改动").experimental(),
|
|
179
|
+
loggerinfo: Schema.boolean().default(false).description("日志调试输出 `日常使用无需开启`<br>非开发者请勿改动").experimental(),
|
|
180
|
+
loggerinfofulljson: Schema.boolean().default(false).description("打印完整的机器人发送的json输出").experimental(),
|
|
181
|
+
}).description("开发者选项"),
|
|
182
|
+
]);
|
|
183
|
+
|
|
184
|
+
export function apply(ctx: Context, config: Config) {
|
|
185
|
+
const bilibiliParser = new BilibiliParser(ctx, config, logger);
|
|
186
|
+
|
|
187
|
+
if (config.enablebilianalysis) {
|
|
188
|
+
ctx.middleware(async (session, next) => {
|
|
189
|
+
// 尝试解析JSON卡片
|
|
190
|
+
let isCard = false;
|
|
191
|
+
try {
|
|
192
|
+
if (session.stripped.content.startsWith('<json data=')) {
|
|
193
|
+
isCard = true;
|
|
194
|
+
}
|
|
195
|
+
} catch (e) {
|
|
196
|
+
// Not a valid JSON card
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
if (isCard) {
|
|
200
|
+
if (!config.videoParseMode.includes('card')) {
|
|
201
|
+
return next();
|
|
202
|
+
}
|
|
203
|
+
} else {
|
|
204
|
+
if (!config.videoParseMode.includes('link')) {
|
|
205
|
+
return next();
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
let sessioncontent = session.stripped.content;
|
|
210
|
+
if (config.BVnumberParsing) {
|
|
211
|
+
const bvUrls = bilibiliParser.convertBVToUrl(sessioncontent);
|
|
212
|
+
if (bvUrls.length > 0) {
|
|
213
|
+
sessioncontent += '\n' + bvUrls.join('\n');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const links = await bilibiliParser.isProcessLinks(sessioncontent);
|
|
218
|
+
if (links) {
|
|
219
|
+
// 直接将整个 session 加入队列,在队列中串行处理
|
|
220
|
+
await bilibiliParser.queueSession(session, sessioncontent);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
return next();
|
|
224
|
+
}, config.middleware);
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
if (config.demand) {
|
|
228
|
+
ctx.command('B站点播 [keyword]', '点播B站视频')
|
|
229
|
+
.option('video', '-v 解析返回视频')
|
|
230
|
+
.option('audio', '-a 解析返回语音')
|
|
231
|
+
.option('link', '-l 解析返回链接')
|
|
232
|
+
.option('page', '-p <page:number> 指定页数', { fallback: '1' })
|
|
233
|
+
.example('B站点播 遠い空へ -v')
|
|
234
|
+
.action(async ({ options, session }, keyword) => {
|
|
235
|
+
if (!keyword) {
|
|
236
|
+
await session.send(h.text('告诉我 你想要点播的关键词吧~'))
|
|
237
|
+
keyword = await session.prompt(30 * 1000)
|
|
238
|
+
}
|
|
239
|
+
const url = `https://search.bilibili.com/video?keyword=${encodeURIComponent(keyword)}&page=${options.page}&o=30`
|
|
240
|
+
const page = await ctx.puppeteer.page()
|
|
241
|
+
|
|
242
|
+
await page.goto(url, {
|
|
243
|
+
waitUntil: 'networkidle2'
|
|
244
|
+
})
|
|
245
|
+
|
|
246
|
+
await page.addStyleTag({
|
|
247
|
+
content: `
|
|
248
|
+
div.bili-header,
|
|
249
|
+
div.login-tip,
|
|
250
|
+
div.v-popover,
|
|
251
|
+
div.right-entry__outside {
|
|
252
|
+
display: none !important;
|
|
253
|
+
}
|
|
254
|
+
`
|
|
255
|
+
})
|
|
256
|
+
// 获取视频列表并为每个视频元素添加序号
|
|
257
|
+
const videos = await page.evaluate((point: [number, number]) => {
|
|
258
|
+
const items = Array.from(document.querySelectorAll('.video-list-item:not([style*="display: none"])'))
|
|
259
|
+
return items.map((item, index) => {
|
|
260
|
+
const link = item.querySelector('a')
|
|
261
|
+
const href = link?.getAttribute('href') || ''
|
|
262
|
+
const idMatch = href.match(/\/video\/(BV\w+)\//)
|
|
263
|
+
const id = idMatch ? idMatch[1] : ''
|
|
264
|
+
|
|
265
|
+
if (!id) {
|
|
266
|
+
// 如果没有提取到视频ID,隐藏这个元素
|
|
267
|
+
const htmlElement = item as HTMLElement
|
|
268
|
+
htmlElement.style.display = 'none'
|
|
269
|
+
} else {
|
|
270
|
+
// 创建一个包含序号的元素,并将其插入到视频元素的正中央
|
|
271
|
+
const overlay = document.createElement('div')
|
|
272
|
+
overlay.style.position = 'absolute'
|
|
273
|
+
overlay.style.top = `${point[0]}%`
|
|
274
|
+
overlay.style.left = `${point[1]}%`
|
|
275
|
+
overlay.style.transform = 'translate(-50%, -50%)'
|
|
276
|
+
overlay.style.fontSize = '48px'
|
|
277
|
+
overlay.style.fontWeight = 'bold'
|
|
278
|
+
overlay.style.color = 'black'
|
|
279
|
+
overlay.style.zIndex = '10'
|
|
280
|
+
overlay.style.backgroundColor = 'rgba(255, 255, 255, 0.7)' // 半透明白色背景,确保数字清晰可见
|
|
281
|
+
overlay.style.padding = '10px'
|
|
282
|
+
overlay.style.borderRadius = '8px'
|
|
283
|
+
overlay.textContent = `${index + 1}` // 序号
|
|
284
|
+
|
|
285
|
+
// 确保父元素有 `position: relative` 以正确定位
|
|
286
|
+
const videoElement = item as HTMLElement
|
|
287
|
+
videoElement.style.position = 'relative'
|
|
288
|
+
videoElement.appendChild(overlay)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return { id }
|
|
292
|
+
}).filter(video => video.id)
|
|
293
|
+
}, config.point) // 传递配置的 point 参数
|
|
294
|
+
|
|
295
|
+
bilibiliParser.logInfo(options)
|
|
296
|
+
bilibiliParser.logInfo(`共找到 ${videos.length} 个视频:`)
|
|
297
|
+
videos.forEach((video: any, index: number) => {
|
|
298
|
+
bilibiliParser.logInfo(`序号 ${index + 1}: ID - ${video.id}`)
|
|
299
|
+
})
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
if (videos.length === 0) {
|
|
303
|
+
await page.close()
|
|
304
|
+
await session.send(h.text('未找到相关视频。'))
|
|
305
|
+
return
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// 动态调整窗口大小以适应视频数量
|
|
309
|
+
const viewportHeight = 200 + videos.length * 100
|
|
310
|
+
await page.setViewport({
|
|
311
|
+
width: 1440,
|
|
312
|
+
height: viewportHeight
|
|
313
|
+
})
|
|
314
|
+
bilibiliParser.logInfo("窗口:宽度:")
|
|
315
|
+
bilibiliParser.logInfo(1440)
|
|
316
|
+
|
|
317
|
+
bilibiliParser.logInfo("窗口:高度:")
|
|
318
|
+
bilibiliParser.logInfo(viewportHeight)
|
|
319
|
+
let msg: any;
|
|
320
|
+
|
|
321
|
+
// 截图
|
|
322
|
+
const videoListElement = await page.$('.video-list.row')
|
|
323
|
+
if (videoListElement) {
|
|
324
|
+
const imgBuf = await videoListElement.screenshot({
|
|
325
|
+
captureBeyondViewport: false
|
|
326
|
+
}) as Buffer
|
|
327
|
+
msg = h.image(imgBuf, 'image/png')
|
|
328
|
+
}
|
|
329
|
+
if (page && config.pageclose) {
|
|
330
|
+
await page.close()
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// 发送截图
|
|
334
|
+
await session.send(msg + h.text(`请选择视频的序号:`))
|
|
335
|
+
// 等待用户输入
|
|
336
|
+
const userChoice = await session.prompt(config.timeout * 1000)
|
|
337
|
+
const choiceIndex = parseInt(userChoice) - 1
|
|
338
|
+
if (isNaN(choiceIndex) || choiceIndex < 0 || choiceIndex >= videos.length) {
|
|
339
|
+
await session.send(h.text('输入无效,请输入正确的序号。'))
|
|
340
|
+
return
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
// 返回用户选择的视频ID
|
|
344
|
+
const chosenVideo = videos[choiceIndex]
|
|
345
|
+
|
|
346
|
+
bilibiliParser.logInfo(`渲染序号设置\noverlay.style.top = ${config.point[0]}% \noverlay.style.left = ${config.point[1]}%`)
|
|
347
|
+
bilibiliParser.logInfo(`用户选择了序号 ${choiceIndex + 1}: ID - ${chosenVideo.id}`)
|
|
348
|
+
|
|
349
|
+
// 开启自动解析了
|
|
350
|
+
if (config.enable) {
|
|
351
|
+
const link = { type: 'Video', id: chosenVideo.id };
|
|
352
|
+
const ret = await bilibiliParser.extractLinks(session, [link]); // 提取链接
|
|
353
|
+
if (ret && !bilibiliParser.isLinkProcessedRecently(ret, session.channelId)) {
|
|
354
|
+
await bilibiliParser.processVideoFromLink(session, ret, options); // 加入缓冲队列
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
})
|
|
358
|
+
}
|
|
359
|
+
}
|