@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 +73 -5
- package/lib/index.d.cts +1 -1
- package/lib/index.d.mts +1 -1
- package/lib/index.mjs +73 -5
- package/package.json +3 -3
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
|
|
417
|
-
|
|
418
|
-
|
|
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
package/lib/index.d.mts
CHANGED
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
|
|
416
|
-
|
|
417
|
-
|
|
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.
|
|
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.
|
|
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"
|