koishi-plugin-wordpress-notifier 2.5.4 → 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 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 认证,例如:hGR2sPFuYnclxHc4AvJqcUtB)'),
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,61 +49,18 @@ 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('wordpress_group_pushes: id 字段设置为 autoInc: true,唯一键为 groupId+postId');
66
- ctx.logger.info('插件将确保所有插入操作不手动指定 id 字段,让数据库自动生成');
52
+ ctx.logger.info('所有群聊共用一个文章标记,不再区分群聊');
67
53
  // 检查并修复数据库表结构问题
68
54
  await checkAndFixTableStructure();
55
+ // 执行初始推送
56
+ await pushNewPosts();
69
57
  });
70
- // 检查并修复数据库表结构的函数
58
+ // 检查数据库表结构的函数
71
59
  async function checkAndFixTableStructure() {
72
60
  try {
73
61
  ctx.logger.info('开始检查数据库表结构...');
74
- // 获取表中的所有记录,检查是否存在相同 groupId 不同 postId 的记录
75
- const allRecords = await ctx.database.get('wordpress_group_pushes', {});
76
- ctx.logger.info(`当前 wordpress_group_pushes 表共有 ${allRecords.length} 条记录`);
77
- // 检查是否存在相同 groupId 不同 postId 的记录
78
- const groupPostMap = new Map();
79
- let hasDuplicateGroupId = false;
80
- for (const record of allRecords) {
81
- const groupId = record.groupId;
82
- const postId = record.postId;
83
- if (!groupPostMap.has(groupId)) {
84
- groupPostMap.set(groupId, new Set());
85
- }
86
- const postIds = groupPostMap.get(groupId);
87
- if (postIds.has(postId)) {
88
- ctx.logger.warn(`发现重复记录:groupId=${groupId}, postId=${postId}`);
89
- }
90
- else {
91
- postIds.add(postId);
92
- }
93
- // 如果一个群有多个不同的 postId,说明表结构正确
94
- if (postIds.size > 1) {
95
- hasDuplicateGroupId = true;
96
- }
97
- }
98
- if (hasDuplicateGroupId) {
99
- ctx.logger.info('检测到同一群有多个不同文章记录,表结构正确');
100
- }
101
- else {
102
- ctx.logger.warn('检测到每个群只有一个文章记录,可能存在表结构问题');
103
- ctx.logger.warn('建议检查 wordpress_group_pushes 表的唯一键设置,应设置为 groupId+postId 的联合唯一键');
104
- ctx.logger.warn('当前表结构可能导致一个群只能推送一篇文章');
105
- }
106
- // 尝试通过重新创建表来修复结构(使用 Koishi 内置的表重建机制)
107
- ctx.logger.info('尝试通过 Koishi 模型扩展机制修复表结构...');
108
- // 重新扩展模型,确保唯一键设置正确
109
- ctx.model.extend('wordpress_group_pushes', {
110
- id: 'integer',
111
- groupId: 'string',
112
- postId: 'integer',
113
- pushedAt: 'timestamp',
114
- isUpdate: 'boolean'
115
- }, {
116
- primary: 'id',
117
- autoInc: true,
118
- unique: ['groupId', 'postId']
119
- });
62
+ ctx.logger.info('所有群聊现在共用一个文章标记,不再区分群聊');
63
+ ctx.logger.info('wordpress_group_pushes 表已不再使用,已移除相关功能');
120
64
  ctx.logger.info('表结构检查和修复完成');
121
65
  }
122
66
  catch (error) {
@@ -241,28 +185,6 @@ function apply(ctx, config) {
241
185
  return null;
242
186
  }
243
187
  }
244
- async function isGroupPushed(groupId, postId) {
245
- try {
246
- // 明确查询 groupId + postId 组合是否存在
247
- const query = { groupId, postId };
248
- ctx.logger.info(`检查群 ${groupId} 是否已推送文章 ${postId},查询条件:${JSON.stringify(query)}`);
249
- const records = await ctx.database.get('wordpress_group_pushes', query);
250
- const result = records.length > 0;
251
- ctx.logger.info(`检查群 ${groupId} 是否已推送文章 ${postId}:${result ? '是' : '否'}`);
252
- // 添加调试日志,显示所有相关记录
253
- if (records.length > 0) {
254
- ctx.logger.debug(`找到 ${records.length} 条匹配记录:${JSON.stringify(records)}`);
255
- }
256
- return result;
257
- }
258
- catch (error) {
259
- const errorMessage = error instanceof Error ? error.message : String(error);
260
- ctx.logger.error(`检查推送记录失败:${errorMessage}`);
261
- ctx.logger.error(`错误栈:${error instanceof Error ? error.stack : '无'}`);
262
- // 发生错误时,默认返回 false,避免阻塞推送流程
263
- return false;
264
- }
265
- }
266
188
  async function markUserAsPushed(userId) {
267
189
  try {
268
190
  ctx.logger.info(`开始标记用户已推送,用户 ID: ${userId}`);
@@ -317,69 +239,6 @@ function apply(ctx, config) {
317
239
  throw error;
318
240
  }
319
241
  }
320
- async function markGroupAsPushed(groupId, postId, isUpdate) {
321
- try {
322
- ctx.logger.info(`开始标记群 ${groupId} 已推送文章 ${postId}`);
323
- // 1. 获取当前表中的所有记录,检查是否有相同 groupId 的不同 postId 记录
324
- const allGroupRecords = await ctx.database.get('wordpress_group_pushes', { groupId });
325
- ctx.logger.info(`群 ${groupId} 已有 ${allGroupRecords.length} 条推送记录`);
326
- if (allGroupRecords.length > 0) {
327
- ctx.logger.info(`已有记录示例:${JSON.stringify(allGroupRecords.slice(0, 2))}`);
328
- }
329
- // 2. 检查是否已存在该 groupId + postId 的组合
330
- const existingRecords = await ctx.database.get('wordpress_group_pushes', { groupId, postId });
331
- ctx.logger.info(`检查结果:群 ${groupId} 已推送文章 ${postId}:${existingRecords.length > 0 ? '是' : '否'}`);
332
- // 3. 创建新记录,不删除旧记录(除非是同篇文章的更新)
333
- const newRecord = {
334
- groupId,
335
- postId,
336
- pushedAt: new Date(),
337
- isUpdate
338
- };
339
- ctx.logger.info(`准备创建新记录:${JSON.stringify(newRecord)}`);
340
- await ctx.database.create('wordpress_group_pushes', newRecord);
341
- ctx.logger.info(`已创建新记录,群 ${groupId},文章 ${postId}`);
342
- // 4. 写入后验证,确保记录真正存入数据库
343
- const verification = await ctx.database.get('wordpress_group_pushes', { groupId, postId });
344
- if (verification.length > 0) {
345
- ctx.logger.info(`记录写入验证成功,群 ${groupId},文章 ${postId}`);
346
- // 验证后再次检查该群的所有记录
347
- const updatedAllGroupRecords = await ctx.database.get('wordpress_group_pushes', { groupId });
348
- ctx.logger.info(`群 ${groupId} 现在共有 ${updatedAllGroupRecords.length} 条推送记录`);
349
- }
350
- else {
351
- ctx.logger.error(`记录写入验证失败,群 ${groupId},文章 ${postId}`);
352
- }
353
- }
354
- catch (error) {
355
- // 处理唯一约束冲突错误
356
- const errorMessage = error instanceof Error ? error.message : String(error);
357
- ctx.logger.error(`标记推送记录失败:${errorMessage}`);
358
- ctx.logger.error(`错误栈:${error instanceof Error ? error.stack : '无'}`);
359
- ctx.logger.error(`插入参数:groupId=${groupId}, postId=${postId}, isUpdate=${isUpdate}`);
360
- // 详细记录错误信息,便于诊断
361
- if (errorMessage.includes('UNIQUE constraint failed')) {
362
- ctx.logger.error(`唯一约束冲突,可能的原因:`);
363
- ctx.logger.error(`1. 数据库表的 unique 约束设置错误,可能只针对 groupId 而不是 groupId+postId 组合`);
364
- ctx.logger.error(`2. 同一篇文章被多次推送`);
365
- ctx.logger.error(`3. 数据库表结构与模型定义不符`);
366
- // 尝试获取表结构信息(如果可能)
367
- try {
368
- const allRecords = await ctx.database.get('wordpress_group_pushes', {});
369
- ctx.logger.error(`当前表中共有 ${allRecords.length} 条记录`);
370
- if (allRecords.length > 0) {
371
- ctx.logger.error(`前 3 条记录:${JSON.stringify(allRecords.slice(0, 3))}`);
372
- }
373
- }
374
- catch (e) {
375
- ctx.logger.error(`获取表记录失败:${e}`);
376
- }
377
- }
378
- else {
379
- ctx.logger.error(`非约束冲突错误,插件将继续运行`);
380
- }
381
- }
382
- }
383
242
  function formatPostMessage(post, mention = false, isUpdate = false) {
384
243
  // 彻底过滤 HTML 标签和非法字符,只保留安全文本
385
244
  const sanitizeText = (text) => {
@@ -399,7 +258,7 @@ function apply(ctx, config) {
399
258
  }
400
259
  // 合并为单段文本,提升适配器兼容性
401
260
  const messageType = isUpdate ? '📝 文章更新' : '📝 新文章';
402
- const messageDate = isUpdate ? `📅 发布: ${date}\n 更新: ${modifiedDate}` : `� ${date}`;
261
+ const messageDate = isUpdate ? `📅 发布: ${date}\n🔄 更新: ${modifiedDate}` : `📅 ${date}`;
403
262
  const message = `${messageType}\n${messageDate}\n📄 ${excerpt}...\n🔗 ${post.link}`;
404
263
  segments.push(koishi_1.h.text(message));
405
264
  return segments;
@@ -508,31 +367,33 @@ function apply(ctx, config) {
508
367
  for (const post of posts) {
509
368
  ctx.logger.info(`正在处理文章: ${post.id} - ${post.title.rendered}`);
510
369
  ctx.logger.info(`文章 ID: ${post.id}, 发布时间: ${post.date}, 修改时间: ${post.modified}`);
511
- for (const target of config.targets) {
512
- try {
513
- ctx.logger.info(`正在处理目标: ${target}`);
514
- // 直接使用原始目标字符串,不进行数字转换,避免丢失平台前缀等信息
515
- const stringTarget = target;
516
- // 检查该群是否已推送过此文章
517
- const hasPushed = await isGroupPushed(stringTarget, post.id);
518
- ctx.logger.info(`检查结果: 群 ${stringTarget} 已推送文章 ${post.id}:${hasPushed ? '是' : '否'}`);
519
- if (!hasPushed) {
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;
520
381
  const segments = formatPostMessage(post, true, false);
521
382
  ctx.logger.info(`准备推送新文章到目标: ${stringTarget}`);
522
383
  await bot.sendMessage(stringTarget, segments);
523
384
  ctx.logger.info(`已推送新文章到 ${stringTarget}: ${post.title.rendered}`);
524
- // 标记该群已推送此文章
525
- await markGroupAsPushed(stringTarget, post.id, false);
526
- ctx.logger.info(`已标记群 ${stringTarget} 已推送文章 ${post.id}`);
527
385
  }
528
- else {
529
- ctx.logger.info(`跳过推送: ${stringTarget} 已推送过文章 ${post.id}`);
386
+ catch (error) {
387
+ ctx.logger.error(`推送新文章到 ${target} 失败: ${error}`);
388
+ ctx.logger.error(`错误详情: ${JSON.stringify(error)}`);
530
389
  }
531
390
  }
532
- catch (error) {
533
- ctx.logger.error(`推送新文章到 ${target} 失败: ${error}`);
534
- ctx.logger.error(`错误详情: ${JSON.stringify(error)}`);
535
- }
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} 已推送过,所有群聊将不再推送`);
536
397
  }
537
398
  }
538
399
  }
@@ -545,29 +406,26 @@ function apply(ctx, config) {
545
406
  const updateRecord = await getPostUpdateRecord(post.id);
546
407
  const postModifiedDate = new Date(post.modified);
547
408
  // 检查文章是否有更新
548
- if (!updateRecord || postModifiedDate > new Date(updateRecord.lastModified)) {
409
+ if (updateRecord && postModifiedDate > new Date(updateRecord.lastModified)) {
410
+ ctx.logger.info(`文章 ${post.id} 有更新,准备推送更新通知`);
411
+ // 推送到所有目标群聊
549
412
  for (const target of config.targets) {
550
413
  try {
551
414
  ctx.logger.info(`正在处理目标: ${target}`);
552
- // 直接使用原始目标字符串,与新文章推送逻辑保持一致
553
415
  const stringTarget = target;
554
- // 检查该群是否已推送过此文章
555
- if (await isGroupPushed(stringTarget, post.id)) {
556
- const segments = formatPostMessage(post, true, true);
557
- ctx.logger.info(`准备推送文章更新到目标: ${stringTarget}`);
558
- await bot.sendMessage(stringTarget, segments);
559
- ctx.logger.info(`已推送文章更新到 ${stringTarget}: ${post.title.rendered}`);
560
- // 更新该群推送记录
561
- await markGroupAsPushed(stringTarget, post.id, true);
562
- }
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}`);
563
420
  }
564
421
  catch (error) {
565
422
  ctx.logger.error(`推送文章更新到 ${target} 失败: ${error}`);
566
423
  ctx.logger.error(`错误详情: ${JSON.stringify(error)}`);
567
424
  }
568
425
  }
569
- // 更新文章更新记录
426
+ // 更新文章更新记录(所有群聊共用一个标记)
570
427
  await updatePostUpdateRecord(post.id, postModifiedDate);
428
+ ctx.logger.info(`已更新文章 ${post.id} 的推送记录,所有群聊将使用此更新时间作为新的推送基准`);
571
429
  }
572
430
  }
573
431
  }
@@ -749,8 +607,8 @@ ${targetText}
749
607
  ctx.command('wordpress.pushed', '查看已推送的文章列表')
750
608
  .action(async () => {
751
609
  ctx.logger.info('命令 wordpress.pushed 被调用');
752
- // 获取已推送的文章记录,使用 wordpress_group_pushes
753
- const records = await ctx.database.get('wordpress_group_pushes', {}, {
610
+ // 获取已推送的文章记录,使用 wordpress_post_updates
611
+ const records = await ctx.database.get('wordpress_post_updates', {}, {
754
612
  sort: {
755
613
  pushedAt: 'desc'
756
614
  }
@@ -760,7 +618,7 @@ ${targetText}
760
618
  }
761
619
  let message = '📋 已推送文章列表(按时间倒序):\n\n';
762
620
  for (const record of records) {
763
- message += `${record.id}. 文章 ID: ${record.postId}(群:${record.groupId},${record.isUpdate ? '更新' : '新文章'})\n`;
621
+ message += `${record.id}. 文章 ID: ${record.postId}\n`;
764
622
  message += `📅 推送时间: ${new Date(record.pushedAt).toLocaleString('zh-CN')}\n\n`;
765
623
  }
766
624
  return message;
@@ -791,7 +649,6 @@ ${targetText}
791
649
  // 获取所有记录
792
650
  const allUpdateRecords = await ctx.database.get('wordpress_post_updates', {});
793
651
  const allUserRecords = await ctx.database.get('wordpress_user_registrations', {});
794
- const allGroupRecords = await ctx.database.get('wordpress_group_pushes', {});
795
652
  // 筛选需要删除的记录
796
653
  const updateRecordsToRemove = allUpdateRecords.filter(record => {
797
654
  return new Date(record.pushedAt) < cutoffDate;
@@ -799,9 +656,6 @@ ${targetText}
799
656
  const userRecordsToRemove = allUserRecords.filter(record => {
800
657
  return new Date(record.pushedAt) < cutoffDate;
801
658
  });
802
- const groupRecordsToRemove = allGroupRecords.filter(record => {
803
- return new Date(record.pushedAt) < cutoffDate;
804
- });
805
659
  // 删除旧记录
806
660
  let result = 0;
807
661
  for (const record of updateRecordsToRemove) {
@@ -812,10 +666,6 @@ ${targetText}
812
666
  await ctx.database.remove('wordpress_user_registrations', { id: record.id });
813
667
  result++;
814
668
  }
815
- for (const record of groupRecordsToRemove) {
816
- await ctx.database.remove('wordpress_group_pushes', { id: record.id });
817
- result++;
818
- }
819
669
  ctx.logger.info(`已清理 ${result} 条 ${daysToKeep} 天前的推送记录`);
820
670
  return `已清理 ${result} 条 ${daysToKeep} 天前的推送记录`;
821
671
  });
@@ -838,10 +688,6 @@ ${targetText}
838
688
 
839
689
  💡 提示:所有命令都需要加 / 前缀`;
840
690
  });
841
- ctx.on('ready', async () => {
842
- ctx.logger.info('WordPress 推送插件已就绪');
843
- await pushNewPosts();
844
- });
845
691
  ctx.setInterval(() => {
846
692
  pushNewPosts();
847
693
  }, config.interval);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-wordpress-notifier",
3
- "version": "2.5.4",
3
+ "version": "2.5.5",
4
4
  "description": "WordPress 文章自动推送到 QQ",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",