koishi-plugin-bilitester 1.1.0 → 1.2.0
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/README.md +11 -2
- package/lib/index.js +182 -23
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
- Cookie 管理:自动存储和管理登录 Cookie
|
|
9
9
|
- 账号信息查询:查看当前登录账号的详细信息
|
|
10
10
|
- 视频信息查询:获取哔哩哔哩视频的详细信息
|
|
11
|
+
- 视频搜索:搜索哔哩哔哩视频
|
|
11
12
|
- 动态查询:获取用户的最新动态
|
|
12
13
|
- 直播间信息:获取直播间状态和信息
|
|
13
14
|
- 账号信息刷新:更新缓存的账号信息
|
|
@@ -70,10 +71,18 @@ bilitester logout
|
|
|
70
71
|
### 查询视频信息
|
|
71
72
|
|
|
72
73
|
```
|
|
73
|
-
|
|
74
|
+
bilitester video <BV号>
|
|
74
75
|
```
|
|
75
76
|
|
|
76
|
-
例如:`
|
|
77
|
+
例如:`bilitester video BV1xx411c7mD`
|
|
78
|
+
|
|
79
|
+
### 搜索视频
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
bilitester search <关键词>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
例如:`bilitester search 动画`
|
|
77
86
|
|
|
78
87
|
### 查询动态
|
|
79
88
|
|
package/lib/index.js
CHANGED
|
@@ -52,6 +52,105 @@ function apply(ctx, config) {
|
|
|
52
52
|
const getCookieString = (sessdata, biliJct, dedeUserId, dedeUserIdCkMd5) => {
|
|
53
53
|
return `SESSDATA=${sessdata}; bili_jct=${biliJct}; DedeUserID=${dedeUserId}; DedeUserID__ckMd5=${dedeUserIdCkMd5}`;
|
|
54
54
|
};
|
|
55
|
+
const formatNumber = (num) => {
|
|
56
|
+
if (num >= 100000000) {
|
|
57
|
+
return (num / 100000000).toFixed(1) + '亿';
|
|
58
|
+
}
|
|
59
|
+
else if (num >= 10000) {
|
|
60
|
+
return (num / 10000).toFixed(1) + '万';
|
|
61
|
+
}
|
|
62
|
+
return num.toString();
|
|
63
|
+
};
|
|
64
|
+
const formatDuration = (seconds) => {
|
|
65
|
+
const hours = Math.floor(seconds / 3600);
|
|
66
|
+
const minutes = Math.floor((seconds % 3600) / 60);
|
|
67
|
+
const secs = seconds % 60;
|
|
68
|
+
if (hours > 0) {
|
|
69
|
+
return `${hours}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
|
|
70
|
+
}
|
|
71
|
+
else {
|
|
72
|
+
return `${minutes}:${secs.toString().padStart(2, '0')}`;
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
const parseVideoId = (input) => {
|
|
76
|
+
const avMatch = input.match(/(?:^|[\s/])(av\d+)(?:[\s/]|$)/i);
|
|
77
|
+
if (avMatch) {
|
|
78
|
+
return { type: 'av', id: avMatch[1] };
|
|
79
|
+
}
|
|
80
|
+
const bvMatch = input.match(/(?:^|[\s/])(BV[\w]+)(?:[\s/]|$)/i);
|
|
81
|
+
if (bvMatch) {
|
|
82
|
+
return { type: 'bv', id: bvMatch[1] };
|
|
83
|
+
}
|
|
84
|
+
return { type: null, id: null };
|
|
85
|
+
};
|
|
86
|
+
const parseVideoLinks = (content) => {
|
|
87
|
+
const links = [];
|
|
88
|
+
const fullLinkPattern = /bilibili\.com\/video\/([ab]v[\w]+)/gi;
|
|
89
|
+
let match;
|
|
90
|
+
while ((match = fullLinkPattern.exec(content)) !== null) {
|
|
91
|
+
links.push({ type: 'Video', id: match[1] });
|
|
92
|
+
}
|
|
93
|
+
const shortLinkPatterns = [
|
|
94
|
+
/b23\.tv\/([\w]+)/gi,
|
|
95
|
+
/bili22\.cn\/([\w]+)/gi,
|
|
96
|
+
/bili23\.cn\/([\w]+)/gi,
|
|
97
|
+
/bili33\.cn\/([\w]+)/gi,
|
|
98
|
+
/bili2233\.cn\/([\w]+)/gi,
|
|
99
|
+
];
|
|
100
|
+
for (const pattern of shortLinkPatterns) {
|
|
101
|
+
while ((match = pattern.exec(content)) !== null) {
|
|
102
|
+
links.push({ type: 'Short', id: match[1] });
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return links;
|
|
106
|
+
};
|
|
107
|
+
const resolveShortLink = async (shortId) => {
|
|
108
|
+
try {
|
|
109
|
+
const response = await axios_1.default.get(`https://b23.tv/${shortId}`, {
|
|
110
|
+
maxRedirects: 0,
|
|
111
|
+
validateStatus: (status) => status >= 300 && status < 400,
|
|
112
|
+
});
|
|
113
|
+
const location = response.headers['location'];
|
|
114
|
+
if (location) {
|
|
115
|
+
return location;
|
|
116
|
+
}
|
|
117
|
+
return null;
|
|
118
|
+
}
|
|
119
|
+
catch (error) {
|
|
120
|
+
if (error.response && error.response.headers && error.response.headers.location) {
|
|
121
|
+
return error.response.headers.location;
|
|
122
|
+
}
|
|
123
|
+
return null;
|
|
124
|
+
}
|
|
125
|
+
};
|
|
126
|
+
const fetchVideoInfo = async (videoId) => {
|
|
127
|
+
const parsed = parseVideoId(videoId);
|
|
128
|
+
if (!parsed.type || !parsed.id) {
|
|
129
|
+
return null;
|
|
130
|
+
}
|
|
131
|
+
try {
|
|
132
|
+
let url;
|
|
133
|
+
if (parsed.type === 'av') {
|
|
134
|
+
url = `https://api.bilibili.com/x/web-interface/view?aid=${parsed.id.replace('av', '')}`;
|
|
135
|
+
}
|
|
136
|
+
else {
|
|
137
|
+
url = `https://api.bilibili.com/x/web-interface/view?bvid=${parsed.id}`;
|
|
138
|
+
}
|
|
139
|
+
const response = await axios_1.default.get(url, {
|
|
140
|
+
headers: {
|
|
141
|
+
'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36',
|
|
142
|
+
},
|
|
143
|
+
});
|
|
144
|
+
if (response.data.code === 0) {
|
|
145
|
+
return response.data;
|
|
146
|
+
}
|
|
147
|
+
return null;
|
|
148
|
+
}
|
|
149
|
+
catch (error) {
|
|
150
|
+
console.error('获取视频信息失败:', error);
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
};
|
|
55
154
|
const pollLoginStatus = async (qrcodeKey, userId) => {
|
|
56
155
|
const session = loginSessions.get(qrcodeKey);
|
|
57
156
|
if (!session)
|
|
@@ -260,7 +359,7 @@ function apply(ctx, config) {
|
|
|
260
359
|
return '刷新账号信息失败,请稍后重试';
|
|
261
360
|
}
|
|
262
361
|
});
|
|
263
|
-
cmd.subcommand('video <bvid>', '
|
|
362
|
+
cmd.subcommand('video <bvid>', '获取B站视频信息')
|
|
264
363
|
.action(async ({ session }, bvid) => {
|
|
265
364
|
if (!session) {
|
|
266
365
|
return '无法获取会话信息';
|
|
@@ -269,39 +368,99 @@ function apply(ctx, config) {
|
|
|
269
368
|
try {
|
|
270
369
|
const accounts = await ctx.database.get('bilibili', { userId });
|
|
271
370
|
if (!accounts || accounts.length === 0) {
|
|
272
|
-
return '
|
|
371
|
+
return '您还未登录B站账号,请使用 "bilitester login" 进行登录';
|
|
273
372
|
}
|
|
274
373
|
const acc = accounts[0];
|
|
275
374
|
const cookie = getCookieString(acc.sessdata, acc.biliJct, acc.dedeUserId, acc.dedeUserIdCkMd5);
|
|
276
375
|
const axiosInstance = createAxiosInstance(cookie);
|
|
277
|
-
const
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
if (response.data.code === 0) {
|
|
281
|
-
const video = response.data.data;
|
|
282
|
-
const duration = Math.floor(video.duration / 60) + ':' + (video.duration % 60).toString().padStart(2, '0');
|
|
283
|
-
const pubdate = new Date(video.pubdate * 1000).toLocaleString('zh-CN');
|
|
284
|
-
return `视频信息:\n` +
|
|
285
|
-
`标题: ${video.title}\n` +
|
|
286
|
-
`BV号: ${video.bvid}\n` +
|
|
287
|
-
`AV号: ${video.aid}\n` +
|
|
288
|
-
`UP主: ${video.owner.name}\n` +
|
|
289
|
-
`时长: ${duration}\n` +
|
|
290
|
-
`发布时间: ${pubdate}\n` +
|
|
291
|
-
`播放: ${video.stat.view} | 弹幕: ${video.stat.danmaku} | 点赞: ${video.stat.like}\n` +
|
|
292
|
-
`投币: ${video.stat.coin} | 收藏: ${video.stat.favorite} | 分享: ${video.stat.share}\n` +
|
|
293
|
-
`简介: ${video.desc.substring(0, 100)}${video.desc.length > 100 ? '...' : ''}\n` +
|
|
294
|
-
`${koishi_1.h.image(video.pic)}`;
|
|
295
|
-
}
|
|
296
|
-
else {
|
|
297
|
-
return '获取视频信息失败,请检查BV号是否正确';
|
|
376
|
+
const videoInfo = await fetchVideoInfo(bvid);
|
|
377
|
+
if (!videoInfo) {
|
|
378
|
+
return '获取视频信息失败,请检查BV/AV号是否正确';
|
|
298
379
|
}
|
|
380
|
+
const video = videoInfo.data;
|
|
381
|
+
const duration = formatDuration(video.duration);
|
|
382
|
+
const pubdate = new Date(video.pubdate * 1000).toLocaleString('zh-CN');
|
|
383
|
+
return `视频信息:\n` +
|
|
384
|
+
`标题: ${video.title}\n` +
|
|
385
|
+
`BV号: ${video.bvid}\n` +
|
|
386
|
+
`AV号: ${video.aid}\n` +
|
|
387
|
+
`UP主: ${video.owner.name}\n` +
|
|
388
|
+
`时长: ${duration}\n` +
|
|
389
|
+
`发布时间: ${pubdate}\n` +
|
|
390
|
+
`播放: ${formatNumber(video.stat.view)} | 弹幕: ${formatNumber(video.stat.danmaku)} | 点赞: ${formatNumber(video.stat.like)}\n` +
|
|
391
|
+
`投币: ${formatNumber(video.stat.coin)} | 收藏: ${formatNumber(video.stat.favorite)} | 分享: ${formatNumber(video.stat.share)}\n` +
|
|
392
|
+
`简介: ${video.desc.substring(0, 100)}${video.desc.length > 100 ? '...' : ''}\n` +
|
|
393
|
+
`${koishi_1.h.image(video.pic)}`;
|
|
299
394
|
}
|
|
300
395
|
catch (error) {
|
|
301
396
|
console.error('获取视频信息失败:', error);
|
|
302
397
|
return '获取视频信息失败,请稍后重试';
|
|
303
398
|
}
|
|
304
399
|
});
|
|
400
|
+
cmd.subcommand('parse <link>', '解析B站视频链接')
|
|
401
|
+
.action(async ({ session }, link) => {
|
|
402
|
+
if (!session) {
|
|
403
|
+
return '无法获取会话信息';
|
|
404
|
+
}
|
|
405
|
+
const userId = session.userId || session.guildId || 'unknown';
|
|
406
|
+
try {
|
|
407
|
+
const accounts = await ctx.database.get('bilibili', { userId });
|
|
408
|
+
if (!accounts || accounts.length === 0) {
|
|
409
|
+
return '您还未登录B站账号,请使用 "bilitester login" 进行登录';
|
|
410
|
+
}
|
|
411
|
+
const acc = accounts[0];
|
|
412
|
+
const cookie = getCookieString(acc.sessdata, acc.biliJct, acc.dedeUserId, acc.dedeUserIdCkMd5);
|
|
413
|
+
const axiosInstance = createAxiosInstance(cookie);
|
|
414
|
+
let videoId = null;
|
|
415
|
+
const parsed = parseVideoId(link);
|
|
416
|
+
if (parsed.type && parsed.id) {
|
|
417
|
+
videoId = parsed.id;
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
const links = parseVideoLinks(link);
|
|
421
|
+
if (links.length > 0) {
|
|
422
|
+
const videoLink = links[0];
|
|
423
|
+
if (videoLink.type === 'Short') {
|
|
424
|
+
const resolvedLink = await resolveShortLink(videoLink.id);
|
|
425
|
+
if (resolvedLink) {
|
|
426
|
+
const resolvedParsed = parseVideoId(resolvedLink);
|
|
427
|
+
if (resolvedParsed.type && resolvedParsed.id) {
|
|
428
|
+
videoId = resolvedParsed.id;
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
else {
|
|
433
|
+
videoId = videoLink.id;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
if (!videoId) {
|
|
438
|
+
return '无法解析视频链接,请检查链接格式是否正确';
|
|
439
|
+
}
|
|
440
|
+
const videoInfo = await fetchVideoInfo(videoId);
|
|
441
|
+
if (!videoInfo) {
|
|
442
|
+
return '获取视频信息失败,请检查链接是否正确';
|
|
443
|
+
}
|
|
444
|
+
const video = videoInfo.data;
|
|
445
|
+
const duration = formatDuration(video.duration);
|
|
446
|
+
const pubdate = new Date(video.pubdate * 1000).toLocaleString('zh-CN');
|
|
447
|
+
return `视频信息:\n` +
|
|
448
|
+
`标题: ${video.title}\n` +
|
|
449
|
+
`BV号: ${video.bvid}\n` +
|
|
450
|
+
`AV号: ${video.aid}\n` +
|
|
451
|
+
`UP主: ${video.owner.name}\n` +
|
|
452
|
+
`时长: ${duration}\n` +
|
|
453
|
+
`发布时间: ${pubdate}\n` +
|
|
454
|
+
`播放: ${formatNumber(video.stat.view)} | 弹幕: ${formatNumber(video.stat.danmaku)} | 点赞: ${formatNumber(video.stat.like)}\n` +
|
|
455
|
+
`投币: ${formatNumber(video.stat.coin)} | 收藏: ${formatNumber(video.stat.favorite)} | 分享: ${formatNumber(video.stat.share)}\n` +
|
|
456
|
+
`简介: ${video.desc.substring(0, 100)}${video.desc.length > 100 ? '...' : ''}\n` +
|
|
457
|
+
`${koishi_1.h.image(video.pic)}`;
|
|
458
|
+
}
|
|
459
|
+
catch (error) {
|
|
460
|
+
console.error('解析视频链接失败:', error);
|
|
461
|
+
return '解析视频链接失败,请稍后重试';
|
|
462
|
+
}
|
|
463
|
+
});
|
|
305
464
|
cmd.subcommand('dynamic', '获取哔哩哔哩动态')
|
|
306
465
|
.action(async ({ session }) => {
|
|
307
466
|
if (!session) {
|