dsh-shlr-bilitoolkit 0.1.13 → 0.1.15
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/AGENTS.md +1 -1
- package/dist/dsh.js +77 -135
- package/package.json +1 -1
- package/README.md +0 -125
package/AGENTS.md
CHANGED
package/dist/dsh.js
CHANGED
|
@@ -141,31 +141,38 @@ export const TOOLS = [
|
|
|
141
141
|
},
|
|
142
142
|
{
|
|
143
143
|
id: "bili_video_subtitle",
|
|
144
|
-
description: "
|
|
144
|
+
description: "视频字幕:默认返回字幕纯文本(优先中文);list=true 时返回可用字幕轨列表。",
|
|
145
145
|
parameters: {
|
|
146
146
|
type: "object",
|
|
147
147
|
properties: {
|
|
148
148
|
bvid: { type: "string" },
|
|
149
149
|
lang: { type: "string", default: "zh" },
|
|
150
|
+
list: { type: "boolean", default: false, description: "true 则只列字幕轨,不取正文" },
|
|
150
151
|
},
|
|
151
152
|
required: ["bvid"],
|
|
152
153
|
},
|
|
153
154
|
handler: async (a) => {
|
|
154
155
|
const c = await getClient();
|
|
156
|
+
if (Boolean(a.list ?? false)) {
|
|
157
|
+
const subs = await c.video.subtitles(a.bvid);
|
|
158
|
+
return subs.map((s) => s.toDict());
|
|
159
|
+
}
|
|
155
160
|
return { text: await c.video.subtitleText(a.bvid, a.lang ?? "zh") };
|
|
156
161
|
},
|
|
157
162
|
},
|
|
158
163
|
{
|
|
159
|
-
id: "
|
|
160
|
-
description: "搜索 B
|
|
164
|
+
id: "bili_search",
|
|
165
|
+
description: "搜索 B 站视频或用户。",
|
|
161
166
|
parameters: {
|
|
162
167
|
type: "object",
|
|
163
168
|
properties: {
|
|
164
169
|
keyword: { type: "string" },
|
|
170
|
+
type: { type: "string", enum: ["video", "user"], default: "video" },
|
|
165
171
|
order: {
|
|
166
172
|
type: "string",
|
|
167
173
|
enum: ["totalrank", "click", "pubdate", "dm", "stow", "scores"],
|
|
168
174
|
default: "totalrank",
|
|
175
|
+
description: "排序(仅视频)",
|
|
169
176
|
},
|
|
170
177
|
pageSize: { type: "integer", default: 20 },
|
|
171
178
|
},
|
|
@@ -173,27 +180,15 @@ export const TOOLS = [
|
|
|
173
180
|
},
|
|
174
181
|
handler: async (a) => {
|
|
175
182
|
const c = await getClient();
|
|
176
|
-
const
|
|
183
|
+
const ps = Number(a.pageSize ?? 20);
|
|
184
|
+
if (String(a.type ?? "video") === "user") {
|
|
185
|
+
const [users, total] = await c.search.users(a.keyword, 1, ps);
|
|
186
|
+
return { total, users: users.map((u) => u.toDict()) };
|
|
187
|
+
}
|
|
188
|
+
const [videos, total] = await c.search.videos(a.keyword, 1, ps, a.order ?? "totalrank");
|
|
177
189
|
return { total, videos: videos.map((v) => v.toDict()) };
|
|
178
190
|
},
|
|
179
191
|
},
|
|
180
|
-
{
|
|
181
|
-
id: "bili_search_user",
|
|
182
|
-
description: "搜索 B 站用户。",
|
|
183
|
-
parameters: {
|
|
184
|
-
type: "object",
|
|
185
|
-
properties: {
|
|
186
|
-
keyword: { type: "string" },
|
|
187
|
-
pageSize: { type: "integer", default: 20 },
|
|
188
|
-
},
|
|
189
|
-
required: ["keyword"],
|
|
190
|
-
},
|
|
191
|
-
handler: async (a) => {
|
|
192
|
-
const c = await getClient();
|
|
193
|
-
const [users, total] = await c.search.users(a.keyword, 1, Number(a.pageSize ?? 20));
|
|
194
|
-
return { total, users: users.map((u) => u.toDict()) };
|
|
195
|
-
},
|
|
196
|
-
},
|
|
197
192
|
{
|
|
198
193
|
id: "bili_space_info",
|
|
199
194
|
description: "获取 B 站用户空间基本信息(昵称、等级、签名)。",
|
|
@@ -291,54 +286,50 @@ export const TOOLS = [
|
|
|
291
286
|
// ---- 评论 ----
|
|
292
287
|
{
|
|
293
288
|
id: "bili_comment_list",
|
|
294
|
-
description: "
|
|
289
|
+
description: "获取评论列表(视频 / 动态 / 专栏),返回评论列表。",
|
|
295
290
|
parameters: {
|
|
296
291
|
type: "object",
|
|
297
292
|
properties: {
|
|
298
|
-
|
|
293
|
+
target: { type: "string", enum: ["video", "dynamic", "article"], default: "video" },
|
|
294
|
+
id: { type: "string", description: "视频 bvid / 动态 id / 专栏 cv 号" },
|
|
299
295
|
sort: { type: "string", enum: ["time", "like", "reply"], default: "time" },
|
|
300
296
|
pageSize: { type: "integer", default: 20 },
|
|
301
297
|
},
|
|
302
|
-
required: ["
|
|
298
|
+
required: ["id"],
|
|
303
299
|
},
|
|
304
300
|
handler: async (a) => {
|
|
305
301
|
const c = await getClient();
|
|
306
|
-
const
|
|
307
|
-
|
|
302
|
+
const target = String(a.target ?? "video");
|
|
303
|
+
const ps = Number(a.pageSize ?? 20);
|
|
304
|
+
const sort = a.sort ?? "time";
|
|
305
|
+
let items;
|
|
306
|
+
if (target === "dynamic")
|
|
307
|
+
[items] = await c.comment.listDynamic(a.id, 1, ps, sort);
|
|
308
|
+
else if (target === "article")
|
|
309
|
+
[items] = await c.comment.listArticle(a.id, 1, ps, sort);
|
|
310
|
+
else
|
|
311
|
+
[items] = await c.comment.listVideo(a.id, 1, ps, sort);
|
|
312
|
+
return { comments: items.map((x) => x.toDict()) };
|
|
308
313
|
},
|
|
309
314
|
},
|
|
310
315
|
// ---- 关注关系 ----
|
|
311
316
|
{
|
|
312
|
-
id: "
|
|
313
|
-
description: "
|
|
314
|
-
parameters: {
|
|
315
|
-
type: "object",
|
|
316
|
-
properties: {
|
|
317
|
-
uid: { type: "integer" },
|
|
318
|
-
pageSize: { type: "integer", default: 20 },
|
|
319
|
-
},
|
|
320
|
-
required: ["uid"],
|
|
321
|
-
},
|
|
322
|
-
handler: async (a) => {
|
|
323
|
-
const c = await getClient();
|
|
324
|
-
const [users, total] = await c.relation.followings(Number(a.uid), 1, Number(a.pageSize ?? 20));
|
|
325
|
-
return { total, users: users.map((x) => x.toDict()) };
|
|
326
|
-
},
|
|
327
|
-
},
|
|
328
|
-
{
|
|
329
|
-
id: "bili_relation_followers",
|
|
330
|
-
description: "获取某用户的粉丝列表。",
|
|
317
|
+
id: "bili_relation_list",
|
|
318
|
+
description: "获取某用户的关注列表或粉丝列表。",
|
|
331
319
|
parameters: {
|
|
332
320
|
type: "object",
|
|
333
321
|
properties: {
|
|
334
322
|
uid: { type: "integer" },
|
|
323
|
+
type: { type: "string", enum: ["followings", "followers"], default: "followings" },
|
|
335
324
|
pageSize: { type: "integer", default: 20 },
|
|
336
325
|
},
|
|
337
326
|
required: ["uid"],
|
|
338
327
|
},
|
|
339
328
|
handler: async (a) => {
|
|
340
329
|
const c = await getClient();
|
|
341
|
-
const [users, total] =
|
|
330
|
+
const [users, total] = String(a.type ?? "followings") === "followers"
|
|
331
|
+
? await c.relation.followers(Number(a.uid), 1, Number(a.pageSize ?? 20))
|
|
332
|
+
: await c.relation.followings(Number(a.uid), 1, Number(a.pageSize ?? 20));
|
|
342
333
|
return { total, users: users.map((x) => x.toDict()) };
|
|
343
334
|
},
|
|
344
335
|
},
|
|
@@ -349,16 +340,6 @@ export const TOOLS = [
|
|
|
349
340
|
parameters: { type: "object", properties: {} },
|
|
350
341
|
handler: async () => (await getClient()).notify.unread(),
|
|
351
342
|
},
|
|
352
|
-
{
|
|
353
|
-
id: "bili_notify_replies",
|
|
354
|
-
description: "获取「回复我的」消息列表(需登录)。",
|
|
355
|
-
parameters: { type: "object", properties: {} },
|
|
356
|
-
handler: async () => {
|
|
357
|
-
const c = await getClient();
|
|
358
|
-
const [items, cursor, isEnd] = await c.notify.replies();
|
|
359
|
-
return { isEnd, cursor, notices: items.map((x) => x.toDict()) };
|
|
360
|
-
},
|
|
361
|
-
},
|
|
362
343
|
// ---- 收藏夹(读取) ----
|
|
363
344
|
{
|
|
364
345
|
id: "bili_fav_folders",
|
|
@@ -401,35 +382,24 @@ export const TOOLS = [
|
|
|
401
382
|
},
|
|
402
383
|
{
|
|
403
384
|
id: "bili_favorite",
|
|
404
|
-
description: "
|
|
385
|
+
description: "收藏或取消收藏视频到收藏夹(写操作,需登录)。",
|
|
405
386
|
parameters: {
|
|
406
387
|
type: "object",
|
|
407
388
|
properties: {
|
|
408
389
|
bvid: { type: "string" },
|
|
390
|
+
action: { type: "string", enum: ["add", "remove"], default: "add" },
|
|
409
391
|
folderName: { type: "string" },
|
|
410
392
|
},
|
|
411
393
|
required: ["bvid"],
|
|
412
394
|
},
|
|
413
|
-
handler: async (a) => {
|
|
414
|
-
const fid = await (await getClient()).interact.favorite(a.bvid, undefined, a.folderName);
|
|
415
|
-
return { folderId: fid };
|
|
416
|
-
},
|
|
417
|
-
},
|
|
418
|
-
{
|
|
419
|
-
id: "bili_send_comment",
|
|
420
|
-
description: "发表视频评论(写操作,需登录)。",
|
|
421
|
-
parameters: {
|
|
422
|
-
type: "object",
|
|
423
|
-
properties: {
|
|
424
|
-
bvid: { type: "string" },
|
|
425
|
-
message: { type: "string" },
|
|
426
|
-
},
|
|
427
|
-
required: ["bvid", "message"],
|
|
428
|
-
},
|
|
429
395
|
handler: async (a) => {
|
|
430
396
|
const c = await getClient();
|
|
431
|
-
|
|
432
|
-
|
|
397
|
+
if (String(a.action ?? "add") === "remove") {
|
|
398
|
+
const fid = await c.interact.unfavorite(a.bvid, undefined, a.folderName);
|
|
399
|
+
return { ok: true, folderId: fid };
|
|
400
|
+
}
|
|
401
|
+
const fid = await c.interact.favorite(a.bvid, undefined, a.folderName);
|
|
402
|
+
return { ok: true, folderId: fid };
|
|
433
403
|
},
|
|
434
404
|
},
|
|
435
405
|
{
|
|
@@ -522,19 +492,6 @@ export const TOOLS = [
|
|
|
522
492
|
return pages.map((p) => p.toDict());
|
|
523
493
|
},
|
|
524
494
|
},
|
|
525
|
-
{
|
|
526
|
-
id: "bili_video_subtitles",
|
|
527
|
-
description: "列出视频可用的字幕轨(语言 / 名称 / 链接),不含正文。",
|
|
528
|
-
parameters: {
|
|
529
|
-
type: "object",
|
|
530
|
-
properties: { bvid: { type: "string" } },
|
|
531
|
-
required: ["bvid"],
|
|
532
|
-
},
|
|
533
|
-
handler: async (a) => {
|
|
534
|
-
const subs = await (await getClient()).video.subtitles(a.bvid);
|
|
535
|
-
return subs.map((s) => s.toDict());
|
|
536
|
-
},
|
|
537
|
-
},
|
|
538
495
|
{
|
|
539
496
|
id: "bili_video_playurl",
|
|
540
497
|
description: "获取视频播放地址信息(清晰度、可用清晰度列表、durl 直链)。高清晰度可能需登录 / 大会员。",
|
|
@@ -668,22 +625,6 @@ export const TOOLS = [
|
|
|
668
625
|
return { ok: true };
|
|
669
626
|
},
|
|
670
627
|
},
|
|
671
|
-
{
|
|
672
|
-
id: "bili_unfavorite",
|
|
673
|
-
description: "把视频从收藏夹移除(写操作,需登录)。",
|
|
674
|
-
parameters: {
|
|
675
|
-
type: "object",
|
|
676
|
-
properties: {
|
|
677
|
-
bvid: { type: "string" },
|
|
678
|
-
folderName: { type: "string", description: "收藏夹名(可选,默认取默认收藏夹)" },
|
|
679
|
-
},
|
|
680
|
-
required: ["bvid"],
|
|
681
|
-
},
|
|
682
|
-
handler: async (a) => {
|
|
683
|
-
const fid = await (await getClient()).interact.unfavorite(a.bvid, undefined, a.folderName);
|
|
684
|
-
return { ok: true, folderId: fid };
|
|
685
|
-
},
|
|
686
|
-
},
|
|
687
628
|
{
|
|
688
629
|
id: "bili_user_following",
|
|
689
630
|
description: "查询我是否已关注某用户。",
|
|
@@ -885,21 +826,25 @@ export const TOOLS = [
|
|
|
885
826
|
},
|
|
886
827
|
// ---- 消息中心补充 ----
|
|
887
828
|
{
|
|
888
|
-
id: "
|
|
889
|
-
description: "
|
|
890
|
-
parameters: {
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
829
|
+
id: "bili_notify_list",
|
|
830
|
+
description: "消息中心列表:回复我的 / @我的 / 收到的赞(需登录)。",
|
|
831
|
+
parameters: {
|
|
832
|
+
type: "object",
|
|
833
|
+
properties: {
|
|
834
|
+
type: { type: "string", enum: ["replies", "ats", "likes"], default: "replies" },
|
|
835
|
+
},
|
|
894
836
|
},
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
837
|
+
handler: async (a) => {
|
|
838
|
+
const c = await getClient();
|
|
839
|
+
const t = String(a.type ?? "replies");
|
|
840
|
+
let items;
|
|
841
|
+
if (t === "ats")
|
|
842
|
+
[items] = await c.notify.ats();
|
|
843
|
+
else if (t === "likes")
|
|
844
|
+
[items] = await c.notify.likes();
|
|
845
|
+
else
|
|
846
|
+
[items] = await c.notify.replies();
|
|
847
|
+
return { notices: items.map((x) => x.toDict()) };
|
|
903
848
|
},
|
|
904
849
|
},
|
|
905
850
|
// ---- 私信补充 ----
|
|
@@ -959,39 +904,36 @@ export const TOOLS = [
|
|
|
959
904
|
parameters: { type: "object", properties: {} },
|
|
960
905
|
handler: async () => (await getClient()).profile.info(),
|
|
961
906
|
},
|
|
962
|
-
{
|
|
963
|
-
id: "bili_profile_update_sign",
|
|
964
|
-
description: "修改我的个性签名(写操作,需登录,会进入审核)。",
|
|
965
|
-
parameters: {
|
|
966
|
-
type: "object",
|
|
967
|
-
properties: { sign: { type: "string" } },
|
|
968
|
-
required: ["sign"],
|
|
969
|
-
},
|
|
970
|
-
handler: async (a) => {
|
|
971
|
-
await (await getClient()).profile.updateSign(String(a.sign ?? ""));
|
|
972
|
-
return { ok: true };
|
|
973
|
-
},
|
|
974
|
-
},
|
|
975
907
|
{
|
|
976
908
|
id: "bili_profile_update",
|
|
977
|
-
description: "修改我的昵称 / 性别 /
|
|
909
|
+
description: "修改我的昵称 / 性别 / 生日 / 签名(写操作,需登录,至少改一项)。",
|
|
978
910
|
parameters: {
|
|
979
911
|
type: "object",
|
|
980
912
|
properties: {
|
|
981
913
|
uname: { type: "string", description: "新昵称(1~20 字符)" },
|
|
982
914
|
sex: { type: "string", enum: ["男", "女", "保密"] },
|
|
983
915
|
birthday: { type: "string", description: "YYYY-MM-DD" },
|
|
916
|
+
sign: { type: "string", description: "个性签名(≤70 字)" },
|
|
984
917
|
},
|
|
985
918
|
},
|
|
986
919
|
handler: async (a) => {
|
|
987
|
-
|
|
920
|
+
const c = await getClient();
|
|
921
|
+
const hasProfile = a.uname != null || a.sex != null || a.birthday != null;
|
|
922
|
+
if (a.sign == null && !hasProfile) {
|
|
923
|
+
throw new Error("至少修改一项:uname / sex / birthday / sign");
|
|
924
|
+
}
|
|
925
|
+
if (a.sign != null)
|
|
926
|
+
await c.profile.updateSign(String(a.sign));
|
|
927
|
+
if (hasProfile) {
|
|
928
|
+
await c.profile.updateProfile(a.uname != null ? String(a.uname) : undefined, a.sex != null ? String(a.sex) : undefined, a.birthday != null ? String(a.birthday) : undefined);
|
|
929
|
+
}
|
|
988
930
|
return { ok: true };
|
|
989
931
|
},
|
|
990
932
|
},
|
|
991
933
|
// ---- 登录状态 ----
|
|
992
934
|
{
|
|
993
935
|
id: "bili_login_status",
|
|
994
|
-
description: "
|
|
936
|
+
description: "查登录态(返回用户名)。想登录或报未登录(-101)时先调本工具;未登录则必须立即调 bili_gen_qr 展示二维码图片。刚调过 bili_gen_qr 时本工具会顺带轮询,用户确认后自动保存 Cookie。",
|
|
995
937
|
parameters: { type: "object", properties: {} },
|
|
996
938
|
handler: async () => {
|
|
997
939
|
const c = await getClient();
|
|
@@ -1040,7 +982,7 @@ export const TOOLS = [
|
|
|
1040
982
|
},
|
|
1041
983
|
{
|
|
1042
984
|
id: "bili_gen_qr",
|
|
1043
|
-
description: "
|
|
985
|
+
description: "【登录唯一入口】生成扫码登录二维码。必须把返回的二维码图片展示给用户扫(用 qrDataUrl / qrImageUrl / qrImagePath 渲染成图片),不要只给链接。返回 url / qrcodeKey / qrDataUrl / qrImageUrl / qrImagePath。用户确认后调 bili_login_status 完成登录。",
|
|
1044
986
|
parameters: { type: "object", properties: {} },
|
|
1045
987
|
handler: async () => {
|
|
1046
988
|
const c = await getClient();
|
package/package.json
CHANGED
package/README.md
DELETED
|
@@ -1,125 +0,0 @@
|
|
|
1
|
-
# dsh-shlr-bilitoolkit
|
|
2
|
-
|
|
3
|
-
B 站(哔哩哔哩)接口封装的 **TypeScript** 移植版,作为 [dsh](https://dshmp.com)(DeepSeek Harness)插件提供「视频 / 搜索 / 空间」等读取能力。
|
|
4
|
-
|
|
5
|
-
> 本项目从原 Python 工具箱(`bili/`)完整移植而来,覆盖视频、搜索、空间、评论、私信、互动(点赞/收藏/投币/关注/三连)、动态、专栏、关注关系、消息中心、个人资料与扫码登录,并作为 [dsh](https://dshmp.com)(DeepSeek Harness)插件提供上述能力。
|
|
6
|
-
|
|
7
|
-
## 目录结构
|
|
8
|
-
|
|
9
|
-
```
|
|
10
|
-
dsh-shlr-bilitoolkit/
|
|
11
|
-
├── src/
|
|
12
|
-
│ ├── client.ts # BiliClient:Session / Cookie / 请求 / 节流 / 重试 / 风控 / 表单编码
|
|
13
|
-
│ ├── wbi.ts # WbiSigner:w_rid / wts 签名(含官方 mixin_key 算法)
|
|
14
|
-
│ ├── errors.ts # BiliApiError / CaptchaRequired / CookieError
|
|
15
|
-
│ ├── models.ts # Video / Article / Comment / PrivateMessage / RelationUser /
|
|
16
|
-
│ │ # Dynamic / Notice / User / Page / PlayInfo / Subtitle / Collection
|
|
17
|
-
│ ├── formatting.ts # 播放量、时长、HTML 清理等展示格式化
|
|
18
|
-
│ ├── pagination.ts # iterPages:统一翻页异步生成器
|
|
19
|
-
│ ├── endpoints.ts # 所有接口 URL 常量
|
|
20
|
-
│ ├── apis/
|
|
21
|
-
│ │ ├── video.ts # 推荐流 / 详情 / 分P / 播放地址 / 字幕
|
|
22
|
-
│ │ ├── search.ts # 搜索视频 / 用户
|
|
23
|
-
│ │ ├── space.ts # 空间信息 / 投稿 / 合集
|
|
24
|
-
│ │ ├── comment.ts # 评论:视频/动态/专栏 的读取与发表/回复/删除
|
|
25
|
-
│ │ ├── message.ts # 私信:发送 / 撤回 / 记录 / 未读
|
|
26
|
-
│ │ ├── interact.ts # 点赞 / 收藏 / 投币 / 关注 / 三连
|
|
27
|
-
│ │ ├── dynamic.ts # 动态:关注流 / 空间 / 发布 / 转发 / 点赞 / 删除
|
|
28
|
-
│ │ ├── article.ts # 专栏:详情 / UP 主列表
|
|
29
|
-
│ │ ├── relation.ts # 关注 / 粉丝 / 统计
|
|
30
|
-
│ │ ├── notify.ts # 消息中心:未读 / 回复我的 / @我的 / 收到的赞
|
|
31
|
-
│ │ ├── profile.ts # 个人资料:读取 / 改签名 / 改昵称性别生日
|
|
32
|
-
│ │ ├── auth.ts # 扫码登录(generate / poll / wait / login)
|
|
33
|
-
│ │ └── download.ts # 下载与 ffmpeg 合并工具
|
|
34
|
-
│ ├── dsh.ts # dsh 插件入口:apply(ctx) + 工具列表
|
|
35
|
-
│ └── index.ts # 对外导出
|
|
36
|
-
├── tests/ # 离线单测(wbi 签名、格式化、模型)
|
|
37
|
-
├── cordis.patch.yml # dsh bundle 的配置补丁
|
|
38
|
-
├── dsh-manifest.json # 市场目录收录清单(schema v1)
|
|
39
|
-
├── package.json # 含 dsh.bundle.patch 声明
|
|
40
|
-
└── tsconfig.json
|
|
41
|
-
```
|
|
42
|
-
|
|
43
|
-
## 构建与测试
|
|
44
|
-
|
|
45
|
-
```bash
|
|
46
|
-
npm install
|
|
47
|
-
npm run typecheck # tsc --noEmit
|
|
48
|
-
npm test # 离线单测(node --test + tsx)
|
|
49
|
-
npm run build # 产出 dist/
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
> 说明:`tsconfig` 使用 `moduleResolution: Bundler`,相对导入不带扩展名,适合被 dsh 运行时(基于打包器的 cordis 应用)消费。若要在原生 Node ESM 下直接 `node dist/index.js`,需改用 `NodeNext` 并补 `.js` 扩展名。
|
|
53
|
-
|
|
54
|
-
## 当作库用
|
|
55
|
-
|
|
56
|
-
```ts
|
|
57
|
-
import { BiliClient } from "dsh-shlr-bilitoolkit";
|
|
58
|
-
|
|
59
|
-
const client = new BiliClient({ cookieFile: "bili_cookies.json" });
|
|
60
|
-
await client.loadCookies();
|
|
61
|
-
|
|
62
|
-
const videos = await client.video.recommend(2, 10);
|
|
63
|
-
const detail = await client.video.detail("BV1xx411c7mD");
|
|
64
|
-
const text = await client.video.subtitleText("BV1xx411c7mD"); // 字幕纯文本
|
|
65
|
-
const [videos2, total] = await client.search.videos("孤独摇滚");
|
|
66
|
-
const info = await client.space.info(574115689);
|
|
67
|
-
const [ups, _] = await client.space.videos(574115689, 1, 10);
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
## 作为 dsh 插件
|
|
71
|
-
|
|
72
|
-
- 包名 `dsh-shlr-bilitoolkit`(符合 `dsh-` 前缀约定),`package.json` 里声明了 `dsh.bundle.patch: ./cordis.patch.yml`,因此会被 dsh 识别为插件而非普通库。
|
|
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` / `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_comment_like` / `bili_message_recall` / `bili_profile_update_sign` / `bili_profile_update`(均会真实修改账号状态,需登录)
|
|
76
|
-
- 辅助:`bili_login_status`(查登录态)/ `bili_gen_qr`(生成扫码登录二维码)/ `bili_cache_clear`(清空本地缓存)
|
|
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` / `BILI_DEVICE_FILE` 可精确指定单个文件。设备号(私信 `dev_id`)会落盘到 `bili_data/bili_device.json`,跨重启保持稳定,避免被 B 站当成新设备触发风控。兼容旧版:若新目录还没有 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 / 登录态)。
|
|
81
|
-
|
|
82
|
-
> **给 Agent 的约定**:仓库根目录的 [`AGENTS.md`](./AGENTS.md) 写明了登录流程——需要登录时必须调用 `bili_gen_qr` 并把二维码**图片**展示给用户,而不是只给链接或 ASCII。把这份 `AGENTS.md` 放到 dsh 的工作目录(或确保你的 dsh 会读取它),agent 就会按约定输出登录二维码。
|
|
83
|
-
|
|
84
|
-
安装到 dsh(任选其一,取决于你的发行方式):
|
|
85
|
-
|
|
86
|
-
```bash
|
|
87
|
-
dsh plugin --profile <name> add dsh-shlr-bilitoolkit # 已发布到 npm
|
|
88
|
-
dsh plugin --profile <name> add github:your-name/dsh-shlr-bilitoolkit#<ref>
|
|
89
|
-
```
|
|
90
|
-
|
|
91
|
-
> 插件入口已对齐 **官方 DeepSeek Harness(cordis 体系)** 的约定:模块导出 `name` / `inject = ["tools"]` / `apply(ctx)`,并在 `apply` 内用官方 `ctx.tools.register(defineTool({ name, description, parameters, output, execute }))` 注册每个工具(`defineTool` 来自 `@deepseek-ai/dsh-tools`,由宿主提供、插件不打包,保证单实例)。`parameters` 用官方简化 schema(`{ 参数名: { type, required, description, enum? } }`),工具返回规范 JSON 值,由 `output.render` 转成面向模型的文本。`cordis.patch.yml` 是官方组合包配置层(`- insert: - id: bili name: dsh-shlr-bilitoolkit`,按包名解析到 `main` 入口)。若你的 dsh 版本较旧、没有 `ctx.tools`,`apply` 仍保留了 `ctx.registerTool` / `ctx.services.tools.register` 的回退路径。
|
|
92
|
-
|
|
93
|
-
## 与原 Python 版的对应关系
|
|
94
|
-
|
|
95
|
-
| Python | TS |
|
|
96
|
-
| --- | --- |
|
|
97
|
-
| `bili/client.py` | `src/client.ts` |
|
|
98
|
-
| `bili/wbi.py` | `src/wbi.ts` |
|
|
99
|
-
| `bili/video.py` | `src/apis/video.ts` |
|
|
100
|
-
| `bili/search.py` | `src/apis/search.ts` |
|
|
101
|
-
| `bili/space.py` | `src/apis/space.ts` |
|
|
102
|
-
| `bili/comment.py` | `src/apis/comment.ts` |
|
|
103
|
-
| `bili/message.py` | `src/apis/message.ts` |
|
|
104
|
-
| `bili/interact.py` | `src/apis/interact.ts` |
|
|
105
|
-
| `bili/dynamic.py` | `src/apis/dynamic.ts` |
|
|
106
|
-
| `bili/article.py` | `src/apis/article.ts` |
|
|
107
|
-
| `bili/relation.py` | `src/apis/relation.ts` |
|
|
108
|
-
| `bili/notify.py` | `src/apis/notify.ts` |
|
|
109
|
-
| `bili/profile.py` | `src/apis/profile.ts` |
|
|
110
|
-
| `bili/auth.py` | `src/apis/auth.ts` |
|
|
111
|
-
| `bili/download.py` | `src/apis/download.ts` |
|
|
112
|
-
| `bili/models.py` | `src/models.ts` |
|
|
113
|
-
| `bili/formatting.py` | `src/formatting.ts` |
|
|
114
|
-
| `bili/pagination.py` | `src/pagination.ts` |
|
|
115
|
-
|
|
116
|
-
约定保持一致:`client.xxx.yyy()` 调用风格、`iter_*` 异步生成器、`toDict()` 导出、WBI 签名算法与密钥 6 小时缓存(已落地到 `bili_cache.json`,跨进程复用)。
|
|
117
|
-
|
|
118
|
-
## 与原 Python 版的差异 / 已知限制
|
|
119
|
-
|
|
120
|
-
- **写接口为表单编码**:原 Python 用 `requests` 的 `data=` 默认表单;本包的 `client.post` 默认把对象编码为 `application/x-www-form-urlencoded`,仅动态点赞等少数接口显式走 JSON(`jsonBody=true`)。
|
|
121
|
-
- **下载 / ffmpeg**:`src/apis/download.ts` 提供 `downloadUrl` / `mergeWithFfmpeg`,为纯工具函数,未接入 dsh 工具(下载行为在 dsh 里通常不合适)。
|
|
122
|
-
|
|
123
|
-
## 风控说明
|
|
124
|
-
|
|
125
|
-
本包内置与原 Python 版一致的基础降风控策略:请求节流(默认 0.6s + 0~0.4s 抖动)、GET 退避重试、写操作不重试、HTTP 412 / `-412` 立即停手、WBI 签名。但单账号操作量上限、避免短时间切 IP/UA 等仍需使用习惯配合。
|