@zhin.js/scaffold-wizard 0.1.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.
- package/LICENSE +21 -0
- package/README.md +67 -0
- package/lib/adapter-configurers.d.ts +19 -0
- package/lib/adapter-configurers.d.ts.map +1 -0
- package/lib/adapter-configurers.js +502 -0
- package/lib/adapter-configurers.js.map +1 -0
- package/lib/adapter.d.ts +37 -0
- package/lib/adapter.d.ts.map +1 -0
- package/lib/adapter.js +458 -0
- package/lib/adapter.js.map +1 -0
- package/lib/ai.d.ts +88 -0
- package/lib/ai.d.ts.map +1 -0
- package/lib/ai.js +440 -0
- package/lib/ai.js.map +1 -0
- package/lib/apply.d.ts +14 -0
- package/lib/apply.d.ts.map +1 -0
- package/lib/apply.js +86 -0
- package/lib/apply.js.map +1 -0
- package/lib/database.d.ts +3 -0
- package/lib/database.d.ts.map +1 -0
- package/lib/database.js +260 -0
- package/lib/database.js.map +1 -0
- package/lib/index.d.ts +11 -0
- package/lib/index.d.ts.map +1 -0
- package/lib/index.js +8 -0
- package/lib/index.js.map +1 -0
- package/lib/project-deps.d.ts +18 -0
- package/lib/project-deps.d.ts.map +1 -0
- package/lib/project-deps.js +49 -0
- package/lib/project-deps.js.map +1 -0
- package/lib/types.d.ts +22 -0
- package/lib/types.d.ts.map +1 -0
- package/lib/types.js +9 -0
- package/lib/types.js.map +1 -0
- package/package.json +48 -0
- package/src/adapter-configurers.ts +550 -0
- package/src/adapter.ts +524 -0
- package/src/ai.ts +497 -0
- package/src/apply.ts +99 -0
- package/src/database.ts +267 -0
- package/src/index.ts +51 -0
- package/src/project-deps.ts +49 -0
- package/src/types.ts +32 -0
|
@@ -0,0 +1,550 @@
|
|
|
1
|
+
import inquirer from 'inquirer';
|
|
2
|
+
import chalk from 'chalk';
|
|
3
|
+
|
|
4
|
+
/** 平台适配器文档索引 */
|
|
5
|
+
export const ADAPTERS_DOCS_URL = 'https://zhin.js.org/adapters/';
|
|
6
|
+
|
|
7
|
+
/** @deprecated 使用 ADAPTERS_DOCS_URL */
|
|
8
|
+
export const ADAPTERS_INDEX_URL = ADAPTERS_DOCS_URL;
|
|
9
|
+
|
|
10
|
+
/** 框架级适配器概念(多平台、群管工具等) */
|
|
11
|
+
export const ADAPTERS_ESSENTIALS_URL = 'https://zhin.js.org/essentials/adapters';
|
|
12
|
+
|
|
13
|
+
/** 各适配器文档页(与 docs/adapters/*.md 同步) */
|
|
14
|
+
export const adapterDocsUrl = (slug: string) => `https://zhin.js.org/adapters/${slug}`;
|
|
15
|
+
|
|
16
|
+
export interface BotConfigureContext {
|
|
17
|
+
envVars: Record<string, string>;
|
|
18
|
+
markRequiresDatabase: () => void;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function configureTelegramBot(ctx: BotConfigureContext): Promise<Record<string, unknown>> {
|
|
22
|
+
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('telegram')));
|
|
23
|
+
|
|
24
|
+
const { botName } = await inquirer.prompt([
|
|
25
|
+
{
|
|
26
|
+
type: 'input',
|
|
27
|
+
name: 'botName',
|
|
28
|
+
message: ' Bot 标识名称:',
|
|
29
|
+
default: 'telegram-bot',
|
|
30
|
+
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
31
|
+
},
|
|
32
|
+
]);
|
|
33
|
+
|
|
34
|
+
const { token } = await inquirer.prompt([
|
|
35
|
+
{
|
|
36
|
+
type: 'password',
|
|
37
|
+
name: 'token',
|
|
38
|
+
message: ' Telegram Bot Token(@BotFather 获取):',
|
|
39
|
+
validate: (v: string) => (v.trim() ? true : 'Token 不能为空'),
|
|
40
|
+
},
|
|
41
|
+
]);
|
|
42
|
+
ctx.envVars.TELEGRAM_TOKEN = token;
|
|
43
|
+
|
|
44
|
+
const { transport } = await inquirer.prompt([
|
|
45
|
+
{
|
|
46
|
+
type: 'select',
|
|
47
|
+
name: 'transport',
|
|
48
|
+
message: ' 消息接收方式:',
|
|
49
|
+
choices: [
|
|
50
|
+
{
|
|
51
|
+
name: '长轮询 polling(默认,本地开发无需公网)',
|
|
52
|
+
value: 'polling',
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'Webhook(生产环境,需 HTTPS 公网域名)',
|
|
56
|
+
value: 'webhook',
|
|
57
|
+
},
|
|
58
|
+
],
|
|
59
|
+
default: 'polling',
|
|
60
|
+
},
|
|
61
|
+
]);
|
|
62
|
+
|
|
63
|
+
const botConfig: Record<string, unknown> = {
|
|
64
|
+
context: 'telegram',
|
|
65
|
+
name: botName.trim(),
|
|
66
|
+
token: '${TELEGRAM_TOKEN}',
|
|
67
|
+
polling: transport === 'polling',
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
if (transport === 'webhook') {
|
|
71
|
+
console.log('');
|
|
72
|
+
console.log(chalk.yellow(' Webhook 前置条件:'));
|
|
73
|
+
console.log(chalk.gray(' • Bot 进程需绑定可被 Telegram 访问的 HTTPS 域名(有效 TLS 证书)'));
|
|
74
|
+
console.log(chalk.gray(' • 在 @BotFather 可配合 setWebhook,或由 Telegraf 在 launch 时注册'));
|
|
75
|
+
console.log(chalk.gray(' • 防火墙/反向代理需放行 webhook.port(默认由 Telegraf 监听)'));
|
|
76
|
+
|
|
77
|
+
const webhook = await inquirer.prompt([
|
|
78
|
+
{
|
|
79
|
+
type: 'input',
|
|
80
|
+
name: 'domain',
|
|
81
|
+
message: ' Webhook 公网 HTTPS 域名(如 https://bot.example.com):',
|
|
82
|
+
validate: (v: string) => {
|
|
83
|
+
if (!v.trim()) return '域名不能为空';
|
|
84
|
+
if (!/^https:\/\/.+/i.test(v.trim())) return '须以 https:// 开头';
|
|
85
|
+
return true;
|
|
86
|
+
},
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
type: 'input',
|
|
90
|
+
name: 'path',
|
|
91
|
+
message: ' Webhook 路径:',
|
|
92
|
+
default: '/telegram-webhook',
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
type: 'input',
|
|
96
|
+
name: 'port',
|
|
97
|
+
message: ' 本地监听端口(Telegraf webhook server):',
|
|
98
|
+
default: '8443',
|
|
99
|
+
validate: (v: string) => {
|
|
100
|
+
const p = parseInt(v, 10);
|
|
101
|
+
if (Number.isNaN(p) || p < 1 || p > 65535) return '请输入 1–65535 端口';
|
|
102
|
+
return true;
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
]);
|
|
106
|
+
|
|
107
|
+
botConfig.polling = false;
|
|
108
|
+
botConfig.webhook = {
|
|
109
|
+
domain: webhook.domain.trim(),
|
|
110
|
+
path: webhook.path.trim(),
|
|
111
|
+
port: parseInt(webhook.port, 10),
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
return botConfig;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export async function configureGitHubBot(ctx: BotConfigureContext): Promise<Record<string, unknown>> {
|
|
119
|
+
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('github')));
|
|
120
|
+
ctx.markRequiresDatabase();
|
|
121
|
+
|
|
122
|
+
const { botName } = await inquirer.prompt([
|
|
123
|
+
{
|
|
124
|
+
type: 'input',
|
|
125
|
+
name: 'botName',
|
|
126
|
+
message: ' Bot 标识名称:',
|
|
127
|
+
default: 'github-bot',
|
|
128
|
+
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
129
|
+
},
|
|
130
|
+
]);
|
|
131
|
+
|
|
132
|
+
const { authMode } = await inquirer.prompt([
|
|
133
|
+
{
|
|
134
|
+
type: 'select',
|
|
135
|
+
name: 'authMode',
|
|
136
|
+
message: ' 认证方式:',
|
|
137
|
+
choices: [
|
|
138
|
+
{
|
|
139
|
+
name: 'GitHub App(推荐生产:JWT → Installation Token)',
|
|
140
|
+
value: 'github_app',
|
|
141
|
+
},
|
|
142
|
+
{
|
|
143
|
+
name: 'gh CLI(本地开发:需 gh auth login)',
|
|
144
|
+
value: 'gh_cli',
|
|
145
|
+
},
|
|
146
|
+
],
|
|
147
|
+
default: 'github_app',
|
|
148
|
+
},
|
|
149
|
+
]);
|
|
150
|
+
|
|
151
|
+
const botConfig: Record<string, unknown> = {
|
|
152
|
+
context: 'github',
|
|
153
|
+
name: botName.trim(),
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
if (authMode === 'github_app') {
|
|
157
|
+
console.log(chalk.gray(' 在 github.com/settings/apps 创建 App,记录 App ID 并下载 .pem 私钥'));
|
|
158
|
+
|
|
159
|
+
const appConfig = await inquirer.prompt([
|
|
160
|
+
{
|
|
161
|
+
type: 'input',
|
|
162
|
+
name: 'app_id',
|
|
163
|
+
message: ' GitHub App ID:',
|
|
164
|
+
validate: (v: string) => (v.trim() ? true : 'App ID 不能为空'),
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
type: 'input',
|
|
168
|
+
name: 'private_key',
|
|
169
|
+
message: ' 私钥路径或 PEM 内容(如 ./data/github-app.pem):',
|
|
170
|
+
default: './data/github-app.pem',
|
|
171
|
+
validate: (v: string) => (v.trim() ? true : '私钥不能为空'),
|
|
172
|
+
},
|
|
173
|
+
]);
|
|
174
|
+
|
|
175
|
+
ctx.envVars.GITHUB_APP_ID = appConfig.app_id.trim();
|
|
176
|
+
botConfig.app_id = '${GITHUB_APP_ID}';
|
|
177
|
+
botConfig.private_key = appConfig.private_key.trim();
|
|
178
|
+
} else {
|
|
179
|
+
console.log(chalk.gray(' 请确保已执行 gh auth login,且 gh 在 PATH 中'));
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
const { eventsMode } = await inquirer.prompt([
|
|
183
|
+
{
|
|
184
|
+
type: 'select',
|
|
185
|
+
name: 'eventsMode',
|
|
186
|
+
message: ' 事件接收方式:',
|
|
187
|
+
choices: [
|
|
188
|
+
{
|
|
189
|
+
name: 'Webhook 实时推送(需 @zhin.js/host-router + 公网 URL)',
|
|
190
|
+
value: 'webhook',
|
|
191
|
+
},
|
|
192
|
+
{
|
|
193
|
+
name: '轮询 polling(无需公网,默认 60s 间隔)',
|
|
194
|
+
value: 'polling',
|
|
195
|
+
},
|
|
196
|
+
],
|
|
197
|
+
default: 'webhook',
|
|
198
|
+
},
|
|
199
|
+
]);
|
|
200
|
+
|
|
201
|
+
if (eventsMode === 'webhook') {
|
|
202
|
+
console.log('');
|
|
203
|
+
console.log(chalk.yellow(' Webhook 前置条件:'));
|
|
204
|
+
console.log(chalk.gray(' • plugins 需包含 @zhin.js/host-router(脚手架已默认写入)'));
|
|
205
|
+
console.log(chalk.gray(' • GitHub App Webhook URL 填 https://<你的域名>/pub/github/webhook'));
|
|
206
|
+
console.log(chalk.gray(' • /pub/* 路径绕过 Bearer 认证,供 GitHub 回调'));
|
|
207
|
+
|
|
208
|
+
const webhook = await inquirer.prompt([
|
|
209
|
+
{
|
|
210
|
+
type: 'password',
|
|
211
|
+
name: 'webhook_secret',
|
|
212
|
+
message: ' Webhook Secret(与 GitHub App 设置一致):',
|
|
213
|
+
validate: (v: string) => (v.trim() ? true : 'Webhook 模式需填写 Secret'),
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
type: 'input',
|
|
217
|
+
name: 'webhook_path',
|
|
218
|
+
message: ' Webhook 路径:',
|
|
219
|
+
default: '/pub/github/webhook',
|
|
220
|
+
},
|
|
221
|
+
]);
|
|
222
|
+
|
|
223
|
+
ctx.envVars.GITHUB_WEBHOOK_SECRET = webhook.webhook_secret;
|
|
224
|
+
botConfig.webhook_secret = '${GITHUB_WEBHOOK_SECRET}';
|
|
225
|
+
botConfig.webhook_path = webhook.webhook_path.trim();
|
|
226
|
+
} else {
|
|
227
|
+
const { poll_interval } = await inquirer.prompt([
|
|
228
|
+
{
|
|
229
|
+
type: 'input',
|
|
230
|
+
name: 'poll_interval',
|
|
231
|
+
message: ' 轮询间隔(秒):',
|
|
232
|
+
default: '60',
|
|
233
|
+
validate: (v: string) => {
|
|
234
|
+
const n = parseInt(v, 10);
|
|
235
|
+
if (Number.isNaN(n) || n < 10) return '建议不小于 10 秒';
|
|
236
|
+
return true;
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
]);
|
|
240
|
+
botConfig.poll_interval = parseInt(poll_interval, 10);
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
const { enableMcp } = await inquirer.prompt([
|
|
244
|
+
{
|
|
245
|
+
type: 'confirm',
|
|
246
|
+
name: 'enableMcp',
|
|
247
|
+
message: ' 配置 server-github MCP PAT?(可选,用于 fork 等 MCP 工具)',
|
|
248
|
+
default: false,
|
|
249
|
+
},
|
|
250
|
+
]);
|
|
251
|
+
|
|
252
|
+
if (enableMcp) {
|
|
253
|
+
const { pat } = await inquirer.prompt([
|
|
254
|
+
{
|
|
255
|
+
type: 'password',
|
|
256
|
+
name: 'pat',
|
|
257
|
+
message: ' GitHub Personal Access Token:',
|
|
258
|
+
validate: (v: string) => (v.trim() ? true : 'PAT 不能为空'),
|
|
259
|
+
},
|
|
260
|
+
]);
|
|
261
|
+
ctx.envVars.GITHUB_PERSONAL_ACCESS_TOKEN = pat;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
return botConfig;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
export async function configureOneBot11Bot(ctx: BotConfigureContext): Promise<Record<string, unknown>> {
|
|
268
|
+
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('onebot11')));
|
|
269
|
+
|
|
270
|
+
const { connection } = await inquirer.prompt([
|
|
271
|
+
{
|
|
272
|
+
type: 'select',
|
|
273
|
+
name: 'connection',
|
|
274
|
+
message: ' 连接方式:',
|
|
275
|
+
choices: [
|
|
276
|
+
{ name: '正向 WebSocket (ws) — 本 Bot 连接 OneBot 实现', value: 'ws' },
|
|
277
|
+
{ name: '反向 WebSocket (wss) — OneBot 实现连本 Bot(需 host-router)', value: 'wss' },
|
|
278
|
+
],
|
|
279
|
+
default: 'ws',
|
|
280
|
+
},
|
|
281
|
+
]);
|
|
282
|
+
|
|
283
|
+
const { botName } = await inquirer.prompt([
|
|
284
|
+
{
|
|
285
|
+
type: 'input',
|
|
286
|
+
name: 'botName',
|
|
287
|
+
message: ' Bot 标识名称:',
|
|
288
|
+
default: 'onebot-bot',
|
|
289
|
+
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
290
|
+
},
|
|
291
|
+
]);
|
|
292
|
+
|
|
293
|
+
const botConfig: Record<string, unknown> = {
|
|
294
|
+
context: 'onebot11',
|
|
295
|
+
connection,
|
|
296
|
+
name: botName.trim(),
|
|
297
|
+
};
|
|
298
|
+
|
|
299
|
+
if (connection === 'ws') {
|
|
300
|
+
const { url } = await inquirer.prompt([
|
|
301
|
+
{
|
|
302
|
+
type: 'input',
|
|
303
|
+
name: 'url',
|
|
304
|
+
message: ' OneBot WS 地址:',
|
|
305
|
+
default: 'ws://127.0.0.1:6700',
|
|
306
|
+
validate: (v: string) => (v.trim() ? true : '地址不能为空'),
|
|
307
|
+
},
|
|
308
|
+
]);
|
|
309
|
+
botConfig.url = url.trim();
|
|
310
|
+
} else {
|
|
311
|
+
console.log(chalk.gray(' 反向 WS 需启用 @zhin.js/host-router,OneBot 实现配置连接到此 Bot'));
|
|
312
|
+
const { path: wsPath } = await inquirer.prompt([
|
|
313
|
+
{
|
|
314
|
+
type: 'input',
|
|
315
|
+
name: 'path',
|
|
316
|
+
message: ' 反向 WS 路径:',
|
|
317
|
+
default: '/onebot11/ws',
|
|
318
|
+
validate: (v: string) => (v.trim() ? true : '路径不能为空'),
|
|
319
|
+
},
|
|
320
|
+
]);
|
|
321
|
+
botConfig.path = wsPath.trim();
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
const { accessToken } = await inquirer.prompt([
|
|
325
|
+
{
|
|
326
|
+
type: 'password',
|
|
327
|
+
name: 'accessToken',
|
|
328
|
+
message: ' access_token(无则留空):',
|
|
329
|
+
default: '',
|
|
330
|
+
},
|
|
331
|
+
]);
|
|
332
|
+
if (accessToken?.trim()) {
|
|
333
|
+
ctx.envVars.ONEBOT11_ACCESS_TOKEN = accessToken.trim();
|
|
334
|
+
botConfig.access_token = '${ONEBOT11_ACCESS_TOKEN}';
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
return botConfig;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
export async function configureQQBot(ctx: BotConfigureContext): Promise<Record<string, unknown>> {
|
|
341
|
+
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('qq')));
|
|
342
|
+
|
|
343
|
+
const credentials = await inquirer.prompt([
|
|
344
|
+
{
|
|
345
|
+
type: 'input',
|
|
346
|
+
name: 'name',
|
|
347
|
+
message: ' Bot 标识名称:',
|
|
348
|
+
default: 'qq-bot',
|
|
349
|
+
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
350
|
+
},
|
|
351
|
+
{
|
|
352
|
+
type: 'input',
|
|
353
|
+
name: 'appID',
|
|
354
|
+
message: ' App ID(QQ 开放平台):',
|
|
355
|
+
validate: (v: string) => (v.trim() ? true : 'App ID 不能为空'),
|
|
356
|
+
},
|
|
357
|
+
{
|
|
358
|
+
type: 'password',
|
|
359
|
+
name: 'token',
|
|
360
|
+
message: ' Token:',
|
|
361
|
+
validate: (v: string) => (v.trim() ? true : 'Token 不能为空'),
|
|
362
|
+
},
|
|
363
|
+
{
|
|
364
|
+
type: 'password',
|
|
365
|
+
name: 'secret',
|
|
366
|
+
message: ' Secret:',
|
|
367
|
+
validate: (v: string) => (v.trim() ? true : 'Secret 不能为空'),
|
|
368
|
+
},
|
|
369
|
+
]);
|
|
370
|
+
|
|
371
|
+
ctx.envVars.QQ_APP_ID = credentials.appID.trim();
|
|
372
|
+
ctx.envVars.QQ_TOKEN = credentials.token;
|
|
373
|
+
ctx.envVars.QQ_SECRET = credentials.secret;
|
|
374
|
+
|
|
375
|
+
const { mode, sandbox } = await inquirer.prompt([
|
|
376
|
+
{
|
|
377
|
+
type: 'select',
|
|
378
|
+
name: 'mode',
|
|
379
|
+
message: ' 消息接收模式 (mode):',
|
|
380
|
+
choices: [
|
|
381
|
+
{ name: 'public — 公域消息', value: 'public' },
|
|
382
|
+
{ name: 'private — 私域消息', value: 'private' },
|
|
383
|
+
],
|
|
384
|
+
default: 'public',
|
|
385
|
+
},
|
|
386
|
+
{
|
|
387
|
+
type: 'confirm',
|
|
388
|
+
name: 'sandbox',
|
|
389
|
+
message: ' 沙箱环境 (sandbox)?',
|
|
390
|
+
default: false,
|
|
391
|
+
},
|
|
392
|
+
]);
|
|
393
|
+
|
|
394
|
+
return {
|
|
395
|
+
context: 'qq',
|
|
396
|
+
name: credentials.name.trim(),
|
|
397
|
+
appID: '${QQ_APP_ID}',
|
|
398
|
+
token: '${QQ_TOKEN}',
|
|
399
|
+
secret: '${QQ_SECRET}',
|
|
400
|
+
mode,
|
|
401
|
+
platform: 'qq',
|
|
402
|
+
sandbox,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export async function configureDiscordBot(ctx: BotConfigureContext): Promise<Record<string, unknown>> {
|
|
407
|
+
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('discord')));
|
|
408
|
+
|
|
409
|
+
const { connection } = await inquirer.prompt([
|
|
410
|
+
{
|
|
411
|
+
type: 'select',
|
|
412
|
+
name: 'connection',
|
|
413
|
+
message: ' 连接方式:',
|
|
414
|
+
choices: [
|
|
415
|
+
{ name: 'Gateway WebSocket(常规 Bot,推荐)', value: 'gateway' },
|
|
416
|
+
{ name: 'Interactions HTTP(斜杠命令,需 host-router)', value: 'interactions' },
|
|
417
|
+
],
|
|
418
|
+
default: 'gateway',
|
|
419
|
+
},
|
|
420
|
+
]);
|
|
421
|
+
|
|
422
|
+
const { botName, token } = await inquirer.prompt([
|
|
423
|
+
{
|
|
424
|
+
type: 'input',
|
|
425
|
+
name: 'botName',
|
|
426
|
+
message: ' Bot 标识名称:',
|
|
427
|
+
default: 'discord-bot',
|
|
428
|
+
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
429
|
+
},
|
|
430
|
+
{
|
|
431
|
+
type: 'password',
|
|
432
|
+
name: 'token',
|
|
433
|
+
message: ' Bot Token:',
|
|
434
|
+
validate: (v: string) => (v.trim() ? true : 'Token 不能为空'),
|
|
435
|
+
},
|
|
436
|
+
]);
|
|
437
|
+
ctx.envVars.DISCORD_TOKEN = token;
|
|
438
|
+
|
|
439
|
+
const botConfig: Record<string, unknown> = {
|
|
440
|
+
context: 'discord',
|
|
441
|
+
connection,
|
|
442
|
+
name: botName.trim(),
|
|
443
|
+
token: '${DISCORD_TOKEN}',
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
if (connection === 'interactions') {
|
|
447
|
+
console.log(chalk.gray(' Interactions 模式需 @zhin.js/host-router 与 Developer Portal 中的公钥'));
|
|
448
|
+
const interactions = await inquirer.prompt([
|
|
449
|
+
{
|
|
450
|
+
type: 'input',
|
|
451
|
+
name: 'applicationId',
|
|
452
|
+
message: ' Application ID:',
|
|
453
|
+
validate: (v: string) => (v.trim() ? true : '不能为空'),
|
|
454
|
+
},
|
|
455
|
+
{
|
|
456
|
+
type: 'input',
|
|
457
|
+
name: 'publicKey',
|
|
458
|
+
message: ' Public Key:',
|
|
459
|
+
validate: (v: string) => (v.trim() ? true : '不能为空'),
|
|
460
|
+
},
|
|
461
|
+
{
|
|
462
|
+
type: 'input',
|
|
463
|
+
name: 'interactionsPath',
|
|
464
|
+
message: ' Interactions 路径:',
|
|
465
|
+
default: '/discord/interactions',
|
|
466
|
+
},
|
|
467
|
+
]);
|
|
468
|
+
botConfig.applicationId = interactions.applicationId.trim();
|
|
469
|
+
botConfig.publicKey = interactions.publicKey.trim();
|
|
470
|
+
botConfig.interactionsPath = interactions.interactionsPath.trim();
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
return botConfig;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
export async function configureSlackBot(ctx: BotConfigureContext): Promise<Record<string, unknown>> {
|
|
477
|
+
console.log(chalk.gray(' 文档: ' + adapterDocsUrl('slack')));
|
|
478
|
+
|
|
479
|
+
const { socketMode } = await inquirer.prompt([
|
|
480
|
+
{
|
|
481
|
+
type: 'select',
|
|
482
|
+
name: 'socketMode',
|
|
483
|
+
message: ' 连接方式:',
|
|
484
|
+
choices: [
|
|
485
|
+
{ name: 'Socket Mode(无需公网,需 App Token)', value: 'true' },
|
|
486
|
+
{ name: 'HTTP Events API(需公网 URL + 端口)', value: 'false' },
|
|
487
|
+
],
|
|
488
|
+
default: 'true',
|
|
489
|
+
},
|
|
490
|
+
]);
|
|
491
|
+
|
|
492
|
+
const { botName, token, signingSecret } = await inquirer.prompt([
|
|
493
|
+
{
|
|
494
|
+
type: 'input',
|
|
495
|
+
name: 'botName',
|
|
496
|
+
message: ' Bot 标识名称:',
|
|
497
|
+
default: 'slack-bot',
|
|
498
|
+
validate: (v: string) => (v.trim() ? true : '名称不能为空'),
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
type: 'password',
|
|
502
|
+
name: 'token',
|
|
503
|
+
message: ' Bot Token (xoxb-...):',
|
|
504
|
+
validate: (v: string) => (v.trim() ? true : '不能为空'),
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
type: 'password',
|
|
508
|
+
name: 'signingSecret',
|
|
509
|
+
message: ' Signing Secret:',
|
|
510
|
+
validate: (v: string) => (v.trim() ? true : '不能为空'),
|
|
511
|
+
},
|
|
512
|
+
]);
|
|
513
|
+
|
|
514
|
+
ctx.envVars.SLACK_BOT_TOKEN = token;
|
|
515
|
+
ctx.envVars.SLACK_SIGNING_SECRET = signingSecret;
|
|
516
|
+
|
|
517
|
+
const botConfig: Record<string, unknown> = {
|
|
518
|
+
context: 'slack',
|
|
519
|
+
name: botName.trim(),
|
|
520
|
+
token: '${SLACK_BOT_TOKEN}',
|
|
521
|
+
signingSecret: '${SLACK_SIGNING_SECRET}',
|
|
522
|
+
socketMode: socketMode === 'true',
|
|
523
|
+
};
|
|
524
|
+
|
|
525
|
+
if (socketMode === 'true') {
|
|
526
|
+
const { appToken } = await inquirer.prompt([
|
|
527
|
+
{
|
|
528
|
+
type: 'password',
|
|
529
|
+
name: 'appToken',
|
|
530
|
+
message: ' App Token (xapp-...,Socket Mode 必需):',
|
|
531
|
+
validate: (v: string) => (v.trim() ? true : 'App Token 不能为空'),
|
|
532
|
+
},
|
|
533
|
+
]);
|
|
534
|
+
ctx.envVars.SLACK_APP_TOKEN = appToken;
|
|
535
|
+
botConfig.appToken = '${SLACK_APP_TOKEN}';
|
|
536
|
+
} else {
|
|
537
|
+
console.log(chalk.yellow(' HTTP 模式需公网可达并配置 Slack Event Subscriptions URL'));
|
|
538
|
+
const { port } = await inquirer.prompt([
|
|
539
|
+
{
|
|
540
|
+
type: 'input',
|
|
541
|
+
name: 'port',
|
|
542
|
+
message: ' 监听端口:',
|
|
543
|
+
default: '3000',
|
|
544
|
+
},
|
|
545
|
+
]);
|
|
546
|
+
botConfig.port = parseInt(port, 10);
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
return botConfig;
|
|
550
|
+
}
|