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

nestjs-telegram-notify

v0.1.10

Published

NestJS module for queued Telegram notifications via BullMQ and Grammy

Downloads

410

Readme

nestjs-telegram-notify

NestJS module for queued Telegram notifications via Grammy and BullMQ.

Import NotifyModule.forRoot() in your Nest app — it registers Redis/BullMQ, enqueues jobs, processes them, and sends Telegram messages. All configuration is passed explicitly to forRoot().

Architecture

flowchart LR
  subgraph app [Your Nest app]
    NM[NotifyModule]
    Client[NotifyClient]
  end

  subgraph redis [Redis]
    Payloads[(payload keys)]
    Queues[(BullMQ queues)]
  end

  TG[Telegram API]

  Client --> Payloads
  Client --> Queues
  Queues --> NM
  Payloads --> NM
  NM --> TG

Flow:

  1. Your code calls NotifyClient.enqueueBroadcast() — stores payload in Redis and enqueues a BullMQ flow.
  2. Child jobs load the payload and send Telegram messages (text or media group).
  3. Parent job runs after all children finish and deletes the Redis payload.

Prerequisites

  • Node.js 18+ (20 recommended)
  • Redis — BullMQ connection + payload storage
  • Telegram bot token — from @BotFather
  • Public media URLs — media items must be full https://… URLs Telegram can fetch (no file upload / S3 integration)

Install

npm install nestjs-telegram-notify
# or
yarn add nestjs-telegram-notify

Peer dependencies (install in your app if not already present):

npm install @nestjs/common @nestjs/core @nestjs/bullmq bullmq ioredis reflect-metadata rxjs

Package exports

| Import path | Contents | |-------------|----------| | nestjs-telegram-notify | Shared types/helpers + Nest module | | nestjs-telegram-notify/shared | Types, constants, NotifyPayloadStore, flow helpers | | nestjs-telegram-notify/nest | NotifyModule, NotifyClient, enqueue types | | nestjs-telegram-notify/shared | Types, constants, flow helpers, NotifyPayloadStore |

Public API

These are the stable surfaces for v0.x. Import from the paths below; avoid deep imports into dist/... file paths.

nestjs-telegram-notify (root)

Re-exports everything from ./shared and ./nest. Typical imports:

| Export | Kind | Purpose | |--------|------|---------| | NotifyModule | Module | NotifyModule.forRoot({ redis, botToken, ... }) | | NotifyClient | Interface | Inject via NOTIFY_CLIENT or use NotifyClientService | | NotifyClientService | Service | enqueueBroadcast({ recipients, content }) → { batchId } | | NOTIFY_CLIENT | Symbol | DI token for NotifyClient | | Locale | Enum | RU, KZ | | EnqueueBroadcastRequest | Type | Recipients + message/media content | | EnqueueBroadcastResult | Type | { batchId: string } | | NotifyModuleOptions | Type | Module config (redis, botToken, botProxy?, payloadTtlSeconds?) | | NOTIFY_QUEUES | Constant | Package-scoped BullMQ queue names |

nestjs-telegram-notify/nest

Same Nest module and client exports as root, without re-exporting all shared helpers. Use when you want an explicit Nest-only import:

import { NotifyModule, NotifyClientService } from 'nestjs-telegram-notify/nest';

nestjs-telegram-notify/shared

Lower-level types and helpers. Most apps only need root imports; use shared for custom integrations or tests:

| Export | Purpose | |--------|---------| | SharedNotificationPayload | Redis payload shape | | NotificationChildJobData | Child job data (recipient, locale, payloadKey) | | NotifyPayloadStore | Redis get/set/del for payloads | | NOTIFY_QUEUE_* | Individual queue name constants | | buildSendChildJob, buildSendParentJobData, buildSendFlowQueuesOptions | BullMQ flow helpers | | NOTIFY_REDIS_OPTIONS, NOTIFY_MODULE_OPTIONS | DI tokens |

Not public API: src/nest/processors/*, CachedNotifyPayloadStore, and other internal providers are not exported from package entry points.

Setup

Pass all options to NotifyModule.forRoot(). Register BullMQ once at app root with the same Redis connection:

import { Module } from '@nestjs/common';
import { BullModule } from '@nestjs/bullmq';
import { NotifyModule } from 'nestjs-telegram-notify';

const redis = {
  host: process.env.REDIS_HOST!,
  port: Number(process.env.REDIS_PORT),
  ...(process.env.REDIS_PASSWORD ? { password: process.env.REDIS_PASSWORD } : {}),
};

@Module({
  imports: [
    BullModule.forRoot({ connection: redis }),
    NotifyModule.forRoot({
      redis,
      botToken: process.env.BOT_TOKEN!,
      botProxy: process.env.BOT_PROXY, // optional
      payloadTtlSeconds: 24 * 60 * 60, // optional, default 48h
    }),
  ],
})
export class AppModule {}

Enqueueing notifications

Inject NotifyClient (or NotifyClientService) and call enqueueBroadcast():

import { Injectable } from '@nestjs/common';
import {
  Locale,
  NotifyClientService,
} from 'nestjs-telegram-notify';

@Injectable()
export class NotificationService {
  constructor(private readonly notifyClient: NotifyClientService) {}

  async notifyUsers() {
    const { batchId } = await this.notifyClient.enqueueBroadcast({
      recipients: [
        { chatId: '123456789', locale: Locale.RU },
        { chatId: '987654321', locale: Locale.KZ },
      ],
      content: {
        method: 'sendMessage',
        captions: {
          [Locale.RU]: '<b>Hello</b>',
          [Locale.KZ]: '<b>Сәлем</b>',
        },
      },
    });

    console.log(`Enqueued send ${batchId}`);
  }
}

batchId is generated internally and returned for logging or tracing. It keys the shared Redis payload (notify:{NOTIFY_NAMESPACE}:payload:{batchId}) until the send completes.

For tests or swappable implementations, inject by Symbol token:

import { Inject, Injectable } from '@nestjs/common';
import { NOTIFY_CLIENT, NotifyClient } from 'nestjs-telegram-notify';

@Injectable()
export class NotificationService {
  constructor(@Inject(NOTIFY_CLIENT) private readonly notifyClient: NotifyClient) {}
}

Media group example:

const { batchId } = await this.notifyClient.enqueueBroadcast({
  recipients: [{ chatId: '123456789', locale: Locale.RU }],
  content: {
    method: 'sendMediaGroup',
    media: [{ type: 'photo', url: 'https://cdn.example.com/banner.jpg' }],
    captions: { [Locale.RU]: '<b>Sale</b>' },
  },
});

Payload shape

type SharedNotificationPayload = {
  method: 'sendMediaGroup' | 'sendMessage' | 'sendPhoto' | 'sendVideo';
  text?: string;
  captions?: Record<Locale, string>;
  buttons?: InlineKeyboardMarkup;
  media?: Array<{
    type: 'photo' | 'video' | 'document';
    url: string; // full public HTTPS URL
    caption?: string;
    parse_mode?: 'HTML';
  }>;
};
  • sendMessage — uses text or locale-specific captions[locale].
  • sendPhoto — uses media with exactly one { type: 'photo', url } item + optional caption/buttons.
  • sendVideo — uses media with exactly one { type: 'video', url } item + optional caption/buttons.
  • sendMediaGroup — uses media[]; caption is applied to the first item. Each url must be publicly reachable by Telegram.

Configuration

The library reads one environment variable for Redis isolation. All other options are passed to NotifyModule.forRoot():

| Option / env | Required | Description | |--------------|----------|-------------| | NOTIFY_NAMESPACE | Yes | Env var — unique app id for shared Redis (lowercase, [a-z0-9-]). Load .env before importing the package. | | redis | Yes | ioredis RedisOptions (BullMQ + payload storage) | | botToken | Yes | Telegram bot token | | botProxy | No | HTTPS proxy URL for Telegram API | | payloadTtlSeconds | No | Redis payload TTL (default 48h) |

Queue names & DI tokens

Queue names are derived from NOTIFY_NAMESPACE at import time:

| Constant | Pattern (example: NOTIFY_NAMESPACE=whiskas) | |----------|--------------------------------------------------| | NOTIFY_QUEUE_SEND_PRODUCER | whiskas-notify-producer | | NOTIFY_QUEUE_SEND_PARENT | whiskas-notify-parent | | NOTIFY_QUEUE_SEND_CHILD | whiskas-notify-child |

Import NOTIFY_QUEUES or individual NOTIFY_QUEUE_* constants — they already include your namespace prefix.

Internal Nest providers use Symbol tokens to avoid DI collisions:

  • NOTIFY_BOT
  • NOTIFY_MODULE_OPTIONS
  • NOTIFY_REDIS_OPTIONS
  • NOTIFY_CLIENT

Redis payload keys:

  • Payload: notify:{NOTIFY_NAMESPACE}:payload:{batchId} (TTL 48h by default; deleted after send completes)

Development (this repository)

yarn install
yarn build          # build library + verify exports
yarn pack:check     # verify npm tarball contents
yarn verify:exports # check package.json export paths exist in dist/

License

MIT — see LICENSE.