koishi-plugin-qq-github-bridge 0.0.0-stage → 1.0.0

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.
Files changed (74) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +167 -2
  3. package/client/env.d.ts +5 -0
  4. package/client/index.ts +6 -0
  5. package/client/page.vue +164 -0
  6. package/client/tsconfig.json +15 -0
  7. package/dist/index.js +1 -0
  8. package/dist/style.css +1 -0
  9. package/docs/ARCHITECTURE.md +100 -0
  10. package/docs/RELEASING.md +68 -0
  11. package/docs/RESEARCH.md +37 -0
  12. package/docs/REVIEW.md +48 -0
  13. package/docs/TESTING.md +92 -0
  14. package/lib/config.d.ts +35 -0
  15. package/lib/config.js +99 -0
  16. package/lib/config.js.map +1 -0
  17. package/lib/console.d.ts +7 -0
  18. package/lib/console.js +73 -0
  19. package/lib/console.js.map +1 -0
  20. package/lib/database/models.d.ts +37 -0
  21. package/lib/database/models.js +21 -0
  22. package/lib/database/models.js.map +1 -0
  23. package/lib/handlers/commands.d.ts +28 -0
  24. package/lib/handlers/commands.js +184 -0
  25. package/lib/handlers/commands.js.map +1 -0
  26. package/lib/handlers/message.d.ts +4 -0
  27. package/lib/handlers/message.js +58 -0
  28. package/lib/handlers/message.js.map +1 -0
  29. package/lib/index.d.ts +7 -0
  30. package/lib/index.js +44 -0
  31. package/lib/index.js.map +1 -0
  32. package/lib/rpc.d.ts +8 -0
  33. package/lib/rpc.js +3 -0
  34. package/lib/rpc.js.map +1 -0
  35. package/lib/services/github.d.ts +22 -0
  36. package/lib/services/github.js +201 -0
  37. package/lib/services/github.js.map +1 -0
  38. package/lib/services/permission.d.ts +12 -0
  39. package/lib/services/permission.js +56 -0
  40. package/lib/services/permission.js.map +1 -0
  41. package/lib/services/repository.d.ts +8 -0
  42. package/lib/services/repository.js +22 -0
  43. package/lib/services/repository.js.map +1 -0
  44. package/lib/services/storage.d.ts +13 -0
  45. package/lib/services/storage.js +75 -0
  46. package/lib/services/storage.js.map +1 -0
  47. package/lib/types.d.ts +93 -0
  48. package/lib/types.js +3 -0
  49. package/lib/types.js.map +1 -0
  50. package/lib/utils/errors.d.ts +8 -0
  51. package/lib/utils/errors.js +28 -0
  52. package/lib/utils/errors.js.map +1 -0
  53. package/lib/utils/lock.d.ts +11 -0
  54. package/lib/utils/lock.js +38 -0
  55. package/lib/utils/lock.js.map +1 -0
  56. package/lib/utils/parser.d.ts +9 -0
  57. package/lib/utils/parser.js +87 -0
  58. package/lib/utils/parser.js.map +1 -0
  59. package/package.json +99 -4
  60. package/src/config.ts +111 -0
  61. package/src/console.ts +65 -0
  62. package/src/database/models.ts +45 -0
  63. package/src/handlers/commands.ts +177 -0
  64. package/src/handlers/message.ts +60 -0
  65. package/src/index.ts +37 -0
  66. package/src/rpc.ts +10 -0
  67. package/src/services/github.ts +189 -0
  68. package/src/services/permission.ts +47 -0
  69. package/src/services/repository.ts +22 -0
  70. package/src/services/storage.ts +67 -0
  71. package/src/types.ts +75 -0
  72. package/src/utils/errors.ts +18 -0
  73. package/src/utils/lock.ts +30 -0
  74. package/src/utils/parser.ts +71 -0
@@ -0,0 +1,56 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Permissions = void 0;
4
+ const config_1 = require("../config");
5
+ const errors_1 = require("../utils/errors");
6
+ class Permissions {
7
+ ctx;
8
+ superAdmins;
9
+ constructor(ctx, config) {
10
+ this.ctx = ctx;
11
+ this.superAdmins = new Set(config.superAdmins);
12
+ }
13
+ async requireAdmin(actor, superOnly = false) {
14
+ // Console actors are constructed only after a fresh authenticated deployer check.
15
+ if (actor.source === 'console')
16
+ return;
17
+ if (this.superAdmins.has(actor.qq))
18
+ return;
19
+ if (superOnly)
20
+ throw new errors_1.BridgeError('forbidden', '只有超级管理员可以撤销管理员权限。');
21
+ const [admin] = await this.ctx.database.get('qgh_admin', actor.qq);
22
+ if (!admin)
23
+ throw new errors_1.BridgeError('forbidden', '此操作需要 GitHub Bridge 管理员权限。');
24
+ }
25
+ async add(actor, qq) {
26
+ await this.requireAdmin(actor);
27
+ if (!config_1.QQ_PATTERN.test(qq))
28
+ throw new errors_1.BridgeError('qq', '请输入有效的数字 QQ 号。');
29
+ if (this.superAdmins.has(qq))
30
+ return { message: `${qq} 已是后台配置的超级管理员。`, result: 'noop' };
31
+ const [existing] = await this.ctx.database.get('qgh_admin', qq);
32
+ if (existing)
33
+ return { message: `${qq} 已是管理员,无需重复添加。`, result: 'noop' };
34
+ await this.ctx.database.create('qgh_admin', {
35
+ qq, addedBy: actor.source === 'console' ? `console:${actor.id}` : actor.qq, createdAt: new Date(),
36
+ });
37
+ return { message: `已添加管理员 ${qq},权限立即生效。`, result: 'success' };
38
+ }
39
+ async remove(actor, qq) {
40
+ await this.requireAdmin(actor, true);
41
+ if (!config_1.QQ_PATTERN.test(qq))
42
+ throw new errors_1.BridgeError('qq', '请输入有效的数字 QQ 号。');
43
+ if (this.superAdmins.has(qq))
44
+ throw new errors_1.BridgeError('super-admin', '超级管理员只能通过插件后台配置修改,不能在管理员列表或 QQ 指令中撤销。');
45
+ const [existing] = await this.ctx.database.get('qgh_admin', qq);
46
+ if (!existing)
47
+ return { message: `${qq} 当前不是管理员。`, result: 'noop' };
48
+ await this.ctx.database.remove('qgh_admin', qq);
49
+ return { message: `已撤销管理员 ${qq}。此前由其添加的其他管理员保持不变。`, result: 'success' };
50
+ }
51
+ async list(offset = 0, limit = 50) {
52
+ return this.ctx.database.get('qgh_admin', {}, { sort: { createdAt: 'asc', qq: 'asc' }, offset, limit });
53
+ }
54
+ }
55
+ exports.Permissions = Permissions;
56
+ //# sourceMappingURL=permission.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"permission.js","sourceRoot":"","sources":["../../src/services/permission.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,8 @@
1
+ import type { Binding } from '../config';
2
+ import type { Repository } from '../types';
3
+ export declare class RepositoryResolver {
4
+ private readonly groups;
5
+ constructor(bindings: Binding[]);
6
+ resolve(groupId: string): Repository | undefined;
7
+ all(): Repository[];
8
+ }
@@ -0,0 +1,22 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.RepositoryResolver = void 0;
4
+ class RepositoryResolver {
5
+ groups = new Map();
6
+ constructor(bindings) {
7
+ for (const item of bindings) {
8
+ if (item.enabled)
9
+ this.groups.set(item.groupId, {
10
+ owner: item.owner,
11
+ repo: item.repo,
12
+ fullName: `${item.owner}/${item.repo}`,
13
+ });
14
+ }
15
+ }
16
+ resolve(groupId) { return this.groups.get(groupId); }
17
+ all() {
18
+ return [...new Map([...this.groups.values()].map(repo => [repo.fullName.toLowerCase(), repo])).values()];
19
+ }
20
+ }
21
+ exports.RepositoryResolver = RepositoryResolver;
22
+ //# sourceMappingURL=repository.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repository.js","sourceRoot":"","sources":["../../src/services/repository.ts"],"names":[],"mappings":";;;AAGA,MAAa,kBAAkB;IACZ,MAAM,GAAG,IAAI,GAAG,EAAsB,CAAA;IAEvD,YAAY,QAAmB;QAC7B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;YAC5B,IAAI,IAAI,CAAC,OAAO;gBAAE,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE;oBAC9C,KAAK,EAAE,IAAI,CAAC,KAAK;oBACjB,IAAI,EAAE,IAAI,CAAC,IAAI;oBACf,QAAQ,EAAE,GAAG,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,IAAI,EAAE;iBACvC,CAAC,CAAA;QACJ,CAAC;IACH,CAAC;IAED,OAAO,CAAC,OAAe,IAA4B,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,OAAO,CAAC,CAAA,CAAC,CAAC;IAEpF,GAAG;QACD,OAAO,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,WAAW,EAAE,EAAE,IAAI,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC,CAAA;IAC1G,CAAC;CACF;AAlBD,gDAkBC"}
@@ -0,0 +1,13 @@
1
+ import type { Context } from 'koishi';
2
+ import type { Config } from '../config';
3
+ import type { Actor, IncomingMessage } from '../types';
4
+ export declare class Storage {
5
+ private readonly ctx;
6
+ private readonly config;
7
+ constructor(ctx: Context, config: Config);
8
+ claim(message: IncomingMessage): Promise<boolean>;
9
+ cooldown(qq: string): Promise<void>;
10
+ begin(actor: Actor, action: string, target: string): Promise<string | undefined>;
11
+ finish(id: string | undefined, result: string, detail: string): Promise<boolean>;
12
+ cleanup(): Promise<void>;
13
+ }
@@ -0,0 +1,75 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Storage = void 0;
4
+ const node_crypto_1 = require("node:crypto");
5
+ const errors_1 = require("../utils/errors");
6
+ class Storage {
7
+ ctx;
8
+ config;
9
+ constructor(ctx, config) {
10
+ this.ctx = ctx;
11
+ this.config = config;
12
+ }
13
+ async claim(message) {
14
+ // The primary key is the atomic claim, including across reconnects / plugin reloads.
15
+ const [existing] = await this.ctx.database.get('qgh_event', message.eventKey);
16
+ if (existing)
17
+ return false;
18
+ try {
19
+ await this.ctx.database.create('qgh_event', {
20
+ id: message.eventKey,
21
+ // Allow clock skew and avoid cleanup racing a still-valid incoming event.
22
+ expiresAt: new Date(Math.max(Date.now(), message.timestamp) + this.config.dedupWindow + 60000),
23
+ });
24
+ return true;
25
+ }
26
+ catch {
27
+ const [winner] = await this.ctx.database.get('qgh_event', message.eventKey);
28
+ if (winner)
29
+ return false;
30
+ throw new errors_1.BridgeError('database', '数据库去重记录写入失败,未执行操作。');
31
+ }
32
+ }
33
+ async cooldown(qq) {
34
+ if (!this.config.userCooldown)
35
+ return;
36
+ const [existing] = await this.ctx.database.get('qgh_cooldown', qq);
37
+ const now = Date.now();
38
+ if (existing && +existing.expiresAt > now) {
39
+ throw new errors_1.BridgeError('cooldown', `操作过于频繁,请在 ${Math.ceil((+existing.expiresAt - now) / 1000)} 秒后重试。`);
40
+ }
41
+ await this.ctx.database.upsert('qgh_cooldown', [{ qq, expiresAt: new Date(now + this.config.userCooldown) }]);
42
+ }
43
+ async begin(actor, action, target) {
44
+ if (!this.config.auditEnabled)
45
+ return;
46
+ const id = (0, node_crypto_1.randomUUID)();
47
+ await this.ctx.database.create('qgh_audit', {
48
+ id, actorId: actor.id, actorQQ: actor.qq, source: actor.source, action, target,
49
+ groupId: actor.groupId, repository: actor.repository, createdAt: new Date(),
50
+ result: 'pending', detail: '',
51
+ });
52
+ return id;
53
+ }
54
+ async finish(id, result, detail) {
55
+ if (!id)
56
+ return true;
57
+ try {
58
+ // detail is an internal error code or constant, never exception text / API response data.
59
+ await this.ctx.database.set('qgh_audit', id, { result, detail });
60
+ return true;
61
+ }
62
+ catch {
63
+ this.ctx.logger('qq-github-bridge').warn('审计结果写入失败,原始记录保留为 pending;请核对操作结果。');
64
+ return false;
65
+ }
66
+ }
67
+ async cleanup() {
68
+ const now = Date.now();
69
+ await this.ctx.database.remove('qgh_event', { expiresAt: { $lt: new Date(now) } });
70
+ await this.ctx.database.remove('qgh_cooldown', { expiresAt: { $lt: new Date(now) } });
71
+ await this.ctx.database.remove('qgh_audit', { createdAt: { $lt: new Date(now - this.config.auditRetentionDays * 86400000) } });
72
+ }
73
+ }
74
+ exports.Storage = Storage;
75
+ //# sourceMappingURL=storage.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"storage.js","sourceRoot":"","sources":["../../src/services/storage.ts"],"names":[],"mappings":"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"}
package/lib/types.d.ts ADDED
@@ -0,0 +1,93 @@
1
+ export interface Repository {
2
+ owner: string;
3
+ repo: string;
4
+ fullName: string;
5
+ }
6
+ export interface Actor {
7
+ source: 'qq' | 'console';
8
+ id: string;
9
+ qq: string;
10
+ nickname: string;
11
+ groupId: string;
12
+ repository: string;
13
+ }
14
+ export interface IncomingMessage {
15
+ actor: Actor;
16
+ repository: Repository;
17
+ eventKey: string;
18
+ timestamp: number;
19
+ text: string;
20
+ }
21
+ export type Command = {
22
+ type: 'comment';
23
+ number: number;
24
+ body: string;
25
+ } | {
26
+ type: 'close';
27
+ number: number;
28
+ } | {
29
+ type: 'open';
30
+ number: number;
31
+ } | {
32
+ type: 'admin-add';
33
+ qq: string;
34
+ } | {
35
+ type: 'admin-remove';
36
+ qq: string;
37
+ } | {
38
+ type: 'admin-list';
39
+ page: number;
40
+ } | {
41
+ type: 'confirm';
42
+ code: string;
43
+ } | {
44
+ type: 'invalid';
45
+ message: string;
46
+ };
47
+ export type MutatingCommand = Exclude<Command, {
48
+ type: 'invalid' | 'confirm' | 'admin-list';
49
+ }>;
50
+ export interface Outcome {
51
+ message: string;
52
+ result?: 'success' | 'noop';
53
+ }
54
+ export interface AdminView {
55
+ qq: string;
56
+ addedBy: string;
57
+ createdAt: string;
58
+ }
59
+ export interface AuditView {
60
+ id: string;
61
+ actorId: string;
62
+ actorQQ: string;
63
+ source: string;
64
+ action: string;
65
+ target: string;
66
+ groupId: string;
67
+ repository: string;
68
+ createdAt: string;
69
+ result: string;
70
+ detail: string;
71
+ }
72
+ export interface Dashboard {
73
+ admins: AdminView[];
74
+ hasMoreAdmins: boolean;
75
+ superAdmins: string[];
76
+ bindings: Array<{
77
+ groupId: string;
78
+ repository: string;
79
+ enabled: boolean;
80
+ }>;
81
+ audits: AuditView[];
82
+ auditEnabled: boolean;
83
+ auditRetentionDays: number;
84
+ }
85
+ export interface TokenStatus {
86
+ login: string;
87
+ repositories: Array<{
88
+ repository: string;
89
+ accessible: boolean;
90
+ message: string;
91
+ }>;
92
+ message: string;
93
+ }
package/lib/types.js ADDED
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,8 @@
1
+ /** Only deliberately authored messages may cross the chat / console boundary. */
2
+ export declare class BridgeError extends Error {
3
+ readonly code: string;
4
+ readonly uncertain: boolean;
5
+ constructor(code: string, message: string, uncertain?: boolean);
6
+ }
7
+ export declare function publicError(error: unknown): BridgeError;
8
+ export declare function redact(text: string, token: string): string;
@@ -0,0 +1,28 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.BridgeError = void 0;
4
+ exports.publicError = publicError;
5
+ exports.redact = redact;
6
+ /** Only deliberately authored messages may cross the chat / console boundary. */
7
+ class BridgeError extends Error {
8
+ code;
9
+ uncertain;
10
+ constructor(code, message, uncertain = false) {
11
+ super(message);
12
+ this.code = code;
13
+ this.uncertain = uncertain;
14
+ this.name = 'BridgeError';
15
+ }
16
+ }
17
+ exports.BridgeError = BridgeError;
18
+ function publicError(error) {
19
+ return error instanceof BridgeError
20
+ ? error
21
+ : new BridgeError('internal', '内部服务或数据库暂不可用,操作未能确认完成。请联系部署者检查服务。');
22
+ }
23
+ function redact(text, token) {
24
+ if (token)
25
+ text = text.split(token).join('[已隐藏敏感信息]');
26
+ return text.replace(/(?:gh[pousr]_[A-Za-z0-9_]+|github_pat_[A-Za-z0-9_]+)/g, '[已隐藏敏感信息]');
27
+ }
28
+ //# sourceMappingURL=errors.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"errors.js","sourceRoot":"","sources":["../../src/utils/errors.ts"],"names":[],"mappings":";;;AAQA,kCAIC;AAED,wBAGC;AAjBD,iFAAiF;AACjF,MAAa,WAAY,SAAQ,KAAK;IACR;IAA+C;IAA3E,YAA4B,IAAY,EAAE,OAAe,EAAkB,YAAY,KAAK;QAC1F,KAAK,CAAC,OAAO,CAAC,CAAA;QADY,SAAI,GAAJ,IAAI,CAAQ;QAAmC,cAAS,GAAT,SAAS,CAAQ;QAE1F,IAAI,CAAC,IAAI,GAAG,aAAa,CAAA;IAC3B,CAAC;CACF;AALD,kCAKC;AAED,SAAgB,WAAW,CAAC,KAAc;IACxC,OAAO,KAAK,YAAY,WAAW;QACjC,CAAC,CAAC,KAAK;QACP,CAAC,CAAC,IAAI,WAAW,CAAC,UAAU,EAAE,mCAAmC,CAAC,CAAA;AACtE,CAAC;AAED,SAAgB,MAAM,CAAC,IAAY,EAAE,KAAa;IAChD,IAAI,KAAK;QAAE,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;IACrD,OAAO,IAAI,CAAC,OAAO,CAAC,uDAAuD,EAAE,WAAW,CAAC,CAAA;AAC3F,CAAC"}
@@ -0,0 +1,11 @@
1
+ /** Bounded, instance-owned queue. A rejected task never poisons the next one. */
2
+ export declare class SerialQueue {
3
+ private readonly capacity;
4
+ private tail;
5
+ private pending;
6
+ private closed;
7
+ constructor(capacity?: number);
8
+ run<T>(task: () => Promise<T>): Promise<T>;
9
+ close(): void;
10
+ drain(): Promise<void>;
11
+ }
@@ -0,0 +1,38 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.SerialQueue = void 0;
4
+ const errors_1 = require("./errors");
5
+ /** Bounded, instance-owned queue. A rejected task never poisons the next one. */
6
+ class SerialQueue {
7
+ capacity;
8
+ tail = Promise.resolve();
9
+ pending = 0;
10
+ closed = false;
11
+ constructor(capacity = 64) {
12
+ this.capacity = capacity;
13
+ }
14
+ async run(task) {
15
+ if (this.closed)
16
+ throw new errors_1.BridgeError('disposed', '插件正在停用,请稍后再试。');
17
+ if (this.pending >= this.capacity)
18
+ throw new errors_1.BridgeError('busy', '当前操作较多,请稍后再试。');
19
+ this.pending++;
20
+ const previous = this.tail;
21
+ let release;
22
+ this.tail = new Promise(resolve => { release = resolve; });
23
+ try {
24
+ await previous;
25
+ if (this.closed)
26
+ throw new errors_1.BridgeError('disposed', '插件正在停用,请稍后再试。');
27
+ return await task();
28
+ }
29
+ finally {
30
+ this.pending--;
31
+ release();
32
+ }
33
+ }
34
+ close() { this.closed = true; }
35
+ drain() { return this.tail; }
36
+ }
37
+ exports.SerialQueue = SerialQueue;
38
+ //# sourceMappingURL=lock.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lock.js","sourceRoot":"","sources":["../../src/utils/lock.ts"],"names":[],"mappings":";;;AAAA,qCAAsC;AAEtC,iFAAiF;AACjF,MAAa,WAAW;IAKO;IAJrB,IAAI,GAAkB,OAAO,CAAC,OAAO,EAAE,CAAA;IACvC,OAAO,GAAG,CAAC,CAAA;IACX,MAAM,GAAG,KAAK,CAAA;IAEtB,YAA6B,WAAW,EAAE;QAAb,aAAQ,GAAR,QAAQ,CAAK;IAAG,CAAC;IAE9C,KAAK,CAAC,GAAG,CAAI,IAAsB;QACjC,IAAI,IAAI,CAAC,MAAM;YAAE,MAAM,IAAI,oBAAW,CAAC,UAAU,EAAE,eAAe,CAAC,CAAA;QACnE,IAAI,IAAI,CAAC,OAAO,IAAI,IAAI,CAAC,QAAQ;YAAE,MAAM,IAAI,oBAAW,CAAC,MAAM,EAAE,eAAe,CAAC,CAAA;QACjF,IAAI,CAAC,OAAO,EAAE,CAAA;QACd,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,CAAA;QAC1B,IAAI,OAAoB,CAAA;QACxB,IAAI,CAAC,IAAI,GAAG,IAAI,OAAO,CAAO,OAAO,CAAC,EAAE,GAAG,OAAO,GAAG,OAAO,CAAA,CAAC,CAAC,CAAC,CAAA;QAC/D,IAAI,CAAC;YACH,MAAM,QAAQ,CAAA;YACd,IAAI,IAAI,CAAC,MAAM;gBAAE,MAAM,IAAI,oBAAW,CAAC,UAAU,EAAE,eAAe,CAAC,CAAA;YACnE,OAAO,MAAM,IAAI,EAAE,CAAA;QACrB,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,OAAO,EAAE,CAAA;YACd,OAAO,EAAE,CAAA;QACX,CAAC;IACH,CAAC;IAED,KAAK,KAAK,IAAI,CAAC,MAAM,GAAG,IAAI,CAAA,CAAC,CAAC;IAC9B,KAAK,KAAK,OAAO,IAAI,CAAC,IAAI,CAAA,CAAC,CAAC;CAC7B;AA1BD,kCA0BC"}
@@ -0,0 +1,9 @@
1
+ import type { Config } from '../config';
2
+ import type { Command } from '../types';
3
+ export declare class MessageParser {
4
+ private readonly config;
5
+ private readonly reply;
6
+ constructor(config: Config);
7
+ parse(text: string): Command | null;
8
+ private number;
9
+ }
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.MessageParser = void 0;
4
+ const re2_wasm_1 = require("re2-wasm");
5
+ const config_1 = require("../config");
6
+ const errors_1 = require("./errors");
7
+ class MessageParser {
8
+ config;
9
+ reply;
10
+ constructor(config) {
11
+ this.config = config;
12
+ const pattern = config.replyPattern || `^${config.replyPrefix}(?<number>\\d+)\\s+(?<body>[\\s\\S]+)$`;
13
+ if (pattern.length > 1024 || !pattern.startsWith('^') || !pattern.endsWith('$') ||
14
+ !pattern.includes('(?<number>') || !pattern.includes('(?<body>')) {
15
+ throw new errors_1.BridgeError('config-regex', '回复正则最多 1024 字符,须使用 ^ 和 $ 全文匹配,并声明 number 和 body 命名捕获组。');
16
+ }
17
+ try {
18
+ this.reply = new re2_wasm_1.RE2(pattern, 'u');
19
+ }
20
+ catch {
21
+ // Never expose the pattern: a misconfigured pattern could itself contain secrets.
22
+ throw new errors_1.BridgeError('config-regex', '回复正则无效或使用了 RE2 不支持的语法(如回溯引用、前后查找)。');
23
+ }
24
+ }
25
+ parse(text) {
26
+ if (text.length > this.config.maxCommentLength + 4096)
27
+ return null;
28
+ const { adminPrefix, closePrefix, openPrefix } = this.config;
29
+ // Reserve privileged command namespaces before trying any custom comment expression.
30
+ if (text.startsWith(adminPrefix) && /^(?:\s|$)/.test(text.slice(adminPrefix.length))) {
31
+ const parts = text.split(/\s+/);
32
+ if (parts[1] === 'confirm' && parts.length === 3 && /^[a-f0-9]{12}$/.test(parts[2])) {
33
+ return { type: 'confirm', code: parts[2] };
34
+ }
35
+ if (parts[1] === 'list' && (parts.length === 2 || (parts.length === 3 && /^[1-9][0-9]{0,5}$/.test(parts[2])))) {
36
+ return { type: 'admin-list', page: parts[2] ? Number(parts[2]) : 1 };
37
+ }
38
+ if (parts.length === 3 && config_1.QQ_PATTERN.test(parts[2])) {
39
+ if (parts[1] === 'add')
40
+ return { type: 'admin-add', qq: parts[2] };
41
+ if (parts[1] === 'remove')
42
+ return { type: 'admin-remove', qq: parts[2] };
43
+ }
44
+ return { type: 'invalid', message: `用法:${adminPrefix} add/remove QQ号;${adminPrefix} list [页码];${adminPrefix} confirm 确认码。` };
45
+ }
46
+ for (const [prefix, type] of [[closePrefix, 'close'], [openPrefix, 'open']]) {
47
+ if (!text.startsWith(prefix))
48
+ continue;
49
+ const suffix = text.slice(prefix.length);
50
+ // Ordinary words starting with the prefix are not commands.
51
+ if (suffix && !/^[0-9\s]/.test(suffix))
52
+ continue;
53
+ const number = this.number(suffix);
54
+ return number ? { type, number } : { type: 'invalid', message: `用法:${prefix}1003(编号须为正整数)。` };
55
+ }
56
+ const match = this.reply.exec(text);
57
+ // An alternative branch or a terminal newline must not defeat full matching.
58
+ if (match && match.index === 0 && match[0] === text) {
59
+ const number = this.number(match.groups?.number);
60
+ const body = match.groups?.body;
61
+ if (!number)
62
+ return { type: 'invalid', message: 'Issue / PR 编号必须是有效的正整数。' };
63
+ if (!body?.trim())
64
+ return { type: 'invalid', message: '评论正文不能为空。' };
65
+ if (body.length > this.config.maxCommentLength)
66
+ return { type: 'invalid', message: `评论过长,最多 ${this.config.maxCommentLength} 字符。` };
67
+ if (!this.config.allowMultiline && /[\r\n]/.test(body))
68
+ return { type: 'invalid', message: '当前配置不允许多行评论。' };
69
+ if (/\u0000/.test(body))
70
+ return { type: 'invalid', message: '评论包含不支持的空字符。' };
71
+ return { type: 'comment', number, body };
72
+ }
73
+ if (!this.config.replyPattern && new RegExp(`^${this.config.replyPrefix}[0-9]+\\s*$`).test(text)) {
74
+ return { type: 'invalid', message: '评论正文不能为空。' };
75
+ }
76
+ return null;
77
+ }
78
+ number(value) {
79
+ if (!value || !/^[1-9][0-9]*$/.test(value))
80
+ return;
81
+ const number = Number(value);
82
+ if (Number.isSafeInteger(number))
83
+ return number;
84
+ }
85
+ }
86
+ exports.MessageParser = MessageParser;
87
+ //# sourceMappingURL=parser.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/utils/parser.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,101 @@
1
1
  {
2
2
  "name": "koishi-plugin-qq-github-bridge",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "通过 QQ 群安全评论、关闭和重开 GitHub Issue / PR,提供独立管理员与控制台管理。",
5
+ "main": "lib/index.js",
6
+ "types": "lib/index.d.ts",
7
+ "files": [
8
+ "lib",
9
+ "dist",
10
+ "src",
11
+ "client",
12
+ "README.md",
13
+ "LICENSE",
14
+ "docs"
15
+ ],
16
+ "license": "MIT",
17
+ "author": "AoXuan (https://github.com/ClozyA)",
18
+ "homepage": "https://github.com/ClozyA/koishi-plugin-qq-github-bridge#readme",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/ClozyA/koishi-plugin-qq-github-bridge.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/ClozyA/koishi-plugin-qq-github-bridge/issues"
25
+ },
26
+ "publishConfig": {
27
+ "access": "public",
28
+ "registry": "https://registry.npmjs.org/"
29
+ },
30
+ "keywords": [
31
+ "koishi",
32
+ "plugin",
33
+ "qq",
34
+ "onebot",
35
+ "napcat",
36
+ "github"
37
+ ],
38
+ "engines": {
39
+ "node": ">=22"
40
+ },
41
+ "scripts": {
42
+ "typecheck": "tsc --noEmit && node scripts/client-types.cjs && vue-tsc --noEmit -p client/tsconfig.json",
43
+ "build": "tsc -p tsconfig.build.json && node scripts/build-client.cjs",
44
+ "test": "vitest run",
45
+ "preview": "npm run build && node scripts/preview-console.cjs",
46
+ "check": "npm run typecheck && npm run build && npm test",
47
+ "check:package": "node scripts/check-package.cjs",
48
+ "release:publish": "node scripts/publish-release.cjs",
49
+ "prepack": "npm run build"
50
+ },
51
+ "peerDependencies": {
52
+ "@koishijs/plugin-auth": "^4.1.7",
53
+ "@koishijs/plugin-console": "^5.30.11",
54
+ "koishi": "^4.18.11"
55
+ },
56
+ "peerDependenciesMeta": {
57
+ "@koishijs/plugin-console": {
58
+ "optional": true
59
+ },
60
+ "@koishijs/plugin-auth": {
61
+ "optional": true
62
+ }
63
+ },
64
+ "dependencies": {
65
+ "re2-wasm": "^1.0.2"
66
+ },
67
+ "devDependencies": {
68
+ "@koishijs/client": "5.30.11",
69
+ "@koishijs/plugin-adapter-onebot": "6.0.2",
70
+ "@koishijs/plugin-auth": "4.1.7",
71
+ "@koishijs/plugin-console": "5.30.11",
72
+ "@koishijs/plugin-database-sqlite": "4.7.0",
73
+ "@koishijs/plugin-http": "0.6.3",
74
+ "@koishijs/plugin-server": "3.2.9",
75
+ "@types/node": "^22.0.0",
76
+ "@vitejs/plugin-vue": "6.0.10",
77
+ "happy-dom": "21.0.4",
78
+ "koishi": "4.18.11",
79
+ "tar": "7.5.22",
80
+ "typescript": "~5.9.3",
81
+ "vitest": "^5.0.5",
82
+ "vue": "^3.5.12",
83
+ "vue-tsc": "^3.1.0"
84
+ },
85
+ "koishi": {
86
+ "description": {
87
+ "zh": "QQ 群与 GitHub Issue / PR 桥接,独立权限、持久去重和后台管理。"
88
+ },
89
+ "service": {
90
+ "required": [
91
+ "database",
92
+ "http"
93
+ ],
94
+ "optional": [
95
+ "console",
96
+ "auth"
97
+ ]
98
+ },
99
+ "browser": false
100
+ }
101
+ }