grammy-broadcast
v2.0.5
Published
A package for sending broadcast to chats using grammy
Readme
📢 Grammy Broadcast Plugin
A powerful and professional plugin for broadcasting messages to all Telegram bot chats
✨ Key Features
🚀 Performance & Speed
- ⚡ Smart Rate Limiting: Automatic Telegram rate limit management with auto-throttling on errors
- 🔥 Paid Broadcast Support: Support for paid broadcasts at 1000 messages per second (requires Telegram Stars)
- 📊 Concurrent Sending: Send messages concurrently with queue management
- 🎯 Queue Management: Broadcast queue management with pause/resume/stop capabilities
💾 Flexible Storage
- 🔴 Redis Storage: For production environments and clusters
- 💻 Memory Storage: For development and testing
- 📁 File Storage: For local storage without Redis
🎮 Full Control
- ⏸️ Pause/Resume: Pause and resume sending at any time
- 🛑 Stop: Complete stop and removal of broadcast
- 📈 Progress Tracking: Track progress with percentage, sent count, and errors
- 🔍 Preview: Preview messages before sending
- 📌 Pin Support: Ability to pin messages after sending
🎨 Send Types
- 📋 Copy Messages: Copy messages while preserving content
- ➡️ Forward Messages: Forward messages
- ✍️ Text Messages: Send plain text
- 📦 Multi-Message: Support for sending multiple messages in one broadcast
🔐 Security & Access Control
- 👥 Sudo Users: Define authorized users
- 🔒 Custom Permission: Custom permission system
- 🚫 Auto Block Management: Automatic management of blocked users
🌐 Cluster Support
- 🔄 Multi-Instance: Support for multiple instances
- 🎯 Main Instance: Designate main instance for queue processing
📦 Installation
npm install grammy-broadcast
# or
yarn add grammy-broadcast
# or
pnpm add grammy-broadcastRequirements
npm install grammy ioredis
# or
yarn add grammy ioredis🚀 Quick Start
Basic Example with Redis
import { Bot } from "grammy";
import { createBroadcaster, RedisStorage } from "grammy-broadcast";
import Redis from "ioredis";
const bot = new Bot("YOUR_BOT_TOKEN");
const redis = new Redis();
const broadcaster = createBroadcaster({
storage: new RedisStorage(redis),
isMainInstance: true, // For clusters, set true only on the main instance
sudoUsers: [123456789], // Authorized user IDs
getApi: async (botId) => bot.api, // or bot.api for single bot
getBroadcastChats: async (botId, offset, limit, filter) => {
// You can use any database here
// Example with MongoDB:
const users = await UserModel.find({ blocked: { $ne: true } })
.skip(offset)
.limit(limit)
.select("uid")
.lean();
return users.map(u => u.uid);
},
setRestricted: async (botId, chatId, type) => {
// Manage blocked users
if (type === 'block' || type === 'deactivated') {
await UserModel.updateOne({ uid: chatId }, { blocked: true });
}
}
});
// Add middleware to bot
bot.use(broadcaster.getMiddleware());
bot.start();Example with Memory Storage (for testing)
import { Bot } from "grammy";
import { createBroadcaster, MemoryStorage } from "grammy-broadcast";
const bot = new Bot("YOUR_BOT_TOKEN");
const broadcaster = createBroadcaster({
storage: new MemoryStorage(),
isMainInstance: true,
sudoUsers: [123456789],
getApi: async () => bot.api,
getBroadcastChats: async (botId, offset, limit, filter) => {
// Test chat list
return [123456, 789012, 345678];
}
});
bot.use(broadcaster.getMiddleware());
bot.start();Example with File Storage
import { Bot } from "grammy";
import { createBroadcaster, FileStorage } from "grammy-broadcast";
const bot = new Bot("YOUR_BOT_TOKEN");
const broadcaster = createBroadcaster({
storage: new FileStorage("./broadcast-data"), // Storage path
isMainInstance: true,
sudoUsers: [123456789],
getApi: async () => bot.api,
getBroadcastChats: async (botId, offset, limit, filter) => {
// Chat retrieval logic
return [];
}
});
bot.use(broadcaster.getMiddleware());
bot.start();📖 Usage Guide
Available Commands
1️⃣ /broadcast <type> [filter]
Send broadcast with specified type
Parameters:
type: Send type (copyorforward)filter: Optional filter for selecting chats (passed togetBroadcastChats)
Examples:
/broadcast copy users
/broadcast forward groups2️⃣ /copy [filter]
Shortcut for /broadcast copy
Examples:
/copy users
/copy3️⃣ /forward [filter]
Shortcut for /broadcast forward
Examples:
/forward groups
/forward4️⃣ /addmsg <broadcast_id>
Add message to existing broadcast
Example:
/addmsg abc123Usage Steps
Create Broadcast:
- Reply to the message you want to broadcast
- Send
/copyor/forwardcommand - You'll receive a broadcast ID
Add More Messages (Optional):
- Reply to another message
- Send
/addmsg <broadcast_id>command
Settings (Optional):
- Preview button for preview
- Pin button to pin messages after sending
Start Sending:
- Click Start button
- Sending progress is displayed in real-time
Control Sending:
- Pause: Temporary pause
- Resume: Resume sending
- Stop: Complete stop
⚙️ Advanced Configuration
BroadcastOptions
interface BroadcastOptions {
// Storage instance (required)
storage: Storage;
// Function to get chats (required)
getBroadcastChats: (botId: number, offset: number, limit: number, filter?: string) => Promise<string[] | number[]>;
// API instance (required)
getApi: (botId: number) => Promise<Api> | Api;
// Designate main instance (required)
isMainInstance: boolean;
// List of authorized users
sudoUsers?: number[];
// Custom permission check function
hasPermission?: (ctx: Context) => boolean | Promise<boolean>;
// Manage blocked users
setRestricted?: (botId: number, chatId: string, type: 'block' | 'deactivated' | 'banned' | 'restricted') => Promise<void>;
// Number of chats processed per chunk (default: 100)
chunkSize?: number;
// Redis key prefix (default: 'brdc:')
keyPrefix?: string;
// Progress report frequency in milliseconds (default: 60000)
reportFrequency?: number;
// Queue check interval in milliseconds (default: 60000)
checkQueueInterval?: number;
// Progress callback function (optional)
progressCallback?: (id: string, sent: number, error: number, total: number) => void;
// Enable Paid Broadcast (default: false)
allowPaidBroadcast?: boolean;
// Customize command names
cmds?: {
broadcast?: string; // default: 'broadcast'
copy?: string; // default: 'copy'
forward?: string; // default: 'forward'
addmsg?: string; // default: 'addmsg'
};
}Complete Example with All Settings
import { Bot, Context } from "grammy";
import { createBroadcaster, RedisStorage } from "grammy-broadcast";
import Redis from "ioredis";
const bot = new Bot("YOUR_BOT_TOKEN");
const redis = new Redis();
const broadcaster = createBroadcaster({
// Storage
storage: new RedisStorage(redis),
// Get chats with filter
getBroadcastChats: async (botId, offset, limit, filter) => {
if (filter === 'users') {
const users = await UserModel.find({ blocked: { $ne: true } })
.skip(offset)
.limit(limit)
.select("uid")
.lean();
return users.map(u => u.uid);
}
if (filter === 'groups') {
const groups = await GroupModel.find({ botRemoved: { $ne: true } })
.skip(offset)
.limit(limit)
.select("chatId")
.lean();
return groups.map(g => g.chatId);
}
if (filter === 'premium') {
const premiumUsers = await UserModel.find({
premium: true,
blocked: { $ne: true }
})
.skip(offset)
.limit(limit)
.select("uid")
.lean();
return premiumUsers.map(u => u.uid);
}
// No filter - all chats
const allChats = await ChatModel.find({})
.skip(offset)
.limit(limit)
.select("chatId")
.lean();
return allChats.map(c => c.chatId);
},
// Manage blocked users
setRestricted: async (botId, chatId, type) => {
if (type === 'block' || type === 'deactivated') {
await UserModel.updateOne(
{ uid: chatId },
{ blocked: true, blockedAt: new Date() }
);
} else if (type === 'banned' || type === 'restricted') {
await GroupModel.updateOne(
{ chatId },
{ botRemoved: true, removedAt: new Date() }
);
}
},
// API instance
getApi: async (botId) => bot.api,
// Settings
isMainInstance: true,
sudoUsers: [123456789, 987654321],
// Or use custom function for permissions
// hasPermission: async (ctx: Context) => {
// const user = await UserModel.findOne({ uid: ctx.from?.id });
// return user?.isAdmin === true;
// },
// Advanced settings
chunkSize: 50, // Process 50 chats per batch
keyPrefix: 'mybot:brdc:', // Custom prefix
reportFrequency: 30000, // Report every 30 seconds
checkQueueInterval: 10000, // Check queue every 10 seconds
allowPaidBroadcast: true, // Enable paid broadcast
// Progress callback
progressCallback: (id, sent, error, total) => {
console.log(`Broadcast ${id}: ${sent}/${total} sent, ${error} errors`);
},
// Customize commands
cmds: {
broadcast: 'bbroadcast',
copy: 'bcopy',
forward: 'bforward',
addmsg: 'baddmsg'
}
});
bot.use(broadcaster.getMiddleware());
bot.start();🎯 Practical Examples
Example 1: Broadcast to Specific Users
// Send to premium users
/broadcast copy premium
// Send to active groups
/broadcast forward active_groupsExample 2: Using in Cluster
// Main instance
const broadcaster = createBroadcaster({
// ...
isMainInstance: true, // Only in this instance
});
// Other instances
const broadcaster = createBroadcaster({
// ...
isMainInstance: false, // In other instances
});Example 3: Error Management
const broadcaster = createBroadcaster({
// ...
setRestricted: async (botId, chatId, type) => {
console.log(`Chat ${chatId} restricted: ${type}`);
// Save to log
await LogModel.create({
chatId,
type,
timestamp: new Date()
});
// Update database
if (type === 'block') {
await UserModel.updateOne(
{ uid: chatId },
{ blocked: true, blockedReason: 'user_blocked_bot' }
);
}
}
});Example 4: Using Progress Callback
const broadcaster = createBroadcaster({
// ...
progressCallback: async (id, sent, error, total) => {
// Send report to admin
const progress = ((sent + error) / total * 100).toFixed(2);
await bot.api.sendMessage(
ADMIN_CHAT_ID,
`📊 Broadcast ${id}\n` +
`✅ Sent: ${sent}\n` +
`❌ Errors: ${error}\n` +
`📈 Progress: ${progress}%`
);
}
});🔧 Storage Implementations
RedisStorage
For use in production and cluster environments:
import { RedisStorage } from "grammy-broadcast";
import Redis from "ioredis";
const redis = new Redis({
host: 'localhost',
port: 6379,
// or connection string
// host: 'redis://localhost:6379'
});
const storage = new RedisStorage(redis);MemoryStorage
For development and testing (data stored in memory):
import { MemoryStorage } from "grammy-broadcast";
const storage = new MemoryStorage();FileStorage
For local storage without Redis:
import { FileStorage } from "grammy-broadcast";
const storage = new FileStorage("./broadcast-storage");📊 Progress Display
The plugin automatically displays broadcast progress:
⌛ Broadcasting
████████░░ (80%)
⌛ Progress: 800/1000
✅ Sent: 750
❌ Error: 50 (5%)
⚡ Rate: 30 msg/secControl buttons:
- Progress Bar: Show progress details
- Pause: Temporary pause
- Stop: Complete stop
🛡️ Error Management
The plugin automatically handles the following errors:
- ✅ Blocked Users: Blocked users
- ✅ Deactivated Accounts: Deactivated accounts
- ✅ Kicked from Groups: Kicked from groups
- ✅ Restricted: Access restrictions
- ✅ Rate Limiting: Automatic Telegram rate limit management
Errors are automatically handled in the setRestricted callback.
🚀 Paid Broadcast
To use Paid Broadcast feature (sending at 1000 messages/second):
const broadcaster = createBroadcaster({
// ...
allowPaidBroadcast: true, // Requires Telegram Stars
});Note: This feature requires Telegram Stars and is paid.
📝 Important Notes
- Main Instance: In cluster environments, only one instance should have
isMainInstance: true - Storage: Always use Redis for production
- Rate Limiting: The plugin automatically manages rate limiting
- Error Handling: Always implement
setRestricted - Database: You can use any database for
getBroadcastChats
🤝 Contributing
Contributions are always welcome! Please make sure before submitting a PR that:
- ✅ Your code follows the project standards
- ✅ You've added necessary tests
- ✅ You've updated the documentation
📄 License
This project is licensed under the MIT License.
👤 Author
Aria - [email protected]
⭐ Stars
If this project was useful to you, please give it a star ⭐!
Made with ❤️ for the Grammy community
