koishi-plugin-wordpress-notifier 1.9.0 → 2.0.0

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.
Files changed (3) hide show
  1. package/lib/index.d.ts +32 -12
  2. package/lib/index.js +238 -98
  3. package/package.json +1 -1
package/lib/index.d.ts CHANGED
@@ -4,6 +4,9 @@ export declare const name = "wordpress-notifier";
4
4
  declare module 'koishi' {
5
5
  interface Tables {
6
6
  wordpress_posts: WordPressPostRecord;
7
+ wordpress_post_updates: WordPressPostUpdateRecord;
8
+ wordpress_user_registrations: WordPressUserRegistrationRecord;
9
+ wordpress_group_pushes: WordPressGroupPushRecord;
7
10
  }
8
11
  }
9
12
  export interface Config {
@@ -11,6 +14,8 @@ export interface Config {
11
14
  interval: number;
12
15
  targets: string[];
13
16
  enableAutoPush: boolean;
17
+ enableUpdatePush: boolean;
18
+ enableUserPush: boolean;
14
19
  mentionAll: boolean;
15
20
  maxArticles: number;
16
21
  }
@@ -21,6 +26,7 @@ export interface WordPressPost {
21
26
  };
22
27
  link: string;
23
28
  date: string;
29
+ modified: string;
24
30
  excerpt: {
25
31
  rendered: string;
26
32
  };
@@ -31,23 +37,37 @@ export interface WordPressPost {
31
37
  export interface WordPressUser {
32
38
  id: number;
33
39
  name: string;
34
- description: string;
35
- link: string;
36
- avatar_urls: {
37
- '24': string;
38
- '48': string;
39
- '96': string;
40
- };
41
- registered_date?: string;
42
- roles?: string[];
43
- slug?: string;
44
- url?: string;
45
- meta?: any[];
40
+ slug: string;
41
+ date: string;
42
+ email: string;
43
+ roles: string[];
46
44
  }
47
45
  export interface WordPressPostRecord {
48
46
  id: number;
49
47
  postId: number;
50
48
  pushedAt: Date;
51
49
  }
50
+ export interface WordPressPostUpdateRecord {
51
+ id: number;
52
+ postId: number;
53
+ lastModified: Date;
54
+ pushedAt: Date;
55
+ }
56
+ export interface WordPressUserRegistrationRecord {
57
+ id: number;
58
+ userId: number;
59
+ pushedAt: Date;
60
+ }
61
+ export interface WordPressGroupPushRecord {
62
+ id: number;
63
+ groupId: string;
64
+ postId: number;
65
+ pushedAt: Date;
66
+ isUpdate: boolean;
67
+ }
68
+ export interface WordPressNotification {
69
+ type: 'post' | 'update' | 'user';
70
+ data: any;
71
+ }
52
72
  export declare const Config: Schema<Config>;
53
73
  export declare function apply(ctx: Context, config: Config): void;
package/lib/index.js CHANGED
@@ -10,6 +10,8 @@ exports.Config = koishi_1.Schema.object({
10
10
  interval: koishi_1.Schema.number().default(3600000).description('检查间隔(毫秒,默认 1 小时)'),
11
11
  targets: koishi_1.Schema.array(koishi_1.Schema.string()).description('推送目标(群号或 QQ 号)'),
12
12
  enableAutoPush: koishi_1.Schema.boolean().default(true).description('是否启用自动推送'),
13
+ enableUpdatePush: koishi_1.Schema.boolean().default(false).description('是否启用文章更新推送'),
14
+ enableUserPush: koishi_1.Schema.boolean().default(false).description('是否启用新用户注册推送'),
13
15
  mentionAll: koishi_1.Schema.boolean().default(false).description('是否 @全体成员'),
14
16
  maxArticles: koishi_1.Schema.number().default(5).description('每次最多推送的文章数量')
15
17
  });
@@ -23,6 +25,36 @@ function apply(ctx, config) {
23
25
  primary: ['id'],
24
26
  autoInc: true
25
27
  });
28
+ ctx.model.extend('wordpress_post_updates', {
29
+ id: 'integer',
30
+ postId: 'integer',
31
+ lastModified: 'timestamp',
32
+ pushedAt: 'timestamp'
33
+ }, {
34
+ primary: ['id'],
35
+ autoInc: true,
36
+ unique: ['postId']
37
+ });
38
+ ctx.model.extend('wordpress_user_registrations', {
39
+ id: 'integer',
40
+ userId: 'integer',
41
+ pushedAt: 'timestamp'
42
+ }, {
43
+ primary: ['id'],
44
+ autoInc: true,
45
+ unique: ['userId']
46
+ });
47
+ ctx.model.extend('wordpress_group_pushes', {
48
+ id: 'integer',
49
+ groupId: 'string',
50
+ postId: 'integer',
51
+ pushedAt: 'timestamp',
52
+ isUpdate: 'boolean'
53
+ }, {
54
+ primary: ['id'],
55
+ autoInc: true,
56
+ unique: ['groupId', 'postId']
57
+ });
26
58
  async function fetchLatestPosts() {
27
59
  try {
28
60
  const url = `${config.wordpressUrl}/wp-json/wp/v2/posts?per_page=${config.maxArticles}&orderby=date&order=desc`;
@@ -36,43 +68,86 @@ function apply(ctx, config) {
36
68
  return [];
37
69
  }
38
70
  }
39
- async function fetchUsers() {
71
+ async function fetchLatestUsers() {
40
72
  try {
41
- // 添加 per_page=100 以获取更多用户
42
- const url = `${config.wordpressUrl}/wp-json/wp/v2/users?per_page=100`;
43
- ctx.logger.info(`正在获取用户信息: ${url}`);
73
+ const url = `${config.wordpressUrl}/wp-json/wp/v2/users?per_page=${config.maxArticles}&orderby=registered_date&order=desc`;
74
+ ctx.logger.info(`正在获取用户: ${url}`);
44
75
  const response = await ctx.http.get(url);
45
- ctx.logger.info(`成功获取 ${response.length} 个用户`);
76
+ ctx.logger.info(`成功获取 ${response.length} 位用户`);
46
77
  return response;
47
78
  }
48
79
  catch (error) {
49
- ctx.logger.error(`获取 WordPress 用户信息失败: ${error}`);
80
+ ctx.logger.error(`获取 WordPress 用户失败: ${error}`);
50
81
  return [];
51
82
  }
52
83
  }
53
- async function fetchUserById(userId) {
84
+ async function fetchUpdatedPosts() {
54
85
  try {
55
- const url = `${config.wordpressUrl}/wp-json/wp/v2/users/${userId}`;
56
- ctx.logger.info(`正在获取用户信息: ${url}`);
86
+ const url = `${config.wordpressUrl}/wp-json/wp/v2/posts?per_page=${config.maxArticles}&orderby=modified&order=desc`;
87
+ ctx.logger.info(`正在获取更新文章: ${url}`);
57
88
  const response = await ctx.http.get(url);
89
+ ctx.logger.info(`成功获取 ${response.length} 篇更新文章`);
58
90
  return response;
59
91
  }
60
92
  catch (error) {
61
- ctx.logger.error(`获取 WordPress 用户信息失败: ${error}`);
62
- return null;
93
+ ctx.logger.error(`获取 WordPress 更新文章失败: ${error}`);
94
+ return [];
63
95
  }
64
96
  }
65
97
  async function isPostPushed(postId) {
66
98
  const record = await ctx.database.get('wordpress_posts', { postId });
67
99
  return record.length > 0;
68
100
  }
101
+ async function isUserPushed(userId) {
102
+ const record = await ctx.database.get('wordpress_user_registrations', { userId });
103
+ return record.length > 0;
104
+ }
105
+ async function getPostUpdateRecord(postId) {
106
+ const records = await ctx.database.get('wordpress_post_updates', { postId });
107
+ return records.length > 0 ? records[0] : null;
108
+ }
109
+ async function isGroupPushed(groupId, postId) {
110
+ const record = await ctx.database.get('wordpress_group_pushes', { groupId, postId });
111
+ return record.length > 0;
112
+ }
69
113
  async function markPostAsPushed(postId) {
70
114
  await ctx.database.create('wordpress_posts', {
71
115
  postId,
72
116
  pushedAt: new Date()
73
117
  });
74
118
  }
75
- function formatPostMessage(post, mention = false) {
119
+ async function markUserAsPushed(userId) {
120
+ await ctx.database.create('wordpress_user_registrations', {
121
+ userId,
122
+ pushedAt: new Date()
123
+ });
124
+ }
125
+ async function updatePostUpdateRecord(postId, modifiedDate) {
126
+ const record = await getPostUpdateRecord(postId);
127
+ if (record) {
128
+ // Koishi database API 不支持 update 方法,使用 remove + create 代替
129
+ await ctx.database.remove('wordpress_post_updates', { postId });
130
+ }
131
+ await ctx.database.create('wordpress_post_updates', {
132
+ postId,
133
+ lastModified: modifiedDate,
134
+ pushedAt: new Date()
135
+ });
136
+ }
137
+ async function markGroupAsPushed(groupId, postId, isUpdate) {
138
+ const record = await ctx.database.get('wordpress_group_pushes', { groupId, postId });
139
+ if (record) {
140
+ // Koishi database API 不支持 update 方法,使用 remove + create 代替
141
+ await ctx.database.remove('wordpress_group_pushes', { groupId, postId });
142
+ }
143
+ await ctx.database.create('wordpress_group_pushes', {
144
+ groupId,
145
+ postId,
146
+ pushedAt: new Date(),
147
+ isUpdate
148
+ });
149
+ }
150
+ function formatPostMessage(post, mention = false, isUpdate = false) {
76
151
  // 彻底过滤 HTML 标签和非法字符,只保留安全文本
77
152
  const sanitizeText = (text) => {
78
153
  return text
@@ -84,25 +159,39 @@ function apply(ctx, config) {
84
159
  const title = sanitizeText(post.title.rendered);
85
160
  const excerpt = sanitizeText(post.excerpt.rendered).substring(0, 100);
86
161
  const date = new Date(post.date).toLocaleString('zh-CN');
162
+ const modifiedDate = new Date(post.modified).toLocaleString('zh-CN');
87
163
  const segments = [];
88
164
  if (mention && config.mentionAll) {
89
165
  segments.push(koishi_1.h.at('all'));
90
166
  }
91
167
  // 合并为单段文本,提升适配器兼容性
92
- const message = `📝 ${title}\n📅 ${date}\n📄 ${excerpt}...\n🔗 ${post.link}`;
168
+ const messageType = isUpdate ? '📝 文章更新' : '📝 新文章';
169
+ const messageDate = isUpdate ? `📅 发布: ${date}\n� 更新: ${modifiedDate}` : `� ${date}`;
170
+ const message = `${messageType}\n${messageDate}\n📄 ${excerpt}...\n🔗 ${post.link}`;
93
171
  segments.push(koishi_1.h.text(message));
94
172
  return segments;
95
173
  }
96
- async function pushNewPosts() {
97
- if (!config.enableAutoPush) {
98
- ctx.logger.info('自动推送已关闭,跳过推送');
99
- return;
100
- }
101
- const posts = await fetchLatestPosts();
102
- if (posts.length === 0) {
103
- ctx.logger.info('没有获取到新文章,跳过推送');
104
- return;
174
+ function formatUserMessage(user, mention = false) {
175
+ // 彻底过滤 HTML 标签和非法字符,只保留安全文本
176
+ const sanitizeText = (text) => {
177
+ return text
178
+ .replace(/<[^>]*>/g, '') // 移除所有 HTML 标签
179
+ .replace(/[\x00-\x1F\x7F]/g, '') // 移除控制字符
180
+ .replace(/[\s\r\n]+/g, ' ') // 标准化空白字符
181
+ .trim();
182
+ };
183
+ const username = sanitizeText(user.name);
184
+ const registerDate = new Date(user.date).toLocaleString('zh-CN');
185
+ const segments = [];
186
+ if (mention && config.mentionAll) {
187
+ segments.push(koishi_1.h.at('all'));
105
188
  }
189
+ // 合并为单段文本,提升适配器兼容性
190
+ const message = `👤 新用户注册\n📛 用户名: ${username}\n📅 注册时间: ${registerDate}`;
191
+ segments.push(koishi_1.h.text(message));
192
+ return segments;
193
+ }
194
+ async function pushNewPosts() {
106
195
  // 健壮获取 QQ Bot 实例,兼容多种适配器
107
196
  const getValidBot = () => {
108
197
  // 支持的 QQ 相关适配器列表
@@ -124,30 +213,113 @@ function apply(ctx, config) {
124
213
  return;
125
214
  }
126
215
  ctx.logger.info(`使用 bot ${bot.platform}:${bot.selfId} 进行推送`);
127
- for (const post of posts) {
128
- if (!(await isPostPushed(post.id))) {
129
- const segments = formatPostMessage(post, true);
130
- for (const target of config.targets) {
131
- try {
132
- // 验证目标格式,确保是有效的数字字符串
133
- const numericTarget = Number(target);
134
- if (isNaN(numericTarget)) {
135
- ctx.logger.error(`无效的目标 ${target},必须是数字类型`);
136
- continue;
216
+ // 推送新文章
217
+ if (config.enableAutoPush) {
218
+ const posts = await fetchLatestPosts();
219
+ if (posts.length > 0) {
220
+ for (const post of posts) {
221
+ for (const target of config.targets) {
222
+ try {
223
+ // 验证目标格式,确保是有效的数字字符串
224
+ const numericTarget = Number(target);
225
+ if (isNaN(numericTarget)) {
226
+ ctx.logger.error(`无效的目标 ${target},必须是数字类型`);
227
+ continue;
228
+ }
229
+ // 保持字符串类型,但确保内容是有效的数字格式
230
+ const stringTarget = numericTarget.toString();
231
+ // 检查该群是否已推送过此文章
232
+ if (!(await isGroupPushed(stringTarget, post.id))) {
233
+ const segments = formatPostMessage(post, true, false);
234
+ ctx.logger.info(`准备推送新文章到目标: ${stringTarget}`);
235
+ await bot.sendMessage(stringTarget, segments);
236
+ ctx.logger.info(`已推送新文章到 ${stringTarget}: ${post.title.rendered}`);
237
+ // 标记该群已推送此文章
238
+ await markGroupAsPushed(stringTarget, post.id, false);
239
+ }
137
240
  }
138
- // 保持字符串类型,但确保内容是有效的数字格式
139
- const stringTarget = numericTarget.toString();
140
- ctx.logger.info(`准备推送文章到目标: ${stringTarget}`);
141
- // 使用标准 Segment 构造兼容消息,支持多种适配器
142
- await bot.sendMessage(stringTarget, segments);
143
- ctx.logger.info(`已推送文章到 ${stringTarget}: ${post.title.rendered}`);
241
+ catch (error) {
242
+ ctx.logger.error(`推送新文章到 ${target} 失败: ${error}`);
243
+ ctx.logger.error(`错误详情: ${JSON.stringify(error)}`);
244
+ }
245
+ }
246
+ // 标记文章已推送(全局记录)
247
+ if (!(await isPostPushed(post.id))) {
248
+ await markPostAsPushed(post.id);
144
249
  }
145
- catch (error) {
146
- ctx.logger.error(`推送文章到 ${target} 失败: ${error}`);
147
- ctx.logger.error(`错误详情: ${JSON.stringify(error)}`);
250
+ }
251
+ }
252
+ }
253
+ // 推送文章更新
254
+ if (config.enableUpdatePush) {
255
+ const posts = await fetchUpdatedPosts();
256
+ if (posts.length > 0) {
257
+ for (const post of posts) {
258
+ const updateRecord = await getPostUpdateRecord(post.id);
259
+ const postModifiedDate = new Date(post.modified);
260
+ // 检查文章是否有更新
261
+ if (!updateRecord || postModifiedDate > new Date(updateRecord.lastModified)) {
262
+ for (const target of config.targets) {
263
+ try {
264
+ // 验证目标格式,确保是有效的数字字符串
265
+ const numericTarget = Number(target);
266
+ if (isNaN(numericTarget)) {
267
+ ctx.logger.error(`无效的目标 ${target},必须是数字类型`);
268
+ continue;
269
+ }
270
+ // 保持字符串类型,但确保内容是有效的数字格式
271
+ const stringTarget = numericTarget.toString();
272
+ // 检查该群是否已推送过此文章
273
+ if (await isGroupPushed(stringTarget, post.id)) {
274
+ const segments = formatPostMessage(post, true, true);
275
+ ctx.logger.info(`准备推送文章更新到目标: ${stringTarget}`);
276
+ await bot.sendMessage(stringTarget, segments);
277
+ ctx.logger.info(`已推送文章更新到 ${stringTarget}: ${post.title.rendered}`);
278
+ // 更新该群推送记录
279
+ await markGroupAsPushed(stringTarget, post.id, true);
280
+ }
281
+ }
282
+ catch (error) {
283
+ ctx.logger.error(`推送文章更新到 ${target} 失败: ${error}`);
284
+ ctx.logger.error(`错误详情: ${JSON.stringify(error)}`);
285
+ }
286
+ }
287
+ // 更新文章更新记录
288
+ await updatePostUpdateRecord(post.id, postModifiedDate);
289
+ }
290
+ }
291
+ }
292
+ }
293
+ // 推送新用户注册
294
+ if (config.enableUserPush) {
295
+ const users = await fetchLatestUsers();
296
+ if (users.length > 0) {
297
+ for (const user of users) {
298
+ if (!(await isUserPushed(user.id))) {
299
+ for (const target of config.targets) {
300
+ try {
301
+ // 验证目标格式,确保是有效的数字字符串
302
+ const numericTarget = Number(target);
303
+ if (isNaN(numericTarget)) {
304
+ ctx.logger.error(`无效的目标 ${target},必须是数字类型`);
305
+ continue;
306
+ }
307
+ // 保持字符串类型,但确保内容是有效的数字格式
308
+ const stringTarget = numericTarget.toString();
309
+ const segments = formatUserMessage(user, true);
310
+ ctx.logger.info(`准备推送新用户到目标: ${stringTarget}`);
311
+ await bot.sendMessage(stringTarget, segments);
312
+ ctx.logger.info(`已推送新用户到 ${stringTarget}: ${user.name}`);
313
+ }
314
+ catch (error) {
315
+ ctx.logger.error(`推送新用户到 ${target} 失败: ${error}`);
316
+ ctx.logger.error(`错误详情: ${JSON.stringify(error)}`);
317
+ }
318
+ }
319
+ // 标记用户已推送
320
+ await markUserAsPushed(user.id);
148
321
  }
149
322
  }
150
- await markPostAsPushed(post.id);
151
323
  }
152
324
  }
153
325
  }
@@ -182,56 +354,6 @@ function apply(ctx, config) {
182
354
  }
183
355
  return message;
184
356
  });
185
- ctx.command('wordpress.users', '查看站点用户列表')
186
- .action(async () => {
187
- ctx.logger.info('命令 wordpress.users 被调用');
188
- try {
189
- const users = await fetchUsers();
190
- if (users.length === 0) {
191
- return '暂无用户信息';
192
- }
193
- let message = '👥 WordPress 站点用户列表:\n\n';
194
- for (const user of users) {
195
- const roles = user.roles || [];
196
- message += `${user.id}. ${user.name}(${roles.join(', ') || '普通用户'})\n`;
197
- message += `🔗 ${user.link}\n\n`;
198
- }
199
- return message;
200
- }
201
- catch (error) {
202
- ctx.logger.error(`获取用户列表失败: ${error}`);
203
- return '获取用户列表失败';
204
- }
205
- });
206
- ctx.command('wordpress.user <id>', '查看特定用户信息')
207
- .action(async ({}, userId) => {
208
- ctx.logger.info(`命令 wordpress.user 被调用,用户 ID:${userId}`);
209
- const id = parseInt(userId);
210
- if (isNaN(id)) {
211
- return '请输入有效的用户 ID';
212
- }
213
- try {
214
- const user = await fetchUserById(id);
215
- if (!user) {
216
- return `未找到 ID 为 ${id} 的用户`;
217
- }
218
- let message = `👤 用户信息:\n\n`;
219
- message += `ID: ${user.id}\n`;
220
- message += `昵称: ${user.name}\n`;
221
- message += `个人主页: ${user.link}\n`;
222
- if (user.description) {
223
- message += `简介: ${user.description.replace(/<[^>]*>/g, '')}\n`;
224
- }
225
- if (user.registered_date) {
226
- message += `注册时间: ${new Date(user.registered_date).toLocaleString('zh-CN')}\n`;
227
- }
228
- return message;
229
- }
230
- catch (error) {
231
- ctx.logger.error(`获取用户信息失败: ${error}`);
232
- return '获取用户信息失败';
233
- }
234
- });
235
357
  ctx.command('wordpress.push', '手动推送最新文章')
236
358
  .action(async () => {
237
359
  ctx.logger.info('命令 wordpress.push 被调用');
@@ -239,24 +361,42 @@ function apply(ctx, config) {
239
361
  return '已检查并推送最新文章';
240
362
  });
241
363
  ctx.command('wordpress.status', '查看插件状态')
242
- .action(() => {
364
+ .action(({ session }) => {
243
365
  ctx.logger.info('命令 wordpress.status 被调用');
366
+ // 获取当前群号,如果有的话
367
+ const currentGroup = session?.channelId || '未知群聊';
368
+ // 推送目标仅显示本群
369
+ const targetText = `🎯 推送目标: ${currentGroup}`;
244
370
  return `📊 WordPress 推送插件状态:
245
371
  🌐 网站地址: ${config.wordpressUrl}
246
372
  ⏰ 检查间隔: ${config.interval / 1000} 秒
247
- 🎯 推送目标: ${config.targets.join(', ')}
373
+ ${targetText}
248
374
  🔔 自动推送: ${config.enableAutoPush ? '开启' : '关闭'}
375
+ 🔄 更新推送: ${config.enableUpdatePush ? '开启' : '关闭'}
376
+ 👤 用户推送: ${config.enableUserPush ? '开启' : '关闭'}
249
377
  📢 @全体成员: ${config.mentionAll ? '开启' : '关闭'}
250
378
  📝 最多推送: ${config.maxArticles} 篇`;
251
379
  });
380
+ ctx.command('wordpress.toggle-update', '切换文章更新推送开关')
381
+ .action(async ({ session }) => {
382
+ ctx.logger.info('命令 wordpress.toggle-update 被调用');
383
+ config.enableUpdatePush = !config.enableUpdatePush;
384
+ return `文章更新推送已${config.enableUpdatePush ? '开启' : '关闭'}`;
385
+ });
386
+ ctx.command('wordpress.toggle-user', '切换新用户注册推送开关')
387
+ .action(async ({ session }) => {
388
+ ctx.logger.info('命令 wordpress.toggle-user 被调用');
389
+ config.enableUserPush = !config.enableUserPush;
390
+ return `新用户注册推送已${config.enableUserPush ? '开启' : '关闭'}`;
391
+ });
252
392
  ctx.command('wordpress.toggle', '切换自动推送开关')
253
- .action(async () => {
393
+ .action(async ({ session }) => {
254
394
  ctx.logger.info('命令 wordpress.toggle 被调用');
255
395
  config.enableAutoPush = !config.enableAutoPush;
256
396
  return `自动推送已${config.enableAutoPush ? '开启' : '关闭'}`;
257
397
  });
258
398
  ctx.command('wordpress.mention', '切换 @全体成员 开关')
259
- .action(async () => {
399
+ .action(async ({ session }) => {
260
400
  ctx.logger.info('命令 wordpress.mention 被调用');
261
401
  config.mentionAll = !config.mentionAll;
262
402
  return `@全体成员 已${config.mentionAll ? '开启' : '关闭'}`;
@@ -323,13 +463,13 @@ function apply(ctx, config) {
323
463
  🔹 /wordpress.status - 查看插件状态
324
464
  🔹 /wordpress.latest - 查看最新文章
325
465
  🔹 /wordpress.list - 查看文章列表
326
- 🔹 /wordpress.users - 查看站点用户列表
327
- 🔹 /wordpress.user <id> - 查看特定用户信息
328
466
  🔹 /wordpress.push - 手动推送最新文章
329
467
  🔹 /wordpress.set-url <url> - 修改 WordPress 站点地址
330
468
  🔹 /wordpress.pushed - 查看已推送文章列表
331
469
  🔹 /wordpress.clean [days] - 清理旧推送记录
332
470
  🔹 /wordpress.toggle - 切换自动推送开关
471
+ 🔹 /wordpress.toggle-update - 切换文章更新推送开关
472
+ 🔹 /wordpress.toggle-user - 切换新用户注册推送开关
333
473
  🔹 /wordpress.mention - 切换 @全体成员 开关
334
474
 
335
475
  💡 提示:所有命令都需要加 / 前缀`;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-wordpress-notifier",
3
- "version": "1.9.0",
3
+ "version": "2.0.0",
4
4
  "description": "WordPress 文章自动推送到 QQ",
5
5
  "main": "lib/index.js",
6
6
  "types": "lib/index.d.ts",