@2byte/tgbot-framework 1.0.0

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 (37) hide show
  1. package/README.md +301 -0
  2. package/bin/2byte-cli.ts +85 -0
  3. package/package.json +50 -0
  4. package/src/cli/CreateBotCommand.ts +182 -0
  5. package/src/cli/GenerateCommand.ts +112 -0
  6. package/src/cli/InitCommand.ts +108 -0
  7. package/src/console/migrate.ts +83 -0
  8. package/src/core/App.ts +1016 -0
  9. package/src/core/BotArtisan.ts +80 -0
  10. package/src/core/BotMigration.ts +31 -0
  11. package/src/core/BotSeeder.ts +67 -0
  12. package/src/core/utils.ts +3 -0
  13. package/src/illumination/Artisan.ts +149 -0
  14. package/src/illumination/InlineKeyboard.ts +44 -0
  15. package/src/illumination/Message2Byte.ts +254 -0
  16. package/src/illumination/Message2ByteLiveProgressive.ts +278 -0
  17. package/src/illumination/Message2bytePool.ts +108 -0
  18. package/src/illumination/Migration.ts +186 -0
  19. package/src/illumination/RunSectionRoute.ts +85 -0
  20. package/src/illumination/Section.ts +430 -0
  21. package/src/illumination/SectionComponent.ts +64 -0
  22. package/src/illumination/Telegraf2byteContext.ts +33 -0
  23. package/src/index.ts +33 -0
  24. package/src/libs/TelegramAccountControl.ts +523 -0
  25. package/src/types.ts +172 -0
  26. package/src/user/UserModel.ts +132 -0
  27. package/src/user/UserStore.ts +119 -0
  28. package/templates/bot/.env.example +18 -0
  29. package/templates/bot/artisan.ts +9 -0
  30. package/templates/bot/bot.ts +74 -0
  31. package/templates/bot/database/dbConnector.ts +5 -0
  32. package/templates/bot/database/migrate.ts +10 -0
  33. package/templates/bot/database/migrations/001_create_users.sql +17 -0
  34. package/templates/bot/database/seed.ts +15 -0
  35. package/templates/bot/package.json +31 -0
  36. package/templates/bot/sectionList.ts +7 -0
  37. package/templates/bot/sections/HomeSection.ts +63 -0
@@ -0,0 +1,523 @@
1
+ import { TelegramClient } from "telegram";
2
+ import { StringSession } from "telegram/sessions";
3
+ import fs from "fs";
4
+ import { TelegramClientParams } from "telegram/client/telegramBaseClient";
5
+ import { ProxyInterface } from "telegram/network/connection/TCPMTProxy";
6
+
7
+ interface TelegramRegistrarInit {
8
+ appId: string;
9
+ appHash: string;
10
+ credetialsManager: TelegramManagerCredentials;
11
+ }
12
+
13
+ type TelegramClientProxy = {
14
+ ip: string;
15
+ port: number;
16
+ username?: string;
17
+ password?: string;
18
+ secret?: string,
19
+ socksType?: 5 | 4;
20
+ MTProxy?: boolean;
21
+ } & ProxyInterface;
22
+
23
+ interface TelegramInitClient {
24
+ phone: string;
25
+ proxy?: any;
26
+ }
27
+
28
+ export interface TelegramCredentials {
29
+ phone: string;
30
+ proxy?: TelegramClientProxy;
31
+ password?: string;
32
+ session?: string;
33
+ }
34
+
35
+ interface TelegramCredentialsManagerInit {
36
+ pathFileStorage: string;
37
+ pathFileProxyList: string;
38
+ pathFileCounterOffset: string;
39
+ }
40
+
41
+ export class TelegramManagerCredentials {
42
+ private requiredProxyForCredentials: boolean = false;
43
+ private credentials: TelegramCredentials[] = [];
44
+ private proxyList: string[] = [];
45
+
46
+ static init(options: TelegramCredentialsManagerInit) {
47
+ return new TelegramManagerCredentials(options);
48
+ }
49
+
50
+ constructor(private options: TelegramCredentialsManagerInit) {
51
+ this.ensureStorageDirectory();
52
+ this.loadCredentials();
53
+ this.loadProxyList();
54
+ }
55
+
56
+ /**
57
+ * Ensure storage directory exists
58
+ */
59
+ private ensureStorageDirectory(): void {
60
+ const storageDir = this.options.pathFileStorage.split("/").slice(0, -1).join("/");
61
+ if (!fs.existsSync(storageDir)) {
62
+ fs.mkdirSync(storageDir, { recursive: true });
63
+ }
64
+ }
65
+
66
+ /**
67
+ * Load credentials from JSON file
68
+ */
69
+ private loadCredentials(): void {
70
+ try {
71
+ if (fs.existsSync(this.options.pathFileStorage)) {
72
+ const data = fs.readFileSync(this.options.pathFileStorage, "utf8");
73
+ this.credentials = JSON.parse(data) || [];
74
+ } else {
75
+ this.credentials = [];
76
+ this.saveCredentials();
77
+ }
78
+ } catch (error) {
79
+ console.error("Error loading credentials:", error);
80
+ this.credentials = [];
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Загрузить список прокси из файла
86
+ */
87
+ private loadProxyList(): void {
88
+ try {
89
+ if (fs.existsSync(this.options.pathFileProxyList)) {
90
+ const data = fs.readFileSync(this.options.pathFileProxyList, "utf8");
91
+ this.proxyList = data.split('\n').filter(line => line.trim() !== '');
92
+ } else {
93
+ this.proxyList = [];
94
+ }
95
+ } catch (error) {
96
+ console.error("Ошибка загрузки списка прокси:", error);
97
+ this.proxyList = [];
98
+ }
99
+ }
100
+
101
+ /**
102
+ * Получить текущий offset из файла счетчика
103
+ */
104
+ private getCurrentOffset(): number {
105
+ try {
106
+ if (fs.existsSync(this.options.pathFileCounterOffset)) {
107
+ const data = fs.readFileSync(this.options.pathFileCounterOffset, "utf8");
108
+ return parseInt(data.trim()) || 0;
109
+ }
110
+ return 0;
111
+ } catch (error) {
112
+ console.error("Ошибка чтения offset:", error);
113
+ return 0;
114
+ }
115
+ }
116
+
117
+ /**
118
+ * Сохранить новый offset в файл счетчика
119
+ */
120
+ private saveOffset(offset: number): void {
121
+ try {
122
+ fs.writeFileSync(this.options.pathFileCounterOffset, offset.toString(), "utf8");
123
+ } catch (error) {
124
+ console.error("Ошибка сохранения offset:", error);
125
+ throw new Error("Не удалось сохранить offset");
126
+ }
127
+ }
128
+
129
+ /**
130
+ * Парсинг строки прокси в формат TelegramClientProxy
131
+ * Поддерживаемые форматы:
132
+ * - socks4://username:password@host:port
133
+ * - socks5://username:password@host:port
134
+ * - http://username:password@host:port (будет использоваться как SOCKS5)
135
+ */
136
+ private parseProxyString(proxyString: string): TelegramClientProxy {
137
+ // Проверяем SOCKS4 формат
138
+ let match = proxyString.match(/^socks4:\/\/([^:]+):([^@]+)@([^:]+):(\d+)$/);
139
+ if (match) {
140
+ const [, username, password, ip, port] = match;
141
+ return {
142
+ ip,
143
+ port: parseInt(port),
144
+ username,
145
+ password,
146
+ socksType: 4
147
+ };
148
+ }
149
+
150
+ // Проверяем SOCKS5 формат
151
+ match = proxyString.match(/^socks5:\/\/([^:]+):([^@]+)@([^:]+):(\d+)$/);
152
+ if (match) {
153
+ const [, username, password, ip, port] = match;
154
+ return {
155
+ ip,
156
+ port: parseInt(port),
157
+ username,
158
+ password,
159
+ socksType: 5
160
+ };
161
+ }
162
+
163
+ // Проверяем HTTP формат (используем как SOCKS5)
164
+ match = proxyString.match(/^http:\/\/([^:]+):([^@]+)@([^:]+):(\d+)$/);
165
+ if (match) {
166
+ const [, username, password, ip, port] = match;
167
+ return {
168
+ ip,
169
+ port: parseInt(port),
170
+ username,
171
+ password,
172
+ socksType: 5 // HTTP прокси используем как SOCKS5
173
+ };
174
+ }
175
+
176
+ throw new Error(`Неверный формат прокси: ${proxyString}. Поддерживаемые форматы: socks4://, socks5://, http://`);
177
+ }
178
+
179
+ /**
180
+ * Получить следующий прокси из списка
181
+ */
182
+ private getNextProxy(): TelegramClientProxy {
183
+ if (this.proxyList.length === 0) {
184
+ throw new Error("Список прокси пуст");
185
+ }
186
+
187
+ const currentOffset = this.getCurrentOffset();
188
+
189
+ if (currentOffset >= this.proxyList.length) {
190
+ throw new Error("Прокси закончились");
191
+ }
192
+
193
+ const proxyString = this.proxyList[currentOffset];
194
+ const proxy = this.parseProxyString(proxyString);
195
+
196
+ // Увеличиваем offset для следующего использования
197
+ this.saveOffset(currentOffset + 1);
198
+
199
+ return proxy;
200
+ }
201
+
202
+ /**
203
+ * Save credentials to JSON file
204
+ */
205
+ private saveCredentials(): void {
206
+ try {
207
+ fs.writeFileSync(
208
+ this.options.pathFileStorage,
209
+ JSON.stringify(this.credentials, null, 2),
210
+ "utf8"
211
+ );
212
+ } catch (error) {
213
+ console.error("Error saving credentials:", error);
214
+ throw new Error("Failed to save credentials");
215
+ }
216
+ }
217
+
218
+ /**
219
+ * Add or update credential
220
+ */
221
+ addCredential(credential: TelegramCredentials): void {
222
+
223
+ // Attach proxy to credential
224
+ if (this.requiredProxyForCredentials && !credential.proxy) {
225
+ try {
226
+ credential.proxy = this.getNextProxy();
227
+ } catch (error) {
228
+ throw new Error(`Не удалось назначить прокси: ${error.message}`);
229
+ }
230
+ }
231
+
232
+ const existingIndex = this.credentials.findIndex((c) => c != null && c.phone === credential.phone);
233
+
234
+ if (existingIndex !== -1) {
235
+ // Update existing credential
236
+ this.credentials[existingIndex] = { ...this.credentials[existingIndex], ...credential };
237
+ } else {
238
+ // Add new credential
239
+ this.credentials.push(credential);
240
+ }
241
+
242
+ this.credentials = this.credentials.filter((c) => c != null);
243
+
244
+ this.saveCredentials();
245
+ }
246
+
247
+ /**
248
+ * Get credential by phone number
249
+ */
250
+ getCredential(phone: string): TelegramCredentials | null {
251
+ return this.credentials.find((c) => c != null && c.phone === phone) || null;
252
+ }
253
+
254
+ /**
255
+ * Delete credential by phone number
256
+ */
257
+ deleteCredential(phone: string): boolean {
258
+ const initialLength = this.credentials.length;
259
+ this.credentials = this.credentials.filter((c) => c.phone !== phone);
260
+
261
+ if (this.credentials.length < initialLength) {
262
+ this.saveCredentials();
263
+ return true;
264
+ }
265
+
266
+ return false;
267
+ }
268
+
269
+ /**
270
+ * Get all credentials
271
+ */
272
+ getAllCredentials(): TelegramCredentials[] {
273
+ return [...this.credentials];
274
+ }
275
+
276
+ /**
277
+ * Check if credential exists
278
+ */
279
+ hasCredential(phone: string): boolean {
280
+ return this.credentials.some((c) => c.phone === phone);
281
+ }
282
+
283
+ /**
284
+ * Update session for existing credential
285
+ */
286
+ updateSession(phone: string, session: string): boolean {
287
+ const credential = this.credentials.find((c) => c.phone === phone);
288
+ if (credential) {
289
+ credential.session = session;
290
+ this.saveCredentials();
291
+ return true;
292
+ }
293
+ return false;
294
+ }
295
+
296
+ /**
297
+ * Get credentials count
298
+ */
299
+ getCredentialsCount(): number {
300
+ return this.credentials.length;
301
+ }
302
+
303
+ /**
304
+ * Clear all credentials
305
+ */
306
+ clearAllCredentials(): void {
307
+ this.credentials = [];
308
+ this.saveCredentials();
309
+ }
310
+
311
+ /**
312
+ * Get credentials by proxy configuration
313
+ */
314
+ getCredentialsByProxy(proxyHost: string, proxyPort: number): TelegramCredentials[] {
315
+ return this.credentials.filter(
316
+ (c) => c.proxy && c.proxy.ip === proxyHost && c.proxy.port === proxyPort
317
+ );
318
+ }
319
+
320
+ /**
321
+ * Get credentials without proxy
322
+ */
323
+ getCredentialsWithoutProxy(): TelegramCredentials[] {
324
+ return this.credentials.filter((c) => !c.proxy);
325
+ }
326
+ }
327
+
328
+ export class TelegramAccountRemote {
329
+ private initOptions: TelegramRegistrarInit;
330
+ private tgClient: TelegramClient;
331
+ private credentialsManager: TelegramManagerCredentials;
332
+
333
+ static init(initOptions: TelegramRegistrarInit) {
334
+ return new TelegramAccountRemote(initOptions);
335
+ }
336
+
337
+ constructor(initOptions: TelegramRegistrarInit) {
338
+ this.initOptions = initOptions;
339
+ this.credentialsManager = initOptions.credetialsManager;
340
+ }
341
+
342
+ async attemptRestoreSession(credentials: TelegramCredentials): Promise<boolean> {
343
+ const { phone, proxy, session, password } = credentials;
344
+ const { appId, appHash } = this.initOptions;
345
+
346
+ if (session) {
347
+ const clientParams: TelegramClientParams = {connectionRetries: 5};
348
+
349
+ if (credentials.proxy) {
350
+ clientParams.proxy = credentials.proxy;
351
+ }
352
+
353
+ this.tgClient = new TelegramClient(new StringSession(session), Number(appId), appHash, clientParams);
354
+
355
+ return await this.tgClient.connect();
356
+ }
357
+
358
+ return false;
359
+ }
360
+
361
+ async login(
362
+ credentials: TelegramCredentials,
363
+ cbRequestCode: CallableFunction,
364
+ cbRequestPassword: CallableFunction,
365
+ cbError: CallableFunction,
366
+ ): Promise<void> {
367
+ const { phone, proxy, session, password } = credentials;
368
+ const { appId, appHash } = this.initOptions;
369
+
370
+ if (session) {
371
+ throw new Error("Account is already registered");
372
+ }
373
+
374
+ try {
375
+ this.tgClient = new TelegramClient(new StringSession(""), Number(appId), appHash, {
376
+ connectionRetries: 5,
377
+ proxy: credentials.proxy,
378
+ timeout: 10000 * 60 * 10,
379
+ });
380
+ console.log('Login cresentials:', credentials)
381
+ // @ts-ignore
382
+ await this.tgClient.start({
383
+ phoneNumber: async () => phone,
384
+ password: async () => {
385
+ if (!password) {
386
+ return await cbRequestPassword();
387
+ }
388
+ return password;
389
+ },
390
+ phoneCode: async () => {
391
+ return await cbRequestCode();
392
+ },
393
+ onError: (err: Error) => {
394
+ cbError('Error for wait authentiction', err);
395
+ },
396
+ });
397
+
398
+ const session = this.tgClient.session.save();
399
+
400
+ this.credentialsManager.addCredential({
401
+ phone,
402
+ proxy,
403
+ session,
404
+ password,
405
+ });
406
+
407
+ console.log("Регистрация успешна!");
408
+ console.log("Сессия:\n", session);
409
+
410
+ } catch (err) {
411
+ throw new Error("Failed to register account", { cause: err });
412
+ }
413
+ }
414
+
415
+ async disconnect(): Promise<void> {
416
+ await this.tgClient.disconnect();
417
+ }
418
+
419
+ async disconnectWhenConnect() {
420
+ await this.tgClient.disconnect();
421
+ }
422
+
423
+ /**
424
+ * Отправляет команду /start боту
425
+ * @param botUsername username бота (например: 'my_bot' или '@my_bot')
426
+ * @returns true если команда успешно отправлена
427
+ */
428
+ async sendStartCommand(botUsername: string): Promise<boolean> {
429
+ if (!this.tgClient) {
430
+ throw new Error("Client not initialized. Call login or attemptRestoreSession first");
431
+ }
432
+
433
+ try {
434
+ // Убираем @ из username если он есть
435
+ const normalizedUsername = botUsername.startsWith('@') ? botUsername.substring(1) : botUsername;
436
+
437
+ // Ищем бота по username
438
+ const result = await this.tgClient.sendMessage(normalizedUsername, {
439
+ message: '/start'
440
+ });
441
+
442
+ return result ? true : false;
443
+ } catch (error) {
444
+ console.error('Error sending /start command:', error);
445
+ throw new Error(`Failed to send /start command to @${botUsername}: ${error.message}`);
446
+ }
447
+ }
448
+
449
+ /**
450
+ * Получает ID чата с ботом
451
+ * @param botUsername username бота (например: 'my_bot' или '@my_bot')
452
+ * @returns ID чата с ботом
453
+ */
454
+ async getBotChatId(botUsername: string): Promise<number> {
455
+ if (!this.tgClient) {
456
+ throw new Error("Client not initialized. Call login or attemptRestoreSession first");
457
+ }
458
+
459
+ try {
460
+ // Убираем @ из username если он есть
461
+ const normalizedUsername = botUsername.startsWith('@') ? botUsername.substring(1) : botUsername;
462
+
463
+ // Получаем информацию о диалоге с ботом
464
+ const dialog = await this.tgClient.getDialogs({
465
+ limit: 1
466
+ });
467
+
468
+ if (!dialog || dialog.length === 0) {
469
+ throw new Error(`Chat with bot @${normalizedUsername} not found`);
470
+ }
471
+
472
+ return dialog[0].id.toJSNumber();
473
+ } catch (error) {
474
+ console.error('Error getting bot chat ID:', error);
475
+ throw new Error(`Failed to get chat ID for @${botUsername}: ${error.message}`);
476
+ }
477
+ }
478
+
479
+ /**
480
+ * Отправляет жалобу на бота
481
+ * @param botUsername username бота (например: 'my_bot' или '@my_bot')
482
+ * @param reason причина жалобы: 'spam' | 'violence' | 'pornography' | 'custompromoting' | 'other'
483
+ * @returns true если жалоба успешно отправлена
484
+ */
485
+ async reportBot(
486
+ botUsername: string,
487
+ reason: 'spam' | 'violence' | 'pornography' | 'custompromoting' | 'other' = 'spam'
488
+ ): Promise<boolean> {
489
+ if (!this.tgClient) {
490
+ throw new Error("Client not initialized. Call login or attemptRestoreSession first");
491
+ }
492
+
493
+ try {
494
+ const chatId = await this.getBotChatId(botUsername);
495
+
496
+ // Получаем сообщения от бота для репорта
497
+ const messages = await this.tgClient.getMessages(chatId, { limit: 1 });
498
+ if (!messages || messages.length === 0) {
499
+ throw new Error('No messages found to report');
500
+ }
501
+
502
+ // Формируем причину жалобы
503
+ let reportReason = reason;
504
+ if (reason === 'custompromoting') {
505
+ reportReason = 'spam';
506
+ }
507
+
508
+ // Отправляем жалобу на последнее сообщение
509
+ await this.tgClient.sendMessage(chatId, {
510
+ message: `/report ${reportReason}`,
511
+ replyTo: messages[0].id
512
+ });
513
+
514
+ return true;
515
+ } catch (error) {
516
+ console.error('Error reporting bot:', error);
517
+ if (error instanceof Error) {
518
+ throw new Error(`Failed to report @${botUsername}: ${error.message}`);
519
+ }
520
+ throw new Error(`Failed to report @${botUsername}: Unknown error`);
521
+ }
522
+ }
523
+ }
package/src/types.ts ADDED
@@ -0,0 +1,172 @@
1
+ import { Telegraf, Context } from 'telegraf';
2
+ import { Section } from './illumination/Section';
3
+ import { UserStore } from './user/UserStore';
4
+ import { Telegraf2byteContext } from './illumination/Telegraf2byteContext';
5
+ import { RunSectionRoute } from './illumination/RunSectionRoute';
6
+
7
+ export interface AppConfig {
8
+ apiUrl: string | null;
9
+ envConfig: EnvVars;
10
+ botToken: string | null;
11
+ telegrafConfigLaunch: Record<string, any> | null;
12
+ settings: Record<string, any> | null;
13
+ userStorage: UserStore | null;
14
+ builderPromises: Promise<any>[];
15
+ sections: SectionList;
16
+ components: Record<string, string>;
17
+ debug: boolean;
18
+ devHotReloadSections: boolean;
19
+ telegrafLog: boolean;
20
+ mainMenuKeyboard: any[][];
21
+ hears: Record<string, string>;
22
+ terminateSigInt: boolean;
23
+ terminateSigTerm: boolean;
24
+ keepSectionInstances: boolean;
25
+ }
26
+
27
+ export interface SectionOptions {
28
+ ctx: Telegraf2byteContext;
29
+ bot: Telegraf<Telegraf2byteContext>;
30
+ app: any; // App instance,
31
+ route: RunSectionRoute;
32
+ }
33
+
34
+ export interface RunnedSection {
35
+ instance: Section;
36
+ route: RunSectionRoute;
37
+ }
38
+
39
+ export interface UserAttributes {
40
+ id?: number;
41
+ tg_id: number;
42
+ tg_username: string;
43
+ tg_name: string;
44
+ [key: string]: any;
45
+ }
46
+
47
+ export interface FileValidationOptions {
48
+ allowedTypes?: string[]; // ['image/jpeg', 'image/png', 'application/pdf']
49
+ maxSize?: number; // в байтах
50
+ minSize?: number; // в байтах
51
+ }
52
+
53
+ export interface RequestInputOptions {
54
+ validator?: 'number' | 'phone' | 'code' | 'file' | ((value: string | any) => boolean | Promise<boolean>);
55
+ errorMessage?: string;
56
+ allowCancel?: boolean; // по умолчанию true
57
+ cancelButtonText?: string; // текст кнопки отмены
58
+ cancelAction?: string; // действие при отмене
59
+ fileValidation?: FileValidationOptions; // опции для валидации файлов
60
+ runSection?: RunSectionRoute;
61
+ }
62
+
63
+ export interface UserSession {
64
+ previousSection?: RunnedSection;
65
+ awaitingInput?: {
66
+ key: string;
67
+ validator?: 'number' | 'phone' | 'code' | 'file' | ((value: string | any) => boolean | Promise<boolean>);
68
+ errorMessage: string;
69
+ allowCancel: boolean;
70
+ cancelButtonText?: string;
71
+ cancelAction?: string;
72
+ fileValidation?: FileValidationOptions;
73
+ runSection?: RunSectionRoute;
74
+ retryCount?: number; // счетчик попыток
75
+ };
76
+ awaitingInputPromise?: {
77
+ key: string;
78
+ validator?: 'number' | 'phone' | 'code' | 'file' | ((value: string | any) => boolean | Promise<boolean>);
79
+ errorMessage: string;
80
+ allowCancel: boolean;
81
+ cancelButtonText?: string;
82
+ cancelAction?: string;
83
+ fileValidation?: FileValidationOptions;
84
+ retryCount?: number;
85
+ resolve: (value: string | any) => void;
86
+ reject: (reason?: any) => void;
87
+ };
88
+ [key: string]: any;
89
+ }
90
+
91
+ export interface UserRegistrationData {
92
+ tgUsername: string;
93
+ tgName: string;
94
+ tgId: number;
95
+ userRefid?: number;
96
+ }
97
+
98
+ export interface ComponentOptions {
99
+ ctx: Telegraf2byteContext;
100
+ app: any; // App instance
101
+ section: Section;
102
+ }
103
+
104
+ export interface UserServiceAttributes {
105
+ lastActive: Date;
106
+ lastMessageIds: number[];
107
+ }
108
+
109
+ export interface UserAwaitingReply {
110
+ answer: any;
111
+ validator: ((text: string) => Promise<boolean>) | null;
112
+ is_rejected: boolean;
113
+ run: [Section, string] | null;
114
+ type?: any;
115
+ }
116
+
117
+ export interface RunSectionRouteParams {
118
+ section: string | null;
119
+ method: string | null;
120
+ methodArgs: any[] | null;
121
+ callbackParams: URLSearchParams;
122
+ runAsCallcackQuery: boolean;
123
+ actionPath: string | null;
124
+ hearsKey: string | null;
125
+ }
126
+
127
+ export type SectionEnabledList = string[]
128
+
129
+ export interface SectionEntityConfig {
130
+ pathModule?: string;
131
+ }
132
+
133
+ export interface SectionList {
134
+ [key: string]: SectionEntityConfig;
135
+ }
136
+
137
+ export interface EnvVars {
138
+ BOT_TOKEN?: string;
139
+ BOT_API_URL?: string;
140
+ BOT_HOOK_DOMAIN?: string;
141
+ BOT_HOOK_PORT?: string;
142
+ BOT_HOOK_SECRET_TOKEN?: string;
143
+ BOT_DEV_HOT_RELOAD_SECTIONS?: string;
144
+ [key: string]: string | undefined;
145
+ }
146
+
147
+ export type ModelPaginateParams = {
148
+ route: string;
149
+ routeParams?: Record<string, any>;
150
+ page: number;
151
+ limit: number;
152
+ whereSql: string;
153
+ whereParams?: any[];
154
+ };
155
+
156
+ export type PaginateResult = {
157
+ items: any[];
158
+ paginateButtons: any[][];
159
+ total: number;
160
+ totalPages: number;
161
+ hasPreviousPage: boolean;
162
+ hasNextPage: boolean;
163
+ currentPage: number;
164
+ };
165
+
166
+ export type MakeManualPaginateButtonsParams = {
167
+ callbackDataAction: string;
168
+ paramsQuery: Record<string, any>;
169
+ currentPage: number | string;
170
+ totalRecords: number;
171
+ perPage: number;
172
+ };