sa2kit 1.6.91 → 1.6.96
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/dist/client-BlkUL2To.d.ts +26 -0
- package/dist/client-DpMIhrlS.d.mts +26 -0
- package/dist/huarongdao/index.d.mts +8 -0
- package/dist/huarongdao/index.d.ts +8 -0
- package/dist/huarongdao/index.js +360 -0
- package/dist/huarongdao/index.js.map +1 -0
- package/dist/huarongdao/index.mjs +338 -0
- package/dist/huarongdao/index.mjs.map +1 -0
- package/dist/huarongdao/logic/index.d.mts +11 -0
- package/dist/huarongdao/logic/index.d.ts +11 -0
- package/dist/huarongdao/logic/index.js +89 -0
- package/dist/huarongdao/logic/index.js.map +1 -0
- package/dist/huarongdao/logic/index.mjs +81 -0
- package/dist/huarongdao/logic/index.mjs.map +1 -0
- package/dist/huarongdao/routes/index.d.mts +38 -0
- package/dist/huarongdao/routes/index.d.ts +38 -0
- package/dist/huarongdao/routes/index.js +114 -0
- package/dist/huarongdao/routes/index.js.map +1 -0
- package/dist/huarongdao/routes/index.mjs +108 -0
- package/dist/huarongdao/routes/index.mjs.map +1 -0
- package/dist/huarongdao/server/index.d.mts +14 -0
- package/dist/huarongdao/server/index.d.ts +14 -0
- package/dist/huarongdao/server/index.js +60 -0
- package/dist/huarongdao/server/index.js.map +1 -0
- package/dist/huarongdao/server/index.mjs +57 -0
- package/dist/huarongdao/server/index.mjs.map +1 -0
- package/dist/huarongdao/service/index.d.mts +31 -0
- package/dist/huarongdao/service/index.d.ts +31 -0
- package/dist/huarongdao/service/index.js +45 -0
- package/dist/huarongdao/service/index.js.map +1 -0
- package/dist/huarongdao/service/index.mjs +42 -0
- package/dist/huarongdao/service/index.mjs.map +1 -0
- package/dist/huarongdao/types/index.d.mts +46 -0
- package/dist/huarongdao/types/index.d.ts +46 -0
- package/dist/huarongdao/types/index.js +4 -0
- package/dist/huarongdao/types/index.js.map +1 -0
- package/dist/huarongdao/types/index.mjs +3 -0
- package/dist/huarongdao/types/index.mjs.map +1 -0
- package/dist/huarongdao/ui/web/index.d.mts +3 -0
- package/dist/huarongdao/ui/web/index.d.ts +3 -0
- package/dist/huarongdao/ui/web/index.js +237 -0
- package/dist/huarongdao/ui/web/index.js.map +1 -0
- package/dist/huarongdao/ui/web/index.mjs +229 -0
- package/dist/huarongdao/ui/web/index.mjs.map +1 -0
- package/dist/index-B48rcsqv.d.ts +27 -0
- package/dist/index-BNqJdwX4.d.ts +37 -0
- package/dist/index-C7yh6b5Q.d.mts +17 -0
- package/dist/index-CDapUIT5.d.mts +51 -0
- package/dist/index-Cv9jlnNz.d.ts +17 -0
- package/dist/index-D3UbkUai.d.ts +51 -0
- package/dist/index-DOtQI_mz.d.mts +37 -0
- package/dist/index-Da2X78GE.d.mts +27 -0
- package/dist/index.d.mts +18 -0
- package/dist/index.d.ts +18 -0
- package/dist/index.js +1707 -79
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +1675 -82
- package/dist/index.mjs.map +1 -1
- package/dist/mikuContest/index.d.mts +13 -0
- package/dist/mikuContest/index.d.ts +13 -0
- package/dist/mikuContest/index.js +1310 -0
- package/dist/mikuContest/index.js.map +1 -0
- package/dist/mikuContest/index.mjs +1253 -0
- package/dist/mikuContest/index.mjs.map +1 -0
- package/dist/mikuContest/logic/index.d.mts +32 -0
- package/dist/mikuContest/logic/index.d.ts +32 -0
- package/dist/mikuContest/logic/index.js +511 -0
- package/dist/mikuContest/logic/index.js.map +1 -0
- package/dist/mikuContest/logic/index.mjs +483 -0
- package/dist/mikuContest/logic/index.mjs.map +1 -0
- package/dist/mikuContest/routes/index.d.mts +80 -0
- package/dist/mikuContest/routes/index.d.ts +80 -0
- package/dist/mikuContest/routes/index.js +821 -0
- package/dist/mikuContest/routes/index.js.map +1 -0
- package/dist/mikuContest/routes/index.mjs +791 -0
- package/dist/mikuContest/routes/index.mjs.map +1 -0
- package/dist/mikuContest/server/index.d.mts +766 -0
- package/dist/mikuContest/server/index.d.ts +766 -0
- package/dist/mikuContest/server/index.js +705 -0
- package/dist/mikuContest/server/index.js.map +1 -0
- package/dist/mikuContest/server/index.mjs +672 -0
- package/dist/mikuContest/server/index.mjs.map +1 -0
- package/dist/mikuContest/service/index.d.mts +30 -0
- package/dist/mikuContest/service/index.d.ts +30 -0
- package/dist/mikuContest/service/index.js +139 -0
- package/dist/mikuContest/service/index.js.map +1 -0
- package/dist/mikuContest/service/index.mjs +135 -0
- package/dist/mikuContest/service/index.mjs.map +1 -0
- package/dist/mikuContest/types/index.d.mts +179 -0
- package/dist/mikuContest/types/index.d.ts +179 -0
- package/dist/mikuContest/types/index.js +4 -0
- package/dist/mikuContest/types/index.js.map +1 -0
- package/dist/mikuContest/types/index.mjs +3 -0
- package/dist/mikuContest/types/index.mjs.map +1 -0
- package/dist/mikuContest/ui/miniapp/index.d.mts +3 -0
- package/dist/mikuContest/ui/miniapp/index.d.ts +3 -0
- package/dist/mikuContest/ui/miniapp/index.js +566 -0
- package/dist/mikuContest/ui/miniapp/index.js.map +1 -0
- package/dist/mikuContest/ui/miniapp/index.mjs +540 -0
- package/dist/mikuContest/ui/miniapp/index.mjs.map +1 -0
- package/dist/mikuContest/ui/web/index.d.mts +4 -0
- package/dist/mikuContest/ui/web/index.d.ts +4 -0
- package/dist/mikuContest/ui/web/index.js +353 -0
- package/dist/mikuContest/ui/web/index.js.map +1 -0
- package/dist/mikuContest/ui/web/index.mjs +343 -0
- package/dist/mikuContest/ui/web/index.mjs.map +1 -0
- package/dist/qqbot/server/index.d.mts +126 -1
- package/dist/qqbot/server/index.d.ts +126 -1
- package/dist/qqbot/server/index.js +250 -0
- package/dist/qqbot/server/index.js.map +1 -1
- package/dist/qqbot/server/index.mjs +246 -1
- package/dist/qqbot/server/index.mjs.map +1 -1
- package/dist/service-D7DM1wW-.d.ts +38 -0
- package/dist/service-DPr2rlvH.d.mts +38 -0
- package/dist/types-BS7Xz09b.d.mts +14 -0
- package/dist/types-k4koMp4m.d.ts +14 -0
- package/package.json +76 -1
|
@@ -39,4 +39,129 @@ interface NextNapCatRouteConfig {
|
|
|
39
39
|
}
|
|
40
40
|
declare function createNextNapCatRouteHandler(config: NextNapCatRouteConfig): (request: NextRequest) => Promise<NextResponse<NapCatWebApiResponse<unknown>>>;
|
|
41
41
|
|
|
42
|
-
|
|
42
|
+
interface XhsPost {
|
|
43
|
+
id: string;
|
|
44
|
+
url: string;
|
|
45
|
+
title?: string;
|
|
46
|
+
publishedAt?: string;
|
|
47
|
+
}
|
|
48
|
+
interface XhsSource {
|
|
49
|
+
fetchLatestPosts(limit?: number): Promise<XhsPost[]>;
|
|
50
|
+
}
|
|
51
|
+
interface XhsMonitorState {
|
|
52
|
+
seenPostIds: string[];
|
|
53
|
+
updatedAt: string;
|
|
54
|
+
}
|
|
55
|
+
interface XhsMonitorStateStore {
|
|
56
|
+
read(): Promise<XhsMonitorState>;
|
|
57
|
+
write(state: XhsMonitorState): Promise<void>;
|
|
58
|
+
}
|
|
59
|
+
interface XhsNotificationPayload {
|
|
60
|
+
accountLabel: string;
|
|
61
|
+
post: XhsPost;
|
|
62
|
+
detectedAt: string;
|
|
63
|
+
}
|
|
64
|
+
interface XhsNotifier {
|
|
65
|
+
notify(payload: XhsNotificationPayload): Promise<void>;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
interface XhsMonitorServiceOptions {
|
|
69
|
+
accountLabel: string;
|
|
70
|
+
source: XhsSource;
|
|
71
|
+
notifier: XhsNotifier;
|
|
72
|
+
stateStore: XhsMonitorStateStore;
|
|
73
|
+
pollIntervalMs: number;
|
|
74
|
+
skipInitialSnapshot?: boolean;
|
|
75
|
+
postLimit?: number;
|
|
76
|
+
now?: () => Date;
|
|
77
|
+
}
|
|
78
|
+
declare class XhsMonitorService {
|
|
79
|
+
private readonly accountLabel;
|
|
80
|
+
private readonly source;
|
|
81
|
+
private readonly notifier;
|
|
82
|
+
private readonly stateStore;
|
|
83
|
+
private readonly pollIntervalMs;
|
|
84
|
+
private readonly skipInitialSnapshot;
|
|
85
|
+
private readonly postLimit;
|
|
86
|
+
private readonly now;
|
|
87
|
+
private timer;
|
|
88
|
+
private initialized;
|
|
89
|
+
private running;
|
|
90
|
+
constructor(options: XhsMonitorServiceOptions);
|
|
91
|
+
checkOnce(): Promise<{
|
|
92
|
+
notifiedCount: number;
|
|
93
|
+
fetchedCount: number;
|
|
94
|
+
}>;
|
|
95
|
+
start(immediate?: boolean): void;
|
|
96
|
+
stop(): void;
|
|
97
|
+
private persistSeenIds;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
interface CreateXhsMonitorAppOptions {
|
|
101
|
+
accountLabel: string;
|
|
102
|
+
profileUrl: string;
|
|
103
|
+
pollIntervalMs: number;
|
|
104
|
+
stateFilePath: string;
|
|
105
|
+
napCat: {
|
|
106
|
+
baseUrl: string;
|
|
107
|
+
accessToken?: string;
|
|
108
|
+
timeoutMs?: number;
|
|
109
|
+
};
|
|
110
|
+
target: {
|
|
111
|
+
type: 'group';
|
|
112
|
+
groupId: number;
|
|
113
|
+
} | {
|
|
114
|
+
type: 'private';
|
|
115
|
+
userId: number;
|
|
116
|
+
};
|
|
117
|
+
skipInitialSnapshot?: boolean;
|
|
118
|
+
postLimit?: number;
|
|
119
|
+
}
|
|
120
|
+
declare function createXhsMonitorApp(options: CreateXhsMonitorAppOptions): XhsMonitorService;
|
|
121
|
+
|
|
122
|
+
interface XhsProfileHtmlSourceOptions {
|
|
123
|
+
profileUrl: string;
|
|
124
|
+
timeoutMs?: number;
|
|
125
|
+
userAgent?: string;
|
|
126
|
+
}
|
|
127
|
+
declare class XhsProfileHtmlSource implements XhsSource {
|
|
128
|
+
private readonly profileUrl;
|
|
129
|
+
private readonly timeoutMs;
|
|
130
|
+
private readonly userAgent;
|
|
131
|
+
constructor(options: XhsProfileHtmlSourceOptions);
|
|
132
|
+
fetchLatestPosts(limit?: number): Promise<XhsPost[]>;
|
|
133
|
+
private parsePostsFromHtml;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
interface FileXhsMonitorStateStoreOptions {
|
|
137
|
+
filePath: string;
|
|
138
|
+
maxSeenPostIds?: number;
|
|
139
|
+
}
|
|
140
|
+
declare class FileXhsMonitorStateStore implements XhsMonitorStateStore {
|
|
141
|
+
private readonly filePath;
|
|
142
|
+
private readonly maxSeenPostIds;
|
|
143
|
+
constructor(options: FileXhsMonitorStateStoreOptions);
|
|
144
|
+
read(): Promise<XhsMonitorState>;
|
|
145
|
+
write(state: XhsMonitorState): Promise<void>;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
interface XhsQqNotifierOptions {
|
|
149
|
+
client: NapCatClient;
|
|
150
|
+
target: {
|
|
151
|
+
type: 'group';
|
|
152
|
+
groupId: number;
|
|
153
|
+
} | {
|
|
154
|
+
type: 'private';
|
|
155
|
+
userId: number;
|
|
156
|
+
};
|
|
157
|
+
messageTemplate?: (payload: XhsNotificationPayload) => string;
|
|
158
|
+
}
|
|
159
|
+
declare class XhsQqNotifier implements XhsNotifier {
|
|
160
|
+
private readonly client;
|
|
161
|
+
private readonly target;
|
|
162
|
+
private readonly messageTemplate;
|
|
163
|
+
constructor(options: XhsQqNotifierOptions);
|
|
164
|
+
notify(payload: XhsNotificationPayload): Promise<void>;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export { type CreateXhsMonitorAppOptions, FileXhsMonitorStateStore, type FileXhsMonitorStateStoreOptions, type HttpLikeRequest, type HttpLikeResponse, NapCatClient, type NapCatWebApiOptions, type NextNapCatRouteConfig, XhsMonitorService, type XhsMonitorServiceOptions, type XhsMonitorState, type XhsMonitorStateStore, type XhsNotificationPayload, type XhsNotifier, type XhsPost, XhsProfileHtmlSource, type XhsProfileHtmlSourceOptions, XhsQqNotifier, type XhsQqNotifierOptions, type XhsSource, createNapCatWebApi, createNextNapCatRouteHandler, createXhsMonitorApp };
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
var server = require('next/server');
|
|
4
|
+
var promises = require('fs/promises');
|
|
5
|
+
var path = require('path');
|
|
4
6
|
|
|
5
7
|
// src/qqbot/server/client.ts
|
|
6
8
|
var NapCatClient = class {
|
|
@@ -137,8 +139,256 @@ function createNextNapCatRouteHandler(config) {
|
|
|
137
139
|
};
|
|
138
140
|
}
|
|
139
141
|
|
|
142
|
+
// src/qqbot/server/xhsMonitor/notifier.ts
|
|
143
|
+
function defaultMessageTemplate(payload) {
|
|
144
|
+
return [
|
|
145
|
+
`\u3010\u5C0F\u7EA2\u4E66\u65B0\u5E16\u63D0\u9192\u3011${payload.accountLabel}`,
|
|
146
|
+
payload.post.title ? `\u6807\u9898\uFF1A${payload.post.title}` : void 0,
|
|
147
|
+
`\u94FE\u63A5\uFF1A${payload.post.url}`,
|
|
148
|
+
`\u68C0\u6D4B\u65F6\u95F4\uFF1A${payload.detectedAt}`
|
|
149
|
+
].filter(Boolean).join("\n");
|
|
150
|
+
}
|
|
151
|
+
var XhsQqNotifier = class {
|
|
152
|
+
constructor(options) {
|
|
153
|
+
this.client = options.client;
|
|
154
|
+
this.target = options.target;
|
|
155
|
+
this.messageTemplate = options.messageTemplate ?? defaultMessageTemplate;
|
|
156
|
+
}
|
|
157
|
+
async notify(payload) {
|
|
158
|
+
const message = this.messageTemplate(payload);
|
|
159
|
+
const response = this.target.type === "group" ? await this.client.sendGroupMessage({
|
|
160
|
+
group_id: this.target.groupId,
|
|
161
|
+
message
|
|
162
|
+
}) : await this.client.sendPrivateMessage({
|
|
163
|
+
user_id: this.target.userId,
|
|
164
|
+
message
|
|
165
|
+
});
|
|
166
|
+
if (response.status !== "ok") {
|
|
167
|
+
throw new Error(`Failed to send QQ message: ${response.message ?? "Unknown error"}`);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
// src/qqbot/server/xhsMonitor/source.ts
|
|
173
|
+
function decodeHtmlEntities(input) {
|
|
174
|
+
return input.replace(/\\u003c/g, "<").replace(/\\u003e/g, ">").replace(/\\u0026/g, "&").replace(/\\"/g, '"').replace(/\\n/g, " ").trim();
|
|
175
|
+
}
|
|
176
|
+
function normalizeTitle(title) {
|
|
177
|
+
return decodeHtmlEntities(title).replace(/\s+/g, " ").trim();
|
|
178
|
+
}
|
|
179
|
+
var XhsProfileHtmlSource = class {
|
|
180
|
+
constructor(options) {
|
|
181
|
+
this.profileUrl = options.profileUrl;
|
|
182
|
+
this.timeoutMs = options.timeoutMs ?? 12e3;
|
|
183
|
+
this.userAgent = options.userAgent ?? "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0 Safari/537.36";
|
|
184
|
+
}
|
|
185
|
+
async fetchLatestPosts(limit = 20) {
|
|
186
|
+
const controller = new AbortController();
|
|
187
|
+
const timer = setTimeout(() => controller.abort(), this.timeoutMs);
|
|
188
|
+
try {
|
|
189
|
+
const response = await fetch(this.profileUrl, {
|
|
190
|
+
method: "GET",
|
|
191
|
+
headers: {
|
|
192
|
+
"User-Agent": this.userAgent,
|
|
193
|
+
Accept: "text/html,application/xhtml+xml"
|
|
194
|
+
},
|
|
195
|
+
signal: controller.signal
|
|
196
|
+
});
|
|
197
|
+
if (!response.ok) {
|
|
198
|
+
throw new Error(`Failed to fetch profile page: HTTP ${response.status}`);
|
|
199
|
+
}
|
|
200
|
+
const html = await response.text();
|
|
201
|
+
return this.parsePostsFromHtml(html, limit);
|
|
202
|
+
} finally {
|
|
203
|
+
clearTimeout(timer);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
parsePostsFromHtml(html, limit) {
|
|
207
|
+
const idMatches = [...html.matchAll(/(?:\/explore\/|\"noteId\"\s*:\s*\")([a-zA-Z0-9_-]{8,32})/g)];
|
|
208
|
+
const titleMatches = [...html.matchAll(/\"displayTitle\"\s*:\s*\"([^"]{1,120})\"/g)];
|
|
209
|
+
const uniqueIds = [];
|
|
210
|
+
const seen = /* @__PURE__ */ new Set();
|
|
211
|
+
for (const match of idMatches) {
|
|
212
|
+
const id = match[1];
|
|
213
|
+
if (!id || seen.has(id)) {
|
|
214
|
+
continue;
|
|
215
|
+
}
|
|
216
|
+
seen.add(id);
|
|
217
|
+
uniqueIds.push(id);
|
|
218
|
+
if (uniqueIds.length >= limit) {
|
|
219
|
+
break;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
const titles = titleMatches.map((match) => normalizeTitle(match[1] ?? "")).filter(Boolean);
|
|
223
|
+
return uniqueIds.map((id, index) => ({
|
|
224
|
+
id,
|
|
225
|
+
url: `https://www.xiaohongshu.com/explore/${id}`,
|
|
226
|
+
title: titles[index]
|
|
227
|
+
}));
|
|
228
|
+
}
|
|
229
|
+
};
|
|
230
|
+
var DEFAULT_STATE = {
|
|
231
|
+
seenPostIds: [],
|
|
232
|
+
updatedAt: (/* @__PURE__ */ new Date(0)).toISOString()
|
|
233
|
+
};
|
|
234
|
+
var FileXhsMonitorStateStore = class {
|
|
235
|
+
constructor(options) {
|
|
236
|
+
this.filePath = options.filePath;
|
|
237
|
+
this.maxSeenPostIds = Math.max(10, options.maxSeenPostIds ?? 500);
|
|
238
|
+
}
|
|
239
|
+
async read() {
|
|
240
|
+
try {
|
|
241
|
+
const text = await promises.readFile(this.filePath, "utf8");
|
|
242
|
+
const parsed = JSON.parse(text);
|
|
243
|
+
const seenPostIds = Array.isArray(parsed.seenPostIds) ? parsed.seenPostIds.filter((item) => typeof item === "string") : [];
|
|
244
|
+
const updatedAt = typeof parsed.updatedAt === "string" ? parsed.updatedAt : (/* @__PURE__ */ new Date()).toISOString();
|
|
245
|
+
return {
|
|
246
|
+
seenPostIds: seenPostIds.slice(0, this.maxSeenPostIds),
|
|
247
|
+
updatedAt
|
|
248
|
+
};
|
|
249
|
+
} catch {
|
|
250
|
+
return DEFAULT_STATE;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
async write(state) {
|
|
254
|
+
await promises.mkdir(path.dirname(this.filePath), { recursive: true });
|
|
255
|
+
const compactState = {
|
|
256
|
+
seenPostIds: state.seenPostIds.slice(0, this.maxSeenPostIds),
|
|
257
|
+
updatedAt: state.updatedAt
|
|
258
|
+
};
|
|
259
|
+
await promises.writeFile(this.filePath, JSON.stringify(compactState, null, 2), "utf8");
|
|
260
|
+
}
|
|
261
|
+
};
|
|
262
|
+
|
|
263
|
+
// src/qqbot/server/xhsMonitor/service.ts
|
|
264
|
+
function sortPostsByPublishedAt(posts) {
|
|
265
|
+
return [...posts].sort((a, b) => {
|
|
266
|
+
const ta = a.publishedAt ? Date.parse(a.publishedAt) : Number.NaN;
|
|
267
|
+
const tb = b.publishedAt ? Date.parse(b.publishedAt) : Number.NaN;
|
|
268
|
+
if (Number.isNaN(ta) || Number.isNaN(tb)) {
|
|
269
|
+
return 0;
|
|
270
|
+
}
|
|
271
|
+
return ta - tb;
|
|
272
|
+
});
|
|
273
|
+
}
|
|
274
|
+
var XhsMonitorService = class {
|
|
275
|
+
constructor(options) {
|
|
276
|
+
this.timer = null;
|
|
277
|
+
this.initialized = false;
|
|
278
|
+
this.running = false;
|
|
279
|
+
this.accountLabel = options.accountLabel;
|
|
280
|
+
this.source = options.source;
|
|
281
|
+
this.notifier = options.notifier;
|
|
282
|
+
this.stateStore = options.stateStore;
|
|
283
|
+
this.pollIntervalMs = Math.max(5e3, options.pollIntervalMs);
|
|
284
|
+
this.skipInitialSnapshot = options.skipInitialSnapshot ?? true;
|
|
285
|
+
this.postLimit = options.postLimit ?? 20;
|
|
286
|
+
this.now = options.now ?? (() => /* @__PURE__ */ new Date());
|
|
287
|
+
}
|
|
288
|
+
async checkOnce() {
|
|
289
|
+
if (this.running) {
|
|
290
|
+
return { notifiedCount: 0, fetchedCount: 0 };
|
|
291
|
+
}
|
|
292
|
+
this.running = true;
|
|
293
|
+
try {
|
|
294
|
+
const state = await this.stateStore.read();
|
|
295
|
+
const seen = new Set(state.seenPostIds);
|
|
296
|
+
const posts = await this.source.fetchLatestPosts(this.postLimit);
|
|
297
|
+
const orderedPosts = sortPostsByPublishedAt(posts);
|
|
298
|
+
const newlyDetected = orderedPosts.filter((post) => !seen.has(post.id));
|
|
299
|
+
if (!this.initialized && this.skipInitialSnapshot) {
|
|
300
|
+
for (const post of orderedPosts) {
|
|
301
|
+
seen.add(post.id);
|
|
302
|
+
}
|
|
303
|
+
await this.persistSeenIds(seen);
|
|
304
|
+
this.initialized = true;
|
|
305
|
+
return { notifiedCount: 0, fetchedCount: posts.length };
|
|
306
|
+
}
|
|
307
|
+
let notifiedCount = 0;
|
|
308
|
+
for (const post of newlyDetected) {
|
|
309
|
+
await this.notifier.notify({
|
|
310
|
+
accountLabel: this.accountLabel,
|
|
311
|
+
post,
|
|
312
|
+
detectedAt: this.now().toISOString()
|
|
313
|
+
});
|
|
314
|
+
notifiedCount += 1;
|
|
315
|
+
seen.add(post.id);
|
|
316
|
+
}
|
|
317
|
+
if (newlyDetected.length > 0 || !this.initialized) {
|
|
318
|
+
for (const post of orderedPosts) {
|
|
319
|
+
seen.add(post.id);
|
|
320
|
+
}
|
|
321
|
+
await this.persistSeenIds(seen);
|
|
322
|
+
}
|
|
323
|
+
this.initialized = true;
|
|
324
|
+
return { notifiedCount, fetchedCount: posts.length };
|
|
325
|
+
} finally {
|
|
326
|
+
this.running = false;
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
start(immediate = true) {
|
|
330
|
+
if (this.timer) {
|
|
331
|
+
return;
|
|
332
|
+
}
|
|
333
|
+
const run = async () => {
|
|
334
|
+
try {
|
|
335
|
+
await this.checkOnce();
|
|
336
|
+
} catch (error) {
|
|
337
|
+
console.error("[XhsMonitorService] check failed", error);
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
if (immediate) {
|
|
341
|
+
void run();
|
|
342
|
+
}
|
|
343
|
+
this.timer = setInterval(() => {
|
|
344
|
+
void run();
|
|
345
|
+
}, this.pollIntervalMs);
|
|
346
|
+
}
|
|
347
|
+
stop() {
|
|
348
|
+
if (this.timer) {
|
|
349
|
+
clearInterval(this.timer);
|
|
350
|
+
this.timer = null;
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
async persistSeenIds(seenIds) {
|
|
354
|
+
await this.stateStore.write({
|
|
355
|
+
seenPostIds: Array.from(seenIds).slice(-500),
|
|
356
|
+
updatedAt: this.now().toISOString()
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
};
|
|
360
|
+
|
|
361
|
+
// src/qqbot/server/xhsMonitor/app.ts
|
|
362
|
+
function createXhsMonitorApp(options) {
|
|
363
|
+
const client = new NapCatClient(options.napCat);
|
|
364
|
+
const source = new XhsProfileHtmlSource({
|
|
365
|
+
profileUrl: options.profileUrl
|
|
366
|
+
});
|
|
367
|
+
const stateStore = new FileXhsMonitorStateStore({
|
|
368
|
+
filePath: options.stateFilePath
|
|
369
|
+
});
|
|
370
|
+
const notifier = new XhsQqNotifier({
|
|
371
|
+
client,
|
|
372
|
+
target: options.target
|
|
373
|
+
});
|
|
374
|
+
return new XhsMonitorService({
|
|
375
|
+
accountLabel: options.accountLabel,
|
|
376
|
+
source,
|
|
377
|
+
notifier,
|
|
378
|
+
stateStore,
|
|
379
|
+
pollIntervalMs: options.pollIntervalMs,
|
|
380
|
+
skipInitialSnapshot: options.skipInitialSnapshot,
|
|
381
|
+
postLimit: options.postLimit
|
|
382
|
+
});
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
exports.FileXhsMonitorStateStore = FileXhsMonitorStateStore;
|
|
140
386
|
exports.NapCatClient = NapCatClient;
|
|
387
|
+
exports.XhsMonitorService = XhsMonitorService;
|
|
388
|
+
exports.XhsProfileHtmlSource = XhsProfileHtmlSource;
|
|
389
|
+
exports.XhsQqNotifier = XhsQqNotifier;
|
|
141
390
|
exports.createNapCatWebApi = createNapCatWebApi;
|
|
142
391
|
exports.createNextNapCatRouteHandler = createNextNapCatRouteHandler;
|
|
392
|
+
exports.createXhsMonitorApp = createXhsMonitorApp;
|
|
143
393
|
//# sourceMappingURL=index.js.map
|
|
144
394
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../src/qqbot/server/client.ts","../../../src/qqbot/server/webApi.ts","../../../src/qqbot/server/nextRouteAdapter.ts"],"names":["NextResponse"],"mappings":"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","file":"index.js","sourcesContent":["import {\n NapCatConfig,\n OneBotActionResponse,\n OneBotFriendInfo,\n OneBotGroupInfo,\n SendGroupMessagePayload,\n SendPrivateMessagePayload,\n} from '../types';\n\nexport class NapCatClient {\n private readonly baseUrl: string;\n private readonly accessToken?: string;\n private readonly timeoutMs: number;\n\n constructor(config: NapCatConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.accessToken = config.accessToken;\n this.timeoutMs = config.timeoutMs ?? 12000;\n }\n\n async callApi<TData = unknown>(action: string, params?: Record<string, unknown>): Promise<OneBotActionResponse<TData>> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const response = await fetch(`${this.baseUrl}/${action}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}),\n },\n body: JSON.stringify(params ?? {}),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n return {\n status: 'failed',\n retcode: response.status,\n message: `NapCat HTTP ${response.status}`,\n };\n }\n\n const payload = (await response.json()) as OneBotActionResponse<TData>;\n return payload;\n } catch (error) {\n return {\n status: 'failed',\n retcode: -1,\n message: error instanceof Error ? error.message : 'Unknown NapCat request error',\n };\n } finally {\n clearTimeout(timeout);\n }\n }\n\n async sendGroupMessage(payload: SendGroupMessagePayload) {\n return this.callApi<{ message_id: number }>('send_group_msg', payload as unknown as Record<string, unknown>);\n }\n\n async sendPrivateMessage(payload: SendPrivateMessagePayload) {\n return this.callApi<{ message_id: number }>('send_private_msg', payload as unknown as Record<string, unknown>);\n }\n\n async getGroupList() {\n return this.callApi<OneBotGroupInfo[]>('get_group_list');\n }\n\n async getFriendList() {\n return this.callApi<OneBotFriendInfo[]>('get_friend_list');\n }\n}\n","import { NapCatWebhookEvent, NapCatWebApiResponse } from '../types';\nimport { NapCatClient } from './client';\n\nexport interface HttpLikeRequest {\n method: string;\n path: string;\n body?: unknown;\n}\n\nexport interface HttpLikeResponse {\n status: number;\n headers?: Record<string, string>;\n body: NapCatWebApiResponse<unknown>;\n}\n\nexport interface NapCatWebApiOptions {\n onWebhookEvent?: (event: NapCatWebhookEvent) => Promise<void> | void;\n}\n\nfunction json(status: number, body: NapCatWebApiResponse<unknown>): HttpLikeResponse {\n return {\n status,\n headers: { 'Content-Type': 'application/json' },\n body,\n };\n}\n\nexport function createNapCatWebApi(client: NapCatClient, options: NapCatWebApiOptions = {}) {\n return async function handle(request: HttpLikeRequest): Promise<HttpLikeResponse> {\n const method = request.method.toUpperCase();\n\n if (method === 'GET' && request.path === '/health') {\n return json(200, { ok: true, data: { service: 'qqbot-napcat-bridge' } });\n }\n\n if (method === 'POST' && request.path === '/send/group') {\n const result = await client.sendGroupMessage((request.body ?? {}) as any);\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path === '/send/private') {\n const result = await client.sendPrivateMessage((request.body ?? {}) as any);\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path === '/groups') {\n const result = await client.getGroupList();\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path === '/friends') {\n const result = await client.getFriendList();\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path.startsWith('/action/')) {\n const action = request.path.replace('/action/', '');\n const result = await client.callApi(action, (request.body ?? {}) as Record<string, unknown>);\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path === '/webhook/event') {\n const event = (request.body ?? {}) as NapCatWebhookEvent;\n await options.onWebhookEvent?.(event);\n return json(200, { ok: true });\n }\n\n return json(404, { ok: false, error: `Route not found: ${method} ${request.path}` });\n };\n}\n","import { NextRequest, NextResponse } from 'next/server';\nimport { HttpLikeRequest, createNapCatWebApi } from './webApi';\nimport { NapCatClient } from './client';\n\nexport interface NextNapCatRouteConfig {\n client: NapCatClient;\n basePath?: string;\n onWebhookEvent?: (event: Record<string, unknown>) => Promise<void> | void;\n}\n\nexport function createNextNapCatRouteHandler(config: NextNapCatRouteConfig) {\n const basePath = config.basePath ?? '/api/qqbot';\n const handle = createNapCatWebApi(config.client, {\n onWebhookEvent: config.onWebhookEvent,\n });\n\n return async function handler(request: NextRequest) {\n const url = new URL(request.url);\n const path = url.pathname.startsWith(basePath) ? url.pathname.slice(basePath.length) || '/' : url.pathname;\n\n const body = request.method.toUpperCase() === 'GET' ? undefined : await request.json().catch(() => undefined);\n\n const payload: HttpLikeRequest = {\n method: request.method,\n path,\n body,\n };\n\n const result = await handle(payload);\n return NextResponse.json(result.body, { status: result.status, headers: result.headers });\n };\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../../../src/qqbot/server/client.ts","../../../src/qqbot/server/webApi.ts","../../../src/qqbot/server/nextRouteAdapter.ts","../../../src/qqbot/server/xhsMonitor/notifier.ts","../../../src/qqbot/server/xhsMonitor/source.ts","../../../src/qqbot/server/xhsMonitor/stateStore.ts","../../../src/qqbot/server/xhsMonitor/service.ts","../../../src/qqbot/server/xhsMonitor/app.ts"],"names":["NextResponse","readFile","mkdir","dirname","writeFile"],"mappings":"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","file":"index.js","sourcesContent":["import {\n NapCatConfig,\n OneBotActionResponse,\n OneBotFriendInfo,\n OneBotGroupInfo,\n SendGroupMessagePayload,\n SendPrivateMessagePayload,\n} from '../types';\n\nexport class NapCatClient {\n private readonly baseUrl: string;\n private readonly accessToken?: string;\n private readonly timeoutMs: number;\n\n constructor(config: NapCatConfig) {\n this.baseUrl = config.baseUrl.replace(/\\/$/, '');\n this.accessToken = config.accessToken;\n this.timeoutMs = config.timeoutMs ?? 12000;\n }\n\n async callApi<TData = unknown>(action: string, params?: Record<string, unknown>): Promise<OneBotActionResponse<TData>> {\n const controller = new AbortController();\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const response = await fetch(`${this.baseUrl}/${action}`, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n ...(this.accessToken ? { Authorization: `Bearer ${this.accessToken}` } : {}),\n },\n body: JSON.stringify(params ?? {}),\n signal: controller.signal,\n });\n\n if (!response.ok) {\n return {\n status: 'failed',\n retcode: response.status,\n message: `NapCat HTTP ${response.status}`,\n };\n }\n\n const payload = (await response.json()) as OneBotActionResponse<TData>;\n return payload;\n } catch (error) {\n return {\n status: 'failed',\n retcode: -1,\n message: error instanceof Error ? error.message : 'Unknown NapCat request error',\n };\n } finally {\n clearTimeout(timeout);\n }\n }\n\n async sendGroupMessage(payload: SendGroupMessagePayload) {\n return this.callApi<{ message_id: number }>('send_group_msg', payload as unknown as Record<string, unknown>);\n }\n\n async sendPrivateMessage(payload: SendPrivateMessagePayload) {\n return this.callApi<{ message_id: number }>('send_private_msg', payload as unknown as Record<string, unknown>);\n }\n\n async getGroupList() {\n return this.callApi<OneBotGroupInfo[]>('get_group_list');\n }\n\n async getFriendList() {\n return this.callApi<OneBotFriendInfo[]>('get_friend_list');\n }\n}\n","import { NapCatWebhookEvent, NapCatWebApiResponse } from '../types';\nimport { NapCatClient } from './client';\n\nexport interface HttpLikeRequest {\n method: string;\n path: string;\n body?: unknown;\n}\n\nexport interface HttpLikeResponse {\n status: number;\n headers?: Record<string, string>;\n body: NapCatWebApiResponse<unknown>;\n}\n\nexport interface NapCatWebApiOptions {\n onWebhookEvent?: (event: NapCatWebhookEvent) => Promise<void> | void;\n}\n\nfunction json(status: number, body: NapCatWebApiResponse<unknown>): HttpLikeResponse {\n return {\n status,\n headers: { 'Content-Type': 'application/json' },\n body,\n };\n}\n\nexport function createNapCatWebApi(client: NapCatClient, options: NapCatWebApiOptions = {}) {\n return async function handle(request: HttpLikeRequest): Promise<HttpLikeResponse> {\n const method = request.method.toUpperCase();\n\n if (method === 'GET' && request.path === '/health') {\n return json(200, { ok: true, data: { service: 'qqbot-napcat-bridge' } });\n }\n\n if (method === 'POST' && request.path === '/send/group') {\n const result = await client.sendGroupMessage((request.body ?? {}) as any);\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path === '/send/private') {\n const result = await client.sendPrivateMessage((request.body ?? {}) as any);\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path === '/groups') {\n const result = await client.getGroupList();\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path === '/friends') {\n const result = await client.getFriendList();\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path.startsWith('/action/')) {\n const action = request.path.replace('/action/', '');\n const result = await client.callApi(action, (request.body ?? {}) as Record<string, unknown>);\n return json(result.status === 'ok' ? 200 : 502, {\n ok: result.status === 'ok',\n data: result.data,\n error: result.message,\n });\n }\n\n if (method === 'POST' && request.path === '/webhook/event') {\n const event = (request.body ?? {}) as NapCatWebhookEvent;\n await options.onWebhookEvent?.(event);\n return json(200, { ok: true });\n }\n\n return json(404, { ok: false, error: `Route not found: ${method} ${request.path}` });\n };\n}\n","import { NextRequest, NextResponse } from 'next/server';\nimport { HttpLikeRequest, createNapCatWebApi } from './webApi';\nimport { NapCatClient } from './client';\n\nexport interface NextNapCatRouteConfig {\n client: NapCatClient;\n basePath?: string;\n onWebhookEvent?: (event: Record<string, unknown>) => Promise<void> | void;\n}\n\nexport function createNextNapCatRouteHandler(config: NextNapCatRouteConfig) {\n const basePath = config.basePath ?? '/api/qqbot';\n const handle = createNapCatWebApi(config.client, {\n onWebhookEvent: config.onWebhookEvent,\n });\n\n return async function handler(request: NextRequest) {\n const url = new URL(request.url);\n const path = url.pathname.startsWith(basePath) ? url.pathname.slice(basePath.length) || '/' : url.pathname;\n\n const body = request.method.toUpperCase() === 'GET' ? undefined : await request.json().catch(() => undefined);\n\n const payload: HttpLikeRequest = {\n method: request.method,\n path,\n body,\n };\n\n const result = await handle(payload);\n return NextResponse.json(result.body, { status: result.status, headers: result.headers });\n };\n}\n","import { NapCatClient } from '../client';\nimport { XhsNotificationPayload, XhsNotifier } from './types';\n\nexport interface XhsQqNotifierOptions {\n client: NapCatClient;\n target: { type: 'group'; groupId: number } | { type: 'private'; userId: number };\n messageTemplate?: (payload: XhsNotificationPayload) => string;\n}\n\nfunction defaultMessageTemplate(payload: XhsNotificationPayload): string {\n return [\n `【小红书新帖提醒】${payload.accountLabel}`,\n payload.post.title ? `标题:${payload.post.title}` : undefined,\n `链接:${payload.post.url}`,\n `检测时间:${payload.detectedAt}`,\n ]\n .filter(Boolean)\n .join('\\n');\n}\n\nexport class XhsQqNotifier implements XhsNotifier {\n private readonly client: NapCatClient;\n private readonly target: XhsQqNotifierOptions['target'];\n private readonly messageTemplate: (payload: XhsNotificationPayload) => string;\n\n constructor(options: XhsQqNotifierOptions) {\n this.client = options.client;\n this.target = options.target;\n this.messageTemplate = options.messageTemplate ?? defaultMessageTemplate;\n }\n\n async notify(payload: XhsNotificationPayload): Promise<void> {\n const message = this.messageTemplate(payload);\n const response =\n this.target.type === 'group'\n ? await this.client.sendGroupMessage({\n group_id: this.target.groupId,\n message,\n })\n : await this.client.sendPrivateMessage({\n user_id: this.target.userId,\n message,\n });\n\n if (response.status !== 'ok') {\n throw new Error(`Failed to send QQ message: ${response.message ?? 'Unknown error'}`);\n }\n }\n}\n","import { XhsPost, XhsSource } from './types';\n\nexport interface XhsProfileHtmlSourceOptions {\n profileUrl: string;\n timeoutMs?: number;\n userAgent?: string;\n}\n\nfunction decodeHtmlEntities(input: string): string {\n return input\n .replace(/\\\\u003c/g, '<')\n .replace(/\\\\u003e/g, '>')\n .replace(/\\\\u0026/g, '&')\n .replace(/\\\\\"/g, '\"')\n .replace(/\\\\n/g, ' ')\n .trim();\n}\n\nfunction normalizeTitle(title: string): string {\n return decodeHtmlEntities(title).replace(/\\s+/g, ' ').trim();\n}\n\nexport class XhsProfileHtmlSource implements XhsSource {\n private readonly profileUrl: string;\n private readonly timeoutMs: number;\n private readonly userAgent: string;\n\n constructor(options: XhsProfileHtmlSourceOptions) {\n this.profileUrl = options.profileUrl;\n this.timeoutMs = options.timeoutMs ?? 12000;\n this.userAgent =\n options.userAgent ??\n 'Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0 Safari/537.36';\n }\n\n async fetchLatestPosts(limit = 20): Promise<XhsPost[]> {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), this.timeoutMs);\n\n try {\n const response = await fetch(this.profileUrl, {\n method: 'GET',\n headers: {\n 'User-Agent': this.userAgent,\n Accept: 'text/html,application/xhtml+xml',\n },\n signal: controller.signal,\n });\n\n if (!response.ok) {\n throw new Error(`Failed to fetch profile page: HTTP ${response.status}`);\n }\n\n const html = await response.text();\n return this.parsePostsFromHtml(html, limit);\n } finally {\n clearTimeout(timer);\n }\n }\n\n private parsePostsFromHtml(html: string, limit: number): XhsPost[] {\n const idMatches = [...html.matchAll(/(?:\\/explore\\/|\\\"noteId\\\"\\s*:\\s*\\\")([a-zA-Z0-9_-]{8,32})/g)];\n const titleMatches = [...html.matchAll(/\\\"displayTitle\\\"\\s*:\\s*\\\"([^\"]{1,120})\\\"/g)];\n\n const uniqueIds: string[] = [];\n const seen = new Set<string>();\n for (const match of idMatches) {\n const id = match[1];\n if (!id || seen.has(id)) {\n continue;\n }\n seen.add(id);\n uniqueIds.push(id);\n if (uniqueIds.length >= limit) {\n break;\n }\n }\n\n const titles = titleMatches.map((match) => normalizeTitle(match[1] ?? '')).filter(Boolean);\n\n return uniqueIds.map((id, index) => ({\n id,\n url: `https://www.xiaohongshu.com/explore/${id}`,\n title: titles[index],\n }));\n }\n}\n","import { mkdir, readFile, writeFile } from 'node:fs/promises';\nimport { dirname } from 'node:path';\nimport { XhsMonitorState, XhsMonitorStateStore } from './types';\n\nconst DEFAULT_STATE: XhsMonitorState = {\n seenPostIds: [],\n updatedAt: new Date(0).toISOString(),\n};\n\nexport interface FileXhsMonitorStateStoreOptions {\n filePath: string;\n maxSeenPostIds?: number;\n}\n\nexport class FileXhsMonitorStateStore implements XhsMonitorStateStore {\n private readonly filePath: string;\n private readonly maxSeenPostIds: number;\n\n constructor(options: FileXhsMonitorStateStoreOptions) {\n this.filePath = options.filePath;\n this.maxSeenPostIds = Math.max(10, options.maxSeenPostIds ?? 500);\n }\n\n async read(): Promise<XhsMonitorState> {\n try {\n const text = await readFile(this.filePath, 'utf8');\n const parsed = JSON.parse(text) as Partial<XhsMonitorState>;\n const seenPostIds = Array.isArray(parsed.seenPostIds)\n ? parsed.seenPostIds.filter((item): item is string => typeof item === 'string')\n : [];\n const updatedAt = typeof parsed.updatedAt === 'string' ? parsed.updatedAt : new Date().toISOString();\n return {\n seenPostIds: seenPostIds.slice(0, this.maxSeenPostIds),\n updatedAt,\n };\n } catch {\n return DEFAULT_STATE;\n }\n }\n\n async write(state: XhsMonitorState): Promise<void> {\n await mkdir(dirname(this.filePath), { recursive: true });\n const compactState: XhsMonitorState = {\n seenPostIds: state.seenPostIds.slice(0, this.maxSeenPostIds),\n updatedAt: state.updatedAt,\n };\n await writeFile(this.filePath, JSON.stringify(compactState, null, 2), 'utf8');\n }\n}\n","import { XhsMonitorStateStore, XhsNotifier, XhsPost, XhsSource } from './types';\n\nexport interface XhsMonitorServiceOptions {\n accountLabel: string;\n source: XhsSource;\n notifier: XhsNotifier;\n stateStore: XhsMonitorStateStore;\n pollIntervalMs: number;\n skipInitialSnapshot?: boolean;\n postLimit?: number;\n now?: () => Date;\n}\n\nfunction sortPostsByPublishedAt(posts: XhsPost[]): XhsPost[] {\n return [...posts].sort((a, b) => {\n const ta = a.publishedAt ? Date.parse(a.publishedAt) : Number.NaN;\n const tb = b.publishedAt ? Date.parse(b.publishedAt) : Number.NaN;\n if (Number.isNaN(ta) || Number.isNaN(tb)) {\n return 0;\n }\n return ta - tb;\n });\n}\n\nexport class XhsMonitorService {\n private readonly accountLabel: string;\n private readonly source: XhsSource;\n private readonly notifier: XhsNotifier;\n private readonly stateStore: XhsMonitorStateStore;\n private readonly pollIntervalMs: number;\n private readonly skipInitialSnapshot: boolean;\n private readonly postLimit: number;\n private readonly now: () => Date;\n private timer: NodeJS.Timeout | null = null;\n private initialized = false;\n private running = false;\n\n constructor(options: XhsMonitorServiceOptions) {\n this.accountLabel = options.accountLabel;\n this.source = options.source;\n this.notifier = options.notifier;\n this.stateStore = options.stateStore;\n this.pollIntervalMs = Math.max(5000, options.pollIntervalMs);\n this.skipInitialSnapshot = options.skipInitialSnapshot ?? true;\n this.postLimit = options.postLimit ?? 20;\n this.now = options.now ?? (() => new Date());\n }\n\n async checkOnce(): Promise<{ notifiedCount: number; fetchedCount: number }> {\n if (this.running) {\n return { notifiedCount: 0, fetchedCount: 0 };\n }\n\n this.running = true;\n try {\n const state = await this.stateStore.read();\n const seen = new Set(state.seenPostIds);\n const posts = await this.source.fetchLatestPosts(this.postLimit);\n const orderedPosts = sortPostsByPublishedAt(posts);\n const newlyDetected = orderedPosts.filter((post) => !seen.has(post.id));\n\n if (!this.initialized && this.skipInitialSnapshot) {\n for (const post of orderedPosts) {\n seen.add(post.id);\n }\n await this.persistSeenIds(seen);\n this.initialized = true;\n return { notifiedCount: 0, fetchedCount: posts.length };\n }\n\n let notifiedCount = 0;\n for (const post of newlyDetected) {\n await this.notifier.notify({\n accountLabel: this.accountLabel,\n post,\n detectedAt: this.now().toISOString(),\n });\n notifiedCount += 1;\n seen.add(post.id);\n }\n\n if (newlyDetected.length > 0 || !this.initialized) {\n for (const post of orderedPosts) {\n seen.add(post.id);\n }\n await this.persistSeenIds(seen);\n }\n\n this.initialized = true;\n return { notifiedCount, fetchedCount: posts.length };\n } finally {\n this.running = false;\n }\n }\n\n start(immediate = true): void {\n if (this.timer) {\n return;\n }\n\n const run = async () => {\n try {\n await this.checkOnce();\n } catch (error) {\n console.error('[XhsMonitorService] check failed', error);\n }\n };\n\n if (immediate) {\n void run();\n }\n\n this.timer = setInterval(() => {\n void run();\n }, this.pollIntervalMs);\n }\n\n stop(): void {\n if (this.timer) {\n clearInterval(this.timer);\n this.timer = null;\n }\n }\n\n private async persistSeenIds(seenIds: Set<string>): Promise<void> {\n await this.stateStore.write({\n seenPostIds: Array.from(seenIds).slice(-500),\n updatedAt: this.now().toISOString(),\n });\n }\n}\n","import { NapCatClient } from '../client';\nimport { XhsQqNotifier } from './notifier';\nimport { XhsProfileHtmlSource } from './source';\nimport { FileXhsMonitorStateStore } from './stateStore';\nimport { XhsMonitorService } from './service';\n\nexport interface CreateXhsMonitorAppOptions {\n accountLabel: string;\n profileUrl: string;\n pollIntervalMs: number;\n stateFilePath: string;\n napCat: {\n baseUrl: string;\n accessToken?: string;\n timeoutMs?: number;\n };\n target: { type: 'group'; groupId: number } | { type: 'private'; userId: number };\n skipInitialSnapshot?: boolean;\n postLimit?: number;\n}\n\nexport function createXhsMonitorApp(options: CreateXhsMonitorAppOptions): XhsMonitorService {\n const client = new NapCatClient(options.napCat);\n const source = new XhsProfileHtmlSource({\n profileUrl: options.profileUrl,\n });\n const stateStore = new FileXhsMonitorStateStore({\n filePath: options.stateFilePath,\n });\n const notifier = new XhsQqNotifier({\n client,\n target: options.target,\n });\n\n return new XhsMonitorService({\n accountLabel: options.accountLabel,\n source,\n notifier,\n stateStore,\n pollIntervalMs: options.pollIntervalMs,\n skipInitialSnapshot: options.skipInitialSnapshot,\n postLimit: options.postLimit,\n });\n}\n"]}
|