koishi-plugin-github-webhook-pusher 0.0.5 → 0.0.7

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/parser.d.ts CHANGED
@@ -8,6 +8,10 @@ import { ParsedEvent } from './types';
8
8
  * 需求 4.1: 提取 issue 标题、编号、操作者和链接
9
9
  */
10
10
  export declare function parseIssuesEvent(payload: any): ParsedEvent | null;
11
+ /**
12
+ * 解析 Issue Comment 事件
13
+ */
14
+ export declare function parseIssueCommentEvent(payload: any): ParsedEvent | null;
11
15
  /**
12
16
  * 解析 Release 事件
13
17
  * 需求 4.2: 提取版本号、发布者和下载链接
@@ -23,11 +27,35 @@ export declare function parsePushEvent(payload: any): ParsedEvent | null;
23
27
  * 需求 4.4: 提取 PR 标题、编号、操作者和链接
24
28
  */
25
29
  export declare function parsePullRequestEvent(payload: any): ParsedEvent | null;
30
+ /**
31
+ * 解析 Pull Request Review 事件
32
+ */
33
+ export declare function parsePullRequestReviewEvent(payload: any): ParsedEvent | null;
34
+ /**
35
+ * 解析 Pull Request Review Comment 事件
36
+ */
37
+ export declare function parsePullRequestReviewCommentEvent(payload: any): ParsedEvent | null;
26
38
  /**
27
39
  * 解析 Star 事件
28
40
  * 需求 4.5: 提取操作者和当前 star 数量
29
41
  */
30
42
  export declare function parseStarEvent(payload: any): ParsedEvent | null;
43
+ /**
44
+ * 解析 Fork 事件
45
+ */
46
+ export declare function parseForkEvent(payload: any): ParsedEvent | null;
47
+ /**
48
+ * 解析 Create 事件
49
+ */
50
+ export declare function parseCreateEvent(payload: any): ParsedEvent | null;
51
+ /**
52
+ * 解析 Delete 事件
53
+ */
54
+ export declare function parseDeleteEvent(payload: any): ParsedEvent | null;
55
+ /**
56
+ * 解析 Workflow Run 事件
57
+ */
58
+ export declare function parseWorkflowRunEvent(payload: any): ParsedEvent | null;
31
59
  /**
32
60
  * 统一的事件解析入口函数
33
61
  * @param eventName X-GitHub-Event 头的值
package/lib/types.d.ts CHANGED
@@ -3,7 +3,7 @@
3
3
  * 需求: 4.1-4.7
4
4
  */
5
5
  /** 支持的事件类型 */
6
- export type EventType = 'issues' | 'release' | 'push' | 'pull_request' | 'star';
6
+ export type EventType = 'issues' | 'issue_comment' | 'pull_request' | 'pull_request_review' | 'pull_request_review_comment' | 'release' | 'push' | 'star' | 'fork' | 'create' | 'delete' | 'workflow_run';
7
7
  /** 提交信息 */
8
8
  export interface CommitInfo {
9
9
  sha: string;
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "koishi-plugin-github-webhook-pusher",
3
3
  "description": "GitHub Webhook 事件推送插件",
4
- "version": "0.0.5",
4
+ "version": "0.0.7",
5
5
  "contributors": [
6
6
  "ClozyA <aoxuan233@gmail.com>"
7
7
  ],
@@ -19,7 +19,10 @@
19
19
  "license": "MIT",
20
20
  "scripts": {
21
21
  "test": "vitest --run",
22
- "test:watch": "vitest"
22
+ "test:watch": "vitest",
23
+ "build": "tsc -p tsconfig.json",
24
+ "build:ci": "tsc -p tsconfig.github.json",
25
+ "prepublishOnly": "npm run build:ci"
23
26
  },
24
27
  "keywords": [
25
28
  "chatbot",
package/readme.md CHANGED
@@ -2,14 +2,14 @@
2
2
 
3
3
  [![npm](https://img.shields.io/npm/v/koishi-plugin-github-webhook-pusher?style=flat-square)](https://www.npmjs.com/package/koishi-plugin-github-webhook-pusher)
4
4
 
5
- Koishi 机器人框架的 GitHub Webhook 推送插件,支持将 GitHub 仓库事件(Issues、Release、Push、PR、Star)推送到 QQ 群聊或私聊。
5
+ Koishi 机器人框架的 GitHub Webhook 推送插件,支持将 GitHub 仓库事件推送到 QQ 群聊或私聊。
6
6
 
7
7
  ## 功能特性
8
8
 
9
9
  - 🔐 **安全验证** - 支持 HMAC SHA256 签名验证,确保 Webhook 请求来源可信
10
10
  - 📋 **信任仓库管理** - 管理员可控制哪些仓库的事件可以被处理
11
11
  - 🔔 **灵活订阅** - 用户可自由订阅感兴趣的仓库和事件类型
12
- - 📨 **多事件支持** - 支持 Issues、ReleasePush、Pull Request、Star 等事件
12
+ - 📨 **多事件支持** - 支持 Issues、Issue Comment、Pull Request、Review、Release、Push、Star、Fork 等常用事件
13
13
  - 🚀 **并发推送** - 支持并发推送到多个订阅目标,可配置并发数
14
14
  - 💾 **数据持久化** - 订阅和信任仓库数据持久化存储
15
15
 
@@ -74,10 +74,17 @@ https://your-koishi-server.com/github/webhook
74
74
  - **Secret**: 与插件配置中的 `secret` 保持一致
75
75
  - **Which events would you like to trigger this webhook?**: 选择需要的事件
76
76
  - Issues
77
+ - Issue comments
78
+ - Pull requests
79
+ - Pull request reviews
80
+ - Pull request review comments
77
81
  - Releases
78
82
  - Pushes
79
- - Pull requests
80
83
  - Stars (Watch)
84
+ - Forks
85
+ - Create
86
+ - Delete
87
+ - Workflow runs
81
88
  4. 点击 **Add webhook** 保存
82
89
 
83
90
  ### 3. 验证配置
@@ -108,8 +115,9 @@ https://your-koishi-server.com/github/webhook
108
115
  | `gh.sub <repo>` | 订阅仓库 | `gh.sub koishijs/koishi` |
109
116
  | `gh.unsub <repo>` | 取消订阅 | `gh.unsub koishijs/koishi` |
110
117
  | `gh.list` | 列出当前会话的所有订阅 | `gh.list` |
111
- | `gh.events <repo>` | 查看订阅的事件类型 | `gh.events koishijs/koishi` |
112
- | `gh.events <repo> +/-event` | 修改订阅的事件类型 | `gh.events koishijs/koishi +issues -star` |
118
+ | `gh.events [repo]` | 查看订阅的事件类型(无 repo 时列出所有可用事件) | `gh.events koishijs/koishi` |
119
+ | `gh.on <repo> [...events]` | 快捷启用订阅事件 | `gh.on koishijs/koishi issues pull_request` |
120
+ | `gh.off <repo> [...events]` | 快捷禁用订阅事件 | `gh.off koishijs/koishi issues pull_request` |
113
121
 
114
122
  ### 工具命令
115
123
 
@@ -123,10 +131,17 @@ https://your-koishi-server.com/github/webhook
123
131
  | 事件类型 | 显示名称 | Emoji | 说明 |
124
132
  |----------|----------|-------|------|
125
133
  | `issues` | Issue | 📌 | Issue 的创建、关闭、重新打开、编辑 |
126
- | `release` | Release | 🚀 | 版本发布 |
127
- | `push` | Commit | ⬆️ | 代码推送(最多显示 5 条提交) |
134
+ | `issue_comment` | Issue Comment | 💬 | Issue 评论的创建、编辑、删除 |
128
135
  | `pull_request` | PR | 🔀 | Pull Request 的创建、关闭、合并 |
136
+ | `pull_request_review` | PR Review | 🧪 | PR Review 的提交、编辑、撤销 |
137
+ | `pull_request_review_comment` | PR Review Comment | 💬 | PR Review 评论的创建、编辑、删除 |
138
+ | `release` | Release | 🚀 | 版本发布 |
139
+ | `push` | Commit | ⬆️ | 代码推送(最多显示 3 条提交) |
129
140
  | `star` | Star | ⭐ | Star 操作 |
141
+ | `fork` | Fork | 🍴 | Fork 操作 |
142
+ | `create` | Create | ✨ | 分支/标签创建 |
143
+ | `delete` | Delete | 🗑️ | 分支/标签删除 |
144
+ | `workflow_run` | Workflow | 🧩 | Workflow 运行 |
130
145
 
131
146
  ## 消息格式示例
132
147
 
@@ -178,10 +193,9 @@ https://github.com/owner/repo/releases/tag/v1.0.0
178
193
 
179
194
  ### Q: 如何修改订阅的事件类型?
180
195
 
181
- **A:** 使用 `gh.events` 命令:
182
- - 添加事件:`gh.events owner/repo +issues +release`
183
- - 移除事件:`gh.events owner/repo -star -push`
184
- - 混合操作:`gh.events owner/repo +issues -star`
196
+ **A:** 使用 `gh.on` / `gh.off` 命令:
197
+ - 启用事件:`gh.on owner/repo issues release`
198
+ - 禁用事件:`gh.off owner/repo star push`
185
199
 
186
200
  ### Q: 如何查看当前订阅了哪些仓库?
187
201
 
package/lib/index.js DELETED
@@ -1,1016 +0,0 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
7
- var __export = (target, all) => {
8
- for (var name2 in all)
9
- __defProp(target, name2, { get: all[name2], enumerable: true });
10
- };
11
- var __copyProps = (to, from, except, desc) => {
12
- if (from && typeof from === "object" || typeof from === "function") {
13
- for (let key of __getOwnPropNames(from))
14
- if (!__hasOwnProp.call(to, key) && key !== except)
15
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
- }
17
- return to;
18
- };
19
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
-
21
- // src/index.ts
22
- var src_exports = {};
23
- __export(src_exports, {
24
- Config: () => Config,
25
- apply: () => apply,
26
- inject: () => inject,
27
- name: () => name
28
- });
29
- module.exports = __toCommonJS(src_exports);
30
- var import_koishi5 = require("koishi");
31
-
32
- // src/config.ts
33
- var import_koishi = require("koishi");
34
-
35
- // src/types.ts
36
- var EVENT_DISPLAY_MAP = {
37
- issues: { name: "Issue", emoji: "📌" },
38
- release: { name: "Release", emoji: "🚀" },
39
- push: { name: "Commit", emoji: "⬆️" },
40
- pull_request: { name: "PR", emoji: "🔀" },
41
- star: { name: "Star", emoji: "⭐" }
42
- };
43
- function getDisplayType(type) {
44
- return EVENT_DISPLAY_MAP[type].name;
45
- }
46
- __name(getDisplayType, "getDisplayType");
47
- function getEventEmoji(type) {
48
- return EVENT_DISPLAY_MAP[type].emoji;
49
- }
50
- __name(getEventEmoji, "getEventEmoji");
51
-
52
- // src/config.ts
53
- var EVENT_TYPES = ["issues", "release", "push", "pull_request", "star"];
54
- var Config = import_koishi.Schema.object({
55
- path: import_koishi.Schema.string().default("/github/webhook").description("Webhook 接收路径"),
56
- secret: import_koishi.Schema.string().required().description("GitHub Webhook Secret"),
57
- baseUrl: import_koishi.Schema.string().description("显示用基础 URL"),
58
- defaultEvents: import_koishi.Schema.array(import_koishi.Schema.union(EVENT_TYPES.map((e) => import_koishi.Schema.const(e)))).default(["issues", "release", "push"]).description("默认订阅事件"),
59
- debug: import_koishi.Schema.boolean().default(false).description("调试模式"),
60
- allowUntrusted: import_koishi.Schema.boolean().default(false).description("允许非信任仓库"),
61
- concurrency: import_koishi.Schema.number().default(5).description("推送并发数")
62
- });
63
-
64
- // src/database.ts
65
- function extendDatabase(ctx) {
66
- ctx.model.extend("github_trusted_repos", {
67
- id: { type: "unsigned", length: 10 },
68
- repo: { type: "string", length: 255 },
69
- enabled: "boolean",
70
- createdAt: "timestamp",
71
- updatedAt: "timestamp"
72
- }, {
73
- primary: "id",
74
- autoInc: true,
75
- unique: ["repo"]
76
- });
77
- ctx.model.extend("github_subscriptions", {
78
- id: { type: "unsigned", length: 10 },
79
- platform: "string",
80
- channelId: "string",
81
- guildId: "string",
82
- userId: "string",
83
- repo: "string",
84
- events: "json",
85
- enabled: "boolean",
86
- createdAt: "timestamp",
87
- updatedAt: "timestamp"
88
- }, {
89
- primary: "id",
90
- autoInc: true
91
- });
92
- ctx.model.extend("github_deliveries", {
93
- deliveryId: "string",
94
- repo: "string",
95
- event: "string",
96
- receivedAt: "timestamp"
97
- }, {
98
- primary: "deliveryId"
99
- });
100
- }
101
- __name(extendDatabase, "extendDatabase");
102
-
103
- // src/webhook.ts
104
- var import_koishi4 = require("koishi");
105
-
106
- // src/signature.ts
107
- var import_crypto = require("crypto");
108
- function createSignature(payload, secret) {
109
- const hmac = (0, import_crypto.createHmac)("sha256", secret);
110
- hmac.update(payload, "utf8");
111
- return `sha256=${hmac.digest("hex")}`;
112
- }
113
- __name(createSignature, "createSignature");
114
- function verifySignature(payload, signature, secret) {
115
- if (!signature || !signature.startsWith("sha256=")) {
116
- return false;
117
- }
118
- const expected = createSignature(payload, secret);
119
- const sigBuffer = Buffer.from(signature);
120
- const expectedBuffer = Buffer.from(expected);
121
- if (sigBuffer.length !== expectedBuffer.length) {
122
- return false;
123
- }
124
- return (0, import_crypto.timingSafeEqual)(sigBuffer, expectedBuffer);
125
- }
126
- __name(verifySignature, "verifySignature");
127
-
128
- // src/parser.ts
129
- var MAX_COMMITS = 3;
130
- function parseIssuesEvent(payload) {
131
- const { action, issue, repository, sender } = payload;
132
- if (!["opened", "closed", "reopened", "edited"].includes(action)) {
133
- return null;
134
- }
135
- return {
136
- type: "issues",
137
- displayType: getDisplayType("issues"),
138
- repo: repository.full_name,
139
- actor: sender.login,
140
- action,
141
- title: issue.title,
142
- number: issue.number,
143
- url: issue.html_url,
144
- body: issue.body
145
- };
146
- }
147
- __name(parseIssuesEvent, "parseIssuesEvent");
148
- function parseReleaseEvent(payload) {
149
- const { action, release, repository, sender } = payload;
150
- if (!["published", "created"].includes(action)) {
151
- return null;
152
- }
153
- return {
154
- type: "release",
155
- displayType: getDisplayType("release"),
156
- repo: repository.full_name,
157
- actor: sender.login,
158
- action,
159
- title: release.name || release.tag_name,
160
- tagName: release.tag_name,
161
- url: release.html_url,
162
- body: release.body
163
- };
164
- }
165
- __name(parseReleaseEvent, "parseReleaseEvent");
166
- function parsePushEvent(payload) {
167
- const { ref, commits, repository, sender, compare } = payload;
168
- const branch = ref?.replace("refs/heads/", "") || "";
169
- const allCommits = (commits || []).map((commit) => ({
170
- sha: commit.id?.substring(0, 7) || "",
171
- message: commit.message?.split("\n")[0] || "",
172
- // 只取第一行
173
- author: commit.author?.name || commit.author?.username || "",
174
- url: commit.url || ""
175
- }));
176
- const displayCommits = allCommits.slice(0, MAX_COMMITS);
177
- return {
178
- type: "push",
179
- displayType: getDisplayType("push"),
180
- // 显示为 "Commit"
181
- repo: repository.full_name,
182
- actor: sender.login,
183
- ref: branch,
184
- commits: displayCommits,
185
- totalCommits: allCommits.length,
186
- url: compare || `https://github.com/${repository.full_name}`
187
- };
188
- }
189
- __name(parsePushEvent, "parsePushEvent");
190
- function parsePullRequestEvent(payload) {
191
- const { action, pull_request, repository, sender } = payload;
192
- const validActions = ["opened", "closed", "reopened", "edited"];
193
- if (!validActions.includes(action)) {
194
- return null;
195
- }
196
- const actualAction = action === "closed" && pull_request.merged ? "merged" : action;
197
- return {
198
- type: "pull_request",
199
- displayType: getDisplayType("pull_request"),
200
- repo: repository.full_name,
201
- actor: sender.login,
202
- action: actualAction,
203
- title: pull_request.title,
204
- number: pull_request.number,
205
- url: pull_request.html_url,
206
- body: pull_request.body
207
- };
208
- }
209
- __name(parsePullRequestEvent, "parsePullRequestEvent");
210
- function parseStarEvent(payload) {
211
- const { action, repository, sender } = payload;
212
- if (!["created", "deleted"].includes(action)) {
213
- return null;
214
- }
215
- return {
216
- type: "star",
217
- displayType: getDisplayType("star"),
218
- repo: repository.full_name,
219
- actor: sender.login,
220
- action,
221
- starCount: repository.stargazers_count,
222
- url: repository.html_url
223
- };
224
- }
225
- __name(parseStarEvent, "parseStarEvent");
226
- function parseEvent(eventName, payload) {
227
- switch (eventName) {
228
- case "issues":
229
- return parseIssuesEvent(payload);
230
- case "release":
231
- return parseReleaseEvent(payload);
232
- case "push":
233
- return parsePushEvent(payload);
234
- case "pull_request":
235
- return parsePullRequestEvent(payload);
236
- case "star":
237
- return parseStarEvent(payload);
238
- default:
239
- return null;
240
- }
241
- }
242
- __name(parseEvent, "parseEvent");
243
-
244
- // src/pusher.ts
245
- var import_koishi3 = require("koishi");
246
-
247
- // src/repository/subscription.ts
248
- async function createSubscription(ctx, session, repo, defaultEvents) {
249
- const now = /* @__PURE__ */ new Date();
250
- const existing = await ctx.database.get("github_subscriptions", {
251
- platform: session.platform,
252
- channelId: session.channelId,
253
- repo
254
- });
255
- if (existing.length > 0) {
256
- return existing[0];
257
- }
258
- await ctx.database.create("github_subscriptions", {
259
- platform: session.platform,
260
- channelId: session.channelId,
261
- guildId: session.guildId || "",
262
- userId: session.userId || "",
263
- repo,
264
- events: defaultEvents,
265
- enabled: true,
266
- createdAt: now,
267
- updatedAt: now
268
- });
269
- const [created] = await ctx.database.get("github_subscriptions", {
270
- platform: session.platform,
271
- channelId: session.channelId,
272
- repo
273
- });
274
- return created;
275
- }
276
- __name(createSubscription, "createSubscription");
277
- async function removeSubscription(ctx, session, repo) {
278
- const result = await ctx.database.remove("github_subscriptions", {
279
- platform: session.platform,
280
- channelId: session.channelId,
281
- repo
282
- });
283
- return (result.removed ?? 0) > 0;
284
- }
285
- __name(removeSubscription, "removeSubscription");
286
- async function listSubscriptions(ctx, session) {
287
- return ctx.database.get("github_subscriptions", {
288
- platform: session.platform,
289
- channelId: session.channelId
290
- });
291
- }
292
- __name(listSubscriptions, "listSubscriptions");
293
- async function getSubscription(ctx, session, repo) {
294
- const subs = await ctx.database.get("github_subscriptions", {
295
- platform: session.platform,
296
- channelId: session.channelId,
297
- repo
298
- });
299
- return subs.length > 0 ? subs[0] : null;
300
- }
301
- __name(getSubscription, "getSubscription");
302
- async function updateEvents(ctx, session, repo, events) {
303
- const result = await ctx.database.set("github_subscriptions", {
304
- platform: session.platform,
305
- channelId: session.channelId,
306
- repo
307
- }, {
308
- events,
309
- updatedAt: /* @__PURE__ */ new Date()
310
- });
311
- return (result.modified ?? 0) > 0;
312
- }
313
- __name(updateEvents, "updateEvents");
314
- async function queryTargets(ctx, repo, eventType) {
315
- const subscriptions = await ctx.database.get("github_subscriptions", {
316
- repo,
317
- enabled: true
318
- });
319
- return subscriptions.filter((sub) => sub.events.includes(eventType)).map((sub) => ({
320
- platform: sub.platform,
321
- channelId: sub.channelId,
322
- guildId: sub.guildId || void 0,
323
- userId: sub.userId || void 0
324
- }));
325
- }
326
- __name(queryTargets, "queryTargets");
327
-
328
- // src/message.ts
329
- var import_koishi2 = require("koishi");
330
- function buildMessage(event) {
331
- switch (event.type) {
332
- case "issues":
333
- return buildIssuesMessage(event);
334
- case "release":
335
- return buildReleaseMessage(event);
336
- case "push":
337
- return buildPushMessage(event);
338
- case "pull_request":
339
- return buildPullRequestMessage(event);
340
- case "star":
341
- return buildStarMessage(event);
342
- default:
343
- return buildGenericMessage(event);
344
- }
345
- }
346
- __name(buildMessage, "buildMessage");
347
- function buildIssuesMessage(event) {
348
- const emoji = getEventEmoji(event.type);
349
- const actionText = getActionText(event.action);
350
- const lines = [
351
- `${emoji} [${event.repo}] ${event.displayType}`,
352
- `${event.actor} ${actionText} #${event.number}: ${event.title}`,
353
- event.url
354
- ];
355
- return [(0, import_koishi2.h)("text", { content: lines.join("\n") })];
356
- }
357
- __name(buildIssuesMessage, "buildIssuesMessage");
358
- function buildReleaseMessage(event) {
359
- const emoji = getEventEmoji(event.type);
360
- const actionText = getActionText(event.action);
361
- const lines = [
362
- `${emoji} [${event.repo}] ${event.displayType}`,
363
- `${event.actor} ${actionText} ${event.tagName || event.title}`,
364
- event.url
365
- ];
366
- return [(0, import_koishi2.h)("text", { content: lines.join("\n") })];
367
- }
368
- __name(buildReleaseMessage, "buildReleaseMessage");
369
- function buildPushMessage(event) {
370
- const emoji = getEventEmoji(event.type);
371
- const commits = event.commits || [];
372
- const totalCommits = event.totalCommits || commits.length;
373
- const lines = [
374
- `${emoji} [${event.repo}] ${event.displayType}`,
375
- `${event.actor} 推送了 ${totalCommits} 个提交到 ${event.ref}`,
376
- ""
377
- ];
378
- for (const commit of commits) {
379
- lines.push(`• ${commit.sha} - ${commit.message}`);
380
- }
381
- if (totalCommits > commits.length) {
382
- lines.push("");
383
- lines.push(`还有 ${totalCommits - commits.length} 条提交...`);
384
- }
385
- lines.push(event.url);
386
- return [(0, import_koishi2.h)("text", { content: lines.join("\n") })];
387
- }
388
- __name(buildPushMessage, "buildPushMessage");
389
- function buildPullRequestMessage(event) {
390
- const emoji = getEventEmoji(event.type);
391
- const actionText = getActionText(event.action);
392
- const lines = [
393
- `${emoji} [${event.repo}] ${event.displayType}`,
394
- `${event.actor} ${actionText} #${event.number}: ${event.title}`,
395
- event.url
396
- ];
397
- return [(0, import_koishi2.h)("text", { content: lines.join("\n") })];
398
- }
399
- __name(buildPullRequestMessage, "buildPullRequestMessage");
400
- function buildStarMessage(event) {
401
- const emoji = getEventEmoji(event.type);
402
- const actionText = event.action === "created" ? "starred" : "unstarred";
403
- const lines = [
404
- `${emoji} [${event.repo}] ${event.displayType}`,
405
- `${event.actor} ${actionText} (⭐ ${event.starCount})`,
406
- event.url
407
- ];
408
- return [(0, import_koishi2.h)("text", { content: lines.join("\n") })];
409
- }
410
- __name(buildStarMessage, "buildStarMessage");
411
- function buildGenericMessage(event) {
412
- const emoji = getEventEmoji(event.type);
413
- const lines = [
414
- `${emoji} [${event.repo}] ${event.displayType}`,
415
- `${event.actor} ${event.action || "triggered"}`,
416
- event.url
417
- ];
418
- return [(0, import_koishi2.h)("text", { content: lines.join("\n") })];
419
- }
420
- __name(buildGenericMessage, "buildGenericMessage");
421
- function getActionText(action) {
422
- const actionMap = {
423
- opened: "opened",
424
- closed: "closed",
425
- reopened: "reopened",
426
- edited: "edited",
427
- merged: "merged",
428
- published: "published",
429
- created: "created",
430
- deleted: "deleted"
431
- };
432
- return actionMap[action || ""] || action || "";
433
- }
434
- __name(getActionText, "getActionText");
435
-
436
- // src/pusher.ts
437
- var logger = new import_koishi3.Logger("github-webhook-pusher");
438
- async function queryTargets2(ctx, repo, eventType) {
439
- return queryTargets(ctx, repo, eventType);
440
- }
441
- __name(queryTargets2, "queryTargets");
442
- async function sendMessage(ctx, target, message) {
443
- try {
444
- const bot = ctx.bots.find((b) => b.platform === target.platform);
445
- if (!bot) {
446
- throw new Error(`未找到平台 ${target.platform} 的 bot`);
447
- }
448
- await bot.sendMessage(target.channelId, message, target.guildId);
449
- return { target, success: true };
450
- } catch (error) {
451
- return {
452
- target,
453
- success: false,
454
- error: error instanceof Error ? error : new Error(String(error))
455
- };
456
- }
457
- }
458
- __name(sendMessage, "sendMessage");
459
- async function pushWithConcurrency(ctx, targets, message, concurrency) {
460
- if (targets.length === 0) {
461
- return [];
462
- }
463
- const results = [];
464
- const queue = [...targets];
465
- const workers = Array(Math.min(concurrency, queue.length)).fill(null).map(async () => {
466
- while (queue.length > 0) {
467
- const target = queue.shift();
468
- if (!target) break;
469
- const result = await sendMessage(ctx, target, message);
470
- results.push(result);
471
- if (!result.success && result.error) {
472
- logger.error(
473
- `推送失败 [${target.platform}:${target.channelId}]: ${result.error.message}`
474
- );
475
- }
476
- }
477
- });
478
- await Promise.all(workers);
479
- return results;
480
- }
481
- __name(pushWithConcurrency, "pushWithConcurrency");
482
- async function pushMessage(ctx, event, concurrency = 5) {
483
- const targets = await queryTargets2(ctx, event.repo, event.type);
484
- if (targets.length === 0) {
485
- logger.debug(`仓库 ${event.repo} 的 ${event.type} 事件没有订阅目标`);
486
- return [];
487
- }
488
- logger.info(`准备推送 ${event.type} 事件到 ${targets.length} 个目标`);
489
- const message = buildMessage(event);
490
- const results = await pushWithConcurrency(ctx, targets, message, concurrency);
491
- const successCount = results.filter((r) => r.success).length;
492
- const failCount = results.filter((r) => !r.success).length;
493
- logger.info(`推送完成: 成功 ${successCount}, 失败 ${failCount}`);
494
- return results;
495
- }
496
- __name(pushMessage, "pushMessage");
497
- async function pushEvent(ctx, event, concurrency = 5) {
498
- const results = await pushMessage(ctx, event, concurrency);
499
- return {
500
- pushed: results.filter((r) => r.success).length,
501
- failed: results.filter((r) => !r.success).length,
502
- results
503
- };
504
- }
505
- __name(pushEvent, "pushEvent");
506
-
507
- // src/repository/trust.ts
508
- var REPO_NAME_REGEX = /^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/;
509
- function isValidRepoFormat(repo) {
510
- if (!repo || typeof repo !== "string") {
511
- return false;
512
- }
513
- return REPO_NAME_REGEX.test(repo);
514
- }
515
- __name(isValidRepoFormat, "isValidRepoFormat");
516
- async function addTrustedRepo(ctx, repo) {
517
- if (!isValidRepoFormat(repo)) {
518
- return null;
519
- }
520
- const now = /* @__PURE__ */ new Date();
521
- const existing = await ctx.database.get("github_trusted_repos", { repo });
522
- if (existing.length > 0) {
523
- return existing[0];
524
- }
525
- await ctx.database.create("github_trusted_repos", {
526
- repo,
527
- enabled: true,
528
- createdAt: now,
529
- updatedAt: now
530
- });
531
- const [created] = await ctx.database.get("github_trusted_repos", { repo });
532
- return created;
533
- }
534
- __name(addTrustedRepo, "addTrustedRepo");
535
- async function removeTrustedRepo(ctx, repo) {
536
- const result = await ctx.database.remove("github_trusted_repos", { repo });
537
- return (result.removed ?? 0) > 0;
538
- }
539
- __name(removeTrustedRepo, "removeTrustedRepo");
540
- async function listTrustedRepos(ctx) {
541
- return ctx.database.get("github_trusted_repos", {});
542
- }
543
- __name(listTrustedRepos, "listTrustedRepos");
544
- async function isTrusted(ctx, repo) {
545
- const repos = await ctx.database.get("github_trusted_repos", { repo, enabled: true });
546
- return repos.length > 0;
547
- }
548
- __name(isTrusted, "isTrusted");
549
- async function isInTrustList(ctx, repo) {
550
- const repos = await ctx.database.get("github_trusted_repos", { repo });
551
- return repos.length > 0;
552
- }
553
- __name(isInTrustList, "isInTrustList");
554
- async function enableRepo(ctx, repo) {
555
- const result = await ctx.database.set("github_trusted_repos", { repo }, {
556
- enabled: true,
557
- updatedAt: /* @__PURE__ */ new Date()
558
- });
559
- return (result.modified ?? 0) > 0;
560
- }
561
- __name(enableRepo, "enableRepo");
562
- async function disableRepo(ctx, repo) {
563
- const result = await ctx.database.set("github_trusted_repos", { repo }, {
564
- enabled: false,
565
- updatedAt: /* @__PURE__ */ new Date()
566
- });
567
- return (result.modified ?? 0) > 0;
568
- }
569
- __name(disableRepo, "disableRepo");
570
-
571
- // src/repository/delivery.ts
572
- async function recordDelivery(ctx, deliveryId, repo, event) {
573
- const now = /* @__PURE__ */ new Date();
574
- await ctx.database.create("github_deliveries", {
575
- deliveryId,
576
- repo,
577
- event,
578
- receivedAt: now
579
- });
580
- const [created] = await ctx.database.get("github_deliveries", { deliveryId });
581
- return created;
582
- }
583
- __name(recordDelivery, "recordDelivery");
584
- async function isDelivered(ctx, deliveryId) {
585
- const deliveries = await ctx.database.get("github_deliveries", { deliveryId });
586
- return deliveries.length > 0;
587
- }
588
- __name(isDelivered, "isDelivered");
589
-
590
- // src/webhook.ts
591
- var logger2 = new import_koishi4.Logger("github-webhook");
592
- function registerWebhook(ctx, config) {
593
- const path = config.path || "/github/webhook";
594
- logger2.info(`注册 Webhook 处理器: ${path}`);
595
- ctx.server.post(path, async (koaCtx) => {
596
- const startTime = Date.now();
597
- try {
598
- const signature = koaCtx.get("X-Hub-Signature-256");
599
- const eventName = koaCtx.get("X-GitHub-Event");
600
- const deliveryId = koaCtx.get("X-GitHub-Delivery");
601
- const rawBody = await getRawBody(koaCtx);
602
- if (config.debug) {
603
- logger2.debug(`收到 Webhook 请求: event=${eventName}, delivery=${deliveryId}`);
604
- }
605
- if (config.secret) {
606
- if (!signature) {
607
- logger2.warn("请求缺少 X-Hub-Signature-256 头");
608
- koaCtx.status = 401;
609
- koaCtx.body = { error: "Missing signature" };
610
- return;
611
- }
612
- if (!verifySignature(rawBody, signature, config.secret)) {
613
- logger2.warn("签名验证失败");
614
- koaCtx.status = 401;
615
- koaCtx.body = {
616
- error: "Invalid signature. Ensure Content-Type is application/json, and the signed payload matches the raw request body. 签名无效:请确认 Content-Type 为 application/json,且签名内容与实际请求体完全一致。"
617
- };
618
- return;
619
- }
620
- }
621
- let payload;
622
- try {
623
- payload = JSON.parse(rawBody);
624
- } catch (e) {
625
- logger2.warn("无法解析 JSON 负载");
626
- koaCtx.status = 400;
627
- koaCtx.body = { error: "Invalid JSON payload" };
628
- return;
629
- }
630
- const repo = payload.repository?.full_name;
631
- if (!repo) {
632
- logger2.warn("负载中缺少仓库信息");
633
- koaCtx.status = 400;
634
- koaCtx.body = { error: "Missing repository information" };
635
- return;
636
- }
637
- logger2.info(`收到 Webhook: [${repo}] ${eventName} (${deliveryId})`);
638
- if (deliveryId) {
639
- const isDuplicate = await isDelivered(ctx, deliveryId);
640
- if (isDuplicate) {
641
- logger2.info(`跳过重复请求: ${deliveryId}`);
642
- koaCtx.status = 200;
643
- koaCtx.body = { status: "duplicate" };
644
- return;
645
- }
646
- }
647
- if (!config.allowUntrusted) {
648
- const trusted = await isTrusted(ctx, repo);
649
- if (!trusted) {
650
- logger2.info(`忽略非信任仓库事件: ${repo}`);
651
- koaCtx.status = 200;
652
- koaCtx.body = { status: "ignored", reason: "untrusted" };
653
- return;
654
- }
655
- }
656
- const event = parseEvent(eventName, payload);
657
- if (!event) {
658
- logger2.info(`不支持的事件类型: ${eventName}`);
659
- koaCtx.status = 200;
660
- koaCtx.body = { status: "ignored", reason: "unsupported" };
661
- return;
662
- }
663
- if (deliveryId) {
664
- await recordDelivery(ctx, deliveryId, repo, eventName);
665
- }
666
- const result = await pushEvent(ctx, event, config.concurrency);
667
- const elapsed = Date.now() - startTime;
668
- logger2.info(`处理完成: 推送 ${result.pushed} 成功, ${result.failed} 失败 (${elapsed}ms)`);
669
- koaCtx.status = 200;
670
- koaCtx.body = { status: "ok", pushed: result.pushed };
671
- } catch (error) {
672
- const errorMessage = error instanceof Error ? error.message : String(error);
673
- logger2.error(`处理 Webhook 时发生错误: ${errorMessage}`);
674
- if (config.debug && error instanceof Error) {
675
- logger2.error(error.stack || "");
676
- }
677
- koaCtx.status = 500;
678
- koaCtx.body = { status: "error", error: "Internal server error" };
679
- }
680
- });
681
- }
682
- __name(registerWebhook, "registerWebhook");
683
- async function getRawBody(koaCtx) {
684
- if (koaCtx.request.body) {
685
- return JSON.stringify(koaCtx.request.body);
686
- }
687
- return new Promise((resolve, reject) => {
688
- let data = "";
689
- koaCtx.req.on("data", (chunk) => {
690
- data += chunk.toString();
691
- });
692
- koaCtx.req.on("end", () => {
693
- resolve(data);
694
- });
695
- koaCtx.req.on("error", reject);
696
- });
697
- }
698
- __name(getRawBody, "getRawBody");
699
-
700
- // src/commands/trust.ts
701
- var ADMIN_AUTHORITY = 3;
702
- function registerTrustCommands(ctx) {
703
- const trust = ctx.command("gh.trust", "管理信任的 GitHub 仓库").usage("gh.trust <add|remove|list|enable|disable> [repo]");
704
- trust.subcommand(".add <repo:string>", "添加信任仓库").usage("gh.trust.add owner/repo").example("gh.trust.add koishijs/koishi").action(async ({ session }, repo) => {
705
- const user = session?.user;
706
- if ((user?.authority ?? 0) < ADMIN_AUTHORITY) {
707
- return "❌ 权限不足,需要管理员权限";
708
- }
709
- if (!repo) {
710
- return "❌ 请指定仓库名,格式: owner/repo";
711
- }
712
- if (!isValidRepoFormat(repo)) {
713
- return "❌ 仓库格式错误,请使用 owner/repo 格式";
714
- }
715
- const result = await addTrustedRepo(ctx, repo);
716
- if (result) {
717
- return `✅ 已添加信任仓库: ${repo}`;
718
- }
719
- return "❌ 添加失败";
720
- });
721
- trust.subcommand(".remove <repo:string>", "移除信任仓库").usage("gh.trust.remove owner/repo").example("gh.trust.remove koishijs/koishi").action(async ({ session }, repo) => {
722
- const user = session?.user;
723
- if ((user?.authority ?? 0) < ADMIN_AUTHORITY) {
724
- return "❌ 权限不足,需要管理员权限";
725
- }
726
- if (!repo) {
727
- return "❌ 请指定仓库名";
728
- }
729
- const success = await removeTrustedRepo(ctx, repo);
730
- if (success) {
731
- return `✅ 已移除信任仓库: ${repo}`;
732
- }
733
- return `❌ 仓库 ${repo} 不在信任列表中`;
734
- });
735
- trust.subcommand(".list", "列出所有信任仓库").usage("gh.trust.list").action(async ({ session }) => {
736
- const user = session?.user;
737
- if ((user?.authority ?? 0) < ADMIN_AUTHORITY) {
738
- return "❌ 权限不足,需要管理员权限";
739
- }
740
- const repos = await listTrustedRepos(ctx);
741
- if (repos.length === 0) {
742
- return "📋 信任仓库列表为空";
743
- }
744
- const lines = ["📋 信任仓库列表:"];
745
- for (const repo of repos) {
746
- const status = repo.enabled ? "✅" : "⏸️";
747
- lines.push(`${status} ${repo.repo}`);
748
- }
749
- return lines.join("\n");
750
- });
751
- trust.subcommand(".enable <repo:string>", "启用信任仓库").usage("gh.trust.enable owner/repo").example("gh.trust.enable koishijs/koishi").action(async ({ session }, repo) => {
752
- const user = session?.user;
753
- if ((user?.authority ?? 0) < ADMIN_AUTHORITY) {
754
- return "❌ 权限不足,需要管理员权限";
755
- }
756
- if (!repo) {
757
- return "❌ 请指定仓库名";
758
- }
759
- const success = await enableRepo(ctx, repo);
760
- if (success) {
761
- return `✅ 已启用仓库: ${repo}`;
762
- }
763
- return `❌ 仓库 ${repo} 不在信任列表中`;
764
- });
765
- trust.subcommand(".disable <repo:string>", "禁用信任仓库").usage("gh.trust.disable owner/repo").example("gh.trust.disable koishijs/koishi").action(async ({ session }, repo) => {
766
- const user = session?.user;
767
- if ((user?.authority ?? 0) < ADMIN_AUTHORITY) {
768
- return "❌ 权限不足,需要管理员权限";
769
- }
770
- if (!repo) {
771
- return "❌ 请指定仓库名";
772
- }
773
- const success = await disableRepo(ctx, repo);
774
- if (success) {
775
- return `⏸️ 已禁用仓库: ${repo}`;
776
- }
777
- return `❌ 仓库 ${repo} 不在信任列表中`;
778
- });
779
- }
780
- __name(registerTrustCommands, "registerTrustCommands");
781
-
782
- // src/commands/subscription.ts
783
- var ALL_EVENT_TYPES = ["issues", "release", "push", "pull_request", "star"];
784
- function getSessionIdentifier(session) {
785
- return {
786
- platform: session.platform,
787
- channelId: session.channelId,
788
- guildId: session.guildId,
789
- userId: session.userId
790
- };
791
- }
792
- __name(getSessionIdentifier, "getSessionIdentifier");
793
- function parseEventChanges(changes, currentEvents) {
794
- const events = new Set(currentEvents);
795
- for (const change of changes) {
796
- if (!change) continue;
797
- const prefix = change[0];
798
- const eventName = change.slice(1);
799
- if (!ALL_EVENT_TYPES.includes(eventName)) {
800
- continue;
801
- }
802
- if (prefix === "+") {
803
- events.add(eventName);
804
- } else if (prefix === "-") {
805
- events.delete(eventName);
806
- }
807
- }
808
- return Array.from(events);
809
- }
810
- __name(parseEventChanges, "parseEventChanges");
811
- function registerSubscriptionCommands(ctx, config) {
812
- ctx.command("gh.sub <repo:string>", "订阅 GitHub 仓库事件").usage("gh.sub owner/repo").example("gh.sub koishijs/koishi").action(async ({ session }, repo) => {
813
- if (!session) return "❌ 无法获取会话信息";
814
- if (!repo) {
815
- return "❌ 请指定仓库名,格式: owner/repo";
816
- }
817
- const trusted = await isInTrustList(ctx, repo);
818
- if (!trusted) {
819
- return "❌ 该仓库不在信任列表中";
820
- }
821
- const sessionId = getSessionIdentifier(session);
822
- const subscription = await createSubscription(ctx, sessionId, repo, config.defaultEvents);
823
- if (subscription) {
824
- const eventList = subscription.events.join(", ");
825
- return `✅ 已订阅仓库: ${repo}
826
- 📋 订阅事件: ${eventList}`;
827
- }
828
- return "❌ 订阅失败";
829
- });
830
- ctx.command("gh.unsub <repo:string>", "取消订阅 GitHub 仓库").usage("gh.unsub owner/repo").example("gh.unsub koishijs/koishi").action(async ({ session }, repo) => {
831
- if (!session) return "❌ 无法获取会话信息";
832
- if (!repo) {
833
- return "❌ 请指定仓库名";
834
- }
835
- const sessionId = getSessionIdentifier(session);
836
- const success = await removeSubscription(ctx, sessionId, repo);
837
- if (success) {
838
- return `✅ 已取消订阅: ${repo}`;
839
- }
840
- return `❌ 未找到仓库 ${repo} 的订阅`;
841
- });
842
- ctx.command("gh.list", "列出当前会话的所有订阅").usage("gh.list").action(async ({ session }) => {
843
- if (!session) return "❌ 无法获取会话信息";
844
- const sessionId = getSessionIdentifier(session);
845
- const subscriptions = await listSubscriptions(ctx, sessionId);
846
- if (subscriptions.length === 0) {
847
- return "📋 当前会话没有订阅任何仓库";
848
- }
849
- const lines = ["📋 订阅列表:"];
850
- for (const sub of subscriptions) {
851
- const status = sub.enabled ? "✅" : "⏸️";
852
- const events = sub.events.join(", ");
853
- lines.push(`${status} ${sub.repo}`);
854
- lines.push(` 事件: ${events}`);
855
- }
856
- return lines.join("\n");
857
- });
858
- ctx.command("gh.events <repo:string> [...changes:string]", "查看或修改订阅的事件类型").usage("gh.events owner/repo [+event] [-event]").example("gh.events koishijs/koishi").example("gh.events koishijs/koishi +issues -star +release").action(async ({ session }, repo, ...changes) => {
859
- if (!session) return "❌ 无法获取会话信息";
860
- if (!repo) {
861
- const lines = ["📋 可用事件类型:"];
862
- for (const [type, info] of Object.entries(EVENT_DISPLAY_MAP)) {
863
- lines.push(`${info.emoji} ${type} - ${info.name}`);
864
- }
865
- return lines.join("\n");
866
- }
867
- const sessionId = getSessionIdentifier(session);
868
- const subscription = await getSubscription(ctx, sessionId, repo);
869
- if (!subscription) {
870
- return `❌ 未找到仓库 ${repo} 的订阅`;
871
- }
872
- if (!changes || changes.length === 0) {
873
- const events = subscription.events.join(", ");
874
- return `📋 ${repo} 订阅的事件:
875
- ${events}`;
876
- }
877
- const newEvents = parseEventChanges(changes, subscription.events);
878
- const success = await updateEvents(ctx, sessionId, repo, newEvents);
879
- if (success) {
880
- const eventList = newEvents.join(", ");
881
- return `✅ 已更新 ${repo} 的订阅事件:
882
- ${eventList}`;
883
- }
884
- return "❌ 更新失败";
885
- });
886
- }
887
- __name(registerSubscriptionCommands, "registerSubscriptionCommands");
888
-
889
- // src/commands/utils.ts
890
- var PLUGIN_NAME = "github-webhook-pusher";
891
- var ADMIN_AUTHORITY2 = 3;
892
- var ALL_EVENT_TYPES2 = ["issues", "release", "push", "pull_request", "star"];
893
- function generateTestEvent(repo, eventType) {
894
- const baseEvent = {
895
- type: eventType,
896
- displayType: getDisplayType(eventType),
897
- repo,
898
- actor: "test-user",
899
- url: `https://github.com/${repo}`
900
- };
901
- switch (eventType) {
902
- case "issues":
903
- return {
904
- ...baseEvent,
905
- action: "opened",
906
- title: "测试 Issue 标题",
907
- number: 123
908
- };
909
- case "release":
910
- return {
911
- ...baseEvent,
912
- action: "published",
913
- title: "v1.0.0",
914
- tagName: "v1.0.0"
915
- };
916
- case "push":
917
- return {
918
- ...baseEvent,
919
- ref: "main",
920
- commits: [
921
- { sha: "abc1234", message: "测试提交 1", author: "test-user", url: "" },
922
- { sha: "def5678", message: "测试提交 2", author: "test-user", url: "" }
923
- ],
924
- totalCommits: 2
925
- };
926
- case "pull_request":
927
- return {
928
- ...baseEvent,
929
- action: "opened",
930
- title: "测试 PR 标题",
931
- number: 456
932
- };
933
- case "star":
934
- return {
935
- ...baseEvent,
936
- action: "created",
937
- starCount: 1234
938
- };
939
- default:
940
- return baseEvent;
941
- }
942
- }
943
- __name(generateTestEvent, "generateTestEvent");
944
- function registerUtilCommands(ctx, config) {
945
- ctx.command("gh.ping", "查看插件状态").usage("gh.ping").action(async () => {
946
- const repos = await listTrustedRepos(ctx);
947
- const enabledCount = repos.filter((r) => r.enabled).length;
948
- const lines = [
949
- "🏓 GitHub Webhook 推送插件",
950
- `📦 插件名称: ${PLUGIN_NAME}`,
951
- `🔗 Webhook 路径: ${config.path}`,
952
- `📋 信任仓库: ${repos.length} 个 (${enabledCount} 个已启用)`,
953
- `🔧 调试模式: ${config.debug ? "开启" : "关闭"}`
954
- ];
955
- return lines.join("\n");
956
- });
957
- ctx.command("gh.test <repo:string> <event:string>", "生成测试消息").usage("gh.test owner/repo event").example("gh.test koishijs/koishi issues").example("gh.test koishijs/koishi push").action(async ({ session }, repo, event) => {
958
- const user = session?.user;
959
- if ((user?.authority ?? 0) < ADMIN_AUTHORITY2) {
960
- return "❌ 权限不足,需要管理员权限";
961
- }
962
- if (!repo) {
963
- return "❌ 请指定仓库名,格式: owner/repo";
964
- }
965
- if (!event) {
966
- const eventList = ALL_EVENT_TYPES2.map((e) => {
967
- const info = EVENT_DISPLAY_MAP[e];
968
- return `${info.emoji} ${e}`;
969
- }).join(", ");
970
- return `❌ 请指定事件类型
971
- 可用类型: ${eventList}`;
972
- }
973
- if (!ALL_EVENT_TYPES2.includes(event)) {
974
- const eventList = ALL_EVENT_TYPES2.join(", ");
975
- return `❌ 不支持的事件类型: ${event}
976
- 可用类型: ${eventList}`;
977
- }
978
- const testEvent = generateTestEvent(repo, event);
979
- const message = buildMessage(testEvent);
980
- await session?.send(message);
981
- return;
982
- });
983
- }
984
- __name(registerUtilCommands, "registerUtilCommands");
985
-
986
- // src/index.ts
987
- var name = "github-webhook-pusher";
988
- var inject = ["server", "database"];
989
- var logger3 = new import_koishi5.Logger("github-webhook-pusher");
990
- function apply(ctx, config) {
991
- logger3.info(`正在加载 GitHub Webhook 插件...`);
992
- extendDatabase(ctx);
993
- logger3.debug("数据库模型已注册");
994
- registerWebhook(ctx, config);
995
- logger3.debug(`Webhook 处理器已注册: ${config.path}`);
996
- registerTrustCommands(ctx);
997
- logger3.debug("信任仓库管理命令已注册");
998
- registerSubscriptionCommands(ctx, config);
999
- logger3.debug("订阅管理命令已注册");
1000
- registerUtilCommands(ctx, config);
1001
- logger3.debug("工具命令已注册");
1002
- logger3.info(`GitHub Webhook 插件已加载`);
1003
- logger3.info(`Webhook 路径: ${config.path}`);
1004
- logger3.info(`默认订阅事件: ${config.defaultEvents.join(", ")}`);
1005
- if (config.debug) {
1006
- logger3.info("调试模式已启用");
1007
- }
1008
- }
1009
- __name(apply, "apply");
1010
- // Annotate the CommonJS export names for ESM import in node:
1011
- 0 && (module.exports = {
1012
- Config,
1013
- apply,
1014
- inject,
1015
- name
1016
- });