@2byte/tgbot-framework 1.0.3 → 1.0.5
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 +300 -300
- package/bin/2byte-cli.ts +97 -97
- package/package.json +6 -5
- package/src/cli/CreateBotCommand.ts +181 -181
- package/src/cli/GenerateCommand.ts +195 -195
- package/src/cli/InitCommand.ts +107 -107
- package/src/cli/TgAccountManager.ts +50 -0
- package/src/console/migrate.ts +82 -82
- package/src/core/ApiService.ts +20 -20
- package/src/core/ApiServiceManager.ts +63 -63
- package/src/core/App.ts +1143 -1113
- package/src/core/BotArtisan.ts +79 -79
- package/src/core/BotMigration.ts +30 -30
- package/src/core/BotSeeder.ts +66 -66
- package/src/core/Model.ts +84 -84
- package/src/core/utils.ts +2 -2
- package/src/illumination/Artisan.ts +149 -149
- package/src/illumination/InlineKeyboard.ts +61 -61
- package/src/illumination/Message2Byte.ts +255 -255
- package/src/illumination/Message2ByteLiveProgressive.ts +278 -278
- package/src/illumination/Message2bytePool.ts +107 -107
- package/src/illumination/Migration.ts +186 -186
- package/src/illumination/RunSectionRoute.ts +85 -85
- package/src/illumination/Section.ts +410 -410
- package/src/illumination/SectionComponent.ts +64 -64
- package/src/illumination/Telegraf2byteContext.ts +32 -32
- package/src/index.ts +42 -35
- package/src/libs/TelegramAccountControl.ts +1140 -738
- package/src/libs/TgSender.ts +53 -0
- package/src/models/Model.ts +67 -0
- package/src/models/Proxy.ts +218 -0
- package/src/models/TgAccount.ts +362 -0
- package/src/models/index.ts +3 -0
- package/src/types.ts +191 -188
- package/src/user/UserModel.ts +297 -297
- package/src/user/UserStore.ts +119 -119
- package/src/workflow/services/MassSendApiService.ts +80 -80
- package/templates/bot/.env.example +23 -19
- package/templates/bot/artisan.ts +8 -8
- package/templates/bot/bot.ts +82 -79
- package/templates/bot/database/dbConnector.ts +4 -4
- package/templates/bot/database/migrate.ts +9 -9
- package/templates/bot/database/migrations/001_create_users.sql +18 -18
- package/templates/bot/database/migrations/007_proxy.sql +27 -0
- package/templates/bot/database/migrations/008_tg_accounts.sql +32 -0
- package/templates/bot/database/seed.ts +14 -14
- package/templates/bot/docs/CLI_SERVICES.md +536 -0
- package/templates/bot/docs/INPUT_SYSTEM.md +211 -0
- package/templates/bot/docs/SERVICE_EXAMPLES.md +384 -0
- package/templates/bot/docs/TASK_SYSTEM.md +156 -0
- package/templates/bot/models/Model.ts +7 -0
- package/templates/bot/models/index.ts +2 -0
- package/templates/bot/package.json +30 -30
- package/templates/bot/sectionList.ts +9 -9
- package/templates/bot/sections/ExampleInputSection.ts +85 -85
- package/templates/bot/sections/ExampleLiveTaskerSection.ts +60 -60
- package/templates/bot/sections/HomeSection.ts +63 -63
- package/templates/bot/workflow/services/{ExampleServise.ts → ExampleService.ts} +23 -23
|
@@ -1,196 +1,196 @@
|
|
|
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 generateService(name: string): Promise<void> {
|
|
31
|
-
console.log(chalk.blue(`⚙️ Generating service: ${name}`));
|
|
32
|
-
|
|
33
|
-
const currentDir = process.cwd();
|
|
34
|
-
const servicesDir = path.join(currentDir, 'workflow', 'services');
|
|
35
|
-
const serviceName = this.formatServiceName(name);
|
|
36
|
-
const servicePath = path.join(servicesDir, `${serviceName}.ts`);
|
|
37
|
-
|
|
38
|
-
// Ensure services directory exists
|
|
39
|
-
await fs.ensureDir(servicesDir);
|
|
40
|
-
|
|
41
|
-
// Check if service already exists
|
|
42
|
-
if (await fs.pathExists(servicePath)) {
|
|
43
|
-
console.log(chalk.red(`❌ Service ${serviceName} already exists at ${servicePath}`));
|
|
44
|
-
return;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Generate service content
|
|
48
|
-
const template = this.getServiceTemplate(serviceName);
|
|
49
|
-
await fs.writeFile(servicePath, template);
|
|
50
|
-
|
|
51
|
-
console.log(chalk.green(`✅ Created service ${serviceName} at ${servicePath}`));
|
|
52
|
-
console.log(chalk.yellow(`💡 Service will be automatically loaded from workflow/services directory`));
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async generateMigration(name: string): Promise<void> {
|
|
56
|
-
console.log(chalk.blue(`🗃️ Generating migration: ${name}`));
|
|
57
|
-
|
|
58
|
-
const currentDir = process.cwd();
|
|
59
|
-
const migrationsDir = path.join(currentDir, 'database', 'migrations');
|
|
60
|
-
|
|
61
|
-
// Ensure migrations directory exists
|
|
62
|
-
await fs.ensureDir(migrationsDir);
|
|
63
|
-
|
|
64
|
-
// Get next migration ID
|
|
65
|
-
const files = await fs.readdir(migrationsDir);
|
|
66
|
-
const migrationFiles = files
|
|
67
|
-
.filter(file => file.endsWith('.sql'))
|
|
68
|
-
.map(file => parseInt(file.split('_')[0]))
|
|
69
|
-
.filter(id => !isNaN(id));
|
|
70
|
-
|
|
71
|
-
const nextId = (Math.max(0, ...migrationFiles) + 1).toString().padStart(3, '0');
|
|
72
|
-
const fileName = `${nextId}_${name}.sql`;
|
|
73
|
-
const filePath = path.join(migrationsDir, fileName);
|
|
74
|
-
|
|
75
|
-
const template = this.getMigrationTemplate(name);
|
|
76
|
-
await fs.writeFile(filePath, template);
|
|
77
|
-
|
|
78
|
-
console.log(chalk.green(`✅ Created migration: ${fileName}`));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
private formatSectionName(name: string): string {
|
|
82
|
-
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
private formatServiceName(name: string): string {
|
|
86
|
-
// Convert to PascalCase and add "Service" suffix if not present
|
|
87
|
-
const pascalName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
88
|
-
return pascalName.endsWith('Service') ? pascalName : `${pascalName}Service`;
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
private getSectionTemplate(name: string): string {
|
|
92
|
-
return `import { Section } from "@2byte/tgbot-framework";
|
|
93
|
-
import { SectionOptions } from "@2byte/tgbot-framework";
|
|
94
|
-
import { InlineKeyboard } from "@2byte/tgbot-framework";
|
|
95
|
-
|
|
96
|
-
export default class ${name}Section extends Section {
|
|
97
|
-
static command = "${name.toLowerCase()}";
|
|
98
|
-
static description = "${name} section";
|
|
99
|
-
static actionRoutes = {
|
|
100
|
-
"${name}.index": "index",
|
|
101
|
-
};
|
|
102
|
-
|
|
103
|
-
public sectionId = "${name}";
|
|
104
|
-
private mainInlineKeyboard: InlineKeyboard;
|
|
105
|
-
|
|
106
|
-
constructor(options: SectionOptions) {
|
|
107
|
-
super(options);
|
|
108
|
-
|
|
109
|
-
this.mainInlineKeyboard = this.makeInlineKeyboard().addFootFixedButtons(this.btnHome);
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
public async up(): Promise<void> {}
|
|
113
|
-
public async down(): Promise<void> {}
|
|
114
|
-
public async setup(): Promise<void> {}
|
|
115
|
-
public async unsetup(): Promise<void> {}
|
|
116
|
-
|
|
117
|
-
async index() {
|
|
118
|
-
const message = \`
|
|
119
|
-
👋 Welcome to ${name} Section
|
|
120
|
-
\`;
|
|
121
|
-
|
|
122
|
-
await this.message(message)
|
|
123
|
-
.inlineKeyboard(this.mainInlineKeyboard)
|
|
124
|
-
.send();
|
|
125
|
-
}
|
|
126
|
-
}
|
|
127
|
-
`;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
private getServiceTemplate(name: string): string {
|
|
131
|
-
return `import { App } from "@2byte/tgbot-framework";
|
|
132
|
-
import { ApiService } from "@2byte/tgbot-framework";
|
|
133
|
-
|
|
134
|
-
export default class ${name} extends ApiService {
|
|
135
|
-
|
|
136
|
-
constructor(
|
|
137
|
-
protected app: App,
|
|
138
|
-
public name: string = "${name}"
|
|
139
|
-
) {
|
|
140
|
-
super(app, name);
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
/**
|
|
144
|
-
* Setup method called when service is registered
|
|
145
|
-
* Use this for initialization tasks like setting up connections,
|
|
146
|
-
* loading configurations, etc.
|
|
147
|
-
*/
|
|
148
|
-
public async setup(): Promise<void> {
|
|
149
|
-
// TODO: Add setup logic here
|
|
150
|
-
this.app.debugLog(\`[\${this.name}] Service setup completed\`);
|
|
151
|
-
return Promise.resolve();
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Cleanup method called when service is being destroyed
|
|
156
|
-
* Use this for cleanup tasks like closing connections,
|
|
157
|
-
* releasing resources, etc.
|
|
158
|
-
*/
|
|
159
|
-
public async unsetup(): Promise<void> {
|
|
160
|
-
// TODO: Add cleanup logic here
|
|
161
|
-
this.app.debugLog(\`[\${this.name}] Service cleanup completed\`);
|
|
162
|
-
return Promise.resolve();
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Main run method for the service
|
|
167
|
-
* This is where your service's main logic should be implemented
|
|
168
|
-
*/
|
|
169
|
-
public async run(): Promise<void> {
|
|
170
|
-
// TODO: Add your service logic here
|
|
171
|
-
this.app.debugLog(\`[\${this.name}] Service running\`);
|
|
172
|
-
return Promise.resolve();
|
|
173
|
-
}
|
|
174
|
-
|
|
175
|
-
/**
|
|
176
|
-
* Example method - you can add your own methods here
|
|
177
|
-
*/
|
|
178
|
-
// public async exampleMethod(): Promise<void> {
|
|
179
|
-
// // Your custom logic
|
|
180
|
-
// }
|
|
181
|
-
}
|
|
182
|
-
`;
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
private getMigrationTemplate(name: string): string {
|
|
186
|
-
return `-- UP
|
|
187
|
-
CREATE TABLE IF NOT EXISTS ${name} (
|
|
188
|
-
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
189
|
-
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
190
|
-
);
|
|
191
|
-
|
|
192
|
-
-- DOWN
|
|
193
|
-
DROP TABLE IF EXISTS ${name};
|
|
194
|
-
`;
|
|
195
|
-
}
|
|
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 generateService(name: string): Promise<void> {
|
|
31
|
+
console.log(chalk.blue(`⚙️ Generating service: ${name}`));
|
|
32
|
+
|
|
33
|
+
const currentDir = process.cwd();
|
|
34
|
+
const servicesDir = path.join(currentDir, 'workflow', 'services');
|
|
35
|
+
const serviceName = this.formatServiceName(name);
|
|
36
|
+
const servicePath = path.join(servicesDir, `${serviceName}.ts`);
|
|
37
|
+
|
|
38
|
+
// Ensure services directory exists
|
|
39
|
+
await fs.ensureDir(servicesDir);
|
|
40
|
+
|
|
41
|
+
// Check if service already exists
|
|
42
|
+
if (await fs.pathExists(servicePath)) {
|
|
43
|
+
console.log(chalk.red(`❌ Service ${serviceName} already exists at ${servicePath}`));
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Generate service content
|
|
48
|
+
const template = this.getServiceTemplate(serviceName);
|
|
49
|
+
await fs.writeFile(servicePath, template);
|
|
50
|
+
|
|
51
|
+
console.log(chalk.green(`✅ Created service ${serviceName} at ${servicePath}`));
|
|
52
|
+
console.log(chalk.yellow(`💡 Service will be automatically loaded from workflow/services directory`));
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async generateMigration(name: string): Promise<void> {
|
|
56
|
+
console.log(chalk.blue(`🗃️ Generating migration: ${name}`));
|
|
57
|
+
|
|
58
|
+
const currentDir = process.cwd();
|
|
59
|
+
const migrationsDir = path.join(currentDir, 'database', 'migrations');
|
|
60
|
+
|
|
61
|
+
// Ensure migrations directory exists
|
|
62
|
+
await fs.ensureDir(migrationsDir);
|
|
63
|
+
|
|
64
|
+
// Get next migration ID
|
|
65
|
+
const files = await fs.readdir(migrationsDir);
|
|
66
|
+
const migrationFiles = files
|
|
67
|
+
.filter(file => file.endsWith('.sql'))
|
|
68
|
+
.map(file => parseInt(file.split('_')[0]))
|
|
69
|
+
.filter(id => !isNaN(id));
|
|
70
|
+
|
|
71
|
+
const nextId = (Math.max(0, ...migrationFiles) + 1).toString().padStart(3, '0');
|
|
72
|
+
const fileName = `${nextId}_${name}.sql`;
|
|
73
|
+
const filePath = path.join(migrationsDir, fileName);
|
|
74
|
+
|
|
75
|
+
const template = this.getMigrationTemplate(name);
|
|
76
|
+
await fs.writeFile(filePath, template);
|
|
77
|
+
|
|
78
|
+
console.log(chalk.green(`✅ Created migration: ${fileName}`));
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
private formatSectionName(name: string): string {
|
|
82
|
+
return name.charAt(0).toUpperCase() + name.slice(1);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private formatServiceName(name: string): string {
|
|
86
|
+
// Convert to PascalCase and add "Service" suffix if not present
|
|
87
|
+
const pascalName = name.charAt(0).toUpperCase() + name.slice(1);
|
|
88
|
+
return pascalName.endsWith('Service') ? pascalName : `${pascalName}Service`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
private getSectionTemplate(name: string): string {
|
|
92
|
+
return `import { Section } from "@2byte/tgbot-framework";
|
|
93
|
+
import { SectionOptions } from "@2byte/tgbot-framework";
|
|
94
|
+
import { InlineKeyboard } from "@2byte/tgbot-framework";
|
|
95
|
+
|
|
96
|
+
export default class ${name}Section extends Section {
|
|
97
|
+
static command = "${name.toLowerCase()}";
|
|
98
|
+
static description = "${name} section";
|
|
99
|
+
static actionRoutes = {
|
|
100
|
+
"${name}.index": "index",
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
public sectionId = "${name}";
|
|
104
|
+
private mainInlineKeyboard: InlineKeyboard;
|
|
105
|
+
|
|
106
|
+
constructor(options: SectionOptions) {
|
|
107
|
+
super(options);
|
|
108
|
+
|
|
109
|
+
this.mainInlineKeyboard = this.makeInlineKeyboard().addFootFixedButtons(this.btnHome);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
public async up(): Promise<void> {}
|
|
113
|
+
public async down(): Promise<void> {}
|
|
114
|
+
public async setup(): Promise<void> {}
|
|
115
|
+
public async unsetup(): Promise<void> {}
|
|
116
|
+
|
|
117
|
+
async index() {
|
|
118
|
+
const message = \`
|
|
119
|
+
👋 Welcome to ${name} Section
|
|
120
|
+
\`;
|
|
121
|
+
|
|
122
|
+
await this.message(message)
|
|
123
|
+
.inlineKeyboard(this.mainInlineKeyboard)
|
|
124
|
+
.send();
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
`;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private getServiceTemplate(name: string): string {
|
|
131
|
+
return `import { App } from "@2byte/tgbot-framework";
|
|
132
|
+
import { ApiService } from "@2byte/tgbot-framework";
|
|
133
|
+
|
|
134
|
+
export default class ${name} extends ApiService {
|
|
135
|
+
|
|
136
|
+
constructor(
|
|
137
|
+
protected app: App,
|
|
138
|
+
public name: string = "${name}"
|
|
139
|
+
) {
|
|
140
|
+
super(app, name);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
/**
|
|
144
|
+
* Setup method called when service is registered
|
|
145
|
+
* Use this for initialization tasks like setting up connections,
|
|
146
|
+
* loading configurations, etc.
|
|
147
|
+
*/
|
|
148
|
+
public async setup(): Promise<void> {
|
|
149
|
+
// TODO: Add setup logic here
|
|
150
|
+
this.app.debugLog(\`[\${this.name}] Service setup completed\`);
|
|
151
|
+
return Promise.resolve();
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Cleanup method called when service is being destroyed
|
|
156
|
+
* Use this for cleanup tasks like closing connections,
|
|
157
|
+
* releasing resources, etc.
|
|
158
|
+
*/
|
|
159
|
+
public async unsetup(): Promise<void> {
|
|
160
|
+
// TODO: Add cleanup logic here
|
|
161
|
+
this.app.debugLog(\`[\${this.name}] Service cleanup completed\`);
|
|
162
|
+
return Promise.resolve();
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Main run method for the service
|
|
167
|
+
* This is where your service's main logic should be implemented
|
|
168
|
+
*/
|
|
169
|
+
public async run(): Promise<void> {
|
|
170
|
+
// TODO: Add your service logic here
|
|
171
|
+
this.app.debugLog(\`[\${this.name}] Service running\`);
|
|
172
|
+
return Promise.resolve();
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Example method - you can add your own methods here
|
|
177
|
+
*/
|
|
178
|
+
// public async exampleMethod(): Promise<void> {
|
|
179
|
+
// // Your custom logic
|
|
180
|
+
// }
|
|
181
|
+
}
|
|
182
|
+
`;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
private getMigrationTemplate(name: string): string {
|
|
186
|
+
return `-- UP
|
|
187
|
+
CREATE TABLE IF NOT EXISTS ${name} (
|
|
188
|
+
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
189
|
+
created_at DATETIME DEFAULT CURRENT_TIMESTAMP
|
|
190
|
+
);
|
|
191
|
+
|
|
192
|
+
-- DOWN
|
|
193
|
+
DROP TABLE IF EXISTS ${name};
|
|
194
|
+
`;
|
|
195
|
+
}
|
|
196
196
|
}
|
package/src/cli/InitCommand.ts
CHANGED
|
@@ -1,108 +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
|
-
}
|
|
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
108
|
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import {
|
|
2
|
+
TelegramManagerCredentialsDB,
|
|
3
|
+
TelegramAccountRemote,
|
|
4
|
+
} from "../libs/TelegramAccountControl";
|
|
5
|
+
import { Model } from "../models/Model";
|
|
6
|
+
import Input from "input";
|
|
7
|
+
|
|
8
|
+
export const manualAdderTgAccount = async () => {
|
|
9
|
+
const credentialsManager = new TelegramManagerCredentialsDB(Model.getConnection());
|
|
10
|
+
|
|
11
|
+
const tgAccountControl = TelegramAccountRemote.init({
|
|
12
|
+
appId: process.env.TG_APP_ID!,
|
|
13
|
+
appHash: process.env.TG_APP_HASH!,
|
|
14
|
+
credetialsManager: credentialsManager,
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
const phone = await Input.text("Введите номер телефона (с кодом страны, например, 79614416445):");
|
|
18
|
+
|
|
19
|
+
await credentialsManager.addCredential({
|
|
20
|
+
phone,
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
const credentials = await credentialsManager.getCredential(
|
|
24
|
+
phone
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (!credentials) {
|
|
28
|
+
console.log("Учётная запись с таким номером телефона не найдена.");
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
await tgAccountControl.login(
|
|
33
|
+
credentials,
|
|
34
|
+
async () => {
|
|
35
|
+
console.log("Требуется код подтверждения");
|
|
36
|
+
return await Input.text("Введите код подтверждения:");
|
|
37
|
+
},
|
|
38
|
+
async () => {
|
|
39
|
+
console.log("Требуется пароль");
|
|
40
|
+
return await Input.password("Введите пароль:");
|
|
41
|
+
},
|
|
42
|
+
async (err: any) => {
|
|
43
|
+
console.log("Ошибка логина:", err);
|
|
44
|
+
tgAccountControl.disconnect();
|
|
45
|
+
throw new Error(err);
|
|
46
|
+
}
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
tgAccountControl.disconnect();
|
|
50
|
+
};
|