koishi-plugin-githubsth 1.0.3-alpha.1 → 1.0.4

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.
@@ -37,17 +37,12 @@ exports.apply = apply;
37
37
  const repo = __importStar(require("./repo"));
38
38
  const admin = __importStar(require("./admin"));
39
39
  const subscribe = __importStar(require("./subscribe"));
40
+ const render = __importStar(require("./render"));
40
41
  function apply(ctx, config) {
41
- console.log('Applying githubsth commands...');
42
- // Register parent command to show help
43
- ctx.command('githubsth', 'GitHub 推送通知')
44
- .action(({ session }) => {
45
- session?.execute('help githubsth');
46
- });
42
+ ctx.command('githubsth', 'GitHub 订阅通知')
43
+ .action(({ session }) => session?.execute('help githubsth'));
47
44
  ctx.plugin(repo, config);
48
- console.log('githubsth.repo loaded');
49
45
  ctx.plugin(admin, config);
50
- console.log('githubsth.admin loaded');
51
46
  ctx.plugin(subscribe, config);
52
- console.log('githubsth.subscribe loaded');
47
+ ctx.plugin(render, config);
53
48
  }
@@ -0,0 +1,3 @@
1
+ import { Context } from 'koishi';
2
+ import { Config } from '../config';
3
+ export declare function apply(ctx: Context, config: Config): void;
@@ -0,0 +1,55 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.apply = apply;
4
+ const modes = ['text', 'image', 'auto'];
5
+ const themes = ['compact', 'card', 'terminal'];
6
+ const events = ['push', 'issues', 'issue_comment', 'pull_request', 'pull_request_review', 'star', 'fork', 'release', 'discussion', 'workflow_run'];
7
+ function apply(ctx, config) {
8
+ ctx.command('githubsth.render', '通知渲染设置(文本/图片)', { authority: 3 })
9
+ .alias('gh.render');
10
+ ctx.command('githubsth.render.status', '查看渲染状态', { authority: 3 })
11
+ .action(async () => {
12
+ const status = ctx.githubsthNotifier.getRenderStatus();
13
+ return [
14
+ `mode: ${status.mode} (configured: ${status.configuredMode})`,
15
+ `fallback: ${status.fallback}`,
16
+ `theme: ${status.theme}`,
17
+ `width: ${status.width}`,
18
+ `timeout: ${status.timeoutMs}ms`,
19
+ `puppeteer: ${status.hasPuppeteer ? 'ready' : 'missing'}`,
20
+ ].join('\n');
21
+ });
22
+ ctx.command('githubsth.render.mode <mode:string>', '切换渲染模式(text/image/auto)', { authority: 3 })
23
+ .action(async (_, mode) => {
24
+ if (!mode || !modes.includes(mode)) {
25
+ return `无效模式。可选:${modes.join(', ')}`;
26
+ }
27
+ ctx.githubsthNotifier.setRenderMode(mode);
28
+ return `已切换运行时渲染模式为 ${mode}(重启后恢复配置值 ${config.renderMode})。`;
29
+ });
30
+ ctx.command('githubsth.render.theme <theme:string>', '切换图片主题(compact/card/terminal)', { authority: 3 })
31
+ .action(async (_, theme) => {
32
+ if (!theme || !themes.includes(theme)) {
33
+ return `无效主题。可选:${themes.join(', ')}`;
34
+ }
35
+ config.renderTheme = theme;
36
+ return `已切换图片主题为 ${theme}(当前进程生效)。`;
37
+ });
38
+ ctx.command('githubsth.render.width <width:number>', '设置图片宽度', { authority: 3 })
39
+ .action(async (_, width) => {
40
+ if (!width || Number.isNaN(width))
41
+ return '请提供有效的数字宽度。';
42
+ const normalized = Math.max(480, Math.min(1600, Math.floor(width)));
43
+ config.renderWidth = normalized;
44
+ return `已设置图片宽度为 ${normalized}px(当前进程生效)。`;
45
+ });
46
+ ctx.command('githubsth.render.preview [event:string]', '预览通知渲染', { authority: 3 })
47
+ .action(async ({ session }, event) => {
48
+ const selected = event && events.includes(event) ? event : 'issue_comment';
49
+ if (event && !events.includes(event)) {
50
+ await session?.send(`未知事件 ${event},已改用默认事件 issue_comment。`);
51
+ }
52
+ const preview = await ctx.githubsthNotifier.renderPreview(selected);
53
+ return preview || '预览失败:请检查 puppeteer 或渲染配置。';
54
+ });
55
+ }
package/lib/config.d.ts CHANGED
@@ -5,12 +5,25 @@ export interface Rule {
5
5
  platform?: string;
6
6
  events: string[];
7
7
  }
8
+ export type RenderMode = 'text' | 'image' | 'auto';
9
+ export type RenderFallback = 'text' | 'drop';
10
+ export type RenderTheme = 'compact' | 'card' | 'terminal';
8
11
  export interface Config {
9
12
  defaultOwner?: string;
10
13
  defaultRepo?: string;
11
14
  debug: boolean;
12
15
  logUnhandledEvents: boolean;
13
16
  defaultEvents: string[];
17
+ enableSessionFallback: boolean;
18
+ dedupRetentionHours: number;
19
+ sendRetryCount: number;
20
+ sendRetryBaseDelayMs: number;
21
+ formatterLocale: 'zh-CN' | 'en-US';
22
+ renderMode: RenderMode;
23
+ renderFallback: RenderFallback;
24
+ renderTheme: RenderTheme;
25
+ renderWidth: number;
26
+ renderTimeoutMs: number;
14
27
  rules?: Rule[];
15
28
  }
16
29
  export declare const Config: Schema<Config>;
package/lib/config.js CHANGED
@@ -3,17 +3,41 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.Config = void 0;
4
4
  const koishi_1 = require("koishi");
5
5
  exports.Config = koishi_1.Schema.object({
6
- defaultOwner: koishi_1.Schema.string().description('默认仓库拥有者'),
7
- defaultRepo: koishi_1.Schema.string().description('默认仓库名称'),
8
- debug: koishi_1.Schema.boolean().default(false).description('启用调试模式,输出详细日志'),
9
- logUnhandledEvents: koishi_1.Schema.boolean().default(false).description('是否记录未处理的 Webhook 事件 (Unknown events)'),
6
+ defaultOwner: koishi_1.Schema.string().description('默认仓库 Owner。'),
7
+ defaultRepo: koishi_1.Schema.string().description('默认仓库名称。'),
8
+ debug: koishi_1.Schema.boolean().default(false).description('启用调试日志。'),
9
+ logUnhandledEvents: koishi_1.Schema.boolean().default(false).description('记录未处理的 webhook 事件。'),
10
+ enableSessionFallback: koishi_1.Schema.boolean().default(true).description('启用 message-created 回退事件解析。'),
11
+ dedupRetentionHours: koishi_1.Schema.number().min(1).max(720).default(72).description('事件幂等记录保留小时数。'),
12
+ sendRetryCount: koishi_1.Schema.number().min(0).max(10).default(2).description('消息发送失败重试次数。'),
13
+ sendRetryBaseDelayMs: koishi_1.Schema.number().min(100).max(30000).default(800).description('重试基础延迟(毫秒,指数退避)。'),
14
+ formatterLocale: koishi_1.Schema.union([
15
+ koishi_1.Schema.const('zh-CN').description('中文'),
16
+ koishi_1.Schema.const('en-US').description('English'),
17
+ ]).default('zh-CN').description('通知文本语言。'),
18
+ renderMode: koishi_1.Schema.union([
19
+ koishi_1.Schema.const('auto').description('自动:优先图片,失败回退'),
20
+ koishi_1.Schema.const('image').description('仅图片'),
21
+ koishi_1.Schema.const('text').description('仅文本'),
22
+ ]).default('auto').description('通知渲染模式。'),
23
+ renderFallback: koishi_1.Schema.union([
24
+ koishi_1.Schema.const('text').description('图片失败回退文本'),
25
+ koishi_1.Schema.const('drop').description('图片失败则丢弃'),
26
+ ]).default('text').description('图片渲染失败时的回退策略。'),
27
+ renderTheme: koishi_1.Schema.union([
28
+ koishi_1.Schema.const('compact').description('紧凑样式'),
29
+ koishi_1.Schema.const('card').description('卡片样式'),
30
+ koishi_1.Schema.const('terminal').description('终端样式'),
31
+ ]).default('compact').description('图片通知主题。'),
32
+ renderWidth: koishi_1.Schema.number().min(480).max(1600).default(840).description('图片宽度(像素)。'),
33
+ renderTimeoutMs: koishi_1.Schema.number().min(1000).max(60000).default(12000).description('单次图片渲染超时(毫秒)。'),
10
34
  defaultEvents: koishi_1.Schema.array(koishi_1.Schema.string())
11
35
  .default(['push', 'issues', 'issue_comment', 'pull_request', 'pull_request_review', 'release', 'star', 'fork'])
12
- .description('默认订阅事件列表 (当不指定事件时使用)'),
36
+ .description('未显式指定时使用的默认订阅事件列表。'),
13
37
  rules: koishi_1.Schema.array(koishi_1.Schema.object({
14
38
  repo: koishi_1.Schema.string().required(),
15
39
  channelId: koishi_1.Schema.string().required(),
16
40
  platform: koishi_1.Schema.string(),
17
41
  events: koishi_1.Schema.array(koishi_1.Schema.string()).default(['push', 'issues', 'pull_request', 'issue_comment', 'pull_request_review']),
18
- })).hidden().description('已废弃,请使用数据库管理订阅'),
42
+ })).hidden().description('已废弃,仅保留兼容。建议改用数据库订阅管理。'),
19
43
  });
package/lib/database.d.ts CHANGED
@@ -3,6 +3,7 @@ declare module 'koishi' {
3
3
  interface Tables {
4
4
  github_subscription: GithubSubscription;
5
5
  github_trusted_repo: GithubTrustedRepo;
6
+ github_event_dedup: GithubEventDedup;
6
7
  }
7
8
  }
8
9
  export interface GithubSubscription {
@@ -19,4 +20,11 @@ export interface GithubTrustedRepo {
19
20
  addedBy: string;
20
21
  addedAt: Date;
21
22
  }
23
+ export interface GithubEventDedup {
24
+ id: number;
25
+ dedupKey: string;
26
+ event: string;
27
+ repo: string;
28
+ createdAt: Date;
29
+ }
22
30
  export declare function apply(ctx: Context): void;
package/lib/database.js CHANGED
@@ -10,6 +10,7 @@ function apply(ctx) {
10
10
  events: 'list',
11
11
  }, {
12
12
  autoInc: true,
13
+ unique: ['repo', 'channelId', 'platform'],
13
14
  });
14
15
  ctx.model.extend('github_trusted_repo', {
15
16
  id: 'unsigned',
@@ -21,4 +22,14 @@ function apply(ctx) {
21
22
  autoInc: true,
22
23
  unique: ['repo'],
23
24
  });
25
+ ctx.model.extend('github_event_dedup', {
26
+ id: 'unsigned',
27
+ dedupKey: 'string',
28
+ event: 'string',
29
+ repo: 'string',
30
+ createdAt: 'timestamp',
31
+ }, {
32
+ autoInc: true,
33
+ unique: ['dedupKey'],
34
+ });
24
35
  }
package/lib/index.js CHANGED
@@ -44,31 +44,28 @@ exports.apply = apply;
44
44
  const commands_1 = require("./commands");
45
45
  const database = __importStar(require("./database"));
46
46
  const zh_CN_1 = __importDefault(require("./locales/zh-CN"));
47
+ const en_US_1 = __importDefault(require("./locales/en-US"));
47
48
  const notifier_1 = require("./services/notifier");
48
49
  const formatter_1 = require("./services/formatter");
49
50
  exports.name = 'githubsth';
50
51
  exports.inject = {
51
52
  required: ['database'],
52
- optional: ['github'],
53
+ optional: ['github', 'puppeteer'],
53
54
  };
54
55
  __exportStar(require("./config"), exports);
55
56
  function apply(ctx, config) {
56
57
  const logger = ctx.logger('githubsth');
57
58
  logger.info('Plugin loading...');
58
- // 本地化
59
59
  ctx.i18n.define('zh-CN', zh_CN_1.default);
60
- // 数据库
60
+ ctx.i18n.define('en-US', en_US_1.default);
61
61
  ctx.plugin(database);
62
- // 注册服务
63
- ctx.plugin(formatter_1.Formatter);
62
+ ctx.plugin(formatter_1.Formatter, config);
64
63
  ctx.plugin(notifier_1.Notifier, config);
65
- // 注册命令
66
- // admin and subscribe are already loaded in commands/index.ts, remove duplicate loading here
67
64
  try {
68
65
  ctx.plugin(commands_1.apply, config);
69
66
  logger.info('Plugin loaded successfully');
70
67
  }
71
- catch (e) {
72
- logger.error('Plugin failed to load:', e);
68
+ catch (error) {
69
+ logger.error('Plugin failed to load:', error);
73
70
  }
74
71
  }
@@ -0,0 +1,17 @@
1
+ declare const _default: {
2
+ commands: {
3
+ githubsth: {
4
+ description: string;
5
+ };
6
+ 'githubsth.repo': {
7
+ description: string;
8
+ messages: {
9
+ repo_info: string;
10
+ error: string;
11
+ specify_repo: string;
12
+ not_found: string;
13
+ };
14
+ };
15
+ };
16
+ };
17
+ export default _default;
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = {
4
+ commands: {
5
+ githubsth: {
6
+ description: 'GitHub subscription notifier',
7
+ },
8
+ 'githubsth.repo': {
9
+ description: 'Get repository info',
10
+ messages: {
11
+ repo_info: 'Repository: {0}/{1}\nDescription: {2}\nStars: {3}',
12
+ error: 'Failed to fetch repository info: {0}',
13
+ specify_repo: 'Please specify repository as owner/repo.',
14
+ not_found: 'Repository not found or access denied.',
15
+ },
16
+ },
17
+ },
18
+ };
@@ -3,16 +3,16 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.default = {
4
4
  commands: {
5
5
  githubsth: {
6
- description: 'GitHub 交互插件'
6
+ description: 'GitHub 订阅通知插件',
7
7
  },
8
8
  'githubsth.repo': {
9
9
  description: '获取仓库信息',
10
10
  messages: {
11
11
  repo_info: '仓库: {0}/{1}\n描述: {2}\nStars: {3}',
12
- error: '获取信息失败: {0}',
13
- specify_repo: '请指定仓库名称。',
14
- not_found: '未找到仓库或无权限访问。'
15
- }
16
- }
17
- }
12
+ error: '获取仓库信息失败: {0}',
13
+ specify_repo: '请指定仓库名称(owner/repo)。',
14
+ not_found: '未找到仓库或无权限访问。',
15
+ },
16
+ },
17
+ },
18
18
  };
@@ -1,19 +1,25 @@
1
- import { Context, Service, h } from 'koishi';
1
+ import { Context, Service } from 'koishi';
2
+ import type { Config } from '../config';
2
3
  declare module 'koishi' {
3
4
  interface Context {
4
5
  githubsthFormatter: Formatter;
5
6
  }
6
7
  }
7
8
  export declare class Formatter extends Service {
8
- constructor(ctx: Context);
9
- formatPush(payload: any): h | null;
10
- formatIssue(payload: any): h;
11
- formatPullRequest(payload: any): h;
12
- formatStar(payload: any): h | null;
13
- formatFork(payload: any): h;
14
- formatRelease(payload: any): h | null;
15
- formatDiscussion(payload: any): h;
16
- formatWorkflowRun(payload: any): h | null;
17
- formatIssueComment(payload: any): h | null;
18
- formatPullRequestReview(payload: any): h | null;
9
+ private readonly locale;
10
+ constructor(ctx: Context, config?: Partial<Config>);
11
+ formatPush(payload: any): string | null;
12
+ formatIssue(payload: any): string;
13
+ formatPullRequest(payload: any): string;
14
+ formatStar(payload: any): string | null;
15
+ formatFork(payload: any): string;
16
+ formatRelease(payload: any): string | null;
17
+ formatDiscussion(payload: any): string;
18
+ formatWorkflowRun(payload: any): string | null;
19
+ formatIssueComment(payload: any): string | null;
20
+ formatPullRequestReview(payload: any): string | null;
21
+ private summarizeCommentBody;
22
+ private mapAction;
23
+ private t;
24
+ private render;
19
25
  }