koishi-plugin-githubsth 1.0.2-beta1 → 1.0.2-test2
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/commands/subscribe.d.ts +1 -1
- package/lib/commands/subscribe.js +3 -4
- package/lib/config.d.ts +0 -2
- package/lib/config.js +0 -4
- package/lib/services/notifier.d.ts +0 -1
- package/lib/services/notifier.js +27 -186
- package/package.json +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
import { Context } from 'koishi';
|
|
2
|
-
export declare function apply(ctx: Context
|
|
2
|
+
export declare function apply(ctx: Context): void;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.apply = apply;
|
|
4
|
-
function apply(ctx
|
|
4
|
+
function apply(ctx) {
|
|
5
5
|
const logger = ctx.logger('githubsth');
|
|
6
6
|
const repoRegex = /^[\w-]+\/[\w-\.]+$/;
|
|
7
7
|
const validEvents = [
|
|
@@ -9,12 +9,11 @@ function apply(ctx, config) {
|
|
|
9
9
|
'pull_request_review', 'star', 'fork', 'release',
|
|
10
10
|
'discussion', 'workflow_run'
|
|
11
11
|
];
|
|
12
|
-
const defaultConfigEvents = config.defaultEvents || ['push', 'issues', 'issue_comment', 'pull_request', 'pull_request_review', 'release', 'star', 'fork'];
|
|
13
12
|
ctx.command('githubsth.subscribe <repo> [events:text]', '订阅 GitHub 仓库')
|
|
14
13
|
.alias('gh.sub')
|
|
15
14
|
.usage(`
|
|
16
15
|
订阅 GitHub 仓库通知。
|
|
17
|
-
如果不指定事件,默认订阅:
|
|
16
|
+
如果不指定事件,默认订阅: push, issues, issue_comment, pull_request, pull_request_review, release, star, fork
|
|
18
17
|
|
|
19
18
|
可选事件:
|
|
20
19
|
- push: 代码推送
|
|
@@ -59,7 +58,7 @@ gh.sub koishijs/koishi push,issues,star
|
|
|
59
58
|
}
|
|
60
59
|
else {
|
|
61
60
|
// Default events
|
|
62
|
-
events = [
|
|
61
|
+
events = ['push', 'issues', 'issue_comment', 'pull_request', 'pull_request_review', 'release', 'star', 'fork'];
|
|
63
62
|
}
|
|
64
63
|
try {
|
|
65
64
|
// Check if subscription exists
|
package/lib/config.d.ts
CHANGED
package/lib/config.js
CHANGED
|
@@ -6,10 +6,6 @@ exports.Config = koishi_1.Schema.object({
|
|
|
6
6
|
defaultOwner: koishi_1.Schema.string().description('默认仓库拥有者'),
|
|
7
7
|
defaultRepo: koishi_1.Schema.string().description('默认仓库名称'),
|
|
8
8
|
debug: koishi_1.Schema.boolean().default(false).description('启用调试模式,输出详细日志'),
|
|
9
|
-
logUnhandledEvents: koishi_1.Schema.boolean().default(false).description('是否记录未处理的 Webhook 事件 (Unknown events)'),
|
|
10
|
-
defaultEvents: koishi_1.Schema.array(koishi_1.Schema.string())
|
|
11
|
-
.default(['push', 'issues', 'issue_comment', 'pull_request', 'pull_request_review', 'release', 'star', 'fork'])
|
|
12
|
-
.description('默认订阅事件列表 (当不指定事件时使用)'),
|
|
13
9
|
rules: koishi_1.Schema.array(koishi_1.Schema.object({
|
|
14
10
|
repo: koishi_1.Schema.string().required(),
|
|
15
11
|
channelId: koishi_1.Schema.string().required(),
|
package/lib/services/notifier.js
CHANGED
|
@@ -56,15 +56,9 @@ class Notifier extends koishi_1.Service {
|
|
|
56
56
|
eventType = 'discussion';
|
|
57
57
|
else if (payload.workflow_run)
|
|
58
58
|
eventType = 'workflow_run';
|
|
59
|
-
// Handle raw star event if it has repository info directly
|
|
60
|
-
else if (payload.repository && (payload.action === 'created' || payload.action === 'started'))
|
|
61
|
-
eventType = 'star';
|
|
62
59
|
if (eventType !== 'unknown') {
|
|
63
60
|
this.handleEvent(eventType, payload);
|
|
64
61
|
}
|
|
65
|
-
else if (this.config.logUnhandledEvents) {
|
|
66
|
-
this.ctx.logger('githubsth').info(`Unhandled payload structure. Keys: ${Object.keys(payload).join(', ')}`);
|
|
67
|
-
}
|
|
68
62
|
}
|
|
69
63
|
});
|
|
70
64
|
}
|
|
@@ -86,58 +80,18 @@ class Notifier extends koishi_1.Service {
|
|
|
86
80
|
if (!repoName && realPayload.pull_request?.base?.repo?.full_name) {
|
|
87
81
|
repoName = realPayload.pull_request.base.repo.full_name;
|
|
88
82
|
}
|
|
89
|
-
// Special handling for 'star' event (which might be 'watch' event with action 'started')
|
|
90
|
-
// The payload might be missing repository info in the main object but have it in the original session payload
|
|
91
|
-
if (!repoName && event === 'star') {
|
|
92
|
-
// Sometimes the repository info is at the root of the payload, not inside 'payload' property
|
|
93
|
-
if (payload.repository?.full_name) {
|
|
94
|
-
repoName = payload.repository.full_name;
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
83
|
if (!repoName) {
|
|
98
84
|
if (this.config.debug) {
|
|
99
|
-
this.ctx.logger('githubsth').warn(`Missing repo info for event: ${event}
|
|
85
|
+
this.ctx.logger('githubsth').warn(`Missing repo info for event: ${event}`);
|
|
86
|
+
this.ctx.logger('notifier').warn(`Event ${event} missing repository info. Keys: ${Object.keys(realPayload).join(', ')}`);
|
|
100
87
|
}
|
|
101
|
-
else
|
|
102
|
-
// Log at warning level if repo info is missing
|
|
88
|
+
else {
|
|
89
|
+
// Log at warning level even in production if repo info is missing, as this is a critical failure
|
|
90
|
+
// But only log keys to avoid leaking sensitive data
|
|
103
91
|
this.ctx.logger('githubsth').warn(`Missing repo info for event: ${event}. Keys: ${Object.keys(realPayload).join(', ')}`);
|
|
104
92
|
}
|
|
105
93
|
return;
|
|
106
94
|
}
|
|
107
|
-
// Patch realPayload with extracted repo info if missing
|
|
108
|
-
// This is crucial for formatter to work correctly as it expects repository object
|
|
109
|
-
if (!realPayload.repository) {
|
|
110
|
-
realPayload.repository = { full_name: repoName };
|
|
111
|
-
}
|
|
112
|
-
else if (!realPayload.repository.full_name) {
|
|
113
|
-
realPayload.repository.full_name = repoName;
|
|
114
|
-
}
|
|
115
|
-
// Patch realPayload with sender info if missing (e.g. issues event)
|
|
116
|
-
if (!realPayload.sender) {
|
|
117
|
-
if (realPayload.issue?.user) {
|
|
118
|
-
realPayload.sender = realPayload.issue.user;
|
|
119
|
-
}
|
|
120
|
-
else if (realPayload.pull_request?.user) {
|
|
121
|
-
realPayload.sender = realPayload.pull_request.user;
|
|
122
|
-
}
|
|
123
|
-
else if (realPayload.discussion?.user) {
|
|
124
|
-
realPayload.sender = realPayload.discussion.user;
|
|
125
|
-
}
|
|
126
|
-
else if (realPayload.pusher) {
|
|
127
|
-
realPayload.sender = { login: realPayload.pusher.name || 'Pusher' };
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
// Fallback sender
|
|
131
|
-
realPayload.sender = { login: 'GitHub' };
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// Comprehensive patching for specific events to prevent formatter crashes
|
|
135
|
-
try {
|
|
136
|
-
this.patchPayloadForEvent(event, realPayload, repoName);
|
|
137
|
-
}
|
|
138
|
-
catch (e) {
|
|
139
|
-
this.ctx.logger('githubsth').warn(`Failed to patch payload for ${event}:`, e);
|
|
140
|
-
}
|
|
141
95
|
if (this.config.debug) {
|
|
142
96
|
this.ctx.logger('githubsth').info(`Processing event ${event} for ${repoName}`);
|
|
143
97
|
this.ctx.logger('notifier').info(`Received event ${event} for ${repoName}`);
|
|
@@ -172,9 +126,6 @@ class Notifier extends koishi_1.Service {
|
|
|
172
126
|
this.ctx.logger('githubsth').info(`No matching rules for ${repoName} (event: ${event})`);
|
|
173
127
|
this.ctx.logger('notifier').debug(`No matching rules for ${repoName} (event: ${event})`);
|
|
174
128
|
}
|
|
175
|
-
else if (this.config.logUnhandledEvents) {
|
|
176
|
-
this.ctx.logger('githubsth').warn(`No matching rules for ${repoName} (event: ${event})`);
|
|
177
|
-
}
|
|
178
129
|
return;
|
|
179
130
|
}
|
|
180
131
|
if (this.config.debug) {
|
|
@@ -187,160 +138,50 @@ class Notifier extends koishi_1.Service {
|
|
|
187
138
|
this.ctx.logger('notifier').warn('Formatter service not available');
|
|
188
139
|
return;
|
|
189
140
|
}
|
|
190
|
-
try {
|
|
191
|
-
switch (event) {
|
|
192
|
-
case 'push':
|
|
193
|
-
message = this.ctx.githubsthFormatter.formatPush(realPayload);
|
|
194
|
-
break;
|
|
195
|
-
case 'issues':
|
|
196
|
-
message = this.ctx.githubsthFormatter.formatIssue(realPayload);
|
|
197
|
-
break;
|
|
198
|
-
case 'pull_request':
|
|
199
|
-
message = this.ctx.githubsthFormatter.formatPullRequest(realPayload);
|
|
200
|
-
break;
|
|
201
|
-
case 'star':
|
|
202
|
-
message = this.ctx.githubsthFormatter.formatStar(realPayload);
|
|
203
|
-
break;
|
|
204
|
-
case 'fork':
|
|
205
|
-
message = this.ctx.githubsthFormatter.formatFork(realPayload);
|
|
206
|
-
break;
|
|
207
|
-
case 'release':
|
|
208
|
-
message = this.ctx.githubsthFormatter.formatRelease(realPayload);
|
|
209
|
-
break;
|
|
210
|
-
case 'discussion':
|
|
211
|
-
message = this.ctx.githubsthFormatter.formatDiscussion(realPayload);
|
|
212
|
-
break;
|
|
213
|
-
case 'workflow_run':
|
|
214
|
-
message = this.ctx.githubsthFormatter.formatWorkflowRun(realPayload);
|
|
215
|
-
break;
|
|
216
|
-
case 'issue_comment':
|
|
217
|
-
message = this.ctx.githubsthFormatter.formatIssueComment(realPayload);
|
|
218
|
-
break;
|
|
219
|
-
case 'pull_request_review':
|
|
220
|
-
message = this.ctx.githubsthFormatter.formatPullRequestReview(realPayload);
|
|
221
|
-
break;
|
|
222
|
-
}
|
|
223
|
-
}
|
|
224
|
-
catch (e) {
|
|
225
|
-
this.ctx.logger('githubsth').error(`Error formatting event ${event}:`, e);
|
|
226
|
-
if (this.config.debug) {
|
|
227
|
-
this.ctx.logger('notifier').error(`Error formatting event ${event}:`, e);
|
|
228
|
-
}
|
|
229
|
-
return;
|
|
230
|
-
}
|
|
231
|
-
if (!message) {
|
|
232
|
-
if (this.config.debug) {
|
|
233
|
-
this.ctx.logger('notifier').debug(`Formatter returned null for event ${event}`);
|
|
234
|
-
}
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
for (const rule of matchedRules) {
|
|
238
|
-
if (this.config.debug) {
|
|
239
|
-
this.ctx.logger('notifier').debug(`Sending message to channel ${rule.channelId} (platform: ${rule.platform || 'any'})`);
|
|
240
|
-
}
|
|
241
|
-
await this.sendMessage(rule, message);
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
patchPayloadForEvent(event, payload, repoName) {
|
|
245
|
-
// Ensure sender exists (handled before, but good for type safety)
|
|
246
|
-
const defaultUser = { login: 'GitHub', id: 0, avatar_url: '' };
|
|
247
|
-
if (!payload.sender)
|
|
248
|
-
payload.sender = defaultUser;
|
|
249
|
-
// Ensure repository exists (handled before, but good for type safety)
|
|
250
|
-
const defaultRepo = { full_name: repoName, stargazers_count: 0, html_url: `https://github.com/${repoName}` };
|
|
251
|
-
if (!payload.repository)
|
|
252
|
-
payload.repository = defaultRepo;
|
|
253
141
|
switch (event) {
|
|
254
142
|
case 'push':
|
|
255
|
-
|
|
256
|
-
payload.pusher = { name: payload.sender.login };
|
|
257
|
-
if (!payload.commits)
|
|
258
|
-
payload.commits = [];
|
|
259
|
-
if (!payload.ref)
|
|
260
|
-
payload.ref = 'refs/heads/unknown';
|
|
261
|
-
if (!payload.compare)
|
|
262
|
-
payload.compare = '';
|
|
263
|
-
// Ensure author exists in commits
|
|
264
|
-
if (payload.commits.length > 0) {
|
|
265
|
-
payload.commits.forEach((c) => {
|
|
266
|
-
if (!c.author)
|
|
267
|
-
c.author = { name: 'Unknown' };
|
|
268
|
-
if (!c.id)
|
|
269
|
-
c.id = '0000000';
|
|
270
|
-
if (!c.message)
|
|
271
|
-
c.message = 'No message';
|
|
272
|
-
});
|
|
273
|
-
}
|
|
143
|
+
message = this.ctx.githubsthFormatter.formatPush(realPayload);
|
|
274
144
|
break;
|
|
275
145
|
case 'issues':
|
|
276
|
-
|
|
277
|
-
payload.action = 'updated';
|
|
278
|
-
if (!payload.issue)
|
|
279
|
-
payload.issue = { number: 0, title: 'Unknown Issue', html_url: '', user: payload.sender };
|
|
280
|
-
// Ensure user exists in issue
|
|
281
|
-
if (!payload.issue.user)
|
|
282
|
-
payload.issue.user = payload.sender;
|
|
146
|
+
message = this.ctx.githubsthFormatter.formatIssue(realPayload);
|
|
283
147
|
break;
|
|
284
148
|
case 'pull_request':
|
|
285
|
-
|
|
286
|
-
payload.action = 'updated';
|
|
287
|
-
if (!payload.pull_request)
|
|
288
|
-
payload.pull_request = { number: 0, title: 'Unknown PR', state: 'unknown', html_url: '', user: payload.sender };
|
|
289
|
-
if (!payload.pull_request.user)
|
|
290
|
-
payload.pull_request.user = payload.sender;
|
|
149
|
+
message = this.ctx.githubsthFormatter.formatPullRequest(realPayload);
|
|
291
150
|
break;
|
|
292
151
|
case 'star':
|
|
293
|
-
|
|
294
|
-
payload.action = 'created';
|
|
295
|
-
if (payload.repository && payload.repository.stargazers_count === undefined) {
|
|
296
|
-
payload.repository.stargazers_count = '?';
|
|
297
|
-
}
|
|
152
|
+
message = this.ctx.githubsthFormatter.formatStar(realPayload);
|
|
298
153
|
break;
|
|
299
154
|
case 'fork':
|
|
300
|
-
|
|
301
|
-
payload.forkee = { full_name: 'unknown/fork' };
|
|
155
|
+
message = this.ctx.githubsthFormatter.formatFork(realPayload);
|
|
302
156
|
break;
|
|
303
157
|
case 'release':
|
|
304
|
-
|
|
305
|
-
payload.action = 'published';
|
|
306
|
-
if (!payload.release)
|
|
307
|
-
payload.release = { tag_name: 'unknown', name: 'Unknown Release', html_url: '' };
|
|
158
|
+
message = this.ctx.githubsthFormatter.formatRelease(realPayload);
|
|
308
159
|
break;
|
|
309
160
|
case 'discussion':
|
|
310
|
-
|
|
311
|
-
payload.action = 'updated';
|
|
312
|
-
if (!payload.discussion)
|
|
313
|
-
payload.discussion = { number: 0, title: 'Unknown Discussion', html_url: '', user: payload.sender };
|
|
314
|
-
if (!payload.discussion.user)
|
|
315
|
-
payload.discussion.user = payload.sender;
|
|
161
|
+
message = this.ctx.githubsthFormatter.formatDiscussion(realPayload);
|
|
316
162
|
break;
|
|
317
163
|
case 'workflow_run':
|
|
318
|
-
|
|
319
|
-
payload.action = 'completed';
|
|
320
|
-
if (!payload.workflow_run)
|
|
321
|
-
payload.workflow_run = { conclusion: 'unknown', name: 'Unknown Workflow', head_branch: 'unknown', html_url: '' };
|
|
164
|
+
message = this.ctx.githubsthFormatter.formatWorkflowRun(realPayload);
|
|
322
165
|
break;
|
|
323
166
|
case 'issue_comment':
|
|
324
|
-
|
|
325
|
-
payload.action = 'created';
|
|
326
|
-
if (!payload.issue)
|
|
327
|
-
payload.issue = { number: 0, title: 'Unknown Issue', html_url: '', user: payload.sender };
|
|
328
|
-
if (!payload.comment)
|
|
329
|
-
payload.comment = { body: '', html_url: '' };
|
|
330
|
-
if (!payload.issue.user)
|
|
331
|
-
payload.issue.user = payload.sender;
|
|
167
|
+
message = this.ctx.githubsthFormatter.formatIssueComment(realPayload);
|
|
332
168
|
break;
|
|
333
169
|
case 'pull_request_review':
|
|
334
|
-
|
|
335
|
-
payload.action = 'submitted';
|
|
336
|
-
if (!payload.pull_request)
|
|
337
|
-
payload.pull_request = { number: 0, title: 'Unknown PR', html_url: '', user: payload.sender };
|
|
338
|
-
if (!payload.review)
|
|
339
|
-
payload.review = { state: 'unknown', html_url: '' };
|
|
340
|
-
if (!payload.pull_request.user)
|
|
341
|
-
payload.pull_request.user = payload.sender;
|
|
170
|
+
message = this.ctx.githubsthFormatter.formatPullRequestReview(realPayload);
|
|
342
171
|
break;
|
|
343
172
|
}
|
|
173
|
+
if (!message) {
|
|
174
|
+
if (this.config.debug) {
|
|
175
|
+
this.ctx.logger('notifier').debug(`Formatter returned null for event ${event}`);
|
|
176
|
+
}
|
|
177
|
+
return;
|
|
178
|
+
}
|
|
179
|
+
for (const rule of matchedRules) {
|
|
180
|
+
if (this.config.debug) {
|
|
181
|
+
this.ctx.logger('notifier').debug(`Sending message to channel ${rule.channelId} (platform: ${rule.platform || 'any'})`);
|
|
182
|
+
}
|
|
183
|
+
await this.sendMessage(rule, message);
|
|
184
|
+
}
|
|
344
185
|
}
|
|
345
186
|
async sendMessage(rule, message) {
|
|
346
187
|
// Find suitable bots
|
package/package.json
CHANGED