dsh-shlr-bilitoolkit 0.1.22 → 0.1.24

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
@@ -121,7 +121,7 @@ dsh plugin --profile <name> add github:your-name/dsh-shlr-bilitoolkit#<ref>
121
121
  ## 与原 Python 版的差异 / 已知限制
122
122
 
123
123
  - **写接口为表单编码**:原 Python 用 `requests` 的 `data=` 默认表单;本包的 `client.post` 默认把对象编码为 `application/x-www-form-urlencoded`,仅动态点赞等少数接口显式走 JSON(`jsonBody=true`)。
124
- - **图文动态**:`bili_dynamic_publish` 支持「文字 + 本地图片」(先上传 B 站动态图床拿到 url/尺寸,再带 `pictures` 以 type=2 创建)。上传 / pictures 组装按社区通用接口实现,**未在真实账号验证**;若失败请把报错码反馈回来校准。
124
+ - **图文动态**:`bili_dynamic_publish` 支持「文字 + 本地图片」:先上传动态图床(`x/dynamic/feed/draw/upload_bfs`,`biz=draw` + `category=daily`)拿到 `image_url` / 宽高 / `img_size`,再以新版 `x/dynamic/feed/create/dyn` 创建(JSON,外层 `dyn_req` + `content.contents` + `pics`)。已在真实账号验证通过(发布成功、详情图片可访问)。
125
125
  - **下载 / ffmpeg**:`src/apis/download.ts` 提供 `downloadUrl` / `mergeWithFfmpeg`,为纯工具函数,未接入 dsh 工具(下载行为在 dsh 里通常不合适)。
126
126
 
127
127
  ## 风控说明
@@ -17,8 +17,8 @@ export declare class DynamicAPI {
17
17
  publish(text: string, atUids?: number[], ctrl?: any[], closeComment?: boolean): Promise<string>;
18
18
  /** 上传单张图片到 B 站动态图床,返回 pictures 需要的一段信息。 */
19
19
  private uploadImage;
20
- /** 发布图文动态(文字 + 最多若干张图片)。图片可以是本地路径或 Buffer。 */
21
- publishWithImages(text: string, images: Array<string | Buffer>, opts?: {
20
+ /** 发布图文动态(文字 + 若干张图片),走新版 JSON 创建接口。图片可传本地路径或 Buffer。 */
21
+ publishWithImages(text: string, images: Array<string | Buffer>, _opts?: {
22
22
  atUids?: number[];
23
23
  closeComment?: boolean;
24
24
  }): Promise<string>;
@@ -12,6 +12,16 @@ import { Dynamic } from "../models.js";
12
12
  import { guessImageType } from "../util.js";
13
13
  const FEATURES = "itemOpusStyle";
14
14
  const REFERER = "https://t.bilibili.com/";
15
+ // 动态详情里的 type 是字符串枚举,删除接口要的是数字码
16
+ const DYN_TYPE_CODE = {
17
+ DYNAMIC_TYPE_NONE: 0,
18
+ DYNAMIC_TYPE_FORWARD: 1,
19
+ DYNAMIC_TYPE_DRAW: 2,
20
+ DYNAMIC_TYPE_WORD: 4,
21
+ DYNAMIC_TYPE_AV: 8,
22
+ DYNAMIC_TYPE_ARTICLE: 64,
23
+ DYNAMIC_TYPE_MUSIC: 256,
24
+ };
15
25
  export class DynamicAPI {
16
26
  client;
17
27
  constructor(client) {
@@ -124,6 +134,7 @@ export class DynamicAPI {
124
134
  // 动态图床上传是 multipart,用 form-data 包(Node 下 axios 靠它正确序列化二进制)
125
135
  const form = new FormData();
126
136
  form.append("biz", "draw");
137
+ form.append("category", "daily");
127
138
  form.append("csrf", this.client.csrf);
128
139
  form.append("file_up", bytes, { filename: name, contentType: guessImageType(name) });
129
140
  const resp = await this.client.http.post(endpoints.DYNAMIC_IMAGE_UPLOAD, form, {
@@ -146,8 +157,8 @@ export class DynamicAPI {
146
157
  img_size: Number(meta.img_size ?? Math.round(bytes.length / 1024)),
147
158
  };
148
159
  }
149
- /** 发布图文动态(文字 + 最多若干张图片)。图片可以是本地路径或 Buffer。 */
150
- async publishWithImages(text, images, opts = {}) {
160
+ /** 发布图文动态(文字 + 若干张图片),走新版 JSON 创建接口。图片可传本地路径或 Buffer。 */
161
+ async publishWithImages(text, images, _opts = {}) {
151
162
  text = (text || "").trim();
152
163
  const pics = [];
153
164
  for (const img of images ?? []) {
@@ -155,23 +166,18 @@ export class DynamicAPI {
155
166
  }
156
167
  if (!text && !pics.length)
157
168
  throw new Error("动态内容不能为空");
158
- const extension = { emoji_type: 1, from: { emoji_type: 1 }, flag_cfg: {} };
159
- const form = {
160
- dynamic_id: 0,
161
- type: pics.length ? 2 : 4, // 2 = 图文,4 = 纯文字
162
- rid: 0,
163
- content: text,
164
- up_choose_comment: 0,
165
- up_close_comment: opts.closeComment ? 1 : 0,
166
- extension: JSON.stringify(extension),
167
- at_uids: (opts.atUids ?? []).map(String).join(","),
168
- ctrl: JSON.stringify([]),
169
- csrf: this.client.csrf,
170
- csrf_token: this.client.csrf,
169
+ // 新版创建接口(对齐网页端抓包):JSON body,外层包 dyn_req
170
+ const contents = text ? [{ raw_text: text, type: 1, biz_id: "" }] : [];
171
+ const body = {
172
+ dyn_req: {
173
+ content: { contents },
174
+ pics,
175
+ scene: 1,
176
+ option: {},
177
+ draft_id: 0,
178
+ },
171
179
  };
172
- if (pics.length)
173
- form["pictures"] = JSON.stringify(pics);
174
- const payload = await this.client.post(endpoints.DYNAMIC_CREATE, form, { Referer: REFERER });
180
+ const payload = await this.client.post(endpoints.DYNAMIC_CREATE_V2, body, { Referer: REFERER }, false, { csrf: this.client.csrf }, true);
175
181
  return DynamicAPI.dynamicId(payload);
176
182
  }
177
183
  async repost(dynamicId, text = "") {
@@ -195,18 +201,38 @@ export class DynamicAPI {
195
201
  }, false, { csrf: this.client.csrf }, true);
196
202
  }
197
203
  async delete(dynamicId) {
198
- const form = {
199
- dynamic_id: String(Math.floor(Number(dynamicId))),
200
- csrf: this.client.csrf,
201
- csrf_token: this.client.csrf,
202
- };
203
- await this.client.post(endpoints.DYNAMIC_DELETE, form, { Referer: REFERER });
204
+ // 动态 id 常超出 JS 安全整数范围(>2^53),必须全程按字符串传递,不能过 Number
205
+ const id = String(dynamicId);
206
+ // 删除接口需要 dyn_type(数字码)+ rid_str,都要先从动态详情取
207
+ let rawType;
208
+ let ridStr = "0";
209
+ try {
210
+ const resp = await this.client.http.get(`${endpoints.DYNAMIC_DETAIL}?id=${encodeURIComponent(id)}`, { headers: { Referer: REFERER } });
211
+ const item = resp?.data?.data?.item ?? {};
212
+ rawType = item?.type;
213
+ ridStr = String(item?.basic?.rid_str ?? item?.basic?.rid ?? "0");
214
+ }
215
+ catch {
216
+ // 详情取不到:rid_str 用默认值,类型仍必须能确定
217
+ }
218
+ const dynType = typeof rawType === "number" ? rawType : DYN_TYPE_CODE[String(rawType)];
219
+ if (dynType == null) {
220
+ throw new BiliApiError(`无法确定该动态的类型码(原始 type=${JSON.stringify(rawType)}),暂不支持删除`);
221
+ }
222
+ await this.client.post(endpoints.DYNAMIC_DELETE_V2, { dyn_id_str: id, dyn_type: dynType, rid_str: ridStr }, { Referer: REFERER }, false, { platform: "web", csrf: this.client.csrf }, true);
204
223
  }
205
224
  static dynamicId(payload) {
206
225
  const data = payload?.data ?? {};
207
- const value = data.dynamic_id_str ?? data.dynamic_id;
226
+ const req = data.dyn_req ?? {};
227
+ const value = data.dynamic_id_str ??
228
+ data.dynamic_id ??
229
+ data.dyn_id_str ??
230
+ data.dyn_id ??
231
+ req.dyn_id_str ??
232
+ req.dyn_id ??
233
+ data.id;
208
234
  if (!value) {
209
- throw new BiliApiError("动态接口未返回 dynamic_id,可能被风控拦截");
235
+ throw new BiliApiError("动态接口未返回 dynamic_id,可能被风控拦截", payload?.code, data);
210
236
  }
211
237
  return String(value);
212
238
  }
@@ -44,6 +44,8 @@ export declare const DYNAMIC_FEED_ALL = "https://api.bilibili.com/x/polymer/web-
44
44
  export declare const DYNAMIC_FEED_SPACE = "https://api.bilibili.com/x/polymer/web-dynamic/v1/feed/space";
45
45
  export declare const DYNAMIC_DETAIL = "https://api.bilibili.com/x/polymer/web-dynamic/v1/detail";
46
46
  export declare const DYNAMIC_CREATE = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/create";
47
+ export declare const DYNAMIC_CREATE_V2 = "https://api.bilibili.com/x/dynamic/feed/create/dyn";
48
+ export declare const DYNAMIC_DELETE_V2 = "https://api.bilibili.com/x/dynamic/feed/operate/remove";
47
49
  export declare const DYNAMIC_REPUBLISH = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/republish";
48
50
  export declare const DYNAMIC_DELETE = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/rm_dynamic";
49
51
  export declare const DYNAMIC_THUMB = "https://api.bilibili.com/x/dynamic/feed/dyn/thumb";
package/dist/endpoints.js CHANGED
@@ -56,6 +56,8 @@ export const DYNAMIC_FEED_ALL = `${API_BASE}/x/polymer/web-dynamic/v1/feed/all`;
56
56
  export const DYNAMIC_FEED_SPACE = `${API_BASE}/x/polymer/web-dynamic/v1/feed/space`;
57
57
  export const DYNAMIC_DETAIL = `${API_BASE}/x/polymer/web-dynamic/v1/detail`;
58
58
  export const DYNAMIC_CREATE = `${VC_BASE}/dynamic_svr/v1/dynamic_svr/create`;
59
+ export const DYNAMIC_CREATE_V2 = `${API_BASE}/x/dynamic/feed/create/dyn`;
60
+ export const DYNAMIC_DELETE_V2 = `${API_BASE}/x/dynamic/feed/operate/remove`;
59
61
  export const DYNAMIC_REPUBLISH = `${VC_BASE}/dynamic_svr/v1/dynamic_svr/republish`;
60
62
  export const DYNAMIC_DELETE = `${VC_BASE}/dynamic_svr/v1/dynamic_svr/rm_dynamic`;
61
63
  export const DYNAMIC_THUMB = `${API_BASE}/x/dynamic/feed/dyn/thumb`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dsh-shlr-bilitoolkit",
3
- "version": "0.1.22",
3
+ "version": "0.1.24",
4
4
  "description": "深海猎人专用B站接口工具箱 dsh 插件。",
5
5
  "type": "module",
6
6
  "main": "dist/dsh.js",