grammy-broadcast 2.0.2

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Smaznet
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/Readme.md ADDED
@@ -0,0 +1,603 @@
1
+ # 📢 Grammy Broadcast Plugin
2
+
3
+ <div align="center">
4
+
5
+ **A powerful and professional plugin for broadcasting messages to all Telegram bot chats**
6
+
7
+ [![TypeScript](https://img.shields.io/badge/TypeScript-007ACC?style=for-the-badge&logo=typescript&logoColor=white)](https://www.typescriptlang.org/)
8
+ [![Grammy](https://img.shields.io/badge/Grammy-0088CC?style=for-the-badge&logo=telegram&logoColor=white)](https://grammy.dev/)
9
+ [![License](https://img.shields.io/badge/License-MIT-green.svg?style=for-the-badge)](LICENSE)
10
+
11
+ </div>
12
+
13
+ ---
14
+
15
+ ## ✨ Key Features
16
+
17
+ ### 🚀 Performance & Speed
18
+ - ⚡ **Smart Rate Limiting**: Automatic Telegram rate limit management with auto-throttling on errors
19
+ - 🔥 **Paid Broadcast Support**: Support for paid broadcasts at 1000 messages per second (requires Telegram Stars)
20
+ - 📊 **Concurrent Sending**: Send messages concurrently with queue management
21
+ - 🎯 **Queue Management**: Broadcast queue management with pause/resume/stop capabilities
22
+
23
+ ### 💾 Flexible Storage
24
+ - 🔴 **Redis Storage**: For production environments and clusters
25
+ - 💻 **Memory Storage**: For development and testing
26
+ - 📁 **File Storage**: For local storage without Redis
27
+
28
+ ### 🎮 Full Control
29
+ - ⏸️ **Pause/Resume**: Pause and resume sending at any time
30
+ - 🛑 **Stop**: Complete stop and removal of broadcast
31
+ - 📈 **Progress Tracking**: Track progress with percentage, sent count, and errors
32
+ - 🔍 **Preview**: Preview messages before sending
33
+ - 📌 **Pin Support**: Ability to pin messages after sending
34
+
35
+ ### 🎨 Send Types
36
+ - 📋 **Copy Messages**: Copy messages while preserving content
37
+ - ➡️ **Forward Messages**: Forward messages
38
+ - ✍️ **Text Messages**: Send plain text
39
+ - 📦 **Multi-Message**: Support for sending multiple messages in one broadcast
40
+
41
+ ### 🔐 Security & Access Control
42
+ - 👥 **Sudo Users**: Define authorized users
43
+ - 🔒 **Custom Permission**: Custom permission system
44
+ - 🚫 **Auto Block Management**: Automatic management of blocked users
45
+
46
+ ### 🌐 Cluster Support
47
+ - 🔄 **Multi-Instance**: Support for multiple instances
48
+ - 🎯 **Main Instance**: Designate main instance for queue processing
49
+
50
+ ---
51
+
52
+ ## 📦 Installation
53
+
54
+ ```bash
55
+ npm install grammy-broadcast
56
+ # or
57
+ yarn add grammy-broadcast
58
+ # or
59
+ pnpm add grammy-broadcast
60
+ ```
61
+
62
+ ### Requirements
63
+
64
+ ```bash
65
+ npm install grammy ioredis
66
+ # or
67
+ yarn add grammy ioredis
68
+ ```
69
+
70
+ ---
71
+
72
+ ## 🚀 Quick Start
73
+
74
+ ### Basic Example with Redis
75
+
76
+ ```typescript
77
+ import { Bot } from "grammy";
78
+ import { createBroadcaster, RedisStorage } from "grammy-broadcast";
79
+ import Redis from "ioredis";
80
+
81
+ const bot = new Bot("YOUR_BOT_TOKEN");
82
+ const redis = new Redis();
83
+
84
+ const broadcaster = createBroadcaster({
85
+ storage: new RedisStorage(redis),
86
+ isMainInstance: true, // For clusters, set true only on the main instance
87
+ sudoUsers: [123456789], // Authorized user IDs
88
+ getApi: async (botId) => bot.api, // or bot.api for single bot
89
+ getBroadcastChats: async (botId, offset, limit, filter) => {
90
+ // You can use any database here
91
+ // Example with MongoDB:
92
+ const users = await UserModel.find({ blocked: { $ne: true } })
93
+ .skip(offset)
94
+ .limit(limit)
95
+ .select("uid")
96
+ .lean();
97
+ return users.map(u => u.uid);
98
+ },
99
+ setRestricted: async (botId, chatId, type) => {
100
+ // Manage blocked users
101
+ if (type === 'block' || type === 'deactivated') {
102
+ await UserModel.updateOne({ uid: chatId }, { blocked: true });
103
+ }
104
+ }
105
+ });
106
+
107
+ // Add middleware to bot
108
+ bot.use(broadcaster.getMiddleware());
109
+
110
+ bot.start();
111
+ ```
112
+
113
+ ### Example with Memory Storage (for testing)
114
+
115
+ ```typescript
116
+ import { Bot } from "grammy";
117
+ import { createBroadcaster, MemoryStorage } from "grammy-broadcast";
118
+
119
+ const bot = new Bot("YOUR_BOT_TOKEN");
120
+
121
+ const broadcaster = createBroadcaster({
122
+ storage: new MemoryStorage(),
123
+ isMainInstance: true,
124
+ sudoUsers: [123456789],
125
+ getApi: async () => bot.api,
126
+ getBroadcastChats: async (botId, offset, limit, filter) => {
127
+ // Test chat list
128
+ return [123456, 789012, 345678];
129
+ }
130
+ });
131
+
132
+ bot.use(broadcaster.getMiddleware());
133
+ bot.start();
134
+ ```
135
+
136
+ ### Example with File Storage
137
+
138
+ ```typescript
139
+ import { Bot } from "grammy";
140
+ import { createBroadcaster, FileStorage } from "grammy-broadcast";
141
+
142
+ const bot = new Bot("YOUR_BOT_TOKEN");
143
+
144
+ const broadcaster = createBroadcaster({
145
+ storage: new FileStorage("./broadcast-data"), // Storage path
146
+ isMainInstance: true,
147
+ sudoUsers: [123456789],
148
+ getApi: async () => bot.api,
149
+ getBroadcastChats: async (botId, offset, limit, filter) => {
150
+ // Chat retrieval logic
151
+ return [];
152
+ }
153
+ });
154
+
155
+ bot.use(broadcaster.getMiddleware());
156
+ bot.start();
157
+ ```
158
+
159
+ ---
160
+
161
+ ## 📖 Usage Guide
162
+
163
+ ### Available Commands
164
+
165
+ #### 1️⃣ `/broadcast <type> [filter]`
166
+ Send broadcast with specified type
167
+
168
+ **Parameters:**
169
+ - `type`: Send type (`copy` or `forward`)
170
+ - `filter`: Optional filter for selecting chats (passed to `getBroadcastChats`)
171
+
172
+ **Examples:**
173
+ ```
174
+ /broadcast copy users
175
+ /broadcast forward groups
176
+ ```
177
+
178
+ #### 2️⃣ `/copy [filter]`
179
+ Shortcut for `/broadcast copy`
180
+
181
+ **Examples:**
182
+ ```
183
+ /copy users
184
+ /copy
185
+ ```
186
+
187
+ #### 3️⃣ `/forward [filter]`
188
+ Shortcut for `/broadcast forward`
189
+
190
+ **Examples:**
191
+ ```
192
+ /forward groups
193
+ /forward
194
+ ```
195
+
196
+ #### 4️⃣ `/addmsg <broadcast_id>`
197
+ Add message to existing broadcast
198
+
199
+ **Example:**
200
+ ```
201
+ /addmsg abc123
202
+ ```
203
+
204
+ ### Usage Steps
205
+
206
+ 1. **Create Broadcast:**
207
+ - Reply to the message you want to broadcast
208
+ - Send `/copy` or `/forward` command
209
+ - You'll receive a broadcast ID
210
+
211
+ 2. **Add More Messages (Optional):**
212
+ - Reply to another message
213
+ - Send `/addmsg <broadcast_id>` command
214
+
215
+ 3. **Settings (Optional):**
216
+ - **Preview** button for preview
217
+ - **Pin** button to pin messages after sending
218
+
219
+ 4. **Start Sending:**
220
+ - Click **Start** button
221
+ - Sending progress is displayed in real-time
222
+
223
+ 5. **Control Sending:**
224
+ - **Pause**: Temporary pause
225
+ - **Resume**: Resume sending
226
+ - **Stop**: Complete stop
227
+
228
+ ---
229
+
230
+ ## ⚙️ Advanced Configuration
231
+
232
+ ### BroadcastOptions
233
+
234
+ ```typescript
235
+ interface BroadcastOptions {
236
+ // Storage instance (required)
237
+ storage: Storage;
238
+
239
+ // Function to get chats (required)
240
+ getBroadcastChats: (botId: number, offset: number, limit: number, filter?: string) => Promise<string[] | number[]>;
241
+
242
+ // API instance (required)
243
+ getApi: (botId: number) => Promise<Api> | Api;
244
+
245
+ // Designate main instance (required)
246
+ isMainInstance: boolean;
247
+
248
+ // List of authorized users
249
+ sudoUsers?: number[];
250
+
251
+ // Custom permission check function
252
+ hasPermission?: (ctx: Context) => boolean | Promise<boolean>;
253
+
254
+ // Manage blocked users
255
+ setRestricted?: (botId: number, chatId: string, type: 'block' | 'deactivated' | 'banned' | 'restricted') => Promise<void>;
256
+
257
+ // Number of chats processed per chunk (default: 100)
258
+ chunkSize?: number;
259
+
260
+ // Redis key prefix (default: 'brdc:')
261
+ keyPrefix?: string;
262
+
263
+ // Progress report frequency in milliseconds (default: 60000)
264
+ reportFrequency?: number;
265
+
266
+ // Queue check interval in milliseconds (default: 60000)
267
+ checkQueueInterval?: number;
268
+
269
+ // Progress callback function (optional)
270
+ progressCallback?: (id: string, sent: number, error: number, total: number) => void;
271
+
272
+ // Enable Paid Broadcast (default: false)
273
+ allowPaidBroadcast?: boolean;
274
+
275
+ // Customize command names
276
+ cmds?: {
277
+ broadcast?: string; // default: 'broadcast'
278
+ copy?: string; // default: 'copy'
279
+ forward?: string; // default: 'forward'
280
+ addmsg?: string; // default: 'addmsg'
281
+ };
282
+ }
283
+ ```
284
+
285
+ ### Complete Example with All Settings
286
+
287
+ ```typescript
288
+ import { Bot, Context } from "grammy";
289
+ import { createBroadcaster, RedisStorage } from "grammy-broadcast";
290
+ import Redis from "ioredis";
291
+
292
+ const bot = new Bot("YOUR_BOT_TOKEN");
293
+ const redis = new Redis();
294
+
295
+ const broadcaster = createBroadcaster({
296
+ // Storage
297
+ storage: new RedisStorage(redis),
298
+
299
+ // Get chats with filter
300
+ getBroadcastChats: async (botId, offset, limit, filter) => {
301
+ if (filter === 'users') {
302
+ const users = await UserModel.find({ blocked: { $ne: true } })
303
+ .skip(offset)
304
+ .limit(limit)
305
+ .select("uid")
306
+ .lean();
307
+ return users.map(u => u.uid);
308
+ }
309
+
310
+ if (filter === 'groups') {
311
+ const groups = await GroupModel.find({ botRemoved: { $ne: true } })
312
+ .skip(offset)
313
+ .limit(limit)
314
+ .select("chatId")
315
+ .lean();
316
+ return groups.map(g => g.chatId);
317
+ }
318
+
319
+ if (filter === 'premium') {
320
+ const premiumUsers = await UserModel.find({
321
+ premium: true,
322
+ blocked: { $ne: true }
323
+ })
324
+ .skip(offset)
325
+ .limit(limit)
326
+ .select("uid")
327
+ .lean();
328
+ return premiumUsers.map(u => u.uid);
329
+ }
330
+
331
+ // No filter - all chats
332
+ const allChats = await ChatModel.find({})
333
+ .skip(offset)
334
+ .limit(limit)
335
+ .select("chatId")
336
+ .lean();
337
+ return allChats.map(c => c.chatId);
338
+ },
339
+
340
+ // Manage blocked users
341
+ setRestricted: async (botId, chatId, type) => {
342
+ if (type === 'block' || type === 'deactivated') {
343
+ await UserModel.updateOne(
344
+ { uid: chatId },
345
+ { blocked: true, blockedAt: new Date() }
346
+ );
347
+ } else if (type === 'banned' || type === 'restricted') {
348
+ await GroupModel.updateOne(
349
+ { chatId },
350
+ { botRemoved: true, removedAt: new Date() }
351
+ );
352
+ }
353
+ },
354
+
355
+ // API instance
356
+ getApi: async (botId) => bot.api,
357
+
358
+ // Settings
359
+ isMainInstance: true,
360
+ sudoUsers: [123456789, 987654321],
361
+
362
+ // Or use custom function for permissions
363
+ // hasPermission: async (ctx: Context) => {
364
+ // const user = await UserModel.findOne({ uid: ctx.from?.id });
365
+ // return user?.isAdmin === true;
366
+ // },
367
+
368
+ // Advanced settings
369
+ chunkSize: 50, // Process 50 chats per batch
370
+ keyPrefix: 'mybot:brdc:', // Custom prefix
371
+ reportFrequency: 30000, // Report every 30 seconds
372
+ checkQueueInterval: 10000, // Check queue every 10 seconds
373
+ allowPaidBroadcast: true, // Enable paid broadcast
374
+
375
+ // Progress callback
376
+ progressCallback: (id, sent, error, total) => {
377
+ console.log(`Broadcast ${id}: ${sent}/${total} sent, ${error} errors`);
378
+ },
379
+
380
+ // Customize commands
381
+ cmds: {
382
+ broadcast: 'bbroadcast',
383
+ copy: 'bcopy',
384
+ forward: 'bforward',
385
+ addmsg: 'baddmsg'
386
+ }
387
+ });
388
+
389
+ bot.use(broadcaster.getMiddleware());
390
+ bot.start();
391
+ ```
392
+
393
+ ---
394
+
395
+ ## 🎯 Practical Examples
396
+
397
+ ### Example 1: Broadcast to Specific Users
398
+
399
+ ```typescript
400
+ // Send to premium users
401
+ /broadcast copy premium
402
+
403
+ // Send to active groups
404
+ /broadcast forward active_groups
405
+ ```
406
+
407
+ ### Example 2: Using in Cluster
408
+
409
+ ```typescript
410
+ // Main instance
411
+ const broadcaster = createBroadcaster({
412
+ // ...
413
+ isMainInstance: true, // Only in this instance
414
+ });
415
+
416
+ // Other instances
417
+ const broadcaster = createBroadcaster({
418
+ // ...
419
+ isMainInstance: false, // In other instances
420
+ });
421
+ ```
422
+
423
+ ### Example 3: Error Management
424
+
425
+ ```typescript
426
+ const broadcaster = createBroadcaster({
427
+ // ...
428
+ setRestricted: async (botId, chatId, type) => {
429
+ console.log(`Chat ${chatId} restricted: ${type}`);
430
+
431
+ // Save to log
432
+ await LogModel.create({
433
+ chatId,
434
+ type,
435
+ timestamp: new Date()
436
+ });
437
+
438
+ // Update database
439
+ if (type === 'block') {
440
+ await UserModel.updateOne(
441
+ { uid: chatId },
442
+ { blocked: true, blockedReason: 'user_blocked_bot' }
443
+ );
444
+ }
445
+ }
446
+ });
447
+ ```
448
+
449
+ ### Example 4: Using Progress Callback
450
+
451
+ ```typescript
452
+ const broadcaster = createBroadcaster({
453
+ // ...
454
+ progressCallback: async (id, sent, error, total) => {
455
+ // Send report to admin
456
+ const progress = ((sent + error) / total * 100).toFixed(2);
457
+ await bot.api.sendMessage(
458
+ ADMIN_CHAT_ID,
459
+ `📊 Broadcast ${id}\n` +
460
+ `✅ Sent: ${sent}\n` +
461
+ `❌ Errors: ${error}\n` +
462
+ `📈 Progress: ${progress}%`
463
+ );
464
+ }
465
+ });
466
+ ```
467
+
468
+ ---
469
+
470
+ ## 🔧 Storage Implementations
471
+
472
+ ### RedisStorage
473
+
474
+ For use in production and cluster environments:
475
+
476
+ ```typescript
477
+ import { RedisStorage } from "grammy-broadcast";
478
+ import Redis from "ioredis";
479
+
480
+ const redis = new Redis({
481
+ host: 'localhost',
482
+ port: 6379,
483
+ // or connection string
484
+ // host: 'redis://localhost:6379'
485
+ });
486
+
487
+ const storage = new RedisStorage(redis);
488
+ ```
489
+
490
+ ### MemoryStorage
491
+
492
+ For development and testing (data stored in memory):
493
+
494
+ ```typescript
495
+ import { MemoryStorage } from "grammy-broadcast";
496
+
497
+ const storage = new MemoryStorage();
498
+ ```
499
+
500
+ ### FileStorage
501
+
502
+ For local storage without Redis:
503
+
504
+ ```typescript
505
+ import { FileStorage } from "grammy-broadcast";
506
+
507
+ const storage = new FileStorage("./broadcast-storage");
508
+ ```
509
+
510
+ ---
511
+
512
+ ## 📊 Progress Display
513
+
514
+ The plugin automatically displays broadcast progress:
515
+
516
+ ```
517
+ ⌛ Broadcasting
518
+ ████████░░ (80%)
519
+ ⌛ Progress: 800/1000
520
+ ✅ Sent: 750
521
+ ❌ Error: 50 (5%)
522
+ ⚡ Rate: 30 msg/sec
523
+ ```
524
+
525
+ Control buttons:
526
+ - **Progress Bar**: Show progress details
527
+ - **Pause**: Temporary pause
528
+ - **Stop**: Complete stop
529
+
530
+ ---
531
+
532
+ ## 🛡️ Error Management
533
+
534
+ The plugin automatically handles the following errors:
535
+
536
+ - ✅ **Blocked Users**: Blocked users
537
+ - ✅ **Deactivated Accounts**: Deactivated accounts
538
+ - ✅ **Kicked from Groups**: Kicked from groups
539
+ - ✅ **Restricted**: Access restrictions
540
+ - ✅ **Rate Limiting**: Automatic Telegram rate limit management
541
+
542
+ Errors are automatically handled in the `setRestricted` callback.
543
+
544
+ ---
545
+
546
+ ## 🚀 Paid Broadcast
547
+
548
+ To use Paid Broadcast feature (sending at 1000 messages/second):
549
+
550
+ ```typescript
551
+ const broadcaster = createBroadcaster({
552
+ // ...
553
+ allowPaidBroadcast: true, // Requires Telegram Stars
554
+ });
555
+ ```
556
+
557
+ **Note:** This feature requires Telegram Stars and is paid.
558
+
559
+ ---
560
+
561
+ ## 📝 Important Notes
562
+
563
+ 1. **Main Instance**: In cluster environments, only one instance should have `isMainInstance: true`
564
+ 2. **Storage**: Always use Redis for production
565
+ 3. **Rate Limiting**: The plugin automatically manages rate limiting
566
+ 4. **Error Handling**: Always implement `setRestricted`
567
+ 5. **Database**: You can use any database for `getBroadcastChats`
568
+
569
+ ---
570
+
571
+ ## 🤝 Contributing
572
+
573
+ Contributions are always welcome! Please make sure before submitting a PR that:
574
+
575
+ - ✅ Your code follows the project standards
576
+ - ✅ You've added necessary tests
577
+ - ✅ You've updated the documentation
578
+
579
+ ---
580
+
581
+ ## 📄 License
582
+
583
+ This project is licensed under the [MIT](LICENSE) License.
584
+
585
+ ---
586
+
587
+ ## 👤 Author
588
+
589
+ **Aria** - [smaznet98@gmail.com](mailto:smaznet98@gmail.com)
590
+
591
+ ---
592
+
593
+ ## ⭐ Stars
594
+
595
+ If this project was useful to you, please give it a star ⭐!
596
+
597
+ ---
598
+
599
+ <div align="center">
600
+
601
+ **Made with ❤️ for the Grammy community**
602
+
603
+ </div>