dsh-shlr-bilitoolkit 0.1.7 → 0.1.9
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 +6 -5
- package/dist/dsh.d.ts +1 -0
- package/dist/dsh.js +472 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -71,12 +71,13 @@ const [ups, _] = await client.space.videos(574115689, 1, 10);
|
|
|
71
71
|
|
|
72
72
|
- 包名 `dsh-shlr-bilitoolkit`(符合 `dsh-` 前缀约定),`package.json` 里声明了 `dsh.bundle.patch: ./cordis.patch.yml`,因此会被 dsh 识别为插件而非普通库。
|
|
73
73
|
- 入口 `src/dsh.ts` 导出 `apply(ctx)`,注册一组工具(读取为主,写操作明确标注「需登录 / 谨慎」):
|
|
74
|
-
- 读取:`bili_recommend` / `bili_video_detail` / `bili_video_subtitle` / `bili_search_video` / `bili_search_user` / `bili_space_info` / `bili_space_videos` / `bili_article_view` / `bili_article_list` / `bili_dynamic_feed` / `bili_dynamic_space` / `bili_comment_list` / `bili_relation_followings` / `bili_relation_followers` / `bili_notify_unread` / `bili_notify_replies` / `bili_fav_folders`
|
|
75
|
-
- 写操作:`bili_like` / `bili_follow` / `bili_favorite` / `bili_send_comment` / `bili_send_message` / `bili_triple` / `bili_dynamic_publish` / `bili_dynamic_repost`(均会真实修改账号状态,需登录)
|
|
74
|
+
- 读取:`bili_recommend` / `bili_video_detail` / `bili_video_subtitle` / `bili_search_video` / `bili_search_user` / `bili_space_info` / `bili_space_videos` / `bili_article_view` / `bili_article_list` / `bili_dynamic_feed` / `bili_dynamic_space` / `bili_comment_list` / `bili_relation_followings` / `bili_relation_followers` / `bili_notify_unread` / `bili_notify_replies` / `bili_fav_folders` / `bili_video_pagelist` / `bili_video_subtitles` / `bili_video_playurl` / `bili_video_status` / `bili_space_collections` / `bili_space_collection` / `bili_fav_folder_videos` / `bili_dynamic_detail` / `bili_notify_ats` / `bili_notify_likes` / `bili_message_unread` / `bili_message_history` / `bili_relation_stat` / `bili_user_following` / `bili_profile_info` / `bili_comment_replies`
|
|
75
|
+
- 写操作:`bili_like` / `bili_follow` / `bili_favorite` / `bili_send_comment` / `bili_send_message` / `bili_triple` / `bili_dynamic_publish` / `bili_dynamic_repost` / `bili_coin` / `bili_unfavorite` / `bili_fav_folder_create` / `bili_fav_folder_delete` / `bili_dynamic_like` / `bili_dynamic_delete` / `bili_comment_send` / `bili_comment_reply` / `bili_comment_delete` / `bili_message_recall` / `bili_profile_update_sign` / `bili_profile_update`(均会真实修改账号状态,需登录)
|
|
76
76
|
- 辅助:`bili_login_status`(查登录态)/ `bili_gen_qr`(生成扫码登录二维码)/ `bili_cache_clear`(清空本地缓存)
|
|
77
|
-
- 工具读取 `BILI_COOKIE_FILE`(默认 `bili_cookies.json`)作为登录态。
|
|
78
|
-
-
|
|
79
|
-
-
|
|
77
|
+
- 工具读取 `BILI_COOKIE_FILE`(默认 `bili_data/bili_cookies.json`)作为登录态。
|
|
78
|
+
- **数据目录**:Cookie / 缓存 / 二维码统一放在一个目录里,默认 `bili_data/`(相对 dsh 工作目录),避免散落污染工作目录。可用 `BILI_DATA_DIR` 改目录;`BILI_COOKIE_FILE` / `BILI_CACHE_FILE` / `BILI_QR_FILE` 可精确指定单个文件。兼容旧版:若新目录还没有 Cookie、而旧位置(`./bili_cookies.json`)有,会继续沿用旧的。
|
|
79
|
+
- **扫码登录(独立获取 Cookie)**:运行 `npm run login`,终端会打印 ASCII 二维码,用「哔哩哔哩」App 扫码并确认后,Cookie 自动写入 `bili_data/bili_cookies.json`。也可在对话里先调 `bili_login_status` 查登录态、调 `bili_gen_qr` 拿二维码链接与可直接展示的 `ascii` 二维码。写操作(点赞 / 收藏 / 投币 / 关注 / 评论 / 私信 / 三连)都依赖该 Cookie 中的 `SESSDATA` / `bili_jct`。
|
|
80
|
+
- **缓存持久化**:除 Cookie 外,客户端还会把 WBI 签名密钥(`B 站每天轮换`)、`bvid→aid` 映射这类稳定 / 带 TTL 的数据写入本地 JSON 缓存(`BILI_CACHE_FILE`,默认 `bili_data/bili_cache.json`),用临时文件 + 原子替换落盘、文件损坏会自动自愈。冷启动或跨进程时直接复用,避免每次都请求 `nav` 接口;密钥过期或失效时 `refresh()` 会重新拉取并覆盖缓存。若签名反复报 `-403/-352`,可调用 `bili_cache_clear` 强制清空缓存后重试(不影响 Cookie / 登录态)。
|
|
80
81
|
|
|
81
82
|
安装到 dsh(任选其一,取决于你的发行方式):
|
|
82
83
|
|
package/dist/dsh.d.ts
CHANGED
|
@@ -13,6 +13,7 @@ import { BiliClient } from "./client.js";
|
|
|
13
13
|
import { Collection, Page, PlayInfo, Subtitle, User, Video } from "./models.js";
|
|
14
14
|
export declare const name = "bili-toolkit";
|
|
15
15
|
export declare const inject: string[];
|
|
16
|
+
export declare function dataDir(): string;
|
|
16
17
|
export interface DshTool {
|
|
17
18
|
id: string;
|
|
18
19
|
description: string;
|
package/dist/dsh.js
CHANGED
|
@@ -11,8 +11,8 @@
|
|
|
11
11
|
*/
|
|
12
12
|
import { defineTool } from "@deepseek-ai/dsh-tools";
|
|
13
13
|
import QRCode from "qrcode";
|
|
14
|
-
import { writeFileSync } from "node:fs";
|
|
15
|
-
import { resolve } from "node:path";
|
|
14
|
+
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
15
|
+
import { dirname, resolve } from "node:path";
|
|
16
16
|
import { pathToFileURL } from "node:url";
|
|
17
17
|
import { BiliClient } from "./client.js";
|
|
18
18
|
import { Collection, Page, PlayInfo, Subtitle, User, Video } from "./models.js";
|
|
@@ -23,10 +23,26 @@ export const inject = ["tools"];
|
|
|
23
23
|
let cached = null;
|
|
24
24
|
// 最近一次生成、等待扫码确认的二维码 key(登录完成链路用)
|
|
25
25
|
let pendingQrKey = null;
|
|
26
|
+
// 统一的数据目录:Cookie / 缓存 / 二维码都放这里,避免散落在工作目录里。
|
|
27
|
+
// BILI_DATA_DIR 可改目录;BILI_COOKIE_FILE / BILI_CACHE_FILE / BILI_QR_FILE 可精确指定单个文件。
|
|
28
|
+
export function dataDir() {
|
|
29
|
+
return process.env.BILI_DATA_DIR ?? "bili_data";
|
|
30
|
+
}
|
|
31
|
+
function resolveCookieFile() {
|
|
32
|
+
if (process.env.BILI_COOKIE_FILE)
|
|
33
|
+
return process.env.BILI_COOKIE_FILE;
|
|
34
|
+
const primary = resolve(dataDir(), "bili_cookies.json");
|
|
35
|
+
const legacy = resolve("bili_cookies.json");
|
|
36
|
+
// 兼容旧版:新目录还没有 cookie、但旧位置有,就继续用旧的
|
|
37
|
+
if (!existsSync(primary) && existsSync(legacy))
|
|
38
|
+
return legacy;
|
|
39
|
+
return primary;
|
|
40
|
+
}
|
|
26
41
|
async function getClient() {
|
|
27
42
|
if (!cached) {
|
|
28
43
|
cached = new BiliClient({
|
|
29
|
-
cookieFile:
|
|
44
|
+
cookieFile: resolveCookieFile(),
|
|
45
|
+
cacheFile: process.env.BILI_CACHE_FILE ?? resolve(dataDir(), "bili_cache.json"),
|
|
30
46
|
});
|
|
31
47
|
// 没有 Cookie 文件(或为空 / 损坏)= 未登录(匿名)。
|
|
32
48
|
// 不能因此让所有工具崩掉——公开接口、以及最关键的扫码登录入口都依赖这里。
|
|
@@ -491,6 +507,457 @@ export const TOOLS = [
|
|
|
491
507
|
return { ok: true, dynamicId: id, url: `https://t.bilibili.com/${id}` };
|
|
492
508
|
},
|
|
493
509
|
},
|
|
510
|
+
// ---- 视频补充 ----
|
|
511
|
+
{
|
|
512
|
+
id: "bili_video_pagelist",
|
|
513
|
+
description: "获取视频的分 P 列表(标题 + cid),用于定位具体某一 P。",
|
|
514
|
+
parameters: {
|
|
515
|
+
type: "object",
|
|
516
|
+
properties: { bvid: { type: "string" } },
|
|
517
|
+
required: ["bvid"],
|
|
518
|
+
},
|
|
519
|
+
handler: async (a) => {
|
|
520
|
+
const pages = await (await getClient()).video.pagelist(a.bvid);
|
|
521
|
+
return pages.map((p) => p.toDict());
|
|
522
|
+
},
|
|
523
|
+
},
|
|
524
|
+
{
|
|
525
|
+
id: "bili_video_subtitles",
|
|
526
|
+
description: "列出视频可用的字幕轨(语言 / 名称 / 链接),不含正文。",
|
|
527
|
+
parameters: {
|
|
528
|
+
type: "object",
|
|
529
|
+
properties: { bvid: { type: "string" } },
|
|
530
|
+
required: ["bvid"],
|
|
531
|
+
},
|
|
532
|
+
handler: async (a) => {
|
|
533
|
+
const subs = await (await getClient()).video.subtitles(a.bvid);
|
|
534
|
+
return subs.map((s) => s.toDict());
|
|
535
|
+
},
|
|
536
|
+
},
|
|
537
|
+
{
|
|
538
|
+
id: "bili_video_playurl",
|
|
539
|
+
description: "获取视频播放地址信息(清晰度、可用清晰度列表、durl 直链)。高清晰度可能需登录 / 大会员。",
|
|
540
|
+
parameters: {
|
|
541
|
+
type: "object",
|
|
542
|
+
properties: {
|
|
543
|
+
bvid: { type: "string" },
|
|
544
|
+
page: { type: "integer", default: 1 },
|
|
545
|
+
qn: { type: "integer", default: 80 },
|
|
546
|
+
},
|
|
547
|
+
required: ["bvid"],
|
|
548
|
+
},
|
|
549
|
+
handler: async (a) => {
|
|
550
|
+
const info = await (await getClient()).video.playurl(a.bvid, Number(a.page ?? 1), undefined, Number(a.qn ?? 80));
|
|
551
|
+
return info.toDict();
|
|
552
|
+
},
|
|
553
|
+
},
|
|
554
|
+
{
|
|
555
|
+
id: "bili_video_status",
|
|
556
|
+
description: "查询我对某视频的互动状态:是否已点赞、已投硬币数。",
|
|
557
|
+
parameters: {
|
|
558
|
+
type: "object",
|
|
559
|
+
properties: { bvid: { type: "string" } },
|
|
560
|
+
required: ["bvid"],
|
|
561
|
+
},
|
|
562
|
+
handler: async (a) => {
|
|
563
|
+
const c = await getClient();
|
|
564
|
+
const [liked, coins] = await Promise.all([
|
|
565
|
+
c.interact.hasLike(a.bvid),
|
|
566
|
+
c.interact.coinsGiven(a.bvid),
|
|
567
|
+
]);
|
|
568
|
+
return { liked, coins };
|
|
569
|
+
},
|
|
570
|
+
},
|
|
571
|
+
// ---- 空间 / 合集 ----
|
|
572
|
+
{
|
|
573
|
+
id: "bili_space_collections",
|
|
574
|
+
description: "获取某 UP 主的合集 / 系列列表。",
|
|
575
|
+
parameters: {
|
|
576
|
+
type: "object",
|
|
577
|
+
properties: {
|
|
578
|
+
uid: { type: "integer" },
|
|
579
|
+
pageSize: { type: "integer", default: 20 },
|
|
580
|
+
},
|
|
581
|
+
required: ["uid"],
|
|
582
|
+
},
|
|
583
|
+
handler: async (a) => {
|
|
584
|
+
const [cols, total] = await (await getClient()).space.collections(Number(a.uid), 1, Number(a.pageSize ?? 20));
|
|
585
|
+
return { total, collections: cols.map((c) => c.toDict()) };
|
|
586
|
+
},
|
|
587
|
+
},
|
|
588
|
+
{
|
|
589
|
+
id: "bili_space_collection",
|
|
590
|
+
description: "获取某个合集内的视频列表。",
|
|
591
|
+
parameters: {
|
|
592
|
+
type: "object",
|
|
593
|
+
properties: {
|
|
594
|
+
seasonId: { type: "integer" },
|
|
595
|
+
mid: { type: "integer", description: "UP 主 uid(可选,用于正确 Referer)" },
|
|
596
|
+
pageSize: { type: "integer", default: 30 },
|
|
597
|
+
},
|
|
598
|
+
required: ["seasonId"],
|
|
599
|
+
},
|
|
600
|
+
handler: async (a) => {
|
|
601
|
+
const [col, videos, total] = await (await getClient()).space.collection(Number(a.seasonId), 1, Number(a.pageSize ?? 30), a.mid != null ? Number(a.mid) : undefined);
|
|
602
|
+
return { collection: col.toDict(), total, videos: videos.map((v) => v.toDict()) };
|
|
603
|
+
},
|
|
604
|
+
},
|
|
605
|
+
// ---- 收藏夹 ----
|
|
606
|
+
{
|
|
607
|
+
id: "bili_fav_folder_videos",
|
|
608
|
+
description: "获取某个收藏夹内的视频列表(需登录)。",
|
|
609
|
+
parameters: {
|
|
610
|
+
type: "object",
|
|
611
|
+
properties: {
|
|
612
|
+
folderId: { type: "integer" },
|
|
613
|
+
order: { type: "string", enum: ["mtime", "view", "pubtime"], default: "mtime" },
|
|
614
|
+
pageSize: { type: "integer", default: 20 },
|
|
615
|
+
},
|
|
616
|
+
required: ["folderId"],
|
|
617
|
+
},
|
|
618
|
+
handler: async (a) => {
|
|
619
|
+
const [videos, total] = await (await getClient()).interact.folderVideos(Number(a.folderId), 1, Number(a.pageSize ?? 20), a.order ?? "mtime");
|
|
620
|
+
return { total, videos: videos.map((v) => v.toDict()) };
|
|
621
|
+
},
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
id: "bili_fav_folder_create",
|
|
625
|
+
description: "新建收藏夹(写操作,需登录)。",
|
|
626
|
+
parameters: {
|
|
627
|
+
type: "object",
|
|
628
|
+
properties: {
|
|
629
|
+
title: { type: "string" },
|
|
630
|
+
privacy: { type: "integer", enum: [0, 1], default: 0, description: "0 公开 / 1 私密" },
|
|
631
|
+
},
|
|
632
|
+
required: ["title"],
|
|
633
|
+
},
|
|
634
|
+
handler: async (a) => {
|
|
635
|
+
const id = await (await getClient()).interact.createFolder(String(a.title ?? ""), Number(a.privacy ?? 0));
|
|
636
|
+
return { ok: true, folderId: id };
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
{
|
|
640
|
+
id: "bili_fav_folder_delete",
|
|
641
|
+
description: "删除收藏夹(写操作,需登录,谨慎)。",
|
|
642
|
+
parameters: {
|
|
643
|
+
type: "object",
|
|
644
|
+
properties: { folderId: { type: "integer" } },
|
|
645
|
+
required: ["folderId"],
|
|
646
|
+
},
|
|
647
|
+
handler: async (a) => {
|
|
648
|
+
await (await getClient()).interact.deleteFolder(Number(a.folderId));
|
|
649
|
+
return { ok: true };
|
|
650
|
+
},
|
|
651
|
+
},
|
|
652
|
+
// ---- 互动写操作补充 ----
|
|
653
|
+
{
|
|
654
|
+
id: "bili_coin",
|
|
655
|
+
description: "给视频投币(写操作,需登录,硬币每日有限且不可退回)。",
|
|
656
|
+
parameters: {
|
|
657
|
+
type: "object",
|
|
658
|
+
properties: {
|
|
659
|
+
bvid: { type: "string" },
|
|
660
|
+
count: { type: "integer", enum: [1, 2], default: 1 },
|
|
661
|
+
alsoLike: { type: "boolean", default: false },
|
|
662
|
+
},
|
|
663
|
+
required: ["bvid"],
|
|
664
|
+
},
|
|
665
|
+
handler: async (a) => {
|
|
666
|
+
await (await getClient()).interact.coin(a.bvid, undefined, Number(a.count ?? 1), Boolean(a.alsoLike ?? false));
|
|
667
|
+
return { ok: true };
|
|
668
|
+
},
|
|
669
|
+
},
|
|
670
|
+
{
|
|
671
|
+
id: "bili_unfavorite",
|
|
672
|
+
description: "把视频从收藏夹移除(写操作,需登录)。",
|
|
673
|
+
parameters: {
|
|
674
|
+
type: "object",
|
|
675
|
+
properties: {
|
|
676
|
+
bvid: { type: "string" },
|
|
677
|
+
folderName: { type: "string", description: "收藏夹名(可选,默认取默认收藏夹)" },
|
|
678
|
+
},
|
|
679
|
+
required: ["bvid"],
|
|
680
|
+
},
|
|
681
|
+
handler: async (a) => {
|
|
682
|
+
const fid = await (await getClient()).interact.unfavorite(a.bvid, undefined, a.folderName);
|
|
683
|
+
return { ok: true, folderId: fid };
|
|
684
|
+
},
|
|
685
|
+
},
|
|
686
|
+
{
|
|
687
|
+
id: "bili_user_following",
|
|
688
|
+
description: "查询我是否已关注某用户。",
|
|
689
|
+
parameters: {
|
|
690
|
+
type: "object",
|
|
691
|
+
properties: { uid: { type: "integer" } },
|
|
692
|
+
required: ["uid"],
|
|
693
|
+
},
|
|
694
|
+
handler: async (a) => ({
|
|
695
|
+
following: await (await getClient()).interact.isFollowing(Number(a.uid)),
|
|
696
|
+
}),
|
|
697
|
+
},
|
|
698
|
+
// ---- 动态补充 ----
|
|
699
|
+
{
|
|
700
|
+
id: "bili_dynamic_detail",
|
|
701
|
+
description: "获取单条动态详情(正文、类型、统计数据等)。",
|
|
702
|
+
parameters: {
|
|
703
|
+
type: "object",
|
|
704
|
+
properties: { dynamicId: { type: "string" } },
|
|
705
|
+
required: ["dynamicId"],
|
|
706
|
+
},
|
|
707
|
+
handler: async (a) => (await (await getClient()).dynamic.detail(a.dynamicId)).toDict(),
|
|
708
|
+
},
|
|
709
|
+
{
|
|
710
|
+
id: "bili_dynamic_like",
|
|
711
|
+
description: "点赞或取消点赞一条动态(写操作,需登录)。",
|
|
712
|
+
parameters: {
|
|
713
|
+
type: "object",
|
|
714
|
+
properties: {
|
|
715
|
+
dynamicId: { type: "string" },
|
|
716
|
+
liked: { type: "boolean", default: true },
|
|
717
|
+
},
|
|
718
|
+
required: ["dynamicId"],
|
|
719
|
+
},
|
|
720
|
+
handler: async (a) => {
|
|
721
|
+
await (await getClient()).dynamic.thumb(a.dynamicId, Boolean(a.liked ?? true));
|
|
722
|
+
return { ok: true };
|
|
723
|
+
},
|
|
724
|
+
},
|
|
725
|
+
{
|
|
726
|
+
id: "bili_dynamic_delete",
|
|
727
|
+
description: "删除自己发布的一条动态(写操作,需登录,谨慎)。",
|
|
728
|
+
parameters: {
|
|
729
|
+
type: "object",
|
|
730
|
+
properties: { dynamicId: { type: "string" } },
|
|
731
|
+
required: ["dynamicId"],
|
|
732
|
+
},
|
|
733
|
+
handler: async (a) => {
|
|
734
|
+
await (await getClient()).dynamic.delete(a.dynamicId);
|
|
735
|
+
return { ok: true };
|
|
736
|
+
},
|
|
737
|
+
},
|
|
738
|
+
// ---- 评论(动态 / 专栏 / 楼中楼 / 回复 / 删除) ----
|
|
739
|
+
{
|
|
740
|
+
id: "bili_comment_send",
|
|
741
|
+
description: "在视频 / 动态 / 专栏下发表评论(写操作,需登录)。视频用 bvid,动态用动态 id,专栏用 cv 号 / 链接。",
|
|
742
|
+
parameters: {
|
|
743
|
+
type: "object",
|
|
744
|
+
properties: {
|
|
745
|
+
target: { type: "string", enum: ["video", "dynamic", "article"], default: "video" },
|
|
746
|
+
id: { type: "string", description: "视频 bvid / 动态 id / 专栏 cv 号或链接" },
|
|
747
|
+
message: { type: "string" },
|
|
748
|
+
},
|
|
749
|
+
required: ["id", "message"],
|
|
750
|
+
},
|
|
751
|
+
handler: async (a) => {
|
|
752
|
+
const c = await getClient();
|
|
753
|
+
const message = String(a.message ?? "");
|
|
754
|
+
const target = String(a.target ?? "video");
|
|
755
|
+
let comment;
|
|
756
|
+
if (target === "video")
|
|
757
|
+
comment = await c.comment.sendVideo(a.id, message);
|
|
758
|
+
else if (target === "dynamic")
|
|
759
|
+
comment = await c.comment.sendDynamic(a.id, message);
|
|
760
|
+
else if (target === "article")
|
|
761
|
+
comment = await c.comment.sendArticle(a.id, message);
|
|
762
|
+
else
|
|
763
|
+
throw new Error("target 只支持 video / dynamic / article");
|
|
764
|
+
return comment.toDict();
|
|
765
|
+
},
|
|
766
|
+
},
|
|
767
|
+
{
|
|
768
|
+
id: "bili_comment_replies",
|
|
769
|
+
description: "获取某条评论下的楼中楼回复列表。",
|
|
770
|
+
parameters: {
|
|
771
|
+
type: "object",
|
|
772
|
+
properties: {
|
|
773
|
+
target: { type: "string", enum: ["video", "dynamic", "article"], default: "video" },
|
|
774
|
+
id: { type: "string" },
|
|
775
|
+
rootRpid: { type: "integer", description: "根评论 rpid" },
|
|
776
|
+
pageSize: { type: "integer", default: 20 },
|
|
777
|
+
},
|
|
778
|
+
required: ["id", "rootRpid"],
|
|
779
|
+
},
|
|
780
|
+
handler: async (a) => {
|
|
781
|
+
const c = await getClient();
|
|
782
|
+
const target = String(a.target ?? "video");
|
|
783
|
+
const root = Number(a.rootRpid);
|
|
784
|
+
const ps = Number(a.pageSize ?? 20);
|
|
785
|
+
let items;
|
|
786
|
+
if (target === "video")
|
|
787
|
+
[items] = await c.comment.repliesVideo(a.id, root, 1, ps);
|
|
788
|
+
else if (target === "dynamic")
|
|
789
|
+
[items] = await c.comment.repliesDynamic(a.id, root, 1, ps);
|
|
790
|
+
else if (target === "article")
|
|
791
|
+
[items] = await c.comment.repliesArticle(a.id, root, 1, ps);
|
|
792
|
+
else
|
|
793
|
+
throw new Error("target 只支持 video / dynamic / article");
|
|
794
|
+
return { replies: items.map((x) => x.toDict()) };
|
|
795
|
+
},
|
|
796
|
+
},
|
|
797
|
+
{
|
|
798
|
+
id: "bili_comment_reply",
|
|
799
|
+
description: "回复某条评论(楼中楼,写操作,需登录)。",
|
|
800
|
+
parameters: {
|
|
801
|
+
type: "object",
|
|
802
|
+
properties: {
|
|
803
|
+
target: { type: "string", enum: ["video", "dynamic", "article"], default: "video" },
|
|
804
|
+
id: { type: "string" },
|
|
805
|
+
rootRpid: { type: "integer" },
|
|
806
|
+
message: { type: "string" },
|
|
807
|
+
parentRpid: { type: "integer", description: "被回复的评论 rpid(可选,默认同 rootRpid)" },
|
|
808
|
+
},
|
|
809
|
+
required: ["id", "rootRpid", "message"],
|
|
810
|
+
},
|
|
811
|
+
handler: async (a) => {
|
|
812
|
+
const c = await getClient();
|
|
813
|
+
const target = String(a.target ?? "video");
|
|
814
|
+
const root = Number(a.rootRpid);
|
|
815
|
+
const parent = a.parentRpid != null ? Number(a.parentRpid) : root;
|
|
816
|
+
const message = String(a.message ?? "");
|
|
817
|
+
let comment;
|
|
818
|
+
if (target === "video")
|
|
819
|
+
comment = await c.comment.replyVideo(a.id, root, message, parent);
|
|
820
|
+
else if (target === "dynamic")
|
|
821
|
+
comment = await c.comment.sendDynamic(a.id, message, root, parent);
|
|
822
|
+
else if (target === "article")
|
|
823
|
+
comment = await c.comment.sendArticle(a.id, message, root, parent);
|
|
824
|
+
else
|
|
825
|
+
throw new Error("target 只支持 video / dynamic / article");
|
|
826
|
+
return comment.toDict();
|
|
827
|
+
},
|
|
828
|
+
},
|
|
829
|
+
{
|
|
830
|
+
id: "bili_comment_delete",
|
|
831
|
+
description: "删除自己发表的评论(写操作,需登录,谨慎)。",
|
|
832
|
+
parameters: {
|
|
833
|
+
type: "object",
|
|
834
|
+
properties: {
|
|
835
|
+
target: { type: "string", enum: ["video", "dynamic", "article"], default: "video" },
|
|
836
|
+
id: { type: "string" },
|
|
837
|
+
rpid: { type: "integer" },
|
|
838
|
+
},
|
|
839
|
+
required: ["id", "rpid"],
|
|
840
|
+
},
|
|
841
|
+
handler: async (a) => {
|
|
842
|
+
const c = await getClient();
|
|
843
|
+
const target = String(a.target ?? "video");
|
|
844
|
+
const rpid = Number(a.rpid);
|
|
845
|
+
if (target === "video")
|
|
846
|
+
await c.comment.deleteVideo(a.id, rpid);
|
|
847
|
+
else if (target === "dynamic")
|
|
848
|
+
await c.comment.deleteDynamic(a.id, rpid);
|
|
849
|
+
else if (target === "article")
|
|
850
|
+
await c.comment.deleteArticle(a.id, rpid);
|
|
851
|
+
else
|
|
852
|
+
throw new Error("target 只支持 video / dynamic / article");
|
|
853
|
+
return { ok: true };
|
|
854
|
+
},
|
|
855
|
+
},
|
|
856
|
+
// ---- 消息中心补充 ----
|
|
857
|
+
{
|
|
858
|
+
id: "bili_notify_ats",
|
|
859
|
+
description: "获取「@我的」消息列表(需登录)。",
|
|
860
|
+
parameters: { type: "object", properties: {} },
|
|
861
|
+
handler: async () => {
|
|
862
|
+
const [items, , isEnd] = await (await getClient()).notify.ats();
|
|
863
|
+
return { isEnd, notices: items.map((x) => x.toDict()) };
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
{
|
|
867
|
+
id: "bili_notify_likes",
|
|
868
|
+
description: "获取「收到的赞」列表(需登录)。",
|
|
869
|
+
parameters: { type: "object", properties: {} },
|
|
870
|
+
handler: async () => {
|
|
871
|
+
const [items, , isEnd] = await (await getClient()).notify.likes();
|
|
872
|
+
return { isEnd, notices: items.map((x) => x.toDict()) };
|
|
873
|
+
},
|
|
874
|
+
},
|
|
875
|
+
// ---- 私信补充 ----
|
|
876
|
+
{
|
|
877
|
+
id: "bili_message_unread",
|
|
878
|
+
description: "获取私信未读数(需登录)。",
|
|
879
|
+
parameters: { type: "object", properties: {} },
|
|
880
|
+
handler: async () => (await getClient()).message.unread(),
|
|
881
|
+
},
|
|
882
|
+
{
|
|
883
|
+
id: "bili_message_history",
|
|
884
|
+
description: "获取与某用户的私信聊天记录(需登录)。",
|
|
885
|
+
parameters: {
|
|
886
|
+
type: "object",
|
|
887
|
+
properties: {
|
|
888
|
+
talkerUid: { type: "integer" },
|
|
889
|
+
size: { type: "integer", default: 20 },
|
|
890
|
+
},
|
|
891
|
+
required: ["talkerUid"],
|
|
892
|
+
},
|
|
893
|
+
handler: async (a) => {
|
|
894
|
+
const msgs = await (await getClient()).message.history(Number(a.talkerUid), Number(a.size ?? 20));
|
|
895
|
+
return { messages: msgs.map((m) => m.toDict()) };
|
|
896
|
+
},
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
id: "bili_message_recall",
|
|
900
|
+
description: "撤回一条已发送的私信(写操作,需登录,通常有时限)。",
|
|
901
|
+
parameters: {
|
|
902
|
+
type: "object",
|
|
903
|
+
properties: {
|
|
904
|
+
receiverUid: { type: "integer" },
|
|
905
|
+
msgKey: { type: "integer" },
|
|
906
|
+
},
|
|
907
|
+
required: ["receiverUid", "msgKey"],
|
|
908
|
+
},
|
|
909
|
+
handler: async (a) => {
|
|
910
|
+
await (await getClient()).message.recall(Number(a.receiverUid), Number(a.msgKey));
|
|
911
|
+
return { ok: true };
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
// ---- 关注统计 ----
|
|
915
|
+
{
|
|
916
|
+
id: "bili_relation_stat",
|
|
917
|
+
description: "获取某用户的关注数 / 粉丝数统计。",
|
|
918
|
+
parameters: {
|
|
919
|
+
type: "object",
|
|
920
|
+
properties: { uid: { type: "integer" } },
|
|
921
|
+
required: ["uid"],
|
|
922
|
+
},
|
|
923
|
+
handler: async (a) => (await getClient()).relation.stat(Number(a.uid)),
|
|
924
|
+
},
|
|
925
|
+
// ---- 个人资料 ----
|
|
926
|
+
{
|
|
927
|
+
id: "bili_profile_info",
|
|
928
|
+
description: "获取我的个人资料(昵称、等级、硬币、签名等,需登录)。",
|
|
929
|
+
parameters: { type: "object", properties: {} },
|
|
930
|
+
handler: async () => (await getClient()).profile.info(),
|
|
931
|
+
},
|
|
932
|
+
{
|
|
933
|
+
id: "bili_profile_update_sign",
|
|
934
|
+
description: "修改我的个性签名(写操作,需登录,会进入审核)。",
|
|
935
|
+
parameters: {
|
|
936
|
+
type: "object",
|
|
937
|
+
properties: { sign: { type: "string" } },
|
|
938
|
+
required: ["sign"],
|
|
939
|
+
},
|
|
940
|
+
handler: async (a) => {
|
|
941
|
+
await (await getClient()).profile.updateSign(String(a.sign ?? ""));
|
|
942
|
+
return { ok: true };
|
|
943
|
+
},
|
|
944
|
+
},
|
|
945
|
+
{
|
|
946
|
+
id: "bili_profile_update",
|
|
947
|
+
description: "修改我的昵称 / 性别 / 生日(写操作,需登录,至少改一项)。",
|
|
948
|
+
parameters: {
|
|
949
|
+
type: "object",
|
|
950
|
+
properties: {
|
|
951
|
+
uname: { type: "string", description: "新昵称(1~20 字符)" },
|
|
952
|
+
sex: { type: "string", enum: ["男", "女", "保密"] },
|
|
953
|
+
birthday: { type: "string", description: "YYYY-MM-DD" },
|
|
954
|
+
},
|
|
955
|
+
},
|
|
956
|
+
handler: async (a) => {
|
|
957
|
+
await (await getClient()).profile.updateProfile(a.uname != null ? String(a.uname) : undefined, a.sex != null ? String(a.sex) : undefined, a.birthday != null ? String(a.birthday) : undefined);
|
|
958
|
+
return { ok: true };
|
|
959
|
+
},
|
|
960
|
+
},
|
|
494
961
|
// ---- 登录状态 ----
|
|
495
962
|
{
|
|
496
963
|
id: "bili_login_status",
|
|
@@ -560,7 +1027,8 @@ export const TOOLS = [
|
|
|
560
1027
|
width: 320,
|
|
561
1028
|
});
|
|
562
1029
|
qrDataUrl = `data:image/png;base64,${png.toString("base64")}`;
|
|
563
|
-
qrImagePath = resolve(process.
|
|
1030
|
+
qrImagePath = resolve(process.env.BILI_QR_FILE ?? resolve(dataDir(), "bili_qrcode.png"));
|
|
1031
|
+
mkdirSync(dirname(qrImagePath), { recursive: true });
|
|
564
1032
|
writeFileSync(qrImagePath, png);
|
|
565
1033
|
}
|
|
566
1034
|
catch {
|