koishi-plugin-bilitester 1.0.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 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
- bilibili video <BV号>
74
+ bilitester video <BV号>
74
75
  ```
75
76
 
76
- 例如:`bilibili video BV1xx411c7mD`
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 '您还未登录哔哩哔哩账号,请使用 "bilitester login" 进行登录';
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 response = await axiosInstance.get('https://api.bilibili.com/x/web-interface/view', {
278
- params: { bvid }
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) {
@@ -353,7 +512,7 @@ function apply(ctx, config) {
353
512
  return '获取动态失败,请稍后重试';
354
513
  }
355
514
  });
356
- cmd.subcommand('live <roomId>', '获取哔哩哔哩直播间信息')
515
+ cmd.subcommand('live <roomId>', '获取B站直播间信息')
357
516
  .action(async ({ session }, roomId) => {
358
517
  if (!session) {
359
518
  return '无法获取会话信息';
@@ -362,7 +521,7 @@ function apply(ctx, config) {
362
521
  try {
363
522
  const accounts = await ctx.database.get('bilibili', { userId });
364
523
  if (!accounts || accounts.length === 0) {
365
- return '您还未登录哔哩哔哩账号,请使用 "bilitester login" 进行登录';
524
+ return '您还未登录B站账号,请使用 "bilitester login" 进行登录';
366
525
  }
367
526
  const acc = accounts[0];
368
527
  const cookie = getCookieString(acc.sessdata, acc.biliJct, acc.dedeUserId, acc.dedeUserIdCkMd5);
@@ -391,6 +550,65 @@ function apply(ctx, config) {
391
550
  return '获取直播间信息失败,请稍后重试';
392
551
  }
393
552
  });
553
+ cmd.subcommand('search <keyword>', '搜索B站视频')
554
+ .action(async ({ session }, keyword) => {
555
+ if (!session) {
556
+ return '无法获取会话信息';
557
+ }
558
+ const userId = session.userId || session.guildId || 'unknown';
559
+ try {
560
+ const accounts = await ctx.database.get('bilibili', { userId });
561
+ if (!accounts || accounts.length === 0) {
562
+ return '您还未登录B站账号,请使用 "bilitester login" 进行登录';
563
+ }
564
+ const acc = accounts[0];
565
+ const cookie = getCookieString(acc.sessdata, acc.biliJct, acc.dedeUserId, acc.dedeUserIdCkMd5);
566
+ const axiosInstance = createAxiosInstance(cookie);
567
+ const response = await axiosInstance.get('https://api.bilibili.com/x/web-interface/search/type', {
568
+ params: {
569
+ search_type: 'video',
570
+ keyword: keyword,
571
+ page: 1,
572
+ }
573
+ });
574
+ if (response.data.code === 0) {
575
+ const { data } = response.data;
576
+ const videos = data.result[0]?.data || [];
577
+ if (videos.length === 0) {
578
+ return `未找到与"${keyword}"相关的视频`;
579
+ }
580
+ let result = `搜索结果:${keyword}\n`;
581
+ result += `共找到 ${data.numResults} 个视频,第 ${data.page}/${data.numPages} 页\n`;
582
+ result += '─'.repeat(40) + '\n';
583
+ for (let i = 0; i < Math.min(videos.length, 5); i++) {
584
+ const video = videos[i];
585
+ const duration = video.duration ? video.duration : '未知';
586
+ const pubdate = new Date(video.pubdate * 1000).toLocaleString('zh-CN');
587
+ result += `${i + 1}. ${video.title}\n`;
588
+ result += ` UP主: ${video.author}\n`;
589
+ result += ` BV号: ${video.bvid}\n`;
590
+ result += ` 时长: ${duration}\n`;
591
+ result += ` 播放: ${video.play} | 弹幕: ${video.video_review} | 收藏: ${video.favorites}\n`;
592
+ result += ` 发布: ${pubdate}\n`;
593
+ result += '─'.repeat(40) + '\n';
594
+ }
595
+ if (videos.length > 5) {
596
+ result += `还有 ${videos.length - 5} 个结果未显示...\n`;
597
+ }
598
+ return result;
599
+ }
600
+ else if (response.data.code === -412) {
601
+ return '搜索被拦截,请稍后重试';
602
+ }
603
+ else {
604
+ return '搜索失败,请稍后重试';
605
+ }
606
+ }
607
+ catch (error) {
608
+ console.error('搜索视频失败:', error);
609
+ return '搜索视频失败,请稍后重试';
610
+ }
611
+ });
394
612
  ctx.on('dispose', () => {
395
613
  for (const session of loginSessions.values()) {
396
614
  if (session.timer) {
@@ -0,0 +1,43 @@
1
+ export interface QRCodeResponse {
2
+ code: number;
3
+ message: string;
4
+ ttl: number;
5
+ data: {
6
+ url: string;
7
+ qrcode_key: string;
8
+ };
9
+ }
10
+ export interface PollResponse {
11
+ code: number;
12
+ message: string;
13
+ ttl: number;
14
+ data: {
15
+ url: string;
16
+ refresh_token: string;
17
+ timestamp: number;
18
+ code: number;
19
+ message: string;
20
+ };
21
+ }
22
+ export interface LoginResult {
23
+ success: boolean;
24
+ sessdata?: string;
25
+ biliJct?: string;
26
+ dedeUserId?: string;
27
+ dedeUserIdCkMd5?: string;
28
+ }
29
+ export interface LoginSession {
30
+ qrcodeKey: string;
31
+ userId: string;
32
+ startTime: number;
33
+ }
34
+ export declare class AuthService {
35
+ private static readonly POLL_INTERVAL;
36
+ private static readonly QR_CODE_TIMEOUT;
37
+ static generateQRCode(): Promise<{
38
+ url: string;
39
+ qrcodeKey: string;
40
+ } | null>;
41
+ static pollLoginStatus(qrcodeKey: string, onLogin: (result: LoginResult) => void, onTimeout: () => void, onError: (error: string) => void): Promise<NodeJS.Timeout>;
42
+ static getCookieString(sessdata: string, biliJct: string, dedeUserId: string, dedeUserIdCkMd5: string): string;
43
+ }
@@ -0,0 +1,101 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.AuthService = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ class AuthService {
9
+ static async generateQRCode() {
10
+ try {
11
+ const response = await axios_1.default.get('https://passport.bilibili.com/x/passport-login/web/qrcode/generate', {
12
+ headers: {
13
+ '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',
14
+ },
15
+ });
16
+ const { code, data } = response.data;
17
+ if (code === 0 && data) {
18
+ return {
19
+ url: data.url,
20
+ qrcodeKey: data.qrcode_key,
21
+ };
22
+ }
23
+ return null;
24
+ }
25
+ catch (error) {
26
+ console.error('生成二维码失败:', error);
27
+ return null;
28
+ }
29
+ }
30
+ static async pollLoginStatus(qrcodeKey, onLogin, onTimeout, onError) {
31
+ return new Promise((resolve) => {
32
+ const timer = setInterval(async () => {
33
+ try {
34
+ const response = await axios_1.default.get('https://passport.bilibili.com/x/passport-login/web/qrcode/poll', {
35
+ params: { qrcode_key: qrcodeKey },
36
+ headers: {
37
+ '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',
38
+ },
39
+ });
40
+ const { code, data } = response.data;
41
+ if (code === 0) {
42
+ if (data.code === 0) {
43
+ const cookies = response.headers['set-cookie'] || [];
44
+ let sessdata = '';
45
+ let biliJct = '';
46
+ let dedeUserId = '';
47
+ let dedeUserIdCkMd5 = '';
48
+ for (const cookie of cookies) {
49
+ if (cookie.includes('SESSDATA=')) {
50
+ sessdata = cookie.match(/SESSDATA=([^;]+)/)?.[1] || '';
51
+ }
52
+ if (cookie.includes('bili_jct=')) {
53
+ biliJct = cookie.match(/bili_jct=([^;]+)/)?.[1] || '';
54
+ }
55
+ if (cookie.includes('DedeUserID=')) {
56
+ dedeUserId = cookie.match(/DedeUserID=([^;]+)/)?.[1] || '';
57
+ }
58
+ if (cookie.includes('DedeUserID__ckMd5=')) {
59
+ dedeUserIdCkMd5 = cookie.match(/DedeUserID__ckMd5=([^;]+)/)?.[1] || '';
60
+ }
61
+ }
62
+ if (sessdata && biliJct && dedeUserId) {
63
+ onLogin({
64
+ success: true,
65
+ sessdata,
66
+ biliJct,
67
+ dedeUserId,
68
+ dedeUserIdCkMd5,
69
+ });
70
+ clearInterval(timer);
71
+ resolve(timer);
72
+ }
73
+ }
74
+ else if (data.code === 86038) {
75
+ onError('二维码已失效');
76
+ clearInterval(timer);
77
+ resolve(timer);
78
+ }
79
+ }
80
+ }
81
+ catch (error) {
82
+ console.error('轮询登录状态失败:', error);
83
+ onError('网络错误,请稍后重试');
84
+ clearInterval(timer);
85
+ resolve(timer);
86
+ }
87
+ }, this.POLL_INTERVAL);
88
+ setTimeout(() => {
89
+ clearInterval(timer);
90
+ onTimeout();
91
+ resolve(timer);
92
+ }, this.QR_CODE_TIMEOUT * 1000);
93
+ });
94
+ }
95
+ static getCookieString(sessdata, biliJct, dedeUserId, dedeUserIdCkMd5) {
96
+ return `SESSDATA=${sessdata}; bili_jct=${biliJct}; DedeUserID=${dedeUserId}; DedeUserID__ckMd5=${dedeUserIdCkMd5}`;
97
+ }
98
+ }
99
+ exports.AuthService = AuthService;
100
+ AuthService.POLL_INTERVAL = 2000;
101
+ AuthService.QR_CODE_TIMEOUT = 180;
@@ -0,0 +1,98 @@
1
+ export interface VideoInfo {
2
+ bvid: string;
3
+ aid: number;
4
+ title: string;
5
+ desc: string;
6
+ pic: string;
7
+ owner: {
8
+ mid: string;
9
+ name: string;
10
+ face: string;
11
+ };
12
+ stat: {
13
+ view: number;
14
+ danmaku: number;
15
+ reply: number;
16
+ favorite: number;
17
+ coin: number;
18
+ share: number;
19
+ like: number;
20
+ };
21
+ duration: number;
22
+ pubdate: number;
23
+ }
24
+ export interface VideoSearchResult {
25
+ bvid: string;
26
+ aid: number;
27
+ title: string;
28
+ pic: string;
29
+ description: string;
30
+ owner: {
31
+ mid: string;
32
+ name: string;
33
+ face: string;
34
+ };
35
+ stat: {
36
+ view: number;
37
+ danmaku: number;
38
+ reply: number;
39
+ favorite: number;
40
+ coin: number;
41
+ share: number;
42
+ like: number;
43
+ };
44
+ duration: number;
45
+ pubdate: number;
46
+ }
47
+ export interface UserInfo {
48
+ isLogin: boolean;
49
+ mid: string;
50
+ uname: string;
51
+ face: string;
52
+ vipStatus: number;
53
+ vipType: number;
54
+ vipDueDate: number;
55
+ level_info: {
56
+ current_level: number;
57
+ current_exp: number;
58
+ next_exp: string | number;
59
+ };
60
+ money: number;
61
+ }
62
+ export interface DynamicResponse {
63
+ cards: Array<{
64
+ card: string;
65
+ desc: {
66
+ type: number;
67
+ dynamic_id: number;
68
+ timestamp: number;
69
+ };
70
+ }>;
71
+ }
72
+ export interface LiveRoomInfo {
73
+ roomid: number;
74
+ uid: number;
75
+ title: string;
76
+ live_status: number;
77
+ keyframe: string;
78
+ cover: string;
79
+ online: number;
80
+ area_name: string;
81
+ parent_area_name: string;
82
+ }
83
+ export interface SearchResponse {
84
+ code: number;
85
+ message: string;
86
+ data: {
87
+ result: VideoSearchResult[];
88
+ };
89
+ }
90
+ export declare class BilibiliService {
91
+ private axiosInstance;
92
+ constructor(cookie: string);
93
+ getUserInfo(): Promise<UserInfo | null>;
94
+ getVideoInfo(bvid: string): Promise<VideoInfo | null>;
95
+ searchVideos(keyword: string, page?: number): Promise<VideoSearchResult[] | null>;
96
+ getDynamics(uid: string): Promise<DynamicResponse | null>;
97
+ getLiveRoomInfo(roomId: number): Promise<LiveRoomInfo | null>;
98
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.BilibiliService = void 0;
7
+ const axios_1 = __importDefault(require("axios"));
8
+ class BilibiliService {
9
+ constructor(cookie) {
10
+ this.axiosInstance = axios_1.default.create({
11
+ headers: {
12
+ 'Cookie': cookie,
13
+ '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',
14
+ 'Referer': 'https://www.bilibili.com',
15
+ },
16
+ });
17
+ }
18
+ async getUserInfo() {
19
+ try {
20
+ const response = await this.axiosInstance.get('https://api.bilibili.com/x/web-interface/nav');
21
+ if (response.data.code === 0 && response.data.data.isLogin) {
22
+ return response.data.data;
23
+ }
24
+ return null;
25
+ }
26
+ catch (error) {
27
+ console.error('获取用户信息失败:', error);
28
+ return null;
29
+ }
30
+ }
31
+ async getVideoInfo(bvid) {
32
+ try {
33
+ const response = await this.axiosInstance.get('https://api.bilibili.com/x/web-interface/view', {
34
+ params: { bvid }
35
+ });
36
+ if (response.data.code === 0) {
37
+ return response.data.data;
38
+ }
39
+ return null;
40
+ }
41
+ catch (error) {
42
+ console.error('获取视频信息失败:', error);
43
+ return null;
44
+ }
45
+ }
46
+ async searchVideos(keyword, page = 1) {
47
+ try {
48
+ const response = await this.axiosInstance.get('https://api.bilibili.com/x/web-interface/search/all', {
49
+ params: {
50
+ keyword,
51
+ page,
52
+ search_type: 'video',
53
+ }
54
+ });
55
+ if (response.data.code === 0) {
56
+ return response.data.data.result;
57
+ }
58
+ return null;
59
+ }
60
+ catch (error) {
61
+ console.error('搜索视频失败:', error);
62
+ return null;
63
+ }
64
+ }
65
+ async getDynamics(uid) {
66
+ try {
67
+ const response = await this.axiosInstance.get('https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/dynamic_new', {
68
+ params: {
69
+ uid,
70
+ type_list: '8',
71
+ }
72
+ });
73
+ if (response.data.code === 0) {
74
+ return response.data.data;
75
+ }
76
+ return null;
77
+ }
78
+ catch (error) {
79
+ console.error('获取动态失败:', error);
80
+ return null;
81
+ }
82
+ }
83
+ async getLiveRoomInfo(roomId) {
84
+ try {
85
+ const response = await this.axiosInstance.get('https://api.live.bilibili.com/room/v1/Room/get_info', {
86
+ params: { room_id: roomId }
87
+ });
88
+ if (response.data.code === 0) {
89
+ return response.data.data;
90
+ }
91
+ return null;
92
+ }
93
+ catch (error) {
94
+ console.error('获取直播间信息失败:', error);
95
+ return null;
96
+ }
97
+ }
98
+ }
99
+ exports.BilibiliService = BilibiliService;
@@ -0,0 +1,27 @@
1
+ import { Context } from 'koishi';
2
+ export interface BilibiliAccount {
3
+ id: number;
4
+ userId: string;
5
+ sessdata: string;
6
+ biliJct: string;
7
+ dedeUserId: string;
8
+ dedeUserIdCkMd5: string;
9
+ mid: string;
10
+ name: string;
11
+ face: string;
12
+ vipStatus: number;
13
+ vipType: number;
14
+ vipDueDate: number;
15
+ level: number;
16
+ coins: number;
17
+ createdAt: Date;
18
+ updatedAt: Date;
19
+ }
20
+ export declare class DatabaseService {
21
+ private ctx;
22
+ constructor(ctx: Context);
23
+ createAccount(account: Omit<BilibiliAccount, 'id'>): Promise<BilibiliAccount>;
24
+ getAccountByUserId(userId: string): Promise<BilibiliAccount | null>;
25
+ updateAccount(id: number, data: Partial<Omit<BilibiliAccount, 'id' | 'userId' | 'createdAt'>>): Promise<boolean>;
26
+ deleteAccountByUserId(userId: string): Promise<boolean>;
27
+ }
@@ -0,0 +1,67 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DatabaseService = void 0;
4
+ class DatabaseService {
5
+ constructor(ctx) {
6
+ this.ctx = ctx;
7
+ this.ctx.model.extend('bilibili', {
8
+ id: 'unsigned',
9
+ userId: 'text',
10
+ sessdata: 'text',
11
+ biliJct: 'text',
12
+ dedeUserId: 'text',
13
+ dedeUserIdCkMd5: 'text',
14
+ mid: 'text',
15
+ name: 'text',
16
+ face: 'text',
17
+ vipStatus: 'integer',
18
+ vipType: 'integer',
19
+ vipDueDate: 'integer',
20
+ level: 'integer',
21
+ coins: 'float',
22
+ createdAt: 'timestamp',
23
+ updatedAt: 'timestamp',
24
+ }, {
25
+ autoInc: false,
26
+ });
27
+ }
28
+ async createAccount(account) {
29
+ const newAccount = {
30
+ ...account,
31
+ id: Date.now(),
32
+ };
33
+ await this.ctx.database.create('bilibili', newAccount);
34
+ return newAccount;
35
+ }
36
+ async getAccountByUserId(userId) {
37
+ const accounts = await this.ctx.database.get('bilibili', { userId });
38
+ if (!accounts || accounts.length === 0) {
39
+ return null;
40
+ }
41
+ return accounts[0];
42
+ }
43
+ async updateAccount(id, data) {
44
+ try {
45
+ await this.ctx.database.set('bilibili', { id }, {
46
+ ...data,
47
+ updatedAt: new Date(),
48
+ });
49
+ return true;
50
+ }
51
+ catch (error) {
52
+ console.error('更新账号信息失败:', error);
53
+ return false;
54
+ }
55
+ }
56
+ async deleteAccountByUserId(userId) {
57
+ try {
58
+ await this.ctx.database.remove('bilibili', { userId });
59
+ return true;
60
+ }
61
+ catch (error) {
62
+ console.error('删除账号信息失败:', error);
63
+ return false;
64
+ }
65
+ }
66
+ }
67
+ exports.DatabaseService = DatabaseService;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-bilitester",
3
- "version": "1.0.0",
3
+ "version": "1.2.0",
4
4
  "description": "哔哩哔哩登录和API调用插件,支持二维码登录和Cookie管理",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",