@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.
Files changed (37) hide show
  1. package/README.md +301 -0
  2. package/bin/2byte-cli.ts +85 -0
  3. package/package.json +50 -0
  4. package/src/cli/CreateBotCommand.ts +182 -0
  5. package/src/cli/GenerateCommand.ts +112 -0
  6. package/src/cli/InitCommand.ts +108 -0
  7. package/src/console/migrate.ts +83 -0
  8. package/src/core/App.ts +1016 -0
  9. package/src/core/BotArtisan.ts +80 -0
  10. package/src/core/BotMigration.ts +31 -0
  11. package/src/core/BotSeeder.ts +67 -0
  12. package/src/core/utils.ts +3 -0
  13. package/src/illumination/Artisan.ts +149 -0
  14. package/src/illumination/InlineKeyboard.ts +44 -0
  15. package/src/illumination/Message2Byte.ts +254 -0
  16. package/src/illumination/Message2ByteLiveProgressive.ts +278 -0
  17. package/src/illumination/Message2bytePool.ts +108 -0
  18. package/src/illumination/Migration.ts +186 -0
  19. package/src/illumination/RunSectionRoute.ts +85 -0
  20. package/src/illumination/Section.ts +430 -0
  21. package/src/illumination/SectionComponent.ts +64 -0
  22. package/src/illumination/Telegraf2byteContext.ts +33 -0
  23. package/src/index.ts +33 -0
  24. package/src/libs/TelegramAccountControl.ts +523 -0
  25. package/src/types.ts +172 -0
  26. package/src/user/UserModel.ts +132 -0
  27. package/src/user/UserStore.ts +119 -0
  28. package/templates/bot/.env.example +18 -0
  29. package/templates/bot/artisan.ts +9 -0
  30. package/templates/bot/bot.ts +74 -0
  31. package/templates/bot/database/dbConnector.ts +5 -0
  32. package/templates/bot/database/migrate.ts +10 -0
  33. package/templates/bot/database/migrations/001_create_users.sql +17 -0
  34. package/templates/bot/database/seed.ts +15 -0
  35. package/templates/bot/package.json +31 -0
  36. package/templates/bot/sectionList.ts +7 -0
  37. 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
+ }