@2byte/tgbot-framework 1.0.6 → 1.0.7

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 +54 -54
  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 -50
  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 +1157 -1143
  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 -42
  28. package/src/libs/TelegramAccountControl.ts +1140 -1140
  29. package/src/libs/TgSender.ts +53 -53
  30. package/src/models/Model.ts +67 -67
  31. package/src/models/Proxy.ts +217 -217
  32. package/src/models/TgAccount.ts +362 -362
  33. package/src/models/index.ts +2 -2
  34. package/src/types.ts +191 -191
  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 +33 -33
  39. package/templates/bot/artisan.ts +8 -8
  40. package/templates/bot/bot.ts +82 -82
  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 -27
  45. package/templates/bot/database/migrations/008_tg_accounts.sql +32 -32
  46. package/templates/bot/database/seed.ts +14 -14
  47. package/templates/bot/docs/CLI_SERVICES.md +536 -536
  48. package/templates/bot/docs/INPUT_SYSTEM.md +211 -211
  49. package/templates/bot/docs/SERVICE_EXAMPLES.md +384 -384
  50. package/templates/bot/docs/TASK_SYSTEM.md +156 -156
  51. package/templates/bot/models/Model.ts +7 -7
  52. package/templates/bot/models/index.ts +1 -1
  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/ExampleService.ts +23 -23
@@ -1,362 +1,362 @@
1
- import { Model } from "./Model";
2
-
3
- export interface AccountData {
4
- /** PRIMARY KEY */
5
- id?: number;
6
- /** UNIQUE */
7
- phone: string;
8
- session?: string | null;
9
- password?: string | null;
10
- country?: string | null;
11
- proxy_id?: number | null; // Foreign key to proxy table
12
- status?: number;
13
- created_at?: string;
14
- updated_at?: string;
15
- }
16
-
17
- export enum TgAccountStatus {
18
- INACTIVE = 0,
19
- ACTIVE = 1,
20
- BANNED = 2,
21
- }
22
-
23
- export class TgAccount extends Model {
24
- static tableName = 'tg_accounts';
25
-
26
- /**
27
- * Создать новый аккаунт
28
- */
29
- static async create(data: AccountData): Promise<void> {
30
- const {
31
- phone,
32
- session = null,
33
- password = null,
34
- country = null,
35
- proxy_id = null,
36
- status = 0,
37
- } = data;
38
-
39
- await this.execute(
40
- `INSERT INTO ${this.tableName}
41
- (phone, session, password, country, proxy_id, status)
42
- VALUES (?, ?, ?, ?, ?, ?)`,
43
- [phone, session, password, country, proxy_id, status]
44
- );
45
- }
46
-
47
- /**
48
- * Найти аккаунт по номеру телефона
49
- */
50
- static async findByPhone(phone: string): Promise<AccountData | null> {
51
- return this.queryOne(`SELECT * FROM ${this.tableName} WHERE phone = ?`, [phone]);
52
- }
53
-
54
- /**
55
- * Найти аккаунт по ID
56
- */
57
- static async findById(id: number): Promise<AccountData | null> {
58
- return this.queryOne(`SELECT * FROM ${this.tableName} WHERE id = ?`, [id]);
59
- }
60
-
61
- /**
62
- * Обновить сессию аккаунта
63
- */
64
- static async updateSession(phone: string, session: string): Promise<void> {
65
- await this.execute(
66
- `UPDATE ${this.tableName}
67
- SET session = ?, updated_at = CURRENT_TIMESTAMP
68
- WHERE phone = ?`,
69
- [session, phone]
70
- );
71
- }
72
-
73
- /**
74
- * Обновить пароль аккаунта
75
- */
76
- static async updatePassword(phone: string, password: string): Promise<void> {
77
- await this.execute(
78
- `UPDATE ${this.tableName}
79
- SET password = ?, updated_at = CURRENT_TIMESTAMP
80
- WHERE phone = ?`,
81
- [password, phone]
82
- );
83
- }
84
-
85
- /**
86
- * Обновить страну аккаунта
87
- */
88
- static async updateCountry(phone: string, country: string): Promise<void> {
89
- await this.execute(
90
- `UPDATE ${this.tableName}
91
- SET country = ?, updated_at = CURRENT_TIMESTAMP
92
- WHERE phone = ?`,
93
- [country, phone]
94
- );
95
- }
96
-
97
- /**
98
- * Обновить прокси аккаунта
99
- */
100
- static async updateProxy(phone: string, proxy_id: number | null): Promise<void> {
101
- await this.execute(
102
- `UPDATE ${this.tableName}
103
- SET proxy_id = ?, updated_at = CURRENT_TIMESTAMP
104
- WHERE phone = ?`,
105
- [proxy_id, phone]
106
- );
107
- }
108
-
109
- /**
110
- * Обновить статус аккаунта
111
- */
112
- static async updateStatus(phone: string, status: number): Promise<void> {
113
- await this.execute(
114
- `UPDATE ${this.tableName}
115
- SET status = ?, updated_at = CURRENT_TIMESTAMP
116
- WHERE phone = ?`,
117
- [status, phone]
118
- );
119
- }
120
-
121
- /**
122
- * Универсальный метод обновления любых полей
123
- */
124
- static async update(phone: string, fields: Partial<Omit<AccountData, "phone" | "id">>): Promise<void> {
125
- const keys = Object.keys(fields);
126
- if (keys.length === 0) return;
127
-
128
- const setClause = keys.map(k => `${k} = ?`).join(", ");
129
- const values = keys.map(k => (fields as any)[k]);
130
-
131
- await this.execute(
132
- `UPDATE ${this.tableName}
133
- SET ${setClause}, updated_at = CURRENT_TIMESTAMP
134
- WHERE phone = ?`,
135
- [...values, phone]
136
- );
137
- }
138
-
139
- /**
140
- * Вставка или обновление аккаунта (upsert)
141
- */
142
- static async upsert(data: AccountData): Promise<void> {
143
- const {
144
- phone,
145
- session = null,
146
- password = null,
147
- country = null,
148
- proxy_id = null,
149
- status = 0,
150
- } = data;
151
-
152
- await this.execute(
153
- `INSERT INTO ${this.tableName}
154
- (phone, session, password, country, proxy_id, status)
155
- VALUES (?, ?, ?, ?, ?, ?)
156
- ON CONFLICT(phone) DO UPDATE
157
- SET session = excluded.session,
158
- password = excluded.password,
159
- country = excluded.country,
160
- proxy_id = excluded.proxy_id,
161
- status = excluded.status,
162
- updated_at = CURRENT_TIMESTAMP`,
163
- [phone, session, password, country, proxy_id, status]
164
- );
165
- }
166
-
167
- /**
168
- * Получить все аккаунты
169
- */
170
- static async getAll(): Promise<AccountData[]> {
171
- return this.query(`SELECT * FROM ${this.tableName} ORDER BY created_at DESC`);
172
- }
173
-
174
- /**
175
- * Удалить аккаунт
176
- */
177
- static async delete(phone: string): Promise<void> {
178
- await this.execute(`DELETE FROM ${this.tableName} WHERE phone = ?`, [phone]);
179
- }
180
-
181
- /**
182
- * Получить количество аккаунтов
183
- */
184
- static async count(): Promise<number> {
185
- const result: { count: number } = await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName}`);
186
- return result.count;
187
- }
188
-
189
- static async countActive(): Promise<number> {
190
- const result: { count: number } = await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = 1`);
191
- return result.count;
192
- }
193
-
194
- /**
195
- * Получить следующий свободный аккаунт
196
- */
197
- static async getNextAvailable(): Promise<AccountData | null> {
198
- return this.queryOne(`
199
- SELECT a.* FROM ${this.tableName} a
200
- LEFT JOIN report_tasks rt ON a.phone = rt.account_phone AND rt.status = 'pending'
201
- WHERE rt.id IS NULL
202
- LIMIT 1
203
- `);
204
- }
205
-
206
- /**
207
- * Получить аккаунты с прокси
208
- */
209
- static async getWithProxy(): Promise<AccountData[]> {
210
- return this.query(`SELECT * FROM ${this.tableName} WHERE proxy_id IS NOT NULL ORDER BY created_at DESC`);
211
- }
212
-
213
- /**
214
- * Получить аккаунты без прокси
215
- */
216
- static async getWithoutProxy(): Promise<AccountData[]> {
217
- return this.query(`SELECT * FROM ${this.tableName} WHERE proxy_id IS NULL ORDER BY created_at DESC`);
218
- }
219
-
220
- /**
221
- * Получить аккаунт с информацией о прокси (JOIN)
222
- */
223
- static async findWithProxy(phone: string): Promise<(AccountData & { proxy?: any }) | null> {
224
- return this.queryOne(`
225
- SELECT
226
- a.*,
227
- p.ip as proxy_ip,
228
- p.port as proxy_port,
229
- p.username as proxy_username,
230
- p.password as proxy_password,
231
- p.secret as proxy_secret,
232
- p.socksType as proxy_socksType,
233
- p.MTProxy as proxy_MTProxy,
234
- p.status as proxy_status,
235
- p.source as proxy_source
236
- FROM ${this.tableName} a
237
- LEFT JOIN proxy p ON a.proxy_id = p.id
238
- WHERE a.phone = ?
239
- `, [phone]);
240
- }
241
-
242
- /**
243
- * Получить все аккаунты с информацией о прокси (JOIN)
244
- */
245
- static async getAllWithProxy(): Promise<(AccountData & { proxy?: any })[]> {
246
- return this.query(`
247
- SELECT
248
- a.*,
249
- p.ip as proxy_ip,
250
- p.port as proxy_port,
251
- p.username as proxy_username,
252
- p.password as proxy_password,
253
- p.secret as proxy_secret,
254
- p.socksType as proxy_socksType,
255
- p.MTProxy as proxy_MTProxy,
256
- p.status as proxy_status,
257
- p.source as proxy_source
258
- FROM ${this.tableName} a
259
- LEFT JOIN proxy p ON a.proxy_id = p.id
260
- ORDER BY a.created_at DESC
261
- `);
262
- }
263
-
264
- /**
265
- * Привязать прокси к аккаунту
266
- */
267
- static async assignProxy(phone: string, proxyId: number): Promise<void> {
268
- await this.execute(
269
- `UPDATE ${this.tableName}
270
- SET proxy_id = ?, updated_at = CURRENT_TIMESTAMP
271
- WHERE phone = ?`,
272
- [proxyId, phone]
273
- );
274
- }
275
-
276
- /**
277
- * Отвязать прокси от аккаунта
278
- */
279
- static async unassignProxy(phone: string): Promise<void> {
280
- await this.execute(
281
- `UPDATE ${this.tableName}
282
- SET proxy_id = NULL, updated_at = CURRENT_TIMESTAMP
283
- WHERE phone = ?`,
284
- [phone]
285
- );
286
- }
287
-
288
- /**
289
- * Получить аккаунты по статусу
290
- */
291
- static async getByStatus(status: number): Promise<AccountData[]> {
292
- return this.query(`SELECT * FROM ${this.tableName} WHERE status = ? ORDER BY created_at DESC`, [status]);
293
- }
294
-
295
- /**
296
- * Получить активные аккаунты
297
- */
298
- static async getActive(): Promise<AccountData[]> {
299
- return this.getByStatus(TgAccountStatus.ACTIVE);
300
- }
301
-
302
- /**
303
- * Получить заблокированные аккаунты
304
- */
305
- static async getBanned(): Promise<AccountData[]> {
306
- return this.getByStatus(TgAccountStatus.BANNED);
307
- }
308
-
309
- /**
310
- * Проверить существование аккаунта
311
- */
312
- static async exists(phone: string): Promise<boolean> {
313
- const result: { count: number } = await this.queryOne(
314
- `SELECT COUNT(*) as count FROM ${this.tableName} WHERE phone = ?`,
315
- [phone]
316
- );
317
- return result.count > 0;
318
- }
319
-
320
- /**
321
- * Получить статистику аккаунтов
322
- */
323
- static async getStats(): Promise<{
324
- total: number;
325
- active: number;
326
- banned: number;
327
- inactive: number;
328
- withProxy: number;
329
- withoutProxy: number;
330
- withSession: number;
331
- }> {
332
- const total = await this.count();
333
- const active = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [TgAccountStatus.ACTIVE])).count;
334
- const banned = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [TgAccountStatus.BANNED])).count;
335
- const inactive = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [TgAccountStatus.INACTIVE])).count;
336
- const withProxy = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE proxy_id IS NOT NULL`)).count;
337
- const withoutProxy = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE proxy_id IS NULL`)).count;
338
- const withSession = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE session IS NOT NULL`)).count;
339
-
340
- return {
341
- total,
342
- active,
343
- banned,
344
- inactive,
345
- withProxy,
346
- withoutProxy,
347
- withSession
348
- };
349
- }
350
-
351
- /**
352
- * Получить случайный доступный аккаунт
353
- */
354
- static async getRandomAvailable(): Promise<AccountData | null> {
355
- return this.queryOne(`
356
- SELECT * FROM ${this.tableName}
357
- WHERE status = ? AND session IS NOT NULL
358
- ORDER BY RANDOM()
359
- LIMIT 1
360
- `, [TgAccountStatus.ACTIVE]);
361
- }
362
- }
1
+ import { Model } from "./Model";
2
+
3
+ export interface AccountData {
4
+ /** PRIMARY KEY */
5
+ id?: number;
6
+ /** UNIQUE */
7
+ phone: string;
8
+ session?: string | null;
9
+ password?: string | null;
10
+ country?: string | null;
11
+ proxy_id?: number | null; // Foreign key to proxy table
12
+ status?: number;
13
+ created_at?: string;
14
+ updated_at?: string;
15
+ }
16
+
17
+ export enum TgAccountStatus {
18
+ INACTIVE = 0,
19
+ ACTIVE = 1,
20
+ BANNED = 2,
21
+ }
22
+
23
+ export class TgAccount extends Model {
24
+ static tableName = 'tg_accounts';
25
+
26
+ /**
27
+ * Создать новый аккаунт
28
+ */
29
+ static async create(data: AccountData): Promise<void> {
30
+ const {
31
+ phone,
32
+ session = null,
33
+ password = null,
34
+ country = null,
35
+ proxy_id = null,
36
+ status = 0,
37
+ } = data;
38
+
39
+ await this.execute(
40
+ `INSERT INTO ${this.tableName}
41
+ (phone, session, password, country, proxy_id, status)
42
+ VALUES (?, ?, ?, ?, ?, ?)`,
43
+ [phone, session, password, country, proxy_id, status]
44
+ );
45
+ }
46
+
47
+ /**
48
+ * Найти аккаунт по номеру телефона
49
+ */
50
+ static async findByPhone(phone: string): Promise<AccountData | null> {
51
+ return this.queryOne(`SELECT * FROM ${this.tableName} WHERE phone = ?`, [phone]);
52
+ }
53
+
54
+ /**
55
+ * Найти аккаунт по ID
56
+ */
57
+ static async findById(id: number): Promise<AccountData | null> {
58
+ return this.queryOne(`SELECT * FROM ${this.tableName} WHERE id = ?`, [id]);
59
+ }
60
+
61
+ /**
62
+ * Обновить сессию аккаунта
63
+ */
64
+ static async updateSession(phone: string, session: string): Promise<void> {
65
+ await this.execute(
66
+ `UPDATE ${this.tableName}
67
+ SET session = ?, updated_at = CURRENT_TIMESTAMP
68
+ WHERE phone = ?`,
69
+ [session, phone]
70
+ );
71
+ }
72
+
73
+ /**
74
+ * Обновить пароль аккаунта
75
+ */
76
+ static async updatePassword(phone: string, password: string): Promise<void> {
77
+ await this.execute(
78
+ `UPDATE ${this.tableName}
79
+ SET password = ?, updated_at = CURRENT_TIMESTAMP
80
+ WHERE phone = ?`,
81
+ [password, phone]
82
+ );
83
+ }
84
+
85
+ /**
86
+ * Обновить страну аккаунта
87
+ */
88
+ static async updateCountry(phone: string, country: string): Promise<void> {
89
+ await this.execute(
90
+ `UPDATE ${this.tableName}
91
+ SET country = ?, updated_at = CURRENT_TIMESTAMP
92
+ WHERE phone = ?`,
93
+ [country, phone]
94
+ );
95
+ }
96
+
97
+ /**
98
+ * Обновить прокси аккаунта
99
+ */
100
+ static async updateProxy(phone: string, proxy_id: number | null): Promise<void> {
101
+ await this.execute(
102
+ `UPDATE ${this.tableName}
103
+ SET proxy_id = ?, updated_at = CURRENT_TIMESTAMP
104
+ WHERE phone = ?`,
105
+ [proxy_id, phone]
106
+ );
107
+ }
108
+
109
+ /**
110
+ * Обновить статус аккаунта
111
+ */
112
+ static async updateStatus(phone: string, status: number): Promise<void> {
113
+ await this.execute(
114
+ `UPDATE ${this.tableName}
115
+ SET status = ?, updated_at = CURRENT_TIMESTAMP
116
+ WHERE phone = ?`,
117
+ [status, phone]
118
+ );
119
+ }
120
+
121
+ /**
122
+ * Универсальный метод обновления любых полей
123
+ */
124
+ static async update(phone: string, fields: Partial<Omit<AccountData, "phone" | "id">>): Promise<void> {
125
+ const keys = Object.keys(fields);
126
+ if (keys.length === 0) return;
127
+
128
+ const setClause = keys.map(k => `${k} = ?`).join(", ");
129
+ const values = keys.map(k => (fields as any)[k]);
130
+
131
+ await this.execute(
132
+ `UPDATE ${this.tableName}
133
+ SET ${setClause}, updated_at = CURRENT_TIMESTAMP
134
+ WHERE phone = ?`,
135
+ [...values, phone]
136
+ );
137
+ }
138
+
139
+ /**
140
+ * Вставка или обновление аккаунта (upsert)
141
+ */
142
+ static async upsert(data: AccountData): Promise<void> {
143
+ const {
144
+ phone,
145
+ session = null,
146
+ password = null,
147
+ country = null,
148
+ proxy_id = null,
149
+ status = 0,
150
+ } = data;
151
+
152
+ await this.execute(
153
+ `INSERT INTO ${this.tableName}
154
+ (phone, session, password, country, proxy_id, status)
155
+ VALUES (?, ?, ?, ?, ?, ?)
156
+ ON CONFLICT(phone) DO UPDATE
157
+ SET session = excluded.session,
158
+ password = excluded.password,
159
+ country = excluded.country,
160
+ proxy_id = excluded.proxy_id,
161
+ status = excluded.status,
162
+ updated_at = CURRENT_TIMESTAMP`,
163
+ [phone, session, password, country, proxy_id, status]
164
+ );
165
+ }
166
+
167
+ /**
168
+ * Получить все аккаунты
169
+ */
170
+ static async getAll(): Promise<AccountData[]> {
171
+ return this.query(`SELECT * FROM ${this.tableName} ORDER BY created_at DESC`);
172
+ }
173
+
174
+ /**
175
+ * Удалить аккаунт
176
+ */
177
+ static async delete(phone: string): Promise<void> {
178
+ await this.execute(`DELETE FROM ${this.tableName} WHERE phone = ?`, [phone]);
179
+ }
180
+
181
+ /**
182
+ * Получить количество аккаунтов
183
+ */
184
+ static async count(): Promise<number> {
185
+ const result: { count: number } = await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName}`);
186
+ return result.count;
187
+ }
188
+
189
+ static async countActive(): Promise<number> {
190
+ const result: { count: number } = await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = 1`);
191
+ return result.count;
192
+ }
193
+
194
+ /**
195
+ * Получить следующий свободный аккаунт
196
+ */
197
+ static async getNextAvailable(): Promise<AccountData | null> {
198
+ return this.queryOne(`
199
+ SELECT a.* FROM ${this.tableName} a
200
+ LEFT JOIN report_tasks rt ON a.phone = rt.account_phone AND rt.status = 'pending'
201
+ WHERE rt.id IS NULL
202
+ LIMIT 1
203
+ `);
204
+ }
205
+
206
+ /**
207
+ * Получить аккаунты с прокси
208
+ */
209
+ static async getWithProxy(): Promise<AccountData[]> {
210
+ return this.query(`SELECT * FROM ${this.tableName} WHERE proxy_id IS NOT NULL ORDER BY created_at DESC`);
211
+ }
212
+
213
+ /**
214
+ * Получить аккаунты без прокси
215
+ */
216
+ static async getWithoutProxy(): Promise<AccountData[]> {
217
+ return this.query(`SELECT * FROM ${this.tableName} WHERE proxy_id IS NULL ORDER BY created_at DESC`);
218
+ }
219
+
220
+ /**
221
+ * Получить аккаунт с информацией о прокси (JOIN)
222
+ */
223
+ static async findWithProxy(phone: string): Promise<(AccountData & { proxy?: any }) | null> {
224
+ return this.queryOne(`
225
+ SELECT
226
+ a.*,
227
+ p.ip as proxy_ip,
228
+ p.port as proxy_port,
229
+ p.username as proxy_username,
230
+ p.password as proxy_password,
231
+ p.secret as proxy_secret,
232
+ p.socksType as proxy_socksType,
233
+ p.MTProxy as proxy_MTProxy,
234
+ p.status as proxy_status,
235
+ p.source as proxy_source
236
+ FROM ${this.tableName} a
237
+ LEFT JOIN proxy p ON a.proxy_id = p.id
238
+ WHERE a.phone = ?
239
+ `, [phone]);
240
+ }
241
+
242
+ /**
243
+ * Получить все аккаунты с информацией о прокси (JOIN)
244
+ */
245
+ static async getAllWithProxy(): Promise<(AccountData & { proxy?: any })[]> {
246
+ return this.query(`
247
+ SELECT
248
+ a.*,
249
+ p.ip as proxy_ip,
250
+ p.port as proxy_port,
251
+ p.username as proxy_username,
252
+ p.password as proxy_password,
253
+ p.secret as proxy_secret,
254
+ p.socksType as proxy_socksType,
255
+ p.MTProxy as proxy_MTProxy,
256
+ p.status as proxy_status,
257
+ p.source as proxy_source
258
+ FROM ${this.tableName} a
259
+ LEFT JOIN proxy p ON a.proxy_id = p.id
260
+ ORDER BY a.created_at DESC
261
+ `);
262
+ }
263
+
264
+ /**
265
+ * Привязать прокси к аккаунту
266
+ */
267
+ static async assignProxy(phone: string, proxyId: number): Promise<void> {
268
+ await this.execute(
269
+ `UPDATE ${this.tableName}
270
+ SET proxy_id = ?, updated_at = CURRENT_TIMESTAMP
271
+ WHERE phone = ?`,
272
+ [proxyId, phone]
273
+ );
274
+ }
275
+
276
+ /**
277
+ * Отвязать прокси от аккаунта
278
+ */
279
+ static async unassignProxy(phone: string): Promise<void> {
280
+ await this.execute(
281
+ `UPDATE ${this.tableName}
282
+ SET proxy_id = NULL, updated_at = CURRENT_TIMESTAMP
283
+ WHERE phone = ?`,
284
+ [phone]
285
+ );
286
+ }
287
+
288
+ /**
289
+ * Получить аккаунты по статусу
290
+ */
291
+ static async getByStatus(status: number): Promise<AccountData[]> {
292
+ return this.query(`SELECT * FROM ${this.tableName} WHERE status = ? ORDER BY created_at DESC`, [status]);
293
+ }
294
+
295
+ /**
296
+ * Получить активные аккаунты
297
+ */
298
+ static async getActive(): Promise<AccountData[]> {
299
+ return this.getByStatus(TgAccountStatus.ACTIVE);
300
+ }
301
+
302
+ /**
303
+ * Получить заблокированные аккаунты
304
+ */
305
+ static async getBanned(): Promise<AccountData[]> {
306
+ return this.getByStatus(TgAccountStatus.BANNED);
307
+ }
308
+
309
+ /**
310
+ * Проверить существование аккаунта
311
+ */
312
+ static async exists(phone: string): Promise<boolean> {
313
+ const result: { count: number } = await this.queryOne(
314
+ `SELECT COUNT(*) as count FROM ${this.tableName} WHERE phone = ?`,
315
+ [phone]
316
+ );
317
+ return result.count > 0;
318
+ }
319
+
320
+ /**
321
+ * Получить статистику аккаунтов
322
+ */
323
+ static async getStats(): Promise<{
324
+ total: number;
325
+ active: number;
326
+ banned: number;
327
+ inactive: number;
328
+ withProxy: number;
329
+ withoutProxy: number;
330
+ withSession: number;
331
+ }> {
332
+ const total = await this.count();
333
+ const active = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [TgAccountStatus.ACTIVE])).count;
334
+ const banned = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [TgAccountStatus.BANNED])).count;
335
+ const inactive = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [TgAccountStatus.INACTIVE])).count;
336
+ const withProxy = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE proxy_id IS NOT NULL`)).count;
337
+ const withoutProxy = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE proxy_id IS NULL`)).count;
338
+ const withSession = (await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE session IS NOT NULL`)).count;
339
+
340
+ return {
341
+ total,
342
+ active,
343
+ banned,
344
+ inactive,
345
+ withProxy,
346
+ withoutProxy,
347
+ withSession
348
+ };
349
+ }
350
+
351
+ /**
352
+ * Получить случайный доступный аккаунт
353
+ */
354
+ static async getRandomAvailable(): Promise<AccountData | null> {
355
+ return this.queryOne(`
356
+ SELECT * FROM ${this.tableName}
357
+ WHERE status = ? AND session IS NOT NULL
358
+ ORDER BY RANDOM()
359
+ LIMIT 1
360
+ `, [TgAccountStatus.ACTIVE]);
361
+ }
362
+ }