@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.
- 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 +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/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/libs/TgSender.ts
CHANGED
|
@@ -1,53 +1,53 @@
|
|
|
1
|
-
import {
|
|
2
|
-
TelegramAccountRemote,
|
|
3
|
-
TelegramManagerCredentials,
|
|
4
|
-
TelegramCredentials,
|
|
5
|
-
} from "./TelegramAccountControl";
|
|
6
|
-
|
|
7
|
-
interface TgSenderParams {
|
|
8
|
-
tgAppId: string;
|
|
9
|
-
tgAppHash: string;
|
|
10
|
-
app: any;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export class TgSender {
|
|
14
|
-
private makeInstanceTgRemoteControl: () => {
|
|
15
|
-
remoteControl: TelegramAccountRemote;
|
|
16
|
-
credentialsManager: TelegramManagerCredentials;
|
|
17
|
-
};
|
|
18
|
-
|
|
19
|
-
static init(params: TgSenderParams) {
|
|
20
|
-
return new TgSender(params);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
constructor(private params: TgSenderParams) {
|
|
24
|
-
this.makeInstanceTgRemoteControl = () => {
|
|
25
|
-
const credentialsManager = TelegramManagerCredentials.init({
|
|
26
|
-
pathFileStorage: path.join(__dirname, "../storage/tg_credentials.json"),
|
|
27
|
-
pathFileProxyList: path.join(__dirname, "../storage/tg_proxy_list.txt"),
|
|
28
|
-
pathFileCounterOffset: path.join(__dirname, "../storage/tg_counter_offset.txt"),
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
const remoteControl = TelegramAccountRemote.init({
|
|
32
|
-
appId: params.tgAppId!,
|
|
33
|
-
appHash: params.tgAppHash!,
|
|
34
|
-
credetialsManager: credentialsManager,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
return {
|
|
38
|
-
remoteControl,
|
|
39
|
-
credentialsManager,
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
fromRandomAccount() {
|
|
45
|
-
return this;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
fromAccountCredentials(credentials: TelegramCredentials) {
|
|
49
|
-
return this;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
sendMessageByPhone(phone: string, message: string) {}
|
|
53
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
TelegramAccountRemote,
|
|
3
|
+
TelegramManagerCredentials,
|
|
4
|
+
TelegramCredentials,
|
|
5
|
+
} from "./TelegramAccountControl";
|
|
6
|
+
|
|
7
|
+
interface TgSenderParams {
|
|
8
|
+
tgAppId: string;
|
|
9
|
+
tgAppHash: string;
|
|
10
|
+
app: any;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export class TgSender {
|
|
14
|
+
private makeInstanceTgRemoteControl: () => {
|
|
15
|
+
remoteControl: TelegramAccountRemote;
|
|
16
|
+
credentialsManager: TelegramManagerCredentials;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
static init(params: TgSenderParams) {
|
|
20
|
+
return new TgSender(params);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
constructor(private params: TgSenderParams) {
|
|
24
|
+
this.makeInstanceTgRemoteControl = () => {
|
|
25
|
+
const credentialsManager = TelegramManagerCredentials.init({
|
|
26
|
+
pathFileStorage: path.join(__dirname, "../storage/tg_credentials.json"),
|
|
27
|
+
pathFileProxyList: path.join(__dirname, "../storage/tg_proxy_list.txt"),
|
|
28
|
+
pathFileCounterOffset: path.join(__dirname, "../storage/tg_counter_offset.txt"),
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
const remoteControl = TelegramAccountRemote.init({
|
|
32
|
+
appId: params.tgAppId!,
|
|
33
|
+
appHash: params.tgAppHash!,
|
|
34
|
+
credetialsManager: credentialsManager,
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
return {
|
|
38
|
+
remoteControl,
|
|
39
|
+
credentialsManager,
|
|
40
|
+
};
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
fromRandomAccount() {
|
|
45
|
+
return this;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
fromAccountCredentials(credentials: TelegramCredentials) {
|
|
49
|
+
return this;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
sendMessageByPhone(phone: string, message: string) {}
|
|
53
|
+
}
|
package/src/models/Model.ts
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
import type { Database } from "bun:sqlite";
|
|
2
|
-
import { MakeManualPaginateButtonsParams, ModelPaginateParams, PaginateResult } from "..";
|
|
3
|
-
import { Section } from "..";
|
|
4
|
-
|
|
5
|
-
export abstract class Model {
|
|
6
|
-
protected static db: Database;
|
|
7
|
-
protected static tableName: string;
|
|
8
|
-
|
|
9
|
-
static setDatabase(database: Database) {
|
|
10
|
-
this.db = database;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
protected static query(sql: string, params: any[] = []): any {
|
|
14
|
-
const stmt = this.db.prepare(sql);
|
|
15
|
-
return stmt.all(...params);
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
protected static queryOne(sql: string, params: any[] = []): any {
|
|
19
|
-
const stmt = this.db.prepare(sql);
|
|
20
|
-
return stmt.get(...params);
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
protected static execute(sql: string, params: any[] = []): void {
|
|
24
|
-
const stmt = this.db.prepare(sql);
|
|
25
|
-
stmt.run(...params);
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
protected static transaction<T>(callback: () => T): T {
|
|
29
|
-
return this.db.transaction(callback)();
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
public static async paginate(paginateParams: ModelPaginateParams): Promise<PaginateResult> {
|
|
33
|
-
const { page, route, routeParams, limit, whereSql, whereParams } = paginateParams;
|
|
34
|
-
const offset = (page - 1) * limit;
|
|
35
|
-
const sql = `SELECT * FROM ${this.tableName} ${whereSql} LIMIT ${offset}, ${limit}`;
|
|
36
|
-
|
|
37
|
-
const result = await this.query(sql, whereParams);
|
|
38
|
-
const queryTotal = await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} ${whereSql}`, whereParams);
|
|
39
|
-
const total = queryTotal ? queryTotal.count : 0;
|
|
40
|
-
const totalPages = Math.ceil(total / limit);
|
|
41
|
-
const hasPreviousPage = page > 1;
|
|
42
|
-
const hasNextPage = page < totalPages;
|
|
43
|
-
|
|
44
|
-
return {
|
|
45
|
-
items: result,
|
|
46
|
-
paginateButtons: Section.makeManualPaginateButtons({
|
|
47
|
-
callbackDataAction: route,
|
|
48
|
-
paramsQuery: routeParams || {},
|
|
49
|
-
currentPage: page,
|
|
50
|
-
totalRecords: total,
|
|
51
|
-
perPage: limit,
|
|
52
|
-
} as MakeManualPaginateButtonsParams),
|
|
53
|
-
total,
|
|
54
|
-
totalPages,
|
|
55
|
-
hasPreviousPage,
|
|
56
|
-
hasNextPage,
|
|
57
|
-
currentPage: page,
|
|
58
|
-
} as PaginateResult;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
static getConnection(): Database {
|
|
62
|
-
if (db) {
|
|
63
|
-
return db;
|
|
64
|
-
}
|
|
65
|
-
throw new Error("Database connection is not set.");
|
|
66
|
-
}
|
|
67
|
-
}
|
|
1
|
+
import type { Database } from "bun:sqlite";
|
|
2
|
+
import { MakeManualPaginateButtonsParams, ModelPaginateParams, PaginateResult } from "..";
|
|
3
|
+
import { Section } from "..";
|
|
4
|
+
|
|
5
|
+
export abstract class Model {
|
|
6
|
+
protected static db: Database;
|
|
7
|
+
protected static tableName: string;
|
|
8
|
+
|
|
9
|
+
static setDatabase(database: Database) {
|
|
10
|
+
this.db = database;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
protected static query(sql: string, params: any[] = []): any {
|
|
14
|
+
const stmt = this.db.prepare(sql);
|
|
15
|
+
return stmt.all(...params);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
protected static queryOne(sql: string, params: any[] = []): any {
|
|
19
|
+
const stmt = this.db.prepare(sql);
|
|
20
|
+
return stmt.get(...params);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
protected static execute(sql: string, params: any[] = []): void {
|
|
24
|
+
const stmt = this.db.prepare(sql);
|
|
25
|
+
stmt.run(...params);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
protected static transaction<T>(callback: () => T): T {
|
|
29
|
+
return this.db.transaction(callback)();
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
public static async paginate(paginateParams: ModelPaginateParams): Promise<PaginateResult> {
|
|
33
|
+
const { page, route, routeParams, limit, whereSql, whereParams } = paginateParams;
|
|
34
|
+
const offset = (page - 1) * limit;
|
|
35
|
+
const sql = `SELECT * FROM ${this.tableName} ${whereSql} LIMIT ${offset}, ${limit}`;
|
|
36
|
+
|
|
37
|
+
const result = await this.query(sql, whereParams);
|
|
38
|
+
const queryTotal = await this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} ${whereSql}`, whereParams);
|
|
39
|
+
const total = queryTotal ? queryTotal.count : 0;
|
|
40
|
+
const totalPages = Math.ceil(total / limit);
|
|
41
|
+
const hasPreviousPage = page > 1;
|
|
42
|
+
const hasNextPage = page < totalPages;
|
|
43
|
+
|
|
44
|
+
return {
|
|
45
|
+
items: result,
|
|
46
|
+
paginateButtons: Section.makeManualPaginateButtons({
|
|
47
|
+
callbackDataAction: route,
|
|
48
|
+
paramsQuery: routeParams || {},
|
|
49
|
+
currentPage: page,
|
|
50
|
+
totalRecords: total,
|
|
51
|
+
perPage: limit,
|
|
52
|
+
} as MakeManualPaginateButtonsParams),
|
|
53
|
+
total,
|
|
54
|
+
totalPages,
|
|
55
|
+
hasPreviousPage,
|
|
56
|
+
hasNextPage,
|
|
57
|
+
currentPage: page,
|
|
58
|
+
} as PaginateResult;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
static getConnection(): Database {
|
|
62
|
+
if (db) {
|
|
63
|
+
return db;
|
|
64
|
+
}
|
|
65
|
+
throw new Error("Database connection is not set.");
|
|
66
|
+
}
|
|
67
|
+
}
|
package/src/models/Proxy.ts
CHANGED
|
@@ -1,218 +1,218 @@
|
|
|
1
|
-
import { Model } from "./Model";
|
|
2
|
-
|
|
3
|
-
export interface ProxyData {
|
|
4
|
-
/** PRIMARY KEY */
|
|
5
|
-
id?: number;
|
|
6
|
-
ip: string;
|
|
7
|
-
port: number;
|
|
8
|
-
username?: string | null;
|
|
9
|
-
password?: string | null;
|
|
10
|
-
secret?: string | null;
|
|
11
|
-
socksType?: number | null; // 4 or 5
|
|
12
|
-
MTProxy?: number; // 0 or 1
|
|
13
|
-
status?: number; // 0 = inactive, 1 = active
|
|
14
|
-
source?: string | null; // Note: keeping original typo from migration
|
|
15
|
-
last_check?: string | null;
|
|
16
|
-
created_at?: string;
|
|
17
|
-
updated_at?: string;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export enum ProxyStatus {
|
|
21
|
-
INACTIVE = 0,
|
|
22
|
-
ACTIVE = 1
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
export enum ProxySocksType {
|
|
26
|
-
SOCKS4 = 4,
|
|
27
|
-
SOCKS5 = 5
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export class Proxy extends Model {
|
|
31
|
-
static tableName = 'proxy';
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Создать новый прокси
|
|
35
|
-
*/
|
|
36
|
-
static async create(data: ProxyData): Promise<number> {
|
|
37
|
-
const {
|
|
38
|
-
ip,
|
|
39
|
-
port,
|
|
40
|
-
username = null,
|
|
41
|
-
password = null,
|
|
42
|
-
secret = null,
|
|
43
|
-
socksType = null,
|
|
44
|
-
MTProxy = 0,
|
|
45
|
-
status = ProxyStatus.INACTIVE,
|
|
46
|
-
source: souurce = null,
|
|
47
|
-
last_check = null,
|
|
48
|
-
} = data;
|
|
49
|
-
|
|
50
|
-
const stmt = this.db.prepare(
|
|
51
|
-
`INSERT INTO ${this.tableName}
|
|
52
|
-
(ip, port, username, password, secret, socksType, MTProxy, status, souurce, last_check)
|
|
53
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
54
|
-
);
|
|
55
|
-
|
|
56
|
-
const result = stmt.run(ip, port, username, password, secret, socksType, MTProxy, status, souurce, last_check);
|
|
57
|
-
return result.lastInsertRowid as number;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
/**
|
|
61
|
-
* Найти прокси по ID
|
|
62
|
-
*/
|
|
63
|
-
static async findById(id: number): Promise<ProxyData | null> {
|
|
64
|
-
return this.queryOne(`SELECT * FROM ${this.tableName} WHERE id = ?`, [id]);
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* Найти прокси по IP и порту
|
|
69
|
-
*/
|
|
70
|
-
static async findByIpPort(ip: string, port: number): Promise<ProxyData | null> {
|
|
71
|
-
return this.queryOne(`SELECT * FROM ${this.tableName} WHERE ip = ? AND port = ?`, [ip, port]);
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Получить все активные прокси
|
|
76
|
-
*/
|
|
77
|
-
static async getActive(): Promise<ProxyData[]> {
|
|
78
|
-
return this.query(`SELECT * FROM ${this.tableName} WHERE status = ? ORDER BY last_check DESC`, [ProxyStatus.ACTIVE]);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
/**
|
|
82
|
-
* Получить все прокси
|
|
83
|
-
*/
|
|
84
|
-
static async getAll(): Promise<ProxyData[]> {
|
|
85
|
-
return this.query(`SELECT * FROM ${this.tableName} ORDER BY created_at DESC`);
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* Получить прокси по типу SOCKS
|
|
90
|
-
*/
|
|
91
|
-
static async getBySocksType(socksType: ProxySocksType): Promise<ProxyData[]> {
|
|
92
|
-
return this.query(`SELECT * FROM ${this.tableName} WHERE socksType = ? AND status = ? ORDER BY last_check DESC`,
|
|
93
|
-
[socksType, ProxyStatus.ACTIVE]);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Получить MTProxy прокси
|
|
98
|
-
*/
|
|
99
|
-
static async getMTProxies(): Promise<ProxyData[]> {
|
|
100
|
-
return this.query(`SELECT * FROM ${this.tableName} WHERE MTProxy = 1 AND status = ? ORDER BY last_check DESC`,
|
|
101
|
-
[ProxyStatus.ACTIVE]);
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Обновить статус прокси
|
|
106
|
-
*/
|
|
107
|
-
static async updateStatus(id: number, status: ProxyStatus): Promise<void> {
|
|
108
|
-
this.execute(`UPDATE ${this.tableName} SET status = ? WHERE id = ?`, [status, id]);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
/**
|
|
112
|
-
* Обновить время последней проверки
|
|
113
|
-
*/
|
|
114
|
-
static async updateLastCheck(id: number, lastCheck: string = new Date().toISOString()): Promise<void> {
|
|
115
|
-
this.execute(`UPDATE ${this.tableName} SET last_check = ? WHERE id = ?`, [lastCheck, id]);
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
/**
|
|
119
|
-
* Обновить данные прокси
|
|
120
|
-
*/
|
|
121
|
-
static async update(id: number, data: Partial<ProxyData>): Promise<void> {
|
|
122
|
-
const fields = Object.keys(data).filter(key => key !== 'id');
|
|
123
|
-
const values = fields.map(field => data[field as keyof ProxyData]);
|
|
124
|
-
|
|
125
|
-
if (fields.length === 0) return;
|
|
126
|
-
|
|
127
|
-
const setClause = fields.map(field => `${field} = ?`).join(', ');
|
|
128
|
-
this.execute(`UPDATE ${this.tableName} SET ${setClause} WHERE id = ?`, [...values, id]);
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Удалить прокси
|
|
133
|
-
*/
|
|
134
|
-
static async delete(id: number): Promise<void> {
|
|
135
|
-
this.execute(`DELETE FROM ${this.tableName} WHERE id = ?`, [id]);
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Проверить, существует ли прокси
|
|
140
|
-
*/
|
|
141
|
-
static async exists(ip: string, port: number): Promise<boolean> {
|
|
142
|
-
const result = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE ip = ? AND port = ?`, [ip, port]);
|
|
143
|
-
return result.count > 0;
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
/**
|
|
147
|
-
* Получить количество прокси по статусу
|
|
148
|
-
*/
|
|
149
|
-
static async getCountByStatus(status: ProxyStatus): Promise<number> {
|
|
150
|
-
const result = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [status]);
|
|
151
|
-
return result.count;
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
/**
|
|
155
|
-
* Получить случайный активный прокси
|
|
156
|
-
*/
|
|
157
|
-
static async getRandomActive(): Promise<ProxyData | null> {
|
|
158
|
-
return this.queryOne(`SELECT * FROM ${this.tableName} WHERE status = ? ORDER BY RANDOM() LIMIT 1`, [ProxyStatus.ACTIVE]);
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
/**
|
|
162
|
-
* Массовое добавление прокси
|
|
163
|
-
*/
|
|
164
|
-
static async bulkCreate(proxies: ProxyData[]): Promise<number[]> {
|
|
165
|
-
const ids: number[] = [];
|
|
166
|
-
|
|
167
|
-
this.transaction(() => {
|
|
168
|
-
for (const proxy of proxies) {
|
|
169
|
-
const id = this.create(proxy);
|
|
170
|
-
ids.push(id as any);
|
|
171
|
-
}
|
|
172
|
-
});
|
|
173
|
-
|
|
174
|
-
return ids;
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
/**
|
|
178
|
-
* Деактивировать все прокси
|
|
179
|
-
*/
|
|
180
|
-
static async deactivateAll(): Promise<void> {
|
|
181
|
-
this.execute(`UPDATE ${this.tableName} SET status = ?`, [ProxyStatus.INACTIVE]);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
/**
|
|
185
|
-
* Поиск прокси по источнику
|
|
186
|
-
*/
|
|
187
|
-
static async findBySource(source: string): Promise<ProxyData[]> {
|
|
188
|
-
return this.query(`SELECT * FROM ${this.tableName} WHERE souurce = ? ORDER BY created_at DESC`, [source]);
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Получить статистику по прокси
|
|
193
|
-
*/
|
|
194
|
-
static async getStats(): Promise<{
|
|
195
|
-
total: number;
|
|
196
|
-
active: number;
|
|
197
|
-
inactive: number;
|
|
198
|
-
socks4: number;
|
|
199
|
-
socks5: number;
|
|
200
|
-
mtproxy: number;
|
|
201
|
-
}> {
|
|
202
|
-
const total = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName}`).count;
|
|
203
|
-
const active = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [ProxyStatus.ACTIVE]).count;
|
|
204
|
-
const inactive = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [ProxyStatus.INACTIVE]).count;
|
|
205
|
-
const socks4 = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE socksType = ?`, [ProxySocksType.SOCKS4]).count;
|
|
206
|
-
const socks5 = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE socksType = ?`, [ProxySocksType.SOCKS5]).count;
|
|
207
|
-
const mtproxy = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE MTProxy = 1`).count;
|
|
208
|
-
|
|
209
|
-
return {
|
|
210
|
-
total,
|
|
211
|
-
active,
|
|
212
|
-
inactive,
|
|
213
|
-
socks4,
|
|
214
|
-
socks5,
|
|
215
|
-
mtproxy
|
|
216
|
-
};
|
|
217
|
-
}
|
|
1
|
+
import { Model } from "./Model";
|
|
2
|
+
|
|
3
|
+
export interface ProxyData {
|
|
4
|
+
/** PRIMARY KEY */
|
|
5
|
+
id?: number;
|
|
6
|
+
ip: string;
|
|
7
|
+
port: number;
|
|
8
|
+
username?: string | null;
|
|
9
|
+
password?: string | null;
|
|
10
|
+
secret?: string | null;
|
|
11
|
+
socksType?: number | null; // 4 or 5
|
|
12
|
+
MTProxy?: number; // 0 or 1
|
|
13
|
+
status?: number; // 0 = inactive, 1 = active
|
|
14
|
+
source?: string | null; // Note: keeping original typo from migration
|
|
15
|
+
last_check?: string | null;
|
|
16
|
+
created_at?: string;
|
|
17
|
+
updated_at?: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export enum ProxyStatus {
|
|
21
|
+
INACTIVE = 0,
|
|
22
|
+
ACTIVE = 1
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export enum ProxySocksType {
|
|
26
|
+
SOCKS4 = 4,
|
|
27
|
+
SOCKS5 = 5
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export class Proxy extends Model {
|
|
31
|
+
static tableName = 'proxy';
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Создать новый прокси
|
|
35
|
+
*/
|
|
36
|
+
static async create(data: ProxyData): Promise<number> {
|
|
37
|
+
const {
|
|
38
|
+
ip,
|
|
39
|
+
port,
|
|
40
|
+
username = null,
|
|
41
|
+
password = null,
|
|
42
|
+
secret = null,
|
|
43
|
+
socksType = null,
|
|
44
|
+
MTProxy = 0,
|
|
45
|
+
status = ProxyStatus.INACTIVE,
|
|
46
|
+
source: souurce = null,
|
|
47
|
+
last_check = null,
|
|
48
|
+
} = data;
|
|
49
|
+
|
|
50
|
+
const stmt = this.db.prepare(
|
|
51
|
+
`INSERT INTO ${this.tableName}
|
|
52
|
+
(ip, port, username, password, secret, socksType, MTProxy, status, souurce, last_check)
|
|
53
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
54
|
+
);
|
|
55
|
+
|
|
56
|
+
const result = stmt.run(ip, port, username, password, secret, socksType, MTProxy, status, souurce, last_check);
|
|
57
|
+
return result.lastInsertRowid as number;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Найти прокси по ID
|
|
62
|
+
*/
|
|
63
|
+
static async findById(id: number): Promise<ProxyData | null> {
|
|
64
|
+
return this.queryOne(`SELECT * FROM ${this.tableName} WHERE id = ?`, [id]);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Найти прокси по IP и порту
|
|
69
|
+
*/
|
|
70
|
+
static async findByIpPort(ip: string, port: number): Promise<ProxyData | null> {
|
|
71
|
+
return this.queryOne(`SELECT * FROM ${this.tableName} WHERE ip = ? AND port = ?`, [ip, port]);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Получить все активные прокси
|
|
76
|
+
*/
|
|
77
|
+
static async getActive(): Promise<ProxyData[]> {
|
|
78
|
+
return this.query(`SELECT * FROM ${this.tableName} WHERE status = ? ORDER BY last_check DESC`, [ProxyStatus.ACTIVE]);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Получить все прокси
|
|
83
|
+
*/
|
|
84
|
+
static async getAll(): Promise<ProxyData[]> {
|
|
85
|
+
return this.query(`SELECT * FROM ${this.tableName} ORDER BY created_at DESC`);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Получить прокси по типу SOCKS
|
|
90
|
+
*/
|
|
91
|
+
static async getBySocksType(socksType: ProxySocksType): Promise<ProxyData[]> {
|
|
92
|
+
return this.query(`SELECT * FROM ${this.tableName} WHERE socksType = ? AND status = ? ORDER BY last_check DESC`,
|
|
93
|
+
[socksType, ProxyStatus.ACTIVE]);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Получить MTProxy прокси
|
|
98
|
+
*/
|
|
99
|
+
static async getMTProxies(): Promise<ProxyData[]> {
|
|
100
|
+
return this.query(`SELECT * FROM ${this.tableName} WHERE MTProxy = 1 AND status = ? ORDER BY last_check DESC`,
|
|
101
|
+
[ProxyStatus.ACTIVE]);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Обновить статус прокси
|
|
106
|
+
*/
|
|
107
|
+
static async updateStatus(id: number, status: ProxyStatus): Promise<void> {
|
|
108
|
+
this.execute(`UPDATE ${this.tableName} SET status = ? WHERE id = ?`, [status, id]);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Обновить время последней проверки
|
|
113
|
+
*/
|
|
114
|
+
static async updateLastCheck(id: number, lastCheck: string = new Date().toISOString()): Promise<void> {
|
|
115
|
+
this.execute(`UPDATE ${this.tableName} SET last_check = ? WHERE id = ?`, [lastCheck, id]);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Обновить данные прокси
|
|
120
|
+
*/
|
|
121
|
+
static async update(id: number, data: Partial<ProxyData>): Promise<void> {
|
|
122
|
+
const fields = Object.keys(data).filter(key => key !== 'id');
|
|
123
|
+
const values = fields.map(field => data[field as keyof ProxyData]);
|
|
124
|
+
|
|
125
|
+
if (fields.length === 0) return;
|
|
126
|
+
|
|
127
|
+
const setClause = fields.map(field => `${field} = ?`).join(', ');
|
|
128
|
+
this.execute(`UPDATE ${this.tableName} SET ${setClause} WHERE id = ?`, [...values, id]);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Удалить прокси
|
|
133
|
+
*/
|
|
134
|
+
static async delete(id: number): Promise<void> {
|
|
135
|
+
this.execute(`DELETE FROM ${this.tableName} WHERE id = ?`, [id]);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Проверить, существует ли прокси
|
|
140
|
+
*/
|
|
141
|
+
static async exists(ip: string, port: number): Promise<boolean> {
|
|
142
|
+
const result = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE ip = ? AND port = ?`, [ip, port]);
|
|
143
|
+
return result.count > 0;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Получить количество прокси по статусу
|
|
148
|
+
*/
|
|
149
|
+
static async getCountByStatus(status: ProxyStatus): Promise<number> {
|
|
150
|
+
const result = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [status]);
|
|
151
|
+
return result.count;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Получить случайный активный прокси
|
|
156
|
+
*/
|
|
157
|
+
static async getRandomActive(): Promise<ProxyData | null> {
|
|
158
|
+
return this.queryOne(`SELECT * FROM ${this.tableName} WHERE status = ? ORDER BY RANDOM() LIMIT 1`, [ProxyStatus.ACTIVE]);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/**
|
|
162
|
+
* Массовое добавление прокси
|
|
163
|
+
*/
|
|
164
|
+
static async bulkCreate(proxies: ProxyData[]): Promise<number[]> {
|
|
165
|
+
const ids: number[] = [];
|
|
166
|
+
|
|
167
|
+
this.transaction(() => {
|
|
168
|
+
for (const proxy of proxies) {
|
|
169
|
+
const id = this.create(proxy);
|
|
170
|
+
ids.push(id as any);
|
|
171
|
+
}
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
return ids;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Деактивировать все прокси
|
|
179
|
+
*/
|
|
180
|
+
static async deactivateAll(): Promise<void> {
|
|
181
|
+
this.execute(`UPDATE ${this.tableName} SET status = ?`, [ProxyStatus.INACTIVE]);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Поиск прокси по источнику
|
|
186
|
+
*/
|
|
187
|
+
static async findBySource(source: string): Promise<ProxyData[]> {
|
|
188
|
+
return this.query(`SELECT * FROM ${this.tableName} WHERE souurce = ? ORDER BY created_at DESC`, [source]);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Получить статистику по прокси
|
|
193
|
+
*/
|
|
194
|
+
static async getStats(): Promise<{
|
|
195
|
+
total: number;
|
|
196
|
+
active: number;
|
|
197
|
+
inactive: number;
|
|
198
|
+
socks4: number;
|
|
199
|
+
socks5: number;
|
|
200
|
+
mtproxy: number;
|
|
201
|
+
}> {
|
|
202
|
+
const total = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName}`).count;
|
|
203
|
+
const active = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [ProxyStatus.ACTIVE]).count;
|
|
204
|
+
const inactive = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE status = ?`, [ProxyStatus.INACTIVE]).count;
|
|
205
|
+
const socks4 = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE socksType = ?`, [ProxySocksType.SOCKS4]).count;
|
|
206
|
+
const socks5 = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE socksType = ?`, [ProxySocksType.SOCKS5]).count;
|
|
207
|
+
const mtproxy = this.queryOne(`SELECT COUNT(*) as count FROM ${this.tableName} WHERE MTProxy = 1`).count;
|
|
208
|
+
|
|
209
|
+
return {
|
|
210
|
+
total,
|
|
211
|
+
active,
|
|
212
|
+
inactive,
|
|
213
|
+
socks4,
|
|
214
|
+
socks5,
|
|
215
|
+
mtproxy
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
218
|
}
|