@2byte/tgbot-framework 1.0.2 → 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.
- package/bin/2byte-cli.ts +13 -0
- package/package.json +6 -1
- package/src/cli/GenerateCommand.ts +93 -9
- package/src/cli/TgAccountManager.ts +50 -0
- package/src/core/ApiService.ts +21 -0
- package/src/core/ApiServiceManager.ts +63 -0
- package/src/core/App.ts +133 -32
- package/src/illumination/InlineKeyboard.ts +2 -1
- package/src/illumination/Message2Byte.ts +2 -1
- package/src/illumination/Message2ByteLiveProgressive.ts +2 -2
- package/src/illumination/Section.ts +1 -1
- package/src/index.ts +9 -0
- package/src/libs/TelegramAccountControl.ts +409 -7
- package/src/libs/TgSender.ts +53 -0
- package/src/models/Model.ts +67 -0
- package/src/models/Proxy.ts +218 -0
- package/src/models/TgAccount.ts +362 -0
- package/src/models/index.ts +3 -0
- package/src/types.ts +6 -1
- package/src/user/UserModel.ts +9 -0
- package/src/workflow/services/MassSendApiService.ts +80 -0
- package/templates/bot/.env.example +6 -1
- package/templates/bot/bot.ts +6 -1
- package/templates/bot/database/migrations/007_proxy.sql +27 -0
- package/templates/bot/database/migrations/008_tg_accounts.sql +32 -0
- package/templates/bot/docs/CLI_SERVICES.md +536 -0
- package/templates/bot/docs/INPUT_SYSTEM.md +211 -0
- package/templates/bot/docs/SERVICE_EXAMPLES.md +384 -0
- package/templates/bot/docs/TASK_SYSTEM.md +156 -0
- package/templates/bot/models/Model.ts +7 -0
- package/templates/bot/models/index.ts +2 -0
- package/templates/bot/sectionList.ts +4 -2
- package/templates/bot/sections/ExampleInputSection.ts +85 -0
- package/templates/bot/sections/ExampleLiveTaskerSection.ts +60 -0
- package/templates/bot/sections/HomeSection.ts +10 -10
- package/templates/bot/workflow/services/ExampleService.ts +24 -0
|
@@ -191,6 +191,7 @@ export default class Message2byte {
|
|
|
191
191
|
}
|
|
192
192
|
|
|
193
193
|
async send() {
|
|
194
|
+
// console.log("Sending message:", this.messageValue, ' Extra:', this.messageExtra, 'IsUpdate:', this.isUpdate);
|
|
194
195
|
if (this.isUpdate) {
|
|
195
196
|
if (this.section.route.runIsCallbackQuery && this.doAnswerCbQuery) {
|
|
196
197
|
await this.ctx.answerCbQuery();
|
|
@@ -225,7 +226,7 @@ export default class Message2byte {
|
|
|
225
226
|
if (typeof messageEntity === "object" && 'message_id' in messageEntity) {
|
|
226
227
|
this.messageId = messageEntity.message_id as number;
|
|
227
228
|
}
|
|
228
|
-
|
|
229
|
+
|
|
229
230
|
return messageEntity;
|
|
230
231
|
}
|
|
231
232
|
}
|
|
@@ -148,7 +148,7 @@ export default class Message2ByteLiveProgressive {
|
|
|
148
148
|
/**
|
|
149
149
|
* Останавливает прогрессбар
|
|
150
150
|
*/
|
|
151
|
-
stopSleepProgress(): this {
|
|
151
|
+
async stopSleepProgress(): Promise<this> {
|
|
152
152
|
if (this.progressBarTimer) {
|
|
153
153
|
clearInterval(this.progressBarTimer);
|
|
154
154
|
this.progressBarTimer = undefined;
|
|
@@ -162,7 +162,7 @@ export default class Message2ByteLiveProgressive {
|
|
|
162
162
|
}
|
|
163
163
|
this.activeProgressItem = undefined;
|
|
164
164
|
this.updateMessage();
|
|
165
|
-
this.message2bytePool.send();
|
|
165
|
+
await this.message2bytePool.send();
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
return this;
|
|
@@ -355,7 +355,7 @@ export class Section {
|
|
|
355
355
|
return params;
|
|
356
356
|
}
|
|
357
357
|
|
|
358
|
-
makeInlineKeyboard(buttons: any[][]): InlineKeyboard {
|
|
358
|
+
makeInlineKeyboard(buttons: any[][] = []): InlineKeyboard {
|
|
359
359
|
const keyboard = InlineKeyboard.init(this.ctx, this);
|
|
360
360
|
buttons.forEach((row) => {
|
|
361
361
|
keyboard.append(row);
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
// Core classes
|
|
2
2
|
export { App } from './core/App';
|
|
3
|
+
export { ApiService } from './core/ApiService';
|
|
4
|
+
export { ApiServiceManager } from './core/ApiServiceManager';
|
|
3
5
|
export { BotArtisan } from './core/BotArtisan';
|
|
4
6
|
export { BotMigration } from './core/BotMigration';
|
|
5
7
|
export { BotSeeder } from './core/BotSeeder';
|
|
@@ -24,10 +26,17 @@ export * from './types';
|
|
|
24
26
|
export { CreateBotCommand } from './cli/CreateBotCommand';
|
|
25
27
|
export { InitCommand } from './cli/InitCommand';
|
|
26
28
|
export { GenerateCommand } from './cli/GenerateCommand';
|
|
29
|
+
export { manualAdderTgAccount } from './cli/TgAccountManager';
|
|
30
|
+
|
|
31
|
+
// Model exports
|
|
32
|
+
export * from './models';
|
|
27
33
|
|
|
28
34
|
// User exports
|
|
29
35
|
export { UserModel } from './user/UserModel';
|
|
30
36
|
export { UserStore } from './user/UserStore';
|
|
31
37
|
|
|
38
|
+
export * from './libs/TelegramAccountControl';
|
|
39
|
+
export { TgSender } from './libs/TgSender';
|
|
40
|
+
|
|
32
41
|
// Type exports
|
|
33
42
|
export * from './types';
|
|
@@ -3,11 +3,13 @@ import { StringSession } from "telegram/sessions";
|
|
|
3
3
|
import fs from "fs";
|
|
4
4
|
import { TelegramClientParams } from "telegram/client/telegramBaseClient";
|
|
5
5
|
import { ProxyInterface } from "telegram/network/connection/TCPMTProxy";
|
|
6
|
+
import type { Database } from "bun:sqlite";
|
|
7
|
+
import { TgAccount, Proxy, TgAccountStatus, ProxyStatus } from '../models';
|
|
6
8
|
|
|
7
|
-
interface
|
|
9
|
+
interface TelegramAccountRemoteInit {
|
|
8
10
|
appId: string;
|
|
9
11
|
appHash: string;
|
|
10
|
-
credetialsManager:
|
|
12
|
+
credetialsManager: TelegramManagerCredentialsDriver;
|
|
11
13
|
}
|
|
12
14
|
|
|
13
15
|
type TelegramClientProxy = {
|
|
@@ -38,6 +40,8 @@ interface TelegramCredentialsManagerInit {
|
|
|
38
40
|
pathFileCounterOffset: string;
|
|
39
41
|
}
|
|
40
42
|
|
|
43
|
+
export type TelegramManagerCredentialsDriver = TelegramManagerCredentials | TelegramManagerCredentialsDB;
|
|
44
|
+
|
|
41
45
|
export class TelegramManagerCredentials {
|
|
42
46
|
private requiredProxyForCredentials: boolean = false;
|
|
43
47
|
private credentials: TelegramCredentials[] = [];
|
|
@@ -219,7 +223,11 @@ export class TelegramManagerCredentials {
|
|
|
219
223
|
* Add or update credential
|
|
220
224
|
*/
|
|
221
225
|
addCredential(credential: TelegramCredentials): void {
|
|
222
|
-
|
|
226
|
+
// replace +
|
|
227
|
+
if (credential.phone.includes('+')) {
|
|
228
|
+
credential.phone = credential.phone.replace('+', '');
|
|
229
|
+
}
|
|
230
|
+
|
|
223
231
|
// Attach proxy to credential
|
|
224
232
|
if (this.requiredProxyForCredentials && !credential.proxy) {
|
|
225
233
|
try {
|
|
@@ -327,15 +335,15 @@ export class TelegramManagerCredentials {
|
|
|
327
335
|
}
|
|
328
336
|
|
|
329
337
|
export class TelegramAccountRemote {
|
|
330
|
-
private initOptions:
|
|
338
|
+
private initOptions: TelegramAccountRemoteInit;
|
|
331
339
|
private tgClient!: TelegramClient; // Используем definite assignment assertion
|
|
332
|
-
private credentialsManager:
|
|
340
|
+
private credentialsManager: TelegramManagerCredentialsDriver;
|
|
333
341
|
|
|
334
|
-
static init(initOptions:
|
|
342
|
+
static init(initOptions: TelegramAccountRemoteInit) {
|
|
335
343
|
return new TelegramAccountRemote(initOptions);
|
|
336
344
|
}
|
|
337
345
|
|
|
338
|
-
constructor(initOptions:
|
|
346
|
+
constructor(initOptions: TelegramAccountRemoteInit) {
|
|
339
347
|
this.initOptions = initOptions;
|
|
340
348
|
this.credentialsManager = initOptions.credetialsManager;
|
|
341
349
|
}
|
|
@@ -736,3 +744,397 @@ export class TelegramAccountRemote {
|
|
|
736
744
|
}
|
|
737
745
|
}
|
|
738
746
|
}
|
|
747
|
+
|
|
748
|
+
/**
|
|
749
|
+
* Менеджер учетных данных Telegram с поддержкой базы данных SQLite
|
|
750
|
+
*/
|
|
751
|
+
export class TelegramManagerCredentialsDB {
|
|
752
|
+
private database: Database;
|
|
753
|
+
private requiredProxyForCredentials: boolean = false;
|
|
754
|
+
|
|
755
|
+
// Локальные импорты для избежания конфликтов путей
|
|
756
|
+
private TgAccount: any;
|
|
757
|
+
private Proxy: any;
|
|
758
|
+
private TgAccountStatus: any;
|
|
759
|
+
private ProxyStatus: any;
|
|
760
|
+
|
|
761
|
+
constructor(database: Database) {
|
|
762
|
+
this.database = database;
|
|
763
|
+
|
|
764
|
+
// Динамический импорт моделей
|
|
765
|
+
this.initModels();
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
private async initModels() {
|
|
769
|
+
this.TgAccount = TgAccount;
|
|
770
|
+
this.Proxy = Proxy;
|
|
771
|
+
this.TgAccountStatus = TgAccountStatus;
|
|
772
|
+
this.ProxyStatus = ProxyStatus;
|
|
773
|
+
|
|
774
|
+
// Устанавливаем базу данных для моделей
|
|
775
|
+
this.TgAccount.setDatabase(this.database);
|
|
776
|
+
this.Proxy.setDatabase(this.database);
|
|
777
|
+
}
|
|
778
|
+
|
|
779
|
+
/**
|
|
780
|
+
* Включить/выключить обязательное использование прокси для учетных данных
|
|
781
|
+
*/
|
|
782
|
+
setRequiredProxyForCredentials(required: boolean): void {
|
|
783
|
+
this.requiredProxyForCredentials = required;
|
|
784
|
+
}
|
|
785
|
+
|
|
786
|
+
/**
|
|
787
|
+
* Преобразовать данные прокси из БД в формат TelegramClientProxy
|
|
788
|
+
*/
|
|
789
|
+
private convertProxyToTelegramFormat(proxyData: any): TelegramClientProxy {
|
|
790
|
+
return {
|
|
791
|
+
ip: proxyData.ip,
|
|
792
|
+
port: proxyData.port,
|
|
793
|
+
username: proxyData.username || undefined,
|
|
794
|
+
password: proxyData.password || undefined,
|
|
795
|
+
secret: proxyData.secret || undefined,
|
|
796
|
+
socksType: (proxyData.socksType === 4 || proxyData.socksType === 5) ? proxyData.socksType as 4 | 5 : 5,
|
|
797
|
+
MTProxy: proxyData.MTProxy === 1
|
|
798
|
+
};
|
|
799
|
+
}
|
|
800
|
+
|
|
801
|
+
/**
|
|
802
|
+
* Получить следующий доступный прокси из БД
|
|
803
|
+
*/
|
|
804
|
+
private async getNextProxy(): Promise<TelegramClientProxy> {
|
|
805
|
+
const proxy = await this.Proxy.getRandomActive();
|
|
806
|
+
if (!proxy) {
|
|
807
|
+
throw new Error("Нет доступных прокси в базе данных");
|
|
808
|
+
}
|
|
809
|
+
|
|
810
|
+
return this.convertProxyToTelegramFormat(proxy);
|
|
811
|
+
}
|
|
812
|
+
|
|
813
|
+
/**
|
|
814
|
+
* Добавить или обновить учетные данные
|
|
815
|
+
*/
|
|
816
|
+
async addCredential(credential: TelegramCredentials): Promise<void> {
|
|
817
|
+
let proxyId: number | null = null;
|
|
818
|
+
|
|
819
|
+
// Назначить прокси если требуется
|
|
820
|
+
if (this.requiredProxyForCredentials && !credential.proxy) {
|
|
821
|
+
try {
|
|
822
|
+
const proxy = await this.getNextProxy();
|
|
823
|
+
credential.proxy = proxy;
|
|
824
|
+
|
|
825
|
+
// Найти ID прокси в БД
|
|
826
|
+
const proxyData = await this.Proxy.findByIpPort(proxy.ip, proxy.port);
|
|
827
|
+
if (proxyData) {
|
|
828
|
+
proxyId = proxyData.id!;
|
|
829
|
+
}
|
|
830
|
+
} catch (error) {
|
|
831
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
832
|
+
throw new Error(`Не удалось назначить прокси: ${errorMessage}`);
|
|
833
|
+
}
|
|
834
|
+
} else if (credential.proxy) {
|
|
835
|
+
// Найти существующий прокси или создать новый
|
|
836
|
+
let proxyData = await this.Proxy.findByIpPort(credential.proxy.ip, credential.proxy.port);
|
|
837
|
+
if (!proxyData) {
|
|
838
|
+
// Создать новый прокси в БД
|
|
839
|
+
proxyId = await this.Proxy.create({
|
|
840
|
+
ip: credential.proxy.ip,
|
|
841
|
+
port: credential.proxy.port,
|
|
842
|
+
username: credential.proxy.username || null,
|
|
843
|
+
password: credential.proxy.password || null,
|
|
844
|
+
secret: credential.proxy.secret || null,
|
|
845
|
+
socksType: credential.proxy.socksType || null,
|
|
846
|
+
MTProxy: credential.proxy.MTProxy ? 1 : 0,
|
|
847
|
+
status: this.ProxyStatus.ACTIVE
|
|
848
|
+
});
|
|
849
|
+
} else {
|
|
850
|
+
proxyId = proxyData.id!;
|
|
851
|
+
}
|
|
852
|
+
}
|
|
853
|
+
|
|
854
|
+
// Используем upsert для вставки или обновления
|
|
855
|
+
await this.TgAccount.upsert({
|
|
856
|
+
phone: credential.phone.replace('+', ''),
|
|
857
|
+
session: credential.session || null,
|
|
858
|
+
password: credential.password || null,
|
|
859
|
+
proxy_id: proxyId,
|
|
860
|
+
status: credential.session ? this.TgAccountStatus.ACTIVE : this.TgAccountStatus.INACTIVE
|
|
861
|
+
});
|
|
862
|
+
}
|
|
863
|
+
|
|
864
|
+
/**
|
|
865
|
+
* Получить учетные данные по номеру телефона
|
|
866
|
+
*/
|
|
867
|
+
async getCredential(phone: string): Promise<TelegramCredentials | null> {
|
|
868
|
+
const accountData = await this.TgAccount.findWithProxy(phone);
|
|
869
|
+
if (!accountData) {
|
|
870
|
+
return null;
|
|
871
|
+
}
|
|
872
|
+
|
|
873
|
+
let proxy: TelegramClientProxy | undefined;
|
|
874
|
+
if (accountData.proxy_id && accountData.proxy_ip) {
|
|
875
|
+
proxy = {
|
|
876
|
+
ip: accountData.proxy_ip,
|
|
877
|
+
port: accountData.proxy_port,
|
|
878
|
+
username: accountData.proxy_username || undefined,
|
|
879
|
+
password: accountData.proxy_password || undefined,
|
|
880
|
+
secret: accountData.proxy_secret || undefined,
|
|
881
|
+
socksType: (accountData.proxy_socksType === 4 || accountData.proxy_socksType === 5) ?
|
|
882
|
+
accountData.proxy_socksType as 4 | 5 : 5,
|
|
883
|
+
MTProxy: accountData.proxy_MTProxy === 1
|
|
884
|
+
};
|
|
885
|
+
}
|
|
886
|
+
|
|
887
|
+
return {
|
|
888
|
+
phone: accountData.phone,
|
|
889
|
+
session: accountData.session || undefined,
|
|
890
|
+
password: accountData.password || undefined,
|
|
891
|
+
proxy
|
|
892
|
+
};
|
|
893
|
+
}
|
|
894
|
+
|
|
895
|
+
/**
|
|
896
|
+
* Удалить учетные данные по номеру телефона
|
|
897
|
+
*/
|
|
898
|
+
async deleteCredential(phone: string): Promise<boolean> {
|
|
899
|
+
const account = await this.TgAccount.findByPhone(phone);
|
|
900
|
+
if (account) {
|
|
901
|
+
await this.TgAccount.delete(phone);
|
|
902
|
+
return true;
|
|
903
|
+
}
|
|
904
|
+
return false;
|
|
905
|
+
}
|
|
906
|
+
|
|
907
|
+
/**
|
|
908
|
+
* Получить все учетные данные
|
|
909
|
+
*/
|
|
910
|
+
async getAllCredentials(): Promise<TelegramCredentials[]> {
|
|
911
|
+
const accounts = await this.TgAccount.getAllWithProxy();
|
|
912
|
+
const credentials: TelegramCredentials[] = [];
|
|
913
|
+
|
|
914
|
+
for (const account of accounts) {
|
|
915
|
+
let proxy: TelegramClientProxy | undefined;
|
|
916
|
+
|
|
917
|
+
if (account.proxy_id && account.proxy_ip) {
|
|
918
|
+
proxy = {
|
|
919
|
+
ip: account.proxy_ip,
|
|
920
|
+
port: account.proxy_port,
|
|
921
|
+
username: account.proxy_username || undefined,
|
|
922
|
+
password: account.proxy_password || undefined,
|
|
923
|
+
secret: account.proxy_secret || undefined,
|
|
924
|
+
socksType: (account.proxy_socksType === 4 || account.proxy_socksType === 5) ?
|
|
925
|
+
account.proxy_socksType as 4 | 5 : 5,
|
|
926
|
+
MTProxy: account.proxy_MTProxy === 1
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
credentials.push({
|
|
931
|
+
phone: account.phone,
|
|
932
|
+
session: account.session || undefined,
|
|
933
|
+
password: account.password || undefined,
|
|
934
|
+
proxy
|
|
935
|
+
});
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
return credentials;
|
|
939
|
+
}
|
|
940
|
+
|
|
941
|
+
/**
|
|
942
|
+
* Проверить, существуют ли учетные данные
|
|
943
|
+
*/
|
|
944
|
+
async hasCredential(phone: string): Promise<boolean> {
|
|
945
|
+
return await this.TgAccount.exists(phone);
|
|
946
|
+
}
|
|
947
|
+
|
|
948
|
+
/**
|
|
949
|
+
* Обновить сессию для существующего аккаунта
|
|
950
|
+
*/
|
|
951
|
+
async updateSession(phone: string, session: string): Promise<boolean> {
|
|
952
|
+
const account = await this.TgAccount.findByPhone(phone);
|
|
953
|
+
if (account) {
|
|
954
|
+
await this.TgAccount.updateSession(phone, session);
|
|
955
|
+
await this.TgAccount.updateStatus(phone, this.TgAccountStatus.ACTIVE);
|
|
956
|
+
return true;
|
|
957
|
+
}
|
|
958
|
+
return false;
|
|
959
|
+
}
|
|
960
|
+
|
|
961
|
+
/**
|
|
962
|
+
* Получить количество учетных данных
|
|
963
|
+
*/
|
|
964
|
+
async getCredentialsCount(): Promise<number> {
|
|
965
|
+
const stats = await this.TgAccount.getStats();
|
|
966
|
+
return stats.total;
|
|
967
|
+
}
|
|
968
|
+
|
|
969
|
+
/**
|
|
970
|
+
* Очистить все учетные данные
|
|
971
|
+
*/
|
|
972
|
+
async clearAllCredentials(): Promise<void> {
|
|
973
|
+
const accounts = await this.TgAccount.getAll();
|
|
974
|
+
for (const account of accounts) {
|
|
975
|
+
await this.TgAccount.delete(account.id!);
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
|
|
979
|
+
/**
|
|
980
|
+
* Получить учетные данные по прокси
|
|
981
|
+
*/
|
|
982
|
+
async getCredentialsByProxy(proxyHost: string, proxyPort: number): Promise<TelegramCredentials[]> {
|
|
983
|
+
const proxy = await this.Proxy.findByIpPort(proxyHost, proxyPort);
|
|
984
|
+
if (!proxy) {
|
|
985
|
+
return [];
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
const accounts = await this.TgAccount.getAll();
|
|
989
|
+
const credentials: TelegramCredentials[] = [];
|
|
990
|
+
|
|
991
|
+
for (const account of accounts) {
|
|
992
|
+
if (account.proxy_id === proxy.id) {
|
|
993
|
+
const proxyData = this.convertProxyToTelegramFormat(proxy);
|
|
994
|
+
credentials.push({
|
|
995
|
+
phone: account.phone,
|
|
996
|
+
session: account.session || undefined,
|
|
997
|
+
password: account.password || undefined,
|
|
998
|
+
proxy: proxyData
|
|
999
|
+
});
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
|
|
1003
|
+
return credentials;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1006
|
+
/**
|
|
1007
|
+
* Получить учетные данные без прокси
|
|
1008
|
+
*/
|
|
1009
|
+
async getCredentialsWithoutProxy(): Promise<TelegramCredentials[]> {
|
|
1010
|
+
const accounts = await this.TgAccount.getWithoutProxy();
|
|
1011
|
+
|
|
1012
|
+
return accounts.map(account => ({
|
|
1013
|
+
phone: account.phone,
|
|
1014
|
+
session: account.session || undefined,
|
|
1015
|
+
password: account.password || undefined
|
|
1016
|
+
}));
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* Получить активные учетные данные
|
|
1021
|
+
*/
|
|
1022
|
+
async getActiveCredentials(): Promise<TelegramCredentials[]> {
|
|
1023
|
+
const accounts = await this.TgAccount.getByStatus(this.TgAccountStatus.ACTIVE);
|
|
1024
|
+
const credentials: TelegramCredentials[] = [];
|
|
1025
|
+
|
|
1026
|
+
for (const account of accounts) {
|
|
1027
|
+
let proxy: TelegramClientProxy | undefined;
|
|
1028
|
+
|
|
1029
|
+
if (account.proxy_id) {
|
|
1030
|
+
const proxyData = await this.Proxy.findById(account.proxy_id);
|
|
1031
|
+
if (proxyData && proxyData.status === this.ProxyStatus.ACTIVE) {
|
|
1032
|
+
proxy = this.convertProxyToTelegramFormat(proxyData);
|
|
1033
|
+
}
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
credentials.push({
|
|
1037
|
+
phone: account.phone,
|
|
1038
|
+
session: account.session || undefined,
|
|
1039
|
+
password: account.password || undefined,
|
|
1040
|
+
proxy
|
|
1041
|
+
});
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
return credentials;
|
|
1045
|
+
}
|
|
1046
|
+
|
|
1047
|
+
/**
|
|
1048
|
+
* Получить случайные активные учетные данные
|
|
1049
|
+
*/
|
|
1050
|
+
async getRandomActiveCredential(): Promise<TelegramCredentials | null> {
|
|
1051
|
+
const account = await this.TgAccount.getRandomAvailable();
|
|
1052
|
+
if (!account) {
|
|
1053
|
+
return null;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
let proxy: TelegramClientProxy | undefined;
|
|
1057
|
+
if (account.proxy_id) {
|
|
1058
|
+
const proxyData = await this.Proxy.findById(account.proxy_id);
|
|
1059
|
+
if (proxyData && proxyData.status === this.ProxyStatus.ACTIVE) {
|
|
1060
|
+
proxy = this.convertProxyToTelegramFormat(proxyData);
|
|
1061
|
+
}
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
return {
|
|
1065
|
+
phone: account.phone,
|
|
1066
|
+
session: account.session || undefined,
|
|
1067
|
+
password: account.password || undefined,
|
|
1068
|
+
proxy
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
/**
|
|
1073
|
+
* Обновить информацию о пользователе
|
|
1074
|
+
*/
|
|
1075
|
+
async updateUserInfo(phone: string, userInfo: {
|
|
1076
|
+
first_name?: string;
|
|
1077
|
+
last_name?: string;
|
|
1078
|
+
username?: string;
|
|
1079
|
+
user_id?: number;
|
|
1080
|
+
}): Promise<void> {
|
|
1081
|
+
await this.TgAccount.updateUserInfo(phone, userInfo);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
/**
|
|
1085
|
+
* Обновить статус аккаунта
|
|
1086
|
+
*/
|
|
1087
|
+
async updateAccountStatus(phone: string, status: any): Promise<void> {
|
|
1088
|
+
const account = await this.TgAccount.findByPhone(phone);
|
|
1089
|
+
if (account) {
|
|
1090
|
+
await this.TgAccount.updateStatus(account.id!, status);
|
|
1091
|
+
}
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* Отметить активность аккаунта
|
|
1096
|
+
*/
|
|
1097
|
+
async markActivity(phone: string): Promise<void> {
|
|
1098
|
+
const account = await this.TgAccount.findByPhone(phone);
|
|
1099
|
+
if (account) {
|
|
1100
|
+
await this.TgAccount.updateLastActivity(account.id!);
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
/**
|
|
1105
|
+
* Получить статистику
|
|
1106
|
+
*/
|
|
1107
|
+
async getStats(): Promise<{
|
|
1108
|
+
accounts: any;
|
|
1109
|
+
proxies: any;
|
|
1110
|
+
}> {
|
|
1111
|
+
const accountStats = await this.TgAccount.getStats();
|
|
1112
|
+
const proxyStats = await this.Proxy.getStats();
|
|
1113
|
+
|
|
1114
|
+
return {
|
|
1115
|
+
accounts: accountStats,
|
|
1116
|
+
proxies: proxyStats
|
|
1117
|
+
};
|
|
1118
|
+
}
|
|
1119
|
+
|
|
1120
|
+
/**
|
|
1121
|
+
* Добавить прокси в БД
|
|
1122
|
+
*/
|
|
1123
|
+
async addProxy(proxyData: any): Promise<number> {
|
|
1124
|
+
return await this.Proxy.create(proxyData);
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
/**
|
|
1128
|
+
* Получить все прокси
|
|
1129
|
+
*/
|
|
1130
|
+
async getAllProxies(): Promise<any[]> {
|
|
1131
|
+
return await this.Proxy.getAll();
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/**
|
|
1135
|
+
* Получить активные прокси
|
|
1136
|
+
*/
|
|
1137
|
+
async getActiveProxies(): Promise<any[]> {
|
|
1138
|
+
return await this.Proxy.getActive();
|
|
1139
|
+
}
|
|
1140
|
+
}
|
|
@@ -0,0 +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
|
+
}
|
|
@@ -0,0 +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
|
+
}
|