@zhin.js/adapter-github 0.1.27 โ 0.1.30
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/adapter.d.ts +2 -3
- package/lib/adapter.d.ts.map +1 -1
- package/lib/adapter.js +26 -678
- package/lib/adapter.js.map +1 -1
- package/lib/index.js +516 -0
- package/lib/index.js.map +1 -1
- package/package.json +5 -5
- package/skills/github/SKILL.md +53 -4
- package/src/adapter.ts +27 -693
- package/src/index.ts +475 -1
package/src/adapter.ts
CHANGED
|
@@ -6,10 +6,9 @@ import {
|
|
|
6
6
|
Adapter,
|
|
7
7
|
Plugin,
|
|
8
8
|
Message,
|
|
9
|
-
ZhinTool,
|
|
10
9
|
} from 'zhin.js';
|
|
11
10
|
import { GitHubBot } from './bot.js';
|
|
12
|
-
import type { GitHubBotConfig, EventType, GenericWebhookPayload, Subscription
|
|
11
|
+
import type { GitHubBotConfig, EventType, GenericWebhookPayload, Subscription } from './types.js';
|
|
13
12
|
import { GitHubAPI, GitHubOAuthClient, exchangeOAuthCode } from './api.js';
|
|
14
13
|
import type { IssueCommentPayload, PRReviewCommentPayload, PRReviewPayload } from './types.js';
|
|
15
14
|
|
|
@@ -29,36 +28,50 @@ const OAUTH_STATE_TTL = 5 * 60 * 1000;
|
|
|
29
28
|
function formatNotification(event: string, p: GenericWebhookPayload): string {
|
|
30
29
|
const repo = p.repository.full_name;
|
|
31
30
|
const sender = p.sender.login;
|
|
31
|
+
const repoUrl = p.repository.html_url;
|
|
32
32
|
switch (event) {
|
|
33
33
|
case 'push': {
|
|
34
34
|
const branch = p.ref?.replace('refs/heads/', '') || '?';
|
|
35
35
|
const commits = p.commits || [];
|
|
36
|
-
|
|
36
|
+
const compareUrl = commits.length >= 2
|
|
37
|
+
? `${repoUrl}/compare/${commits[0].id.substring(0, 12)}...${commits[commits.length - 1].id.substring(0, 12)}`
|
|
38
|
+
: commits.length === 1 ? `${repoUrl}/commit/${commits[0].id}` : '';
|
|
39
|
+
let msg = `๐ฆ ${repo}\n๐ฟ ${sender} pushed ${commits.length} commit(s) to \`${branch}\`\n`;
|
|
37
40
|
if (commits.length) {
|
|
38
|
-
msg +=
|
|
39
|
-
msg += commits.slice(0,
|
|
40
|
-
|
|
41
|
+
msg += '\n';
|
|
42
|
+
msg += commits.slice(0, 5).map(c =>
|
|
43
|
+
` โข [\`${c.id.substring(0, 7)}\`](${repoUrl}/commit/${c.id}) ${c.message.split('\n')[0]}`
|
|
44
|
+
).join('\n');
|
|
45
|
+
if (commits.length > 5) msg += `\n ... +${commits.length - 5} more`;
|
|
41
46
|
}
|
|
47
|
+
if (compareUrl) msg += `\n\n๐ ${compareUrl}`;
|
|
42
48
|
return msg;
|
|
43
49
|
}
|
|
44
50
|
case 'issues': {
|
|
45
51
|
const i = p.issue!;
|
|
46
|
-
const act = p.action === 'opened' ? 'opened' : p.action === 'closed' ? 'closed' : 'updated'
|
|
47
|
-
|
|
52
|
+
const act = p.action === 'opened' ? '๐ opened' : p.action === 'closed' ? 'โ
closed' : `๐ ${p.action || 'updated'}`;
|
|
53
|
+
let msg = `๐ ${repo}\n๐ค ${sender} ${act} issue #${i.number}\n๐ ${i.title}`;
|
|
54
|
+
msg += `\n๐ ${i.html_url}`;
|
|
55
|
+
return msg;
|
|
48
56
|
}
|
|
49
57
|
case 'star': {
|
|
50
58
|
const starred = p.action !== 'deleted';
|
|
51
|
-
return `${starred ? 'โญ' : '๐'} ${repo}\n๐ค ${sender} ${starred ? 'starred' : 'unstarred'}`;
|
|
59
|
+
return `${starred ? 'โญ' : '๐'} ${repo}\n๐ค ${sender} ${starred ? 'starred' : 'unstarred'}\n๐ ${repoUrl}`;
|
|
52
60
|
}
|
|
53
61
|
case 'fork':
|
|
54
|
-
return `๐ด ${repo}\n๐ค ${sender} forked โ ${p.forkee!.full_name}`;
|
|
62
|
+
return `๐ด ${repo}\n๐ค ${sender} forked โ ${p.forkee!.full_name}\n๐ ${p.forkee!.html_url}`;
|
|
55
63
|
case 'pull_request': {
|
|
56
64
|
const pr = p.pull_request!;
|
|
57
|
-
const act = p.action === 'opened' ? 'opened'
|
|
58
|
-
|
|
65
|
+
const act = p.action === 'opened' ? '๐ opened'
|
|
66
|
+
: p.action === 'closed' ? (pr.state === 'closed' ? 'โ closed' : 'โ
merged')
|
|
67
|
+
: `๐ ${p.action || 'updated'}`;
|
|
68
|
+
let msg = `๐ ${repo}\n๐ค ${sender} ${act} PR #${pr.number}\n๐ ${pr.title}`;
|
|
69
|
+
msg += `\n๐ฟ ${pr.head.ref} โ ${pr.base.ref}`;
|
|
70
|
+
msg += `\n๐ ${pr.html_url}`;
|
|
71
|
+
return msg;
|
|
59
72
|
}
|
|
60
73
|
default:
|
|
61
|
-
return `๐ฌ ${repo}\n${event} by ${sender}`;
|
|
74
|
+
return `๐ฌ ${repo}\n๐ก ${event}${p.action ? ` (${p.action})` : ''} by ${sender}\n๐ ${repoUrl}`;
|
|
62
75
|
}
|
|
63
76
|
}
|
|
64
77
|
|
|
@@ -77,12 +90,11 @@ export class GitHubAdapter extends Adapter<GitHubBot> {
|
|
|
77
90
|
}
|
|
78
91
|
|
|
79
92
|
async start(): Promise<void> {
|
|
80
|
-
this.registerGitHubTools();
|
|
81
93
|
await super.start();
|
|
82
94
|
}
|
|
83
95
|
|
|
84
96
|
/** ่ทๅ็ฌฌไธไธชๅฏ็จ bot ็ API (ๅทฅๅ
ท็จ) */
|
|
85
|
-
|
|
97
|
+
getAPI(): GitHubAPI | null {
|
|
86
98
|
const bot = this.bots.values().next().value as GitHubBot | undefined;
|
|
87
99
|
return bot?.api || null;
|
|
88
100
|
}
|
|
@@ -294,684 +306,6 @@ export class GitHubAdapter extends Adapter<GitHubBot> {
|
|
|
294
306
|
this.plugin.logger.debug('GitHub Webhook: POST /pub/github/webhook');
|
|
295
307
|
}
|
|
296
308
|
|
|
297
|
-
// โโ GitHub ็ฎก็ๅทฅๅ
ท โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
298
|
-
|
|
299
|
-
private registerGitHubTools(): void {
|
|
300
|
-
|
|
301
|
-
// --- PR ---
|
|
302
|
-
this.addTool(
|
|
303
|
-
new ZhinTool('github_pr')
|
|
304
|
-
.desc('GitHub PR ๆไฝ๏ผlist/view/diff/merge/create/review/close')
|
|
305
|
-
.keyword('pr', 'pull request', 'ๅๅนถ', 'merge', 'review', 'ๅฎกๆฅ', 'ๆๅ่ฏทๆฑ')
|
|
306
|
-
.tag('github', 'pr')
|
|
307
|
-
.param('action', { type: 'string', description: 'list|view|diff|merge|create|review|close', enum: ['list','view','diff','merge','create','review','close'] }, true)
|
|
308
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
309
|
-
.param('number', { type: 'number', description: 'PR ็ผๅท' })
|
|
310
|
-
.param('title', { type: 'string', description: 'PR ๆ ้ข (create)' })
|
|
311
|
-
.param('body', { type: 'string', description: 'PR ๆ่ฟฐ / Review ่ฏ่ฏญ' })
|
|
312
|
-
.param('head', { type: 'string', description: 'ๆบๅๆฏ (create)' })
|
|
313
|
-
.param('base', { type: 'string', description: '็ฎๆ ๅๆฏ (create๏ผ้ป่ฎค main)' })
|
|
314
|
-
.param('state', { type: 'string', description: 'open/closed/all (list)' })
|
|
315
|
-
.param('approve', { type: 'boolean', description: 'review ๆถ approve' })
|
|
316
|
-
.param('method', { type: 'string', description: 'squash/merge/rebase (merge)' })
|
|
317
|
-
.execute(async (args) => {
|
|
318
|
-
const api = this.getAPI();
|
|
319
|
-
if (!api) return 'โ ๆฒกๆๅฏ็จ็ GitHub bot';
|
|
320
|
-
const action = args.action as PrAction;
|
|
321
|
-
const repo = args.repo as string;
|
|
322
|
-
const num = args.number as number | undefined;
|
|
323
|
-
|
|
324
|
-
switch (action) {
|
|
325
|
-
case 'list': {
|
|
326
|
-
const r = await api.listPRs(repo, (args.state as string) || 'open');
|
|
327
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
328
|
-
if (!r.data.length) return `๐ญ ๆฒกๆ ${args.state || 'open'} ็ถๆ็ PR`;
|
|
329
|
-
return r.data.map((p: any) =>
|
|
330
|
-
`#${p.number} ${p.draft ? '[Draft] ' : ''}${p.title}\n ๐ค ${p.user.login} | ๐ฟ ${p.head.ref} โ ${p.base.ref} | ${p.state}`
|
|
331
|
-
).join('\n\n');
|
|
332
|
-
}
|
|
333
|
-
case 'view': {
|
|
334
|
-
if (!num) return 'โ ่ฏทๆไพ PR ็ผๅท';
|
|
335
|
-
const r = await api.getPR(repo, num);
|
|
336
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
337
|
-
const p = r.data;
|
|
338
|
-
return [
|
|
339
|
-
`#${p.number} ${p.title}`,
|
|
340
|
-
`๐ค ${p.user.login} | ${p.state} | ๐ฟ ${p.head.ref} โ ${p.base.ref}`,
|
|
341
|
-
`๐
${p.created_at?.split('T')[0]} | +${p.additions} -${p.deletions} (${p.changed_files} files)`,
|
|
342
|
-
p.body ? `\n${p.body.slice(0, 500)}${p.body.length > 500 ? '...' : ''}` : '',
|
|
343
|
-
`\n๐ ${p.html_url}`,
|
|
344
|
-
].filter(Boolean).join('\n');
|
|
345
|
-
}
|
|
346
|
-
case 'diff': {
|
|
347
|
-
if (!num) return 'โ ่ฏทๆไพ PR ็ผๅท';
|
|
348
|
-
const r = await api.getPRDiff(repo, num);
|
|
349
|
-
if (!r.ok) return `โ ่ทๅ diff ๅคฑ่ดฅ`;
|
|
350
|
-
const lines = r.data.split('\n');
|
|
351
|
-
return lines.length > 100 ? lines.slice(0, 100).join('\n') + `\n\n... (ๅ
ฑ ${lines.length} ่ก)` : r.data;
|
|
352
|
-
}
|
|
353
|
-
case 'merge': {
|
|
354
|
-
if (!num) return 'โ ่ฏทๆไพ PR ็ผๅท';
|
|
355
|
-
const r = await api.mergePR(repo, num, (args.method as string) || 'squash');
|
|
356
|
-
return r.ok ? `โ
PR #${num} ๅทฒๅๅนถ` : `โ ${r.data?.message || JSON.stringify(r.data)}`;
|
|
357
|
-
}
|
|
358
|
-
case 'create': {
|
|
359
|
-
if (!args.title) return 'โ ่ฏทๆไพ PR ๆ ้ข';
|
|
360
|
-
if (!args.head) return 'โ ่ฏทๆไพๆบๅๆฏ (head)';
|
|
361
|
-
const r = await api.createPR(repo, args.title as string, (args.body as string) || '', args.head as string, (args.base as string) || 'main');
|
|
362
|
-
return r.ok ? `โ
PR ๅทฒๅๅปบ: ${r.data.html_url}` : `โ ${r.data?.message || JSON.stringify(r.data)}`;
|
|
363
|
-
}
|
|
364
|
-
case 'review': {
|
|
365
|
-
if (!num) return 'โ ่ฏทๆไพ PR ็ผๅท';
|
|
366
|
-
const event = args.approve ? 'APPROVE' : 'COMMENT';
|
|
367
|
-
const r = await api.createPRReview(repo, num, event as any, (args.body as string) || undefined);
|
|
368
|
-
return r.ok ? `โ
PR #${num} ${args.approve ? 'ๅทฒๆนๅ' : 'ๅทฒ่ฏ่ฎบ'}` : `โ ${r.data?.message}`;
|
|
369
|
-
}
|
|
370
|
-
case 'close': {
|
|
371
|
-
if (!num) return 'โ ่ฏทๆไพ PR ็ผๅท';
|
|
372
|
-
const r = await api.closePR(repo, num);
|
|
373
|
-
return r.ok ? `โ
PR #${num} ๅทฒๅ
ณ้ญ` : `โ ${r.data?.message}`;
|
|
374
|
-
}
|
|
375
|
-
default: return `โ ๆช็ฅๆไฝ: ${action}`;
|
|
376
|
-
}
|
|
377
|
-
}),
|
|
378
|
-
);
|
|
379
|
-
|
|
380
|
-
// --- Issue ---
|
|
381
|
-
this.addTool(
|
|
382
|
-
new ZhinTool('github_issue')
|
|
383
|
-
.desc('GitHub Issue ๆไฝ๏ผlist/view/create/close/comment')
|
|
384
|
-
.keyword('issue', '้ฎ้ข', 'bug', 'ๅๅปบissue', 'ๅ
ณ้ญissue')
|
|
385
|
-
.tag('github', 'issue')
|
|
386
|
-
.param('action', { type: 'string', description: 'list|view|create|close|comment', enum: ['list','view','create','close','comment'] }, true)
|
|
387
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
388
|
-
.param('number', { type: 'number', description: 'Issue ็ผๅท' })
|
|
389
|
-
.param('title', { type: 'string', description: 'Issue ๆ ้ข (create)' })
|
|
390
|
-
.param('body', { type: 'string', description: 'Issue ๅ
ๅฎน / ่ฏ่ฎบๅ
ๅฎน' })
|
|
391
|
-
.param('labels', { type: 'string', description: 'ๆ ็ญพ๏ผ้ๅทๅ้ (create)' })
|
|
392
|
-
.param('state', { type: 'string', description: 'open/closed/all (list)' })
|
|
393
|
-
.execute(async (args) => {
|
|
394
|
-
const api = this.getAPI();
|
|
395
|
-
if (!api) return 'โ ๆฒกๆๅฏ็จ็ GitHub bot';
|
|
396
|
-
const action = args.action as IssueAction;
|
|
397
|
-
const repo = args.repo as string;
|
|
398
|
-
const num = args.number as number | undefined;
|
|
399
|
-
|
|
400
|
-
switch (action) {
|
|
401
|
-
case 'list': {
|
|
402
|
-
const r = await api.listIssues(repo, (args.state as string) || 'open');
|
|
403
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
404
|
-
// ่ฟๆปคๆ PR (GitHub API ็ issues ๆฅๅฃไน่ฟๅ PR)
|
|
405
|
-
const issues = r.data.filter((i: any) => !i.pull_request);
|
|
406
|
-
if (!issues.length) return `๐ญ ๆฒกๆ ${args.state || 'open'} ็ถๆ็ Issue`;
|
|
407
|
-
return issues.map((i: any) => {
|
|
408
|
-
const labels = i.labels?.map((l: any) => l.name).join(', ') || '';
|
|
409
|
-
return `#${i.number} ${i.title}\n ๐ค ${i.user.login}${labels ? ` | ๐ท๏ธ ${labels}` : ''} | ${i.state}`;
|
|
410
|
-
}).join('\n\n');
|
|
411
|
-
}
|
|
412
|
-
case 'view': {
|
|
413
|
-
if (!num) return 'โ ่ฏทๆไพ Issue ็ผๅท';
|
|
414
|
-
const r = await api.getIssue(repo, num);
|
|
415
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
416
|
-
const i = r.data;
|
|
417
|
-
return [
|
|
418
|
-
`#${i.number} ${i.title}`,
|
|
419
|
-
`๐ค ${i.user.login} | ${i.state} | ๐
${i.created_at?.split('T')[0]}`,
|
|
420
|
-
i.labels?.length ? `๐ท๏ธ ${i.labels.map((l: any) => l.name).join(', ')}` : null,
|
|
421
|
-
i.body ? `\n${i.body.slice(0, 500)}${i.body.length > 500 ? '...' : ''}` : '',
|
|
422
|
-
`\n๐ ${i.html_url}`,
|
|
423
|
-
].filter(Boolean).join('\n');
|
|
424
|
-
}
|
|
425
|
-
case 'create': {
|
|
426
|
-
if (!args.title) return 'โ ่ฏทๆไพ Issue ๆ ้ข';
|
|
427
|
-
const labels = args.labels ? (args.labels as string).split(',').map(s => s.trim()) : undefined;
|
|
428
|
-
const r = await api.createIssue(repo, args.title as string, (args.body as string) || undefined, labels);
|
|
429
|
-
return r.ok ? `โ
Issue ๅทฒๅๅปบ: ${r.data.html_url}` : `โ ${r.data?.message}`;
|
|
430
|
-
}
|
|
431
|
-
case 'close': {
|
|
432
|
-
if (!num) return 'โ ่ฏทๆไพ Issue ็ผๅท';
|
|
433
|
-
const r = await api.closeIssue(repo, num);
|
|
434
|
-
return r.ok ? `โ
Issue #${num} ๅทฒๅ
ณ้ญ` : `โ ${r.data?.message}`;
|
|
435
|
-
}
|
|
436
|
-
case 'comment': {
|
|
437
|
-
if (!num) return 'โ ่ฏทๆไพ Issue ็ผๅท';
|
|
438
|
-
if (!args.body) return 'โ ่ฏทๆไพ่ฏ่ฎบๅ
ๅฎน';
|
|
439
|
-
const r = await api.createIssueComment(repo, num, args.body as string);
|
|
440
|
-
return r.ok ? `โ
ๅทฒ่ฏ่ฎบ Issue #${num}` : `โ ${JSON.stringify(r.data)}`;
|
|
441
|
-
}
|
|
442
|
-
default: return `โ ๆช็ฅๆไฝ: ${action}`;
|
|
443
|
-
}
|
|
444
|
-
}),
|
|
445
|
-
);
|
|
446
|
-
|
|
447
|
-
// --- Repo ---
|
|
448
|
-
this.addTool(
|
|
449
|
-
new ZhinTool('github_repo')
|
|
450
|
-
.desc('GitHub ไปๅบๆฅ่ฏข๏ผinfo/branches/releases/runs(CI)/stars')
|
|
451
|
-
.keyword('ไปๅบ', 'repo', 'star', 'ๅๆฏ', 'branch', 'release', 'ๅๅธ', 'CI', 'workflow')
|
|
452
|
-
.tag('github', 'repo')
|
|
453
|
-
.param('action', { type: 'string', description: 'info|branches|releases|runs|stars', enum: ['info','branches','releases','runs','stars'] }, true)
|
|
454
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
455
|
-
.param('limit', { type: 'number', description: '่ฟๅๆฐ้๏ผ้ป่ฎค 10' })
|
|
456
|
-
.execute(async (args) => {
|
|
457
|
-
const api = this.getAPI();
|
|
458
|
-
if (!api) return 'โ ๆฒกๆๅฏ็จ็ GitHub bot';
|
|
459
|
-
const action = args.action as RepoAction;
|
|
460
|
-
const repo = args.repo as string;
|
|
461
|
-
const limit = (args.limit as number) || 10;
|
|
462
|
-
|
|
463
|
-
switch (action) {
|
|
464
|
-
case 'info': {
|
|
465
|
-
const r = await api.getRepo(repo);
|
|
466
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
467
|
-
const d = r.data;
|
|
468
|
-
return [
|
|
469
|
-
`๐ฆ ${d.full_name}${d.private ? ' ๐' : ''}`,
|
|
470
|
-
d.description ? `๐ ${d.description}` : null,
|
|
471
|
-
`โญ ${d.stargazers_count} | ๐ด ${d.forks_count} | ๐ ${d.watchers_count}`,
|
|
472
|
-
`๐ฟ ้ป่ฎคๅๆฏ: ${d.default_branch}`,
|
|
473
|
-
d.license ? `๐ ${d.license.name}` : null,
|
|
474
|
-
d.homepage ? `๐ ${d.homepage}` : null,
|
|
475
|
-
`๐
ๅๅปบ: ${d.created_at?.split('T')[0]} | ๆจ้: ${d.pushed_at?.split('T')[0]}`,
|
|
476
|
-
].filter(Boolean).join('\n');
|
|
477
|
-
}
|
|
478
|
-
case 'branches': {
|
|
479
|
-
const r = await api.listBranches(repo, limit);
|
|
480
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
481
|
-
return r.data.length
|
|
482
|
-
? `๐ฟ ๅๆฏ (${r.data.length}):\n${r.data.map((b: any) => ` โข ${b.name}${b.protected ? ' ๐' : ''}`).join('\n')}`
|
|
483
|
-
: 'ๆฒกๆๆพๅฐๅๆฏ';
|
|
484
|
-
}
|
|
485
|
-
case 'releases': {
|
|
486
|
-
const r = await api.listReleases(repo, limit);
|
|
487
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
488
|
-
if (!r.data.length) return '๐ญ ๆๆ ๅๅธ';
|
|
489
|
-
return r.data.map((rel: any) =>
|
|
490
|
-
`${rel.prerelease ? '๐งช' : '๐ฆ'} ${rel.tag_name} โ ${rel.name || '(no title)'}\n ๐
${rel.published_at?.split('T')[0]} | ๐ค ${rel.author?.login}`
|
|
491
|
-
).join('\n\n');
|
|
492
|
-
}
|
|
493
|
-
case 'runs': {
|
|
494
|
-
const r = await api.listWorkflowRuns(repo, limit);
|
|
495
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
496
|
-
const runs = r.data.workflow_runs || [];
|
|
497
|
-
if (!runs.length) return '๐ญ ๆๆ CI ่ฎฐๅฝ';
|
|
498
|
-
return runs.map((run: any) => {
|
|
499
|
-
const icon = run.conclusion === 'success' ? 'โ
' : run.conclusion === 'failure' ? 'โ' : run.status === 'in_progress' ? '๐' : 'โณ';
|
|
500
|
-
return `${icon} #${run.id} ${run.display_title}\n ๐ฟ ${run.head_branch} | ${run.status}${run.conclusion ? '/' + run.conclusion : ''}`;
|
|
501
|
-
}).join('\n\n');
|
|
502
|
-
}
|
|
503
|
-
case 'stars': {
|
|
504
|
-
const r = await api.getRepo(repo);
|
|
505
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
506
|
-
return `โญ ${r.data.stargazers_count} stars | ๐ด ${r.data.forks_count} forks`;
|
|
507
|
-
}
|
|
508
|
-
default: return `โ ๆช็ฅๆฅ่ฏข: ${action}`;
|
|
509
|
-
}
|
|
510
|
-
}),
|
|
511
|
-
);
|
|
512
|
-
|
|
513
|
-
// --- Subscribe ---
|
|
514
|
-
this.addTool(
|
|
515
|
-
new ZhinTool('github_subscribe')
|
|
516
|
-
.desc('่ฎข้
GitHub ไปๅบ Webhook ไบไปถ้็ฅ๏ผ่ทจๅนณๅฐๆจ้ๅฐๅฝๅ่ๅคฉ๏ผ')
|
|
517
|
-
.tag('github', 'subscription')
|
|
518
|
-
.param('repo', { type: 'string', description: 'owner/repo' }, true)
|
|
519
|
-
.param('events', { type: 'array', description: 'ไบไปถ: push, issue, star, fork, unstar, pr๏ผ็็ฉบ=ๅ
จ้จ๏ผ' })
|
|
520
|
-
.execute(async ({ repo, events: evts = [] }, ctx) => {
|
|
521
|
-
if (!ctx?.message) return 'โ ๆ ๆณ่ทๅๆถๆฏไธไธๆ';
|
|
522
|
-
const msg = ctx.message as Message;
|
|
523
|
-
const repoStr = repo as string;
|
|
524
|
-
if (!repoStr.includes('/')) return 'โ ๆ ผๅผๅบไธบ owner/repo';
|
|
525
|
-
|
|
526
|
-
const parsed: EventType[] = [];
|
|
527
|
-
for (const e of evts as string[]) {
|
|
528
|
-
const n = e.toLowerCase();
|
|
529
|
-
if (n === 'pr') parsed.push('pull_request');
|
|
530
|
-
else if (VALID_EVENTS.includes(n as EventType)) parsed.push(n as EventType);
|
|
531
|
-
else return `โ ไธๆฏๆ: ${e}`;
|
|
532
|
-
}
|
|
533
|
-
const subEvents = parsed.length > 0 ? parsed : VALID_EVENTS;
|
|
534
|
-
const db = this.plugin.root?.inject('database') as any;
|
|
535
|
-
const model = db?.models?.get('github_subscriptions');
|
|
536
|
-
if (!model) return 'โ ๆฐๆฎๅบๆชๅฐฑ็ปช';
|
|
537
|
-
|
|
538
|
-
const where = { repo: repoStr, target_id: msg.$channel.id, target_type: msg.$channel.type, adapter: msg.$adapter, bot: msg.$bot };
|
|
539
|
-
const [existing] = await model.select().where(where);
|
|
540
|
-
const eventsJson = JSON.stringify(subEvents);
|
|
541
|
-
if (existing) {
|
|
542
|
-
await model.update({ events: eventsJson }).where({ id: existing.id });
|
|
543
|
-
return `โ
ๅทฒๆดๆฐ่ฎข้
${repoStr}\n๐ข ${subEvents.join(', ')}`;
|
|
544
|
-
}
|
|
545
|
-
const { repo: _r, ...rest } = where;
|
|
546
|
-
await model.insert({ id: Date.now(), repo: repoStr, events: eventsJson, ...rest });
|
|
547
|
-
return `โ
ๅทฒ่ฎข้
${repoStr}\n๐ข ${subEvents.join(', ')}\n๐ก ่ฎฐๅพๅจ GitHub App ๆไปๅบ Settings โ Webhooks ไธญ้
็ฝฎ Webhook`;
|
|
548
|
-
}),
|
|
549
|
-
);
|
|
550
|
-
|
|
551
|
-
// --- Unsubscribe ---
|
|
552
|
-
this.addTool(
|
|
553
|
-
new ZhinTool('github_unsubscribe')
|
|
554
|
-
.desc('ๅๆถ่ฎข้
GitHub ไปๅบไบไปถ้็ฅ')
|
|
555
|
-
.tag('github', 'subscription')
|
|
556
|
-
.param('repo', { type: 'string', description: 'owner/repo' }, true)
|
|
557
|
-
.execute(async ({ repo }, ctx) => {
|
|
558
|
-
if (!ctx?.message) return 'โ ๆ ๆณ่ทๅๆถๆฏไธไธๆ';
|
|
559
|
-
const msg = ctx.message as Message;
|
|
560
|
-
const db = this.plugin.root?.inject('database') as any;
|
|
561
|
-
const model = db?.models?.get('github_subscriptions');
|
|
562
|
-
if (!model) return 'โ ๆฐๆฎๅบๆชๅฐฑ็ปช';
|
|
563
|
-
const [sub] = await model.select().where({ repo: repo as string, target_id: msg.$channel.id, target_type: msg.$channel.type, adapter: msg.$adapter, bot: msg.$bot });
|
|
564
|
-
if (!sub) return `โ ๆชๆพๅฐ่ฎข้
: ${repo}`;
|
|
565
|
-
await model.delete({ id: sub.id });
|
|
566
|
-
return `โ
ๅทฒๅๆถ่ฎข้
${repo}`;
|
|
567
|
-
}),
|
|
568
|
-
);
|
|
569
|
-
|
|
570
|
-
// --- List subscriptions ---
|
|
571
|
-
this.addTool(
|
|
572
|
-
new ZhinTool('github_subscriptions')
|
|
573
|
-
.desc('ๆฅ็ๅฝๅ่ๅคฉ็ GitHub ไบไปถ่ฎข้
ๅ่กจ')
|
|
574
|
-
.tag('github', 'subscription')
|
|
575
|
-
.execute(async (_args, ctx) => {
|
|
576
|
-
if (!ctx?.message) return 'โ ๆ ๆณ่ทๅๆถๆฏไธไธๆ';
|
|
577
|
-
const msg = ctx.message as Message;
|
|
578
|
-
const db = this.plugin.root?.inject('database') as any;
|
|
579
|
-
const model = db?.models?.get('github_subscriptions');
|
|
580
|
-
if (!model) return 'โ ๆฐๆฎๅบๆชๅฐฑ็ปช';
|
|
581
|
-
const subs = await model.select().where({ target_id: msg.$channel.id, target_type: msg.$channel.type, adapter: msg.$adapter, bot: msg.$bot });
|
|
582
|
-
if (!subs?.length) return '๐ญ ๅฝๅๆฒกๆ่ฎข้
';
|
|
583
|
-
return `๐ ่ฎข้
(${subs.length}):\n\n` + subs.map((s: any, i: number) => {
|
|
584
|
-
return `${i + 1}. ${s.repo}\n ๐ข ${safeParseEvents(s.events).join(', ')}`;
|
|
585
|
-
}).join('\n\n');
|
|
586
|
-
}),
|
|
587
|
-
);
|
|
588
|
-
|
|
589
|
-
// --- GitHub OAuth Bind ---
|
|
590
|
-
this.addTool(
|
|
591
|
-
new ZhinTool('github_bind')
|
|
592
|
-
.desc('็ปๅฎ GitHub ่ดฆๅท โ ้่ฟ OAuth ๆๆ๏ผ่ฎฉ bot ไปฅไฝ ็่บซไปฝๆง่ก star/fork ็ญๆไฝ')
|
|
593
|
-
.keyword('github bind', '็ปๅฎgithub', 'github ็ปๅฎ', 'github ๆๆ')
|
|
594
|
-
.tag('github', 'oauth')
|
|
595
|
-
.execute(async (_args, ctx) => {
|
|
596
|
-
if (!ctx?.message) return 'โ ๆ ๆณ่ทๅๆถๆฏไธไธๆ';
|
|
597
|
-
const msg = ctx.message as Message;
|
|
598
|
-
|
|
599
|
-
const bot = this.bots.values().next().value as GitHubBot | undefined;
|
|
600
|
-
if (!bot?.$config.client_id) {
|
|
601
|
-
return 'โ GitHub OAuth ๆช้
็ฝฎ๏ผ้่ฆๅจ bot ้
็ฝฎไธญๆทปๅ client_id ๅ client_secret๏ผ';
|
|
602
|
-
}
|
|
603
|
-
|
|
604
|
-
const nonce = crypto.randomBytes(16).toString('hex');
|
|
605
|
-
const state = `${msg.$adapter}:${msg.$sender.id}:${nonce}`;
|
|
606
|
-
oauthStates.set(state, {
|
|
607
|
-
platform: msg.$adapter,
|
|
608
|
-
platformUid: msg.$sender.id,
|
|
609
|
-
expires: Date.now() + OAUTH_STATE_TTL,
|
|
610
|
-
});
|
|
611
|
-
|
|
612
|
-
const baseUrl = this.publicUrl;
|
|
613
|
-
if (!baseUrl) {
|
|
614
|
-
return 'โ ๆช้
็ฝฎ public_url๏ผๆ ๆณ็ๆ OAuth ้พๆฅ\n๐ก ่ฏทๅจ bot ้
็ฝฎไธญๆทปๅ public_url๏ผๅฆ https://bot.example.com๏ผ';
|
|
615
|
-
}
|
|
616
|
-
const link = `${baseUrl}/pub/github/oauth?state=${encodeURIComponent(state)}`;
|
|
617
|
-
const fullText = `๐ ่ฏท็นๅปไปฅไธ้พๆฅๆๆไฝ ็ GitHub ่ดฆๅท๏ผ\n\n${link}\n\nโฑ๏ธ ้พๆฅๆๆๆ 5 ๅ้`;
|
|
618
|
-
|
|
619
|
-
// ็ฑๅทฅๅ
ท็ดๆฅๅๅฐ็จๆท๏ผ้ฟๅ
AI ๆป็ปๆถๆ้พๆฅๅๆ
|
|
620
|
-
try {
|
|
621
|
-
const targetAdapter = this.plugin.root?.inject(msg.$adapter)
|
|
622
|
-
if (targetAdapter instanceof Adapter) {
|
|
623
|
-
await targetAdapter.sendMessage({
|
|
624
|
-
context: msg.$adapter,
|
|
625
|
-
bot: msg.$bot,
|
|
626
|
-
id: msg.$channel.id,
|
|
627
|
-
type: msg.$channel.type,
|
|
628
|
-
content: fullText,
|
|
629
|
-
});
|
|
630
|
-
}
|
|
631
|
-
} catch (e) {
|
|
632
|
-
this.plugin.logger.warn('github.bind ็ดๅ้พๆฅๅคฑ่ดฅ๏ผๅฐไป
้่ฟ่ฟๅๅผ่ฟๅ้พๆฅ', e);
|
|
633
|
-
}
|
|
634
|
-
|
|
635
|
-
return 'ๅทฒๅๅฝๅไผ่ฏๅ้็ปๅฎ้พๆฅ๏ผ่ฏทๆ้็จๆทๆฅๆถๅนถ็นๅป้พๆฅๅฎๆๆๆใ';
|
|
636
|
-
}),
|
|
637
|
-
);
|
|
638
|
-
|
|
639
|
-
// --- GitHub OAuth Unbind ---
|
|
640
|
-
this.addTool(
|
|
641
|
-
new ZhinTool('github_unbind')
|
|
642
|
-
.desc('่งฃ้ค GitHub ่ดฆๅท็ปๅฎ')
|
|
643
|
-
.keyword('github unbind', '่งฃ็ปgithub', 'github ่งฃ็ป')
|
|
644
|
-
.tag('github', 'oauth')
|
|
645
|
-
.execute(async (_args, ctx) => {
|
|
646
|
-
if (!ctx?.message) return 'โ ๆ ๆณ่ทๅๆถๆฏไธไธๆ';
|
|
647
|
-
const msg = ctx.message as Message;
|
|
648
|
-
const db = this.plugin.root?.inject('database') as any;
|
|
649
|
-
const model = db?.models?.get('github_oauth_users');
|
|
650
|
-
if (!model) return 'โ ๆฐๆฎๅบๆชๅฐฑ็ปช';
|
|
651
|
-
|
|
652
|
-
const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
|
|
653
|
-
if (!existing) return 'โ ไฝ ่ฟๆฒกๆ็ปๅฎ GitHub ่ดฆๅท';
|
|
654
|
-
|
|
655
|
-
await model.delete({ id: existing.id });
|
|
656
|
-
return `โ
ๅทฒ่งฃ้ค GitHub ่ดฆๅท็ปๅฎ๏ผ${existing.github_login}๏ผ`;
|
|
657
|
-
}),
|
|
658
|
-
);
|
|
659
|
-
|
|
660
|
-
// --- GitHub OAuth Whoami ---
|
|
661
|
-
this.addTool(
|
|
662
|
-
new ZhinTool('github_whoami')
|
|
663
|
-
.desc('ๆฅ็ๅฝๅ็ปๅฎ็ GitHub ่ดฆๅทไฟกๆฏ')
|
|
664
|
-
.keyword('github whoami', 'github ๆๆฏ่ฐ', 'github ่ดฆๅท')
|
|
665
|
-
.tag('github', 'oauth')
|
|
666
|
-
.execute(async (_args, ctx) => {
|
|
667
|
-
if (!ctx?.message) return 'โ ๆ ๆณ่ทๅๆถๆฏไธไธๆ';
|
|
668
|
-
const msg = ctx.message as Message;
|
|
669
|
-
const db = this.plugin.root?.inject('database') as any;
|
|
670
|
-
const model = db?.models?.get('github_oauth_users');
|
|
671
|
-
if (!model) return 'โ ๆฐๆฎๅบๆชๅฐฑ็ปช';
|
|
672
|
-
|
|
673
|
-
const [existing] = await model.select().where({ platform: msg.$adapter, platform_uid: msg.$sender.id });
|
|
674
|
-
if (!existing) return 'โ ไฝ ่ฟๆฒกๆ็ปๅฎ GitHub ่ดฆๅท\n๐ก ไฝฟ็จ /github bind ่ฟ่ก็ปๅฎ';
|
|
675
|
-
|
|
676
|
-
const oauthClient = new GitHubOAuthClient(existing.access_token);
|
|
677
|
-
const userRes = await oauthClient.getUser();
|
|
678
|
-
if (!userRes.ok) {
|
|
679
|
-
return `โ ๏ธ ๅทฒ็ปๅฎ ${existing.github_login}๏ผไฝ token ๅฏ่ฝๅทฒๅคฑๆ\n๐ก ่ฏทไฝฟ็จ /github bind ้ๆฐๆๆ`;
|
|
680
|
-
}
|
|
681
|
-
|
|
682
|
-
const u = userRes.data;
|
|
683
|
-
return [
|
|
684
|
-
`๐ GitHub ่ดฆๅทๅทฒ็ปๅฎ`,
|
|
685
|
-
`๐ค ${u.login}${u.name ? ` (${u.name})` : ''}`,
|
|
686
|
-
`๐ ๆๆ่ๅด: ${existing.scope || 'N/A'}`,
|
|
687
|
-
`๐
็ปๅฎๆถ้ด: ${new Date(existing.created_at).toLocaleDateString()}`,
|
|
688
|
-
].join('\n');
|
|
689
|
-
}),
|
|
690
|
-
);
|
|
691
|
-
|
|
692
|
-
// --- GitHub Star (็จๆท็บงๆไฝ) ---
|
|
693
|
-
this.addTool(
|
|
694
|
-
new ZhinTool('github_star')
|
|
695
|
-
.desc('Star / Unstar ไธไธชไปๅบ๏ผไฝฟ็จไฝ ็ GitHub ่ดฆๅท๏ผ')
|
|
696
|
-
.keyword('star', 'unstar', 'ๆถ่', 'ๅๆถๆถ่')
|
|
697
|
-
.tag('github', 'user')
|
|
698
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
699
|
-
.param('unstar', { type: 'boolean', description: '่ฎพไธบ true ๅๅๆถ star' })
|
|
700
|
-
.execute(async (args, ctx) => {
|
|
701
|
-
if (!ctx?.message) return 'โ ๆ ๆณ่ทๅๆถๆฏไธไธๆ';
|
|
702
|
-
const msg = ctx.message as Message;
|
|
703
|
-
const repo = args.repo as string;
|
|
704
|
-
if (!repo.includes('/')) return 'โ ๆ ผๅผๅบไธบ owner/repo';
|
|
705
|
-
|
|
706
|
-
const oauthClient = await this.getOAuthClient(msg.$adapter, msg.$sender.id);
|
|
707
|
-
if (!oauthClient) {
|
|
708
|
-
return 'โ ไฝ ่ฟๆฒกๆ็ปๅฎ GitHub ่ดฆๅท๏ผstar ้่ฆไฝฟ็จไฝ ่ชๅทฑ็่บซไปฝ\n๐ก ไฝฟ็จ /github bind ็ปๅฎ';
|
|
709
|
-
}
|
|
710
|
-
|
|
711
|
-
if (args.unstar) {
|
|
712
|
-
const r = await oauthClient.unstarRepo(repo);
|
|
713
|
-
return r.ok || r.status === 204 ? `โ
ๅทฒๅๆถ star: ${repo}` : `โ ๆไฝๅคฑ่ดฅ: ${JSON.stringify(r.data)}`;
|
|
714
|
-
} else {
|
|
715
|
-
const r = await oauthClient.starRepo(repo);
|
|
716
|
-
return r.ok || r.status === 204 ? `โญ ๅทฒ star: ${repo}` : `โ ๆไฝๅคฑ่ดฅ: ${JSON.stringify(r.data)}`;
|
|
717
|
-
}
|
|
718
|
-
}),
|
|
719
|
-
);
|
|
720
|
-
|
|
721
|
-
// --- GitHub Fork (็จๆท็บงๆไฝ) ---
|
|
722
|
-
this.addTool(
|
|
723
|
-
new ZhinTool('github_fork')
|
|
724
|
-
.desc('Fork ไธไธชไปๅบๅฐไฝ ็ GitHub ่ดฆๅท')
|
|
725
|
-
.keyword('fork', 'ๅคๅป')
|
|
726
|
-
.tag('github', 'user')
|
|
727
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
728
|
-
.execute(async (args, ctx) => {
|
|
729
|
-
if (!ctx?.message) return 'โ ๆ ๆณ่ทๅๆถๆฏไธไธๆ';
|
|
730
|
-
const msg = ctx.message as Message;
|
|
731
|
-
const repo = args.repo as string;
|
|
732
|
-
if (!repo.includes('/')) return 'โ ๆ ผๅผๅบไธบ owner/repo';
|
|
733
|
-
|
|
734
|
-
const oauthClient = await this.getOAuthClient(msg.$adapter, msg.$sender.id);
|
|
735
|
-
if (!oauthClient) {
|
|
736
|
-
return 'โ ไฝ ่ฟๆฒกๆ็ปๅฎ GitHub ่ดฆๅท๏ผfork ้่ฆไฝฟ็จไฝ ่ชๅทฑ็่บซไปฝ\n๐ก ไฝฟ็จ /github bind ็ปๅฎ';
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
const r = await oauthClient.forkRepo(repo);
|
|
740
|
-
return r.ok ? `๐ด ๅทฒ fork: ${r.data.full_name}\n๐ ${r.data.html_url}` : `โ ๆไฝๅคฑ่ดฅ: ${r.data?.message || JSON.stringify(r.data)}`;
|
|
741
|
-
}),
|
|
742
|
-
);
|
|
743
|
-
|
|
744
|
-
// --- Search ---
|
|
745
|
-
this.addTool(
|
|
746
|
-
new ZhinTool('github_search')
|
|
747
|
-
.desc('GitHub ๆ็ดข๏ผๅจ issues/repos/code ไธญๆ็ดข')
|
|
748
|
-
.keyword('search', 'ๆ็ดข', 'ๆฅๆพ', 'github search')
|
|
749
|
-
.tag('github', 'search')
|
|
750
|
-
.param('action', { type: 'string', description: 'issues|repos|code', enum: ['issues', 'repos', 'code'] }, true)
|
|
751
|
-
.param('query', { type: 'string', description: 'ๆ็ดขๅ
ณ้ฎ่ฏ' }, true)
|
|
752
|
-
.param('limit', { type: 'number', description: '่ฟๅๆฐ้๏ผ้ป่ฎค 10' })
|
|
753
|
-
.execute(async (args) => {
|
|
754
|
-
const api = this.getAPI();
|
|
755
|
-
if (!api) return 'โ ๆฒกๆๅฏ็จ็ GitHub bot';
|
|
756
|
-
const q = args.query as string;
|
|
757
|
-
const limit = (args.limit as number) || 10;
|
|
758
|
-
|
|
759
|
-
switch (args.action) {
|
|
760
|
-
case 'issues': {
|
|
761
|
-
const r = await api.searchIssues(q, limit);
|
|
762
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
763
|
-
if (!r.data.items.length) return `๐ญ ๆฒกๆๅน้
็ Issue/PR`;
|
|
764
|
-
return `๐ ๅ
ฑ ${r.data.total_count} ๆก๏ผๆพ็คบๅ ${r.data.items.length}:\n\n` +
|
|
765
|
-
r.data.items.map((i: any) =>
|
|
766
|
-
`${i.pull_request ? '๐' : '๐'} ${i.repository_url.replace('https://api.github.com/repos/', '')}#${i.number}\n ${i.title}\n ๐ค ${i.user.login} | ${i.state}`
|
|
767
|
-
).join('\n\n');
|
|
768
|
-
}
|
|
769
|
-
case 'repos': {
|
|
770
|
-
const r = await api.searchRepos(q, limit);
|
|
771
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
772
|
-
if (!r.data.items.length) return `๐ญ ๆฒกๆๅน้
็ไปๅบ`;
|
|
773
|
-
return `๐ ๅ
ฑ ${r.data.total_count} ๆก๏ผๆพ็คบๅ ${r.data.items.length}:\n\n` +
|
|
774
|
-
r.data.items.map((repo: any) =>
|
|
775
|
-
`๐ฆ ${repo.full_name}${repo.private ? ' ๐' : ''}\n ${repo.description || '(ๆ ๆ่ฟฐ)'}\n โญ ${repo.stargazers_count} | ๐ด ${repo.forks_count} | ๐ ${repo.language || '?'}`
|
|
776
|
-
).join('\n\n');
|
|
777
|
-
}
|
|
778
|
-
case 'code': {
|
|
779
|
-
const r = await api.searchCode(q, limit);
|
|
780
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
781
|
-
if (!r.data.items.length) return `๐ญ ๆฒกๆๅน้
็ไปฃ็ `;
|
|
782
|
-
return `๐ ๅ
ฑ ${r.data.total_count} ๆก๏ผๆพ็คบๅ ${r.data.items.length}:\n\n` +
|
|
783
|
-
r.data.items.map((c: any) =>
|
|
784
|
-
`๐ ${c.repository.full_name}/${c.path}\n ๐ ${c.html_url}`
|
|
785
|
-
).join('\n\n');
|
|
786
|
-
}
|
|
787
|
-
default: return `โ ๆช็ฅๆ็ดข็ฑปๅ: ${args.action}`;
|
|
788
|
-
}
|
|
789
|
-
}),
|
|
790
|
-
);
|
|
791
|
-
|
|
792
|
-
// --- Label ---
|
|
793
|
-
this.addTool(
|
|
794
|
-
new ZhinTool('github_label')
|
|
795
|
-
.desc('GitHub ๆ ็ญพ็ฎก็๏ผๆฅ็/ๆทปๅ /็งป้ค Issue/PR ๆ ็ญพ')
|
|
796
|
-
.keyword('label', 'ๆ ็ญพ', 'tag')
|
|
797
|
-
.tag('github', 'label')
|
|
798
|
-
.param('action', { type: 'string', description: 'list|add|remove', enum: ['list', 'add', 'remove'] }, true)
|
|
799
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
800
|
-
.param('number', { type: 'number', description: 'Issue/PR ็ผๅท (add/remove ๅฟ
ๅกซ)' })
|
|
801
|
-
.param('labels', { type: 'string', description: 'ๆ ็ญพๅ๏ผ้ๅทๅ้ (add/remove)' })
|
|
802
|
-
.execute(async (args) => {
|
|
803
|
-
const api = this.getAPI();
|
|
804
|
-
if (!api) return 'โ ๆฒกๆๅฏ็จ็ GitHub bot';
|
|
805
|
-
const repo = args.repo as string;
|
|
806
|
-
|
|
807
|
-
switch (args.action) {
|
|
808
|
-
case 'list': {
|
|
809
|
-
const r = await api.listLabels(repo);
|
|
810
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
811
|
-
if (!r.data.length) return `๐ญ ไปๅบๆฒกๆๆ ็ญพ`;
|
|
812
|
-
return `๐ท๏ธ ${repo} ๆ ็ญพ (${r.data.length}):\n` +
|
|
813
|
-
r.data.map((l: any) => ` โข ${l.name}${l.description ? ` โ ${l.description}` : ''}`).join('\n');
|
|
814
|
-
}
|
|
815
|
-
case 'add': {
|
|
816
|
-
if (!args.number) return 'โ ่ฏทๆไพ Issue/PR ็ผๅท';
|
|
817
|
-
if (!args.labels) return 'โ ่ฏทๆไพๆ ็ญพๅ';
|
|
818
|
-
const labels = (args.labels as string).split(',').map(s => s.trim());
|
|
819
|
-
const r = await api.addLabels(repo, args.number as number, labels);
|
|
820
|
-
return r.ok ? `โ
ๅทฒๆทปๅ ๆ ็ญพ: ${labels.join(', ')}` : `โ ${JSON.stringify(r.data)}`;
|
|
821
|
-
}
|
|
822
|
-
case 'remove': {
|
|
823
|
-
if (!args.number) return 'โ ่ฏทๆไพ Issue/PR ็ผๅท';
|
|
824
|
-
if (!args.labels) return 'โ ่ฏทๆไพ่ฆ็งป้ค็ๆ ็ญพๅ';
|
|
825
|
-
const labels = (args.labels as string).split(',').map(s => s.trim());
|
|
826
|
-
const results: string[] = [];
|
|
827
|
-
for (const label of labels) {
|
|
828
|
-
const r = await api.removeLabel(repo, args.number as number, label);
|
|
829
|
-
results.push(r.ok ? `โ
${label}` : `โ ${label}: ${r.data?.message || 'failed'}`);
|
|
830
|
-
}
|
|
831
|
-
return results.join('\n');
|
|
832
|
-
}
|
|
833
|
-
default: return `โ ๆช็ฅๆไฝ: ${args.action}`;
|
|
834
|
-
}
|
|
835
|
-
}),
|
|
836
|
-
);
|
|
837
|
-
|
|
838
|
-
// --- Assign ---
|
|
839
|
-
this.addTool(
|
|
840
|
-
new ZhinTool('github_assign')
|
|
841
|
-
.desc('GitHub ๆๆดพ็ฎก็๏ผ็ป Issue/PR ๆทปๅ /็งป้คๆๆดพไบบ')
|
|
842
|
-
.keyword('assign', 'ๆๆดพ', 'ๅ้
')
|
|
843
|
-
.tag('github', 'assign')
|
|
844
|
-
.param('action', { type: 'string', description: 'add|remove', enum: ['add', 'remove'] }, true)
|
|
845
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
846
|
-
.param('number', { type: 'number', description: 'Issue/PR ็ผๅท (ๅฟ
ๅกซ)' }, true)
|
|
847
|
-
.param('assignees', { type: 'string', description: '็จๆทๅ๏ผ้ๅทๅ้ (ๅฟ
ๅกซ)' }, true)
|
|
848
|
-
.execute(async (args) => {
|
|
849
|
-
const api = this.getAPI();
|
|
850
|
-
if (!api) return 'โ ๆฒกๆๅฏ็จ็ GitHub bot';
|
|
851
|
-
const repo = args.repo as string;
|
|
852
|
-
const num = args.number as number;
|
|
853
|
-
const assignees = (args.assignees as string).split(',').map(s => s.trim());
|
|
854
|
-
|
|
855
|
-
if (args.action === 'add') {
|
|
856
|
-
const r = await api.addAssignees(repo, num, assignees);
|
|
857
|
-
return r.ok ? `โ
ๅทฒๆๆดพ: ${assignees.join(', ')}` : `โ ${r.data?.message || JSON.stringify(r.data)}`;
|
|
858
|
-
} else {
|
|
859
|
-
const r = await api.removeAssignees(repo, num, assignees);
|
|
860
|
-
return r.ok ? `โ
ๅทฒ็งป้คๆๆดพ: ${assignees.join(', ')}` : `โ ${r.data?.message || JSON.stringify(r.data)}`;
|
|
861
|
-
}
|
|
862
|
-
}),
|
|
863
|
-
);
|
|
864
|
-
|
|
865
|
-
// --- File ---
|
|
866
|
-
this.addTool(
|
|
867
|
-
new ZhinTool('github_file')
|
|
868
|
-
.desc('่ฏปๅ GitHub ไปๅบไธญ็ๆไปถๅ
ๅฎน')
|
|
869
|
-
.keyword('file', 'ๆไปถ', 'ๆฅ็ๆไปถ', '่ฏปๅๆไปถ', 'cat')
|
|
870
|
-
.tag('github', 'file')
|
|
871
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
872
|
-
.param('path', { type: 'string', description: 'ๆไปถ่ทฏๅพ (ๅฟ
ๅกซ)' }, true)
|
|
873
|
-
.param('ref', { type: 'string', description: 'ๅๆฏ/tag/commit SHA (ๅฏ้๏ผ้ป่ฎคไธปๅๆฏ)' })
|
|
874
|
-
.execute(async (args) => {
|
|
875
|
-
const api = this.getAPI();
|
|
876
|
-
if (!api) return 'โ ๆฒกๆๅฏ็จ็ GitHub bot';
|
|
877
|
-
const r = await api.getFileContent(args.repo as string, args.path as string, args.ref as string | undefined);
|
|
878
|
-
if (!r.ok) return `โ ${r.data?.message || JSON.stringify(r.data)}`;
|
|
879
|
-
|
|
880
|
-
if (Array.isArray(r.data)) {
|
|
881
|
-
return `๐ ${args.path} (็ฎๅฝ๏ผ${r.data.length} ้กน):\n` +
|
|
882
|
-
r.data.map((f: any) => ` ${f.type === 'dir' ? '๐' : '๐'} ${f.name}`).join('\n');
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
if (r.data.type === 'file' && r.data.content) {
|
|
886
|
-
const decoded = Buffer.from(r.data.content, 'base64').toString('utf-8');
|
|
887
|
-
const maxLen = 3000;
|
|
888
|
-
const truncated = decoded.length > maxLen;
|
|
889
|
-
return `๐ ${r.data.path} (${r.data.size} bytes)\n\n${decoded.slice(0, maxLen)}${truncated ? `\n\n... (ๆชๆญ๏ผๅ
ฑ ${decoded.length} ๅญ็ฌฆ)` : ''}`;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
return `๐ ${r.data.path} โ ${r.data.type} (${r.data.size} bytes)\n๐ ${r.data.html_url}`;
|
|
893
|
-
}),
|
|
894
|
-
);
|
|
895
|
-
|
|
896
|
-
// --- Commits ---
|
|
897
|
-
this.addTool(
|
|
898
|
-
new ZhinTool('github_commits')
|
|
899
|
-
.desc('GitHub ๆไบคๆฅ่ฏข๏ผๅๅบๆไบค่ฎฐๅฝๆๅฏนๆฏไธคไธชๅๆฏ')
|
|
900
|
-
.keyword('commit', 'ๆไบค', 'ๅๅฒ', 'log', 'compare', 'ๅฏนๆฏ')
|
|
901
|
-
.tag('github', 'commits')
|
|
902
|
-
.param('action', { type: 'string', description: 'list|compare', enum: ['list', 'compare'] }, true)
|
|
903
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
904
|
-
.param('sha', { type: 'string', description: 'ๅๆฏ/SHA (list)' })
|
|
905
|
-
.param('path', { type: 'string', description: 'ๆๆไปถ่ทฏๅพ่ฟๆปค (list)' })
|
|
906
|
-
.param('base', { type: 'string', description: 'ๅบๅๅๆฏ (compare)' })
|
|
907
|
-
.param('head', { type: 'string', description: '็ฎๆ ๅๆฏ (compare)' })
|
|
908
|
-
.param('limit', { type: 'number', description: '่ฟๅๆฐ้๏ผ้ป่ฎค 10' })
|
|
909
|
-
.execute(async (args) => {
|
|
910
|
-
const api = this.getAPI();
|
|
911
|
-
if (!api) return 'โ ๆฒกๆๅฏ็จ็ GitHub bot';
|
|
912
|
-
const repo = args.repo as string;
|
|
913
|
-
|
|
914
|
-
if (args.action === 'list') {
|
|
915
|
-
const r = await api.listCommits(repo, args.sha as string | undefined, args.path as string | undefined, (args.limit as number) || 10);
|
|
916
|
-
if (!r.ok) return `โ ${JSON.stringify(r.data)}`;
|
|
917
|
-
if (!r.data.length) return '๐ญ ๆฒกๆๆพๅฐๆไบค่ฎฐๅฝ';
|
|
918
|
-
return r.data.map((c: any) =>
|
|
919
|
-
`โข ${c.sha.substring(0, 7)} ${c.commit.message.split('\n')[0]}\n ๐ค ${c.commit.author?.name || '?'} | ๐
${c.commit.author?.date?.split('T')[0] || '?'}`
|
|
920
|
-
).join('\n\n');
|
|
921
|
-
} else {
|
|
922
|
-
if (!args.base || !args.head) return 'โ compare ้่ฆ base ๅ head ๅๆฐ';
|
|
923
|
-
const r = await api.compareCommits(repo, args.base as string, args.head as string);
|
|
924
|
-
if (!r.ok) return `โ ${r.data?.message || JSON.stringify(r.data)}`;
|
|
925
|
-
const d = r.data;
|
|
926
|
-
return [
|
|
927
|
-
`๐ ${args.base} โ ${args.head}`,
|
|
928
|
-
`๐ ${d.status} | ${d.ahead_by} ahead, ${d.behind_by} behind`,
|
|
929
|
-
`๐ ${d.total_commits} commits | ${d.files?.length || 0} files changed`,
|
|
930
|
-
d.commits?.length ? '\nๆ่ฟๆไบค:\n' + d.commits.slice(0, 5).map((c: any) =>
|
|
931
|
-
` โข ${c.sha.substring(0, 7)} ${c.commit.message.split('\n')[0]}`
|
|
932
|
-
).join('\n') : '',
|
|
933
|
-
].filter(Boolean).join('\n');
|
|
934
|
-
}
|
|
935
|
-
}),
|
|
936
|
-
);
|
|
937
|
-
|
|
938
|
-
// --- Edit (Issue/PR) ---
|
|
939
|
-
this.addTool(
|
|
940
|
-
new ZhinTool('github_edit')
|
|
941
|
-
.desc('็ผ่พ GitHub Issue ๆ PR ็ๆ ้ขใๆญฃๆใ็ถๆ')
|
|
942
|
-
.keyword('edit', '็ผ่พ', 'ไฟฎๆน', 'update', 'ๆดๆฐ')
|
|
943
|
-
.tag('github', 'edit')
|
|
944
|
-
.param('type', { type: 'string', description: 'issue|pr', enum: ['issue', 'pr'] }, true)
|
|
945
|
-
.param('repo', { type: 'string', description: 'owner/repo (ๅฟ
ๅกซ)' }, true)
|
|
946
|
-
.param('number', { type: 'number', description: 'Issue/PR ็ผๅท (ๅฟ
ๅกซ)' }, true)
|
|
947
|
-
.param('title', { type: 'string', description: 'ๆฐๆ ้ข' })
|
|
948
|
-
.param('body', { type: 'string', description: 'ๆฐๆญฃๆ' })
|
|
949
|
-
.param('state', { type: 'string', description: 'open|closed' })
|
|
950
|
-
.execute(async (args) => {
|
|
951
|
-
const api = this.getAPI();
|
|
952
|
-
if (!api) return 'โ ๆฒกๆๅฏ็จ็ GitHub bot';
|
|
953
|
-
const repo = args.repo as string;
|
|
954
|
-
const num = args.number as number;
|
|
955
|
-
|
|
956
|
-
const data: any = {};
|
|
957
|
-
if (args.title) data.title = args.title;
|
|
958
|
-
if (args.body) data.body = args.body;
|
|
959
|
-
if (args.state) data.state = args.state;
|
|
960
|
-
|
|
961
|
-
if (!Object.keys(data).length) return 'โ ่ฏท่ณๅฐๆไพไธไธช่ฆไฟฎๆน็ๅญๆฎต (title/body/state)';
|
|
962
|
-
|
|
963
|
-
const r = args.type === 'pr'
|
|
964
|
-
? await api.updatePR(repo, num, data)
|
|
965
|
-
: await api.updateIssue(repo, num, data);
|
|
966
|
-
|
|
967
|
-
if (!r.ok) return `โ ${r.data?.message || JSON.stringify(r.data)}`;
|
|
968
|
-
return `โ
${args.type === 'pr' ? 'PR' : 'Issue'} #${num} ๅทฒๆดๆฐ\n๐ ${r.data.html_url}`;
|
|
969
|
-
}),
|
|
970
|
-
);
|
|
971
|
-
|
|
972
|
-
this.plugin.logger.debug('GitHub ๅทฅๅ
ทๅทฒๆณจๅ: pr, issue, repo, search, label, assign, file, commits, edit, subscribe, unsubscribe, subscriptions, bind, unbind, whoami, star, fork');
|
|
973
|
-
}
|
|
974
|
-
|
|
975
309
|
|
|
976
310
|
// โโ ้็ฅๆจ้ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
|
|
977
311
|
|