1. Подготовка бота
Взаимодействие с телеграмом можно осуществлять как через обычные запросы по их АПИ, так и через модуль Telegraf
, который реализует у себя всю нужную функциональность
Для начала работы от самого телеграма нам понадобится токен нашего бота и id чата
Чтобы получить id чата, нужно добавить в чат бота, который по полученному сообщению будет доставать id
Для генерации id бота нужно будет обратиться к @BotFather
2. Интеграция Telegraf
Первым делом нужно установить библиотеку, которая будет осуществлять взаимодействие с телегой
npm i telegraf
Контроллер тут нам не понадобится, так как мы будем пользоваться уже существующим
nest g module telegram
nest g service telegram --no-spec
Добавляем в среду окружения полученные id
.env
CHAT_ID=-100...
TELEGRAM_TOKEN=6052658640:AAG...
Создадим интерфейс ITelegramOptions
, который опишет ту конфигурацию, которую мы будем передавать в телеграф и ITelegramModuleAsyncOptions
, который опишет те данные, которые должны будут принимать в себя опции для создания нашей собственной реализации интеграции модуля forRootAsync
(как это работает в Typegoose
)
src > telegram > telegram.interface.ts
import { ModuleMetadata } from '@nestjs/common';
export interface ITelegramOptions {
chatId: string;
token: string;
}
export interface ITelegramModuleAsyncOptions extends Pick<ModuleMetadata, 'imports'> {
useFactory: (...args: any[]) => Promise<ITelegramOptions> | ITelegramOptions;
inject?: any[];
}
Далее нам нужно сюда заинжектить ConfigService
(инжектится он сюда, потому что мы подключим его через app.module.ts
дальше), который предоставляет возможность взаимодействовать с .env
src > configs > telegram.config.ts
import { ConfigService } from '@nestjs/config';
import { ITelegramOptions } from 'src/telegram/telegram.interface';
export const getTelegramConfig = (configService: ConfigService): ITelegramOptions => {
const token = configService.get('TELEGRAM_TOKEN');
if (!token) {
throw new Error('TELEGRAM_TOKEN не задан');
}
return {
token,
chatId: configService.get('CHAT_ID') ?? '',
};
};
Далее создадим константу, по которой мы сможем получать опции для нашего модуля телеграма по всему приложению через инжект
src > telegram > telegram.constants.ts
export const TELEGRAM_MODULE_OPTIONS = 'TELEGRAM_MODULE_OPTIONS';
Дальше в модуле мы должны описать две функции, которые мы будем использовать при внедрении данного модуля в app.module.ts
:
forRootAsync()
- создаст конфиг для подключения данного модуляcreateAsyncOptionsProvider()
- соберёт опции для подключения
src > telegram > telegram.module.ts
import { DynamicModule, Global, Module, Provider } from '@nestjs/common';
import { TelegramService } from './telegram.service';
import { ITelegramModuleAsyncOptions } from './telegram.interface';
import { TELEGRAM_MODULE_OPTIONS } from './telegram.constants';
@Global()
@Module({})
export class TelegramModule {
// тут мы создаём асинхронно наш модуль
static forRootAsync(options: ITelegramModuleAsyncOptions): DynamicModule {
// создаём асинхронные опции в качестве провайдера
// в качестве провайдера для того, чтобы в любом месте его можно было достать по токену TELEGRAM_MODULE_OPTIONS
const asyncOptions = this.createAsyncOptionsProvider(options);
return {
module: TelegramModule,
imports: options.imports,
providers: [TelegramService, asyncOptions],
exports: [TelegramService],
};
}
// эта функция будет возвращать готовый провайдер, в который будет внедрены useFactory и опции
private static createAsyncOptionsProvider(options: ITelegramModuleAsyncOptions): Provider {
return {
// провайдим опции по токену
provide: TELEGRAM_MODULE_OPTIONS,
// используем фэктори
useFactory: async (...args: any[]) => {
// тут мы вызываем фэктори, который передали уже внутри options для формирования конфигурации
const config = await options.useFactory(...args);
return config;
},
// инжектим внутрь провайдера необходимые зависимости (например, если нам нужен будет ConfigService)
inject: options.inject || [],
};
}
}
В сервисе уже реализуем работу с телеграмом через сам телеграф
src > telegram > telegram.service.ts
import { Inject, Injectable } from '@nestjs/common';
import { Telegraf } from 'telegraf';
import { TELEGRAM_MODULE_OPTIONS } from './telegram.constants';
import { ITelegramOptions } from './telegram.interface';
@Injectable()
export class TelegramService {
bot: Telegraf;
options: ITelegramOptions;
constructor(@Inject(TELEGRAM_MODULE_OPTIONS) options: ITelegramOptions) {
this.bot = new Telegraf(options.token);
this.options = options;
}
async sendMessage(message: string, chatId: string = this.options.chatId) {
// отправляем сообщение, написав id чата и само сообщение для отправки
await this.bot.telegram.sendMessage(chatId, message);
}
}
Далее нам нужно глобально подключить наш TelegramModule
и добавить ему ConfigService
, а так же для useFactory
назначить функцию сборки конфига getTelegramConfig
src > app.module.ts
@Module({
imports: [
ConfigModule.forRoot(),
TypegooseModule.forRootAsync({
imports: [ConfigModule],
inject: [ConfigService],
useFactory: getMongoConfig,
}),
AuthModule,
ProductModule,
ReviewModule,
TopPageModule,
FilesModule,
TelegramModule.forRootAsync({
// импортируем ConfigModule, потому что в нём находится ConfigService
imports: [ConfigModule],
// инжектим ConfigService, чтобы фектори мог его использовать
inject: [ConfigService],
useFactory: getTelegramConfig,
}),
],
controllers: [AppController],
providers: [AppService],
})
export class AppModule {}
Далее подключаем TelegramModule
в модуль для создания обзоров
src > review > review.module.ts
@Module({
controllers: [ReviewController],
imports: [
TypegooseModule.forFeature([
{
typegooseClass: ReviewModel,
schemaOptions: {
collection: 'Review',
},
},
]),
TelegramModule,
],
providers: [ReviewService],
})
export class ReviewModule {}
Далее нам нужно инжектнуть TelegramService
в контроллер обзоров и тут же вызвать отправку сообщения через метод notify
src > review > review.controller.ts
Далее нам остаётся дать запрос на роут нотифая
И мы получаем имя нашего бота