koishi-plugin-githubsth 1.0.2-test2 → 1.0.2

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.
@@ -1,2 +1,2 @@
1
1
  import { Context } from 'koishi';
2
- export declare function apply(ctx: Context): void;
2
+ export declare function apply(ctx: Context, config: any): 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, config) {
5
5
  const logger = ctx.logger('githubsth');
6
6
  const repoRegex = /^[\w-]+\/[\w-\.]+$/;
7
7
  const validEvents = [
@@ -9,11 +9,12 @@ function apply(ctx) {
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'];
12
13
  ctx.command('githubsth.subscribe <repo> [events:text]', '订阅 GitHub 仓库')
13
14
  .alias('gh.sub')
14
15
  .usage(`
15
16
  订阅 GitHub 仓库通知。
16
- 如果不指定事件,默认订阅: push, issues, issue_comment, pull_request, pull_request_review, release, star, fork
17
+ 如果不指定事件,默认订阅: ${defaultConfigEvents.join(', ')}
17
18
 
18
19
  可选事件:
19
20
  - push: 代码推送
@@ -58,7 +59,7 @@ gh.sub koishijs/koishi push,issues,star
58
59
  }
59
60
  else {
60
61
  // Default events
61
- events = ['push', 'issues', 'issue_comment', 'pull_request', 'pull_request_review', 'release', 'star', 'fork'];
62
+ events = [...defaultConfigEvents];
62
63
  }
63
64
  try {
64
65
  // Check if subscription exists
package/lib/config.d.ts CHANGED
@@ -9,6 +9,8 @@ export interface Config {
9
9
  defaultOwner?: string;
10
10
  defaultRepo?: string;
11
11
  debug: boolean;
12
+ logUnhandledEvents: boolean;
13
+ defaultEvents: string[];
12
14
  rules?: Rule[];
13
15
  }
14
16
  export declare const Config: Schema<Config>;
package/lib/config.js CHANGED
@@ -6,6 +6,10 @@ 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('默认订阅事件列表 (当不指定事件时使用)'),
9
13
  rules: koishi_1.Schema.array(koishi_1.Schema.object({
10
14
  repo: koishi_1.Schema.string().required(),
11
15
  channelId: koishi_1.Schema.string().required(),
@@ -8,39 +8,40 @@ class Formatter extends koishi_1.Service {
8
8
  ctx.logger('githubsth').info('Formatter service initialized');
9
9
  }
10
10
  formatPush(payload) {
11
- const { repository, pusher, commits, compare } = payload;
11
+ const { repository, pusher, commits, compare, ref } = payload;
12
12
  if (!commits || commits.length === 0)
13
13
  return null;
14
14
  const commitLines = commits.map((c) => {
15
- const shortHash = c.id.substring(0, 7);
16
- const message = c.message.split('\n')[0];
17
- return `[${shortHash}] ${message} - ${c.author.name}`;
15
+ const shortHash = c.id?.substring(0, 7) || '???????';
16
+ const message = c.message?.split('\n')[0] || 'No message';
17
+ const author = c.author?.name || 'Unknown';
18
+ return `[${shortHash}] ${message} - ${author}`;
18
19
  }).join('\n');
19
20
  return (0, koishi_1.h)('message', [
20
- (0, koishi_1.h)('text', { content: `[GitHub] ${repository.full_name} 收到新的推送\n` }),
21
- (0, koishi_1.h)('text', { content: `提交者: ${pusher.name}\n` }),
22
- (0, koishi_1.h)('text', { content: `分支: ${payload.ref.replace('refs/heads/', '')}\n` }),
23
- (0, koishi_1.h)('text', { content: `详情: ${compare}\n` }),
21
+ (0, koishi_1.h)('text', { content: `[GitHub] ${repository?.full_name || 'Unknown Repo'} 收到新的推送\n` }),
22
+ (0, koishi_1.h)('text', { content: `提交者: ${pusher?.name || 'Unknown'}\n` }),
23
+ (0, koishi_1.h)('text', { content: `分支: ${ref ? ref.replace('refs/heads/', '') : 'unknown'}\n` }),
24
+ (0, koishi_1.h)('text', { content: `详情: ${compare || ''}\n` }),
24
25
  (0, koishi_1.h)('text', { content: commitLines })
25
26
  ]);
26
27
  }
27
28
  formatIssue(payload) {
28
29
  const { action, issue, repository, sender } = payload;
29
30
  return (0, koishi_1.h)('message', [
30
- (0, koishi_1.h)('text', { content: `[GitHub] ${repository.full_name} Issue ${action}\n` }),
31
- (0, koishi_1.h)('text', { content: `标题: #${issue.number} ${issue.title}\n` }),
32
- (0, koishi_1.h)('text', { content: `发起人: ${sender.login}\n` }),
33
- (0, koishi_1.h)('text', { content: `链接: ${issue.html_url}` })
31
+ (0, koishi_1.h)('text', { content: `[GitHub] ${repository?.full_name || 'Unknown Repo'} Issue ${action || 'updated'}\n` }),
32
+ (0, koishi_1.h)('text', { content: `标题: #${issue?.number || '?'} ${issue?.title || 'No Title'}\n` }),
33
+ (0, koishi_1.h)('text', { content: `发起人: ${sender?.login || 'Unknown'}\n` }),
34
+ (0, koishi_1.h)('text', { content: `链接: ${issue?.html_url || ''}` })
34
35
  ]);
35
36
  }
36
37
  formatPullRequest(payload) {
37
38
  const { action, pull_request, repository, sender } = payload;
38
39
  return (0, koishi_1.h)('message', [
39
- (0, koishi_1.h)('text', { content: `[GitHub] ${repository.full_name} PR ${action}\n` }),
40
- (0, koishi_1.h)('text', { content: `标题: #${pull_request.number} ${pull_request.title}\n` }),
41
- (0, koishi_1.h)('text', { content: `发起人: ${sender.login}\n` }),
42
- (0, koishi_1.h)('text', { content: `状态: ${pull_request.state}\n` }),
43
- (0, koishi_1.h)('text', { content: `链接: ${pull_request.html_url}` })
40
+ (0, koishi_1.h)('text', { content: `[GitHub] ${repository?.full_name || 'Unknown Repo'} PR ${action || 'updated'}\n` }),
41
+ (0, koishi_1.h)('text', { content: `标题: #${pull_request?.number || '?'} ${pull_request?.title || 'No Title'}\n` }),
42
+ (0, koishi_1.h)('text', { content: `发起人: ${sender?.login || 'Unknown'}\n` }),
43
+ (0, koishi_1.h)('text', { content: `状态: ${pull_request?.state || 'unknown'}\n` }),
44
+ (0, koishi_1.h)('text', { content: `链接: ${pull_request?.html_url || ''}` })
44
45
  ]);
45
46
  }
46
47
  formatStar(payload) {
@@ -48,15 +49,15 @@ class Formatter extends koishi_1.Service {
48
49
  if (action !== 'created')
49
50
  return null;
50
51
  return (0, koishi_1.h)('message', [
51
- (0, koishi_1.h)('text', { content: `[GitHub] ${sender.login} Star 了仓库 ${repository.full_name} 🌟\n` }),
52
- (0, koishi_1.h)('text', { content: `当前 Star 数: ${repository.stargazers_count}` })
52
+ (0, koishi_1.h)('text', { content: `[GitHub] ${sender?.login || 'Unknown'} Star 了仓库 ${repository?.full_name || 'Unknown Repo'} 🌟\n` }),
53
+ (0, koishi_1.h)('text', { content: `当前 Star 数: ${repository?.stargazers_count !== undefined ? repository.stargazers_count : '?'}` })
53
54
  ]);
54
55
  }
55
56
  formatFork(payload) {
56
57
  const { forkee, repository, sender } = payload;
57
58
  return (0, koishi_1.h)('message', [
58
- (0, koishi_1.h)('text', { content: `[GitHub] ${sender.login} Fork 了仓库 ${repository.full_name}\n` }),
59
- (0, koishi_1.h)('text', { content: `新仓库: ${forkee.full_name}` })
59
+ (0, koishi_1.h)('text', { content: `[GitHub] ${sender?.login || 'Unknown'} Fork 了仓库 ${repository?.full_name || 'Unknown Repo'}\n` }),
60
+ (0, koishi_1.h)('text', { content: `新仓库: ${forkee?.full_name || 'Unknown'}` })
60
61
  ]);
61
62
  }
62
63
  formatRelease(payload) {
@@ -64,43 +65,45 @@ class Formatter extends koishi_1.Service {
64
65
  if (action !== 'published')
65
66
  return null;
66
67
  return (0, koishi_1.h)('message', [
67
- (0, koishi_1.h)('text', { content: `[GitHub] ${repository.full_name} 发布了新版本 ${release.tag_name} 🎉\n` }),
68
- (0, koishi_1.h)('text', { content: `标题: ${release.name}\n` }),
69
- (0, koishi_1.h)('text', { content: `链接: ${release.html_url}` })
68
+ (0, koishi_1.h)('text', { content: `[GitHub] ${repository?.full_name || 'Unknown Repo'} 发布了新版本 ${release?.tag_name || 'unknown'} 🎉\n` }),
69
+ (0, koishi_1.h)('text', { content: `标题: ${release?.name || 'No Title'}\n` }),
70
+ (0, koishi_1.h)('text', { content: `链接: ${release?.html_url || ''}` })
70
71
  ]);
71
72
  }
72
73
  formatDiscussion(payload) {
73
74
  const { action, discussion, repository, sender } = payload;
74
75
  return (0, koishi_1.h)('message', [
75
- (0, koishi_1.h)('text', { content: `[GitHub] ${repository.full_name} Discussion ${action}\n` }),
76
- (0, koishi_1.h)('text', { content: `标题: #${discussion.number} ${discussion.title}\n` }),
77
- (0, koishi_1.h)('text', { content: `发起人: ${sender.login}\n` }),
78
- (0, koishi_1.h)('text', { content: `链接: ${discussion.html_url}` })
76
+ (0, koishi_1.h)('text', { content: `[GitHub] ${repository?.full_name || 'Unknown Repo'} Discussion ${action || 'updated'}\n` }),
77
+ (0, koishi_1.h)('text', { content: `标题: #${discussion?.number || '?'} ${discussion?.title || 'No Title'}\n` }),
78
+ (0, koishi_1.h)('text', { content: `发起人: ${sender?.login || 'Unknown'}\n` }),
79
+ (0, koishi_1.h)('text', { content: `链接: ${discussion?.html_url || ''}` })
79
80
  ]);
80
81
  }
81
82
  formatWorkflowRun(payload) {
82
83
  const { action, workflow_run, repository } = payload;
83
84
  if (action !== 'completed')
84
85
  return null;
85
- const statusIcon = workflow_run.conclusion === 'success' ? '✅' : '❌';
86
+ const statusIcon = workflow_run?.conclusion === 'success' ? '✅' : '❌';
86
87
  return (0, koishi_1.h)('message', [
87
- (0, koishi_1.h)('text', { content: `[GitHub] ${repository.full_name} 工作流运行完成 ${statusIcon}\n` }),
88
- (0, koishi_1.h)('text', { content: `工作流: ${workflow_run.name}\n` }),
89
- (0, koishi_1.h)('text', { content: `结果: ${workflow_run.conclusion}\n` }),
90
- (0, koishi_1.h)('text', { content: `分支: ${workflow_run.head_branch}\n` }),
91
- (0, koishi_1.h)('text', { content: `链接: ${workflow_run.html_url}` })
88
+ (0, koishi_1.h)('text', { content: `[GitHub] ${repository?.full_name || 'Unknown Repo'} 工作流运行完成 ${statusIcon}\n` }),
89
+ (0, koishi_1.h)('text', { content: `工作流: ${workflow_run?.name || 'Unknown'}\n` }),
90
+ (0, koishi_1.h)('text', { content: `结果: ${workflow_run?.conclusion || 'unknown'}\n` }),
91
+ (0, koishi_1.h)('text', { content: `分支: ${workflow_run?.head_branch || 'unknown'}\n` }),
92
+ (0, koishi_1.h)('text', { content: `链接: ${workflow_run?.html_url || ''}` })
92
93
  ]);
93
94
  }
94
95
  formatIssueComment(payload) {
95
96
  const { action, issue, comment, repository, sender } = payload;
96
97
  if (action !== 'created')
97
98
  return null;
99
+ const body = comment?.body || '';
100
+ const shortBody = body.length > 100 ? body.substring(0, 100) + '...' : body;
98
101
  return (0, koishi_1.h)('message', [
99
- (0, koishi_1.h)('text', { content: `[GitHub] ${repository.full_name} Issue 收到新评论\n` }),
100
- (0, koishi_1.h)('text', { content: `标题: #${issue.number} ${issue.title}\n` }),
101
- (0, koishi_1.h)('text', { content: `评论人: ${sender.login}\n` }),
102
- (0, koishi_1.h)('text', { content: `内容: ${comment.body.substring(0, 100)}${comment.body.length > 100 ? '...' : ''}\n` }),
103
- (0, koishi_1.h)('text', { content: `链接: ${comment.html_url}` })
102
+ (0, koishi_1.h)('text', { content: `[GitHub] ${repository?.full_name || 'Unknown Repo'} Issue 收到新评论\n` }),
103
+ (0, koishi_1.h)('text', { content: `标题: #${issue?.number || '?'} ${issue?.title || 'No Title'}\n` }),
104
+ (0, koishi_1.h)('text', { content: `评论人: ${sender?.login || 'Unknown'}\n` }),
105
+ (0, koishi_1.h)('text', { content: `内容: ${shortBody}\n` }),
106
+ (0, koishi_1.h)('text', { content: `链接: ${comment?.html_url || ''}` })
104
107
  ]);
105
108
  }
106
109
  formatPullRequestReview(payload) {
@@ -108,11 +111,11 @@ class Formatter extends koishi_1.Service {
108
111
  if (action !== 'submitted')
109
112
  return null;
110
113
  return (0, koishi_1.h)('message', [
111
- (0, koishi_1.h)('text', { content: `[GitHub] ${repository.full_name} PR 收到 Review\n` }),
112
- (0, koishi_1.h)('text', { content: `标题: #${pull_request.number} ${pull_request.title}\n` }),
113
- (0, koishi_1.h)('text', { content: `Reviewer: ${sender.login}\n` }),
114
- (0, koishi_1.h)('text', { content: `状态: ${review.state}\n` }),
115
- (0, koishi_1.h)('text', { content: `链接: ${review.html_url}` })
114
+ (0, koishi_1.h)('text', { content: `[GitHub] ${repository?.full_name || 'Unknown Repo'} PR 收到 Review\n` }),
115
+ (0, koishi_1.h)('text', { content: `标题: #${pull_request?.number || '?'} ${pull_request?.title || 'No Title'}\n` }),
116
+ (0, koishi_1.h)('text', { content: `Reviewer: ${sender?.login || 'Unknown'}\n` }),
117
+ (0, koishi_1.h)('text', { content: `状态: ${review?.state || 'unknown'}\n` }),
118
+ (0, koishi_1.h)('text', { content: `链接: ${review?.html_url || ''}` })
116
119
  ]);
117
120
  }
118
121
  }
@@ -10,5 +10,6 @@ export declare class Notifier extends Service {
10
10
  constructor(ctx: Context, config: Config);
11
11
  private registerListeners;
12
12
  private handleEvent;
13
+ private patchPayloadForEvent;
13
14
  private sendMessage;
14
15
  }
@@ -34,31 +34,40 @@ class Notifier extends koishi_1.Service {
34
34
  if (this.config.debug) {
35
35
  this.ctx.logger('githubsth').info('Found payload in session, attempting to handle');
36
36
  }
37
+ // Check if payload is wrapped (adapter-github structure)
38
+ const realPayload = payload.payload || payload;
37
39
  // Infer event type
38
40
  let eventType = 'unknown';
39
- if (payload.issue && payload.comment)
41
+ // Check inner payload first if it exists
42
+ if (realPayload.issue && realPayload.comment)
40
43
  eventType = 'issue_comment';
41
- else if (payload.issue)
44
+ else if (realPayload.issue)
42
45
  eventType = 'issues';
43
- else if (payload.pull_request && payload.review)
46
+ else if (realPayload.pull_request && realPayload.review)
44
47
  eventType = 'pull_request_review';
45
- else if (payload.pull_request)
48
+ else if (realPayload.pull_request)
46
49
  eventType = 'pull_request';
47
- else if (payload.commits)
50
+ else if (realPayload.commits)
48
51
  eventType = 'push';
49
- else if (payload.starred_at !== undefined || (payload.action === 'started'))
52
+ else if (realPayload.starred_at !== undefined || (realPayload.action === 'started'))
50
53
  eventType = 'star'; // star event usually has action 'created' but check payload structure
51
- else if (payload.forkee)
54
+ else if (realPayload.forkee)
52
55
  eventType = 'fork';
53
- else if (payload.release)
56
+ else if (realPayload.release)
54
57
  eventType = 'release';
55
- else if (payload.discussion)
58
+ else if (realPayload.discussion)
56
59
  eventType = 'discussion';
57
- else if (payload.workflow_run)
60
+ else if (realPayload.workflow_run)
58
61
  eventType = 'workflow_run';
62
+ // Handle raw star event if it has repository info directly
63
+ else if (realPayload.repository && (realPayload.action === 'created' || realPayload.action === 'started'))
64
+ eventType = 'star';
59
65
  if (eventType !== 'unknown') {
60
66
  this.handleEvent(eventType, payload);
61
67
  }
68
+ else if (this.config.logUnhandledEvents) {
69
+ this.ctx.logger('githubsth').info(`Unhandled payload structure. Keys: ${Object.keys(realPayload).join(', ')}`);
70
+ }
62
71
  }
63
72
  });
64
73
  }
@@ -69,6 +78,14 @@ class Notifier extends koishi_1.Service {
69
78
  // Check if payload is nested in an 'event' object (common in some adapter versions)
70
79
  // or if the event data is directly in payload
71
80
  const realPayload = payload.payload || payload;
81
+ // Extract sender from wrapper if available (adapter-github often puts it in 'actor')
82
+ if (payload.actor && !realPayload.sender) {
83
+ realPayload.sender = payload.actor;
84
+ }
85
+ // Extract repository from wrapper if available
86
+ if (payload.repository && !realPayload.repository) {
87
+ realPayload.repository = payload.repository;
88
+ }
72
89
  let repoName = realPayload.repository?.full_name;
73
90
  // Try to fallback if repoName is missing
74
91
  if (!repoName && realPayload.issue?.repository_url) {
@@ -80,17 +97,57 @@ class Notifier extends koishi_1.Service {
80
97
  if (!repoName && realPayload.pull_request?.base?.repo?.full_name) {
81
98
  repoName = realPayload.pull_request.base.repo.full_name;
82
99
  }
100
+ // Special handling for 'star' event (which might be 'watch' event with action 'started')
101
+ // The payload might be missing repository info in the main object but have it in the original session payload
102
+ if (!repoName && event === 'star') {
103
+ // Sometimes the repository info is at the root of the payload, not inside 'payload' property
104
+ if (payload.repository?.full_name) {
105
+ repoName = payload.repository.full_name;
106
+ }
107
+ }
83
108
  if (!repoName) {
84
109
  if (this.config.debug) {
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(', ')}`);
110
+ this.ctx.logger('githubsth').warn(`Missing repo info for event: ${event}. Keys: ${Object.keys(realPayload).join(', ')}`);
87
111
  }
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
112
+ else if (this.config.logUnhandledEvents) {
113
+ // Log at warning level if repo info is missing and logUnhandledEvents is on
91
114
  this.ctx.logger('githubsth').warn(`Missing repo info for event: ${event}. Keys: ${Object.keys(realPayload).join(', ')}`);
92
115
  }
93
- return;
116
+ // Do not return here, let patching logic handle it with defaultRepo
117
+ }
118
+ // Patch realPayload with extracted repo info if missing
119
+ // This is crucial for formatter to work correctly as it expects repository object
120
+ if (!realPayload.repository) {
121
+ realPayload.repository = { full_name: repoName || 'Unknown/Repo' };
122
+ }
123
+ else if (!realPayload.repository.full_name) {
124
+ realPayload.repository.full_name = repoName || 'Unknown/Repo';
125
+ }
126
+ // Patch realPayload with sender info if missing (e.g. issues event)
127
+ if (!realPayload.sender) {
128
+ if (realPayload.issue?.user) {
129
+ realPayload.sender = realPayload.issue.user;
130
+ }
131
+ else if (realPayload.pull_request?.user) {
132
+ realPayload.sender = realPayload.pull_request.user;
133
+ }
134
+ else if (realPayload.discussion?.user) {
135
+ realPayload.sender = realPayload.discussion.user;
136
+ }
137
+ else if (realPayload.pusher) {
138
+ realPayload.sender = { login: realPayload.pusher.name || 'Pusher' };
139
+ }
140
+ else {
141
+ // Fallback sender
142
+ realPayload.sender = { login: 'GitHub' };
143
+ }
144
+ }
145
+ // Comprehensive patching for specific events to prevent formatter crashes
146
+ try {
147
+ this.patchPayloadForEvent(event, realPayload, repoName || 'Unknown/Repo');
148
+ }
149
+ catch (e) {
150
+ this.ctx.logger('githubsth').warn(`Failed to patch payload for ${event}:`, e);
94
151
  }
95
152
  if (this.config.debug) {
96
153
  this.ctx.logger('githubsth').info(`Processing event ${event} for ${repoName}`);
@@ -99,6 +156,12 @@ class Notifier extends koishi_1.Service {
99
156
  }
100
157
  // Get rules from database
101
158
  // Try to match both exact name and lowercase name to handle case sensitivity
159
+ // If repoName is missing (undefined), we can't query rules effectively by repo name
160
+ // But we might want to support global subscriptions or handle it gracefully
161
+ if (!repoName) {
162
+ this.ctx.logger('githubsth').warn('Cannot query rules: repoName is missing');
163
+ return;
164
+ }
102
165
  const repoNames = [repoName];
103
166
  if (repoName !== repoName.toLowerCase()) {
104
167
  repoNames.push(repoName.toLowerCase());
@@ -126,6 +189,9 @@ class Notifier extends koishi_1.Service {
126
189
  this.ctx.logger('githubsth').info(`No matching rules for ${repoName} (event: ${event})`);
127
190
  this.ctx.logger('notifier').debug(`No matching rules for ${repoName} (event: ${event})`);
128
191
  }
192
+ else if (this.config.logUnhandledEvents) {
193
+ this.ctx.logger('githubsth').warn(`No matching rules for ${repoName} (event: ${event})`);
194
+ }
129
195
  return;
130
196
  }
131
197
  if (this.config.debug) {
@@ -138,50 +204,160 @@ class Notifier extends koishi_1.Service {
138
204
  this.ctx.logger('notifier').warn('Formatter service not available');
139
205
  return;
140
206
  }
207
+ try {
208
+ switch (event) {
209
+ case 'push':
210
+ message = this.ctx.githubsthFormatter.formatPush(realPayload);
211
+ break;
212
+ case 'issues':
213
+ message = this.ctx.githubsthFormatter.formatIssue(realPayload);
214
+ break;
215
+ case 'pull_request':
216
+ message = this.ctx.githubsthFormatter.formatPullRequest(realPayload);
217
+ break;
218
+ case 'star':
219
+ message = this.ctx.githubsthFormatter.formatStar(realPayload);
220
+ break;
221
+ case 'fork':
222
+ message = this.ctx.githubsthFormatter.formatFork(realPayload);
223
+ break;
224
+ case 'release':
225
+ message = this.ctx.githubsthFormatter.formatRelease(realPayload);
226
+ break;
227
+ case 'discussion':
228
+ message = this.ctx.githubsthFormatter.formatDiscussion(realPayload);
229
+ break;
230
+ case 'workflow_run':
231
+ message = this.ctx.githubsthFormatter.formatWorkflowRun(realPayload);
232
+ break;
233
+ case 'issue_comment':
234
+ message = this.ctx.githubsthFormatter.formatIssueComment(realPayload);
235
+ break;
236
+ case 'pull_request_review':
237
+ message = this.ctx.githubsthFormatter.formatPullRequestReview(realPayload);
238
+ break;
239
+ }
240
+ }
241
+ catch (e) {
242
+ this.ctx.logger('githubsth').error(`Error formatting event ${event}:`, e);
243
+ if (this.config.debug) {
244
+ this.ctx.logger('notifier').error(`Error formatting event ${event}:`, e);
245
+ }
246
+ return;
247
+ }
248
+ if (!message) {
249
+ if (this.config.debug) {
250
+ this.ctx.logger('notifier').debug(`Formatter returned null for event ${event}`);
251
+ }
252
+ return;
253
+ }
254
+ for (const rule of matchedRules) {
255
+ if (this.config.debug) {
256
+ this.ctx.logger('notifier').debug(`Sending message to channel ${rule.channelId} (platform: ${rule.platform || 'any'})`);
257
+ }
258
+ await this.sendMessage(rule, message);
259
+ }
260
+ }
261
+ patchPayloadForEvent(event, payload, repoName) {
262
+ // Ensure sender exists (handled before, but good for type safety)
263
+ const defaultUser = { login: 'GitHub', id: 0, avatar_url: '' };
264
+ if (!payload.sender)
265
+ payload.sender = defaultUser;
266
+ // Ensure repository exists (handled before, but good for type safety)
267
+ const defaultRepo = { full_name: repoName, stargazers_count: 0, html_url: `https://github.com/${repoName}` };
268
+ if (!payload.repository)
269
+ payload.repository = defaultRepo;
141
270
  switch (event) {
142
271
  case 'push':
143
- message = this.ctx.githubsthFormatter.formatPush(realPayload);
272
+ if (!payload.pusher)
273
+ payload.pusher = { name: payload.sender.login };
274
+ if (!payload.commits)
275
+ payload.commits = [];
276
+ if (!payload.ref)
277
+ payload.ref = 'refs/heads/unknown';
278
+ if (!payload.compare)
279
+ payload.compare = '';
280
+ // Ensure author exists in commits
281
+ if (payload.commits.length > 0) {
282
+ payload.commits.forEach((c) => {
283
+ if (!c.author)
284
+ c.author = { name: 'Unknown' };
285
+ if (!c.id)
286
+ c.id = '0000000';
287
+ if (!c.message)
288
+ c.message = 'No message';
289
+ });
290
+ }
144
291
  break;
145
292
  case 'issues':
146
- message = this.ctx.githubsthFormatter.formatIssue(realPayload);
293
+ if (!payload.action)
294
+ payload.action = 'updated';
295
+ if (!payload.issue)
296
+ payload.issue = { number: 0, title: 'Unknown Issue', html_url: '', user: payload.sender };
297
+ // Ensure user exists in issue
298
+ if (!payload.issue.user)
299
+ payload.issue.user = payload.sender;
147
300
  break;
148
301
  case 'pull_request':
149
- message = this.ctx.githubsthFormatter.formatPullRequest(realPayload);
302
+ if (!payload.action)
303
+ payload.action = 'updated';
304
+ if (!payload.pull_request)
305
+ payload.pull_request = { number: 0, title: 'Unknown PR', state: 'unknown', html_url: '', user: payload.sender };
306
+ if (!payload.pull_request.user)
307
+ payload.pull_request.user = payload.sender;
150
308
  break;
151
309
  case 'star':
152
- message = this.ctx.githubsthFormatter.formatStar(realPayload);
310
+ if (!payload.action)
311
+ payload.action = 'created';
312
+ if (payload.repository && payload.repository.stargazers_count === undefined) {
313
+ payload.repository.stargazers_count = '?';
314
+ }
153
315
  break;
154
316
  case 'fork':
155
- message = this.ctx.githubsthFormatter.formatFork(realPayload);
317
+ if (!payload.forkee)
318
+ payload.forkee = { full_name: 'unknown/fork' };
156
319
  break;
157
320
  case 'release':
158
- message = this.ctx.githubsthFormatter.formatRelease(realPayload);
321
+ if (!payload.action)
322
+ payload.action = 'published';
323
+ if (!payload.release)
324
+ payload.release = { tag_name: 'unknown', name: 'Unknown Release', html_url: '' };
159
325
  break;
160
326
  case 'discussion':
161
- message = this.ctx.githubsthFormatter.formatDiscussion(realPayload);
327
+ if (!payload.action)
328
+ payload.action = 'updated';
329
+ if (!payload.discussion)
330
+ payload.discussion = { number: 0, title: 'Unknown Discussion', html_url: '', user: payload.sender };
331
+ if (!payload.discussion.user)
332
+ payload.discussion.user = payload.sender;
162
333
  break;
163
334
  case 'workflow_run':
164
- message = this.ctx.githubsthFormatter.formatWorkflowRun(realPayload);
335
+ if (!payload.action)
336
+ payload.action = 'completed';
337
+ if (!payload.workflow_run)
338
+ payload.workflow_run = { conclusion: 'unknown', name: 'Unknown Workflow', head_branch: 'unknown', html_url: '' };
165
339
  break;
166
340
  case 'issue_comment':
167
- message = this.ctx.githubsthFormatter.formatIssueComment(realPayload);
341
+ if (!payload.action)
342
+ payload.action = 'created';
343
+ if (!payload.issue)
344
+ payload.issue = { number: 0, title: 'Unknown Issue', html_url: '', user: payload.sender };
345
+ if (!payload.comment)
346
+ payload.comment = { body: '', html_url: '' };
347
+ if (!payload.issue.user)
348
+ payload.issue.user = payload.sender;
168
349
  break;
169
350
  case 'pull_request_review':
170
- message = this.ctx.githubsthFormatter.formatPullRequestReview(realPayload);
351
+ if (!payload.action)
352
+ payload.action = 'submitted';
353
+ if (!payload.pull_request)
354
+ payload.pull_request = { number: 0, title: 'Unknown PR', html_url: '', user: payload.sender };
355
+ if (!payload.review)
356
+ payload.review = { state: 'unknown', html_url: '' };
357
+ if (!payload.pull_request.user)
358
+ payload.pull_request.user = payload.sender;
171
359
  break;
172
360
  }
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
- }
185
361
  }
186
362
  async sendMessage(rule, message) {
187
363
  // Find suitable bots
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "koishi-plugin-githubsth",
3
- "version": "1.0.2-test2",
3
+ "version": "1.0.2",
4
4
  "description": "Github Subscriptions Notifications, push notifications for GitHub subscriptions For koishi",
5
5
  "main": "lib/index.js",
6
6
  "typings": "lib/index.d.ts",