nestjs-telegram-notify
v0.1.10
Published
NestJS module for queued Telegram notifications via BullMQ and Grammy
Downloads
410
Maintainers
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 --> TGFlow:
- Your code calls
NotifyClient.enqueueBroadcast()— stores payload in Redis and enqueues a BullMQ flow. - Child jobs load the payload and send Telegram messages (text or media group).
- 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-notifyPeer dependencies (install in your app if not already present):
npm install @nestjs/common @nestjs/core @nestjs/bullmq bullmq ioredis reflect-metadata rxjsPackage 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— usestextor locale-specificcaptions[locale].sendPhoto— usesmediawith exactly one{ type: 'photo', url }item + optional caption/buttons.sendVideo— usesmediawith exactly one{ type: 'video', url }item + optional caption/buttons.sendMediaGroup— usesmedia[]; caption is applied to the first item. Eachurlmust 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_BOTNOTIFY_MODULE_OPTIONSNOTIFY_REDIS_OPTIONSNOTIFY_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.
