npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

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

TypeScript Grammy License


✨ 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-broadcast

Requirements

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 (copy or forward)
  • filter: Optional filter for selecting chats (passed to getBroadcastChats)

Examples:

/broadcast copy users
/broadcast forward groups

2️⃣ /copy [filter]

Shortcut for /broadcast copy

Examples:

/copy users
/copy

3️⃣ /forward [filter]

Shortcut for /broadcast forward

Examples:

/forward groups
/forward

4️⃣ /addmsg <broadcast_id>

Add message to existing broadcast

Example:

/addmsg abc123

Usage Steps

  1. Create Broadcast:

    • Reply to the message you want to broadcast
    • Send /copy or /forward command
    • You'll receive a broadcast ID
  2. Add More Messages (Optional):

    • Reply to another message
    • Send /addmsg <broadcast_id> command
  3. Settings (Optional):

    • Preview button for preview
    • Pin button to pin messages after sending
  4. Start Sending:

    • Click Start button
    • Sending progress is displayed in real-time
  5. 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_groups

Example 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/sec

Control 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

  1. Main Instance: In cluster environments, only one instance should have isMainInstance: true
  2. Storage: Always use Redis for production
  3. Rate Limiting: The plugin automatically manages rate limiting
  4. Error Handling: Always implement setRestricted
  5. 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