@bilibili-notify/dynamic 0.1.0-alpha.4 → 0.1.0-alpha.5

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
@@ -126,6 +126,68 @@ function renderDynamicText(template, name, url) {
126
126
  url
127
127
  }).replaceAll("\\n", "\n");
128
128
  }
129
+ function parseUid(raw) {
130
+ if (typeof raw === "number" && Number.isFinite(raw)) return String(Math.trunc(raw));
131
+ if (typeof raw !== "string") return void 0;
132
+ const trimmed = raw.trim();
133
+ return /^\d+$/.test(trimmed) ? trimmed : void 0;
134
+ }
135
+ function normalizeUnixSeconds(raw) {
136
+ if (typeof raw !== "number" && typeof raw !== "string") return void 0;
137
+ if (typeof raw === "string" && !/^\d+(?:\.\d+)?$/.test(raw.trim())) return void 0;
138
+ const n = Number(raw);
139
+ if (!Number.isFinite(n) || n <= 0) return void 0;
140
+ if (n > 1e10) {
141
+ const seconds = Math.floor(n / 1e3);
142
+ return seconds <= 1e10 ? seconds : void 0;
143
+ }
144
+ return Math.floor(n);
145
+ }
146
+ function parsePubTimeFallback(raw, now = luxon.DateTime.now()) {
147
+ if (typeof raw !== "string") return void 0;
148
+ const text = raw.trim();
149
+ if (!text) return void 0;
150
+ if (text === "刚刚") return Math.floor(now.toSeconds());
151
+ const relative = text.match(/^(\d+)(秒|分钟|小时)前$/);
152
+ if (relative) {
153
+ const amount = Number(relative[1]);
154
+ if (!Number.isFinite(amount)) return void 0;
155
+ const unit = relative[2] === "秒" ? "seconds" : relative[2] === "分钟" ? "minutes" : "hours";
156
+ return Math.floor(now.minus({ [unit]: amount }).toSeconds());
157
+ }
158
+ const normalized = text.replace(/[年月]/g, "-").replace(/日/g, "").replace(/\//g, "-").replace(/\s+/g, " ");
159
+ for (const fmt of [
160
+ "yyyy-M-d H:mm:ss",
161
+ "yyyy-M-d H:mm",
162
+ "yyyy-M-d",
163
+ "M-d H:mm:ss",
164
+ "M-d H:mm"
165
+ ]) {
166
+ let dt = luxon.DateTime.fromFormat(normalized, fmt);
167
+ if (!dt.isValid) continue;
168
+ if (!fmt.startsWith("yyyy")) {
169
+ dt = dt.set({ year: now.year });
170
+ if (dt > now.plus({ days: 1 })) dt = dt.minus({ years: 1 });
171
+ }
172
+ return Math.floor(dt.toSeconds());
173
+ }
174
+ const yesterday = normalized.match(/^昨天\s*(\d{1,2}):(\d{2})(?::(\d{2}))?$/);
175
+ if (yesterday) {
176
+ const hour = Number(yesterday[1]);
177
+ const minute = Number(yesterday[2]);
178
+ const second = Number(yesterday[3] ?? 0);
179
+ const dt = now.minus({ days: 1 }).set({
180
+ hour,
181
+ minute,
182
+ second,
183
+ millisecond: 0
184
+ });
185
+ return dt.isValid ? Math.floor(dt.toSeconds()) : void 0;
186
+ }
187
+ }
188
+ function getDynamicPostTime(author) {
189
+ return normalizeUnixSeconds(author.pub_ts) ?? parsePubTimeFallback(author.pub_time);
190
+ }
129
191
  /** 从动态数据中提取图片 URL,用于多模态 AI 点评(最多 4 张) */
130
192
  function extractDynamicImages(item) {
131
193
  const mod = item.modules.module_dynamic;
@@ -413,15 +475,21 @@ var DynamicEngine = class {
413
475
  };
414
476
  for (const item of content.data.items) {
415
477
  if (!item) continue;
416
- const postTime = item.modules.module_author.pub_ts;
417
- if (typeof postTime !== "number" || !Number.isFinite(postTime)) {
418
- this.logger.warn(`[detector] 跳过无效动态:pub_ts 缺失或非数字,ID=${item.id_str ?? "unknown"}`);
478
+ const author = item.modules?.module_author;
479
+ const uid = parseUid(author?.mid);
480
+ if (!uid) {
481
+ this.logger.debug(`[detector] 跳过无作者 UID 的动态,ID=${item.id_str ?? "unknown"}`);
419
482
  continue;
420
483
  }
421
- const uid = item.modules.module_author.mid.toString();
422
- const name = item.modules.module_author.name;
423
484
  const timeline = this.dynamicTimelineManager.get(uid);
424
485
  if (timeline === void 0) continue;
486
+ const postTime = getDynamicPostTime(author);
487
+ if (postTime === void 0) {
488
+ const rawPubTs = author.pub_ts;
489
+ this.logger.warn(`[detector] 跳过无效动态:无法解析发布时间,UID=${uid} ID=${item.id_str ?? "unknown"} type=${item.type ?? "unknown"} pub_ts_type=${typeof rawPubTs} pub_ts=${JSON.stringify(rawPubTs)} pub_time=${JSON.stringify(author.pub_time)}`);
490
+ continue;
491
+ }
492
+ const name = author.name;
425
493
  this.logger.debug(`[detector] 检查动态 UP=${name} UID=${uid} 发布时间=${luxon.DateTime.fromSeconds(postTime).toFormat("yyyy-MM-dd HH:mm:ss")}`);
426
494
  if (timeline >= postTime) continue;
427
495
  const subAtCapture = this.dynamicSubManager.get(uid);
package/lib/index.d.cts CHANGED
@@ -34,7 +34,7 @@ type Dynamic = {
34
34
  name: string;
35
35
  pub_action: string;
36
36
  pub_time: string;
37
- pub_ts: number;
37
+ pub_ts?: number | string;
38
38
  type: string;
39
39
  [key: string]: any;
40
40
  };
package/lib/index.d.mts CHANGED
@@ -34,7 +34,7 @@ type Dynamic = {
34
34
  name: string;
35
35
  pub_action: string;
36
36
  pub_time: string;
37
- pub_ts: number;
37
+ pub_ts?: number | string;
38
38
  type: string;
39
39
  [key: string]: any;
40
40
  };
package/lib/index.mjs CHANGED
@@ -125,6 +125,68 @@ function renderDynamicText(template, name, url) {
125
125
  url
126
126
  }).replaceAll("\\n", "\n");
127
127
  }
128
+ function parseUid(raw) {
129
+ if (typeof raw === "number" && Number.isFinite(raw)) return String(Math.trunc(raw));
130
+ if (typeof raw !== "string") return void 0;
131
+ const trimmed = raw.trim();
132
+ return /^\d+$/.test(trimmed) ? trimmed : void 0;
133
+ }
134
+ function normalizeUnixSeconds(raw) {
135
+ if (typeof raw !== "number" && typeof raw !== "string") return void 0;
136
+ if (typeof raw === "string" && !/^\d+(?:\.\d+)?$/.test(raw.trim())) return void 0;
137
+ const n = Number(raw);
138
+ if (!Number.isFinite(n) || n <= 0) return void 0;
139
+ if (n > 1e10) {
140
+ const seconds = Math.floor(n / 1e3);
141
+ return seconds <= 1e10 ? seconds : void 0;
142
+ }
143
+ return Math.floor(n);
144
+ }
145
+ function parsePubTimeFallback(raw, now = DateTime.now()) {
146
+ if (typeof raw !== "string") return void 0;
147
+ const text = raw.trim();
148
+ if (!text) return void 0;
149
+ if (text === "刚刚") return Math.floor(now.toSeconds());
150
+ const relative = text.match(/^(\d+)(秒|分钟|小时)前$/);
151
+ if (relative) {
152
+ const amount = Number(relative[1]);
153
+ if (!Number.isFinite(amount)) return void 0;
154
+ const unit = relative[2] === "秒" ? "seconds" : relative[2] === "分钟" ? "minutes" : "hours";
155
+ return Math.floor(now.minus({ [unit]: amount }).toSeconds());
156
+ }
157
+ const normalized = text.replace(/[年月]/g, "-").replace(/日/g, "").replace(/\//g, "-").replace(/\s+/g, " ");
158
+ for (const fmt of [
159
+ "yyyy-M-d H:mm:ss",
160
+ "yyyy-M-d H:mm",
161
+ "yyyy-M-d",
162
+ "M-d H:mm:ss",
163
+ "M-d H:mm"
164
+ ]) {
165
+ let dt = DateTime.fromFormat(normalized, fmt);
166
+ if (!dt.isValid) continue;
167
+ if (!fmt.startsWith("yyyy")) {
168
+ dt = dt.set({ year: now.year });
169
+ if (dt > now.plus({ days: 1 })) dt = dt.minus({ years: 1 });
170
+ }
171
+ return Math.floor(dt.toSeconds());
172
+ }
173
+ const yesterday = normalized.match(/^昨天\s*(\d{1,2}):(\d{2})(?::(\d{2}))?$/);
174
+ if (yesterday) {
175
+ const hour = Number(yesterday[1]);
176
+ const minute = Number(yesterday[2]);
177
+ const second = Number(yesterday[3] ?? 0);
178
+ const dt = now.minus({ days: 1 }).set({
179
+ hour,
180
+ minute,
181
+ second,
182
+ millisecond: 0
183
+ });
184
+ return dt.isValid ? Math.floor(dt.toSeconds()) : void 0;
185
+ }
186
+ }
187
+ function getDynamicPostTime(author) {
188
+ return normalizeUnixSeconds(author.pub_ts) ?? parsePubTimeFallback(author.pub_time);
189
+ }
128
190
  /** 从动态数据中提取图片 URL,用于多模态 AI 点评(最多 4 张) */
129
191
  function extractDynamicImages(item) {
130
192
  const mod = item.modules.module_dynamic;
@@ -412,15 +474,21 @@ var DynamicEngine = class {
412
474
  };
413
475
  for (const item of content.data.items) {
414
476
  if (!item) continue;
415
- const postTime = item.modules.module_author.pub_ts;
416
- if (typeof postTime !== "number" || !Number.isFinite(postTime)) {
417
- this.logger.warn(`[detector] 跳过无效动态:pub_ts 缺失或非数字,ID=${item.id_str ?? "unknown"}`);
477
+ const author = item.modules?.module_author;
478
+ const uid = parseUid(author?.mid);
479
+ if (!uid) {
480
+ this.logger.debug(`[detector] 跳过无作者 UID 的动态,ID=${item.id_str ?? "unknown"}`);
418
481
  continue;
419
482
  }
420
- const uid = item.modules.module_author.mid.toString();
421
- const name = item.modules.module_author.name;
422
483
  const timeline = this.dynamicTimelineManager.get(uid);
423
484
  if (timeline === void 0) continue;
485
+ const postTime = getDynamicPostTime(author);
486
+ if (postTime === void 0) {
487
+ const rawPubTs = author.pub_ts;
488
+ this.logger.warn(`[detector] 跳过无效动态:无法解析发布时间,UID=${uid} ID=${item.id_str ?? "unknown"} type=${item.type ?? "unknown"} pub_ts_type=${typeof rawPubTs} pub_ts=${JSON.stringify(rawPubTs)} pub_time=${JSON.stringify(author.pub_time)}`);
489
+ continue;
490
+ }
491
+ const name = author.name;
424
492
  this.logger.debug(`[detector] 检查动态 UP=${name} UID=${uid} 发布时间=${DateTime.fromSeconds(postTime).toFormat("yyyy-MM-dd HH:mm:ss")}`);
425
493
  if (timeline >= postTime) continue;
426
494
  const subAtCapture = this.dynamicSubManager.get(uid);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bilibili-notify/dynamic",
3
- "version": "0.1.0-alpha.4",
3
+ "version": "0.1.0-alpha.5",
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/api": "^0.2.0-alpha.2",
33
32
  "@bilibili-notify/image": "^0.0.1-alpha.2",
34
- "@bilibili-notify/internal": "^0.1.0-alpha.3"
33
+ "@bilibili-notify/internal": "^0.1.0-alpha.4",
34
+ "@bilibili-notify/api": "^0.2.0-alpha.2"
35
35
  },
36
36
  "devDependencies": {
37
37
  "@types/luxon": "^3.4.2"