@2byte/tgbot-framework 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.
- package/README.md +301 -0
- package/bin/2byte-cli.ts +85 -0
- package/package.json +50 -0
- package/src/cli/CreateBotCommand.ts +182 -0
- package/src/cli/GenerateCommand.ts +112 -0
- package/src/cli/InitCommand.ts +108 -0
- package/src/console/migrate.ts +83 -0
- package/src/core/App.ts +1016 -0
- package/src/core/BotArtisan.ts +80 -0
- package/src/core/BotMigration.ts +31 -0
- package/src/core/BotSeeder.ts +67 -0
- package/src/core/utils.ts +3 -0
- package/src/illumination/Artisan.ts +149 -0
- package/src/illumination/InlineKeyboard.ts +44 -0
- package/src/illumination/Message2Byte.ts +254 -0
- package/src/illumination/Message2ByteLiveProgressive.ts +278 -0
- package/src/illumination/Message2bytePool.ts +108 -0
- package/src/illumination/Migration.ts +186 -0
- package/src/illumination/RunSectionRoute.ts +85 -0
- package/src/illumination/Section.ts +430 -0
- package/src/illumination/SectionComponent.ts +64 -0
- package/src/illumination/Telegraf2byteContext.ts +33 -0
- package/src/index.ts +33 -0
- package/src/libs/TelegramAccountControl.ts +523 -0
- package/src/types.ts +172 -0
- package/src/user/UserModel.ts +132 -0
- package/src/user/UserStore.ts +119 -0
- package/templates/bot/.env.example +18 -0
- package/templates/bot/artisan.ts +9 -0
- package/templates/bot/bot.ts +74 -0
- package/templates/bot/database/dbConnector.ts +5 -0
- package/templates/bot/database/migrate.ts +10 -0
- package/templates/bot/database/migrations/001_create_users.sql +17 -0
- package/templates/bot/database/seed.ts +15 -0
- package/templates/bot/package.json +31 -0
- package/templates/bot/sectionList.ts +7 -0
- package/templates/bot/sections/HomeSection.ts +63 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import * as fs from 'fs-extra';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
export class GenerateCommand {
|
|
6
|
+
async generateSection(name: string): Promise<void> {
|
|
7
|
+
console.log(chalk.blue(`🎯 Generating section: ${name}`));
|
|
8
|
+
|
|
9
|
+
const currentDir = process.cwd();
|
|
10
|
+
const sectionsDir = path.join(currentDir, 'sections');
|
|
11
|
+
const sectionName = this.formatSectionName(name);
|
|
12
|
+
const sectionPath = path.join(sectionsDir, `${sectionName}Section.ts`);
|
|
13
|
+
|
|
14
|
+
// Ensure sections directory exists
|
|
15
|
+
await fs.ensureDir(sectionsDir);
|
|
16
|
+
|
|
17
|
+
// Check if section already exists
|
|
18
|
+
if (await fs.pathExists(sectionPath)) {
|
|
19
|
+
console.log(chalk.red(`❌ Section ${sectionName} already exists at ${sectionPath}`));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
// Generate section content
|
|
24
|
+
const template = this.getSectionTemplate(sectionName);
|
|
25
|
+
await fs.writeFile(sectionPath, template);
|
|
26
|
+
|
|
27
|
+
console.log(chalk.green(`✅ Created section ${sectionName} at ${sectionPath}`));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
async generateMigration(name: string): Promise<void> {
|
|
31
|
+
console.log(chalk.blue(`🗃️ Generating migration: ${name}`));
|
|
32
|
+
|
|
33
|
+
const currentDir = process.cwd();
|
|
34
|
+
const migrationsDir = path.join(currentDir, 'database', 'migrations');
|
|
35
|
+
|
|
36
|
+
// Ensure migrations directory exists
|
|
37
|
+
await fs.ensureDir(migrationsDir);
|
|
38
|
+
|
|
39
|
+
// Get next migration ID
|
|
40
|
+
const files = await fs.readdir(migrationsDir);
|
|
41
|
+
const migrationFiles = files
|
|
42
|
+
.filter(file => file.endsWith('.sql'))
|
|
43
|
+
.map(file => parseInt(file.split('_')[0]))
|
|
44
|
+
.filter(id => !isNaN(id));
|
|
45
|
+
|
|
46
|
+
const nextId = (Math.max(0, ...migrationFiles) + 1).toString().padStart(3, '0');
|
|
47
|
+
const fileName = `${nextId}_${name}.sql`;
|
|
48
|
+
const filePath = path.join(migrationsDir, fileName);
|
|
49
|
+
|
|
50
|
+
const template = this.getMigrationTemplate(name);
|
|
51
|
+
await fs.writeFile(filePath, template);
|
|
52
|
+
|
|
53
|
+
console.log(chalk.green(`✅ Created migration: ${fileName}`));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
private formatSectionName(name: string): string {
|
|
57
|
+
return name.charAt(0).toUpperCase() + name.slice(1).toLowerCase();
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
private getSectionTemplate(name: string): string {
|
|
61
|
+
return `import { Section } from "2bytetgbot";
|
|
62
|
+
import { SectionOptions } from "2bytetgbot";
|
|
63
|
+
import { InlineKeyboard } from "2bytetgbot";
|
|
64
|
+
|
|
65
|
+
export default class ${name}Section extends Section {
|
|
66
|
+
static command = "${name.toLowerCase()}";
|
|
67
|
+
static description = "${name} section";
|
|
68
|
+
static actionRoutes = {
|
|
69
|
+
"${name.toLowerCase()}.index": "index",
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
public sectionId = "${name.toLowerCase()}";
|
|
73
|
+
private mainInlineKeyboard: InlineKeyboard;
|
|
74
|
+
|
|
75
|
+
constructor(options: SectionOptions) {
|
|
76
|
+
super(options);
|
|
77
|
+
|
|
78
|
+
this.mainInlineKeyboard = this.makeInlineKeyboard([
|
|
79
|
+
[this.makeInlineButton("🏠 На главную", "home.index")],
|
|
80
|
+
]);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
public async up(): Promise<void> {}
|
|
84
|
+
public async down(): Promise<void> {}
|
|
85
|
+
public async setup(): Promise<void> {}
|
|
86
|
+
public async unsetup(): Promise<void> {}
|
|
87
|
+
|
|
88
|
+
async index() {
|
|
89
|
+
const message = \`
|
|
90
|
+
👋 Welcome to ${name} Section
|
|
91
|
+
\`;
|
|
92
|
+
|
|
93
|
+
await this.message(message)
|
|
94
|
+
.inlineKeyboard(this.mainInlineKeyboard)
|
|
95
|
+
.send();
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
`;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
private getMigrationTemplate(name: string): string {
|
|
102
|
+
return `-- UP
|
|
103
|
+
CREATE TABLE IF NOT EXISTS ${name} (
|
|
104
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
105
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
106
|
+
);
|
|
107
|
+
|
|
108
|
+
-- DOWN
|
|
109
|
+
DROP TABLE IF EXISTS ${name};
|
|
110
|
+
`;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import * as fs from 'fs-extra';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
|
|
5
|
+
export class InitCommand {
|
|
6
|
+
async execute(options: any): Promise<void> {
|
|
7
|
+
const currentDir = process.cwd();
|
|
8
|
+
const packageJsonPath = path.join(currentDir, 'package.json');
|
|
9
|
+
|
|
10
|
+
console.log(chalk.blue('🔧 Initializing 2byte bot in current directory...'));
|
|
11
|
+
|
|
12
|
+
// Check if already a 2byte bot
|
|
13
|
+
if (await fs.pathExists(packageJsonPath)) {
|
|
14
|
+
const packageJson = await fs.readJson(packageJsonPath);
|
|
15
|
+
|
|
16
|
+
if (packageJson.dependencies && packageJson.dependencies['2bytetgbot']) {
|
|
17
|
+
if (!options.force) {
|
|
18
|
+
console.log(chalk.yellow('⚠️ This directory already contains a 2byte bot.'));
|
|
19
|
+
console.log(chalk.yellow(' Use --force to override existing files.'));
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Create basic bot structure
|
|
26
|
+
await this.createBotStructure(currentDir, options);
|
|
27
|
+
|
|
28
|
+
console.log(chalk.green('✅ 2byte bot initialized successfully!'));
|
|
29
|
+
console.log(chalk.blue('📋 Next steps:'));
|
|
30
|
+
console.log(' bun install # Install dependencies');
|
|
31
|
+
console.log(' bun run migrate # Run migrations');
|
|
32
|
+
console.log(' bun run seed # Seed database');
|
|
33
|
+
console.log(' bun run dev # Start bot');
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
private async createBotStructure(targetPath: string, options: any): Promise<void> {
|
|
37
|
+
const templatesPath = path.join(__dirname, '../../templates/bot');
|
|
38
|
+
|
|
39
|
+
// Copy essential files
|
|
40
|
+
const essentialFiles = [
|
|
41
|
+
'package.json',
|
|
42
|
+
'artisan.ts',
|
|
43
|
+
'bot.ts',
|
|
44
|
+
'sections.ts',
|
|
45
|
+
'.env.example',
|
|
46
|
+
'database/migrate.ts',
|
|
47
|
+
'database/seed.ts',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
for (const file of essentialFiles) {
|
|
51
|
+
const sourcePath = path.join(templatesPath, file);
|
|
52
|
+
const targetFilePath = path.join(targetPath, file);
|
|
53
|
+
|
|
54
|
+
if (await fs.pathExists(sourcePath)) {
|
|
55
|
+
if (!options.force && await fs.pathExists(targetFilePath)) {
|
|
56
|
+
console.log(chalk.yellow(`⚠️ Skipping ${file} (already exists)`));
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
await fs.ensureDir(path.dirname(targetFilePath));
|
|
61
|
+
|
|
62
|
+
// Read and process template
|
|
63
|
+
let content = await fs.readFile(sourcePath, 'utf-8');
|
|
64
|
+
|
|
65
|
+
// For now, use current directory name as bot name
|
|
66
|
+
const botName = path.basename(targetPath);
|
|
67
|
+
const config = {
|
|
68
|
+
botName,
|
|
69
|
+
className: this.toPascalCase(botName),
|
|
70
|
+
kebabName: this.toKebabCase(botName),
|
|
71
|
+
description: `A telegram bot created with 2byte framework`,
|
|
72
|
+
author: '',
|
|
73
|
+
useDatabase: true,
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// Simple template replacement
|
|
77
|
+
content = this.processTemplate(content, config);
|
|
78
|
+
|
|
79
|
+
await fs.writeFile(targetFilePath, content);
|
|
80
|
+
console.log(chalk.green(`✅ Created ${file}`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private processTemplate(content: string, config: any): string {
|
|
86
|
+
// Simple mustache-style replacement
|
|
87
|
+
return content
|
|
88
|
+
.replace(/\{\{botName\}\}/g, config.botName)
|
|
89
|
+
.replace(/\{\{className\}\}/g, config.className)
|
|
90
|
+
.replace(/\{\{kebabName\}\}/g, config.kebabName)
|
|
91
|
+
.replace(/\{\{description\}\}/g, config.description)
|
|
92
|
+
.replace(/\{\{author\}\}/g, config.author);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
private toPascalCase(str: string): string {
|
|
96
|
+
return str
|
|
97
|
+
.split(/[-_\s]+/)
|
|
98
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
99
|
+
.join('');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
private toKebabCase(str: string): string {
|
|
103
|
+
return str
|
|
104
|
+
.replace(/([a-z])([A-Z])/g, '$1-$2')
|
|
105
|
+
.replace(/[\s_]+/g, '-')
|
|
106
|
+
.toLowerCase();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
#!/usr/bin/env bun
|
|
2
|
+
import { Database } from 'bun:sqlite';
|
|
3
|
+
import { Migration } from '../illumination/Migration';
|
|
4
|
+
import path from 'path';
|
|
5
|
+
|
|
6
|
+
type setupParams = {
|
|
7
|
+
pathDatabase?: string;
|
|
8
|
+
pathMigrations?: string;
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
async function main(params: setupParams = {}) {
|
|
12
|
+
|
|
13
|
+
const MIGRATIONS_PATH = params.pathMigrations || path.join(__dirname, 'migrations');
|
|
14
|
+
|
|
15
|
+
const command = process.argv[2];
|
|
16
|
+
const args = process.argv.slice(3);
|
|
17
|
+
|
|
18
|
+
if (!command) {
|
|
19
|
+
showHelp();
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const db = new Database(params.pathDatabase || __dirname + '/database/database.sqlite');
|
|
24
|
+
|
|
25
|
+
const migration = new Migration(db, MIGRATIONS_PATH);
|
|
26
|
+
|
|
27
|
+
try {
|
|
28
|
+
switch (command) {
|
|
29
|
+
case 'create':
|
|
30
|
+
if (args.length === 0) {
|
|
31
|
+
console.error('❌ Ошибка: Требуется имя миграции');
|
|
32
|
+
console.log('Использование: migrate create migration_name');
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await Migration.create(args[0], MIGRATIONS_PATH);
|
|
36
|
+
break;
|
|
37
|
+
|
|
38
|
+
case 'up':
|
|
39
|
+
await migration.up();
|
|
40
|
+
console.log('✅ Миграции выполнены');
|
|
41
|
+
break;
|
|
42
|
+
|
|
43
|
+
case 'down':
|
|
44
|
+
const steps = args[0] ? parseInt(args[0]) : 1;
|
|
45
|
+
await migration.down(steps);
|
|
46
|
+
console.log('✅ Откат миграций выполнен');
|
|
47
|
+
break;
|
|
48
|
+
|
|
49
|
+
case 'status':
|
|
50
|
+
migration.status();
|
|
51
|
+
break;
|
|
52
|
+
|
|
53
|
+
default:
|
|
54
|
+
console.error(`❌ Неизвестная команда: ${command}`);
|
|
55
|
+
showHelp();
|
|
56
|
+
}
|
|
57
|
+
} catch (error) {
|
|
58
|
+
console.error('❌ Ошибка:', error);
|
|
59
|
+
} finally {
|
|
60
|
+
db.close();
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function showHelp() {
|
|
65
|
+
console.log(`
|
|
66
|
+
🗃️ Migration CLI для SQLite
|
|
67
|
+
|
|
68
|
+
Доступные команды:
|
|
69
|
+
create <name> Создать новую миграцию
|
|
70
|
+
up Выполнить все новые миграции
|
|
71
|
+
down [steps] Откатить последние миграции (по умолчанию 1)
|
|
72
|
+
status Показать статус миграций
|
|
73
|
+
|
|
74
|
+
Примеры:
|
|
75
|
+
migrate create create_users_table
|
|
76
|
+
migrate up
|
|
77
|
+
migrate down 2
|
|
78
|
+
migrate status
|
|
79
|
+
`);
|
|
80
|
+
}
|
|
81
|
+
export const setupMigrations = async (params: setupParams) => {
|
|
82
|
+
return main(params).catch(console.error);
|
|
83
|
+
}
|