@bilibili-notify/dynamic 0.0.1-alpha.3 → 0.1.0-alpha.4

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/lib/index.cjs CHANGED
@@ -105,6 +105,27 @@ const LOG_TAG = "bilibili-notify-dynamic";
105
105
  * 后即自愈,无需人工重启进程。
106
106
  */
107
107
  const DETECTOR_RESTART_BACKOFF_MS = 5 * 6e4;
108
+ /**
109
+ * 动态推送文本模板的内建兜底,仅在 adapter 未填 config.dynamicTemplate /
110
+ * videoTemplate 时使用(真实 adapter 都会从 globals.defaults.templates 填充)。
111
+ * 与 `@bilibili-notify/internal` 的 `DEFAULT_TEMPLATES.dynamic/.dynamicVideo`
112
+ * 保持一致。变量 `{name}`(UP 名) / `{url}`(链接,未启用 URL 时为空)。
113
+ */
114
+ const DEFAULT_DYNAMIC_TEXT = {
115
+ dynamic: "{name}发布了一条动态:{url}",
116
+ video: "{name}发布了新视频:{url}"
117
+ };
118
+ /**
119
+ * 渲染动态推送文本:`{name}` / `{url}` 插值 + `\n` 展开。`url` 为空(未启用 URL)
120
+ * 时连同 `{url}` 的前导分隔符一起去掉,避免行尾残留孤立的「:」。两个推送分支
121
+ * (有图 / 无图)都走此函数 → 文字内容一致。
122
+ */
123
+ function renderDynamicText(template, name, url) {
124
+ return (0, _bilibili_notify_internal.interpolate)(url ? template : template.replace(/\s*[::]?\s*\{url\}/g, ""), {
125
+ name,
126
+ url
127
+ }).replaceAll("\\n", "\n");
128
+ }
108
129
  /** 从动态数据中提取图片 URL,用于多模态 AI 点评(最多 4 张) */
109
130
  function extractDynamicImages(item) {
110
131
  const mod = item.modules.module_dynamic;
@@ -456,14 +477,15 @@ var DynamicEngine = class {
456
477
  this.imageFailureStreak = 0;
457
478
  this.imageFailureNotified = false;
458
479
  }
459
- let dUrl = "";
460
- if (this.config.dynamicUrl) if (item.type === "DYNAMIC_TYPE_AV") {
480
+ const isVideo = item.type === "DYNAMIC_TYPE_AV";
481
+ let url = "";
482
+ if (this.config.dynamicUrl) if (isVideo) {
461
483
  const jumpUrl = item.modules.module_dynamic.major?.archive?.jump_url ?? "";
462
484
  if (this.config.dynamicVideoUrlToBV) {
463
485
  const bvMatch = jumpUrl.match(/BV[0-9A-Za-z]+/);
464
- dUrl = bvMatch ? bvMatch[0] : "";
465
- } else dUrl = `${name}发布了新视频:https:${jumpUrl}`;
466
- } else dUrl = `${name}发布了一条动态:https://t.bilibili.com/${item.id_str}`;
486
+ url = bvMatch ? bvMatch[0] : "";
487
+ } else url = `https:${jumpUrl}`;
488
+ } else url = `https://t.bilibili.com/${item.id_str}`;
467
489
  let aiComment;
468
490
  if (this.ai && this.config.aiEnabled !== false) {
469
491
  const dynamicText = extractDynamicText(item);
@@ -483,17 +505,18 @@ var DynamicEngine = class {
483
505
  this.logger.debug(`[detector] UID=${uid} 在本轮处理中已退订/被替换,跳过推送`);
484
506
  continue;
485
507
  }
486
- const textPart = aiComment ?? (dUrl || void 0);
508
+ const tmpl = isVideo ? sub?.customVideoTemplate ?? this.config.videoTemplate ?? DEFAULT_DYNAMIC_TEXT.video : sub?.customDynamicTemplate ?? this.config.dynamicTemplate ?? DEFAULT_DYNAMIC_TEXT.dynamic;
509
+ const text = aiComment ?? renderDynamicText(tmpl, name, url);
487
510
  const segments = buffer ? [{
488
511
  type: "image",
489
512
  buffer,
490
513
  mime: "image/jpeg"
491
- }, ...textPart ? [{
514
+ }, ...text ? [{
492
515
  type: "text",
493
- text: textPart
516
+ text
494
517
  }] : []] : [{
495
518
  type: "text",
496
- text: aiComment ?? `${name}发布了一条动态${dUrl ? `:${dUrl}` : ""}`
519
+ text
497
520
  }];
498
521
  await this.push.broadcastDynamic(uid, segments, "dynamic");
499
522
  const subForImgs = this.dynamicSubManager.get(uid);
package/lib/index.d.cts CHANGED
@@ -180,6 +180,16 @@ interface SubItemView {
180
180
  * 单图永远不走合并转发(在 engine 内已守卫)。
181
181
  */
182
182
  imageGroupForward?: boolean;
183
+ /**
184
+ * Per-UP 非视频动态文本模板;undefined 继承 engine config `dynamicTemplate`。
185
+ * Adapter 折叠 `Subscription.overrides.templates.dynamic` 后填入。
186
+ */
187
+ customDynamicTemplate?: string;
188
+ /**
189
+ * Per-UP 视频投稿文本模板;undefined 继承 engine config `videoTemplate`。
190
+ * Adapter 折叠 `Subscription.overrides.templates.dynamicVideo` 后填入。
191
+ */
192
+ customVideoTemplate?: string;
183
193
  }
184
194
  type SubscriptionsView = Record<string, SubItemView>;
185
195
  type SubManagerView = Map<string, SubItemView>;
@@ -218,6 +228,17 @@ interface DynamicEngineConfig {
218
228
  dynamicCron: string;
219
229
  /** 视频动态时是否将 URL 替换为 BV 号。 */
220
230
  dynamicVideoUrlToBV: boolean;
231
+ /**
232
+ * 非视频动态的推送文本模板。变量 `{name}`(UP 名) / `{url}`(动态链接)。
233
+ * `{url}` 在 `dynamicUrl=false` 时为空,引擎会顺带去掉相邻分隔符。
234
+ * 缺省时回退到内建文案。Adapter 通常用 `globals.defaults.templates.dynamic` 填充。
235
+ */
236
+ dynamicTemplate?: string;
237
+ /**
238
+ * 视频投稿的推送文本模板。变量 `{name}` / `{url}`(视频链接或 BV)。
239
+ * 缺省时回退到内建文案。Adapter 通常用 `globals.defaults.templates.dynamicVideo` 填充。
240
+ */
241
+ videoTemplate?: string;
221
242
  /**
222
243
  * DYNAMIC_TYPE_DRAW 图集图片推送行为。enable=false 时跳过图集广播,
223
244
  * 只发文本/卡片。forward=true 时走合并转发(聊天记录卡片,走 OneBot
package/lib/index.d.mts CHANGED
@@ -180,6 +180,16 @@ interface SubItemView {
180
180
  * 单图永远不走合并转发(在 engine 内已守卫)。
181
181
  */
182
182
  imageGroupForward?: boolean;
183
+ /**
184
+ * Per-UP 非视频动态文本模板;undefined 继承 engine config `dynamicTemplate`。
185
+ * Adapter 折叠 `Subscription.overrides.templates.dynamic` 后填入。
186
+ */
187
+ customDynamicTemplate?: string;
188
+ /**
189
+ * Per-UP 视频投稿文本模板;undefined 继承 engine config `videoTemplate`。
190
+ * Adapter 折叠 `Subscription.overrides.templates.dynamicVideo` 后填入。
191
+ */
192
+ customVideoTemplate?: string;
183
193
  }
184
194
  type SubscriptionsView = Record<string, SubItemView>;
185
195
  type SubManagerView = Map<string, SubItemView>;
@@ -218,6 +228,17 @@ interface DynamicEngineConfig {
218
228
  dynamicCron: string;
219
229
  /** 视频动态时是否将 URL 替换为 BV 号。 */
220
230
  dynamicVideoUrlToBV: boolean;
231
+ /**
232
+ * 非视频动态的推送文本模板。变量 `{name}`(UP 名) / `{url}`(动态链接)。
233
+ * `{url}` 在 `dynamicUrl=false` 时为空,引擎会顺带去掉相邻分隔符。
234
+ * 缺省时回退到内建文案。Adapter 通常用 `globals.defaults.templates.dynamic` 填充。
235
+ */
236
+ dynamicTemplate?: string;
237
+ /**
238
+ * 视频投稿的推送文本模板。变量 `{name}` / `{url}`(视频链接或 BV)。
239
+ * 缺省时回退到内建文案。Adapter 通常用 `globals.defaults.templates.dynamicVideo` 填充。
240
+ */
241
+ videoTemplate?: string;
221
242
  /**
222
243
  * DYNAMIC_TYPE_DRAW 图集图片推送行为。enable=false 时跳过图集广播,
223
244
  * 只发文本/卡片。forward=true 时走合并转发(聊天记录卡片,走 OneBot
package/lib/index.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { checkUserRegex, withLock } from "@bilibili-notify/internal";
1
+ import { checkUserRegex, interpolate, withLock } from "@bilibili-notify/internal";
2
2
  import { CronJob } from "cron";
3
3
  import { DateTime } from "luxon";
4
4
  //#region src/types.ts
@@ -104,6 +104,27 @@ const LOG_TAG = "bilibili-notify-dynamic";
104
104
  * 后即自愈,无需人工重启进程。
105
105
  */
106
106
  const DETECTOR_RESTART_BACKOFF_MS = 5 * 6e4;
107
+ /**
108
+ * 动态推送文本模板的内建兜底,仅在 adapter 未填 config.dynamicTemplate /
109
+ * videoTemplate 时使用(真实 adapter 都会从 globals.defaults.templates 填充)。
110
+ * 与 `@bilibili-notify/internal` 的 `DEFAULT_TEMPLATES.dynamic/.dynamicVideo`
111
+ * 保持一致。变量 `{name}`(UP 名) / `{url}`(链接,未启用 URL 时为空)。
112
+ */
113
+ const DEFAULT_DYNAMIC_TEXT = {
114
+ dynamic: "{name}发布了一条动态:{url}",
115
+ video: "{name}发布了新视频:{url}"
116
+ };
117
+ /**
118
+ * 渲染动态推送文本:`{name}` / `{url}` 插值 + `\n` 展开。`url` 为空(未启用 URL)
119
+ * 时连同 `{url}` 的前导分隔符一起去掉,避免行尾残留孤立的「:」。两个推送分支
120
+ * (有图 / 无图)都走此函数 → 文字内容一致。
121
+ */
122
+ function renderDynamicText(template, name, url) {
123
+ return interpolate(url ? template : template.replace(/\s*[::]?\s*\{url\}/g, ""), {
124
+ name,
125
+ url
126
+ }).replaceAll("\\n", "\n");
127
+ }
107
128
  /** 从动态数据中提取图片 URL,用于多模态 AI 点评(最多 4 张) */
108
129
  function extractDynamicImages(item) {
109
130
  const mod = item.modules.module_dynamic;
@@ -455,14 +476,15 @@ var DynamicEngine = class {
455
476
  this.imageFailureStreak = 0;
456
477
  this.imageFailureNotified = false;
457
478
  }
458
- let dUrl = "";
459
- if (this.config.dynamicUrl) if (item.type === "DYNAMIC_TYPE_AV") {
479
+ const isVideo = item.type === "DYNAMIC_TYPE_AV";
480
+ let url = "";
481
+ if (this.config.dynamicUrl) if (isVideo) {
460
482
  const jumpUrl = item.modules.module_dynamic.major?.archive?.jump_url ?? "";
461
483
  if (this.config.dynamicVideoUrlToBV) {
462
484
  const bvMatch = jumpUrl.match(/BV[0-9A-Za-z]+/);
463
- dUrl = bvMatch ? bvMatch[0] : "";
464
- } else dUrl = `${name}发布了新视频:https:${jumpUrl}`;
465
- } else dUrl = `${name}发布了一条动态:https://t.bilibili.com/${item.id_str}`;
485
+ url = bvMatch ? bvMatch[0] : "";
486
+ } else url = `https:${jumpUrl}`;
487
+ } else url = `https://t.bilibili.com/${item.id_str}`;
466
488
  let aiComment;
467
489
  if (this.ai && this.config.aiEnabled !== false) {
468
490
  const dynamicText = extractDynamicText(item);
@@ -482,17 +504,18 @@ var DynamicEngine = class {
482
504
  this.logger.debug(`[detector] UID=${uid} 在本轮处理中已退订/被替换,跳过推送`);
483
505
  continue;
484
506
  }
485
- const textPart = aiComment ?? (dUrl || void 0);
507
+ const tmpl = isVideo ? sub?.customVideoTemplate ?? this.config.videoTemplate ?? DEFAULT_DYNAMIC_TEXT.video : sub?.customDynamicTemplate ?? this.config.dynamicTemplate ?? DEFAULT_DYNAMIC_TEXT.dynamic;
508
+ const text = aiComment ?? renderDynamicText(tmpl, name, url);
486
509
  const segments = buffer ? [{
487
510
  type: "image",
488
511
  buffer,
489
512
  mime: "image/jpeg"
490
- }, ...textPart ? [{
513
+ }, ...text ? [{
491
514
  type: "text",
492
- text: textPart
515
+ text
493
516
  }] : []] : [{
494
517
  type: "text",
495
- text: aiComment ?? `${name}发布了一条动态${dUrl ? `:${dUrl}` : ""}`
518
+ text
496
519
  }];
497
520
  await this.push.broadcastDynamic(uid, segments, "dynamic");
498
521
  const subForImgs = this.dynamicSubManager.get(uid);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bilibili-notify/dynamic",
3
- "version": "0.0.1-alpha.3",
3
+ "version": "0.1.0-alpha.4",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/Akokk0/bilibili-notify"
@@ -29,9 +29,9 @@
29
29
  "cron": "^3.1.7",
30
30
  "luxon": "^3.5.0",
31
31
  "@bilibili-notify/ai": "^0.0.1-alpha.1",
32
- "@bilibili-notify/image": "^0.0.1-alpha.2",
33
32
  "@bilibili-notify/api": "^0.2.0-alpha.2",
34
- "@bilibili-notify/internal": "^0.1.0-alpha.2"
33
+ "@bilibili-notify/image": "^0.0.1-alpha.2",
34
+ "@bilibili-notify/internal": "^0.1.0-alpha.3"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/luxon": "^3.4.2"