Security News
pnpm 10.0.0 Blocks Lifecycle Scripts by Default
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
@grammyjs/nestjs
Advanced tools
:information_source: This project would not be possible without the help and assistance of Aleksandr Bukhalo and the fantastic bukhalo/nestjs-telegraf project.
NestJS grammY – powerful solution for creating Telegram bots.
This package uses the best of the NodeJS world under the hood. grammY is the most powerful library for creating bots and NestJS is a progressive framework for creating well-architectured applications. This module provides fast and easy way for creating Telegram bots and deep integration with your NestJS application.
npm i @grammyjs/nestjs
# or
yarn add @grammyjs/nestjs
Once the installation process is complete, we can import the NestjsGrammyModule
into the root AppModule
:
import { Module } from '@nestjs/common'
import { NestjsGrammyModule } from '@grammyjs/nestjs'
@Module({
imports: [
NestjsGrammyModule.forRoot({
token: 'TELEGRAM_BOT_TOKEN',
}),
],
})
export class AppModule {}
Then create app.update.ts
file and add some decorators for handling Telegram bot API updates:
import { Bot, Context } from 'grammy'
import { InjectBot, Update, Message, Start, Hears, Ctx, Help, Admin } from '@grammyjs/nestjs'
@Update()
@UseInterceptors(ResponseTimeInterceptor)
@UseFilters(GrammyExceptionFilter)
export class EchoUpdate {
constructor(
@InjectBot(EchoBotName)
private readonly bot: Bot<Context>,
private readonly echoService: EchoService,
) {
log('echo update starting', this.bot ? this.bot.botInfo.id : '(booting)')
}
@Start()
async onStart(@Ctx() ctx: Context): Promise<void> {
// const me = await this.bot.api.getMe()
log('onStart!!', this.bot ? this.bot.botInfo : '(booting)')
ctx.reply(`Hey, I'm ${this.bot.botInfo.first_name}`)
}
@Help()
async onHelp(@Ctx() ctx: Context): Promise<void> {
ctx.reply('Send me any text')
}
@Admin()
@UseGuards(AdminGuard)
async onAdminCommand(@Ctx() ctx: Context): Promise<void> {
ctx.reply('Welcome, Judge')
}
@Hears('greetings')
async onMessage(@Ctx() ctx: Context, @Message('text', new ReverseTextPipe()) reversedText: string): Promise<void> {
ctx.reply(reversedText)
}
}
If you want to use grammY
instance directly, you can use @InjectBot
for that.
import { Injectable } from '@nestjs/common'
import { Bot, Context } from 'grammy'
import { InjectBot } from '@grammyjs/nestjs'
@Injectable()
export class EchoService {
constructor(@InjectBot(EchoBotName) private readonly bot: Bot<Context>) {}
...
}
When you need to pass module options asynchronously instead of statically, use the forRootAsync() method. As with most dynamic modules, Nest provides several techniques to deal with async configuration.
One technique is to use a factory function:
NestjsGrammyModule.forRootAsync({
useFactory: () => ({
token: 'TELEGRAM_BOT_TOKEN',
}),
})
Like other factory providers, our factory function can be async and can inject dependencies through inject.
NestjsGrammyModule.forRootAsync({
imports: [ConfigModule.forFeature(grammyModuleConfig)],
useFactory: async (configService: ConfigService) => ({
token: configService.get<string>('TELEGRAM_BOT_TOKEN'),
}),
inject: [ConfigService],
})
Alternatively, you can configure the NestjsGrammyModule using a class instead of a factory, as shown below:
NestjsGrammyModule.forRootAsync({
useClass: MyConfigService,
})
The construction above instantiates MyConfigService
inside NestjsGrammyModule
, using it to create the required options object. Note that in this example, the MyConfigService
has to implement the MyOptionsFactory
interface, as shown below. The NestjsGrammyModule
will call the createMyOptions()
method on the instantiated object of the supplied class.
@Injectable()
class MyConfigService implements MyOptionsFactory {
createMyOptions(): NestjsGrammyModuleOptions {
return {
token: 'TELEGRAM_BOT_TOKEN',
}
}
}
If you want to reuse an existing options provider instead of creating a private copy inside the NestjsGrammyModule
, use the useExisting
syntax.
NestjsGrammyModule.forRootAsync({
imports: [ConfigModule.forFeature(grammyModuleConfig)],
useExisting: ConfigService,
})
By default, the bot receives updates using long-polling and requires no additional action.
To utilize webhooks, the best documentation is to review the Sample Firebase Bot package and how it configures webhooks.
At a high level, you simply enable webhooks and pass the webhook module as follows:
useWebhook: true,
include: [FirebaseWebhookModule],
This module must initialize the webhooks for grammY as such:
configure(consumer: MiddlewareConsumer) {
consumer.apply(webhookCallback(this.bot, 'express')).forRoutes('*')
}
The last step is to write the update functions in a module like this:
@Update()
@UseInterceptors(ResponseTimeInterceptor)
@UseFilters(GrammyExceptionFilter)
export class WebhookUpdater {
private readonly inlineKeyboard: InlineKeyboard
constructor(
@InjectBot(FirebaseBotName)
private readonly bot: Bot<Context>,
private readonly botService: FirebaseBotService,
) {
log(`Initializing`, bot.isInited() ? bot.botInfo.first_name : '(pending)')
}
@Start()
async onStart(@Ctx() ctx: Context): Promise<any> {
log('onStart!!', this.bot ? this.bot.botInfo.first_name : '(booting)')
return ctx.reply('Curious? Click me!', { reply_markup: this.inlineKeyboard })
}
@CallbackQuery('click-payload')
async onCallback(@Ctx() ctx: Context): Promise<any> {
return ctx.answerCallbackQuery({
text: 'You were curious, indeed!',
})
}
...
@grammyjs/nestjs
has support of the grammY middleware packages. To use an existing middleware package, simply import it and add it to the middlewares array:
NestjsGrammyModule.forRoot({
middlewares: [session()],
}),
In some cases, you may need to run multiple bots at the same time. This can also be achieved with this module. To work with multiple bots, first create the bots. In this case, bot naming becomes mandatory.
import { Module } from '@nestjs/common'
import { ConfigModule } from '@nestjs/config'
import { NestjsGrammyModule } from '@grammyjs/nestjs'
@Module({
imports: [
ConfigModule.forRoot(),
NestjsGrammyModule.forRootAsync({
imports: [ConfigModule],
botName: 'cat',
useFactory: (configService: ConfigService) => ({
token: configService.get<string>('CAT_BOT_TOKEN'),
}),
inject: [ConfigService],
}),
NestjsGrammyModule.forRootAsync({
imports: [ConfigModule.forFeature(myModuleConfig)],
botName: 'dog',
useFactory: async (configService: ConfigService) => ({
token: configService.get<string>('DOG_BOT_TOKEN'),
}),
inject: [ConfigService],
}),
],
})
export class AppModule {}
:::caution Please note that you shouldn't have multiple bots without a name, or with the same name, otherwise they will get overridden. :::
You can also inject the Bot
for a given bot:
import { Injectable } from '@nestjs/common'
import { InjectBot, Bot, Context } from '@grammyjs/nestjs'
@Injectable()
export class EchoService {
constructor(@InjectBot('cat') private catBot: Bot<Context>) {}
}
To inject a given Bot
to a custom provider (for example, factory provider), use the getBotToken()
function passing the name of the bot as an argument.
{
provide: CatsService,
useFactory: (catBot: Bot<Context>) => {
return new CatsService(catBot);
},
inject: [getBotToken('cat')],
}
Another useful feature of the @grammyjs/nestjs
module is the ability to choose which modules should handle updates for each launched bot. By default, module searches for handlers throughout the whole app. To limit this scan to only a subset of modules, use the include property.
NestjsGrammyModule.forRootAsync({
imports: [ConfigModule],
botName: 'cat',
useFactory: (configService: ConfigService) => ({
token: configService.get<string>('CAT_BOT_TOKEN'),
include: [CatsModule],
}),
inject: [ConfigService],
}),
If you initialized your application with the Nest CLI, Express framework will be installed by default along with Nest. Nest and NestJS grammY does not require Express for work. So if you don't plan to getting bot updates through webhooks, and you don't need a web server, you can remove Express.
To do this, change the bootstrap
function in the main.ts
file of your project on something like that:
async function bootstrap() {
const app = await NestFactory.createApplicationContext(AppModule)
}
bootstrap()
This initializes Nest as a standalone application (without any network listeners).
All that remains is to remove unused dependencies:
npm un @nestjs/platform-express @types/express
:::info More information about standalone applications located at Nest documentation :::
FAQs
grammY module for NestJS
The npm package @grammyjs/nestjs receives a total of 126 weekly downloads. As such, @grammyjs/nestjs popularity was classified as not popular.
We found that @grammyjs/nestjs demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 3 open source maintainers collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
pnpm 10 blocks lifecycle scripts by default to improve security, addressing supply chain attack risks but sparking debate over compatibility and workflow changes.
Product
Socket now supports uv.lock files to ensure consistent, secure dependency resolution for Python projects and enhance supply chain security.
Research
Security News
Socket researchers have discovered multiple malicious npm packages targeting Solana private keys, abusing Gmail to exfiltrate the data and drain Solana wallets.