dsh-shlr-bilitoolkit 0.1.21 → 0.1.23

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>;
@@ -124,6 +124,7 @@ export class DynamicAPI {
124
124
  // 动态图床上传是 multipart,用 form-data 包(Node 下 axios 靠它正确序列化二进制)
125
125
  const form = new FormData();
126
126
  form.append("biz", "draw");
127
+ form.append("category", "daily");
127
128
  form.append("csrf", this.client.csrf);
128
129
  form.append("file_up", bytes, { filename: name, contentType: guessImageType(name) });
129
130
  const resp = await this.client.http.post(endpoints.DYNAMIC_IMAGE_UPLOAD, form, {
@@ -133,7 +134,7 @@ export class DynamicAPI {
133
134
  });
134
135
  const data = resp?.data ?? {};
135
136
  const raw = data.data;
136
- const url = typeof raw === "string" ? raw : raw?.url;
137
+ const url = typeof raw === "string" ? raw : (raw?.image_url ?? raw?.url);
137
138
  if (data.code !== 0 || !url) {
138
139
  throw new BiliApiError(`图片上传失败: ${data.message ?? "接口未返回 url"}`, data.code, raw);
139
140
  }
@@ -146,8 +147,8 @@ export class DynamicAPI {
146
147
  img_size: Number(meta.img_size ?? Math.round(bytes.length / 1024)),
147
148
  };
148
149
  }
149
- /** 发布图文动态(文字 + 最多若干张图片)。图片可以是本地路径或 Buffer。 */
150
- async publishWithImages(text, images, opts = {}) {
150
+ /** 发布图文动态(文字 + 若干张图片),走新版 JSON 创建接口。图片可传本地路径或 Buffer。 */
151
+ async publishWithImages(text, images, _opts = {}) {
151
152
  text = (text || "").trim();
152
153
  const pics = [];
153
154
  for (const img of images ?? []) {
@@ -155,23 +156,18 @@ export class DynamicAPI {
155
156
  }
156
157
  if (!text && !pics.length)
157
158
  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,
159
+ // 新版创建接口(对齐网页端抓包):JSON body,外层包 dyn_req
160
+ const contents = text ? [{ raw_text: text, type: 1, biz_id: "" }] : [];
161
+ const body = {
162
+ dyn_req: {
163
+ content: { contents },
164
+ pics,
165
+ scene: 1,
166
+ option: {},
167
+ draft_id: 0,
168
+ },
171
169
  };
172
- if (pics.length)
173
- form["pictures"] = JSON.stringify(pics);
174
- const payload = await this.client.post(endpoints.DYNAMIC_CREATE, form, { Referer: REFERER });
170
+ const payload = await this.client.post(endpoints.DYNAMIC_CREATE_V2, body, { Referer: REFERER }, false, { csrf: this.client.csrf }, true);
175
171
  return DynamicAPI.dynamicId(payload);
176
172
  }
177
173
  async repost(dynamicId, text = "") {
@@ -204,9 +200,16 @@ export class DynamicAPI {
204
200
  }
205
201
  static dynamicId(payload) {
206
202
  const data = payload?.data ?? {};
207
- const value = data.dynamic_id_str ?? data.dynamic_id;
203
+ const req = data.dyn_req ?? {};
204
+ const value = data.dynamic_id_str ??
205
+ data.dynamic_id ??
206
+ data.dyn_id_str ??
207
+ data.dyn_id ??
208
+ req.dyn_id_str ??
209
+ req.dyn_id ??
210
+ data.id;
208
211
  if (!value) {
209
- throw new BiliApiError("动态接口未返回 dynamic_id,可能被风控拦截");
212
+ throw new BiliApiError("动态接口未返回 dynamic_id,可能被风控拦截", payload?.code, data);
210
213
  }
211
214
  return String(value);
212
215
  }
@@ -83,7 +83,7 @@ export class ProfileAPI {
83
83
  });
84
84
  const data = resp?.data ?? {};
85
85
  const raw = data.data;
86
- const url = typeof raw === "string" ? raw : raw?.url;
86
+ const url = typeof raw === "string" ? raw : (raw?.image_url ?? raw?.url);
87
87
  if (data.code !== 0 || !url) {
88
88
  throw new BiliApiError(`头像图片上传失败: ${data.message ?? "接口未返回 url"}`, data.code, raw);
89
89
  }
@@ -44,6 +44,7 @@ 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";
47
48
  export declare const DYNAMIC_REPUBLISH = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/republish";
48
49
  export declare const DYNAMIC_DELETE = "https://api.vc.bilibili.com/dynamic_svr/v1/dynamic_svr/rm_dynamic";
49
50
  export declare const DYNAMIC_THUMB = "https://api.bilibili.com/x/dynamic/feed/dyn/thumb";
package/dist/endpoints.js CHANGED
@@ -56,6 +56,7 @@ 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`;
59
60
  export const DYNAMIC_REPUBLISH = `${VC_BASE}/dynamic_svr/v1/dynamic_svr/republish`;
60
61
  export const DYNAMIC_DELETE = `${VC_BASE}/dynamic_svr/v1/dynamic_svr/rm_dynamic`;
61
62
  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.21",
3
+ "version": "0.1.23",
4
4
  "description": "深海猎人专用B站接口工具箱 dsh 插件。",
5
5
  "type": "module",
6
6
  "main": "dist/dsh.js",