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

telenest

v1.5.0

Published

Fully-typed NestJS module for Telegram: the Bot API (via Telegraf) and the MTProto user-account client (via GramJS). Send/receive as a bot, or sign in with your own account and control it from your app.

Readme

telenest

CI License: MIT Node

A fully-typed NestJS module for Telegram that wraps two different Telegram APIs behind one cohesive, strictly-typed package. Use the Bot API (powered by Telegraf) to run a normal @BotFather bot, and/or use the MTProto user-account client (powered by GramJS) to sign in as your own Telegram account and drive it from your app. Both sides share one error hierarchy, ship with pluggable session persistence, and are designed around testable seams so you can unit-test everything without ever touching the network.


Features

Bot API side (Telegraf — a normal bot)

  • TelegramBotModule.forRoot / forRootAsync — synchronous or ConfigService-driven async configuration.
  • TelegramBotService — an injectable, strongly-typed facade over Telegraf whose method signatures are derived from Telegraf's own Telegram type (via Parameters/ReturnType), so they never drift from the installed version.
    • Messaging: sendMessage, sendPhoto, sendDocument, sendVideo, sendAudio, sendMediaGroup, sendLocation, sendChatAction, forwardMessage, copyMessage.
    • Editing & deletion: editMessageText, editMessageReplyMarkup, deleteMessage.
    • Callbacks: answerCbQuery.
    • Chat & member management: getMe, getChat, getChatMembersCount, banChatMember, pinChatMessage.
    • Commands: setMyCommands, getMyCommands.
    • Files: getFile, getFileLink.
    • Webhooks: setWebhook, deleteWebhook, getWebhookInfo, webhookCallback.
    • Handler delegates: start, help, command, hears, action, on, use, catch.
  • Automatic launch/stop wired into the Nest lifecycle (long-polling by default, webhook mode supported); set launch: false to take manual control.
  • Escape hatches: TelegramBotService.instance (raw Telegraf) and TelegramBotService.telegram (raw Telegraf Telegram client), plus the TELEGRAM_BOT injection token — nothing is hidden behind the facade.
  • Fluent keyboard builders: InlineKeyboardBuilder, ReplyKeyboardBuilder, plus the removeKeyboard and forceReply helpers.
  • Multiple named bots in one app: register each with forRoot({ name }) / forRootAsync({ name }), inject its facade with @InjectBot(name), and scope decorator handlers with @TelegramUpdate({ bot: name }) — each bot fully isolated, no nestjs-telegraf. Omitting name keeps the single-bot API unchanged. See MULTIPLE-BOTS.md.

User-account side (GramJS / MTProto — sign in as yourself)

  • TelegramClientModule.forRoot / forRootAsync — configured with the apiId / apiHash from my.telegram.org.
  • TelegramAuthService — drives the login state machine: sendCodesignIn → (checkPassword for 2FA), plus logOut, isAuthorized, and exportSession.
  • TelegramUserService — act as your own account: getMe, getDialogs, getMessages, sendMessage/sendToSelf, plus media (sendFile, downloadMedia, downloadProfilePhoto, getMediaInfo, and downloadMediaRange/streamMedia for progressive-video HTTP Range streaming), chat/channel management (joinChannel, leaveChannel, getParticipants, searchMessages, getFullChat), and message operations (editMessage, deleteMessages, forwardMessages, markAsRead, pinMessage).
  • Pluggable session persistence via the SessionStore interface, with InMemorySessionStore and FileSessionStore (writes 0o600, owner-only) included — bring your own (Redis, a secrets manager, etc.) by implementing three methods (load / save / clear).
  • A clean test seam: services depend only on the IGramClient interface, and the GramJS package is touched in exactly one adapter file. Inject a fake via clientFactory and you never hit the network.
  • Library-owned DTOs (GramUser, GramDialog, GramMessage, GramSendMessageParams, …) so consumers never import GramJS just to model a user or a message.
  • Multiple named user accounts in one app: register each with forRoot({ name }) / forRootAsync({ name }), inject its services with @InjectTelegramUser(name) / @InjectTelegramAuth(name), and scope inbound handlers with @OnUserMessage(filter, { client: name }) — each account fully isolated, with its own session. Omitting name keeps the single-account API unchanged. See MULTIPLE-ACCOUNTS.md.

Shared

  • One typed error hierarchy: a TelegramError base with TelegramConfigError, TelegramBotApiError, TelegramClientError, TelegramAuthError (carrying a closed TelegramAuthErrorCode set), and TelegramSessionError, plus the isTelegramError type guard.
  • TelegramModule.forRoot({ bot?, client?, isGlobal? }) — an umbrella module that composes both sides from a single synchronous options object.
  • Strict TypeScript throughout (no any, no enumas const unions only), and every export documented with JSDoc.

Bot extras

  • Decorator handler system@TelegramUpdate classes with @Command/@Hears/@Action/@On/@Use and @Ctx/@Sender/… param decorators; optional auto-registration of the Telegram command menu from @Command descriptions.
  • Nest-style enhancers@UseTelegramGuards/Interceptors/Filters, with built-in allowlist and (memory-bounded) rate-limit guards.
  • Built-in webhook controller — secret-token-verified POST route (generateWebhookSecret() helper; a secret is required by default).
  • Mini App init-data validationvalidateWebAppInitData() (HMAC-SHA256, constant-time, 24h freshness by default).
  • Helperscallback-data codec (64-byte limit), splitMessageText (4096-char chunking, used by sendLongMessage), and withRetry (429 retry_after back-off).

Production

  • Observability — health indicators, update metrics, and an OpenTelemetry tracer bridge.
  • Five session stores — in-memory, file (0o600, atomic), key-value, Redis, and an AES-256-GCM encrypted wrapper.
  • telenest/testing — ready-made mocks (createMockGramClient, createMockBotContext) and DTO builders, pulling in no SDK and no test runner.

Install

npm i telenest

The NestJS runtime peers are always required:

npm i @nestjs/common @nestjs/core reflect-metadata rxjs

Then install only the Telegram client(s) you usetelegraf and telegram are optional peer dependencies:

# Bot API only
npm i telegraf

# User account (MTProto) only
npm i telegram

# Both
npm i telegraf telegram

Import only the side you need (subpath exports)

The package exposes four subpath entry points (plus the root) so a bot-only app never pulls in GramJS, and a user-account-only app never pulls in Telegraf:

| Import | Pulls in | Use when | | ------------------------- | ------------------------ | --------------------------------- | | telenest/bot | telegraf only | You only run a bot | | telenest/client | telegram (GramJS) only | You only control a user account | | telenest/common | neither | Just the shared error/types layer | | telenest/testing | neither (test-only) | Mocking the library in your tests | | telenest (root) | both | You use both sides |

import { TelegramBotModule } from "telenest/bot"; // no GramJS loaded
import { TelegramClientModule } from "telenest/client"; // no Telegraf loaded

Importing from the root (telenest) re-exports everything and therefore loads both SDKs — use the subpaths when you only need one side.


Two APIs at a glance

| | Bot API (Telegraf) | User account (MTProto / GramJS) | | ------------------ | ------------------------------------------------------------------------------------------------------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------- | | Identity | A bot created via @BotFather | Your own real Telegram account | | Auth | A single bot token | apiId + apiHash from my.telegram.org, then a phone/code/2FA login that yields a string session | | What it can do | Reply to users, run commands, push notifications, manage groups/channels where the bot is an admin, inline keyboards, webhooks | Read your dialog list, fetch message history, send messages as you (including to "Saved Messages"), reach any chat you're a member of | | Module | TelegramBotModule | TelegramClientModule | | Main service | TelegramBotService | TelegramUserService (+ TelegramAuthService) | | When to use | Building a bot users talk to; sending automated notifications from a server | Automating your own account; reading/aggregating your own chats; user-only actions a bot cannot perform |


📚 Documentation

→ Complete Documentation Index

Quick Links

By Topic

flowchart LR
  App[Your NestJS App]
  App -->|TelegramBotService| BotMod[TelegramBotModule]
  App -->|TelegramUserService / TelegramAuthService| ClientMod[TelegramClientModule]
  BotMod -->|Telegraf| BotAPI[(Telegram Bot API)]
  ClientMod -->|GramJS / IGramClient| MTProto[(Telegram MTProto)]
  ClientMod -. session string .-> Store[SessionStore]

A bot cannot do everything a user can, and vice-versa. Many apps run both: a bot for user-facing interactions and a user-account client for reading/automating your own chats.


Quick Start — Bot API

Configure the bot module (async, pulling the token from ConfigService) and inject TelegramBotService anywhere.

import { Module, Injectable } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { TelegramBotModule, TelegramBotService } from "telenest";

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    TelegramBotModule.forRootAsync({
      isGlobal: true,
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        token: config.getOrThrow<string>("BOT_TOKEN"),
        // launch: false, // disable auto-launch to mount a webhook yourself
      }),
    }),
  ],
})
export class AppModule {}
@Injectable()
export class NotificationsService {
  public constructor(private readonly bot: TelegramBotService) {}

  /** Sends a notification to a chat as the bot. */
  public async notify(chatId: number | string, text: string): Promise<void> {
    await this.bot.sendMessage(chatId, text);
  }
}

Register handlers and attach an inline keyboard with the builders:

import { InlineKeyboardBuilder } from "telenest";

this.bot.start(async (ctx) => {
  const keyboard = new InlineKeyboardBuilder()
    .url("Docs", "https://core.telegram.org/bots/api")
    .callback("Ping", "ping")
    .build();
  await ctx.reply("Welcome!", { reply_markup: keyboard });
});

this.bot.action("ping", async (ctx) => {
  await ctx.answerCbQuery("pong");
});

By default the bot launches in long-polling mode on application bootstrap and stops on shutdown. Pass a launchOptions.webhook block (or set launch: false and mount bot.webhookCallback(...) yourself) to run in webhook mode.


Quick Start — User account (MTProto)

1. Get a session string

The user-account side authenticates your account, so it needs a one-time interactive login. The bundled CLI (examples/login-cli.ts) walks you through it and prints a portable session string.

# .env: TG_API_ID and TG_API_HASH from https://my.telegram.org
npm run login
Phone (+countrycode…): +15551234567
Login code from Telegram: 12345
Two-factor (2FA) password: ********   # only if 2FA is enabled
Signed in successfully.

=== SESSION STRING (save as TG_SESSION) ===
1BVtsOMM...   # copy this into .env as TG_SESSION

The session string grants full access to your account — treat it like a password and never commit it. See the Security note below.

2. Wire the module and use your account

Configure TelegramClientModule with your apiId / apiHash, resume from TG_SESSION, and persist future sessions with a SessionStore.

import { Module, Injectable } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { TelegramClientModule, TelegramUserService, FileSessionStore } from "telenest";

@Module({
  imports: [
    ConfigModule.forRoot({ isGlobal: true }),
    TelegramClientModule.forRootAsync({
      isGlobal: true,
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        apiId: Number(config.getOrThrow<string>("TG_API_ID")),
        apiHash: config.getOrThrow<string>("TG_API_HASH"),
        session: config.get<string>("TG_SESSION"), // resume from the CLI output
        sessionStore: new FileSessionStore("./.telegram.session"),
      }),
    }),
  ],
})
export class AppModule {}
@Injectable()
export class SelfNotesService {
  public constructor(private readonly user: TelegramUserService) {}

  /** Sends a note to your own "Saved Messages" chat — as YOU, not a bot. */
  public async remember(text: string): Promise<void> {
    await this.user.sendToSelf(text);
  }
}

Need both sides at once? Use the umbrella module:

import { TelegramModule } from "telenest";

TelegramModule.forRoot({
  isGlobal: true,
  bot: { token: process.env.BOT_TOKEN! },
  client: {
    apiId: Number(process.env.TG_API_ID),
    apiHash: process.env.TG_API_HASH!,
  },
});

Documentation

| Guide | What it covers | | -------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------- | | docs/TELEGRAM-MODULE.md | The umbrella TelegramModule, module composition, and global registration | | docs/BOT-API.md | TelegramBotModule, TelegramBotService, keyboards, and the launch/webhook lifecycle | | docs/BOT-UPDATE-DECORATORS.md | @TelegramUpdate handler classes — @Command/@Hears/@Action/@On + @Ctx/@Sender param decorators | | docs/BOT-GUARDS-FILTERS-INTERCEPTORS.md | @UseTelegramGuards/@UseTelegramInterceptors/@UseTelegramFilters, built-in allowlist & rate-limit guards, default exception filter | | docs/MULTIPLE-BOTS.md | Multiple named bots in one app — forRoot({ name }), @InjectBot(name), @TelegramUpdate({ bot }) scoping | | docs/MINI-APP-INIT-DATA.md | validateWebAppInitData() — verify & parse Telegram Mini App initData server-side | | docs/USER-CLIENT-MTPROTO.md | TelegramClientModule, TelegramUserService, dialogs/messages, and the DTOs | | docs/MULTIPLE-ACCOUNTS.md | Multiple named user accounts in one app — forRoot({ name }), @InjectTelegramUser(name), @OnUserMessage(f, { client }) | | docs/AUTHENTICATION.md | The sendCodesignIncheckPassword flow and SessionStore persistence | | docs/TESTING.md | Unit-testing both sides — ready-made telenest/testing mocks plus the IGramClient / clientFactory seam |


Testing

The library ships with 740+ tests and is built to keep network I/O out of your suite:

  • Ready-made utilities in telenest/testing: createMockGramClient() (a fully-typed jest.Mocked<IGramClient>), provideMockGramClient() (binds it to the TELEGRAM_GRAM_CLIENT token), createMockBotContext() (a spyable Telegraf Context), and DTO builders (aGramUser/aGramMessage/aGramDialog). The subpath pulls in no SDK and no test runner. See docs/TESTING.md.
  • The MTProto services depend only on the IGramClient interface — the telegram (GramJS) package is imported in exactly one adapter file. Supply a fake IGramClient and construct TelegramUserService / TelegramAuthService directly (the bundled login CLI does exactly this when run outside of Nest DI).
  • Inside Nest, pass a clientFactory to TelegramClientModule (or override the TELEGRAM_GRAM_CLIENT token) to swap in a fake client without touching the network.
  • On the Bot side, InMemorySessionStore gives the client side a zero-I/O session backend, and every facade method funnels through a single error-normalizing path that wraps failures in TelegramBotApiError.

Security

  • A session string is a credential equivalent to your password. It encodes the auth keys that let anyone reconnect as your account without the phone code or 2FA. Never commit it, never log it, never paste it into a chat or an issue.
  • Keep session files (FileSessionStore writes them 0o600, owner read/write only) out of version control and off shared volumes. Add .telegram.session and TG_SESSION to your .gitignore / secret store.
  • If a session leaks, revoke it immediately: call TelegramAuthService.logOut() (or terminate the session from Telegram's Settings → Devices), then run the login flow again to mint a fresh one.
  • apiId / apiHash and the bot token are likewise secrets — load them from the environment or a secrets manager, not from source.

License

MIT