@2byte/tgbot-framework 1.0.5 → 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.
- package/README.md +300 -300
- package/bin/2byte-cli.ts +97 -97
- package/package.json +54 -54
- 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 +80 -80
- package/templates/bot/.env.example +34 -23
- 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/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
package/src/models/TgAccount.ts
CHANGED
|
@@ -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
|
+
}
|