@2byte/tgbot-framework 1.0.7 → 1.0.9

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.7",
3
+ "version": "1.0.9",
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",
@@ -52,4 +52,4 @@
52
52
  "templates/**/*",
53
53
  "README.md"
54
54
  ]
55
- }
55
+ }
package/src/core/App.ts CHANGED
@@ -731,19 +731,23 @@ export class App {
731
731
  throw new Error(`Section ${sectionId} not found at path ${pathSectionModule}.ts`);
732
732
  }
733
733
 
734
- if (freshVersion) {
735
- pathSectionModule += "?update=" + Date.now();
736
- }
737
-
738
- // Для обхода кеша в Node.js/Bun используем file:// протокол с query параметром
739
- let importPath = pathSectionModule;
740
- if (freshVersion) {
741
- // Преобразуем путь в file:// URL с query параметром
742
- const fileUrl = new URL(`file:///${pathSectionModule.replace(/\\/g, "/")}`);
743
- fileUrl.searchParams.set("t", Date.now().toString());
744
- importPath = fileUrl.href;
734
+ // For bypassing cache in Bun, we need to clear the module cache
735
+ if (freshVersion && typeof Bun !== 'undefined') {
736
+ // Clear Bun's module cache for this specific module
737
+ const modulePath = pathSectionModule + ".ts";
738
+ this.debugLog('Clearing cache for fresh version of section:', modulePath);
739
+
740
+ // In Bun, we can use dynamic import with a unique query to bypass cache
741
+ // But we need to resolve the absolute path first
742
+ const absolutePath = path.resolve(modulePath);
743
+
744
+ // Try to delete from require cache if it exists
745
+ if (require.cache && require.cache[absolutePath]) {
746
+ delete require.cache[absolutePath];
747
+ }
745
748
  }
746
- const sectionClass = (await import(importPath)).default as typeof Section;
749
+
750
+ const sectionClass = (await import(pathSectionModule)).default as typeof Section;
747
751
  this.debugLog("Loaded section", sectionId);
748
752
 
749
753
  return sectionClass;
@@ -46,9 +46,9 @@ export class Artisan {
46
46
  * Возвращает шаблон для новой секции
47
47
  */
48
48
  private getSectionTemplate(name: string): string {
49
- return `import { Section } from "../../src/illumination/Section";
50
- import { SectionOptions } from "../../src/types";
51
- import { InlineKeyboard } from "../../src/illumination/InlineKeyboard";
49
+ return `import { Section } from "@2byte/tgbot-framework";
50
+ import { SectionOptions } from "@2byte/tgbot-framework";
51
+ import { InlineKeyboard } from "@2byte/tgbot-framework";
52
52
 
53
53
  export default class ${name}Section extends Section {
54
54
  static command = "${name.toLowerCase()}";
@@ -1,3 +1,4 @@
1
+ import { ExtraReplyMessage } from 'telegraf/typings/telegram-types';
1
2
  import { ApiService} from '../../core/ApiService';
2
3
  import { App } from '../../core/App';
3
4
  import { UserModel } from '../../user/UserModel';
@@ -26,7 +27,7 @@ export default class MassSendApiService extends ApiService {
26
27
  port: this.app.configApp.envConfig.BOT_APP_API_PORT || 3033,
27
28
  routes: {
28
29
  '/': async (req) => {
29
- const receivedData = (await req.json()) as { userIds?: number[]; message?: string };
30
+ const receivedData = (await req.json()) as { userIds?: number[]; message?: string, extra?: ExtraReplyMessage };
30
31
  this.app.debugLog("Received data for mass message:", receivedData);
31
32
 
32
33
  let userIds: number[] = [];
@@ -37,7 +38,7 @@ export default class MassSendApiService extends ApiService {
37
38
  message = receivedData?.message || "Hello from MassSendApiService";
38
39
  }
39
40
 
40
- this.sendMassMessage(userIds, message);
41
+ this.sendMassMessage(userIds, message, receivedData.extra);
41
42
 
42
43
  return Response.json({ status: 200, body: 'Mass message sending initiated.' });
43
44
  }
@@ -49,7 +50,7 @@ export default class MassSendApiService extends ApiService {
49
50
  return Promise.resolve();
50
51
  }
51
52
 
52
- private async sendMassMessage(userIds: number[] = [], message: string): Promise<void> {
53
+ private async sendMassMessage(userIds: number[] = [], message: string, extra?: ExtraReplyMessage): Promise<void> {
53
54
  if (userIds.length === 0) {
54
55
 
55
56
  if (!db) {
@@ -68,9 +69,11 @@ export default class MassSendApiService extends ApiService {
68
69
  this.app.debugLog(`Sending message to user ID: ${user.tgId} username: ${user.username}`);
69
70
 
70
71
  try {
71
- await this.app.bot.telegram.sendMessage(user.tgId, message);
72
+ const extraOptions = extra || {};
73
+ await this.app.bot.telegram.sendMessage(user.tgId, message, extraOptions);
72
74
  this.app.debugLog(`Message sent to user ID: ${user.tgId} username: ${user.username}`);
73
75
  } catch (error) {
76
+ this.app.debugLog(`Sending message ${message} to user ID: ${user.tgId} username: ${user.username} failed`, error);
74
77
  this.app.debugLog(`Failed to send message to user ID: ${user.tgId} username: ${user.username}`, error);
75
78
  }
76
79
  }
@@ -0,0 +1,327 @@
1
+ # MassSendApiService - Массовая рассылка сообщений
2
+
3
+ ## Описание
4
+
5
+ `MassSendApiService` - это встроенный API-сервис для массовой рассылки сообщений пользователям Telegram бота. Сервис предоставляет HTTP API для отправки сообщений всем пользователям или конкретной группе пользователей.
6
+
7
+ ## Возможности
8
+
9
+ - ✅ Массовая рассылка всем пользователям бота
10
+ - ✅ Целевая рассылка по списку Telegram ID
11
+ - ✅ Поддержка дополнительных параметров Telegram (кнопки, форматирование)
12
+ - ✅ HTTP API для интеграции с внешними системами
13
+ - ✅ Автоматическая обработка ошибок отправки
14
+ - ✅ Подробное логирование процесса рассылки
15
+
16
+ ## Установка и настройка
17
+
18
+ ### 1. Включение сервиса
19
+
20
+ Сервис автоматически загружается при запуске бота. Убедитесь, что в файле `.env` указан порт для API:
21
+
22
+ ```env
23
+ BOT_APP_API_PORT=3033
24
+ ```
25
+
26
+ ### 2. Настройка базы данных
27
+
28
+ Сервис требует подключения к базе данных для получения списка пользователей. Убедитесь, что `UserModel` настроен корректно в вашем боте.
29
+
30
+ ## Использование
31
+
32
+ ### HTTP API Endpoint
33
+
34
+ **URL:** `http://localhost:3033/`
35
+ **Метод:** `POST`
36
+ **Content-Type:** `application/json`
37
+
38
+ ### Структура запроса
39
+
40
+ ```typescript
41
+ {
42
+ userIds?: number[]; // Массив Telegram ID пользователей (опционально)
43
+ message?: string; // Текст сообщения
44
+ extra?: ExtraReplyMessage; // Дополнительные параметры Telegram
45
+ }
46
+ ```
47
+
48
+ ### Примеры использования
49
+
50
+ #### 1. Рассылка всем пользователям
51
+
52
+ ```bash
53
+ curl -X POST http://localhost:3033/ \
54
+ -H "Content-Type: application/json" \
55
+ -d '{
56
+ "message": "🎉 Важное объявление для всех пользователей!"
57
+ }'
58
+ ```
59
+
60
+ ```javascript
61
+ fetch('http://localhost:3033/', {
62
+ method: 'POST',
63
+ headers: { 'Content-Type': 'application/json' },
64
+ body: JSON.stringify({
65
+ message: '🎉 Важное объявление для всех пользователей!'
66
+ })
67
+ });
68
+ ```
69
+
70
+ #### 2. Рассылка конкретным пользователям
71
+
72
+ ```bash
73
+ curl -X POST http://localhost:3033/ \
74
+ -H "Content-Type: application/json" \
75
+ -d '{
76
+ "userIds": [123456789, 987654321],
77
+ "message": "Персональное сообщение для вас!"
78
+ }'
79
+ ```
80
+
81
+ ```javascript
82
+ fetch('http://localhost:3033/', {
83
+ method: 'POST',
84
+ headers: { 'Content-Type': 'application/json' },
85
+ body: JSON.stringify({
86
+ userIds: [123456789, 987654321],
87
+ message: 'Персональное сообщение для вас!'
88
+ })
89
+ });
90
+ ```
91
+
92
+ #### 3. Рассылка с форматированием и кнопками
93
+
94
+ ```bash
95
+ curl -X POST http://localhost:3033/ \
96
+ -H "Content-Type: application/json" \
97
+ -d '{
98
+ "message": "*Жирный текст*\n_Курсив_\n`Код`",
99
+ "extra": {
100
+ "parse_mode": "Markdown",
101
+ "reply_markup": {
102
+ "inline_keyboard": [
103
+ [
104
+ { "text": "Кнопка 1", "callback_data": "action1" },
105
+ { "text": "Кнопка 2", "callback_data": "action2" }
106
+ ]
107
+ ]
108
+ }
109
+ }
110
+ }'
111
+ ```
112
+
113
+ ```javascript
114
+ fetch('http://localhost:3033/', {
115
+ method: 'POST',
116
+ headers: { 'Content-Type': 'application/json' },
117
+ body: JSON.stringify({
118
+ message: '*Жирный текст*\n_Курсив_\n`Код`',
119
+ extra: {
120
+ parse_mode: 'Markdown',
121
+ reply_markup: {
122
+ inline_keyboard: [
123
+ [
124
+ { text: 'Кнопка 1', callback_data: 'action1' },
125
+ { text: 'Кнопка 2', callback_data: 'action2' }
126
+ ]
127
+ ]
128
+ }
129
+ }
130
+ })
131
+ });
132
+ ```
133
+
134
+ #### 4. Использование из другого Node.js сервиса
135
+
136
+ ```typescript
137
+ import axios from 'axios';
138
+
139
+ async function sendMassMessage(message: string, userIds?: number[]) {
140
+ try {
141
+ const response = await axios.post('http://localhost:3033/', {
142
+ message,
143
+ userIds,
144
+ extra: {
145
+ parse_mode: 'HTML'
146
+ }
147
+ });
148
+
149
+ console.log('Рассылка инициирована:', response.data);
150
+ } catch (error) {
151
+ console.error('Ошибка рассылки:', error);
152
+ }
153
+ }
154
+
155
+ // Рассылка всем
156
+ await sendMassMessage('<b>Привет всем!</b>');
157
+
158
+ // Рассылка конкретным пользователям
159
+ await sendMassMessage('Персональное сообщение', [123456, 789012]);
160
+ ```
161
+
162
+ ## Параметры Extra (Telegram API)
163
+
164
+ Параметр `extra` поддерживает все опции Telegram Bot API для отправки сообщений:
165
+
166
+ ```typescript
167
+ {
168
+ parse_mode?: 'Markdown' | 'HTML' | 'MarkdownV2';
169
+ disable_web_page_preview?: boolean;
170
+ disable_notification?: boolean;
171
+ protect_content?: boolean;
172
+ reply_markup?: {
173
+ inline_keyboard?: Array<Array<{
174
+ text: string;
175
+ callback_data?: string;
176
+ url?: string;
177
+ }>>;
178
+ // ... другие типы клавиатур
179
+ };
180
+ }
181
+ ```
182
+
183
+ ## Логирование
184
+
185
+ Сервис подробно логирует процесс рассылки:
186
+
187
+ ```
188
+ [MassSendApiService] Received data for mass message: {...}
189
+ [MassSendApiService] Fetching all users for mass message...
190
+ [MassSendApiService] Fetched users for mass message: [...]
191
+ [MassSendApiService] Sending message to user ID: 123456 username: john_doe
192
+ [MassSendApiService] Message sent to user ID: 123456 username: john_doe
193
+ [MassSendApiService] Failed to send message to user ID: 789012 username: blocked_user
194
+ ```
195
+
196
+ Для включения логов установите `DEBUG=true` в `.env` файле.
197
+
198
+ ## Обработка ошибок
199
+
200
+ Сервис автоматически обрабатывает ошибки при отправке сообщений:
201
+
202
+ - ❌ Пользователь заблокировал бота - сообщение пропускается, логируется ошибка
203
+ - ❌ Невалидный Telegram ID - сообщение пропускается, логируется ошибка
204
+ - ✅ Процесс рассылки продолжается даже при ошибках отдельных сообщений
205
+
206
+ ## Безопасность
207
+
208
+ ### Рекомендации:
209
+
210
+ 1. **Ограничьте доступ к API** - используйте firewall или reverse proxy
211
+ 2. **Добавьте аутентификацию** - расширьте сервис для проверки токенов
212
+ 3. **Rate limiting** - ограничьте частоту запросов
213
+ 4. **Используйте HTTPS** - в продакшене используйте SSL/TLS
214
+
215
+ ### Пример добавления аутентификации:
216
+
217
+ ```typescript
218
+ // В MassSendApiService.ts
219
+ public async run(): Promise<void> {
220
+ this.bunServerInstance = Bun.serve({
221
+ port: this.app.configApp.envConfig.BOT_APP_API_PORT || 3033,
222
+ routes: {
223
+ '/': async (req) => {
224
+ // Проверка токена
225
+ const authHeader = req.headers.get('Authorization');
226
+ const apiToken = this.app.configApp.envConfig.MASS_SEND_API_TOKEN;
227
+
228
+ if (!authHeader || authHeader !== `Bearer ${apiToken}`) {
229
+ return Response.json({
230
+ status: 401,
231
+ error: 'Unauthorized'
232
+ }, { status: 401 });
233
+ }
234
+
235
+ // Остальная логика...
236
+ }
237
+ }
238
+ });
239
+ }
240
+ ```
241
+
242
+ Добавьте в `.env`:
243
+ ```env
244
+ MASS_SEND_API_TOKEN=your_secret_token_here
245
+ ```
246
+
247
+ ## Интеграция с cron-задачами
248
+
249
+ Используйте для регулярных рассылок:
250
+
251
+ ```bash
252
+ # Ежедневная рассылка в 10:00
253
+ 0 10 * * * curl -X POST http://localhost:3033/ -H "Content-Type: application/json" -d '{"message":"Доброе утро!"}'
254
+ ```
255
+
256
+ ## Расширение функциональности
257
+
258
+ ### Добавление планировщика рассылок
259
+
260
+ ```typescript
261
+ // workflow/services/ScheduledMassSendService.ts
262
+ import MassSendApiService from './MassSendApiService';
263
+
264
+ export default class ScheduledMassSendService extends MassSendApiService {
265
+
266
+ public async run(): Promise<void> {
267
+ await super.run();
268
+
269
+ // Ежедневная рассылка в 9:00
270
+ this.scheduleDaily('09:00', async () => {
271
+ await this.sendMassMessage(
272
+ [],
273
+ '☀️ Доброе утро! Хорошего дня!',
274
+ { parse_mode: 'HTML' }
275
+ );
276
+ });
277
+ }
278
+
279
+ private scheduleDaily(time: string, callback: () => void) {
280
+ // Реализация планировщика
281
+ }
282
+ }
283
+ ```
284
+
285
+ ## Troubleshooting
286
+
287
+ ### Проблема: Сообщения не отправляются
288
+
289
+ **Решение:**
290
+ 1. Проверьте подключение к базе данных
291
+ 2. Убедитесь, что в базе есть пользователи
292
+ 3. Проверьте, что бот имеет права отправлять сообщения
293
+ 4. Включите DEBUG режим для просмотра логов
294
+
295
+ ### Проблема: Порт уже занят
296
+
297
+ **Решение:**
298
+ 1. Измените `BOT_APP_API_PORT` в `.env`
299
+ 2. Остановите другие процессы на порту 3033
300
+
301
+ ### Проблема: База данных не найдена
302
+
303
+ **Решение:**
304
+ Убедитесь, что переменная `db` доступна в контексте сервиса. Возможно, нужно импортировать и инициализировать подключение к БД.
305
+
306
+ ## API Response
307
+
308
+ ```json
309
+ {
310
+ "status": 200,
311
+ "body": "Mass message sending initiated."
312
+ }
313
+ ```
314
+
315
+ Рассылка выполняется асинхронно, ответ приходит сразу после инициации процесса.
316
+
317
+ ## Производительность
318
+
319
+ - Рассылка выполняется последовательно для избежания rate limits Telegram API
320
+ - Рекомендуется не отправлять более 30 сообщений в секунду
321
+ - Для больших объемов рассылки используйте очереди (например, BullMQ)
322
+
323
+ ## См. также
324
+
325
+ - [Telegram Bot API Documentation](https://core.telegram.org/bots/api)
326
+ - [API Services Guide](API_SERVICES.md)
327
+ - [Service Examples](SERVICE_EXAMPLES.md)
@@ -1,7 +1,8 @@
1
1
  import type { Database } from "bun:sqlite";
2
2
  import { MakeManualPaginateButtonsParams, ModelPaginateParams, PaginateResult } from "@2byte/tgbot-framework";
3
3
  import { Section } from "@2byte/tgbot-framework";
4
+ import { Model as BaseModel } from "@2byte/tgbot-framework";
4
5
 
5
- export abstract class Model {
6
+ export abstract class Model extends BaseModel {
6
7
 
7
8
  }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "{{kebabName}}",
3
- "version": "1.0.0",
3
+ "version": "1.0.9",
4
4
  "description": "{{description}}",
5
5
  "main": "bot.ts",
6
6
  "scripts": {