koishi-plugin-githubsth 1.0.5-alpha.1 → 1.0.5-alpha.3

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.
@@ -0,0 +1,198 @@
1
+ <!doctype html>
2
+ <html>
3
+ <head>
4
+ <meta charset="utf-8" />
5
+ <style>
6
+ * { box-sizing: border-box; }
7
+ body {
8
+ margin: 0;
9
+ width: {{width}}px;
10
+ background: {{background}};
11
+ color: {{text}};
12
+ font-family: {{font}};
13
+ --status-accent: {{statusAccent}};
14
+ }
15
+ .wrap { padding: 18px; position: relative; }
16
+ .wrap::before {
17
+ content: '';
18
+ position: absolute;
19
+ inset: 0;
20
+ pointer-events: none;
21
+ background: {{overlayPattern}};
22
+ opacity: 0.5;
23
+ }
24
+ .card {
25
+ position: relative;
26
+ border-radius: 14px;
27
+ border: 1px solid {{border}};
28
+ background: {{card}};
29
+ overflow: hidden;
30
+ box-shadow: 0 12px 34px rgba(0, 0, 0, 0.28);
31
+ z-index: 1;
32
+ }
33
+ .card::before {
34
+ content: '';
35
+ position: absolute;
36
+ inset: 0;
37
+ pointer-events: none;
38
+ background: {{cardTexture}};
39
+ opacity: 0.28;
40
+ }
41
+ .status-bar {
42
+ position: relative;
43
+ z-index: 2;
44
+ height: 3px;
45
+ background: linear-gradient(90deg, var(--status-accent), transparent);
46
+ }
47
+ .head {
48
+ position: relative;
49
+ z-index: 1;
50
+ padding: 12px 16px;
51
+ border-bottom: 1px solid {{border}};
52
+ display: flex;
53
+ align-items: center;
54
+ justify-content: space-between;
55
+ gap: 12px;
56
+ }
57
+ .head-left { display: flex; align-items: center; gap: 8px; min-width: 0; }
58
+ .dot { width: 10px; height: 10px; border-radius: 999px; background: var(--status-accent); box-shadow: 0 0 0 3px {{pillBg}}; }
59
+ .repo { font-weight: 700; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; letter-spacing: 0.2px; }
60
+ .meta { color: {{muted}}; font-size: 12px; letter-spacing: 0.3px; }
61
+ .type-badge {
62
+ display: inline-flex;
63
+ align-items: center;
64
+ padding: 2px 8px;
65
+ border-radius: 999px;
66
+ font-size: 11px;
67
+ border: 1px solid {{border}};
68
+ color: var(--status-accent);
69
+ margin-right: 8px;
70
+ }
71
+ .pill {
72
+ display: inline-flex;
73
+ align-items: center;
74
+ padding: 2px 9px;
75
+ border-radius: 999px;
76
+ font-size: 12px;
77
+ border: 1px solid {{border}};
78
+ color: {{pillText}};
79
+ background: {{pillBg}};
80
+ margin-left: 6px;
81
+ }
82
+ .body { position: relative; z-index: 1; padding: 14px 16px 16px; }
83
+ .title { font-size: 18px; font-weight: 700; margin-bottom: 6px; letter-spacing: 0.2px; }
84
+ .sub { color: {{muted}}; margin-bottom: 12px; }
85
+ .content {
86
+ border: 1px dashed {{border}};
87
+ border-radius: 10px;
88
+ padding: 10px 12px;
89
+ line-height: 1.62;
90
+ font-size: 14px;
91
+ word-break: break-word;
92
+ margin-top: 8px;
93
+ background: {{contentBg}};
94
+ }
95
+ .detail-grid {
96
+ margin-top: 10px;
97
+ border: 1px solid {{border}};
98
+ border-radius: 10px;
99
+ background: {{contentBg}};
100
+ overflow: hidden;
101
+ }
102
+ .detail-row {
103
+ display: grid;
104
+ grid-template-columns: 120px 1fr;
105
+ gap: 10px;
106
+ padding: 8px 10px;
107
+ border-bottom: 1px solid {{border}};
108
+ font-size: 13px;
109
+ }
110
+ .detail-row:last-child { border-bottom: 0; }
111
+ .detail-key { color: {{muted}}; }
112
+ .detail-value { word-break: break-word; }
113
+ .commit-list { margin-top: 10px; border-top: 1px solid {{border}}; padding-top: 10px; }
114
+ .commit-item { display: flex; align-items: baseline; gap: 6px; font-size: 13px; margin-bottom: 6px; }
115
+ .commit-icon { width: 22px; text-align: center; color: var(--status-accent); }
116
+ .commit-hash {
117
+ display: inline-block;
118
+ padding: 1px 6px;
119
+ border-radius: 8px;
120
+ border: 1px solid {{border}};
121
+ background: {{pillBg}};
122
+ color: {{pillText}};
123
+ font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
124
+ }
125
+ .commit-author { color: {{muted}}; }
126
+ .digest-list {
127
+ margin-top: 10px;
128
+ border: 1px solid {{border}};
129
+ border-radius: 10px;
130
+ background: {{contentBg}};
131
+ overflow: hidden;
132
+ }
133
+ .digest-item {
134
+ display: grid;
135
+ grid-template-columns: 90px 1fr;
136
+ gap: 10px;
137
+ padding: 8px 10px;
138
+ border-bottom: 1px solid {{border}};
139
+ font-size: 13px;
140
+ }
141
+ .digest-item:last-child { border-bottom: 0; }
142
+ .digest-event { color: var(--status-accent); font-weight: 600; }
143
+
144
+ body.style-glass .card { border-radius: 18px; backdrop-filter: blur(6px); box-shadow: 0 20px 50px rgba(0,0,0,0.35); }
145
+ body.style-glass .head { background: linear-gradient(180deg, rgba(255,255,255,0.08), rgba(255,255,255,0)); }
146
+
147
+ body.style-neon .card { border-width: 2px; box-shadow: 0 0 0 1px var(--status-accent) inset, 0 0 28px rgba(0,0,0,0.38); }
148
+ body.style-neon .dot { box-shadow: 0 0 0 4px {{pillBg}}, 0 0 14px var(--status-accent); }
149
+ body.style-neon .title { text-transform: uppercase; letter-spacing: 0.8px; }
150
+
151
+ body.style-compact .wrap { padding: 10px; }
152
+ body.style-compact .head { padding: 10px 12px; }
153
+ body.style-compact .body { padding: 10px 12px 12px; }
154
+ body.style-compact .title { font-size: 16px; }
155
+ body.style-compact .content { font-size: 13px; }
156
+ body.style-compact .detail-row { grid-template-columns: 90px 1fr; padding: 6px 8px; }
157
+
158
+ body.style-card .card { border-radius: 24px; box-shadow: 0 26px 54px rgba(0,0,0,0.35); }
159
+ body.style-card .head { border-bottom-style: dashed; }
160
+ body.style-card .content { border-style: solid; }
161
+
162
+ body.style-terminal .card { border-radius: 0; border-width: 2px; box-shadow: none; }
163
+ body.style-terminal .head,
164
+ body.style-terminal .content,
165
+ body.style-terminal .pill,
166
+ body.style-terminal .commit-hash,
167
+ body.style-terminal .detail-grid,
168
+ body.style-terminal .digest-list { border-radius: 0; }
169
+ body.style-terminal .title { font-size: 17px; letter-spacing: 0.5px; }
170
+
171
+ body.style-github .repo::before { content: '#'; margin-right: 4px; color: {{muted}}; }
172
+
173
+ {{extraCss}}
174
+ </style>
175
+ </head>
176
+ <body class="theme-{{themeKey}} style-{{styleVariant}}">
177
+ <div class="wrap">
178
+ <div class="card">
179
+ <div class="status-bar"></div>
180
+ <div class="head">
181
+ <div class="head-left">
182
+ <span class="dot"></span>
183
+ <span class="repo">{{repo}}</span>
184
+ </div>
185
+ <div class="meta">{{themeTitle}}</div>
186
+ </div>
187
+ <div class="body">
188
+ <div class="title"><span class="type-badge">{{typeLabel}}</span>{{eventTitle}}</div>
189
+ <div class="sub">by {{actor}}{{actionPill}}{{statusPills}}</div>
190
+ <div class="content">{{content}}</div>
191
+ {{detailBlock}}
192
+ {{commitBlock}}
193
+ {{digestBlock}}
194
+ </div>
195
+ </div>
196
+ </div>
197
+ </body>
198
+ </html>
@@ -2,104 +2,176 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.apply = apply;
4
4
  const modes = ['text', 'image', 'auto'];
5
- const events = ['push', 'issues', 'issue_comment', 'pull_request', 'pull_request_review', 'star', 'fork', 'release', 'discussion', 'workflow_run'];
5
+ const events = ['push', 'issues', 'issue_comment', 'pull_request', 'pull_request_review', 'star', 'fork', 'release', 'discussion', 'workflow_run', 'digest'];
6
6
  function apply(ctx, config) {
7
- ctx.command('githubsth.render', '通知渲染设置(文本/图片)', { authority: 3 })
8
- .alias('gh.render');
9
- ctx.command('githubsth.render.status', '查看渲染状态', { authority: 3 })
7
+ ctx.command('githubsth.render', 'Render settings', { authority: 3 }).alias('gh.render');
8
+ ctx.command('githubsth.render.status', 'Show render status', { authority: 3 })
10
9
  .action(async () => {
11
10
  const status = ctx.githubsthNotifier.getRenderStatus();
12
11
  return [
13
12
  `mode: ${status.mode} (configured: ${status.configuredMode})`,
14
13
  `fallback: ${status.fallback}`,
15
14
  `theme(default): ${status.theme}`,
15
+ `style(default): ${status.style}`,
16
16
  `width: ${status.width}`,
17
17
  `timeout: ${status.timeoutMs}ms`,
18
+ `digest: ${status.digestEnabled ? 'on' : 'off'} (${status.digestWindowSec}s, max ${status.digestMaxItems})`,
18
19
  `puppeteer: ${status.hasPuppeteer ? 'ready' : 'missing'}`,
19
20
  ].join('\n');
20
21
  });
21
- ctx.command('githubsth.render.mode <mode:string>', '切换渲染模式(text/image/auto)', { authority: 3 })
22
+ ctx.command('githubsth.render.mode <mode:string>', 'Set render mode', { authority: 3 })
22
23
  .action(async (_, mode) => {
23
- if (!mode || !modes.includes(mode)) {
24
- return `无效模式。可选:${modes.join(', ')}`;
25
- }
24
+ if (!mode || !modes.includes(mode))
25
+ return `Invalid mode. Allowed: ${modes.join(', ')}`;
26
26
  ctx.githubsthNotifier.setRenderMode(mode);
27
- return `已切换运行时渲染模式为 ${mode}(重启后恢复配置值 ${config.renderMode})。`;
27
+ return `Runtime render mode set to ${mode} (config default is ${config.renderMode}).`;
28
28
  });
29
- ctx.command('githubsth.render.theme <theme:string>', '设置全局默认主题', { authority: 3 })
29
+ ctx.command('githubsth.render.theme <theme:string>', 'Set default theme', { authority: 3 })
30
30
  .action(async (_, theme) => {
31
31
  const normalized = ctx.githubsthNotifier.normalizeTheme(theme);
32
- if (!normalized) {
33
- return `无效主题。请使用 githubsth.render.themes 查看可用主题。`;
34
- }
32
+ if (!normalized)
33
+ return 'Invalid theme. Run githubsth.render.themes first.';
35
34
  config.renderTheme = normalized;
36
- return `已设置全局默认主题为 ${normalized}(当前进程生效)。`;
35
+ return `Default theme set: ${normalized}`;
36
+ });
37
+ ctx.command('githubsth.render.style <style:string>', 'Set default style', { authority: 3 })
38
+ .action(async (_, style) => {
39
+ const normalized = ctx.githubsthNotifier.normalizeStyle(style);
40
+ if (!normalized)
41
+ return 'Invalid style. Run githubsth.render.styles first.';
42
+ config.renderStyle = normalized;
43
+ return `Default style set: ${normalized}`;
37
44
  });
38
- ctx.command('githubsth.render.width <width:number>', '设置图片宽度', { authority: 3 })
45
+ ctx.command('githubsth.render.width <width:number>', 'Set image width', { authority: 3 })
39
46
  .action(async (_, width) => {
40
47
  if (!width || Number.isNaN(width))
41
- return '请提供有效的数字宽度。';
48
+ return 'Please provide a valid width number.';
42
49
  const normalized = Math.max(480, Math.min(1600, Math.floor(width)));
43
50
  config.renderWidth = normalized;
44
- return `已设置图片宽度为 ${normalized}px(当前进程生效)。`;
51
+ return `Image width set: ${normalized}px`;
45
52
  });
46
- ctx.command('githubsth.render.themes', '查看主题列表', { authority: 3 })
47
- .action(async () => {
48
- const themes = ctx.githubsthNotifier.listThemes();
49
- return `可用主题:\n- ${themes.join('\n- ')}`;
53
+ ctx.command('githubsth.render.digest <enabled:string>', 'Toggle digest mode (on/off)', { authority: 3 })
54
+ .action(async (_, enabled) => {
55
+ const key = String(enabled || '').toLowerCase();
56
+ if (!['on', 'off', 'true', 'false', '1', '0'].includes(key))
57
+ return 'Usage: githubsth.render.digest on|off';
58
+ config.digestEnabled = ['on', 'true', '1'].includes(key);
59
+ return `Digest mode: ${config.digestEnabled ? 'on' : 'off'}`;
50
60
  });
51
- ctx.command('githubsth.render.preview [event:string] [theme:string]', '预览通知渲染', { authority: 3 })
52
- .action(async ({ session }, event, theme) => {
61
+ ctx.command('githubsth.render.digest.window <seconds:number>', 'Set digest window seconds', { authority: 3 })
62
+ .action(async (_, seconds) => {
63
+ if (!seconds || Number.isNaN(seconds))
64
+ return 'Please provide a valid seconds value.';
65
+ config.digestWindowSec = Math.max(5, Math.min(3600, Math.floor(seconds)));
66
+ return `Digest window set: ${config.digestWindowSec}s`;
67
+ });
68
+ ctx.command('githubsth.render.digest.max <count:number>', 'Set digest max items', { authority: 3 })
69
+ .action(async (_, count) => {
70
+ if (!count || Number.isNaN(count))
71
+ return 'Please provide a valid count.';
72
+ config.digestMaxItems = Math.max(2, Math.min(100, Math.floor(count)));
73
+ return `Digest max items set: ${config.digestMaxItems}`;
74
+ });
75
+ ctx.command('githubsth.render.themes', 'List themes', { authority: 3 })
76
+ .action(async () => `Themes:\n- ${ctx.githubsthNotifier.listThemes().join('\n- ')}`);
77
+ ctx.command('githubsth.render.styles', 'List styles', { authority: 3 })
78
+ .action(async () => `Styles:\n- ${ctx.githubsthNotifier.listStyles().join('\n- ')}`);
79
+ ctx.command('githubsth.render.preview [event:string] [theme:string] [style:string]', 'Preview renderer', { authority: 3 })
80
+ .action(async ({ session }, event, theme, style) => {
53
81
  const selectedEvent = event && events.includes(event) ? event : 'issue_comment';
54
- if (event && !events.includes(event)) {
55
- await session?.send(`未知事件 ${event},已改用默认事件 issue_comment。`);
56
- }
82
+ if (event && !events.includes(event))
83
+ await session?.send(`Unknown event ${event}, fallback to issue_comment.`);
57
84
  const normalizedTheme = theme ? ctx.githubsthNotifier.normalizeTheme(theme) : null;
58
- if (theme && !normalizedTheme) {
59
- await session?.send(`未知主题 ${theme},将使用默认主题。`);
60
- }
85
+ if (theme && !normalizedTheme)
86
+ await session?.send(`Unknown theme ${theme}, fallback to default.`);
87
+ const normalizedStyle = style ? ctx.githubsthNotifier.normalizeStyle(style) : null;
88
+ if (style && !normalizedStyle)
89
+ await session?.send(`Unknown style ${style}, fallback to default.`);
90
+ const prevTheme = config.renderTheme;
91
+ const prevStyle = config.renderStyle;
92
+ if (normalizedTheme)
93
+ config.renderTheme = normalizedTheme;
94
+ if (normalizedStyle)
95
+ config.renderStyle = normalizedStyle;
61
96
  const preview = await ctx.githubsthNotifier.renderPreview(selectedEvent, normalizedTheme || undefined);
62
- return preview || '预览失败:请检查 puppeteer 或渲染配置。';
97
+ config.renderTheme = prevTheme;
98
+ config.renderStyle = prevStyle;
99
+ return preview || 'Preview failed. Check puppeteer/render settings.';
63
100
  });
64
- ctx.command('githubsth.render.repo-theme <repo:string> <theme:string>', '为当前频道某订阅设置单独主题', { authority: 3 })
101
+ ctx.command('githubsth.render.repo-theme <repo:string> <theme:string>', 'Set per-subscription theme', { authority: 3 })
65
102
  .action(async ({ session }, repo, theme) => {
66
103
  if (!repo)
67
- return '请提供仓库(owner/repo)。';
104
+ return 'Please provide owner/repo.';
68
105
  if (!session?.channelId)
69
- return '请在群聊/频道中执行该命令。';
106
+ return 'Run this command in a group/channel.';
70
107
  const normalized = ctx.githubsthNotifier.normalizeTheme(theme);
71
108
  if (!normalized)
72
- return '主题不存在,请先执行 githubsth.render.themes';
109
+ return 'Invalid theme. Run githubsth.render.themes.';
73
110
  const target = await ctx.database.get('github_subscription', {
74
111
  repo,
75
112
  channelId: session.channelId,
76
113
  platform: session.platform || 'unknown',
77
114
  });
78
115
  if (!target.length)
79
- return '当前频道没有该仓库订阅。';
116
+ return 'No subscription for this repo in current channel.';
80
117
  await ctx.database.set('github_subscription', { id: target[0].id }, { renderTheme: normalized });
81
- return `已为 ${repo} 设置专属主题:${normalized}`;
118
+ return `Per-subscription theme set: ${repo} -> ${normalized}`;
82
119
  });
83
- ctx.command('githubsth.render.repo-theme.clear <repo:string>', '清除当前频道某订阅的专属主题', { authority: 3 })
120
+ ctx.command('githubsth.render.repo-theme.clear <repo:string>', 'Clear per-subscription theme', { authority: 3 })
84
121
  .action(async ({ session }, repo) => {
85
122
  if (!repo)
86
- return '请提供仓库(owner/repo)。';
123
+ return 'Please provide owner/repo.';
87
124
  if (!session?.channelId)
88
- return '请在群聊/频道中执行该命令。';
125
+ return 'Run this command in a group/channel.';
89
126
  const target = await ctx.database.get('github_subscription', {
90
127
  repo,
91
128
  channelId: session.channelId,
92
129
  platform: session.platform || 'unknown',
93
130
  });
94
131
  if (!target.length)
95
- return '当前频道没有该仓库订阅。';
132
+ return 'No subscription for this repo in current channel.';
96
133
  await ctx.database.set('github_subscription', { id: target[0].id }, { renderTheme: null });
97
- return `已清除 ${repo} 的专属主题,回退到全局主题。`;
134
+ return `Per-subscription theme cleared: ${repo}`;
135
+ });
136
+ ctx.command('githubsth.render.repo-style <repo:string> <style:string>', 'Set per-subscription style', { authority: 3 })
137
+ .action(async ({ session }, repo, style) => {
138
+ if (!repo)
139
+ return 'Please provide owner/repo.';
140
+ if (!session?.channelId)
141
+ return 'Run this command in a group/channel.';
142
+ const normalized = ctx.githubsthNotifier.normalizeStyle(style);
143
+ if (!normalized)
144
+ return 'Invalid style. Run githubsth.render.styles.';
145
+ const target = await ctx.database.get('github_subscription', {
146
+ repo,
147
+ channelId: session.channelId,
148
+ platform: session.platform || 'unknown',
149
+ });
150
+ if (!target.length)
151
+ return 'No subscription for this repo in current channel.';
152
+ await ctx.database.set('github_subscription', { id: target[0].id }, { renderStyle: normalized });
153
+ return `Per-subscription style set: ${repo} -> ${normalized}`;
154
+ });
155
+ ctx.command('githubsth.render.repo-style.clear <repo:string>', 'Clear per-subscription style', { authority: 3 })
156
+ .action(async ({ session }, repo) => {
157
+ if (!repo)
158
+ return 'Please provide owner/repo.';
159
+ if (!session?.channelId)
160
+ return 'Run this command in a group/channel.';
161
+ const target = await ctx.database.get('github_subscription', {
162
+ repo,
163
+ channelId: session.channelId,
164
+ platform: session.platform || 'unknown',
165
+ });
166
+ if (!target.length)
167
+ return 'No subscription for this repo in current channel.';
168
+ await ctx.database.set('github_subscription', { id: target[0].id }, { renderStyle: null });
169
+ return `Per-subscription style cleared: ${repo}`;
98
170
  });
99
- ctx.command('githubsth.render.repo-theme.list [repo:string]', '查看当前频道订阅的专属主题', { authority: 3 })
171
+ ctx.command('githubsth.render.repo-theme.list [repo:string]', 'List per-subscription theme/style', { authority: 3 })
100
172
  .action(async ({ session }, repo) => {
101
173
  if (!session?.channelId)
102
- return '请在群聊/频道中执行该命令。';
174
+ return 'Run this command in a group/channel.';
103
175
  const query = {
104
176
  channelId: session.channelId,
105
177
  platform: session.platform || 'unknown',
@@ -108,7 +180,9 @@ function apply(ctx, config) {
108
180
  query.repo = repo;
109
181
  const subs = await ctx.database.get('github_subscription', query);
110
182
  if (!subs.length)
111
- return '当前频道没有匹配订阅。';
112
- return subs.map((sub) => `${sub.repo} => ${sub.renderTheme || '(default)'}`).join('\n');
183
+ return 'No matched subscriptions.';
184
+ return subs
185
+ .map((sub) => `${sub.repo} => theme=${sub.renderTheme || '(default)'} style=${sub.renderStyle || '(default)'}`)
186
+ .join('\n');
113
187
  });
114
188
  }
@@ -10,31 +10,31 @@ function apply(ctx, config) {
10
10
  'discussion', 'workflow_run',
11
11
  ];
12
12
  const defaultConfigEvents = config.defaultEvents || ['push', 'issues', 'issue_comment', 'pull_request', 'pull_request_review', 'release', 'star', 'fork'];
13
- ctx.command('githubsth.subscribe <repo> [events:text]', '订阅 GitHub 仓库事件')
13
+ ctx.command('githubsth.subscribe <repo> [events:text]', 'Subscribe GitHub repository events')
14
14
  .alias('gh.sub')
15
15
  .usage(`
16
- 订阅 GitHub 仓库通知。若不指定事件,默认订阅:${defaultConfigEvents.join(', ')}
16
+ Subscribe GitHub repository notifications. If events are omitted, default events are used: ${defaultConfigEvents.join(', ')}
17
17
 
18
- 示例:
18
+ Examples:
19
19
  - gh.sub koishijs/koishi
20
20
  - gh.sub koishijs/koishi push,issues,star
21
21
  `)
22
22
  .action(async ({ session }, repo, eventsStr) => {
23
23
  if (!repo)
24
- return '请指定仓库(owner/repo)。';
24
+ return 'Please provide repository as owner/repo.';
25
25
  if (!repoRegex.test(repo))
26
- return '仓库格式错误,应为 owner/repo';
26
+ return 'Invalid repository format. Expected owner/repo.';
27
27
  if (!session?.channelId)
28
- return '请在群聊/频道中执行该命令。';
28
+ return 'Please run this command in a group/channel.';
29
29
  const trusted = await ctx.database.get('github_trusted_repo', { repo, enabled: true });
30
30
  if (trusted.length === 0)
31
- return '该仓库不在信任列表中,请先由管理员添加。';
31
+ return 'Repository is not in trusted list. Ask admin to add it first.';
32
32
  let events;
33
33
  if (eventsStr) {
34
34
  events = eventsStr.split(/[,,\s]+/).map((e) => e.trim()).filter(Boolean).map((e) => e.replace(/-/g, '_'));
35
35
  const invalidEvents = events.filter((e) => !validEvents.includes(e) && e !== '*');
36
36
  if (invalidEvents.length) {
37
- return `无效事件:${invalidEvents.join(', ')}\n可选事件:${validEvents.join(', ')}`;
37
+ return `Invalid events: ${invalidEvents.join(', ')}\nAllowed events: ${validEvents.join(', ')}`;
38
38
  }
39
39
  }
40
40
  else {
@@ -48,7 +48,7 @@ function apply(ctx, config) {
48
48
  });
49
49
  if (existing.length > 0) {
50
50
  await ctx.database.set('github_subscription', { id: existing[0].id }, { events });
51
- return `已更新订阅:${repo}\n事件:${events.join(', ')}`;
51
+ return `Subscription updated: ${repo}\nEvents: ${events.join(', ')}`;
52
52
  }
53
53
  await ctx.database.create('github_subscription', {
54
54
  repo,
@@ -56,40 +56,42 @@ function apply(ctx, config) {
56
56
  platform: session.platform || 'unknown',
57
57
  events,
58
58
  });
59
- return `已订阅 ${repo}\n事件:${events.join(', ')}`;
59
+ return `Subscribed: ${repo}\nEvents: ${events.join(', ')}`;
60
60
  }
61
61
  catch (error) {
62
62
  logger.warn(error);
63
- return '订阅失败,请稍后重试。';
63
+ return 'Subscribe failed. Please try again later.';
64
64
  }
65
65
  });
66
- ctx.command('githubsth.unsubscribe <repo>', '取消订阅 GitHub 仓库')
66
+ ctx.command('githubsth.unsubscribe <repo>', 'Unsubscribe GitHub repository')
67
67
  .alias('gh.unsub')
68
68
  .action(async ({ session }, repo) => {
69
69
  if (!repo)
70
- return '请指定仓库(owner/repo)。';
70
+ return 'Please provide repository as owner/repo.';
71
71
  if (!session?.channelId)
72
- return '请在群聊/频道中执行该命令。';
72
+ return 'Please run this command in a group/channel.';
73
73
  const result = await ctx.database.remove('github_subscription', {
74
74
  repo,
75
75
  channelId: session.channelId,
76
76
  platform: session.platform || 'unknown',
77
77
  });
78
78
  if (result.matched === 0)
79
- return '未找到该订阅。';
80
- return `已取消订阅 ${repo}`;
79
+ return 'Subscription not found.';
80
+ return `Unsubscribed: ${repo}`;
81
81
  });
82
- ctx.command('githubsth.list', '查看当前频道订阅')
82
+ ctx.command('githubsth.list', 'List current channel subscriptions')
83
83
  .alias('gh.list')
84
84
  .action(async ({ session }) => {
85
85
  if (!session?.channelId)
86
- return '请在群聊/频道中执行该命令。';
86
+ return 'Please run this command in a group/channel.';
87
87
  const subs = await ctx.database.get('github_subscription', {
88
88
  channelId: session.channelId,
89
89
  platform: session.platform || 'unknown',
90
90
  });
91
91
  if (subs.length === 0)
92
- return '当前频道没有订阅。';
93
- return subs.map((sub) => `${sub.repo} [${sub.events.join(', ')}] theme=${sub.renderTheme || '(default)'}`).join('\n');
92
+ return 'No subscriptions in this channel.';
93
+ return subs
94
+ .map((sub) => `${sub.repo} [${sub.events.join(', ')}] theme=${sub.renderTheme || '(default)'} style=${sub.renderStyle || '(default)'}`)
95
+ .join('\n');
94
96
  });
95
97
  }
package/lib/config.d.ts CHANGED
@@ -2,12 +2,14 @@ import { Schema } from 'koishi';
2
2
  export type RenderMode = 'text' | 'image' | 'auto';
3
3
  export type RenderFallback = 'text' | 'drop';
4
4
  export type RenderTheme = 'github-light' | 'github-dark' | 'aurora' | 'sunset' | 'matrix' | 'compact' | 'card' | 'terminal';
5
+ export type RenderStyle = 'auto' | 'github' | 'glass' | 'neon' | 'compact' | 'card' | 'terminal';
5
6
  export interface Rule {
6
7
  repo: string;
7
8
  channelId: string;
8
9
  platform?: string;
9
10
  events: string[];
10
11
  renderTheme?: RenderTheme;
12
+ renderStyle?: RenderStyle;
11
13
  }
12
14
  export interface Config {
13
15
  defaultOwner?: string;
@@ -23,8 +25,12 @@ export interface Config {
23
25
  renderMode: RenderMode;
24
26
  renderFallback: RenderFallback;
25
27
  renderTheme: RenderTheme;
28
+ renderStyle: RenderStyle;
26
29
  renderWidth: number;
27
30
  renderTimeoutMs: number;
31
+ digestEnabled: boolean;
32
+ digestWindowSec: number;
33
+ digestMaxItems: number;
28
34
  rules?: Rule[];
29
35
  }
30
36
  export declare const Config: Schema<Config>;