koishi-plugin-bilibili-videolink-analysis 1.1.4 → 1.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/index.js +869 -893
- package/package.json +27 -27
- package/readme.md +281 -281
package/lib/index.js
CHANGED
|
@@ -1,894 +1,870 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.apply = exports.usage = exports.Config = exports.name = exports.inject = void 0;
|
|
4
|
-
const koishi_1 = require("koishi");
|
|
5
|
-
const { Schema, Logger, h } = require("koishi");
|
|
6
|
-
const logger = new Logger('bilibili-videolink-analysis');
|
|
7
|
-
exports.name = 'bilibili-videolink-analysis';
|
|
8
|
-
exports.inject = {
|
|
9
|
-
optional: ['puppeteer'],
|
|
10
|
-
//required: ['BiliBiliVideo']
|
|
11
|
-
}
|
|
12
|
-
exports.usage = `
|
|
13
|
-
|
|
14
|
-
<h1>→ <a href="https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis" target="_blank">可以点击这里查看详细的文档说明✨</a></h1>
|
|
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
|
-
exports.Config = Schema.intersect([
|
|
56
|
-
Schema.object({
|
|
57
|
-
demand: Schema.boolean().default(true).description("开启点播指令功能"),
|
|
58
|
-
timeout: Schema.number().role('slider').min(1).max(300).step(1).default(60).description('指定播放视频的输入时限。`单位 秒`'),
|
|
59
|
-
point: Schema.tuple([Number, Number]).description('序号标注位置。分别表示`距离顶部 距离左侧`的百分比').default([50, 50]),
|
|
60
|
-
enable: Schema.boolean().description('是否开启自动解析`选择对应视频 会自动解析视频内容`').default(true),
|
|
61
|
-
}).description('点播设置(需要puppeteer服务)'),
|
|
62
|
-
|
|
63
|
-
Schema.object({
|
|
64
|
-
waitTip_Switch: Schema.union([
|
|
65
|
-
Schema.const().description('不返回文字提示'),
|
|
66
|
-
Schema.string().description('返回文字提示(请在右侧填写文字内容)'),
|
|
67
|
-
]).description("是否返回等待提示。开启后,会发送`等待提示语`"),
|
|
68
|
-
linktextParsing: Schema.boolean().default(true).description("是否返回 视频图文数据 `开启后,才发送视频数据的图文解析。`"),
|
|
69
|
-
VideoParsing_ToLink: Schema.union([
|
|
70
|
-
Schema.const('1').description('不返回视频/视频直链'),
|
|
71
|
-
Schema.const('2').description('仅返回视频'),
|
|
72
|
-
Schema.const('3').description('仅返回视频直链'),
|
|
73
|
-
Schema.const('4').description('返回视频和视频直链'),
|
|
74
|
-
Schema.const('5').description('返回视频,仅在日志记录视频直链'),
|
|
75
|
-
]).role('radio').default('2').description("是否返回` 视频/视频直链 `"),
|
|
76
|
-
//Video_ClarityPriority: Schema.union([
|
|
77
|
-
// Schema.const('1').description('低清晰度优先(低清晰度的视频发得快一点)'),
|
|
78
|
-
// Schema.const('2').description('高清晰度优先(清晰的还是去B站看吧)'),
|
|
79
|
-
//]).role('radio').default('1').description("发送的视频清晰度优先策略"),
|
|
80
|
-
BVnumberParsing: Schema.boolean().default(true).description("是否允许根据`独立的BV号`解析视频 `开启后,可以通过视频的BV号解析视频。` <br> [触发说明见README](https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis)"),
|
|
81
|
-
Maximumduration: Schema.number().default(25).description("允许解析的视频最大时长(分钟)`超过这个时长 就不会发视频`").min(1),
|
|
82
|
-
Maximumduration_tip: Schema.union([
|
|
83
|
-
Schema.const('不返回文字提示').description('不返回文字提示'),
|
|
84
|
-
Schema.string().description('返回文字提示(请在右侧填写文字内容)').default('视频太长啦!还是去B站看吧~'),
|
|
85
|
-
]).description("对过长视频的文字提示内容").default('视频太长啦!还是去B站看吧~'),
|
|
86
|
-
MinimumTimeInterval: Schema.number().default(180).description("若干`秒`内 不再处理相同链接 `防止多bot互相触发 导致的刷屏/性能浪费`").min(1),
|
|
87
|
-
}).description("基础设置"),
|
|
88
|
-
|
|
89
|
-
Schema.object({
|
|
90
|
-
parseLimit: Schema.number().default(3).description("单对话多链接解析上限").hidden(),
|
|
91
|
-
useNumeral: Schema.boolean().default(true).description("使用格式化数字").hidden(),
|
|
92
|
-
showError: Schema.boolean().default(false).description("当链接不正确时提醒发送者").hidden(),
|
|
93
|
-
bVideoIDPreference: Schema.union([
|
|
94
|
-
Schema.const("bv").description("BV 号"),
|
|
95
|
-
Schema.const("av").description("AV 号"),
|
|
96
|
-
]).default("bv").description("ID 偏好").hidden(),
|
|
97
|
-
bVideoImage: Schema.boolean().default(true).description("显示封面"),
|
|
98
|
-
bVideoOwner: Schema.boolean().default(true).description("显示 UP 主"),
|
|
99
|
-
bVideoDesc: Schema.boolean().default(false).description("显示简介`有的简介真的很长`"),
|
|
100
|
-
bVideoStat: Schema.boolean().default(true).description("显示状态(*三连数据*)"),
|
|
101
|
-
bVideoExtraStat: Schema.boolean().default(true).description("显示额外状态(*弹幕&观看*)"),
|
|
102
|
-
bVideoShowLink: Schema.boolean().default(false).description("显示视频链接`开启可能会导致其他bot循环解析`"),
|
|
103
|
-
}).description("链接的图文解析设置"),
|
|
104
|
-
|
|
105
|
-
Schema.object({
|
|
106
|
-
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"),
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
const
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
await
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
await
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
await
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
const
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
await page.
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
await
|
|
196
|
-
await
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
.option('
|
|
223
|
-
.
|
|
224
|
-
.
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
div.
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
const
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
overlay
|
|
269
|
-
overlay.style.
|
|
270
|
-
overlay.style.
|
|
271
|
-
overlay.style.
|
|
272
|
-
overlay.style.
|
|
273
|
-
overlay.style.
|
|
274
|
-
overlay.style.
|
|
275
|
-
overlay.style.
|
|
276
|
-
overlay.style.
|
|
277
|
-
overlay.
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
videoElement
|
|
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
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
await
|
|
325
|
-
|
|
326
|
-
//
|
|
327
|
-
await session.send(
|
|
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
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
.option('
|
|
361
|
-
.
|
|
362
|
-
.
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
const
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
overlay
|
|
389
|
-
overlay.style.
|
|
390
|
-
overlay.style.
|
|
391
|
-
overlay.style.
|
|
392
|
-
overlay.style.
|
|
393
|
-
overlay.style.
|
|
394
|
-
overlay.style.
|
|
395
|
-
overlay.style.
|
|
396
|
-
overlay.style.
|
|
397
|
-
overlay.
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
msg
|
|
430
|
-
|
|
431
|
-
await page.
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
await
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
let
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
ret =
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
}
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
//
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
const
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
return;
|
|
562
|
-
}
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
case '
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
break;
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
}
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
}
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
}
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
*
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
*
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
*
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
break;
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
*
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
{
|
|
779
|
-
pattern: /
|
|
780
|
-
type: "
|
|
781
|
-
},
|
|
782
|
-
{
|
|
783
|
-
pattern: /
|
|
784
|
-
type: "
|
|
785
|
-
},
|
|
786
|
-
{
|
|
787
|
-
pattern: /
|
|
788
|
-
type: "
|
|
789
|
-
},
|
|
790
|
-
{
|
|
791
|
-
pattern: /
|
|
792
|
-
type: "
|
|
793
|
-
},
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
constructor(ctx, config) {
|
|
871
|
-
this.ctx = ctx;
|
|
872
|
-
this.config = config;
|
|
873
|
-
}
|
|
874
|
-
/**
|
|
875
|
-
* 根据短链接重定向获取正常链接
|
|
876
|
-
* @param id 短链接 ID
|
|
877
|
-
* @returns 正常链接
|
|
878
|
-
*/
|
|
879
|
-
async get_redir_url(id) {
|
|
880
|
-
var data = await this.ctx.http.get("https://b23.tv/" + id, {
|
|
881
|
-
redirect: "manual",
|
|
882
|
-
headers: {
|
|
883
|
-
"User-Agent": this.config.userAgent,
|
|
884
|
-
},
|
|
885
|
-
});
|
|
886
|
-
const match = data.match(/<a\s+(?:[^>]*?\s+)?href="([^"]*)"/i);
|
|
887
|
-
if (match)
|
|
888
|
-
return match[1];
|
|
889
|
-
else
|
|
890
|
-
return null;
|
|
891
|
-
}
|
|
892
|
-
}
|
|
893
|
-
}
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.apply = exports.usage = exports.Config = exports.name = exports.inject = void 0;
|
|
4
|
+
const koishi_1 = require("koishi");
|
|
5
|
+
const { Schema, Logger, h } = require("koishi");
|
|
6
|
+
const logger = new Logger('bilibili-videolink-analysis');
|
|
7
|
+
exports.name = 'bilibili-videolink-analysis';
|
|
8
|
+
exports.inject = {
|
|
9
|
+
optional: ['puppeteer'],
|
|
10
|
+
//required: ['BiliBiliVideo']
|
|
11
|
+
}
|
|
12
|
+
exports.usage = `
|
|
13
|
+
|
|
14
|
+
<h1>→ <a href="https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis" target="_blank">可以点击这里查看详细的文档说明✨</a></h1>
|
|
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
|
+
exports.Config = Schema.intersect([
|
|
56
|
+
Schema.object({
|
|
57
|
+
demand: Schema.boolean().default(true).description("开启点播指令功能"),
|
|
58
|
+
timeout: Schema.number().role('slider').min(1).max(300).step(1).default(60).description('指定播放视频的输入时限。`单位 秒`'),
|
|
59
|
+
point: Schema.tuple([Number, Number]).description('序号标注位置。分别表示`距离顶部 距离左侧`的百分比').default([50, 50]),
|
|
60
|
+
enable: Schema.boolean().description('是否开启自动解析`选择对应视频 会自动解析视频内容`').default(true),
|
|
61
|
+
}).description('点播设置(需要puppeteer服务)'),
|
|
62
|
+
|
|
63
|
+
Schema.object({
|
|
64
|
+
waitTip_Switch: Schema.union([
|
|
65
|
+
Schema.const().description('不返回文字提示'),
|
|
66
|
+
Schema.string().description('返回文字提示(请在右侧填写文字内容)'),
|
|
67
|
+
]).description("是否返回等待提示。开启后,会发送`等待提示语`"),
|
|
68
|
+
linktextParsing: Schema.boolean().default(true).description("是否返回 视频图文数据 `开启后,才发送视频数据的图文解析。`"),
|
|
69
|
+
VideoParsing_ToLink: Schema.union([
|
|
70
|
+
Schema.const('1').description('不返回视频/视频直链'),
|
|
71
|
+
Schema.const('2').description('仅返回视频'),
|
|
72
|
+
Schema.const('3').description('仅返回视频直链'),
|
|
73
|
+
Schema.const('4').description('返回视频和视频直链'),
|
|
74
|
+
Schema.const('5').description('返回视频,仅在日志记录视频直链'),
|
|
75
|
+
]).role('radio').default('2').description("是否返回` 视频/视频直链 `"),
|
|
76
|
+
//Video_ClarityPriority: Schema.union([
|
|
77
|
+
// Schema.const('1').description('低清晰度优先(低清晰度的视频发得快一点)'),
|
|
78
|
+
// Schema.const('2').description('高清晰度优先(清晰的还是去B站看吧)'),
|
|
79
|
+
//]).role('radio').default('1').description("发送的视频清晰度优先策略"),
|
|
80
|
+
BVnumberParsing: Schema.boolean().default(true).description("是否允许根据`独立的BV号`解析视频 `开启后,可以通过视频的BV号解析视频。` <br> [触发说明见README](https://www.npmjs.com/package/koishi-plugin-bilibili-videolink-analysis)"),
|
|
81
|
+
Maximumduration: Schema.number().default(25).description("允许解析的视频最大时长(分钟)`超过这个时长 就不会发视频`").min(1),
|
|
82
|
+
Maximumduration_tip: Schema.union([
|
|
83
|
+
Schema.const('不返回文字提示').description('不返回文字提示'),
|
|
84
|
+
Schema.string().description('返回文字提示(请在右侧填写文字内容)').default('视频太长啦!还是去B站看吧~'),
|
|
85
|
+
]).description("对过长视频的文字提示内容").default('视频太长啦!还是去B站看吧~'),
|
|
86
|
+
MinimumTimeInterval: Schema.number().default(180).description("若干`秒`内 不再处理相同链接 `防止多bot互相触发 导致的刷屏/性能浪费`").min(1),
|
|
87
|
+
}).description("基础设置"),
|
|
88
|
+
|
|
89
|
+
Schema.object({
|
|
90
|
+
parseLimit: Schema.number().default(3).description("单对话多链接解析上限").hidden(),
|
|
91
|
+
useNumeral: Schema.boolean().default(true).description("使用格式化数字").hidden(),
|
|
92
|
+
showError: Schema.boolean().default(false).description("当链接不正确时提醒发送者").hidden(),
|
|
93
|
+
bVideoIDPreference: Schema.union([
|
|
94
|
+
Schema.const("bv").description("BV 号"),
|
|
95
|
+
Schema.const("av").description("AV 号"),
|
|
96
|
+
]).default("bv").description("ID 偏好").hidden(),
|
|
97
|
+
bVideoImage: Schema.boolean().default(true).description("显示封面"),
|
|
98
|
+
bVideoOwner: Schema.boolean().default(true).description("显示 UP 主"),
|
|
99
|
+
bVideoDesc: Schema.boolean().default(false).description("显示简介`有的简介真的很长`"),
|
|
100
|
+
bVideoStat: Schema.boolean().default(true).description("显示状态(*三连数据*)"),
|
|
101
|
+
bVideoExtraStat: Schema.boolean().default(true).description("显示额外状态(*弹幕&观看*)"),
|
|
102
|
+
bVideoShowLink: Schema.boolean().default(false).description("显示视频链接`开启可能会导致其他bot循环解析`"),
|
|
103
|
+
}).description("链接的图文解析设置"),
|
|
104
|
+
|
|
105
|
+
Schema.object({
|
|
106
|
+
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"),
|
|
107
|
+
middleware: Schema.boolean().default(false).description("前置中间件模式"),
|
|
108
|
+
loggerinfo: Schema.boolean().default(false).description("日志调试输出 `日常使用无需开启`"),
|
|
109
|
+
}).description("调试设置"),
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
function apply(ctx, config) {
|
|
113
|
+
|
|
114
|
+
ctx.middleware(async (session, next) => {
|
|
115
|
+
|
|
116
|
+
let sessioncontent = session.content;
|
|
117
|
+
// 如果允许解析 BV 号,则进行解析
|
|
118
|
+
if (config.BVnumberParsing) {
|
|
119
|
+
const bvUrls = convertBVToUrl(sessioncontent);
|
|
120
|
+
if (bvUrls.length > 0) {
|
|
121
|
+
sessioncontent += '\n' + bvUrls.join('\n');
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
const links = await isProcessLinks(sessioncontent); // 判断是否需要解析
|
|
125
|
+
if (links) {
|
|
126
|
+
const ret = await extractLinks(session, config, ctx, links); // 提取链接
|
|
127
|
+
if (ret && !isLinkProcessedRecently(ret, lastProcessedUrls, config, logger)) {
|
|
128
|
+
await processVideoFromLink(session, config, ctx, lastProcessedUrls, logger, ret); // 解析视频并返回
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return next();
|
|
132
|
+
}, config.middleware);
|
|
133
|
+
if (config.demand) {
|
|
134
|
+
ctx.command('B站点播/退出登录', '退出B站账号')
|
|
135
|
+
.action(async ({ session }) => {
|
|
136
|
+
const page = await ctx.puppeteer.page();
|
|
137
|
+
await page.goto('https://www.bilibili.com/', { waitUntil: 'networkidle2' });
|
|
138
|
+
|
|
139
|
+
const loginButtonSelector = '.right-entry__outside.go-login-btn';
|
|
140
|
+
const isLoggedIn = await page.$(loginButtonSelector) === null;
|
|
141
|
+
|
|
142
|
+
if (!isLoggedIn) {
|
|
143
|
+
await page.close();
|
|
144
|
+
await session.send(h.text('您尚未登录。'))
|
|
145
|
+
return;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const avatarLinkSelector = '.header-entry-mini';
|
|
149
|
+
const logoutButtonSelector = '.logout-item';
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const avatarElement = await page.$(avatarLinkSelector);
|
|
153
|
+
if (avatarElement) {
|
|
154
|
+
await avatarElement.hover();
|
|
155
|
+
await page.waitForSelector(logoutButtonSelector, { visible: true });
|
|
156
|
+
|
|
157
|
+
await page.click(logoutButtonSelector);
|
|
158
|
+
|
|
159
|
+
await new Promise(resolve => setTimeout(resolve, 1000));
|
|
160
|
+
|
|
161
|
+
await page.close();
|
|
162
|
+
await session.send(h.text('已成功退出登录。'))
|
|
163
|
+
return;
|
|
164
|
+
} else {
|
|
165
|
+
await page.close();
|
|
166
|
+
await session.send(h.text('找不到用户头像,无法退出登录。'))
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
} catch (error) {
|
|
170
|
+
await page.close();
|
|
171
|
+
logger.error('Error during logout:', error);
|
|
172
|
+
await session.send(h.text('退出登录时出错。'))
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
ctx.command('B站点播/登录', '登录B站账号')
|
|
178
|
+
.alias("登陆")
|
|
179
|
+
.action(async ({ session }) => {
|
|
180
|
+
const page = await ctx.puppeteer.page();
|
|
181
|
+
await page.goto('https://www.bilibili.com/', { waitUntil: 'networkidle2' });
|
|
182
|
+
|
|
183
|
+
const loginButtonSelector = '.right-entry__outside.go-login-btn';
|
|
184
|
+
const isLoggedIn = await page.$(loginButtonSelector) === null;
|
|
185
|
+
|
|
186
|
+
if (isLoggedIn) {
|
|
187
|
+
await page.close();
|
|
188
|
+
await session.send(h.text('您已经登录了。'))
|
|
189
|
+
return;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
await page.click(loginButtonSelector);
|
|
193
|
+
|
|
194
|
+
const qrCodeSelector = '.login-scan-box img';
|
|
195
|
+
await page.waitForSelector(qrCodeSelector);
|
|
196
|
+
const qrCodeUrl = await page.$eval(qrCodeSelector, img => img.src);
|
|
197
|
+
|
|
198
|
+
await session.send(h.image(qrCodeUrl, 'image/png'));
|
|
199
|
+
await session.send('请扫描二维码进行登录。');
|
|
200
|
+
|
|
201
|
+
let attempts = 0;
|
|
202
|
+
let loginSuccessful = false;
|
|
203
|
+
|
|
204
|
+
while (attempts < 6) {
|
|
205
|
+
await new Promise(resolve => setTimeout(resolve, 5000)); // Wait
|
|
206
|
+
const isStillLoggedIn = await page.$(loginButtonSelector) === null;
|
|
207
|
+
|
|
208
|
+
if (isStillLoggedIn) {
|
|
209
|
+
loginSuccessful = true;
|
|
210
|
+
break;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
attempts++;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
await page.close();
|
|
217
|
+
await session.send(h.text(loginSuccessful ? '登录成功!' : '登录失败,请重试。'))
|
|
218
|
+
return;
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
ctx.command('B站点播 [keyword]', '点播B站视频')
|
|
222
|
+
.option('video', '-v 解析返回视频')
|
|
223
|
+
.option('audio', '-a 解析返回语音')
|
|
224
|
+
.option('link', '-l 解析返回链接')
|
|
225
|
+
.option('page', '-p <page:number> 指定页数', { fallback: '1' })
|
|
226
|
+
.example('点播 遠い空へ -v')
|
|
227
|
+
.action(async ({ options, session }, keyword) => {
|
|
228
|
+
if (!keyword) {
|
|
229
|
+
await session.execute('点播 -h')
|
|
230
|
+
await session.send(h.text('没输入点播内容'))
|
|
231
|
+
return
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
const url = `https://search.bilibili.com/video?keyword=${encodeURIComponent(keyword)}&page=${options.page}&o=30`
|
|
236
|
+
const page = await ctx.puppeteer.page()
|
|
237
|
+
|
|
238
|
+
await page.goto(url, {
|
|
239
|
+
waitUntil: 'networkidle2'
|
|
240
|
+
})
|
|
241
|
+
|
|
242
|
+
await page.addStyleTag({
|
|
243
|
+
content: `
|
|
244
|
+
div.bili-header,
|
|
245
|
+
div.login-tip,
|
|
246
|
+
div.v-popover,
|
|
247
|
+
div.right-entry__outside {
|
|
248
|
+
display: none !important;
|
|
249
|
+
}
|
|
250
|
+
`
|
|
251
|
+
})
|
|
252
|
+
// 获取视频列表并为每个视频元素添加序号
|
|
253
|
+
const videos = await page.evaluate((point) => {
|
|
254
|
+
const items = Array.from(document.querySelectorAll('.video-list-item:not([style*="display: none"])'))
|
|
255
|
+
return items.map((item, index) => {
|
|
256
|
+
const link = item.querySelector('a')
|
|
257
|
+
const href = link?.getAttribute('href') || ''
|
|
258
|
+
const idMatch = href.match(/\/video\/(BV\w+)\//)
|
|
259
|
+
const id = idMatch ? idMatch[1] : ''
|
|
260
|
+
|
|
261
|
+
if (!id) {
|
|
262
|
+
// 如果没有提取到视频ID,隐藏这个元素
|
|
263
|
+
//const htmlElement = item as HTMLElement
|
|
264
|
+
const htmlElement = item
|
|
265
|
+
htmlElement.style.display = 'none'
|
|
266
|
+
} else {
|
|
267
|
+
// 创建一个包含序号的元素,并将其插入到视频元素的正中央
|
|
268
|
+
const overlay = document.createElement('div')
|
|
269
|
+
overlay.style.position = 'absolute'
|
|
270
|
+
overlay.style.top = `${point[0]}%`
|
|
271
|
+
overlay.style.left = `${point[1]}%`
|
|
272
|
+
overlay.style.transform = 'translate(-50%, -50%)'
|
|
273
|
+
overlay.style.fontSize = '48px'
|
|
274
|
+
overlay.style.fontWeight = 'bold'
|
|
275
|
+
overlay.style.color = 'black'
|
|
276
|
+
overlay.style.zIndex = '10'
|
|
277
|
+
overlay.style.backgroundColor = 'rgba(255, 255, 255, 0.7)' // 半透明白色背景,确保数字清晰可见
|
|
278
|
+
overlay.style.padding = '10px'
|
|
279
|
+
overlay.style.borderRadius = '8px'
|
|
280
|
+
overlay.textContent = `${index + 1}` // 序号
|
|
281
|
+
|
|
282
|
+
// 确保父元素有 `position: relative` 以正确定位
|
|
283
|
+
//const videoElement = item as HTMLElement
|
|
284
|
+
const videoElement = item
|
|
285
|
+
videoElement.style.position = 'relative'
|
|
286
|
+
videoElement.appendChild(overlay)
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
return { id }
|
|
290
|
+
}).filter(video => video.id)
|
|
291
|
+
}, config.point) // 传递配置的 point 参数
|
|
292
|
+
|
|
293
|
+
// 如果开启了日志调试模式,打印获取到的视频信息
|
|
294
|
+
if (config.loggerinfo) {
|
|
295
|
+
ctx.logger.info(options)
|
|
296
|
+
ctx.logger.info(`共找到 ${videos.length} 个视频:`)
|
|
297
|
+
videos.forEach((video, index) => {
|
|
298
|
+
ctx.logger.info(`序号 ${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
|
+
let msg;
|
|
315
|
+
// 截图
|
|
316
|
+
const videoListElement = await page.$('.video-list.row')
|
|
317
|
+
if (videoListElement) {
|
|
318
|
+
const imgBuf = await videoListElement.screenshot({
|
|
319
|
+
captureBeyondViewport: false
|
|
320
|
+
})
|
|
321
|
+
msg = h.image(imgBuf, 'image/png')
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
await page.close()
|
|
325
|
+
|
|
326
|
+
// 发送截图
|
|
327
|
+
await session.send(msg)
|
|
328
|
+
|
|
329
|
+
// 提示用户输入
|
|
330
|
+
await session.send(`请选择视频的序号:`)
|
|
331
|
+
|
|
332
|
+
// 等待用户输入
|
|
333
|
+
const userChoice = await session.prompt(config.timeout * 1000)
|
|
334
|
+
const choiceIndex = parseInt(userChoice) - 1
|
|
335
|
+
if (isNaN(choiceIndex) || choiceIndex < 0 || choiceIndex >= videos.length) {
|
|
336
|
+
await session.send(h.text('输入无效,请输入正确的序号。'))
|
|
337
|
+
return
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// 返回用户选择的视频ID
|
|
341
|
+
const chosenVideo = videos[choiceIndex]
|
|
342
|
+
|
|
343
|
+
// 如果开启了日志调试模式,打印用户选择的视频信息
|
|
344
|
+
if (config.loggerinfo) {
|
|
345
|
+
ctx.logger.info(`渲染序号设置\noverlay.style.top = ${config.point[0]}% \noverlay.style.left = ${config.point[1]}%`)
|
|
346
|
+
ctx.logger.info(`用户选择了序号 ${choiceIndex + 1}: ID - ${chosenVideo.id}`)
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (config.enable) { // 开启自动解析了
|
|
350
|
+
session.content = `https://www.bilibili.com/video/${chosenVideo.id}`
|
|
351
|
+
const ret = await extractLinks(session, config, ctx, lastProcessedUrls, logger); // 提取链接
|
|
352
|
+
if (ret && !isLinkProcessedRecently(ret, lastProcessedUrls, config, logger)) {
|
|
353
|
+
await processVideoFromLink(session, config, ctx, lastProcessedUrls, logger, ret, options); // 解析视频并返回
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
})
|
|
357
|
+
}
|
|
358
|
+
if (config.loggerinfo) {
|
|
359
|
+
ctx.command('B站点播/调试点播 [keyword]', '调试时点播B站视频')
|
|
360
|
+
.option('video', '-v 解析返回视频')
|
|
361
|
+
.option('audio', '-a 解析返回语音')
|
|
362
|
+
.option('link', '-l 解析返回链接')
|
|
363
|
+
.option('page', '-p <page:number> 指定页数', { fallback: '1' })
|
|
364
|
+
.example('调试点播 遠い空へ -v')
|
|
365
|
+
.action(async ({ options, session }, keyword) => {
|
|
366
|
+
if (!keyword) {
|
|
367
|
+
await session.execute('调试点播 -h');
|
|
368
|
+
return '没输入keyword';
|
|
369
|
+
}
|
|
370
|
+
const url = `https://search.bilibili.com/video?keyword=${encodeURIComponent(keyword)}&page=${options.page}&o=30`;
|
|
371
|
+
const page = await ctx.puppeteer.page();
|
|
372
|
+
await page.goto(url, {
|
|
373
|
+
waitUntil: 'networkidle2',
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// 获取视频列表并为每个视频元素添加序号
|
|
377
|
+
const videos = await page.evaluate((point) => {
|
|
378
|
+
const items = Array.from(document.querySelectorAll('.video-list-item:not([style*="display: none"])'));
|
|
379
|
+
return items.map((item, index) => {
|
|
380
|
+
const link = item.querySelector('a');
|
|
381
|
+
const href = link?.getAttribute('href') || '';
|
|
382
|
+
const idMatch = href.match(/\/video\/(BV\w+)\//);
|
|
383
|
+
const id = idMatch ? idMatch[1] : '';
|
|
384
|
+
if (!id) {
|
|
385
|
+
const htmlElement = item;
|
|
386
|
+
htmlElement.style.display = 'none';
|
|
387
|
+
} else {
|
|
388
|
+
const overlay = document.createElement('div');
|
|
389
|
+
overlay.style.position = 'absolute';
|
|
390
|
+
overlay.style.top = `${point[0]}%`;
|
|
391
|
+
overlay.style.left = `${point[1]}%`;
|
|
392
|
+
overlay.style.transform = 'translate(-50%, -50%)';
|
|
393
|
+
overlay.style.fontSize = '48px';
|
|
394
|
+
overlay.style.fontWeight = 'bold';
|
|
395
|
+
overlay.style.color = 'black';
|
|
396
|
+
overlay.style.zIndex = '10';
|
|
397
|
+
overlay.style.backgroundColor = 'rgba(255, 255, 255, 0.7)';
|
|
398
|
+
overlay.style.padding = '10px';
|
|
399
|
+
overlay.style.borderRadius = '8px';
|
|
400
|
+
overlay.textContent = `${index + 1}`;
|
|
401
|
+
const videoElement = item;
|
|
402
|
+
videoElement.style.position = 'relative';
|
|
403
|
+
videoElement.appendChild(overlay);
|
|
404
|
+
}
|
|
405
|
+
return { id };
|
|
406
|
+
}).filter(video => video.id);
|
|
407
|
+
}, config.point);
|
|
408
|
+
|
|
409
|
+
// 如果开启了日志调试模式,打印获取到的视频信息
|
|
410
|
+
if (config.loggerinfo) {
|
|
411
|
+
ctx.logger.info(options);
|
|
412
|
+
ctx.logger.info(`共找到 ${videos.length} 个视频:`);
|
|
413
|
+
videos.forEach((video, index) => {
|
|
414
|
+
ctx.logger.info(`序号 ${index + 1}: ID - ${video.id}`);
|
|
415
|
+
});
|
|
416
|
+
}
|
|
417
|
+
if (videos.length === 0) {
|
|
418
|
+
await page.close();
|
|
419
|
+
return '未找到相关视频。';
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
// 动态调整窗口大小以适应视频数量
|
|
423
|
+
const viewportHeight = 200 + videos.length * 100;
|
|
424
|
+
await page.setViewport({
|
|
425
|
+
width: 1440,
|
|
426
|
+
height: viewportHeight,
|
|
427
|
+
});
|
|
428
|
+
|
|
429
|
+
let msg;
|
|
430
|
+
// 截取整个页面
|
|
431
|
+
const imgBuf = await page.screenshot({ fullPage: true });
|
|
432
|
+
msg = h.image(imgBuf, 'image/png');
|
|
433
|
+
|
|
434
|
+
await page.close();
|
|
435
|
+
|
|
436
|
+
// 发送截图
|
|
437
|
+
await session.send(msg);
|
|
438
|
+
// 提示用户输入
|
|
439
|
+
await session.send(`请选择视频的序号:`);
|
|
440
|
+
// 等待用户输入
|
|
441
|
+
const userChoice = await session.prompt(config.timeout * 1000);
|
|
442
|
+
const choiceIndex = parseInt(userChoice) - 1;
|
|
443
|
+
if (isNaN(choiceIndex) || choiceIndex < 0 || choiceIndex >= videos.length) {
|
|
444
|
+
return '输入无效,请输入正确的序号。';
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
// 返回用户选择的视频ID
|
|
448
|
+
const chosenVideo = videos[choiceIndex];
|
|
449
|
+
// 如果开启了日志调试模式,打印用户选择的视频信息
|
|
450
|
+
if (config.loggerinfo) {
|
|
451
|
+
ctx.logger.info(`渲染序号设置\noverlay.style.top = ${config.point[0]}% \noverlay.style.left = ${config.point[1]}%`);
|
|
452
|
+
ctx.logger.info(`用户选择了序号 ${choiceIndex + 1}: ID - ${chosenVideo.id}`);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
if (config.enable) {
|
|
456
|
+
// 开启自动解析了
|
|
457
|
+
session.content = `https://www.bilibili.com/video/${chosenVideo.id}`;
|
|
458
|
+
const ret = await extractLinks(session, config, ctx, lastProcessedUrls, logger);
|
|
459
|
+
if (ret && !isLinkProcessedRecently(ret, lastProcessedUrls, config, logger)) {
|
|
460
|
+
await processVideoFromLink(session, config, ctx, lastProcessedUrls, logger, ret, options);
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
//判断是否需要解析
|
|
466
|
+
async function isProcessLinks(sessioncontent) {
|
|
467
|
+
// 解析内容中的链接
|
|
468
|
+
const links = link_type_parser(sessioncontent);
|
|
469
|
+
if (links.length === 0) {
|
|
470
|
+
return false; // 如果没有找到链接,返回 false
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return links; // 返回解析出的链接
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
//提取链接
|
|
477
|
+
async function extractLinks(session, config, ctx, links) {
|
|
478
|
+
let ret = "";
|
|
479
|
+
ret += [(0, h)("quote", { id: session.messageId })];
|
|
480
|
+
let countLink = 0;
|
|
481
|
+
let tp_ret;
|
|
482
|
+
|
|
483
|
+
// 循环检测链接类型
|
|
484
|
+
for (const element of links) {
|
|
485
|
+
if (countLink >= 1) ret += "\n";
|
|
486
|
+
if (countLink >= config.parseLimit) {
|
|
487
|
+
ret += "已达到解析上限…";
|
|
488
|
+
break;
|
|
489
|
+
}
|
|
490
|
+
tp_ret = await (0, type_processer)(ctx, config, element);
|
|
491
|
+
if (tp_ret == "") {
|
|
492
|
+
if (config.showError)
|
|
493
|
+
ret = "无法解析链接信息。可能是 ID 不存在,或该类型可能暂不支持。";
|
|
494
|
+
else
|
|
495
|
+
ret = null;
|
|
496
|
+
} else {
|
|
497
|
+
ret += tp_ret;
|
|
498
|
+
}
|
|
499
|
+
countLink++;
|
|
500
|
+
}
|
|
501
|
+
return ret;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
//判断链接是否已经处理过
|
|
505
|
+
function isLinkProcessedRecently(ret, lastProcessedUrls, config, logger) {
|
|
506
|
+
const lastretUrl = extractLastUrl(ret); // 提取 ret 最后一个 http 链接作为解析目标
|
|
507
|
+
const currentTime = Date.now();
|
|
508
|
+
|
|
509
|
+
if (lastProcessedUrls[lastretUrl] && (currentTime - lastProcessedUrls[lastretUrl] < config.MinimumTimeInterval * 1000)) {
|
|
510
|
+
if (config.loggerinfo) {
|
|
511
|
+
logger.info(`重复出现,略过处理:\n ${lastretUrl}`);
|
|
512
|
+
}
|
|
513
|
+
return true; // 已经处理过
|
|
514
|
+
}
|
|
515
|
+
|
|
516
|
+
// 更新该链接的最后处理时间
|
|
517
|
+
lastProcessedUrls[lastretUrl] = currentTime;
|
|
518
|
+
return false; // 没有处理过
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
//解析视频并返回
|
|
522
|
+
async function processVideoFromLink(session, config, ctx, lastProcessedUrls, logger, ret, options = { video: true }) {
|
|
523
|
+
const lastretUrl = extractLastUrl(ret);
|
|
524
|
+
|
|
525
|
+
let mediaData = '';
|
|
526
|
+
|
|
527
|
+
if (config.waitTip_Switch) {
|
|
528
|
+
// 等候的提示文字
|
|
529
|
+
await session.send(config.waitTip_Switch);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
if (config.linktextParsing) {
|
|
533
|
+
if (config.bVideoShowLink) {
|
|
534
|
+
await session.send(ret); // 发送完整信息
|
|
535
|
+
} else {
|
|
536
|
+
// 去掉最后一个链接
|
|
537
|
+
const retWithoutLastLink = ret.replace(lastretUrl, '');
|
|
538
|
+
await session.send(retWithoutLastLink);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
if (config.VideoParsing_ToLink) {
|
|
542
|
+
const fullAPIurl = `https://api.xingzhige.com/API/b_parse/?url=${encodeURIComponent(lastretUrl)}`;
|
|
543
|
+
|
|
544
|
+
try {
|
|
545
|
+
const responseData = await ctx.http.get(fullAPIurl);
|
|
546
|
+
|
|
547
|
+
if (responseData.code === 0 && responseData.msg === "video" && responseData.data) {
|
|
548
|
+
const { bvid, cid, video } = responseData.data;
|
|
549
|
+
const bilibiliUrl = `https://api.bilibili.com/x/player/playurl?fnval=80&cid=${cid}&bvid=${bvid}`;
|
|
550
|
+
const playData = await ctx.http.get(bilibiliUrl);
|
|
551
|
+
if (config.loggerinfo) {
|
|
552
|
+
ctx.logger.info(bilibiliUrl);
|
|
553
|
+
}
|
|
554
|
+
if (playData.code === 0 && playData.data && playData.data.dash.duration) {
|
|
555
|
+
const videoDurationSeconds = playData.data.dash.duration;
|
|
556
|
+
const videoDurationMinutes = videoDurationSeconds / 60;
|
|
557
|
+
|
|
558
|
+
if (videoDurationMinutes > config.Maximumduration) {
|
|
559
|
+
if (config.Maximumduration_tip !== '不返回文字提示') {
|
|
560
|
+
await session.send(config.Maximumduration_tip);
|
|
561
|
+
return;
|
|
562
|
+
} else {
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
const videoUrl = video.url;
|
|
568
|
+
if (config.loggerinfo) {
|
|
569
|
+
ctx.logger.info(videoUrl);
|
|
570
|
+
}
|
|
571
|
+
if (videoUrl) {
|
|
572
|
+
if (options.link) {
|
|
573
|
+
await session.send(h.text(videoUrl));
|
|
574
|
+
return;
|
|
575
|
+
} else if (options.audio) {
|
|
576
|
+
await session.send(h.audio(videoUrl));
|
|
577
|
+
return;
|
|
578
|
+
} else {
|
|
579
|
+
switch (config.VideoParsing_ToLink) {
|
|
580
|
+
case '1':
|
|
581
|
+
break;
|
|
582
|
+
case '2':
|
|
583
|
+
await session.send(h.video(videoUrl));
|
|
584
|
+
break;
|
|
585
|
+
case '3':
|
|
586
|
+
await session.send(h.text(videoUrl));
|
|
587
|
+
break;
|
|
588
|
+
case '4':
|
|
589
|
+
await session.send(h.text(videoUrl));
|
|
590
|
+
await session.send(h.video(videoUrl));
|
|
591
|
+
break;
|
|
592
|
+
case '5':
|
|
593
|
+
logger.info(videoUrl);
|
|
594
|
+
await session.send(h.video(videoUrl));
|
|
595
|
+
break;
|
|
596
|
+
default:
|
|
597
|
+
break;
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
} else {
|
|
601
|
+
throw new Error("解析视频直链失败");
|
|
602
|
+
}
|
|
603
|
+
} else {
|
|
604
|
+
throw new Error("获取播放数据失败");
|
|
605
|
+
}
|
|
606
|
+
} else {
|
|
607
|
+
throw new Error("解析视频信息失败或非视频类型内容");
|
|
608
|
+
}
|
|
609
|
+
} catch (error) {
|
|
610
|
+
logger.error("请求解析 API 失败或处理出错:", error);
|
|
611
|
+
}
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
|
|
615
|
+
if (config.loggerinfo) {
|
|
616
|
+
//logger.info(`视频信息内容:\n ${JSON.stringify(mediaData)}`);
|
|
617
|
+
logger.info(`机器人发送完整消息为:\n ${ret}`);
|
|
618
|
+
}
|
|
619
|
+
return;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
|
|
623
|
+
// 提取最后一个URL
|
|
624
|
+
function extractLastUrl(text) {
|
|
625
|
+
const urlPattern = /https?:\/\/[^\s]+/g;
|
|
626
|
+
const urls = text.match(urlPattern);
|
|
627
|
+
return urls ? urls.pop() : null;
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
// 检测BV号并转换为URL
|
|
631
|
+
function convertBVToUrl(text) {
|
|
632
|
+
const bvPattern = /(?:^|\s)(BV\w{10})(?:\s|$)/g;
|
|
633
|
+
const bvMatches = [];
|
|
634
|
+
let match;
|
|
635
|
+
while ((match = bvPattern.exec(text)) !== null) {
|
|
636
|
+
bvMatches.push(match[1]);
|
|
637
|
+
}
|
|
638
|
+
return bvMatches.length ? bvMatches.map(bv => `https://www.bilibili.com/video/${bv}`) : [];
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
// 记录上次处理链接的时间
|
|
642
|
+
const lastProcessedUrls = {};
|
|
643
|
+
|
|
644
|
+
/////////////////////////////////////////////////////////////////////////////////////////////////////////
|
|
645
|
+
|
|
646
|
+
function numeral(number, config) {
|
|
647
|
+
if (config.useNumeral) {
|
|
648
|
+
if (number >= 10000 && number < 100000000) {
|
|
649
|
+
return (number / 10000).toFixed(1) + "万";
|
|
650
|
+
}
|
|
651
|
+
else if (number >= 100000000) {
|
|
652
|
+
return (number / 100000000).toFixed(1) + "亿";
|
|
653
|
+
}
|
|
654
|
+
else {
|
|
655
|
+
return number.toString();
|
|
656
|
+
}
|
|
657
|
+
}
|
|
658
|
+
else {
|
|
659
|
+
return number;
|
|
660
|
+
}
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
class Bili_Video {
|
|
664
|
+
ctx;
|
|
665
|
+
config;
|
|
666
|
+
constructor(ctx, config) {
|
|
667
|
+
this.ctx = ctx;
|
|
668
|
+
this.config = config;
|
|
669
|
+
}
|
|
670
|
+
/**
|
|
671
|
+
* 解析 ID 类型
|
|
672
|
+
* @param id 视频 ID
|
|
673
|
+
* @returns type: ID 类型, id: 视频 ID
|
|
674
|
+
*/
|
|
675
|
+
vid_type_parse(id) {
|
|
676
|
+
var idRegex = [
|
|
677
|
+
{
|
|
678
|
+
pattern: /av([0-9]+)/i,
|
|
679
|
+
type: "av",
|
|
680
|
+
},
|
|
681
|
+
{
|
|
682
|
+
pattern: /bv([0-9a-zA-Z]+)/i,
|
|
683
|
+
type: "bv",
|
|
684
|
+
},
|
|
685
|
+
];
|
|
686
|
+
for (const rule of idRegex) {
|
|
687
|
+
var match = id.match(rule.pattern);
|
|
688
|
+
if (match) {
|
|
689
|
+
return {
|
|
690
|
+
type: rule.type,
|
|
691
|
+
id: match[1],
|
|
692
|
+
};
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
return {
|
|
696
|
+
type: null,
|
|
697
|
+
id: null,
|
|
698
|
+
};
|
|
699
|
+
}
|
|
700
|
+
/**
|
|
701
|
+
* 根据视频 ID 查找视频信息
|
|
702
|
+
* @param id 视频 ID
|
|
703
|
+
* @returns 视频信息 Json
|
|
704
|
+
*/
|
|
705
|
+
async fetch_video_info(id) {
|
|
706
|
+
var ret;
|
|
707
|
+
const vid = this.vid_type_parse(id);
|
|
708
|
+
switch (vid["type"]) {
|
|
709
|
+
case "av":
|
|
710
|
+
ret = await this.ctx.http.get("https://api.bilibili.com/x/web-interface/view?aid=" + vid["id"], {
|
|
711
|
+
headers: {
|
|
712
|
+
"User-Agent": this.config.userAgent,
|
|
713
|
+
},
|
|
714
|
+
});
|
|
715
|
+
break;
|
|
716
|
+
case "bv":
|
|
717
|
+
ret = await this.ctx.http.get("https://api.bilibili.com/x/web-interface/view?bvid=" + vid["id"], {
|
|
718
|
+
headers: {
|
|
719
|
+
"User-Agent": this.config.userAgent,
|
|
720
|
+
},
|
|
721
|
+
});
|
|
722
|
+
break;
|
|
723
|
+
default:
|
|
724
|
+
ret = null;
|
|
725
|
+
break;
|
|
726
|
+
}
|
|
727
|
+
return ret;
|
|
728
|
+
}
|
|
729
|
+
/**
|
|
730
|
+
* 生成视频信息
|
|
731
|
+
* @param id 视频 ID
|
|
732
|
+
* @returns 文字视频信息
|
|
733
|
+
*/
|
|
734
|
+
async gen_context(id) {
|
|
735
|
+
const info = await this.fetch_video_info(id);
|
|
736
|
+
if (!info || !info["data"])
|
|
737
|
+
return null;
|
|
738
|
+
var ret = `${info["data"]["title"]}\n`;
|
|
739
|
+
this.config.bVideoImage
|
|
740
|
+
? (ret += `<img src=\"${info["data"]["pic"]}\"/>\n`)
|
|
741
|
+
: null;
|
|
742
|
+
this.config.bVideoOwner
|
|
743
|
+
? (ret += `UP主: ${info["data"]["owner"]["name"]}\n`)
|
|
744
|
+
: null;
|
|
745
|
+
this.config.bVideoDesc ? (ret += `${info["data"]["desc"]}\n`) : null;
|
|
746
|
+
this.config.bVideoStat
|
|
747
|
+
? (ret += `点赞:${(0, numeral)(info["data"]["stat"]["like"], this.config)}\t\t投币:${(0, numeral)(info["data"]["stat"]["coin"], this.config)}\n`)
|
|
748
|
+
: null;
|
|
749
|
+
this.config.bVideoStat
|
|
750
|
+
? (ret += `收藏:${(0, numeral)(info["data"]["stat"]["favorite"], this.config)}\t\t转发:${(0, numeral)(info["data"]["stat"]["share"], this.config)}\n`)
|
|
751
|
+
: null;
|
|
752
|
+
this.config.bVideoExtraStat
|
|
753
|
+
? (ret += `观看:${(0, numeral)(info["data"]["stat"]["view"], this.config)}\t\t弹幕:${(0, numeral)(info["data"]["stat"]["danmaku"], this.config)}\n`)
|
|
754
|
+
: null;
|
|
755
|
+
switch (this.config.bVideoIDPreference) {
|
|
756
|
+
case "bv":
|
|
757
|
+
ret += `https://www.bilibili.com/video/${info["data"]["bvid"]}\n`;
|
|
758
|
+
break;
|
|
759
|
+
case "av":
|
|
760
|
+
ret += `https://www.bilibili.com/video/av${info["data"]["aid"]}\n`;
|
|
761
|
+
break;
|
|
762
|
+
default:
|
|
763
|
+
break;
|
|
764
|
+
}
|
|
765
|
+
return ret;
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
/**
|
|
770
|
+
* 链接类型解析
|
|
771
|
+
* @param content 传入消息
|
|
772
|
+
* @returns type: "链接类型", id :"内容ID"
|
|
773
|
+
*/
|
|
774
|
+
function link_type_parser(content) {
|
|
775
|
+
// 先替换转义斜杠
|
|
776
|
+
content = content.replace(/\\\//g, '/');
|
|
777
|
+
var linkRegex = [
|
|
778
|
+
{
|
|
779
|
+
pattern: /bilibili\.com\/video\/([ab]v[0-9a-zA-Z]+)/gim,
|
|
780
|
+
type: "Video",
|
|
781
|
+
},
|
|
782
|
+
{
|
|
783
|
+
pattern: /b23\.tv(?:\\)?\/([0-9a-zA-Z]+)/gim,
|
|
784
|
+
type: "Short",
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
pattern: /bili(?:22|23|33)\.cn\/([0-9a-zA-Z]+)/gim,
|
|
788
|
+
type: "Short",
|
|
789
|
+
},
|
|
790
|
+
{
|
|
791
|
+
pattern: /bili2233\.cn\/([0-9a-zA-Z]+)/gim,
|
|
792
|
+
type: "Short",
|
|
793
|
+
},
|
|
794
|
+
];
|
|
795
|
+
var ret = [];
|
|
796
|
+
for (const rule of linkRegex) {
|
|
797
|
+
var match;
|
|
798
|
+
let lastID;
|
|
799
|
+
while ((match = rule.pattern.exec(content)) !== null) {
|
|
800
|
+
if (lastID == match[1])
|
|
801
|
+
continue;
|
|
802
|
+
ret.push({
|
|
803
|
+
type: rule.type,
|
|
804
|
+
id: match[1],
|
|
805
|
+
});
|
|
806
|
+
lastID = match[1];
|
|
807
|
+
}
|
|
808
|
+
}
|
|
809
|
+
return ret;
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
/**
|
|
813
|
+
* 类型执行器
|
|
814
|
+
* @param ctx Context
|
|
815
|
+
* @param config Config
|
|
816
|
+
* @param element 链接列表
|
|
817
|
+
* @returns 解析来的文本
|
|
818
|
+
*/
|
|
819
|
+
async function type_processer(ctx, config, element) {
|
|
820
|
+
var ret = "";
|
|
821
|
+
switch (element["type"]) {
|
|
822
|
+
case "Video":
|
|
823
|
+
const bili_video = new Bili_Video(ctx, config);
|
|
824
|
+
const video_info = await bili_video.gen_context(element["id"]);
|
|
825
|
+
if (video_info != null)
|
|
826
|
+
ret += video_info;
|
|
827
|
+
break;
|
|
828
|
+
|
|
829
|
+
case "Short":
|
|
830
|
+
const bili_short = new Bili_Short(ctx, config);
|
|
831
|
+
const typed_link = link_type_parser(await bili_short.get_redir_url(element["id"]));
|
|
832
|
+
for (const element of typed_link) {
|
|
833
|
+
const final_info = await type_processer(ctx, config, element);
|
|
834
|
+
if (final_info != null)
|
|
835
|
+
ret += final_info;
|
|
836
|
+
break;
|
|
837
|
+
}
|
|
838
|
+
break;
|
|
839
|
+
}
|
|
840
|
+
return ret;
|
|
841
|
+
}
|
|
842
|
+
|
|
843
|
+
class Bili_Short {
|
|
844
|
+
ctx;
|
|
845
|
+
config;
|
|
846
|
+
constructor(ctx, config) {
|
|
847
|
+
this.ctx = ctx;
|
|
848
|
+
this.config = config;
|
|
849
|
+
}
|
|
850
|
+
/**
|
|
851
|
+
* 根据短链接重定向获取正常链接
|
|
852
|
+
* @param id 短链接 ID
|
|
853
|
+
* @returns 正常链接
|
|
854
|
+
*/
|
|
855
|
+
async get_redir_url(id) {
|
|
856
|
+
var data = await this.ctx.http.get("https://b23.tv/" + id, {
|
|
857
|
+
redirect: "manual",
|
|
858
|
+
headers: {
|
|
859
|
+
"User-Agent": this.config.userAgent,
|
|
860
|
+
},
|
|
861
|
+
});
|
|
862
|
+
const match = data.match(/<a\s+(?:[^>]*?\s+)?href="([^"]*)"/i);
|
|
863
|
+
if (match)
|
|
864
|
+
return match[1];
|
|
865
|
+
else
|
|
866
|
+
return null;
|
|
867
|
+
}
|
|
868
|
+
}
|
|
869
|
+
}
|
|
894
870
|
exports.apply = apply;
|