koishi-plugin-wordpress-notifier 2.9.3 → 2.9.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/README.md +7 -0
- package/lib/schedule.js +75 -8
- package/lib/wordpress.d.ts +1 -0
- package/lib/wordpress.js +34 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -205,6 +205,13 @@ npm install koishi-plugin-wordpress-notifier
|
|
|
205
205
|
|
|
206
206
|
## 版本历史
|
|
207
207
|
|
|
208
|
+
### v2.9.4
|
|
209
|
+
- 🎉 **Bug 修复**:修复文章更新检测误判问题,仅使用修改时间判断,避免因哈希值变化导致所有文章被重复推送
|
|
210
|
+
- 🚀 **性能优化**:将文章检查数量从 20 篇减少到 10 篇,减少不必要的检查
|
|
211
|
+
- ✨ **功能完善**:完整实现新用户注册推送功能
|
|
212
|
+
- 📊 **用户体验**:添加详细的更新检测日志,便于调试和追踪
|
|
213
|
+
- 🔧 **代码优化**:改进更新检测逻辑,使用秒级时间戳比较,避免精度问题
|
|
214
|
+
|
|
208
215
|
### v2.9.3
|
|
209
216
|
- 🎉 **并发控制**:使用 p-limit 替代手动队列,彻底解决线程安全问题
|
|
210
217
|
- 🚀 **性能优化**:添加 10 秒缓存防抖,避免重复请求
|
package/lib/schedule.js
CHANGED
|
@@ -52,7 +52,10 @@ class ScheduleService {
|
|
|
52
52
|
const interval = (this.config.checkInterval || 30) * 60 * 1000;
|
|
53
53
|
this.intervalId = setInterval(async () => {
|
|
54
54
|
try {
|
|
55
|
+
// 检查文章更新
|
|
55
56
|
await this.checkAndPushArticles();
|
|
57
|
+
// 检查新用户注册
|
|
58
|
+
await this.checkNewUsers();
|
|
56
59
|
}
|
|
57
60
|
catch (error) {
|
|
58
61
|
this.ctx.logger.error('Error in scheduled task:', error);
|
|
@@ -178,8 +181,8 @@ class ScheduleService {
|
|
|
178
181
|
if (!this.isPushTimeAllowed()) {
|
|
179
182
|
return;
|
|
180
183
|
}
|
|
181
|
-
//
|
|
182
|
-
const articles = await this.wordpressService.getLatestPosts(
|
|
184
|
+
// 获取最新文章(只获取最新的 10 篇,减少不必要的检查)
|
|
185
|
+
const articles = await this.wordpressService.getLatestPosts(10);
|
|
183
186
|
if (articles.length === 0) {
|
|
184
187
|
this.ctx.logger.info('暂无新文章');
|
|
185
188
|
return;
|
|
@@ -230,17 +233,24 @@ class ScheduleService {
|
|
|
230
233
|
if (pushedArticle) {
|
|
231
234
|
const currentModified = new Date(article.modified || article.date);
|
|
232
235
|
const storedModified = new Date(pushedArticle.modified);
|
|
233
|
-
|
|
234
|
-
//
|
|
235
|
-
|
|
236
|
+
// 修复:仅比较修改时间,移除哈希值比较,避免因动态字段变化导致误判
|
|
237
|
+
// 使用时间戳比较,忽略毫秒级差异,避免因时间精度导致误判
|
|
238
|
+
const currentTime = Math.floor(currentModified.getTime() / 1000); // 秒级时间戳
|
|
239
|
+
const storedTime = Math.floor(storedModified.getTime() / 1000);
|
|
240
|
+
// 检查是否有更新(仅当修改时间真正发生变化时才推送)
|
|
241
|
+
if (currentTime > storedTime) {
|
|
242
|
+
this.ctx.logger.info(`检测到文章 ${article.id} 有更新,修改时间从 ${storedModified.toISOString()} 变更为 ${currentModified.toISOString()}`);
|
|
236
243
|
// 推送更新通知
|
|
237
244
|
const success = await this.pushService.pushArticleUpdate(article, groupId);
|
|
238
245
|
if (success) {
|
|
239
246
|
// 更新存储的修改时间和哈希值
|
|
240
|
-
await this.storageService.updatePushedArticle(article.id, groupId, currentModified,
|
|
247
|
+
await this.storageService.updatePushedArticle(article.id, groupId, currentModified, this.pushService.generateHash(JSON.stringify(article)));
|
|
241
248
|
this.ctx.logger.info(`已推送文章 ${article.id} 的更新到群聊 ${groupId}`);
|
|
242
249
|
}
|
|
243
250
|
}
|
|
251
|
+
else {
|
|
252
|
+
this.ctx.logger.debug(`文章 ${article.id} 无更新,跳过`);
|
|
253
|
+
}
|
|
244
254
|
}
|
|
245
255
|
}
|
|
246
256
|
}
|
|
@@ -289,13 +299,70 @@ class ScheduleService {
|
|
|
289
299
|
}
|
|
290
300
|
// 检查新用户注册
|
|
291
301
|
async checkNewUsers() {
|
|
292
|
-
|
|
293
|
-
|
|
302
|
+
try {
|
|
303
|
+
// 检查是否启用用户注册推送
|
|
304
|
+
if (!this.config.enableUserRegisterPush) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
// 获取最新注册的用户(最近10个)
|
|
308
|
+
const users = await this.wordpressService.getLatestUsers(10);
|
|
309
|
+
if (users.length === 0) {
|
|
310
|
+
this.ctx.logger.info('暂无新用户注册');
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
// 获取所有启用推送的群聊
|
|
314
|
+
const groups = await this.storageService.getEnabledGroups();
|
|
315
|
+
if (groups.length === 0) {
|
|
316
|
+
this.ctx.logger.info('没有启用推送的群聊');
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
this.ctx.logger.info(`检查 ${users.length} 个新用户,推送至 ${groups.length} 个群聊`);
|
|
320
|
+
// 收集所有推送任务
|
|
321
|
+
const pushTasks = [];
|
|
322
|
+
// 检查每个用户
|
|
323
|
+
for (const user of users) {
|
|
324
|
+
for (const groupId of groups) {
|
|
325
|
+
pushTasks.push(async () => {
|
|
326
|
+
try {
|
|
327
|
+
// 记录用户并检查是否为新用户
|
|
328
|
+
const isNewUser = await this.storageService.recordWpUser(user.id, user.name, new Date(user.registered_date));
|
|
329
|
+
if (isNewUser) {
|
|
330
|
+
// 获取未通知的用户
|
|
331
|
+
const unnotifiedUsers = await this.storageService.getUnnotifiedWpUsers();
|
|
332
|
+
// 为每个未通知的用户推送
|
|
333
|
+
for (const unnotifiedUser of unnotifiedUsers) {
|
|
334
|
+
const success = await this.pushService.pushUserRegister(unnotifiedUser, groupId);
|
|
335
|
+
if (success) {
|
|
336
|
+
// 标记为已通知
|
|
337
|
+
await this.storageService.markWpUserNotified(unnotifiedUser.wpUserId);
|
|
338
|
+
this.ctx.logger.info(`已推送新用户 ${unnotifiedUser.username} 到群聊 ${groupId}`);
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
this.ctx.logger.warn(`推送新用户 ${unnotifiedUser.username} 到群聊 ${groupId} 失败`);
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
catch (error) {
|
|
347
|
+
this.ctx.logger.error(`处理用户 ${user.id} 注册推送失败:`, error);
|
|
348
|
+
}
|
|
349
|
+
});
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
// 执行推送任务
|
|
353
|
+
const result = await this.executeWithConcurrencyLimit(pushTasks);
|
|
354
|
+
// 记录推送统计
|
|
355
|
+
this.ctx.logger.info(`新用户注册推送完成:成功 ${result.success} 次,失败 ${result.failed} 次`);
|
|
356
|
+
}
|
|
357
|
+
catch (error) {
|
|
358
|
+
this.ctx.logger.error('检查新用户注册失败:', error);
|
|
359
|
+
}
|
|
294
360
|
}
|
|
295
361
|
// 手动触发检查
|
|
296
362
|
async triggerCheck() {
|
|
297
363
|
try {
|
|
298
364
|
await this.checkAndPushArticles();
|
|
365
|
+
await this.checkNewUsers();
|
|
299
366
|
}
|
|
300
367
|
catch (error) {
|
|
301
368
|
this.ctx.logger.error('Error triggering check:', error);
|
package/lib/wordpress.d.ts
CHANGED
|
@@ -91,6 +91,7 @@ export declare class WordPressService {
|
|
|
91
91
|
private cleanupCache;
|
|
92
92
|
getLatestPosts(perPage?: number, page?: number): Promise<WordPressPost[]>;
|
|
93
93
|
getPostById(id: number): Promise<WordPressPost>;
|
|
94
|
+
getLatestUsers(perPage?: number, page?: number): Promise<any[]>;
|
|
94
95
|
getStatus(): {
|
|
95
96
|
wordpressUrl: string;
|
|
96
97
|
apiEndpoint: string;
|
package/lib/wordpress.js
CHANGED
|
@@ -393,6 +393,40 @@ class WordPressService {
|
|
|
393
393
|
throw error;
|
|
394
394
|
}
|
|
395
395
|
}
|
|
396
|
+
// 获取最新注册的用户
|
|
397
|
+
async getLatestUsers(perPage = 10, page = 1) {
|
|
398
|
+
this.stats.commandCount++;
|
|
399
|
+
const cacheKey = `latest-users-${perPage}-${page}`;
|
|
400
|
+
const cachedData = this.getCache(cacheKey);
|
|
401
|
+
if (cachedData) {
|
|
402
|
+
return cachedData;
|
|
403
|
+
}
|
|
404
|
+
try {
|
|
405
|
+
// 获取最新注册的用户
|
|
406
|
+
const requestParams = {
|
|
407
|
+
per_page: Math.min(perPage, 100),
|
|
408
|
+
page: Math.max(1, page),
|
|
409
|
+
orderby: 'registered_date',
|
|
410
|
+
order: 'desc'
|
|
411
|
+
};
|
|
412
|
+
const response = await this.executeWithConcurrencyControl(() => this.client.get(`${this.config.apiEndpoint || '/wp-json/wp/v2'}/users`, {
|
|
413
|
+
params: requestParams
|
|
414
|
+
}), { method: 'GET', params: requestParams });
|
|
415
|
+
const users = response.data.map((user) => ({
|
|
416
|
+
id: user.id,
|
|
417
|
+
name: user.name,
|
|
418
|
+
registered_date: user.registered_date,
|
|
419
|
+
email: user.email,
|
|
420
|
+
link: user.link
|
|
421
|
+
}));
|
|
422
|
+
this.setCache(cacheKey, users);
|
|
423
|
+
return users;
|
|
424
|
+
}
|
|
425
|
+
catch (error) {
|
|
426
|
+
this.logError('Failed to get latest users:', error);
|
|
427
|
+
throw error;
|
|
428
|
+
}
|
|
429
|
+
}
|
|
396
430
|
// 获取服务状态
|
|
397
431
|
getStatus() {
|
|
398
432
|
const successRate = this.stats.totalRequests > 0
|