@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.
Files changed (58) hide show
  1. package/README.md +300 -300
  2. package/bin/2byte-cli.ts +97 -97
  3. package/package.json +6 -5
  4. package/src/cli/CreateBotCommand.ts +181 -181
  5. package/src/cli/GenerateCommand.ts +195 -195
  6. package/src/cli/InitCommand.ts +107 -107
  7. package/src/cli/TgAccountManager.ts +50 -0
  8. package/src/console/migrate.ts +82 -82
  9. package/src/core/ApiService.ts +20 -20
  10. package/src/core/ApiServiceManager.ts +63 -63
  11. package/src/core/App.ts +1143 -1113
  12. package/src/core/BotArtisan.ts +79 -79
  13. package/src/core/BotMigration.ts +30 -30
  14. package/src/core/BotSeeder.ts +66 -66
  15. package/src/core/Model.ts +84 -84
  16. package/src/core/utils.ts +2 -2
  17. package/src/illumination/Artisan.ts +149 -149
  18. package/src/illumination/InlineKeyboard.ts +61 -61
  19. package/src/illumination/Message2Byte.ts +255 -255
  20. package/src/illumination/Message2ByteLiveProgressive.ts +278 -278
  21. package/src/illumination/Message2bytePool.ts +107 -107
  22. package/src/illumination/Migration.ts +186 -186
  23. package/src/illumination/RunSectionRoute.ts +85 -85
  24. package/src/illumination/Section.ts +410 -410
  25. package/src/illumination/SectionComponent.ts +64 -64
  26. package/src/illumination/Telegraf2byteContext.ts +32 -32
  27. package/src/index.ts +42 -35
  28. package/src/libs/TelegramAccountControl.ts +1140 -738
  29. package/src/libs/TgSender.ts +53 -0
  30. package/src/models/Model.ts +67 -0
  31. package/src/models/Proxy.ts +218 -0
  32. package/src/models/TgAccount.ts +362 -0
  33. package/src/models/index.ts +3 -0
  34. package/src/types.ts +191 -188
  35. package/src/user/UserModel.ts +297 -297
  36. package/src/user/UserStore.ts +119 -119
  37. package/src/workflow/services/MassSendApiService.ts +80 -80
  38. package/templates/bot/.env.example +23 -19
  39. package/templates/bot/artisan.ts +8 -8
  40. package/templates/bot/bot.ts +82 -79
  41. package/templates/bot/database/dbConnector.ts +4 -4
  42. package/templates/bot/database/migrate.ts +9 -9
  43. package/templates/bot/database/migrations/001_create_users.sql +18 -18
  44. package/templates/bot/database/migrations/007_proxy.sql +27 -0
  45. package/templates/bot/database/migrations/008_tg_accounts.sql +32 -0
  46. package/templates/bot/database/seed.ts +14 -14
  47. package/templates/bot/docs/CLI_SERVICES.md +536 -0
  48. package/templates/bot/docs/INPUT_SYSTEM.md +211 -0
  49. package/templates/bot/docs/SERVICE_EXAMPLES.md +384 -0
  50. package/templates/bot/docs/TASK_SYSTEM.md +156 -0
  51. package/templates/bot/models/Model.ts +7 -0
  52. package/templates/bot/models/index.ts +2 -0
  53. package/templates/bot/package.json +30 -30
  54. package/templates/bot/sectionList.ts +9 -9
  55. package/templates/bot/sections/ExampleInputSection.ts +85 -85
  56. package/templates/bot/sections/ExampleLiveTaskerSection.ts +60 -60
  57. package/templates/bot/sections/HomeSection.ts +63 -63
  58. package/templates/bot/workflow/services/{ExampleServise.ts → ExampleService.ts} +23 -23
@@ -1,19 +1,19 @@
1
- -- UP
2
- CREATE TABLE IF NOT EXISTS `users` (
3
- `id` INTEGER PRIMARY KEY AUTOINCREMENT,
4
- `user_refid` INTEGER DEFAULT 0,
5
- `role` TEXT DEFAULT 'user',
6
- `tg_id` INTEGER NOT NULL UNIQUE,
7
- `tg_username` TEXT,
8
- `tg_full_name` TEXT GENERATED ALWAYS AS (trim(coalesce(tg_first_name, '') || ' ' || coalesce(tg_last_name, ''))) VIRTUAL,
9
- `tg_first_name` TEXT,
10
- `tg_last_name` TEXT,
11
- `is_banned_by_user` INTEGER DEFAULT 0,
12
- `is_banned_by_admin` INTEGER DEFAULT 0,
13
- `banned_reason` TEXT,
14
- `language` TEXT DEFAULT 'en',
15
- `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
16
- `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
17
- );
18
- -- DOWN
1
+ -- UP
2
+ CREATE TABLE IF NOT EXISTS `users` (
3
+ `id` INTEGER PRIMARY KEY AUTOINCREMENT,
4
+ `user_refid` INTEGER DEFAULT 0,
5
+ `role` TEXT DEFAULT 'user',
6
+ `tg_id` INTEGER NOT NULL UNIQUE,
7
+ `tg_username` TEXT,
8
+ `tg_full_name` TEXT GENERATED ALWAYS AS (trim(coalesce(tg_first_name, '') || ' ' || coalesce(tg_last_name, ''))) VIRTUAL,
9
+ `tg_first_name` TEXT,
10
+ `tg_last_name` TEXT,
11
+ `is_banned_by_user` INTEGER DEFAULT 0,
12
+ `is_banned_by_admin` INTEGER DEFAULT 0,
13
+ `banned_reason` TEXT,
14
+ `language` TEXT DEFAULT 'en',
15
+ `updated_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
16
+ `created_at` TIMESTAMP DEFAULT CURRENT_TIMESTAMP
17
+ );
18
+ -- DOWN
19
19
  DROP TABLE IF EXISTS `users`;
@@ -0,0 +1,27 @@
1
+ -- UP
2
+ CREATE TABLE IF NOT EXISTS proxy (
3
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
4
+ ip TEXT NOT NULL,
5
+ port INTEGER NOT NULL,
6
+ username TEXT,
7
+ password TEXT,
8
+ secret TEXT,
9
+ socksType INTEGER CHECK(socksType IN (4, 5)),
10
+ MTProxy INTEGER DEFAULT 0,
11
+ country TEXT,
12
+ status INTEGER DEFAULT 0,
13
+ source TEXT,
14
+ last_check DATETIME,
15
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
16
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
17
+ );
18
+
19
+ CREATE TRIGGER IF NOT EXISTS proxy_updated_at
20
+ AFTER UPDATE ON proxy
21
+ FOR EACH ROW
22
+ BEGIN
23
+ UPDATE proxy SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
24
+ END;
25
+
26
+ -- DOWN
27
+ DROP TABLE IF EXISTS proxy;
@@ -0,0 +1,32 @@
1
+ -- UP
2
+ CREATE TABLE IF NOT EXISTS tg_accounts (
3
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
4
+ phone TEXT NOT NULL UNIQUE,
5
+ session TEXT NULL,
6
+ password TEXT NULL,
7
+ country TEXT NULL,
8
+ proxy_id INTEGER NULL,
9
+ status INTEGER DEFAULT 0,
10
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
11
+ updated_at DATETIME DEFAULT CURRENT_TIMESTAMP
12
+ );
13
+
14
+ -- Создаем индекс для быстрого поиска по номеру телефона
15
+ CREATE INDEX IF NOT EXISTS idx_accounts_phone ON accounts(phone);
16
+
17
+ -- Создаем индекс для фильтрации по статусу
18
+ CREATE INDEX IF NOT EXISTS idx_accounts_status ON accounts(status);
19
+
20
+ -- Создаем индекс для фильтрации по стране
21
+ CREATE INDEX IF NOT EXISTS idx_accounts_country ON accounts(country);
22
+
23
+ -- Создаем триггер для автоматического обновления updated_at
24
+ CREATE TRIGGER IF NOT EXISTS accounts_updated_at
25
+ AFTER UPDATE ON accounts
26
+ FOR EACH ROW
27
+ BEGIN
28
+ UPDATE accounts SET updated_at = CURRENT_TIMESTAMP WHERE id = NEW.id;
29
+ END;
30
+
31
+ -- DOWN
32
+ DROP TABLE IF EXISTS tg_accounts;
@@ -1,15 +1,15 @@
1
- import { BotSeeder } from '@2byte/tgbot-framework';
2
- import path from 'path';
3
-
4
- // Импортируйте здесь ваши сидеры
5
- // import { seedUsers } from './seeds/users';
6
-
7
- const seeder = new BotSeeder({
8
- botPath: __dirname,
9
- databasePath: path.join(__dirname, 'database.sqlite'),
10
- seeders: [
11
- // seedUsers,
12
- ]
13
- });
14
-
1
+ import { BotSeeder } from '@2byte/tgbot-framework';
2
+ import path from 'path';
3
+
4
+ // Импортируйте здесь ваши сидеры
5
+ // import { seedUsers } from './seeds/users';
6
+
7
+ const seeder = new BotSeeder({
8
+ botPath: __dirname,
9
+ databasePath: path.join(__dirname, 'database.sqlite'),
10
+ seeders: [
11
+ // seedUsers,
12
+ ]
13
+ });
14
+
15
15
  seeder.run();
@@ -0,0 +1,536 @@
1
+ # Service Generator - CLI Documentation
2
+
3
+ ## Описание
4
+
5
+ Генератор для создания API сервисов в 2byte Telegram Bot Framework. Сервисы - это переиспользуемые компоненты, которые предоставляют функциональность для всего бота.
6
+
7
+ ## Использование
8
+
9
+ ### Базовая команда
10
+
11
+ ```bash
12
+ 2byte generate service <name>
13
+ # или короткая форма
14
+ 2byte g service <name>
15
+ ```
16
+
17
+ ### Примеры
18
+
19
+ ```bash
20
+ # Создать сервис для работы с API
21
+ 2byte generate service PaymentAPI
22
+
23
+ # Создать сервис для уведомлений
24
+ 2byte g service Notification
25
+
26
+ # Создать сервис для кеширования
27
+ 2byte g service Cache
28
+
29
+ # Создать сервис для логирования
30
+ 2byte g service Logger
31
+ ```
32
+
33
+ ## Что создается
34
+
35
+ ### Структура файла
36
+
37
+ Генератор создает файл в директории `workflow/services/`:
38
+
39
+ ```
40
+ your-bot/
41
+ ├── workflow/
42
+ │ └── services/
43
+ │ ├── PaymentAPIService.ts ← новый файл
44
+ │ ├── NotificationService.ts
45
+ │ └── ...
46
+ ```
47
+
48
+ ### Имя файла
49
+
50
+ - Автоматически добавляется суффикс `Service` если его нет
51
+ - Используется PascalCase форматирование
52
+
53
+ **Примеры:**
54
+ - `payment` → `PaymentService.ts`
55
+ - `PaymentAPI` → `PaymentAPIService.ts`
56
+ - `notification-sender` → `Notification-senderService.ts` (лучше использовать camelCase)
57
+
58
+ ## Структура сервиса
59
+
60
+ Сгенерированный сервис содержит:
61
+
62
+ ```typescript
63
+ import { App } from "@2byte/tgbot-framework";
64
+ import { ApiService } from "@2byte/tgbot-framework";
65
+
66
+ export default class PaymentAPIService extends ApiService {
67
+
68
+ constructor(
69
+ protected app: App,
70
+ public name: string = "PaymentAPIService"
71
+ ) {
72
+ super(app, name);
73
+ }
74
+
75
+ public async setup(): Promise<void> {
76
+ // Инициализация при запуске
77
+ }
78
+
79
+ public async unsetup(): Promise<void> {
80
+ // Очистка при остановке
81
+ }
82
+
83
+ public async run(): Promise<void> {
84
+ // Основная логика сервиса
85
+ }
86
+ }
87
+ ```
88
+
89
+ ## Методы сервиса
90
+
91
+ ### `setup()`
92
+
93
+ Вызывается при регистрации сервиса во время запуска бота.
94
+
95
+ **Используйте для:**
96
+ - Инициализации подключений к БД
97
+ - Загрузки конфигураций
98
+ - Настройки HTTP клиентов
99
+ - Подписки на события
100
+
101
+ **Пример:**
102
+ ```typescript
103
+ public async setup(): Promise<void> {
104
+ this.apiClient = axios.create({
105
+ baseURL: 'https://api.example.com',
106
+ timeout: 5000
107
+ });
108
+
109
+ this.app.debugLog(`[${this.name}] API client initialized`);
110
+ }
111
+ ```
112
+
113
+ ### `unsetup()`
114
+
115
+ Вызывается при остановке бота или выгрузке сервиса.
116
+
117
+ **Используйте для:**
118
+ - Закрытия подключений
119
+ - Сохранения состояния
120
+ - Отписки от событий
121
+ - Освобождения ресурсов
122
+
123
+ **Пример:**
124
+ ```typescript
125
+ public async unsetup(): Promise<void> {
126
+ await this.apiClient?.disconnect();
127
+ this.app.debugLog(`[${this.name}] Cleanup completed`);
128
+ }
129
+ ```
130
+
131
+ ### `run()`
132
+
133
+ Основной метод для логики сервиса.
134
+
135
+ **Используйте для:**
136
+ - Запуска фоновых задач
137
+ - Периодических операций
138
+ - Обработки очередей
139
+ - Любой основной логики
140
+
141
+ **Пример:**
142
+ ```typescript
143
+ public async run(): Promise<void> {
144
+ // Запуск периодической задачи
145
+ setInterval(async () => {
146
+ await this.checkPayments();
147
+ }, 60000); // каждую минуту
148
+ }
149
+ ```
150
+
151
+ ## Автоматическая загрузка
152
+
153
+ Сервисы автоматически загружаются из директории `workflow/services/` при запуске бота.
154
+
155
+ **Процесс:**
156
+ 1. Бот сканирует `workflow/services/`
157
+ 2. Импортирует все `.ts` файлы
158
+ 3. Создает экземпляры сервисов
159
+ 4. Вызывает `setup()` для каждого
160
+ 5. Регистрирует в `ApiServiceManager`
161
+
162
+ ## Использование сервисов
163
+
164
+ ### Из секций
165
+
166
+ ```typescript
167
+ // В любой секции
168
+ export default class HomeSection extends Section {
169
+ async index() {
170
+ // Получить сервис
171
+ const paymentService = this.app.getService('PaymentAPIService');
172
+
173
+ // Использовать
174
+ const result = await paymentService.processPayment(100);
175
+ }
176
+ }
177
+ ```
178
+
179
+ ### Из других сервисов
180
+
181
+ ```typescript
182
+ export default class NotificationService extends ApiService {
183
+ public async sendNotification(userId: number, message: string) {
184
+ // Получить другой сервис
185
+ const logger = this.app.getService('LoggerService');
186
+ logger.log(`Sending notification to ${userId}`);
187
+
188
+ // Ваша логика
189
+ }
190
+ }
191
+ ```
192
+
193
+ ### Из App
194
+
195
+ ```typescript
196
+ const app = new App(config);
197
+
198
+ // После инициализации
199
+ const service = app.getService('PaymentAPIService');
200
+ ```
201
+
202
+ ## Примеры сервисов
203
+
204
+ ### 1. API клиент
205
+
206
+ ```typescript
207
+ import { App, ApiService } from "@2byte/tgbot-framework";
208
+ import axios, { AxiosInstance } from 'axios';
209
+
210
+ export default class PaymentAPIService extends ApiService {
211
+ private client: AxiosInstance;
212
+
213
+ constructor(protected app: App, public name: string = "PaymentAPIService") {
214
+ super(app, name);
215
+ }
216
+
217
+ public async setup(): Promise<void> {
218
+ this.client = axios.create({
219
+ baseURL: process.env.PAYMENT_API_URL,
220
+ headers: {
221
+ 'Authorization': `Bearer ${process.env.PAYMENT_API_KEY}`
222
+ }
223
+ });
224
+ }
225
+
226
+ public async createPayment(amount: number, userId: number) {
227
+ const response = await this.client.post('/payments', {
228
+ amount,
229
+ userId,
230
+ currency: 'USD'
231
+ });
232
+ return response.data;
233
+ }
234
+
235
+ public async checkPaymentStatus(paymentId: string) {
236
+ const response = await this.client.get(`/payments/${paymentId}`);
237
+ return response.data;
238
+ }
239
+ }
240
+ ```
241
+
242
+ ### 2. Кеш сервис
243
+
244
+ ```typescript
245
+ import { App, ApiService } from "@2byte/tgbot-framework";
246
+
247
+ export default class CacheService extends ApiService {
248
+ private cache: Map<string, any> = new Map();
249
+ private ttl: Map<string, number> = new Map();
250
+
251
+ constructor(protected app: App, public name: string = "CacheService") {
252
+ super(app, name);
253
+ }
254
+
255
+ public async setup(): Promise<void> {
256
+ // Очистка истекших записей каждые 5 минут
257
+ setInterval(() => this.cleanExpired(), 5 * 60 * 1000);
258
+ }
259
+
260
+ public set(key: string, value: any, ttlSeconds: number = 3600): void {
261
+ this.cache.set(key, value);
262
+ this.ttl.set(key, Date.now() + ttlSeconds * 1000);
263
+ }
264
+
265
+ public get(key: string): any | null {
266
+ if (!this.cache.has(key)) return null;
267
+
268
+ const expiry = this.ttl.get(key);
269
+ if (expiry && expiry < Date.now()) {
270
+ this.cache.delete(key);
271
+ this.ttl.delete(key);
272
+ return null;
273
+ }
274
+
275
+ return this.cache.get(key);
276
+ }
277
+
278
+ private cleanExpired(): void {
279
+ const now = Date.now();
280
+ for (const [key, expiry] of this.ttl.entries()) {
281
+ if (expiry < now) {
282
+ this.cache.delete(key);
283
+ this.ttl.delete(key);
284
+ }
285
+ }
286
+ }
287
+ }
288
+ ```
289
+
290
+ ### 3. Логгер сервис
291
+
292
+ ```typescript
293
+ import { App, ApiService } from "@2byte/tgbot-framework";
294
+ import * as fs from 'fs-extra';
295
+ import * as path from 'path';
296
+
297
+ export default class LoggerService extends ApiService {
298
+ private logPath: string;
299
+
300
+ constructor(protected app: App, public name: string = "LoggerService") {
301
+ super(app, name);
302
+ this.logPath = path.join(process.cwd(), 'logs');
303
+ }
304
+
305
+ public async setup(): Promise<void> {
306
+ await fs.ensureDir(this.logPath);
307
+ }
308
+
309
+ public async log(message: string, level: 'info' | 'error' | 'warn' = 'info'): Promise<void> {
310
+ const timestamp = new Date().toISOString();
311
+ const logMessage = `[${timestamp}] [${level.toUpperCase()}] ${message}\n`;
312
+
313
+ const filename = `${new Date().toISOString().split('T')[0]}.log`;
314
+ const filepath = path.join(this.logPath, filename);
315
+
316
+ await fs.appendFile(filepath, logMessage);
317
+
318
+ // Также выводим в консоль
319
+ console.log(logMessage);
320
+ }
321
+
322
+ public async error(message: string): Promise<void> {
323
+ await this.log(message, 'error');
324
+ }
325
+
326
+ public async warn(message: string): Promise<void> {
327
+ await this.log(message, 'warn');
328
+ }
329
+ }
330
+ ```
331
+
332
+ ### 4. Планировщик задач
333
+
334
+ ```typescript
335
+ import { App, ApiService } from "@2byte/tgbot-framework";
336
+
337
+ interface ScheduledTask {
338
+ id: string;
339
+ callback: () => Promise<void>;
340
+ interval: number;
341
+ timer?: NodeJS.Timeout;
342
+ }
343
+
344
+ export default class SchedulerService extends ApiService {
345
+ private tasks: Map<string, ScheduledTask> = new Map();
346
+
347
+ constructor(protected app: App, public name: string = "SchedulerService") {
348
+ super(app, name);
349
+ }
350
+
351
+ public async setup(): Promise<void> {
352
+ this.app.debugLog(`[${this.name}] Scheduler ready`);
353
+ }
354
+
355
+ public async unsetup(): Promise<void> {
356
+ // Останавливаем все задачи
357
+ for (const task of this.tasks.values()) {
358
+ if (task.timer) {
359
+ clearInterval(task.timer);
360
+ }
361
+ }
362
+ this.tasks.clear();
363
+ }
364
+
365
+ public addTask(id: string, callback: () => Promise<void>, intervalMs: number): void {
366
+ if (this.tasks.has(id)) {
367
+ throw new Error(`Task ${id} already exists`);
368
+ }
369
+
370
+ const timer = setInterval(async () => {
371
+ try {
372
+ await callback();
373
+ } catch (error) {
374
+ this.app.debugLog(`[${this.name}] Task ${id} error:`, error);
375
+ }
376
+ }, intervalMs);
377
+
378
+ this.tasks.set(id, { id, callback, interval: intervalMs, timer });
379
+ this.app.debugLog(`[${this.name}] Task ${id} scheduled every ${intervalMs}ms`);
380
+ }
381
+
382
+ public removeTask(id: string): boolean {
383
+ const task = this.tasks.get(id);
384
+ if (!task) return false;
385
+
386
+ if (task.timer) {
387
+ clearInterval(task.timer);
388
+ }
389
+ this.tasks.delete(id);
390
+ return true;
391
+ }
392
+
393
+ public async run(): Promise<void> {
394
+ // Пример: добавление задачи при запуске
395
+ // this.addTask('example', async () => {
396
+ // console.log('Scheduled task executed');
397
+ // }, 60000);
398
+ }
399
+ }
400
+ ```
401
+
402
+ ## Best Practices
403
+
404
+ ### 1. Именование
405
+ - ✅ Используйте описательные имена: `EmailService`, `PaymentAPIService`
406
+ - ❌ Избегайте общих имен: `Service1`, `Helper`
407
+
408
+ ### 2. Ответственность
409
+ - ✅ Один сервис - одна ответственность
410
+ - ❌ Не создавайте "God Objects"
411
+
412
+ ### 3. Зависимости
413
+ - ✅ Инжектите зависимости через конструктор
414
+ - ✅ Используйте другие сервисы через `app.getService()`
415
+ - ❌ Не создавайте жесткие связи
416
+
417
+ ### 4. Обработка ошибок
418
+ ```typescript
419
+ public async setup(): Promise<void> {
420
+ try {
421
+ // Инициализация
422
+ } catch (error) {
423
+ this.app.debugLog(`[${this.name}] Setup failed:`, error);
424
+ throw error; // Пробросить для остановки бота
425
+ }
426
+ }
427
+ ```
428
+
429
+ ### 5. Логирование
430
+ ```typescript
431
+ this.app.debugLog(`[${this.name}] Important action`);
432
+ ```
433
+
434
+ ## Жизненный цикл
435
+
436
+ ```
437
+ Bot Start
438
+
439
+ Load Services from workflow/services/
440
+
441
+ new Service(app, name)
442
+
443
+ service.setup()
444
+
445
+ service.run()
446
+
447
+ [Service is active]
448
+
449
+ Bot Stop
450
+
451
+ service.unsetup()
452
+
453
+ Service destroyed
454
+ ```
455
+
456
+ ## Отладка
457
+
458
+ ### Включить debug логи
459
+
460
+ ```typescript
461
+ const app = new App({
462
+ debug: true,
463
+ // ...
464
+ });
465
+ ```
466
+
467
+ ### Проверить загруженные сервисы
468
+
469
+ ```typescript
470
+ // В коде
471
+ const services = app.getAllServices();
472
+ console.log('Loaded services:', Array.from(services.keys()));
473
+ ```
474
+
475
+ ### Логи при загрузке
476
+
477
+ ```
478
+ [App] Registered API services: [ 'PaymentAPIService', 'CacheService', 'LoggerService' ]
479
+ [PaymentAPIService] Service setup completed
480
+ [CacheService] Service setup completed
481
+ [LoggerService] Service setup completed
482
+ ```
483
+
484
+ ## Troubleshooting
485
+
486
+ ### Сервис не загружается
487
+
488
+ **Проверьте:**
489
+ 1. Файл находится в `workflow/services/`
490
+ 2. Файл экспортирует класс как `export default`
491
+ 3. Класс наследуется от `ApiService`
492
+ 4. Нет ошибок в конструкторе
493
+
494
+ ### Ошибка при setup()
495
+
496
+ ```
497
+ Error: Service setup failed
498
+ ```
499
+
500
+ **Решение:**
501
+ - Проверьте логи в консоли
502
+ - Добавьте try-catch в setup()
503
+ - Проверьте доступность внешних ресурсов
504
+
505
+ ### Сервис недоступен из секций
506
+
507
+ ```typescript
508
+ const service = this.app.getService('ServiceName');
509
+ if (!service) {
510
+ console.error('Service not found!');
511
+ }
512
+ ```
513
+
514
+ **Решение:**
515
+ - Проверьте имя сервиса (case-sensitive)
516
+ - Убедитесь что сервис загружен
517
+ - Проверьте `app.getAllServices()`
518
+
519
+ ## Дополнительные команды
520
+
521
+ ```bash
522
+ # Создать секцию
523
+ 2byte generate section Settings
524
+
525
+ # Создать миграцию
526
+ 2byte generate migration create_users_table
527
+
528
+ # Показать помощь
529
+ 2byte generate --help
530
+ ```
531
+
532
+ ## См. также
533
+
534
+ - [API Reference - ApiService](../API_SERVICE.md)
535
+ - [Architecture - Services](../ARCHITECTURE.md#services)
536
+ - [Examples - Service Patterns](../examples/services/)