koishi-plugin-wordpress-notifier 2.5.3 → 2.5.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.d.ts +0 -8
- package/lib/index.js +53 -147
- package/package.json +1 -1
package/lib/index.d.ts
CHANGED
|
@@ -5,7 +5,6 @@ declare module 'koishi' {
|
|
|
5
5
|
interface Tables {
|
|
6
6
|
wordpress_post_updates: WordPressPostUpdateRecord;
|
|
7
7
|
wordpress_user_registrations: WordPressUserRegistrationRecord;
|
|
8
|
-
wordpress_group_pushes: WordPressGroupPushRecord;
|
|
9
8
|
}
|
|
10
9
|
}
|
|
11
10
|
export interface Config {
|
|
@@ -61,13 +60,6 @@ export interface WordPressUserRegistrationRecord {
|
|
|
61
60
|
userId: number;
|
|
62
61
|
pushedAt: Date;
|
|
63
62
|
}
|
|
64
|
-
export interface WordPressGroupPushRecord {
|
|
65
|
-
id: number;
|
|
66
|
-
groupId: string;
|
|
67
|
-
postId: number;
|
|
68
|
-
pushedAt: Date;
|
|
69
|
-
isUpdate: boolean;
|
|
70
|
-
}
|
|
71
63
|
export interface WordPressNotification {
|
|
72
64
|
type: 'post' | 'update' | 'user';
|
|
73
65
|
data: any;
|
package/lib/index.js
CHANGED
|
@@ -15,7 +15,7 @@ exports.Config = koishi_1.Schema.object({
|
|
|
15
15
|
mentionAll: koishi_1.Schema.boolean().default(false).description('是否 @全体成员'),
|
|
16
16
|
maxArticles: koishi_1.Schema.number().default(5).description('每次最多推送的文章数量'),
|
|
17
17
|
username: koishi_1.Schema.string().default('').description('WordPress 用户名(用于 Basic 认证,与应用程序密码配合使用)'),
|
|
18
|
-
applicationPassword: koishi_1.Schema.string().default('').description('WordPress 应用程序密码(用于 Basic 认证,例如:
|
|
18
|
+
applicationPassword: koishi_1.Schema.string().default('').description('WordPress 应用程序密码(用于 Basic 认证,例如:hGR2sPFuYnclxHc4AvJq cUtB)'),
|
|
19
19
|
superAdmins: koishi_1.Schema.array(koishi_1.Schema.string()).default([]).description('超级管理员 QQ 号列表')
|
|
20
20
|
});
|
|
21
21
|
function apply(ctx, config) {
|
|
@@ -42,19 +42,6 @@ function apply(ctx, config) {
|
|
|
42
42
|
autoInc: true,
|
|
43
43
|
unique: ['userId']
|
|
44
44
|
});
|
|
45
|
-
ctx.model.extend('wordpress_group_pushes', {
|
|
46
|
-
id: 'integer',
|
|
47
|
-
groupId: 'string',
|
|
48
|
-
postId: 'integer',
|
|
49
|
-
pushedAt: 'timestamp',
|
|
50
|
-
isUpdate: 'boolean'
|
|
51
|
-
}, {
|
|
52
|
-
primary: 'id',
|
|
53
|
-
autoInc: true,
|
|
54
|
-
unique: ['groupId', 'postId']
|
|
55
|
-
});
|
|
56
|
-
// 确保所有插入操作都不手动指定 id 字段,让数据库自动生成
|
|
57
|
-
// Koishi 框架的 autoInc: true 配置会自动处理自增主键
|
|
58
45
|
ctx.logger.info('数据库表配置完成,autoInc: true 已启用,确保插入操作不手动指定 id 字段');
|
|
59
46
|
// 为所有数据库操作添加详细日志,便于诊断自增主键问题
|
|
60
47
|
ctx.on('ready', async () => {
|
|
@@ -62,9 +49,26 @@ function apply(ctx, config) {
|
|
|
62
49
|
ctx.logger.info('数据库表配置:');
|
|
63
50
|
ctx.logger.info('wordpress_post_updates: id 字段设置为 autoInc: true');
|
|
64
51
|
ctx.logger.info('wordpress_user_registrations: id 字段设置为 autoInc: true');
|
|
65
|
-
ctx.logger.info('
|
|
66
|
-
|
|
52
|
+
ctx.logger.info('所有群聊共用一个文章标记,不再区分群聊');
|
|
53
|
+
// 检查并修复数据库表结构问题
|
|
54
|
+
await checkAndFixTableStructure();
|
|
55
|
+
// 执行初始推送
|
|
56
|
+
await pushNewPosts();
|
|
67
57
|
});
|
|
58
|
+
// 检查数据库表结构的函数
|
|
59
|
+
async function checkAndFixTableStructure() {
|
|
60
|
+
try {
|
|
61
|
+
ctx.logger.info('开始检查数据库表结构...');
|
|
62
|
+
ctx.logger.info('所有群聊现在共用一个文章标记,不再区分群聊');
|
|
63
|
+
ctx.logger.info('wordpress_group_pushes 表已不再使用,已移除相关功能');
|
|
64
|
+
ctx.logger.info('表结构检查和修复完成');
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
68
|
+
ctx.logger.error(`检查表结构失败:${errorMessage}`);
|
|
69
|
+
ctx.logger.error(`错误栈:${error instanceof Error ? error.stack : '无'}`);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
68
72
|
async function fetchLatestPosts() {
|
|
69
73
|
try {
|
|
70
74
|
const url = `${config.wordpressUrl}/wp-json/wp/v2/posts?per_page=${config.maxArticles}&orderby=date&order=desc`;
|
|
@@ -181,28 +185,6 @@ function apply(ctx, config) {
|
|
|
181
185
|
return null;
|
|
182
186
|
}
|
|
183
187
|
}
|
|
184
|
-
async function isGroupPushed(groupId, postId) {
|
|
185
|
-
try {
|
|
186
|
-
// 明确查询 groupId + postId 组合是否存在
|
|
187
|
-
const query = { groupId, postId };
|
|
188
|
-
ctx.logger.info(`检查群 ${groupId} 是否已推送文章 ${postId},查询条件:${JSON.stringify(query)}`);
|
|
189
|
-
const records = await ctx.database.get('wordpress_group_pushes', query);
|
|
190
|
-
const result = records.length > 0;
|
|
191
|
-
ctx.logger.info(`检查群 ${groupId} 是否已推送文章 ${postId}:${result ? '是' : '否'}`);
|
|
192
|
-
// 添加调试日志,显示所有相关记录
|
|
193
|
-
if (records.length > 0) {
|
|
194
|
-
ctx.logger.debug(`找到 ${records.length} 条匹配记录:${JSON.stringify(records)}`);
|
|
195
|
-
}
|
|
196
|
-
return result;
|
|
197
|
-
}
|
|
198
|
-
catch (error) {
|
|
199
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
200
|
-
ctx.logger.error(`检查推送记录失败:${errorMessage}`);
|
|
201
|
-
ctx.logger.error(`错误栈:${error instanceof Error ? error.stack : '无'}`);
|
|
202
|
-
// 发生错误时,默认返回 false,避免阻塞推送流程
|
|
203
|
-
return false;
|
|
204
|
-
}
|
|
205
|
-
}
|
|
206
188
|
async function markUserAsPushed(userId) {
|
|
207
189
|
try {
|
|
208
190
|
ctx.logger.info(`开始标记用户已推送,用户 ID: ${userId}`);
|
|
@@ -257,69 +239,6 @@ function apply(ctx, config) {
|
|
|
257
239
|
throw error;
|
|
258
240
|
}
|
|
259
241
|
}
|
|
260
|
-
async function markGroupAsPushed(groupId, postId, isUpdate) {
|
|
261
|
-
try {
|
|
262
|
-
ctx.logger.info(`开始标记群 ${groupId} 已推送文章 ${postId}`);
|
|
263
|
-
// 1. 获取当前表中的所有记录,检查是否有相同 groupId 的不同 postId 记录
|
|
264
|
-
const allGroupRecords = await ctx.database.get('wordpress_group_pushes', { groupId });
|
|
265
|
-
ctx.logger.info(`群 ${groupId} 已有 ${allGroupRecords.length} 条推送记录`);
|
|
266
|
-
if (allGroupRecords.length > 0) {
|
|
267
|
-
ctx.logger.info(`已有记录示例:${JSON.stringify(allGroupRecords.slice(0, 2))}`);
|
|
268
|
-
}
|
|
269
|
-
// 2. 检查是否已存在该 groupId + postId 的组合
|
|
270
|
-
const existingRecords = await ctx.database.get('wordpress_group_pushes', { groupId, postId });
|
|
271
|
-
ctx.logger.info(`检查结果:群 ${groupId} 已推送文章 ${postId}:${existingRecords.length > 0 ? '是' : '否'}`);
|
|
272
|
-
// 3. 创建新记录,不删除旧记录(除非是同篇文章的更新)
|
|
273
|
-
const newRecord = {
|
|
274
|
-
groupId,
|
|
275
|
-
postId,
|
|
276
|
-
pushedAt: new Date(),
|
|
277
|
-
isUpdate
|
|
278
|
-
};
|
|
279
|
-
ctx.logger.info(`准备创建新记录:${JSON.stringify(newRecord)}`);
|
|
280
|
-
await ctx.database.create('wordpress_group_pushes', newRecord);
|
|
281
|
-
ctx.logger.info(`已创建新记录,群 ${groupId},文章 ${postId}`);
|
|
282
|
-
// 4. 写入后验证,确保记录真正存入数据库
|
|
283
|
-
const verification = await ctx.database.get('wordpress_group_pushes', { groupId, postId });
|
|
284
|
-
if (verification.length > 0) {
|
|
285
|
-
ctx.logger.info(`记录写入验证成功,群 ${groupId},文章 ${postId}`);
|
|
286
|
-
// 验证后再次检查该群的所有记录
|
|
287
|
-
const updatedAllGroupRecords = await ctx.database.get('wordpress_group_pushes', { groupId });
|
|
288
|
-
ctx.logger.info(`群 ${groupId} 现在共有 ${updatedAllGroupRecords.length} 条推送记录`);
|
|
289
|
-
}
|
|
290
|
-
else {
|
|
291
|
-
ctx.logger.error(`记录写入验证失败,群 ${groupId},文章 ${postId}`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
catch (error) {
|
|
295
|
-
// 处理唯一约束冲突错误
|
|
296
|
-
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
297
|
-
ctx.logger.error(`标记推送记录失败:${errorMessage}`);
|
|
298
|
-
ctx.logger.error(`错误栈:${error instanceof Error ? error.stack : '无'}`);
|
|
299
|
-
ctx.logger.error(`插入参数:groupId=${groupId}, postId=${postId}, isUpdate=${isUpdate}`);
|
|
300
|
-
// 详细记录错误信息,便于诊断
|
|
301
|
-
if (errorMessage.includes('UNIQUE constraint failed')) {
|
|
302
|
-
ctx.logger.error(`唯一约束冲突,可能的原因:`);
|
|
303
|
-
ctx.logger.error(`1. 数据库表的 unique 约束设置错误,可能只针对 groupId 而不是 groupId+postId 组合`);
|
|
304
|
-
ctx.logger.error(`2. 同一篇文章被多次推送`);
|
|
305
|
-
ctx.logger.error(`3. 数据库表结构与模型定义不符`);
|
|
306
|
-
// 尝试获取表结构信息(如果可能)
|
|
307
|
-
try {
|
|
308
|
-
const allRecords = await ctx.database.get('wordpress_group_pushes', {});
|
|
309
|
-
ctx.logger.error(`当前表中共有 ${allRecords.length} 条记录`);
|
|
310
|
-
if (allRecords.length > 0) {
|
|
311
|
-
ctx.logger.error(`前 3 条记录:${JSON.stringify(allRecords.slice(0, 3))}`);
|
|
312
|
-
}
|
|
313
|
-
}
|
|
314
|
-
catch (e) {
|
|
315
|
-
ctx.logger.error(`获取表记录失败:${e}`);
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
else {
|
|
319
|
-
ctx.logger.error(`非约束冲突错误,插件将继续运行`);
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
}
|
|
323
242
|
function formatPostMessage(post, mention = false, isUpdate = false) {
|
|
324
243
|
// 彻底过滤 HTML 标签和非法字符,只保留安全文本
|
|
325
244
|
const sanitizeText = (text) => {
|
|
@@ -339,7 +258,7 @@ function apply(ctx, config) {
|
|
|
339
258
|
}
|
|
340
259
|
// 合并为单段文本,提升适配器兼容性
|
|
341
260
|
const messageType = isUpdate ? '📝 文章更新' : '📝 新文章';
|
|
342
|
-
const messageDate = isUpdate ? `📅 发布: ${date}\n
|
|
261
|
+
const messageDate = isUpdate ? `📅 发布: ${date}\n🔄 更新: ${modifiedDate}` : `📅 ${date}`;
|
|
343
262
|
const message = `${messageType}\n${messageDate}\n📄 ${excerpt}...\n🔗 ${post.link}`;
|
|
344
263
|
segments.push(koishi_1.h.text(message));
|
|
345
264
|
return segments;
|
|
@@ -448,31 +367,33 @@ function apply(ctx, config) {
|
|
|
448
367
|
for (const post of posts) {
|
|
449
368
|
ctx.logger.info(`正在处理文章: ${post.id} - ${post.title.rendered}`);
|
|
450
369
|
ctx.logger.info(`文章 ID: ${post.id}, 发布时间: ${post.date}, 修改时间: ${post.modified}`);
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
370
|
+
// 检查文章是否已推送过(所有群聊共用一个标记)
|
|
371
|
+
const postRecord = await getPostUpdateRecord(post.id);
|
|
372
|
+
const hasPushed = !!postRecord;
|
|
373
|
+
ctx.logger.info(`检查结果: 文章 ${post.id} 是否已推送:${hasPushed ? '是' : '否'}`);
|
|
374
|
+
if (!hasPushed) {
|
|
375
|
+
// 推送到所有目标群聊
|
|
376
|
+
for (const target of config.targets) {
|
|
377
|
+
try {
|
|
378
|
+
ctx.logger.info(`正在处理目标: ${target}`);
|
|
379
|
+
// 直接使用原始目标字符串,不进行数字转换,避免丢失平台前缀等信息
|
|
380
|
+
const stringTarget = target;
|
|
460
381
|
const segments = formatPostMessage(post, true, false);
|
|
461
382
|
ctx.logger.info(`准备推送新文章到目标: ${stringTarget}`);
|
|
462
383
|
await bot.sendMessage(stringTarget, segments);
|
|
463
384
|
ctx.logger.info(`已推送新文章到 ${stringTarget}: ${post.title.rendered}`);
|
|
464
|
-
// 标记该群已推送此文章
|
|
465
|
-
await markGroupAsPushed(stringTarget, post.id, false);
|
|
466
|
-
ctx.logger.info(`已标记群 ${stringTarget} 已推送文章 ${post.id}`);
|
|
467
385
|
}
|
|
468
|
-
|
|
469
|
-
ctx.logger.
|
|
386
|
+
catch (error) {
|
|
387
|
+
ctx.logger.error(`推送新文章到 ${target} 失败: ${error}`);
|
|
388
|
+
ctx.logger.error(`错误详情: ${JSON.stringify(error)}`);
|
|
470
389
|
}
|
|
471
390
|
}
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
391
|
+
// 标记文章已推送(所有群聊共用一个标记)
|
|
392
|
+
await updatePostUpdateRecord(post.id, new Date(post.modified));
|
|
393
|
+
ctx.logger.info(`已标记文章 ${post.id} 为已推送,所有群聊将不再推送此文章`);
|
|
394
|
+
}
|
|
395
|
+
else {
|
|
396
|
+
ctx.logger.info(`跳过推送: 文章 ${post.id} 已推送过,所有群聊将不再推送`);
|
|
476
397
|
}
|
|
477
398
|
}
|
|
478
399
|
}
|
|
@@ -485,29 +406,26 @@ function apply(ctx, config) {
|
|
|
485
406
|
const updateRecord = await getPostUpdateRecord(post.id);
|
|
486
407
|
const postModifiedDate = new Date(post.modified);
|
|
487
408
|
// 检查文章是否有更新
|
|
488
|
-
if (
|
|
409
|
+
if (updateRecord && postModifiedDate > new Date(updateRecord.lastModified)) {
|
|
410
|
+
ctx.logger.info(`文章 ${post.id} 有更新,准备推送更新通知`);
|
|
411
|
+
// 推送到所有目标群聊
|
|
489
412
|
for (const target of config.targets) {
|
|
490
413
|
try {
|
|
491
414
|
ctx.logger.info(`正在处理目标: ${target}`);
|
|
492
|
-
// 直接使用原始目标字符串,与新文章推送逻辑保持一致
|
|
493
415
|
const stringTarget = target;
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
await bot.sendMessage(stringTarget, segments);
|
|
499
|
-
ctx.logger.info(`已推送文章更新到 ${stringTarget}: ${post.title.rendered}`);
|
|
500
|
-
// 更新该群推送记录
|
|
501
|
-
await markGroupAsPushed(stringTarget, post.id, true);
|
|
502
|
-
}
|
|
416
|
+
const segments = formatPostMessage(post, true, true);
|
|
417
|
+
ctx.logger.info(`准备推送文章更新到目标: ${stringTarget}`);
|
|
418
|
+
await bot.sendMessage(stringTarget, segments);
|
|
419
|
+
ctx.logger.info(`已推送文章更新到 ${stringTarget}: ${post.title.rendered}`);
|
|
503
420
|
}
|
|
504
421
|
catch (error) {
|
|
505
422
|
ctx.logger.error(`推送文章更新到 ${target} 失败: ${error}`);
|
|
506
423
|
ctx.logger.error(`错误详情: ${JSON.stringify(error)}`);
|
|
507
424
|
}
|
|
508
425
|
}
|
|
509
|
-
//
|
|
426
|
+
// 更新文章更新记录(所有群聊共用一个标记)
|
|
510
427
|
await updatePostUpdateRecord(post.id, postModifiedDate);
|
|
428
|
+
ctx.logger.info(`已更新文章 ${post.id} 的推送记录,所有群聊将使用此更新时间作为新的推送基准`);
|
|
511
429
|
}
|
|
512
430
|
}
|
|
513
431
|
}
|
|
@@ -689,8 +607,8 @@ ${targetText}
|
|
|
689
607
|
ctx.command('wordpress.pushed', '查看已推送的文章列表')
|
|
690
608
|
.action(async () => {
|
|
691
609
|
ctx.logger.info('命令 wordpress.pushed 被调用');
|
|
692
|
-
// 获取已推送的文章记录,使用
|
|
693
|
-
const records = await ctx.database.get('
|
|
610
|
+
// 获取已推送的文章记录,使用 wordpress_post_updates 表
|
|
611
|
+
const records = await ctx.database.get('wordpress_post_updates', {}, {
|
|
694
612
|
sort: {
|
|
695
613
|
pushedAt: 'desc'
|
|
696
614
|
}
|
|
@@ -700,7 +618,7 @@ ${targetText}
|
|
|
700
618
|
}
|
|
701
619
|
let message = '📋 已推送文章列表(按时间倒序):\n\n';
|
|
702
620
|
for (const record of records) {
|
|
703
|
-
message += `${record.id}. 文章 ID: ${record.postId}
|
|
621
|
+
message += `${record.id}. 文章 ID: ${record.postId}\n`;
|
|
704
622
|
message += `📅 推送时间: ${new Date(record.pushedAt).toLocaleString('zh-CN')}\n\n`;
|
|
705
623
|
}
|
|
706
624
|
return message;
|
|
@@ -731,7 +649,6 @@ ${targetText}
|
|
|
731
649
|
// 获取所有记录
|
|
732
650
|
const allUpdateRecords = await ctx.database.get('wordpress_post_updates', {});
|
|
733
651
|
const allUserRecords = await ctx.database.get('wordpress_user_registrations', {});
|
|
734
|
-
const allGroupRecords = await ctx.database.get('wordpress_group_pushes', {});
|
|
735
652
|
// 筛选需要删除的记录
|
|
736
653
|
const updateRecordsToRemove = allUpdateRecords.filter(record => {
|
|
737
654
|
return new Date(record.pushedAt) < cutoffDate;
|
|
@@ -739,9 +656,6 @@ ${targetText}
|
|
|
739
656
|
const userRecordsToRemove = allUserRecords.filter(record => {
|
|
740
657
|
return new Date(record.pushedAt) < cutoffDate;
|
|
741
658
|
});
|
|
742
|
-
const groupRecordsToRemove = allGroupRecords.filter(record => {
|
|
743
|
-
return new Date(record.pushedAt) < cutoffDate;
|
|
744
|
-
});
|
|
745
659
|
// 删除旧记录
|
|
746
660
|
let result = 0;
|
|
747
661
|
for (const record of updateRecordsToRemove) {
|
|
@@ -752,10 +666,6 @@ ${targetText}
|
|
|
752
666
|
await ctx.database.remove('wordpress_user_registrations', { id: record.id });
|
|
753
667
|
result++;
|
|
754
668
|
}
|
|
755
|
-
for (const record of groupRecordsToRemove) {
|
|
756
|
-
await ctx.database.remove('wordpress_group_pushes', { id: record.id });
|
|
757
|
-
result++;
|
|
758
|
-
}
|
|
759
669
|
ctx.logger.info(`已清理 ${result} 条 ${daysToKeep} 天前的推送记录`);
|
|
760
670
|
return `已清理 ${result} 条 ${daysToKeep} 天前的推送记录`;
|
|
761
671
|
});
|
|
@@ -778,10 +688,6 @@ ${targetText}
|
|
|
778
688
|
|
|
779
689
|
💡 提示:所有命令都需要加 / 前缀`;
|
|
780
690
|
});
|
|
781
|
-
ctx.on('ready', async () => {
|
|
782
|
-
ctx.logger.info('WordPress 推送插件已就绪');
|
|
783
|
-
await pushNewPosts();
|
|
784
|
-
});
|
|
785
691
|
ctx.setInterval(() => {
|
|
786
692
|
pushNewPosts();
|
|
787
693
|
}, config.interval);
|