@2byte/tgbot-framework 1.0.7 → 1.0.8
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.
|
|
3
|
+
"version": "1.0.8",
|
|
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
|
@@ -735,10 +735,10 @@ export class App {
|
|
|
735
735
|
pathSectionModule += "?update=" + Date.now();
|
|
736
736
|
}
|
|
737
737
|
|
|
738
|
-
//
|
|
738
|
+
// For bypassing cache in Node.js/Bun, we use file:// protocol with a query parameter
|
|
739
739
|
let importPath = pathSectionModule;
|
|
740
740
|
if (freshVersion) {
|
|
741
|
-
//
|
|
741
|
+
// We convert the path to a file:// URL with a query parameter
|
|
742
742
|
const fileUrl = new URL(`file:///${pathSectionModule.replace(/\\/g, "/")}`);
|
|
743
743
|
fileUrl.searchParams.set("t", Date.now().toString());
|
|
744
744
|
importPath = fileUrl.href;
|
|
@@ -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
|
-
|
|
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)
|