Add a module for debugging messages in admin mode and user mode for Telegram bot on NestJS
Tags: kaufmanbot, nestjs, debug, telegram
Published: 2022-04-01 18:49:15

## Links

https://github.com/EndyKaufman/kaufman-bot - source code of bot

https://telegram.me/DevelopKaufmanBot - current bot in telegram

## Add new field to user for store debug mode state

### Create migrations for append new user field
_migrations/V202203310937__AddDebugFieldToUserTable.pgsql_
```sql
DO $$
BEGIN
    ALTER TABLE "User"
        ADD "debugMode" boolean DEFAULT FALSE;
EXCEPTION
    WHEN duplicate_column THEN
        NULL;
END
$$;
```

### Apply migrations 
> npm run migrate:local
```sh
endy@endy-virtual-machine:~/Projects/current/kaufman-bot$ npm run migrate:local

> kaufman-bot@0.0.0 migrate:local
> export $(xargs < ./.env.local) > /dev/null 2>&1 && export DATABASE_URL=$SERVER_POSTGRES_URL && npm run migrate


> kaufman-bot@0.0.0 migrate
> npm run flyway -- migrate


> kaufman-bot@0.0.0 flyway
> flyway -c .flyway.js "migrate"

Flyway Community Edition 6.3.2 by Redgate
Database: jdbc:postgresql://localhost:5432/kaufman_bot_develop (PostgreSQL 13.3)
WARNING: Flyway upgrade recommended: PostgreSQL 13.3 is newer than this version of Flyway and support has not been tested. The latest supported version of PostgreSQL is 12.
Successfully validated 3 migrations (execution time 00:00.018s)
Current version of schema "public": 202203252144
Migrating schema "public" to version 202203310937 - AddDebugFieldToUserTable
Successfully applied 1 migration to schema "public" (execution time 00:00.032s)
```
![Apply migrations](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/rsuj6euxdgjykzyvjvwh.png)

### Update prisma schema and sdk
> npm run prisma:pull:local
```sh
endy@endy-virtual-machine:~/Projects/current/kaufman-bot$ npm run prisma:pull:local

> kaufman-bot@0.0.0 prisma:pull:local
> export $(xargs < ./.env.local) > /dev/null 2>&1 && export DATABASE_URL=$SERVER_POSTGRES_URL && npm run -- prisma db pull && npm run prisma:generate


> kaufman-bot@0.0.0 prisma
> prisma "db" "pull"

Prisma schema loaded from prisma/schema.prisma
Datasource "db": PostgreSQL database "kaufman_bot_develop", schema "public" at "localhost:5432"

Introspecting based on datasource defined in prisma/schema.prisma …

✔ Introspected 2 models and wrote them into prisma/schema.prisma in 303ms
      
Run prisma generate to generate Prisma Client.


> kaufman-bot@0.0.0 prisma:generate
> npm run -- prisma generate


> kaufman-bot@0.0.0 prisma
> prisma "generate"

Prisma schema loaded from prisma/schema.prisma

✔ Generated Prisma Client (3.11.1 | library) to ./node_modules/@prisma/client in 165ms
You can now start using Prisma Client in your code. Reference: https://pris.ly/d/client

import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()

```
![Update prisma schema and sdk](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/7yemfu3g3xcm3t22jany.png)
 
Check prisma schema
_prisma/schema.prisma_
```prisma
generator client {
  provider      = "prisma-client-js"
  binaryTargets = ["native", "linux-musl"]
}

datasource db {
  provider = "postgresql"
  url      = env("DATABASE_URL")
}

model User {
  id         String  @id(map: "PK_USERS") @default(dbgenerated("uuid_generate_v4()")) @db.Uuid
  telegramId String  @unique(map: "UQ_USERS__TELEGRAM_ID") @db.VarChar(64)
  langCode   String  @default("en") @db.VarChar(64)
  debugMode  Boolean @default(false)
}

model migrations {
  installed_rank Int      @id(map: "__migrations_pk")
  version        String?  @db.VarChar(50)
  description    String   @db.VarChar(200)
  type           String   @db.VarChar(20)
  script         String   @db.VarChar(1000)
  checksum       Int?
  installed_by   String   @db.VarChar(100)
  installed_on   DateTime @default(now()) @db.Timestamp(6)
  execution_time Int
  success        Boolean

  @@index([success], map: "__migrations_s_idx")
  @@map("__migrations")
}

```


## Update core logic for correct work all logic needed in debug command

### Update all interfaces

Before hook

_libs/core/server/src/lib/bot-commands/bot-commands-types/on-before-bot-commands.interface.ts_
```ts
import { BotCommandsProviderActionMsg } from './bot-commands-provider.interface';

export interface OnBeforeBotCommands {
  onBeforeBotCommands<
    TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
  >(
    msg: TMsg,
    ctx?
  ): Promise<TMsg>;
}

```

After hook

_libs/core/server/src/lib/bot-commands/bot-commands-types/on-after-bot-commands.interface.ts_
```ts
import { BotCommandsProviderActionResultType } from './bot-commands-provider-action-result-type';
import { BotCommandsProviderActionMsg } from './bot-commands-provider.interface';

export interface OnAfterBotCommands {
  onAfterBotCommands<
    TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg,
    TResult extends BotCommandsProviderActionResultType<TMsg> = BotCommandsProviderActionResultType<TMsg>
  >(
    result: TResult,
    msg: TMsg,
    ctx?
  ): Promise<{ result: TResult; msg: TMsg }>;
}

```

Update provider interface

_libs/core/server/src/lib/bot-commands/bot-commands-types/bot-commands-provider.interface.ts_
```ts
import { Context } from 'telegraf';
import { Update } from 'telegraf/typings/core/types/typegram';
import { BotCommandsProviderActionResultType } from './bot-commands-provider-action-result-type';

export const BOT_COMMANDS_PROVIDER = Symbol('BOT_COMMANDS_PROVIDER');

export type BotCommandsProviderActionMsg = Update.MessageUpdate['message'] & {
  text: string;
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
  botContext?: Record<string, any>;
};

export type BotCommandsProviderActionContext = Context<Update.MessageUpdate>;

export interface BotCommandsProvider {
  onHelp<
    TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
  >(
    msg: TMsg,
    ctx
  ): Promise<BotCommandsProviderActionResultType<TMsg>>;

  onMessage<
    TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
  >(
    msg: TMsg,
    ctx
  ): Promise<BotCommandsProviderActionResultType<TMsg>>;
}

```

### Update core service

Add main logic of work with telegram to service

_libs/core/server/src/lib/bot-commands/bot-commands-services/bot-commands.service.ts_
```ts
import { Injectable } from '@nestjs/common';
import { CustomInject } from 'nestjs-custom-injector';
import { BotCommandsEnum } from '../bot-commands-types/bot-commands-enum';
import { BotCommandsProviderActionResultType } from '../bot-commands-types/bot-commands-provider-action-result-type';
import {
  BotCommandsProvider,
  BotCommandsProviderActionContext,
  BotCommandsProviderActionMsg,
  BOT_COMMANDS_PROVIDER,
} from '../bot-commands-types/bot-commands-provider.interface';
import { OnAfterBotCommands } from '../bot-commands-types/on-after-bot-commands.interface';
import { OnBeforeBotCommands } from '../bot-commands-types/on-before-bot-commands.interface';
import { BotСommandsToolsService } from './bot-commands-tools.service';
@Injectable()
export class BotСommandsService implements BotCommandsProvider {
  @CustomInject(BOT_COMMANDS_PROVIDER, { multi: true })
  private botCommandsProviders!: (BotCommandsProvider &
    Partial<OnBeforeBotCommands> &
    Partial<OnAfterBotCommands>)[];

  constructor(
    private readonly botСommandsToolsService: BotСommandsToolsService
  ) {}

  async process(ctx, defaultHandler?: () => Promise<unknown>) {
    let msg: BotCommandsProviderActionMsg = ctx.update.message;
    const result = await this.onMessage(msg, ctx, defaultHandler);
    if (result?.type === 'message') {
      msg = result.message;
    }
    if (result?.type === 'markdown') {
      await ctx.reply(result.markdown, { parse_mode: 'MarkdownV2' });
      return;
    }
    if (result?.type === 'text') {
      await ctx.reply(result.text);
      return;
    }
  }

  async onHelp<TMsg extends BotCommandsProviderActionMsg>(
    msg: TMsg,
    ctx: BotCommandsProviderActionContext
  ): Promise<BotCommandsProviderActionResultType<TMsg>> {
    const allResults: string[] = [];
    const len = this.botCommandsProviders.length;
    for (let i = 0; i < len; i++) {
      const botCommandsProvider = this.botCommandsProviders[i];

      const result = await botCommandsProvider.onHelp(msg, ctx);

      if (result !== null && result.type === 'text') {
        allResults.push(result.text);
      }

      if (result !== null && result.type === 'markdown') {
        allResults.push(result.markdown);
      }
    }
    return {
      type: 'markdown',
      markdown: allResults.join('\n\n'),
    };
  }

  async onMessage<TMsg extends BotCommandsProviderActionMsg>(
    msg: TMsg,
    ctx: BotCommandsProviderActionContext,
    defaultHandler?: () => Promise<unknown>
  ): Promise<BotCommandsProviderActionResultType<TMsg>> {
    msg = await this.processOnBeforeBotCommands(msg, ctx);

    const len = this.botCommandsProviders.length;
    let result: BotCommandsProviderActionResultType<TMsg> = null;
    for (let i = 0; i < len; i++) {
      if (!result) {
        const botCommandsProvider = this.botCommandsProviders[i];

        result = await botCommandsProvider.onMessage(msg, ctx);
      }
    }

    if (
      result === null &&
      this.botСommandsToolsService.checkCommands(
        msg.text,
        [BotCommandsEnum.help],
        msg.from.language_code
      )
    ) {
      return this.onHelp(msg, ctx);
    }

    const afterBotCommand = await this.processOnAfterBotCommands(
      result,
      msg,
      ctx
    );

    if (defaultHandler) {
      await defaultHandler();
    }

    return afterBotCommand.result;
  }

  async processOnBeforeBotCommands<TMsg extends BotCommandsProviderActionMsg>(
    msg: TMsg,
    ctx?: BotCommandsProviderActionContext
  ): Promise<TMsg> {
    const len = this.botCommandsProviders.length;
    for (let i = 0; i < len; i++) {
      const botCommandsProvider = this.botCommandsProviders[i];
      if (botCommandsProvider.onBeforeBotCommands)
        msg = await botCommandsProvider.onBeforeBotCommands(msg, ctx);
    }
    return msg;
  }

  async processOnAfterBotCommands<
    TMsg extends BotCommandsProviderActionMsg,
    TResult extends BotCommandsProviderActionResultType<TMsg> = BotCommandsProviderActionResultType<TMsg>
  >(
    result: TResult,
    msg: TMsg,
    ctx?: BotCommandsProviderActionContext
  ): Promise<{ result: TResult; msg: TMsg }> {
    const len = this.botCommandsProviders.length;
    for (let i = 0; i < len; i++) {
      const botCommandsProvider = this.botCommandsProviders[i];
      if (botCommandsProvider.onAfterBotCommands) {
        const afterBotCommand = await botCommandsProvider.onAfterBotCommands<
          TMsg,
          TResult
        >(result, msg, ctx);
        result = afterBotCommand.result;
        msg = afterBotCommand.msg;
      }
    }
    return { result, msg };
  }
}

```

## Create DebugMessagesModule

### Create new nx lib debug-messages
> npm run -- nx g @nrwl/nest:lib debug-messages/server
```sh
endy@endy-virtual-machine:~/Projects/current/kaufman-bot$ npm run -- nx g @nrwl/nest:lib debug-messages/server

> kaufman-bot@0.0.0 nx
> nx "g" "@nrwl/nest:lib" "debug-messages/server"

CREATE libs/debug-messages/server/README.md
CREATE libs/debug-messages/server/.babelrc
CREATE libs/debug-messages/server/src/index.ts
CREATE libs/debug-messages/server/tsconfig.json
CREATE libs/debug-messages/server/tsconfig.lib.json
UPDATE tsconfig.base.json
CREATE libs/debug-messages/server/project.json
UPDATE workspace.json
CREATE libs/debug-messages/server/.eslintrc.json
CREATE libs/debug-messages/server/jest.config.js
CREATE libs/debug-messages/server/tsconfig.spec.json
CREATE libs/debug-messages/server/src/lib/debug-messages-server.module.ts
```
![Create new nx lib debug-messages](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fydfw1x2x691qfut80eo.png)
 
### Create config file

_libs/debug-messages/server/src/lib/debug-messages-config/debug-messages.config.ts_
```ts
export const DEBUG_MESSAGES_CONFIG = 'DEBUG_MESSAGES_CONFIG';

export interface DebugMessagesConfig {
  name: string;
  descriptions: string;
  usage: string[];
  spyWords: string[];
}

```

### Create commands enum

_libs/debug-messages/server/src/lib/debug-messages-types/debug-messages-commands.ts_
```ts
import { getText } from 'class-validator-multi-lang';

export const DebugMessagesCommandsEnum = {
  on: getText('on'),
  off: getText('off'),
  current: getText('state'),
};

```

### Create storage service for store user settings

_libs/debug-messages/server/src/lib/debug-messages-services/debug-messages.storage.ts_
```ts
import { PrismaClientService } from '@kaufman-bot/core/server';
import { Injectable } from '@nestjs/common';

@Injectable()
export class DebugMessagesStorage {
  private readonly debugModeOfUsers: Record<number, boolean> = {};

  constructor(private readonly prismaClientService: PrismaClientService) {}

  async getDebugModeOfUser(telegramUserId: number): Promise<boolean> {
    const currentDebugMode = this.debugModeOfUsers[telegramUserId];
    if (currentDebugMode) {
      return currentDebugMode;
    }
    try {
      const currentDebugModeFromDatabase =
        await this.prismaClientService.user.findFirst({
          where: { telegramId: telegramUserId.toString() },
          rejectOnNotFound: true,
        });
      this.debugModeOfUsers[telegramUserId] =
        currentDebugModeFromDatabase.debugMode;
      return this.debugModeOfUsers[telegramUserId];
    } catch (error) {
      return false;
    }
  }

  async setDebugModeOfUser(userId: number, debugMode: boolean): Promise<void> {
    await this.prismaClientService.user.upsert({
      create: { telegramId: userId.toString(), debugMode },
      update: { debugMode },
      where: { telegramId: userId.toString() },
    });
    this.debugModeOfUsers[userId] = debugMode;
  }
}

```

### Create service with main logic of bot command

_libs/debug-messages/server/src/lib/debug-messages-services/debug-messages.service.ts_
```ts
import {
  BotCommandsEnum,
  BotCommandsProvider,
  BotCommandsProviderActionMsg,
  BotCommandsProviderActionResultType,
  BotСommandsToolsService,
  OnAfterBotCommands,
  OnBeforeBotCommands,
} from '@kaufman-bot/core/server';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { getText } from 'class-validator-multi-lang';
import { TranslatesService } from 'nestjs-translates';
import {
  DebugMessagesConfig,
  DEBUG_MESSAGES_CONFIG,
} from '../debug-messages-config/debug-messages.config';
import { DebugMessagesCommandsEnum } from '../debug-messages-types/debug-messages-commands';
import { DebugMessagesStorage } from './debug-messages.storage';

const DEBUG_MODE = 'debugMode';

@Injectable()
export class DebugMessagesService
  implements BotCommandsProvider, OnBeforeBotCommands, OnAfterBotCommands
{
  private readonly logger = new Logger(DebugMessagesService.name);

  constructor(
    @Inject(DEBUG_MESSAGES_CONFIG)
    private readonly debugMessagesConfig: DebugMessagesConfig,
    private readonly translatesService: TranslatesService,
    private readonly debugMessagesStorage: DebugMessagesStorage,
    private readonly commandToolsService: BotСommandsToolsService
  ) {}

  async onAfterBotCommands<
    TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg,
    TResult extends BotCommandsProviderActionResultType<TMsg> = BotCommandsProviderActionResultType<TMsg>
  >(result: TResult, msg: TMsg, ctx): Promise<{ result: TResult; msg: TMsg }> {
    const { botContext, ...debugData } = msg;
    if (botContext?.[DEBUG_MODE] && ctx) {
      ctx.reply(
        ['```', JSON.stringify(debugData, undefined, 4), '```'].join('\n'),
        {
          parse_mode: 'MarkdownV2',
        }
      );
    }
    return {
      msg,
      result,
    };
  }

  async onBeforeBotCommands<
    TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
  >(msg: TMsg): Promise<TMsg> {
    const debugMode = await this.debugMessagesStorage.getDebugModeOfUser(
      msg.from?.id
    );
    if (!msg.botContext) {
      msg.botContext = {};
    }
    msg.botContext[DEBUG_MODE] = debugMode;
    return msg;
  }

  async onHelp<
    TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
  >(msg: TMsg): Promise<BotCommandsProviderActionResultType<TMsg>> {
    return await this.onMessage({
      ...msg,
      text: `${this.debugMessagesConfig.name} ${BotCommandsEnum.help}`,
    });
  }

  async onMessage<
    TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
  >(msg: TMsg): Promise<BotCommandsProviderActionResultType<TMsg>> {
    const locale = msg.from?.language_code || 'en';

    const spyWord = this.debugMessagesConfig.spyWords.find((spyWord) =>
      this.commandToolsService.checkCommands(msg.text, [spyWord], locale)
    );
    if (spyWord) {
      if (
        this.commandToolsService.checkCommands(
          msg.text,
          [BotCommandsEnum.help],
          locale
        )
      ) {
        return {
          type: 'markdown',
          markdown: this.commandToolsService.generateHelpMessage(
            locale,
            this.debugMessagesConfig.name,
            this.debugMessagesConfig.descriptions,
            this.debugMessagesConfig.usage
          ),
        };
      }

      const processedMsg = await this.process(msg, locale);

      if (typeof processedMsg === 'string') {
        return {
          type: 'text',
          text: processedMsg,
        };
      }
      if (processedMsg) {
        return { type: 'message', message: processedMsg };
      }

      this.logger.warn(`Unhandled commands for text: "${msg.text}"`);
      this.logger.debug(msg);
    }
    return null;
  }

  private async process<
    TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
  >(msg: TMsg, locale: string) {
    const debugMode = await this.debugMessagesStorage.getDebugModeOfUser(
      msg.from?.id
    );
    if (
      this.commandToolsService.checkCommands(
        msg.text,
        [DebugMessagesCommandsEnum.on],
        locale
      )
    ) {
      if (!debugMode) {
        await this.debugMessagesStorage.setDebugModeOfUser(msg.from?.id, true);
        return this.translatesService.translate(
          getText(`debug enabled`),
          locale,
          {
            locale,
          }
        );
      } else {
        return this.translatesService.translate(
          getText(`debug already enabled`),
          locale,
          {
            locale,
          }
        );
      }
    }
    if (
      this.commandToolsService.checkCommands(
        msg.text,
        [DebugMessagesCommandsEnum.off],
        locale
      )
    ) {
      if (debugMode) {
        await this.debugMessagesStorage.setDebugModeOfUser(msg.from?.id, false);
        return this.translatesService.translate(
          getText(`debug disabled`),
          locale,
          {
            locale,
          }
        );
      } else {
        return this.translatesService.translate(
          getText(`debug already disabled`),
          locale,
          {
            locale,
          }
        );
      }
    }
    if (
      this.commandToolsService.checkCommands(
        msg.text,
        [DebugMessagesCommandsEnum.current],
        locale
      )
    ) {
      return this.translatesService.translate(
        getText(`debug: {{debugMode}}`),
        locale,
        { debugMode: debugMode ? 'enable' : 'disable' }
      );
    }
    return msg;
  }
}

```

### Create module

_libs/debug-messages/server/src/lib/debug-messages.module.ts_
```ts
import {
  BotCommandsModule,
  BOT_COMMANDS_PROVIDER,
  PrismaClientModule,
} from '@kaufman-bot/core/server';
import { DynamicModule, Module } from '@nestjs/common';
import { getText } from 'class-validator-multi-lang';
import { CustomInjectorModule } from 'nestjs-custom-injector';
import { TranslatesModule } from 'nestjs-translates';
import {
  DebugMessagesConfig,
  DEBUG_MESSAGES_CONFIG,
} from './debug-messages-config/debug-messages.config';
import { DebugMessagesService } from './debug-messages-services/debug-messages.service';
import { DebugMessagesStorage } from './debug-messages-services/debug-messages.storage';

@Module({
  imports: [TranslatesModule, PrismaClientModule, BotCommandsModule],
  providers: [DebugMessagesStorage],
  exports: [
    TranslatesModule,
    PrismaClientModule,
    BotCommandsModule,
    DebugMessagesStorage,
  ],
})
export class DebugMessagesModule {
  static forRoot(): DynamicModule {
    return {
      module: DebugMessagesModule,
      imports: [
        CustomInjectorModule.forFeature({
          imports: [DebugMessagesModule],
          providers: [
            {
              provide: DEBUG_MESSAGES_CONFIG,
              useValue: <DebugMessagesConfig>{
                name: getText('Debug messages'),
                usage: [
                  getText('debug on'),
                  getText('debug off'),
                  getText('debug state'),
                ],
                descriptions: getText(
                  'Commands for enable and disable debug mode'
                ),
                spyWords: [getText('debug')],
              },
            },
            {
              provide: BOT_COMMANDS_PROVIDER,
              useClass: DebugMessagesService,
            },
          ],
          exports: [DEBUG_MESSAGES_CONFIG],
        }),
      ],
    };
  }
}

```

## Update application

### Update app service

_apps/server/src/app/app.service.ts_
```ts
import {
  BotCommandsProviderActionMsg,
  BotСommandsService,
} from '@kaufman-bot/core/server';
import { Injectable, Logger } from '@nestjs/common';
import { Hears, On, Start, Update } from 'nestjs-telegraf';
import { Context } from 'telegraf';

@Update()
@Injectable()
export class AppService {
  private readonly logger = new Logger(AppService.name);

  constructor(private readonly botСommandsService: BotСommandsService) {}

  getData(): { message: string } {
    return { message: 'Welcome to server!' };
  }

  @Start()
  async startCommand(ctx: Context) {
    await this.botСommandsService.process(ctx, () => ctx.reply('Welcome'));
  }

  @On('sticker')
  async onSticker(ctx) {
    await this.botСommandsService.process(ctx, () => ctx.reply('👍'));
  }

  @Hears('hi')
  async hearsHi(ctx: Context) {
    await this.botСommandsService.process(ctx, () => ctx.reply('Hey there'));
  }

  @On('text')
  async onMessage(ctx) {
    let msg: BotCommandsProviderActionMsg = ctx.update.message;
    const result = await this.botСommandsService.onMessage(msg, ctx);
    if (result?.type === 'message') {
      msg = result.message;
    }
    if (result?.type === 'markdown') {
      await ctx.reply(result.markdown, { parse_mode: 'MarkdownV2' });
      return;
    }
    if (result?.type === 'text') {
      await ctx.reply(result.text);
      return;
    }
  }
}

```

### Update app module

_apps/server/src/app/app.module.ts_
```ts
import {
  BotCommandsModule,
  PrismaClientModule,
} from '@kaufman-bot/core/server';
import { CurrencyConverterModule } from '@kaufman-bot/currency-converter/server';
import { DebugMessagesModule } from '@kaufman-bot/debug-messages/server';
import { FactsGeneratorModule } from '@kaufman-bot/facts-generator/server';
import {
  DEFAULT_LANGUAGE,
  LanguageSwitherModule,
} from '@kaufman-bot/language-swither/server';
import { Module } from '@nestjs/common';
import env from 'env-var';
import { TelegrafModule } from 'nestjs-telegraf';
import {
  getDefaultTranslatesModuleOptions,
  TranslatesModule,
} from 'nestjs-translates';
import { join } from 'path';
import { AppController } from './app.controller';
import { AppService } from './app.service';

@Module({
  imports: [
    TelegrafModule.forRoot({
      token: env.get('TELEGRAM_BOT_TOKEN').required().asString(),
    }),
    PrismaClientModule.forRoot({
      databaseUrl: env.get('SERVER_POSTGRES_URL').required().asString(),
      logging: 'long_queries',
      maxQueryExecutionTime: 5000,
    }),
    TranslatesModule.forRoot(
      getDefaultTranslatesModuleOptions({
        localePaths: [
          join(__dirname, 'assets', 'i18n'),
          join(__dirname, 'assets', 'i18n', 'class-validator-messages'),
        ],
        vendorLocalePaths: [join(__dirname, 'assets', 'i18n')],
        locales: [DEFAULT_LANGUAGE, 'ru'],
      })
    ),
    BotCommandsModule,
    LanguageSwitherModule.forRoot(),
    DebugMessagesModule.forRoot(),
    CurrencyConverterModule.forRoot(),
    FactsGeneratorModule.forRoot(),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

```

## Translate all dictionaries

### Run generate all need files

> npm run generate
```
endy@endy-virtual-machine:~/Projects/current/kaufman-bot$ npm run generate

> kaufman-bot@0.0.0 generate
> npm run prisma:generate && npm run rucken -- prepare --locales=en,ru && npm run lint:fix


> kaufman-bot@0.0.0 prisma:generate
> npm run -- prisma generate


> kaufman-bot@0.0.0 prisma
> prisma "generate"

Prisma schema loaded from prisma/schema.prisma

✔ Generated Prisma Client (3.11.1 | library) to ./node_modules/@prisma/client in 193ms
You can now start using Prisma Client in your code. Reference: https://pris.ly/d/client
```
import { PrismaClient } from '@prisma/client'
const prisma = new PrismaClient()
```

> kaufman-bot@0.0.0 rucken
> rucken "prepare" "--locales=en,ru"


> kaufman-bot@0.0.0 lint:fix
> npm run tsc:lint && nx workspace-lint --fix && nx run-many --target=lint --all --fix && nx format:write --all


> kaufman-bot@0.0.0 tsc:lint
> tsc --noEmit -p tsconfig.base.json


    ✔  nx run currency-converter-server:lint  [local cache]
    ✔  nx run language-swither-server:lint  [local cache]
    ✔  nx run facts-generator-server:lint  [local cache]
    ✔  nx run debug-messages-server:lint (1s)
    ✔  nx run html-scraper-server:lint  [local cache]
    ✔  nx run core-server:lint (1s)
    ✔  nx run server:lint (1s)

 ——————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————

 >  NX   Successfully ran target lint for 7 projects (4s)
 
         With additional flags:
           --fix=true
 
   Nx read the output from the cache instead of running the command for 4 out of 7 tasks.
 

 >  NX   Running affected:* commands with --all can result in very slow builds.

   --all is not meant to be used for any sizable project or to be used in CI.
   
   Learn more about checking only what is affected: https://nx.dev/latest/angular/cli/affected#affected.
```

### Add translate for all empty words

View list of dictionaries for translate
![View list of dictionaries for translate](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/jsw5fxknns2weuvja7kp.png)
 
Add translates in ru language
![Add translates in ru language](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/spjhimp86dnis25v3on9.png)

Run generate for generate json dictionaries from po files
> npm run generate

## Check new logic in telegram bot

Common help message
![Common help message](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9kpwgs4337uuetng1udh.png)
 
Help message for debug command
![Help message for debug command](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/mzlhj561bdorgb9w07l6.png)
 
Enable debug mode and check work it
![Enable debug mode and check work it](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tmj9tnrknmorj7m35b5a.png)
 
Change user locale with show debug info
![Change user locale with show debug info](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/bllhwtgfgkoufn06ajih.png)
 
Common help message on Russia locale
![Common help message on Russia locale](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ppe5kjx3kaolypy6expe.png)

Example of debug emoji

Send
![Send](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/l9e83a0wdxg67je9fkqk.png)
 
Debug
```json
{
    "message_id": 822,
    "from": {
        "id": 102375526,
        "is_bot": false,
        "first_name": "IL'shat",
        "last_name": "Khamitov",
        "username": "KaufmanEndy",
        "language_code": "ru"
    },
    "chat": {
        "id": 102375526,
        "first_name": "IL'shat",
        "last_name": "Khamitov",
        "username": "KaufmanEndy",
        "type": "private"
    },
    "date": 1648837576,
    "sticker": {
        "width": 512,
        "height": 512,
        "emoji": "😥",
        "set_name": "PeopleMemes",
        "is_animated": true,
        "is_video": false,
        "thumb": {
            "file_id": "AAMCAgADGQEAAgM2YkdDyN-EhN0TaqqYga12aOy6N3EAApoUAALJ5JFJ6q9bB_WLS2YBAAdtAAMjBA",
            "file_unique_id": "AQADmhQAAsnkkUly",
            "file_size": 6304,
            "width": 128,
            "height": 128
        },
        "file_id": "CAACAgIAAxkBAAIDNmJHQ8jfhITdE2qqmIGtdmjsujdxAAKaFAACyeSRSeqvWwf1i0tmIwQ",
        "file_unique_id": "AgADmhQAAsnkkUk",
        "file_size": 29792
    }
}
```
![Debug](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/f3xefz4agpq9xs0g97km.png)
  
Answer
![Answer](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/2ijkniyfzzq3hl4m1p0j.png)

For emoji used top app handler
![For emoji used top app handler](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/z17c6ex9ec40bgrapu86.png)

In the next post, I will add a module for process unhandled message with Dialogflow API...