create-bunspace 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/README.md +181 -0
- package/dist/bin.js +5755 -0
- package/dist/templates/monorepo/CLAUDE.md +164 -0
- package/dist/templates/monorepo/LICENSE +21 -0
- package/dist/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +269 -0
- package/dist/templates/monorepo/README.md +74 -0
- package/dist/templates/monorepo/SYNC_VERIFICATION.md +1 -0
- package/dist/templates/monorepo/apps/example/package.json +19 -0
- package/dist/templates/monorepo/apps/example/src/index.ts +23 -0
- package/dist/templates/monorepo/apps/example/src/types/index.ts +7 -0
- package/dist/templates/monorepo/apps/example/src/utils/index.ts +7 -0
- package/dist/templates/monorepo/core/packages/main/package.json +41 -0
- package/dist/templates/monorepo/core/packages/main/rolldown.config.ts +24 -0
- package/dist/templates/monorepo/core/packages/main/src/index.ts +80 -0
- package/dist/templates/monorepo/core/packages/main/src/types/constants.ts +15 -0
- package/dist/templates/monorepo/core/packages/main/src/types/index.ts +8 -0
- package/dist/templates/monorepo/core/packages/main/src/types/main.types.ts +25 -0
- package/dist/templates/monorepo/core/packages/main/src/utils/index.ts +5 -0
- package/dist/templates/monorepo/core/packages/utils/package.json +43 -0
- package/dist/templates/monorepo/core/packages/utils/rolldown.config.ts +34 -0
- package/dist/templates/monorepo/core/packages/utils/src/index.ts +2 -0
- package/dist/templates/monorepo/core/packages/utils/src/logger.ts +68 -0
- package/dist/templates/monorepo/core/packages/utils/src/result.ts +146 -0
- package/dist/templates/monorepo/core/packages/utils/src/types/constants.ts +15 -0
- package/dist/templates/monorepo/core/packages/utils/src/types/index.ts +8 -0
- package/dist/templates/monorepo/core/packages/utils/src/types/utils.types.ts +32 -0
- package/dist/templates/monorepo/core/packages/utils/src/utils/index.ts +5 -0
- package/dist/templates/monorepo/oxlint.json +14 -0
- package/dist/templates/monorepo/package.json +39 -0
- package/dist/templates/monorepo/tsconfig.json +35 -0
- package/dist/templates/telegram-bot/.oxlintrc.json +33 -0
- package/dist/templates/telegram-bot/.prettierignore +5 -0
- package/dist/templates/telegram-bot/.prettierrc +26 -0
- package/dist/templates/telegram-bot/CLAUDE.deploy.md +356 -0
- package/dist/templates/telegram-bot/CLAUDE.dev.md +266 -0
- package/dist/templates/telegram-bot/CLAUDE.md +280 -0
- package/dist/templates/telegram-bot/Dockerfile +46 -0
- package/dist/templates/telegram-bot/README.md +245 -0
- package/dist/templates/telegram-bot/apps/.gitkeep +0 -0
- package/dist/templates/telegram-bot/bun.lock +208 -0
- package/dist/templates/telegram-bot/core/.env.example +71 -0
- package/dist/templates/telegram-bot/core/README.md +1067 -0
- package/dist/templates/telegram-bot/core/package.json +15 -0
- package/dist/templates/telegram-bot/core/src/config/env.ts +131 -0
- package/dist/templates/telegram-bot/core/src/config/index.ts +97 -0
- package/dist/templates/telegram-bot/core/src/config/logging.ts +110 -0
- package/dist/templates/telegram-bot/core/src/handlers/control.ts +85 -0
- package/dist/templates/telegram-bot/core/src/handlers/health.ts +83 -0
- package/dist/templates/telegram-bot/core/src/handlers/logs.ts +126 -0
- package/dist/templates/telegram-bot/core/src/index.ts +161 -0
- package/dist/templates/telegram-bot/core/src/middleware/auth.ts +41 -0
- package/dist/templates/telegram-bot/core/src/middleware/error-handler.ts +41 -0
- package/dist/templates/telegram-bot/core/src/middleware/logging.ts +1 -0
- package/dist/templates/telegram-bot/core/src/middleware/topics.ts +55 -0
- package/dist/templates/telegram-bot/core/src/types/bot.ts +92 -0
- package/dist/templates/telegram-bot/core/src/types/constants.ts +50 -0
- package/dist/templates/telegram-bot/core/src/types/result.ts +1 -0
- package/dist/templates/telegram-bot/core/src/utils/bot-manager.test.ts +111 -0
- package/dist/templates/telegram-bot/core/src/utils/bot-manager.ts +201 -0
- package/dist/templates/telegram-bot/core/src/utils/commands.ts +63 -0
- package/dist/templates/telegram-bot/core/src/utils/formatters.ts +82 -0
- package/dist/templates/telegram-bot/core/src/utils/instance-manager.ts +189 -0
- package/dist/templates/telegram-bot/core/src/utils/memory.ts +33 -0
- package/dist/templates/telegram-bot/core/src/utils/result.ts +26 -0
- package/dist/templates/telegram-bot/core/src/utils/telegram.ts +31 -0
- package/dist/templates/telegram-bot/core/src/utils/type-guards.ts +71 -0
- package/dist/templates/telegram-bot/core/tsconfig.json +9 -0
- package/dist/templates/telegram-bot/docker-compose.yml +37 -0
- package/dist/templates/telegram-bot/docs/cli-commands.md +377 -0
- package/dist/templates/telegram-bot/docs/development.md +363 -0
- package/dist/templates/telegram-bot/docs/environment.md +460 -0
- package/dist/templates/telegram-bot/docs/examples/middleware-auth.md +335 -0
- package/dist/templates/telegram-bot/docs/examples/simple-command.md +207 -0
- package/dist/templates/telegram-bot/docs/examples/webhook-setup.md +362 -0
- package/dist/templates/telegram-bot/docs/getting-started.md +223 -0
- package/dist/templates/telegram-bot/docs/troubleshooting.md +489 -0
- package/dist/templates/telegram-bot/package.json +49 -0
- package/dist/templates/telegram-bot/packages/utils/package.json +12 -0
- package/dist/templates/telegram-bot/packages/utils/src/index.ts +2 -0
- package/dist/templates/telegram-bot/packages/utils/src/logger.ts +72 -0
- package/dist/templates/telegram-bot/packages/utils/src/result.ts +80 -0
- package/dist/templates/telegram-bot/tools/README.md +47 -0
- package/dist/templates/telegram-bot/tools/commands/doctor.ts +460 -0
- package/dist/templates/telegram-bot/tools/commands/index.ts +35 -0
- package/dist/templates/telegram-bot/tools/commands/ngrok.ts +207 -0
- package/dist/templates/telegram-bot/tools/commands/setup.ts +368 -0
- package/dist/templates/telegram-bot/tools/commands/status.ts +140 -0
- package/dist/templates/telegram-bot/tools/index.ts +16 -0
- package/dist/templates/telegram-bot/tools/package.json +12 -0
- package/dist/templates/telegram-bot/tools/utils/index.ts +13 -0
- package/dist/templates/telegram-bot/tsconfig.json +22 -0
- package/dist/templates/telegram-bot/vitest.config.ts +29 -0
- package/package.json +35 -0
- package/templates/monorepo/CLAUDE.md +164 -0
- package/templates/monorepo/LICENSE +21 -0
- package/templates/monorepo/MUST-FOLLOW-GUIDELINES.md +269 -0
- package/templates/monorepo/README.md +74 -0
- package/templates/monorepo/apps/example/package.json +19 -0
- package/templates/monorepo/apps/example/src/index.ts +23 -0
- package/templates/monorepo/apps/example/src/types/index.ts +7 -0
- package/templates/monorepo/apps/example/src/utils/index.ts +7 -0
- package/templates/monorepo/core/packages/main/package.json +41 -0
- package/templates/monorepo/core/packages/main/rolldown.config.ts +24 -0
- package/templates/monorepo/core/packages/main/src/index.ts +80 -0
- package/templates/monorepo/core/packages/main/src/types/constants.ts +15 -0
- package/templates/monorepo/core/packages/main/src/types/index.ts +8 -0
- package/templates/monorepo/core/packages/main/src/types/main.types.ts +25 -0
- package/templates/monorepo/core/packages/main/src/utils/index.ts +5 -0
- package/templates/monorepo/core/packages/utils/package.json +43 -0
- package/templates/monorepo/core/packages/utils/rolldown.config.ts +34 -0
- package/templates/monorepo/core/packages/utils/src/index.ts +2 -0
- package/templates/monorepo/core/packages/utils/src/logger.ts +68 -0
- package/templates/monorepo/core/packages/utils/src/result.ts +146 -0
- package/templates/monorepo/core/packages/utils/src/types/constants.ts +15 -0
- package/templates/monorepo/core/packages/utils/src/types/index.ts +8 -0
- package/templates/monorepo/core/packages/utils/src/types/utils.types.ts +32 -0
- package/templates/monorepo/core/packages/utils/src/utils/index.ts +5 -0
- package/templates/monorepo/oxlint.json +14 -0
- package/templates/monorepo/package.json +39 -0
- package/templates/monorepo/tsconfig.json +35 -0
- package/templates/telegram-bot/.oxlintrc.json +33 -0
- package/templates/telegram-bot/.prettierignore +5 -0
- package/templates/telegram-bot/.prettierrc +26 -0
- package/templates/telegram-bot/CLAUDE.deploy.md +356 -0
- package/templates/telegram-bot/CLAUDE.dev.md +266 -0
- package/templates/telegram-bot/CLAUDE.md +280 -0
- package/templates/telegram-bot/Dockerfile +46 -0
- package/templates/telegram-bot/README.md +245 -0
- package/templates/telegram-bot/apps/.gitkeep +0 -0
- package/templates/telegram-bot/bun.lock +208 -0
- package/templates/telegram-bot/core/.env.example +71 -0
- package/templates/telegram-bot/core/README.md +1067 -0
- package/templates/telegram-bot/core/package.json +15 -0
- package/templates/telegram-bot/core/src/config/env.ts +131 -0
- package/templates/telegram-bot/core/src/config/index.ts +97 -0
- package/templates/telegram-bot/core/src/config/logging.ts +110 -0
- package/templates/telegram-bot/core/src/handlers/control.ts +85 -0
- package/templates/telegram-bot/core/src/handlers/health.ts +83 -0
- package/templates/telegram-bot/core/src/handlers/logs.ts +126 -0
- package/templates/telegram-bot/core/src/index.ts +161 -0
- package/templates/telegram-bot/core/src/middleware/auth.ts +41 -0
- package/templates/telegram-bot/core/src/middleware/error-handler.ts +41 -0
- package/templates/telegram-bot/core/src/middleware/logging.ts +1 -0
- package/templates/telegram-bot/core/src/middleware/topics.ts +55 -0
- package/templates/telegram-bot/core/src/types/bot.ts +92 -0
- package/templates/telegram-bot/core/src/types/constants.ts +50 -0
- package/templates/telegram-bot/core/src/types/result.ts +1 -0
- package/templates/telegram-bot/core/src/utils/bot-manager.test.ts +111 -0
- package/templates/telegram-bot/core/src/utils/bot-manager.ts +201 -0
- package/templates/telegram-bot/core/src/utils/commands.ts +63 -0
- package/templates/telegram-bot/core/src/utils/formatters.ts +82 -0
- package/templates/telegram-bot/core/src/utils/instance-manager.ts +189 -0
- package/templates/telegram-bot/core/src/utils/memory.ts +33 -0
- package/templates/telegram-bot/core/src/utils/result.ts +26 -0
- package/templates/telegram-bot/core/src/utils/telegram.ts +31 -0
- package/templates/telegram-bot/core/src/utils/type-guards.ts +71 -0
- package/templates/telegram-bot/core/tsconfig.json +9 -0
- package/templates/telegram-bot/docker-compose.yml +37 -0
- package/templates/telegram-bot/docs/cli-commands.md +377 -0
- package/templates/telegram-bot/docs/development.md +363 -0
- package/templates/telegram-bot/docs/environment.md +460 -0
- package/templates/telegram-bot/docs/examples/middleware-auth.md +335 -0
- package/templates/telegram-bot/docs/examples/simple-command.md +207 -0
- package/templates/telegram-bot/docs/examples/webhook-setup.md +362 -0
- package/templates/telegram-bot/docs/getting-started.md +223 -0
- package/templates/telegram-bot/docs/troubleshooting.md +489 -0
- package/templates/telegram-bot/package.json +49 -0
- package/templates/telegram-bot/packages/utils/package.json +12 -0
- package/templates/telegram-bot/packages/utils/src/index.ts +2 -0
- package/templates/telegram-bot/packages/utils/src/logger.ts +72 -0
- package/templates/telegram-bot/packages/utils/src/result.ts +80 -0
- package/templates/telegram-bot/tools/README.md +47 -0
- package/templates/telegram-bot/tools/commands/doctor.ts +460 -0
- package/templates/telegram-bot/tools/commands/index.ts +35 -0
- package/templates/telegram-bot/tools/commands/ngrok.ts +207 -0
- package/templates/telegram-bot/tools/commands/setup.ts +368 -0
- package/templates/telegram-bot/tools/commands/status.ts +140 -0
- package/templates/telegram-bot/tools/index.ts +16 -0
- package/templates/telegram-bot/tools/package.json +12 -0
- package/templates/telegram-bot/tools/utils/index.ts +13 -0
- package/templates/telegram-bot/tsconfig.json +22 -0
- package/templates/telegram-bot/vitest.config.ts +29 -0
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
# CLI Tools
|
|
2
|
+
|
|
3
|
+
## Usage
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun run cli # Show all commands
|
|
7
|
+
bun run cli ngrok # Run ngrok command
|
|
8
|
+
bun run ngrok # Shortcut for ngrok command
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Adding New Commands
|
|
12
|
+
|
|
13
|
+
1. Create file in `tools/commands/mycommand.ts`
|
|
14
|
+
2. Export default object with `BotCommand` interface
|
|
15
|
+
3. Implement `register(program)` method
|
|
16
|
+
|
|
17
|
+
Example:
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
import { Command } from 'commander'
|
|
21
|
+
import type { BotCommand } from './index.js'
|
|
22
|
+
|
|
23
|
+
interface MyCommand extends BotCommand {
|
|
24
|
+
name: 'mycommand'
|
|
25
|
+
description: string
|
|
26
|
+
register: (program: Command) => void
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const command: MyCommand = {
|
|
30
|
+
name: 'mycommand',
|
|
31
|
+
description: 'My command description',
|
|
32
|
+
|
|
33
|
+
register(program: Command) {
|
|
34
|
+
program
|
|
35
|
+
.command('mycommand')
|
|
36
|
+
.description('Does something')
|
|
37
|
+
.option('-o, --option <value>', 'Option description', 'default')
|
|
38
|
+
.action(async (options) => {
|
|
39
|
+
// Implementation
|
|
40
|
+
})
|
|
41
|
+
},
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export default command
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Commands are auto-discovered and registered on CLI startup.
|
|
@@ -0,0 +1,460 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { readFile } from 'fs/promises'
|
|
3
|
+
import { existsSync } from 'fs'
|
|
4
|
+
import { join } from 'path'
|
|
5
|
+
import chalk from 'chalk'
|
|
6
|
+
import type { BotCommand } from './index.js'
|
|
7
|
+
|
|
8
|
+
const cliLogger = {
|
|
9
|
+
info: (msg: string) => console.log(chalk.blue('ℹ'), msg),
|
|
10
|
+
success: (msg: string) => console.log(chalk.green('✓'), msg),
|
|
11
|
+
error: (msg: string) => console.error(chalk.red('✗'), msg),
|
|
12
|
+
warn: (msg: string) => console.log(chalk.yellow('⚠'), msg),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
interface DoctorCommand extends BotCommand {
|
|
16
|
+
name: 'doctor'
|
|
17
|
+
description: string
|
|
18
|
+
register: (program: Command) => void
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
interface CheckResult {
|
|
22
|
+
name: string
|
|
23
|
+
status: 'pass' | 'warn' | 'fail'
|
|
24
|
+
message: string
|
|
25
|
+
details?: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const command: DoctorCommand = {
|
|
29
|
+
name: 'doctor',
|
|
30
|
+
description: 'Diagnose bot configuration and environment',
|
|
31
|
+
|
|
32
|
+
register(program: Command) {
|
|
33
|
+
program
|
|
34
|
+
.command('doctor')
|
|
35
|
+
.description('Run diagnostics on bot configuration')
|
|
36
|
+
.action(async () => {
|
|
37
|
+
await handleDoctor()
|
|
38
|
+
})
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export default command
|
|
43
|
+
|
|
44
|
+
async function handleDoctor(): Promise<void> {
|
|
45
|
+
console.log('')
|
|
46
|
+
console.log(chalk.cyan.bold('mks-telegram-bot Diagnostics'))
|
|
47
|
+
console.log('')
|
|
48
|
+
|
|
49
|
+
const checks: CheckResult[] = []
|
|
50
|
+
|
|
51
|
+
// Run all checks
|
|
52
|
+
checks.push(await checkNodeVersion())
|
|
53
|
+
checks.push(await checkBunVersion())
|
|
54
|
+
checks.push(await checkDependencies())
|
|
55
|
+
checks.push(await checkEnvFile())
|
|
56
|
+
checks.push(await checkRequiredEnvVars())
|
|
57
|
+
checks.push(await checkBotToken())
|
|
58
|
+
checks.push(await checkTmpDirectory())
|
|
59
|
+
checks.push(await checkLogsDirectory())
|
|
60
|
+
checks.push(await checkPorts())
|
|
61
|
+
checks.push(await checkGitIgnore())
|
|
62
|
+
|
|
63
|
+
// Display results
|
|
64
|
+
displayResults(checks)
|
|
65
|
+
|
|
66
|
+
// Exit with appropriate code
|
|
67
|
+
const failed = checks.filter((c) => c.status === 'fail')
|
|
68
|
+
if (failed.length > 0) {
|
|
69
|
+
process.exit(1)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async function checkNodeVersion(): Promise<CheckResult> {
|
|
74
|
+
const version = process.version
|
|
75
|
+
const versionParts = version.slice(1).split('.')
|
|
76
|
+
const major = Number.parseInt(versionParts[0] ?? '0', 10)
|
|
77
|
+
|
|
78
|
+
if (major >= 20) {
|
|
79
|
+
return {
|
|
80
|
+
name: 'Node.js version',
|
|
81
|
+
status: 'pass',
|
|
82
|
+
message: `Node.js ${version} (requires >= 20)`,
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
name: 'Node.js version',
|
|
88
|
+
status: 'fail',
|
|
89
|
+
message: `Node.js ${version} (requires >= 20)`,
|
|
90
|
+
details: 'Please upgrade Node.js to version 20 or later',
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
async function checkBunVersion(): Promise<CheckResult> {
|
|
95
|
+
try {
|
|
96
|
+
const result = await Bun.spawn(['bun', '--version'], {
|
|
97
|
+
stdout: 'pipe',
|
|
98
|
+
stderr: 'pipe',
|
|
99
|
+
}).exited
|
|
100
|
+
|
|
101
|
+
if (result === 0) {
|
|
102
|
+
return {
|
|
103
|
+
name: 'Bun installation',
|
|
104
|
+
status: 'pass',
|
|
105
|
+
message: 'Bun is installed',
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
return {
|
|
110
|
+
name: 'Bun installation',
|
|
111
|
+
status: 'fail',
|
|
112
|
+
message: 'Bun not found',
|
|
113
|
+
details: 'Install Bun from https://bun.sh',
|
|
114
|
+
}
|
|
115
|
+
} catch {
|
|
116
|
+
return {
|
|
117
|
+
name: 'Bun installation',
|
|
118
|
+
status: 'fail',
|
|
119
|
+
message: 'Bun not found',
|
|
120
|
+
details: 'Install Bun from https://bun.sh',
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async function checkDependencies(): Promise<CheckResult> {
|
|
126
|
+
const packageJsonPath = join(process.cwd(), 'package.json')
|
|
127
|
+
const lockPath = join(process.cwd(), 'bun.lock')
|
|
128
|
+
|
|
129
|
+
if (!existsSync(packageJsonPath)) {
|
|
130
|
+
return {
|
|
131
|
+
name: 'Dependencies',
|
|
132
|
+
status: 'fail',
|
|
133
|
+
message: 'package.json not found',
|
|
134
|
+
details: 'You are not in a valid project directory',
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
if (!existsSync(lockPath)) {
|
|
139
|
+
return {
|
|
140
|
+
name: 'Dependencies',
|
|
141
|
+
status: 'fail',
|
|
142
|
+
message: 'Dependencies not installed',
|
|
143
|
+
details: 'Run: bun install',
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const corePackagePath = join(process.cwd(), 'core', 'node_modules')
|
|
148
|
+
|
|
149
|
+
if (!existsSync(corePackagePath)) {
|
|
150
|
+
return {
|
|
151
|
+
name: 'Dependencies',
|
|
152
|
+
status: 'fail',
|
|
153
|
+
message: 'Workspace dependencies not installed',
|
|
154
|
+
details: 'Run: bun install',
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
return {
|
|
159
|
+
name: 'Dependencies',
|
|
160
|
+
status: 'pass',
|
|
161
|
+
message: 'All dependencies installed',
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
async function checkEnvFile(): Promise<CheckResult> {
|
|
166
|
+
const envFiles = ['.env.local', '.env.staging', '.env.production']
|
|
167
|
+
const foundFiles: string[] = []
|
|
168
|
+
|
|
169
|
+
for (const file of envFiles) {
|
|
170
|
+
const path = join(process.cwd(), 'core', file)
|
|
171
|
+
if (existsSync(path)) {
|
|
172
|
+
foundFiles.push(file)
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
if (foundFiles.length > 0) {
|
|
177
|
+
return {
|
|
178
|
+
name: 'Environment files',
|
|
179
|
+
status: 'pass',
|
|
180
|
+
message: `Found: ${foundFiles.join(', ')}`,
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return {
|
|
185
|
+
name: 'Environment files',
|
|
186
|
+
status: 'fail',
|
|
187
|
+
message: 'No .env file found',
|
|
188
|
+
details: 'Run: bun run setup',
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
async function checkRequiredEnvVars(): Promise<CheckResult> {
|
|
193
|
+
const envPath = join(process.cwd(), 'core', '.env.local')
|
|
194
|
+
|
|
195
|
+
if (!existsSync(envPath)) {
|
|
196
|
+
return {
|
|
197
|
+
name: 'Environment variables',
|
|
198
|
+
status: 'warn',
|
|
199
|
+
message: 'No .env.local file to check',
|
|
200
|
+
details: 'Run: bun run setup',
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
try {
|
|
205
|
+
const content = await readFile(envPath, 'utf-8')
|
|
206
|
+
const missing: string[] = []
|
|
207
|
+
|
|
208
|
+
// Check for required variables
|
|
209
|
+
if (!content.includes('TG_BOT_TOKEN=') || content.includes('TG_BOT_TOKEN=123456:')) {
|
|
210
|
+
missing.push('TG_BOT_TOKEN')
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
if (!content.includes('TG_MODE=')) {
|
|
214
|
+
missing.push('TG_MODE')
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (missing.length === 0) {
|
|
218
|
+
return {
|
|
219
|
+
name: 'Environment variables',
|
|
220
|
+
status: 'pass',
|
|
221
|
+
message: 'Required variables set',
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return {
|
|
226
|
+
name: 'Environment variables',
|
|
227
|
+
status: 'fail',
|
|
228
|
+
message: `Missing: ${missing.join(', ')}`,
|
|
229
|
+
details: 'Edit core/.env.local or run: bun run setup',
|
|
230
|
+
}
|
|
231
|
+
} catch {
|
|
232
|
+
return {
|
|
233
|
+
name: 'Environment variables',
|
|
234
|
+
status: 'warn',
|
|
235
|
+
message: 'Could not read .env.local',
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
async function checkBotToken(): Promise<CheckResult> {
|
|
241
|
+
const envPath = join(process.cwd(), 'core', '.env.local')
|
|
242
|
+
|
|
243
|
+
if (!existsSync(envPath)) {
|
|
244
|
+
return {
|
|
245
|
+
name: 'Bot token validation',
|
|
246
|
+
status: 'warn',
|
|
247
|
+
message: 'No .env.local file found',
|
|
248
|
+
details: 'Run: bun run setup',
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
try {
|
|
253
|
+
const content = await readFile(envPath, 'utf-8')
|
|
254
|
+
const match = content.match(/TG_BOT_TOKEN=([^\n]+)/)
|
|
255
|
+
|
|
256
|
+
if (!match || match[1] === '123456:ABC-DEF1234ghIkl-zyx57W2v1u123ew11') {
|
|
257
|
+
return {
|
|
258
|
+
name: 'Bot token validation',
|
|
259
|
+
status: 'fail',
|
|
260
|
+
message: 'Invalid or placeholder bot token',
|
|
261
|
+
details: 'Get a token from @BotFather and run: bun run setup',
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
const token = (match[1] ?? '').trim()
|
|
266
|
+
|
|
267
|
+
// Try to validate with Telegram API
|
|
268
|
+
try {
|
|
269
|
+
const response = await fetch(`https://api.telegram.org/bot${token}/getMe`, {
|
|
270
|
+
signal: AbortSignal.timeout(5000),
|
|
271
|
+
})
|
|
272
|
+
const data = (await response.json()) as { ok: boolean; description?: string }
|
|
273
|
+
|
|
274
|
+
if (data.ok) {
|
|
275
|
+
return {
|
|
276
|
+
name: 'Bot token validation',
|
|
277
|
+
status: 'pass',
|
|
278
|
+
message: 'Bot token is valid',
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
return {
|
|
283
|
+
name: 'Bot token validation',
|
|
284
|
+
status: 'fail',
|
|
285
|
+
message: 'Invalid bot token',
|
|
286
|
+
details: data.description ?? 'Token rejected by Telegram API',
|
|
287
|
+
}
|
|
288
|
+
} catch {
|
|
289
|
+
return {
|
|
290
|
+
name: 'Bot token validation',
|
|
291
|
+
status: 'warn',
|
|
292
|
+
message: 'Could not validate token (network error)',
|
|
293
|
+
details: 'Check your internet connection',
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
} catch {
|
|
297
|
+
return {
|
|
298
|
+
name: 'Bot token validation',
|
|
299
|
+
status: 'warn',
|
|
300
|
+
message: 'Could not read bot token',
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
async function checkTmpDirectory(): Promise<CheckResult> {
|
|
306
|
+
const tmpPath = join(process.cwd(), 'core', 'tmp')
|
|
307
|
+
|
|
308
|
+
if (!existsSync(tmpPath)) {
|
|
309
|
+
return {
|
|
310
|
+
name: 'Temp directory',
|
|
311
|
+
status: 'warn',
|
|
312
|
+
message: 'core/tmp does not exist',
|
|
313
|
+
details: 'It will be created automatically when the bot starts',
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// Check if writable
|
|
318
|
+
try {
|
|
319
|
+
const testFile = join(tmpPath, `.write-test-${Date.now()}`)
|
|
320
|
+
await Bun.write(testFile, 'test')
|
|
321
|
+
await Bun.file(testFile).delete()
|
|
322
|
+
return {
|
|
323
|
+
name: 'Temp directory',
|
|
324
|
+
status: 'pass',
|
|
325
|
+
message: 'core/tmp is writable',
|
|
326
|
+
}
|
|
327
|
+
} catch {
|
|
328
|
+
return {
|
|
329
|
+
name: 'Temp directory',
|
|
330
|
+
status: 'fail',
|
|
331
|
+
message: 'core/tmp is not writable',
|
|
332
|
+
details: 'Check permissions on core/tmp directory',
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function checkLogsDirectory(): Promise<CheckResult> {
|
|
338
|
+
const logsPath = join(process.cwd(), 'core', 'logs')
|
|
339
|
+
|
|
340
|
+
if (!existsSync(logsPath)) {
|
|
341
|
+
return {
|
|
342
|
+
name: 'Logs directory',
|
|
343
|
+
status: 'warn',
|
|
344
|
+
message: 'core/logs does not exist',
|
|
345
|
+
details: 'It will be created automatically when needed',
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
return {
|
|
350
|
+
name: 'Logs directory',
|
|
351
|
+
status: 'pass',
|
|
352
|
+
message: 'core/logs exists',
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
async function checkPorts(): Promise<CheckResult> {
|
|
357
|
+
const port = 3000
|
|
358
|
+
const server = Bun.serve({
|
|
359
|
+
port,
|
|
360
|
+
fetch: () => new Response('OK'),
|
|
361
|
+
})
|
|
362
|
+
|
|
363
|
+
const portInUse = server.port === port
|
|
364
|
+
|
|
365
|
+
await server.stop(true)
|
|
366
|
+
|
|
367
|
+
if (portInUse) {
|
|
368
|
+
return {
|
|
369
|
+
name: 'Port availability',
|
|
370
|
+
status: 'pass',
|
|
371
|
+
message: `Port ${port} is available`,
|
|
372
|
+
}
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
return {
|
|
376
|
+
name: 'Port availability',
|
|
377
|
+
status: 'warn',
|
|
378
|
+
message: `Port ${port} may be in use`,
|
|
379
|
+
details: 'If you have issues, check for other processes using this port',
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
async function checkGitIgnore(): Promise<CheckResult> {
|
|
384
|
+
const gitIgnorePath = join(process.cwd(), '.gitignore')
|
|
385
|
+
|
|
386
|
+
if (!existsSync(gitIgnorePath)) {
|
|
387
|
+
return {
|
|
388
|
+
name: 'Git ignore',
|
|
389
|
+
status: 'warn',
|
|
390
|
+
message: '.gitignore not found',
|
|
391
|
+
details: 'Consider adding .gitignore to exclude .env files',
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
try {
|
|
396
|
+
const content = await readFile(gitIgnorePath, 'utf-8')
|
|
397
|
+
const hasEnvIgnore = content.includes('.env') || content.includes('core/.env')
|
|
398
|
+
|
|
399
|
+
if (hasEnvIgnore) {
|
|
400
|
+
return {
|
|
401
|
+
name: 'Git ignore',
|
|
402
|
+
status: 'pass',
|
|
403
|
+
message: '.env files are excluded from git',
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
name: 'Git ignore',
|
|
409
|
+
status: 'warn',
|
|
410
|
+
message: '.env files may not be excluded from git',
|
|
411
|
+
details: 'Add "*.env" or "core/.env*" to .gitignore',
|
|
412
|
+
}
|
|
413
|
+
} catch {
|
|
414
|
+
return {
|
|
415
|
+
name: 'Git ignore',
|
|
416
|
+
status: 'warn',
|
|
417
|
+
message: 'Could not read .gitignore',
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
function displayResults(checks: CheckResult[]): void {
|
|
423
|
+
const passed = checks.filter((c) => c.status === 'pass')
|
|
424
|
+
const warnings = checks.filter((c) => c.status === 'warn')
|
|
425
|
+
const failed = checks.filter((c) => c.status === 'fail')
|
|
426
|
+
|
|
427
|
+
// Display checks
|
|
428
|
+
for (const check of checks) {
|
|
429
|
+
const icon = check.status === 'pass' ? chalk.green('✓') : check.status === 'warn' ? chalk.yellow('⚠') : chalk.red('✗')
|
|
430
|
+
const name = check.name.padEnd(30)
|
|
431
|
+
|
|
432
|
+
console.log(`${icon} ${chalk.bold(name)} ${check.message}`)
|
|
433
|
+
|
|
434
|
+
if (check.details) {
|
|
435
|
+
console.log(` ${chalk.dim(check.details)}`)
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
// Summary
|
|
440
|
+
console.log('')
|
|
441
|
+
console.log(chalk.bold('Summary:'))
|
|
442
|
+
console.log(` ${chalk.green('✓')} Passed: ${passed.length}`)
|
|
443
|
+
console.log(` ${chalk.yellow('⚠')} Warnings: ${warnings.length}`)
|
|
444
|
+
console.log(` ${chalk.red('✗')} Failed: ${failed.length}`)
|
|
445
|
+
console.log('')
|
|
446
|
+
|
|
447
|
+
if (failed.length === 0) {
|
|
448
|
+
cliLogger.success('All checks passed! Your bot is ready to run.')
|
|
449
|
+
console.log('')
|
|
450
|
+
console.log('Next steps:')
|
|
451
|
+
console.log(' 1. Run: bun run dev')
|
|
452
|
+
console.log(' 2. Send /start to your bot')
|
|
453
|
+
} else {
|
|
454
|
+
cliLogger.error('Some checks failed. Please fix the issues above.')
|
|
455
|
+
console.log('')
|
|
456
|
+
console.log('Common fixes:')
|
|
457
|
+
console.log(' - Run: bun run setup')
|
|
458
|
+
console.log(' - Run: bun install')
|
|
459
|
+
}
|
|
460
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { Command } from 'commander'
|
|
2
|
+
import { readdirSync } from 'fs'
|
|
3
|
+
import { join } from 'path'
|
|
4
|
+
import { URL, fileURLToPath } from 'url'
|
|
5
|
+
|
|
6
|
+
const __dirname = fileURLToPath(new URL('.', import.meta.url))
|
|
7
|
+
|
|
8
|
+
export interface BotCommand {
|
|
9
|
+
name: string
|
|
10
|
+
description: string
|
|
11
|
+
register: (program: Command) => void
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export async function registerCommands(program: Command): Promise<void> {
|
|
15
|
+
const commandsDir = join(__dirname)
|
|
16
|
+
|
|
17
|
+
const commandFiles = readdirSync(commandsDir).filter(
|
|
18
|
+
(file) => file.endsWith('.ts') && file !== 'index.ts'
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
// Sequential command loading is intentional for order preservation
|
|
22
|
+
/* oxlint-disable no-await-in-loop */
|
|
23
|
+
for (const file of commandFiles) {
|
|
24
|
+
try {
|
|
25
|
+
const module = await import(`./${file}`)
|
|
26
|
+
const command: BotCommand = module.default
|
|
27
|
+
if (command?.register) {
|
|
28
|
+
command.register(program)
|
|
29
|
+
}
|
|
30
|
+
} catch (error) {
|
|
31
|
+
console.error(`Failed to load command from ${file}:`, error)
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
/* oxlint-enable no-await-in-loop */
|
|
35
|
+
}
|