@2byte/tgbot-framework 1.0.13 → 1.0.15

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@2byte/tgbot-framework",
3
- "version": "1.0.13",
3
+ "version": "1.0.15",
4
4
  "description": "A TypeScript framework for creating Telegram bots with sections-based architecture (Bun optimized)",
5
5
  "main": "src/index.ts",
6
6
  "types": "src/index.ts",
@@ -1,10 +1,13 @@
1
1
  import { App } from "./App";
2
2
 
3
- export abstract class ApiService {
3
+ export class ApiService<T = any> {
4
+
5
+ public name: string = "ApiService";
6
+
7
+ protected app!: App;
4
8
 
5
9
  constructor(
6
- protected app: App,
7
- public name: string
10
+ public params: T = {} as T
8
11
  ) {}
9
12
 
10
13
  async setup(): Promise<void> {
@@ -18,4 +21,9 @@ export abstract class ApiService {
18
21
  async run(): Promise<void> {
19
22
  // Implement your API logic here
20
23
  }
24
+
25
+ public setApp(app: App): this {
26
+ this.app = app;
27
+ return this;
28
+ }
21
29
  }
@@ -51,13 +51,29 @@ export class ApiServiceManager {
51
51
  }
52
52
  }
53
53
 
54
+ public async runAllServices(): Promise<void> {
55
+ for (const [name, service] of this.services) {
56
+ this.app.debugLog(`Running API service: ${name}`);
57
+ await service.run();
58
+ this.app.debugLog(`API service run completed: ${name}`);
59
+ }
60
+ }
61
+
54
62
  public getAll(): Map<string, ApiService> {
55
63
  return this.services;
56
64
  }
57
65
 
66
+ public async setupAllServices(): Promise<void> {
67
+ for (const [name, service] of this.services) {
68
+ this.app.debugLog(`Setting up API service: ${name}`);
69
+ await this.setupService(name);
70
+ this.app.debugLog(`API service setup completed: ${name}`);
71
+ }
72
+ }
73
+
58
74
  public async unsetupAllServices(): Promise<void> {
59
75
  for (const [name, service] of this.services) {
60
- await service.unsetup();
76
+ await this.unsetupService(name);
61
77
  }
62
78
  }
63
79
  }
package/src/core/App.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  import { nameToCapitalize } from "./utils";
22
22
  import { ApiServiceManager } from "./ApiServiceManager";
23
23
  import { message } from "telegraf/filters";
24
+ import { ApiService } from "./ApiService";
24
25
 
25
26
  export class App {
26
27
  private config: AppConfig = {
@@ -43,6 +44,7 @@ export class App {
43
44
  terminateSigTerm: true,
44
45
  keepSectionInstances: false,
45
46
  botCwd: process.cwd(),
47
+ services: [],
46
48
  };
47
49
 
48
50
  public bot!: Telegraf<Telegraf2byteContext>;
@@ -72,7 +74,7 @@ export class App {
72
74
  }
73
75
  > = new Map();
74
76
 
75
- private messageHandlers: any[] = [];
77
+ private messageHandlers: RunSectionRoute[] | CallableFunction<this>[] = [];
76
78
 
77
79
  constructor() {
78
80
  this.middlewares.push(this.mainMiddleware.bind(this));
@@ -160,13 +162,12 @@ export class App {
160
162
  return this;
161
163
  }
162
164
 
163
- messageHandlers(handlers: any[]): this {
165
+ messageHandlers(handlers: RunSectionRoute[] | CallableFunction<this>[]): this {
164
166
  this.app.messageHandlers = handlers;
165
167
  return this;
166
168
  }
167
169
 
168
170
  /**
169
- *
170
171
  * @param keep Whether to keep section instances in memory after they are run.
171
172
  * If true, sections will not be reloaded on each request, improving performance for frequently accessed sections.
172
173
  * If false, sections will be reloaded each time they are accessed, ensuring the latest version is used.
@@ -188,6 +189,11 @@ export class App {
188
189
  return this;
189
190
  }
190
191
 
192
+ services(services: ApiService[]): this {
193
+ this.app.config.services = services;
194
+ return this;
195
+ }
196
+
191
197
  build(): App {
192
198
  return this.app;
193
199
  }
@@ -274,7 +280,7 @@ export class App {
274
280
  }
275
281
 
276
282
  // Check access by username and register user if not exists
277
- if (!this.config.userStorage.exists(tgUsername)) {
283
+ if (!this.config.userStorage.exists(tgUsername) && !this.rememberUser(tgUsername)) {
278
284
  const isAuthByUsername = !this.config.accessPublic && !accessKey;
279
285
 
280
286
  // check access by username for private bots
@@ -288,6 +294,7 @@ export class App {
288
294
  checkAccess &&
289
295
  checkAccess.every((name) => name.toLowerCase() !== requestUsername.toLowerCase())
290
296
  ) {
297
+ this.debugLog("Username access denied:", requestUsername);
291
298
  return ctx.reply("Access denied. Your username is not in the access list.");
292
299
  }
293
300
  this.debugLog("Username access granted.");
@@ -425,20 +432,53 @@ export class App {
425
432
  !ctx.userSession.stateAfterValidatedUserResponse
426
433
  ) {
427
434
  this.messageHandlers.forEach(async (handler: any) => {
435
+ if (ctx.caught) {
436
+ this.debugLog("Message already caught by another handler, skipping remaining handlers.");
437
+ return;
438
+ }
439
+
440
+ const isHandlerRunSectionRoute = handler instanceof RunSectionRoute;
441
+
442
+ if (isHandlerRunSectionRoute) {
443
+ this.debugLog("Checking message handler section route:", handler);
444
+ await this.runSection(ctx, handler, {
445
+ cbBeforeRunMethod: async (sectionInstance: Section) => {
446
+ sectionInstance.runForMessageHandler = true;
447
+ },
448
+ });
449
+ if (ctx.caught) {
450
+ this.debugLog("Message handler route caught the message, skipping remaining handlers.");
451
+ return;
452
+ }
453
+ }
454
+
428
455
  const handlerIsClass =
429
456
  typeof handler === "function" && /^\s*class\s+/.test(handler.toString());
430
457
  const nameHandler = handlerIsClass
431
458
  ? handler.name
432
459
  : handler.constructor?.name || "unknown";
433
460
 
434
- this.debugLog("Handling message with handler class:", nameHandler);
435
-
436
- if (handlerIsClass) {
461
+ if (handlerIsClass && !ctx.caught) {
462
+ this.debugLog(`Running message handler class ${nameHandler} for user ${ctx.user.username}`);
437
463
  await new handler(this).handle(ctx);
464
+ if (ctx.caught) {
465
+ this.debugLog("Message handler class caught the message, skipping remaining handlers.");
466
+ return;
467
+ }
468
+ } else if (!handlerIsClass && typeof handler === "function" && !ctx.caught) {
469
+ this.debugLog(`Running message handler function ${nameHandler} for user ${ctx.user.username}`);
470
+ await handler(ctx);
471
+ if (ctx.caught) {
472
+ this.debugLog("Message handler function caught the message, skipping remaining handlers.");
473
+ return;
474
+ }
438
475
  }
439
476
  });
440
477
  } else {
441
- this.debugLog("Message input already handled by awaitingInput or awaitingInputPromise. stateAfterValidatedUserResponse:", ctx.userSession.stateAfterValidatedUserResponse);
478
+ this.debugLog(
479
+ "Message input already handled by awaitingInput or awaitingInputPromise. stateAfterValidatedUserResponse:",
480
+ ctx.userSession.stateAfterValidatedUserResponse
481
+ );
442
482
  }
443
483
 
444
484
  delete ctx.userSession.stateAfterValidatedUserResponse; // Clear the state after handling the message
@@ -462,7 +502,7 @@ export class App {
462
502
  // Get the largest photo (the last one in the array is usually the largest)
463
503
  const largestPhoto = photo[photo.length - 1];
464
504
  await this.handleUserInput(ctx, largestPhoto, "photo");
465
-
505
+
466
506
  delete ctx.userSession.stateAfterValidatedUserResponse; // Clear the state after handling the message
467
507
  });
468
508
  }
@@ -470,39 +510,38 @@ export class App {
470
510
  private async registerServices() {
471
511
  this.apiServiceManager = ApiServiceManager.init(this);
472
512
 
473
- const registerServices = async (pathDirectory: string) => {
474
- try {
475
- await this.apiServiceManager.loadServicesFromDirectory(pathDirectory);
476
- } catch (error) {
477
- this.debugLog("Error loading services:", error);
478
- throw error;
479
- }
513
+ // Register services from config
514
+ this.debugLog(
515
+ "Registering services from config:",
516
+ this.config.services.map((service) => service.constructor.name)
517
+ );
480
518
 
481
- this.debugLog(
482
- "Registered API services:%s in dir: %s",
483
- Array.from(this.apiServiceManager.getAll().keys()),
484
- pathDirectory
485
- );
519
+ this.config.services.forEach((service) => {
520
+ this.debugLog(`Registering service: ${service.constructor.name}`);
521
+ this.apiServiceManager.registerService(service.name, service.setApp(this));
522
+ this.debugLog(`Service ${service.constructor.name} registered`);
523
+ });
486
524
 
487
- for (const [name, service] of this.apiServiceManager.getAll()) {
488
- await service.setup();
489
- this.debugLog(`Service ${name} setup completed`);
490
- await service.run();
491
- this.debugLog(`Service ${name} run completed`);
492
- }
493
- };
525
+ try {
526
+ await this.apiServiceManager.setupAllServices();
527
+ } catch (error) {
528
+ this.debugLog("Error setting up services:", error);
529
+ throw error;
530
+ }
494
531
 
495
- // Register services from bot directory
496
- await registerServices(this.config.botCwd + "/workflow/services");
497
- // Register services from framework directory
498
- await registerServices(path.resolve(__dirname, "../workflow/services"));
532
+ try {
533
+ await this.apiServiceManager.runAllServices();
534
+ } catch (error) {
535
+ this.debugLog("Error running services:", error);
536
+ throw error;
537
+ }
499
538
  }
500
539
 
501
540
  private async unregisterServices() {
502
541
  this.apiServiceManager = ApiServiceManager.init(this);
503
542
 
504
543
  try {
505
- this.apiServiceManager.unsetupAllServices();
544
+ await this.apiServiceManager.unsetupAllServices();
506
545
  } catch (error) {
507
546
  this.debugLog("Error unsetting up services:", error);
508
547
  throw error;
@@ -923,7 +962,7 @@ export class App {
923
962
  if (sectionRoute.hasTriggers()) {
924
963
  this.debugLog("Section route has triggers, executing them before running method:", sectionId);
925
964
  sectionRoute.getTriggers().forEach((trigger) => {
926
- if (trigger.name === 'cbBeforeRunMethod') {
965
+ if (trigger.name === "cbBeforeRunMethod") {
927
966
  this.debugLog(`Executing cbBeforeRunMethod trigger for section ${sectionId}`);
928
967
  this.debugLog("Trigger details:", trigger);
929
968
 
@@ -1019,6 +1058,32 @@ export class App {
1019
1058
  }
1020
1059
  }
1021
1060
 
1061
+ /**
1062
+ * Remembers a user in storage by their Telegram username. If the user does not exist in storage, it attempts to fetch the user from the database and add them to storage. This is useful for ensuring that the storage has the latest user data from the database, especially in cases where user information might have been updated.
1063
+ * @param tgUsername Telegram username of the user to remember. This method checks if the user exists in storage, and if not, tries to fetch it from the database and add to storage. This is useful for cases when user data might be updated in the database and we want to refresh the storage with the latest data.
1064
+ * @returns A boolean indicating whether the user was successfully remembered (true) or not (false).
1065
+ */
1066
+ async rememberUser(tgUsername: string): Promise<boolean> {
1067
+ if (this.config.userStorage && !this.config.userStorage.exists(tgUsername)) {
1068
+ this.debugLog("Warning: Username not found in storage:", tgUsername);
1069
+ this.debugLog("Trying getting to database:", tgUsername);
1070
+
1071
+ // Try to get user from database and add to storage
1072
+ UserModel.resolveDb();
1073
+ const userFromDb = UserModel.findByUsername(tgUsername);
1074
+
1075
+ if (userFromDb) {
1076
+ this.config.userStorage.add(tgUsername, userFromDb);
1077
+ this.debugLog("Success: User found in database and added to storage:", tgUsername);
1078
+ this.debugLog('Success: Remembered user "' + tgUsername + '"');
1079
+ return true;
1080
+ } else {
1081
+ this.debugLog("Warning: User not found in database:", tgUsername);
1082
+ }
1083
+ }
1084
+ return false;
1085
+ }
1086
+
1022
1087
  /**
1023
1088
  * Runs a task with bidirectional communication support
1024
1089
  * @param ctx Telegram context
@@ -1227,9 +1292,71 @@ export class App {
1227
1292
  }
1228
1293
 
1229
1294
  debugLog(...args: any[]): void {
1230
- if (this.config.debug) {
1231
- console.log(...args);
1232
- }
1295
+ if (!this.config.debug) return;
1296
+
1297
+ // Color palette
1298
+ const colors = {
1299
+ reset: "\x1b[0m",
1300
+ bright: "\x1b[1m",
1301
+ dim: "\x1b[2m",
1302
+ underscore: "\x1b[4m",
1303
+ fg: {
1304
+ red: "\x1b[31m",
1305
+ green: "\x1b[32m",
1306
+ yellow: "\x1b[33m",
1307
+ blue: "\x1b[34m",
1308
+ magenta: "\x1b[35m",
1309
+ cyan: "\x1b[36m",
1310
+ white: "\x1b[37m",
1311
+ },
1312
+ bg: {
1313
+ red: "\x1b[41m",
1314
+ green: "\x1b[42m",
1315
+ yellow: "\x1b[43m",
1316
+ blue: "\x1b[44m",
1317
+ magenta: "\x1b[45m",
1318
+ cyan: "\x1b[46m",
1319
+ white: "\x1b[47m",
1320
+ },
1321
+ };
1322
+
1323
+ // Timestamp
1324
+ const now = new Date();
1325
+ const timestamp = `${colors.dim}${colors.fg.cyan}[${now.toLocaleTimeString()}]${colors.reset}`;
1326
+
1327
+ // Source (App debug)
1328
+ const source = `${colors.bright}${colors.fg.magenta}AppDebug${colors.reset}`;
1329
+
1330
+ // Format args: highlight objects, errors, etc.
1331
+ const formattedArgs = args.map(arg => {
1332
+ if (arg instanceof Error) {
1333
+ return `${colors.fg.red}${arg.stack || arg.message}${colors.reset}`;
1334
+ }
1335
+ if (typeof arg === "object" && arg !== null) {
1336
+ try {
1337
+ return `${colors.fg.yellow}${JSON.stringify(arg, null, 2)}${colors.reset}`;
1338
+ } catch {
1339
+ return `${colors.fg.yellow}[Object]${colors.reset}`;
1340
+ }
1341
+ }
1342
+ if (typeof arg === "string") {
1343
+ // Highlight keywords
1344
+ if (/error|fail|exception/i.test(arg)) {
1345
+ return `${colors.fg.red}${arg}${colors.reset}`;
1346
+ }
1347
+ if (/success|done|complete/i.test(arg)) {
1348
+ return `${colors.fg.green}${arg}${colors.reset}`;
1349
+ }
1350
+ if (/warn|warning/i.test(arg)) {
1351
+ return `${colors.fg.yellow}${arg}${colors.reset}`;
1352
+ }
1353
+ return `${colors.fg.white}${arg}${colors.reset}`;
1354
+ }
1355
+ return String(arg);
1356
+ });
1357
+
1358
+ // Compose and print
1359
+ console.log(`${timestamp} ${source}:`, ...formattedArgs);
1233
1360
  }
1234
1361
 
1235
1362
  get sections(): SectionList {
package/src/core/Model.ts CHANGED
@@ -14,7 +14,7 @@ export abstract class Model {
14
14
  this.db = database;
15
15
  }
16
16
 
17
- protected static resolveDb(): Database {
17
+ public static resolveDb(): Database {
18
18
  if (globalThis.db) {
19
19
  this.db = globalThis.db;
20
20
  } else {
@@ -63,6 +63,10 @@ export class RunSectionRoute {
63
63
  return this;
64
64
  }
65
65
 
66
+ static for(sectionId: string, methodName: string = 'index'): RunSectionRoute {
67
+ return new RunSectionRoute().section(sectionId).method(methodName);
68
+ }
69
+
66
70
  hasTriggers(): boolean {
67
71
  return this.runParams.triggers.length > 0;
68
72
  }
@@ -15,6 +15,7 @@ export class Section {
15
15
  static actionRoutes: { [key: string]: string };
16
16
  public sectionId: string = "BaseSection";
17
17
  public route: RunSectionRoute;
18
+ public runForMessageHandler: boolean = false;
18
19
  protected ctx: Telegraf2byteContext;
19
20
  protected bot: Telegraf<Telegraf2byteContext>;
20
21
  protected app: App;
@@ -11,6 +11,7 @@ export interface Telegraf2byteContext extends Context, ITelegraf2byteContextExtr
11
11
  user: UserModel;
12
12
  userStorage: UserStore;
13
13
  userSession: UserSession;
14
+ caught: boolean;
14
15
  // msgId?: number;
15
16
  }
16
17
 
package/src/index.ts CHANGED
@@ -2,6 +2,7 @@
2
2
  export { App } from './core/App';
3
3
  export { ApiService } from './core/ApiService';
4
4
  export { ApiServiceManager } from './core/ApiServiceManager';
5
+ export { MassSendApiService } from './workflow/services/MassSendApiService';
5
6
  export { BotArtisan } from './core/BotArtisan';
6
7
  export { BotMigration } from './core/BotMigration';
7
8
  export { BotSeeder } from './core/BotSeeder';
package/src/types.ts CHANGED
@@ -3,6 +3,7 @@ import { Section } from './illumination/Section';
3
3
  import { UserStore } from './user/UserStore';
4
4
  import { Telegraf2byteContext } from './illumination/Telegraf2byteContext';
5
5
  import { RunSectionRoute } from './illumination/RunSectionRoute';
6
+ import { ApiService } from './core/ApiService';
6
7
 
7
8
  export interface AppConfig {
8
9
  accessPublic: boolean;
@@ -24,6 +25,7 @@ export interface AppConfig {
24
25
  terminateSigTerm: boolean;
25
26
  keepSectionInstances: boolean;
26
27
  botCwd: string;
28
+ services: ApiService[];
27
29
  }
28
30
 
29
31
  export interface SectionOptions {
@@ -1,83 +1,94 @@
1
- import { ExtraReplyMessage } from 'telegraf/typings/telegram-types';
2
- import { ApiService} from '../../core/ApiService';
3
- import { App } from '../../core/App';
4
- import { UserModel } from '../../user/UserModel';
5
-
6
- export default class MassSendApiService extends ApiService {
7
-
8
- private bunServerInstance: any;
9
-
10
- constructor(
11
- protected app: App,
12
- public name: string = "MassSendApiService"
13
- ) {
14
- super(app, name);
15
- }
16
-
17
- public async setup(): Promise<void> {
18
- return Promise.resolve();
19
- }
20
-
21
- public async unsetup(): Promise<void> {
22
- return Promise.resolve();
23
- }
24
-
25
- public async run(): Promise<void> {
26
- this.bunServerInstance = Bun.serve({
27
- port: this.app.configApp.envConfig.BOT_APP_API_PORT || 3033,
28
- routes: {
29
- '/': async (req) => {
30
- const receivedData = (await req.json()) as { userIds?: number[]; message?: string, extra?: ExtraReplyMessage };
31
- this.app.debugLog("Received data for mass message:", receivedData);
32
-
33
- let userIds: number[] = [];
34
- let message: string = "Hello from MassSendApiService";
35
-
36
- if (receivedData && typeof receivedData == 'object') {
37
- userIds = receivedData?.userIds || [];
38
- message = receivedData?.message || "Hello from MassSendApiService";
39
- }
40
-
41
- this.sendMassMessage(userIds, message, receivedData.extra);
42
-
43
- return Response.json({ status: 200, body: 'Mass message sending initiated.' });
44
- }
45
- }
46
- });
47
-
48
- this.app.debugLog(`MassSendApiService Bun server running at http://localhost:${this.bunServerInstance.port}/`);
49
-
50
- return Promise.resolve();
51
- }
52
-
53
- private async sendMassMessage(userIds: number[] = [], message: string, extra?: ExtraReplyMessage): Promise<void> {
54
- if (userIds.length === 0) {
55
-
56
- if (!db) {
57
- throw new Error("Database connection is not established.");
58
- }
59
-
60
- UserModel.setDatabase(db);
61
-
62
- this.app.debugLog("Fetching all users for mass message...");
63
- const users = UserModel.getAll();
64
-
65
- this.app.debugLog("Fetched users for mass message:", users);
66
-
67
- if (users && users.length > 0) {
68
- for (const user of users) {
69
- this.app.debugLog(`Sending message to user ID: ${user.tgId} username: ${user.username}`);
70
-
71
- try {
72
- const extraOptions = extra || {};
73
- await this.app.bot.telegram.sendMessage(user.tgId, message, extraOptions);
74
- this.app.debugLog(`Message sent to user ID: ${user.tgId} username: ${user.username}`);
75
- } catch (error) {
76
- this.app.debugLog(`Sending message ${message} to user ID: ${user.tgId} username: ${user.username} failed`, error);
77
- this.app.debugLog(`Failed to send message to user ID: ${user.tgId} username: ${user.username}`, error);
78
- }
79
- }
80
- }
1
+ import { ExtraReplyMessage } from "telegraf/typings/telegram-types";
2
+ import { ApiService } from "../../core/ApiService";
3
+ import { UserModel } from "../../user/UserModel";
4
+
5
+ export type MassSendApiParams = {
6
+ port?: number;
7
+ };
8
+
9
+ export class MassSendApiService extends ApiService<MassSendApiParams> {
10
+ public override name = "MassSendApiService";
11
+ private bunServerInstance: any;
12
+
13
+ public async setup(): Promise<void> {
14
+ return Promise.resolve();
15
+ }
16
+
17
+ public async unsetup(): Promise<void> {
18
+ return Promise.resolve();
19
+ }
20
+
21
+ public async run(): Promise<void> {
22
+ this.bunServerInstance = Bun.serve({
23
+ port: this.params.port || this.app.configApp.envConfig.BOT_APP_API_PORT || 3033,
24
+ routes: {
25
+ "/": async (req) => {
26
+ const receivedData = (await req.json()) as {
27
+ userIds?: number[];
28
+ message?: string;
29
+ extra?: ExtraReplyMessage;
30
+ };
31
+ this.app.debugLog("Received data for mass message:", receivedData);
32
+
33
+ let userIds: number[] = [];
34
+ let message: string = "Hello from MassSendApiService";
35
+
36
+ if (receivedData && typeof receivedData == "object") {
37
+ userIds = receivedData?.userIds || [];
38
+ message = receivedData?.message || "Hello from MassSendApiService";
39
+
40
+ this.sendMassMessage(userIds, message, receivedData.extra);
41
+ }
42
+
43
+ return Response.json({ status: 200, body: "Mass message sending initiated." });
44
+ },
45
+ },
46
+ });
47
+
48
+ this.app.debugLog(
49
+ `MassSendApiService Bun server running at http://localhost:${this.bunServerInstance.port}/`
50
+ );
51
+
52
+ return Promise.resolve();
53
+ }
54
+
55
+ private async sendMassMessage(
56
+ userIds: number[] = [],
57
+ message: string,
58
+ extra?: ExtraReplyMessage
59
+ ): Promise<void> {
60
+ if (userIds.length === 0) {
61
+ if (!db) {
62
+ throw new Error("Database connection is not established.");
63
+ }
64
+
65
+ UserModel.setDatabase(db);
66
+
67
+ this.app.debugLog("Fetching all users for mass message...");
68
+ const users = UserModel.getAll();
69
+
70
+ this.app.debugLog("Fetched users for mass message:", users);
71
+
72
+ if (users && users.length > 0) {
73
+ for (const user of users) {
74
+ this.app.debugLog(`Sending message to user ID: ${user.tgId} username: ${user.username}`);
75
+
76
+ try {
77
+ const extraOptions = extra || {};
78
+ await this.app.bot.telegram.sendMessage(user.tgId, message, extraOptions);
79
+ this.app.debugLog(`Message sent to user ID: ${user.tgId} username: ${user.username}`);
80
+ } catch (error) {
81
+ this.app.debugLog(
82
+ `Sending message ${message} to user ID: ${user.tgId} username: ${user.username} failed`,
83
+ error
84
+ );
85
+ this.app.debugLog(
86
+ `Failed to send message to user ID: ${user.tgId} username: ${user.username}`,
87
+ error
88
+ );
89
+ }
81
90
  }
91
+ }
82
92
  }
93
+ }
83
94
  }
@@ -21,7 +21,7 @@
21
21
  "author": "{{author}}",
22
22
  "license": "MIT",
23
23
  "dependencies": {
24
- "@2byte/tgbot-framework": "^1.0.13"
24
+ "@2byte/tgbot-framework": "^1.0.15"
25
25
  },
26
26
  "devDependencies": {
27
27
  "@types/node": "^20.19.8",