@2byte/tgbot-framework 1.0.6 → 1.0.8
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 +55 -55
- 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 -50
- 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 +1157 -1143
- 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 -42
- package/src/libs/TelegramAccountControl.ts +1140 -1140
- package/src/libs/TgSender.ts +53 -53
- package/src/models/Model.ts +67 -67
- package/src/models/Proxy.ts +217 -217
- package/src/models/TgAccount.ts +362 -362
- package/src/models/index.ts +2 -2
- package/src/types.ts +191 -191
- package/src/user/UserModel.ts +297 -297
- package/src/user/UserStore.ts +119 -119
- package/src/workflow/services/MassSendApiService.ts +83 -80
- package/templates/bot/.env.example +33 -33
- package/templates/bot/artisan.ts +8 -8
- package/templates/bot/bot.ts +82 -82
- 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 -27
- package/templates/bot/database/migrations/008_tg_accounts.sql +32 -32
- package/templates/bot/database/seed.ts +14 -14
- package/templates/bot/docs/CLI_SERVICES.md +536 -536
- package/templates/bot/docs/INPUT_SYSTEM.md +211 -211
- package/templates/bot/docs/MASS_SEND_SERVICE.md +327 -0
- package/templates/bot/docs/SERVICE_EXAMPLES.md +384 -384
- package/templates/bot/docs/TASK_SYSTEM.md +156 -156
- package/templates/bot/models/Model.ts +7 -7
- package/templates/bot/models/index.ts +1 -1
- 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/ExampleService.ts +23 -23
|
@@ -1,384 +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
|
-
- Нет ошибок в логах
|
|
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
|
+
- Нет ошибок в логах
|