@2byte/tgbot-framework 1.0.3 → 1.0.4
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
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
# Улучшенная система ввода пользователя
|
|
2
|
+
|
|
3
|
+
## Обзор
|
|
4
|
+
|
|
5
|
+
Система ввода была значительно улучшена для поддержки:
|
|
6
|
+
- Возможности отмены ввода пользователем
|
|
7
|
+
- Настраиваемых опций отмены при создании запроса
|
|
8
|
+
- Сохранения состояния ожидания при неверном вводе
|
|
9
|
+
- Расширенной валидации, включая файлы
|
|
10
|
+
- Гибкой системы валидации для различных типов ботов
|
|
11
|
+
|
|
12
|
+
## Основные возможности
|
|
13
|
+
|
|
14
|
+
### 1. Настройка отмены ввода
|
|
15
|
+
|
|
16
|
+
При создании запроса на ввод можно настроить возможность отмены:
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// Разрешить отмену (по умолчанию)
|
|
20
|
+
.requestInput("phone", {
|
|
21
|
+
validator: "phone",
|
|
22
|
+
allowCancel: true, // по умолчанию true
|
|
23
|
+
cancelButtonText: "Отмена", // текст кнопки
|
|
24
|
+
cancelAction: "home.index[cancel_wait=1]", // действие при отмене
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
// Запретить отмену (обязательный ввод)
|
|
28
|
+
.requestInputWithAwait("password", {
|
|
29
|
+
allowCancel: false, // отмена запрещена
|
|
30
|
+
errorMessage: "Пароль обязателен для ввода"
|
|
31
|
+
})
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### 2. Сохранение состояния при ошибках
|
|
35
|
+
|
|
36
|
+
При неверном вводе пользователь остается в режиме ожидания:
|
|
37
|
+
|
|
38
|
+
```typescript
|
|
39
|
+
.requestInput("code", {
|
|
40
|
+
validator: "code",
|
|
41
|
+
errorMessage: "Неверный формат кода. Введите 5-6 цифр",
|
|
42
|
+
allowCancel: true
|
|
43
|
+
})
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Система будет:
|
|
47
|
+
- Показывать сообщение об ошибке
|
|
48
|
+
- Увеличивать счетчик попыток
|
|
49
|
+
- Сохранять состояние ожидания
|
|
50
|
+
- Позволять отмену только если `allowCancel: true`
|
|
51
|
+
|
|
52
|
+
### 3. Валидация файлов
|
|
53
|
+
|
|
54
|
+
Поддержка загрузки и валидации файлов:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
.requestInput("uploadedFile", {
|
|
58
|
+
validator: "file",
|
|
59
|
+
errorMessage: "Неподдерживаемый формат файла",
|
|
60
|
+
fileValidation: {
|
|
61
|
+
allowedTypes: ['image/jpeg', 'image/png', 'application/pdf'],
|
|
62
|
+
maxSize: 10 * 1024 * 1024, // 10 МБ
|
|
63
|
+
minSize: 1024, // 1 КБ
|
|
64
|
+
},
|
|
65
|
+
allowCancel: true
|
|
66
|
+
})
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 4. Кастомная валидация
|
|
70
|
+
|
|
71
|
+
Поддержка асинхронных валидаторов:
|
|
72
|
+
|
|
73
|
+
```typescript
|
|
74
|
+
.requestInput("customData", {
|
|
75
|
+
validator: async (value) => {
|
|
76
|
+
// Асинхронная проверка
|
|
77
|
+
const isValid = await checkInDatabase(value);
|
|
78
|
+
return isValid;
|
|
79
|
+
},
|
|
80
|
+
errorMessage: "Данные не прошли проверку"
|
|
81
|
+
})
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
## Типы валидаторов
|
|
85
|
+
|
|
86
|
+
### Встроенные валидаторы:
|
|
87
|
+
- `"number"` - числовые значения
|
|
88
|
+
- `"phone"` - российские номера телефонов (79xxxxxxxxx)
|
|
89
|
+
- `"code"` - коды подтверждения (5-6 цифр)
|
|
90
|
+
- `"file"` - файлы (с дополнительными опциями)
|
|
91
|
+
|
|
92
|
+
### Кастомные валидаторы:
|
|
93
|
+
```typescript
|
|
94
|
+
// Синхронный валидатор
|
|
95
|
+
validator: (value: string) => value.length > 5
|
|
96
|
+
|
|
97
|
+
// Асинхронный валидатор
|
|
98
|
+
validator: async (value: string) => {
|
|
99
|
+
const result = await apiCall(value);
|
|
100
|
+
return result.isValid;
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
## Примеры использования
|
|
105
|
+
|
|
106
|
+
### Базовый ввод с отменой
|
|
107
|
+
```typescript
|
|
108
|
+
async startRegistration() {
|
|
109
|
+
await this.message("Введите ваш номер телефона:")
|
|
110
|
+
.requestInput("phone", {
|
|
111
|
+
validator: "phone",
|
|
112
|
+
errorMessage: "Неверный формат. Используйте: 79000000000",
|
|
113
|
+
allowCancel: true,
|
|
114
|
+
cancelButtonText: "Отмена",
|
|
115
|
+
cancelAction: "home.index[cancel_wait=1]",
|
|
116
|
+
runSection: new RunSectionRoute().section("home").method("processPhone")
|
|
117
|
+
})
|
|
118
|
+
.send();
|
|
119
|
+
}
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Обязательный ввод без отмены
|
|
123
|
+
```typescript
|
|
124
|
+
async requestPassword() {
|
|
125
|
+
return await this.message("Введите пароль:")
|
|
126
|
+
.requestInputWithAwait("password", {
|
|
127
|
+
errorMessage: "Пароль не может быть пустым",
|
|
128
|
+
allowCancel: false // отмена запрещена
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Загрузка файлов с валидацией
|
|
134
|
+
```typescript
|
|
135
|
+
async uploadDocument() {
|
|
136
|
+
await this.message("Отправьте документ (PDF, до 5 МБ):")
|
|
137
|
+
.requestInput("document", {
|
|
138
|
+
validator: "file",
|
|
139
|
+
fileValidation: {
|
|
140
|
+
allowedTypes: ['application/pdf'],
|
|
141
|
+
maxSize: 5 * 1024 * 1024,
|
|
142
|
+
minSize: 1024
|
|
143
|
+
},
|
|
144
|
+
errorMessage: "Только PDF файлы до 5 МБ",
|
|
145
|
+
allowCancel: true,
|
|
146
|
+
runSection: new RunSectionRoute().section("home").method("processDocument")
|
|
147
|
+
})
|
|
148
|
+
.send();
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Кастомная валидация с API
|
|
153
|
+
```typescript
|
|
154
|
+
async requestUsername() {
|
|
155
|
+
await this.message("Введите желаемое имя пользователя:")
|
|
156
|
+
.requestInput("username", {
|
|
157
|
+
validator: async (username: string) => {
|
|
158
|
+
// Проверяем доступность имени через API
|
|
159
|
+
const response = await fetch(`/api/check-username/${username}`);
|
|
160
|
+
const data = await response.json();
|
|
161
|
+
return data.available;
|
|
162
|
+
},
|
|
163
|
+
errorMessage: "Это имя уже занято. Попробуйте другое.",
|
|
164
|
+
allowCancel: true,
|
|
165
|
+
runSection: new RunSectionRoute().section("user").method("processUsername")
|
|
166
|
+
})
|
|
167
|
+
.send();
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
## Обработка отмены
|
|
172
|
+
|
|
173
|
+
Отмена обрабатывается автоматически через параметр `cancel_wait`:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
// В конструкторе Section автоматически вызывается:
|
|
177
|
+
this.cancelUserWaitingReply();
|
|
178
|
+
|
|
179
|
+
// Который проверяет параметр cancel_wait и очищает состояние ожидания
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
## Миграция с старой системы
|
|
183
|
+
|
|
184
|
+
### Было:
|
|
185
|
+
```typescript
|
|
186
|
+
.requestInput("phone", {
|
|
187
|
+
validator: "phone",
|
|
188
|
+
errorMessage: "Неверный номер"
|
|
189
|
+
})
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
### Стало:
|
|
193
|
+
```typescript
|
|
194
|
+
.requestInput("phone", {
|
|
195
|
+
validator: "phone",
|
|
196
|
+
errorMessage: "Неверный номер",
|
|
197
|
+
allowCancel: true, // новый параметр
|
|
198
|
+
cancelButtonText: "Отмена", // настройка текста кнопки
|
|
199
|
+
cancelAction: "home.index[cancel_wait=1]" // действие при отмене
|
|
200
|
+
})
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Старый код будет работать с настройками по умолчанию (отмена разрешена).
|
|
204
|
+
|
|
205
|
+
## Лучшие практики
|
|
206
|
+
|
|
207
|
+
1. **Всегда предоставляйте возможность отмены** для необязательных действий
|
|
208
|
+
2. **Используйте понятные сообщения об ошибках** с указанием формата
|
|
209
|
+
3. **Ограничивайте размеры файлов** для предотвращения злоупотреблений
|
|
210
|
+
4. **Используйте асинхронную валидацию** для проверок через API
|
|
211
|
+
5. **Тестируйте различные сценарии** включая отмену и повторные попытки
|
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
# Примеры использования Service Generator
|
|
2
|
+
|
|
3
|
+
## Тестовые сценарии
|
|
4
|
+
|
|
5
|
+
### Сценарий 1: Создание простого сервиса
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
cd your-bot-directory
|
|
9
|
+
2byte generate service Payment
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
**Ожидаемый результат:**
|
|
13
|
+
- Создан файл `workflow/services/PaymentService.ts`
|
|
14
|
+
- Выведено: `✅ Created service PaymentService at workflow/services/PaymentService.ts`
|
|
15
|
+
|
|
16
|
+
### Сценарий 2: Создание с полным именем
|
|
17
|
+
|
|
18
|
+
```bash
|
|
19
|
+
2byte g service NotificationService
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
**Ожидаемый результат:**
|
|
23
|
+
- Создан файл `workflow/services/NotificationService.ts`
|
|
24
|
+
- Суффикс Service не дублируется
|
|
25
|
+
|
|
26
|
+
### Сценарий 3: Попытка создать существующий сервис
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
2byte g service Payment # Второй раз
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
**Ожидаемый результат:**
|
|
33
|
+
- Ошибка: `❌ Service PaymentService already exists`
|
|
34
|
+
- Файл не перезаписывается
|
|
35
|
+
|
|
36
|
+
### Сценарий 4: Создание нескольких сервисов
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
2byte g service Cache
|
|
40
|
+
2byte g service Logger
|
|
41
|
+
2byte g service EmailSender
|
|
42
|
+
2byte g service DatabaseConnector
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
**Структура после создания:**
|
|
46
|
+
```
|
|
47
|
+
workflow/
|
|
48
|
+
└── services/
|
|
49
|
+
├── CacheService.ts
|
|
50
|
+
├── LoggerService.ts
|
|
51
|
+
├── EmailSenderService.ts
|
|
52
|
+
└── DatabaseConnectorService.ts
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Проверка работы созданного сервиса
|
|
56
|
+
|
|
57
|
+
### 1. Создайте тестовый сервис
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
2byte g service Test
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
### 2. Измените сервис
|
|
64
|
+
|
|
65
|
+
Откройте `workflow/services/TestService.ts` и добавьте логику:
|
|
66
|
+
|
|
67
|
+
```typescript
|
|
68
|
+
import { App } from "@2byte/tgbot-framework";
|
|
69
|
+
import { ApiService } from "@2byte/tgbot-framework";
|
|
70
|
+
|
|
71
|
+
export default class TestService extends ApiService {
|
|
72
|
+
|
|
73
|
+
constructor(
|
|
74
|
+
protected app: App,
|
|
75
|
+
public name: string = "TestService"
|
|
76
|
+
) {
|
|
77
|
+
super(app, name);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
public async setup(): Promise<void> {
|
|
81
|
+
this.app.debugLog(`[${this.name}] Setting up test service`);
|
|
82
|
+
return Promise.resolve();
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
public async unsetup(): Promise<void> {
|
|
86
|
+
this.app.debugLog(`[${this.name}] Cleaning up test service`);
|
|
87
|
+
return Promise.resolve();
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
public async run(): Promise<void> {
|
|
91
|
+
this.app.debugLog(`[${this.name}] Test service is running!`);
|
|
92
|
+
|
|
93
|
+
// Тестовая задача каждые 10 секунд
|
|
94
|
+
setInterval(() => {
|
|
95
|
+
this.app.debugLog(`[${this.name}] Heartbeat - ${new Date().toISOString()}`);
|
|
96
|
+
}, 10000);
|
|
97
|
+
|
|
98
|
+
return Promise.resolve();
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// Кастомный метод для тестирования
|
|
102
|
+
public getStatus(): string {
|
|
103
|
+
return `TestService is active at ${new Date().toISOString()}`;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### 3. Запустите бота
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
bun run index.ts
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Ожидаемые логи:**
|
|
115
|
+
```
|
|
116
|
+
[App] Registered API services: [ 'TestService', ... ]
|
|
117
|
+
[TestService] Setting up test service
|
|
118
|
+
[TestService] Service setup completed
|
|
119
|
+
[TestService] Test service is running!
|
|
120
|
+
[TestService] Heartbeat - 2025-10-29T...
|
|
121
|
+
[TestService] Heartbeat - 2025-10-29T...
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### 4. Используйте сервис в секции
|
|
125
|
+
|
|
126
|
+
Создайте или откройте секцию:
|
|
127
|
+
|
|
128
|
+
```typescript
|
|
129
|
+
import { Section } from "@2byte/tgbot-framework";
|
|
130
|
+
|
|
131
|
+
export default class HomeSection extends Section {
|
|
132
|
+
async index() {
|
|
133
|
+
// Получаем наш тестовый сервис
|
|
134
|
+
const testService = this.app.getService('TestService');
|
|
135
|
+
|
|
136
|
+
if (testService) {
|
|
137
|
+
const status = testService.getStatus();
|
|
138
|
+
|
|
139
|
+
await this.message(`🤖 Bot Status\n\n${status}`)
|
|
140
|
+
.send();
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
## Интеграционные тесты
|
|
147
|
+
|
|
148
|
+
### Test 1: Автозагрузка сервиса
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# Создать сервис
|
|
152
|
+
2byte g service AutoLoad
|
|
153
|
+
|
|
154
|
+
# Запустить бота
|
|
155
|
+
bun run index.ts
|
|
156
|
+
|
|
157
|
+
# Проверить логи
|
|
158
|
+
# Должно быть: [App] Registered API services: [ 'AutoLoadService', ... ]
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
### Test 2: Использование в секциях
|
|
162
|
+
|
|
163
|
+
```typescript
|
|
164
|
+
// В любой секции
|
|
165
|
+
const service = this.app.getService('AutoLoadService');
|
|
166
|
+
console.log('Service loaded:', service ? 'YES' : 'NO');
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
### Test 3: Жизненный цикл
|
|
170
|
+
|
|
171
|
+
```bash
|
|
172
|
+
# Запустить бота
|
|
173
|
+
bun run index.ts
|
|
174
|
+
# Лог: [ServiceName] Service setup completed
|
|
175
|
+
|
|
176
|
+
# Остановить бота (Ctrl+C)
|
|
177
|
+
# Лог: [ServiceName] Service cleanup completed
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## Примеры реальных сервисов
|
|
181
|
+
|
|
182
|
+
### Email Service
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
2byte g service Email
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// workflow/services/EmailService.ts
|
|
190
|
+
import { App, ApiService } from "@2byte/tgbot-framework";
|
|
191
|
+
import nodemailer from 'nodemailer';
|
|
192
|
+
|
|
193
|
+
export default class EmailService extends ApiService {
|
|
194
|
+
private transporter: any;
|
|
195
|
+
|
|
196
|
+
constructor(protected app: App, public name: string = "EmailService") {
|
|
197
|
+
super(app, name);
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
public async setup(): Promise<void> {
|
|
201
|
+
this.transporter = nodemailer.createTransport({
|
|
202
|
+
host: process.env.SMTP_HOST,
|
|
203
|
+
port: parseInt(process.env.SMTP_PORT || '587'),
|
|
204
|
+
auth: {
|
|
205
|
+
user: process.env.SMTP_USER,
|
|
206
|
+
pass: process.env.SMTP_PASS
|
|
207
|
+
}
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
this.app.debugLog(`[${this.name}] Email service ready`);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
public async sendEmail(to: string, subject: string, text: string) {
|
|
214
|
+
const info = await this.transporter.sendMail({
|
|
215
|
+
from: process.env.EMAIL_FROM,
|
|
216
|
+
to,
|
|
217
|
+
subject,
|
|
218
|
+
text
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
this.app.debugLog(`[${this.name}] Email sent: ${info.messageId}`);
|
|
222
|
+
return info;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Database Service
|
|
228
|
+
|
|
229
|
+
```bash
|
|
230
|
+
2byte g service Database
|
|
231
|
+
```
|
|
232
|
+
|
|
233
|
+
```typescript
|
|
234
|
+
// workflow/services/DatabaseService.ts
|
|
235
|
+
import { App, ApiService } from "@2byte/tgbot-framework";
|
|
236
|
+
import { Database } from 'sqlite3';
|
|
237
|
+
|
|
238
|
+
export default class DatabaseService extends ApiService {
|
|
239
|
+
private db: Database | null = null;
|
|
240
|
+
|
|
241
|
+
constructor(protected app: App, public name: string = "DatabaseService") {
|
|
242
|
+
super(app, name);
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
public async setup(): Promise<void> {
|
|
246
|
+
return new Promise((resolve, reject) => {
|
|
247
|
+
this.db = new Database('./database.db', (err) => {
|
|
248
|
+
if (err) {
|
|
249
|
+
this.app.debugLog(`[${this.name}] DB connection failed`, err);
|
|
250
|
+
reject(err);
|
|
251
|
+
} else {
|
|
252
|
+
this.app.debugLog(`[${this.name}] DB connected`);
|
|
253
|
+
resolve();
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
});
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
public async unsetup(): Promise<void> {
|
|
260
|
+
return new Promise((resolve) => {
|
|
261
|
+
this.db?.close(() => {
|
|
262
|
+
this.app.debugLog(`[${this.name}] DB connection closed`);
|
|
263
|
+
resolve();
|
|
264
|
+
});
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
public async query(sql: string, params: any[] = []): Promise<any> {
|
|
269
|
+
return new Promise((resolve, reject) => {
|
|
270
|
+
this.db?.all(sql, params, (err, rows) => {
|
|
271
|
+
if (err) reject(err);
|
|
272
|
+
else resolve(rows);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### Redis Cache Service
|
|
280
|
+
|
|
281
|
+
```bash
|
|
282
|
+
2byte g service RedisCache
|
|
283
|
+
```
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// workflow/services/RedisCacheService.ts
|
|
287
|
+
import { App, ApiService } from "@2byte/tgbot-framework";
|
|
288
|
+
import Redis from 'ioredis';
|
|
289
|
+
|
|
290
|
+
export default class RedisCacheService extends ApiService {
|
|
291
|
+
private redis: Redis | null = null;
|
|
292
|
+
|
|
293
|
+
constructor(protected app: App, public name: string = "RedisCacheService") {
|
|
294
|
+
super(app, name);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
public async setup(): Promise<void> {
|
|
298
|
+
this.redis = new Redis({
|
|
299
|
+
host: process.env.REDIS_HOST || 'localhost',
|
|
300
|
+
port: parseInt(process.env.REDIS_PORT || '6379'),
|
|
301
|
+
password: process.env.REDIS_PASSWORD
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
this.redis.on('connect', () => {
|
|
305
|
+
this.app.debugLog(`[${this.name}] Redis connected`);
|
|
306
|
+
});
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
public async unsetup(): Promise<void> {
|
|
310
|
+
await this.redis?.quit();
|
|
311
|
+
this.app.debugLog(`[${this.name}] Redis disconnected`);
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
public async get(key: string): Promise<string | null> {
|
|
315
|
+
return this.redis?.get(key) || null;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
public async set(key: string, value: string, ttl?: number): Promise<void> {
|
|
319
|
+
if (ttl) {
|
|
320
|
+
await this.redis?.setex(key, ttl, value);
|
|
321
|
+
} else {
|
|
322
|
+
await this.redis?.set(key, value);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
public async del(key: string): Promise<void> {
|
|
327
|
+
await this.redis?.del(key);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
## Troubleshooting Examples
|
|
333
|
+
|
|
334
|
+
### Проблема: Сервис не создается
|
|
335
|
+
|
|
336
|
+
```bash
|
|
337
|
+
$ 2byte g service Test
|
|
338
|
+
Error: EACCES: permission denied
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Решение:**
|
|
342
|
+
```bash
|
|
343
|
+
# Проверьте права доступа
|
|
344
|
+
ls -la workflow/services/
|
|
345
|
+
|
|
346
|
+
# Создайте директорию вручную если нужно
|
|
347
|
+
mkdir -p workflow/services
|
|
348
|
+
chmod 755 workflow/services
|
|
349
|
+
```
|
|
350
|
+
|
|
351
|
+
### Проблема: Сервис создан но не загружается
|
|
352
|
+
|
|
353
|
+
**Проверка:**
|
|
354
|
+
```bash
|
|
355
|
+
# Запустите бота с debug
|
|
356
|
+
DEBUG=* bun run index.ts
|
|
357
|
+
|
|
358
|
+
# Проверьте структуру файла
|
|
359
|
+
cat workflow/services/TestService.ts
|
|
360
|
+
```
|
|
361
|
+
|
|
362
|
+
**Типичные ошибки:**
|
|
363
|
+
- Файл не экспортирует класс как default
|
|
364
|
+
- Класс не наследуется от ApiService
|
|
365
|
+
- Синтаксическая ошибка в коде
|
|
366
|
+
|
|
367
|
+
## Performance Tests
|
|
368
|
+
|
|
369
|
+
### Тест загрузки множества сервисов
|
|
370
|
+
|
|
371
|
+
```bash
|
|
372
|
+
# Создать 10 сервисов
|
|
373
|
+
for i in {1..10}; do
|
|
374
|
+
2byte g service Service$i
|
|
375
|
+
done
|
|
376
|
+
|
|
377
|
+
# Запустить и проверить время загрузки
|
|
378
|
+
time bun run index.ts
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
**Ожидаемый результат:**
|
|
382
|
+
- Все 10 сервисов загружены
|
|
383
|
+
- Время загрузки < 1 секунды
|
|
384
|
+
- Нет ошибок в логах
|