NestJS VK – powerful solution for creating VK bots.
This package uses the best of the NodeJS world under the hood. VK-IO is a powerful Node.js module that allows you to easily interact with the VK API and for creating bots, and NestJS is a progressive framework for creating well-architectured applications.
This module provides a quick and easy way to interact with the VK API and create VK bots and deep integration with your NestJS application.
Features
- Simple. Easy to use.
- Ability to create custom decorators.
- Scenes support.
- Ability to run multiple bots simultaneously.
- Full support of NestJS guards, interceptors, filters and pipes!
$ npm i nestjs-vk vk-io
$ yarn add nestjs-vk vk-io
Once the installation process is complete, we can import the VkModule
into the root AppModule
:
import { Module } from '@nestjs/common';
import { VkModule } from 'nestjs-vk';
import { AppUpdate } from './app.update';
import { SimpleScene } from './scene/simple.scene';
@Module({
imports: [
VkModule.forManagers({
useSessionManager: false,
useSceneManager: false,
useHearManager: false,
}),
VkModule.forRootAsync({
inject: [MainMiddleware],
useFactory: async (mainMiddleware: MainMiddleware) => ({
token: process.env.VK_BOT_TOKEN,
options: {
pollingGroupId: +process.env.VK_BOT_GROUP_ID,
apiMode: 'sequential',
},
// launchOptions: false,
// notReplyMessage: true,
middlewaresBefore: [mainMiddleware.middlewaresBefore],
middlewaresAfter: [mainMiddleware.middlewaresAfter],
}),
}),
// VkModule.forRoot({
// token: process.env.VK_BOT_TOKEN,
// options: {
// pollingGroupId: +process.env.VK_BOT_GROUP_ID,
// apiMode: 'sequential',
// },
// }),
],
providers: [MainMiddleware, AppUpdate, SimpleScene],
exports: [MainMiddleware],
})
export class AppModule {}
Main middleware main.middleware.ts
:
import { Inject, Injectable } from '@nestjs/common';
import { VK_HEAR_MANAGER, VK_SCENE_MANAGER } from 'nestjs-vk';
import { MessageContext, Context, Composer } from 'vk-io';
import { HearManager } from '@vk-io/hear';
import { SessionManager } from '@vk-io/session';
import { SceneManager } from '@vk-io/scenes';
@Injectable()
export class MainMiddleware {
private readonly sessionManager: SessionManager;
@Inject(VK_HEAR_MANAGER)
private readonly hearManagerProvider: HearManager<MessageContext>;
@Inject(VK_SCENE_MANAGER)
private readonly sceneManager: SceneManager;
constructor() {
this.sessionManager = new SessionManager({
// ...
});
}
get middlewaresBefore() {
const composer = Composer.builder<Context>();
composer.use(this.sessionManager.middleware);
composer.use(this.sceneManager.middleware);
return composer.compose();
}
get middlewaresAfter() {
const composer = Composer.builder<Context>();
composer.use(this.hearManagerProvider.middleware);
return composer.compose();
}
}
Then create app.update.ts
file and add some decorators for handling VK bot API updates:
import { InjectVkApi, Update, Ctx, Message, Hears, HearFallback } from 'nestjs-vk';
import { MessageContext, VK } from 'vk-io';
import { AppService } from './app.service';
import { SIMPLE_SCENE } from './vk.constants';
@Update()
export class AppUpdate {
public groupId: number;
constructor(
@InjectVkApi()
private readonly vk: VK,
private readonly appService: AppService,
) {}
async onModuleInit() {
try {
const [group] = await this.vk.api.groups.getById({});
this.groupId = group.id;
} catch (err) {
console.error(err);
}
}
@Hears(/^\/?(start|старт)$/i)
async onStartCommand(@Ctx() ctx: MessageContext) {
await ctx.reply('Welcome');
}
@Hears('hi')
async hearsHi(@Ctx() ctx: MessageContext) {
return 'Hey there';
}
@Hears(/scene( ?(?<state>[0-9]+))?$/i)
async hearsScene(@Ctx() ctx: MessageContext) {
const stateNumber = ((e) => (isNaN(Number(e)) ? null : Number(e)))(ctx.$match?.groups?.state);
ctx.scene.enter(SIMPLE_SCENE, { state: { stateNumber } });
}
@Hears(['/sub', 'subscriber'])
async onSubscriberCommand(@Ctx() ctx: MessageContext) {
const isSib = await this.vk.api.groups.isMember({
group_id: String(this.groupId),
user_id: ctx.senderId,
});
return isSib ? 'So good!' : 'No sub';
}
@HearFallback()
onHearFallback(@Ctx() ctx: MessageContext, @Message('text') text: string) {
if (text) {
return this.appService.echo(text);
} else if (ctx.hasAttachments('sticker')) {
ctx.send({ sticker_id: ctx.getAttachments('sticker')[0].id % 24 });
return;
}
return 'What?..';
}
}
For a simple scene, let's create an simple.scene.ts
file and do a few steps for it:
import { Scene, AddStep, Ctx, SceneEnter, SceneLeave } from 'nestjs-vk';
import { MessageContext } from 'vk-io';
import { IStepContext } from '@vk-io/scenes';
import { SIMPLE_SCENE } from './vk.constants';
@Scene(SIMPLE_SCENE)
export class SimpleScene {
@SceneEnter()
async onSceneEnter(@Ctx() ctx: IStepContext) {
const { stateNumber } = ctx.scene.state;
await ctx.reply(
`Hello! I am a simple scene. Your state number is ${stateNumber}.\n` +
` You can send me a number and I will multiply it by 2.`,
);
}
@SceneLeave()
async onSceneLeave(@Ctx() ctx: IStepContext) {
await ctx.reply('Bye!');
}
// @Hears('exit')
// async onHearsExit(@Ctx() ctx: IStepContext) {
// await ctx.scene.leave();
// }
@AddStep()
async onAddStep(@Ctx() ctx: IStepContext) {
let { stateNumber: number } = ctx.scene.state;
if (!ctx.scene.step.firstTime) {
number = Number(ctx.text);
}
if (ctx.scene.step.firstTime && number === null) {
await ctx.reply('Please send me a number.');
return;
}
if (isNaN(number)) {
await ctx.reply('Wrong. Please send me a number.');
return;
}
await ctx.reply(`Your number multiplied by 2 is ${number * 2}.`);
if (number > 20 && number % 2 === 0) {
await ctx.scene.leave();
}
}
}