Links
https://github.com/EndyKaufman/kaufman-bot - source code of bot
https://telegram.me/KaufmanBot - current bot in telegram
https://github.com/kaufman-bot/schematics-example - project generated with @kaufman-bot/schematics
Description of work
In the current post, I'm copying the NestJS schemas for building apps and libraries, and after a lot of modifications, I'll make the schemas for building kaufman-bot apps and libraries.
Create new libraries
Create new library for schematics
npm run nx -- g lib schematics
endy@endy-virtual-machine:~/Projects/current/kaufman-bot$ npm run nx -- g lib schematics
> kaufman-bot@2.2.2 nx
> nx "g" "lib" "schematics"
CREATE libs/schematics/README.md
CREATE libs/schematics/.babelrc
CREATE libs/schematics/src/index.ts
CREATE libs/schematics/tsconfig.json
CREATE libs/schematics/tsconfig.lib.json
UPDATE tsconfig.base.json
CREATE libs/schematics/project.json
UPDATE workspace.json
CREATE libs/schematics/.eslintrc.json
CREATE libs/schematics/jest.config.js
CREATE libs/schematics/tsconfig.spec.json
CREATE libs/schematics/src/lib/schematics.module.ts
Append logic for schematics
Remove not need files
rm -rf libs/schematics/src/lib
Clone nrwl nx repository
git clone git@github.com:nrwl/nx.git
endy@endy-virtual-machine:~/Projects/current/kaufman-bot$ git clone git@github.com:nrwl/nx.git
Cloning into 'nx'...
remote: Enumerating objects: 94099, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (10/10), done.
remote: Total 94099 (delta 0), reused 2 (delta 0), pack-reused 94089
Receiving objects: 100% (94099/94099), 69.44 MiB | 4.43 MiB/s, done.
Resolving deltas: 100% (64903/64903), done.
Copy-past logic
Copy nest schematics to kaufman-bot libs
cp -Rf ./nx/packages/nest/** ./libs/schematics
Remove not need files
rm -rf libs/schematics/src/generators/class libs/schematics/src/generators/controller libs/schematics/src/generators/convert-tslint-to-eslint libs/schematics/src/generators/decorator libs/schematics/src/generators/filter libs/schematics/src/generators/gateway libs/schematics/src/generators/guard libs/schematics/src/generators/interceptor libs/schematics/src/generators/interface libs/schematics/src/generators/middleware libs/schematics/src/generators/module libs/schematics/src/generators/pipe libs/schematics/src/generators/provider libs/schematics/src/generators/resolver libs/schematics/src/generators/resource libs/schematics/src/generators/service libs/schematics/src/lib libs/schematics/src/migrations libs/schematics/README.md libs/schematics/migrations.spec.ts libs/schematics/migrations.json libs/schematics/index.ts nx libs/schematics/index.ts libs/schematics/src/generators/library/library.spec.ts libs/schematics/src/generators/library/snapshots libs/schematics/src/index.ts libs/schematics/src/generators/init/init.spec.ts libs/schematics/src/generators/utils/run-nest-schematic.spec.ts libs/schematics/src/generators/application/application.spec.ts libs/schematics/src/generators/application/files/app/app.controller.spec.ts_tmpl_ libs/schematics/src/generators/application/files/app/app.controller.ts_tmpl_ libs/schematics/src/generators/application/files/app/app.service.spec.ts_tmpl_ libs/schematics/src/generators/application/files/app/app.service.ts_tmpl_ libs/schematics/src/generators/library/snapshots/library.spec.ts.snap libs/schematics/src/generators/library/library.spec.ts libs/schematics/src/generators/utils/run-nest-schematic.spec.ts libs/schematics/src/index.ts libs/schematics/src/generators/library/files/controller/src/lib/fileName.controller.spec.ts_tmpl_ libs/schematics/src/generators/library/files/controller/src/lib/fileName.controller.ts_tmpl_ libs/schematics/src/generators/library/files/service/src/lib/fileName.service.spec.ts_tmpl_
Update generators list
libs/schematics/generators.json
{
"name": "nx/nest",
"version": "0.1",
"extends": ["@nrwl/workspace"],
"schematics": {
"application": {
"factory": "./src/generators/application/application#applicationSchematic",
"schema": "./src/generators/application/schema.json",
"aliases": ["app"],
"x-type": "application",
"description": "Create a KaufmanBot application."
},
"init": {
"factory": "./src/generators/init/init#initSchematic",
"schema": "./src/generators/init/schema.json",
"description": "Initialize the `@kaufman-bot/schematics` plugin.",
"aliases": ["ng-add"],
"hidden": true
},
"library": {
"factory": "./src/generators/library/library#librarySchematic",
"schema": "./src/generators/library/schema.json",
"aliases": ["lib"],
"x-type": "library",
"description": "Create a new KaufmanBot library."
}
},
"generators": {
"application": {
"factory": "./src/generators/application/application",
"schema": "./src/generators/application/schema.json",
"aliases": ["app"],
"x-type": "application",
"description": "Create a KaufmanBot application."
},
"init": {
"factory": "./src/generators/init/init",
"schema": "./src/generators/init/schema.json",
"description": "Initialize the `@kaufman-bot/schematics` plugin.",
"aliases": ["ng-add"],
"hidden": true
},
"library": {
"factory": "./src/generators/library/library",
"schema": "./src/generators/library/schema.json",
"aliases": ["lib"],
"x-type": "library",
"description": "Create a new KaufmanBot library."
}
}
}
Replace all names
npx -y replace-in-files-cli --string="build/packages/nest" --replacement="dist/libs/schematics" './libs/schematics/**'
npx -y replace-in-files-cli --string="packages/nest" --replacement="libs/schematics" './libs/schematics/**'
Update template file of main
libs/schematics/src/generators/application/files/main.ts__tmpl__
// eslint-disable-next-line @typescript-eslint/no-var-requires
require('source-map-support').install();
import { Logger } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import env from 'env-var';
import { getBotToken } from 'nestjs-telegraf';
import { AppModule } from './app/app.module';
const logger = new Logger('Application');
//do something when app is closing
process.on('exit', exitHandler.bind(null, { cleanup: true }));
//catches ctrl+c event
process.on('SIGINT', exitHandler.bind(null, { exit: true }));
// catches "kill pid" (for example: nodemon restart)
process.on('SIGUSR1', exitHandler.bind(null, { exit: true }));
process.on('SIGUSR2', exitHandler.bind(null, { exit: true }));
//catches uncaught exceptions
process.on('uncaughtException', exitHandler.bind(null, { exit: true }));
async function bootstrap() {
const app = await NestFactory.create(AppModule);
const TELEGRAM_BOT_WEB_HOOKS_PATH = env
.get('TELEGRAM_BOT_WEB_HOOKS_PATH')
.asString();
if (TELEGRAM_BOT_WEB_HOOKS_PATH) {
const bot = app.get(getBotToken());
app.use(bot.webhookCallback(TELEGRAM_BOT_WEB_HOOKS_PATH));
}
const port = env.get('PORT').default(3333).asPortNumber();
await app.listen(port);
logger.log(`🚀 Application is running on: http://localhost:${port}`);
}
try {
bootstrap().catch((err) => {
logger.error(err, err.stack);
});
} catch (err) {
logger.error(err, err.stack);
}
function exitHandler(options, exitCode) {
if (options.cleanup) {
logger.log('exit: clean');
}
if (exitCode || exitCode === 0) {
if (exitCode !== 0) {
logger.error(exitCode, exitCode.stack);
logger.log(`exit: code - ${exitCode}`);
} else {
logger.log(`exit: code - ${exitCode}`);
}
}
if (options.exit) {
process.exit();
}
}
Update template of service
libs/schematics/src/generators/application/files/app/app.service.ts__tmpl__
import { BotInGroupsProcessorService } from '@kaufman-bot/bot-in-groups-server';
import { BotCommandsService } from '@kaufman-bot/core-server';
import { Injectable, Logger } from '@nestjs/common';
import { On, Start, Update, Use } from 'nestjs-telegraf';
import { Context } from 'telegraf';
@Update()
@Injectable()
export class AppService {
private readonly logger = new Logger(AppService.name);
constructor(
private readonly botCommandsService: BotCommandsService,
private readonly botInGroupsProcessorService: BotInGroupsProcessorService
) {}
@Start()
async startCommand(ctx: Context) {
await this.botCommandsService.start(ctx);
}
@Use()
async use(ctx) {
try {
await this.botInGroupsProcessorService.process(ctx);
} catch (err) {
this.logger.error(err, err.stack);
}
}
@On('sticker')
async onSticker(ctx) {
try {
await this.botCommandsService.process(ctx);
} catch (err) {
this.logger.error(err, err.stack);
}
}
@On('text')
async onMessage(ctx) {
try {
await this.botCommandsService.process(ctx);
} catch (err) {
this.logger.error(err, err.stack);
}
}
}
Update template of module
libs/schematics/src/generators/application/files/app/app.module.ts__tmpl__
import { BotInGroupsModule } from '@kaufman-bot/bot-in-groups-server';
import { BotCommandsModule } from '@kaufman-bot/core-server';
import { DebugMessagesModule } from '@kaufman-bot/debug-messages-server';
import { FactsGeneratorModule } from '@kaufman-bot/facts-generator-server';
import { LanguageSwitherModule } from '@kaufman-bot/language-swither-server';
import { ShortCommandsModule } from '@kaufman-bot/short-commands-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 { AppService } from './app.service';
const TELEGRAM_BOT_WEB_HOOKS_DOMAIN = env
.get('TELEGRAM_BOT_WEB_HOOKS_DOMAIN')
.asString();
const TELEGRAM_BOT_WEB_HOOKS_PATH = env
.get('TELEGRAM_BOT_WEB_HOOKS_PATH')
.asString();
const BOT_NAMES = env.get('BOT_NAMES').required().asArray();
@Module({
imports: [
TelegrafModule.forRoot({
token: env.get('TELEGRAM_BOT_TOKEN').required().asString(),
launchOptions: {
dropPendingUpdates: true,
...(TELEGRAM_BOT_WEB_HOOKS_DOMAIN && TELEGRAM_BOT_WEB_HOOKS_PATH
? {
webhook: {
domain: TELEGRAM_BOT_WEB_HOOKS_DOMAIN,
hookPath: TELEGRAM_BOT_WEB_HOOKS_PATH,
},
}
: {}),
},
}),
TranslatesModule.forRoot(
getDefaultTranslatesModuleOptions({
localePaths: [
join(__dirname, 'assets', 'i18n'),
join(__dirname, 'assets', 'i18n', 'getText'),
join(__dirname, 'assets', 'i18n', 'class-validator-messages'),
],
vendorLocalePaths: [join(__dirname, 'assets', 'i18n')],
locales: ['en'],
})
),
DebugMessagesModule.forRoot(),
BotCommandsModule.forRoot({
admins: env.get('TELEGRAM_BOT_ADMINS').default('').asArray(','),
commit: env.get('DEPLOY_COMMIT').default('').asString(),
date: env.get('DEPLOY_DATE').default('').asString(),
version: env.get('DEPLOY_VERSION').default('').asString(),
}),
ShortCommandsModule.forRoot({
commands: {
en: {
'*fact*|history': 'get facts',
'*what you can do*|faq': 'help',
'disable debug': 'debug off',
'enable debug': 'debug on',
},
},
}),
BotInGroupsModule.forRoot({
botNames: {
en: BOT_NAMES,
},
botMeetingInformation: {
en: [`Hello! I'm ${BOT_NAMES[0]} 😉`, 'Hello!', 'Hello 🖖'],
},
}),
LanguageSwitherModule.forRoot(),
FactsGeneratorModule.forRoot(),
],
providers: [AppService],
})
export class AppModule {}
Update library service template
libs/schematics/src/generators/library/files/service/src/lib/__fileName__.service.ts__tmpl__
import {
BotCommandsEnum,
BotCommandsProvider,
BotCommandsProviderActionMsg,
BotCommandsProviderActionResultType,
BotCommandsToolsService,
} from '@kaufman-bot/core-server';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { getText } from 'class-validator-multi-lang';
import { TranslatesService } from 'nestjs-translates';
export const <%= constantName %>_CONFIG = '<%= constantName %>_CONFIG';
export interface <%= className %>Config {
title: string;
name: string;
descriptions: string;
usage: string[];
spyWords: string[];
category: string;
}
@Injectable()
export class <%= className %>Service implements BotCommandsProvider {
private readonly logger = new Logger(<%= className %>Service.name);
constructor(
@Inject(<%= constantName %>_CONFIG)
private readonly <%= propertyName %>Config: <%= className %>Config,
private readonly translatesService: TranslatesService,
private readonly commandToolsService: BotCommandsToolsService,
private readonly botCommandsToolsService: BotCommandsToolsService
) {}
async onHelp<
TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
>(msg: TMsg): Promise<BotCommandsProviderActionResultType<TMsg>> {
return await this.onMessage({
...msg,
text: `${this.<%= propertyName %>Config.name} ${BotCommandsEnum.help}`,
});
}
async onMessage<
TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
>(msg: TMsg): Promise<BotCommandsProviderActionResultType<TMsg>> {
const locale = this.botCommandsToolsService.getLocale(msg, 'en');
const spyWord = this.<%= propertyName %>Config.spyWords.find((spyWord) =>
this.commandToolsService.checkCommands(msg.text, [spyWord], locale)
);
if (spyWord) {
if (
this.commandToolsService.checkCommands(
msg.text,
[BotCommandsEnum.help],
locale
)
) {
return {
type: 'markdown',
message: msg,
markdown: this.commandToolsService.generateHelpMessage(msg, {
locale,
name: this.<%= propertyName %>Config.title,
descriptions: this.<%= propertyName %>Config.descriptions,
usage: this.<%= propertyName %>Config.usage,
category: this.<%= propertyName %>Config.category,
}),
};
}
const processedMsg = await this.process(msg, locale);
if (typeof processedMsg === 'string') {
return {
type: 'text',
message: msg,
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) {
if (
this.commandToolsService.checkCommands(
msg.text,
[getText('ping')],
locale
)
) {
return this.translatesService.translate(
getText('pong'),
locale
);
}
return null;
}
}
Update library module template
libs/schematics/src/generators/library/files/common/src/lib/__fileName__.module.ts__tmpl__
import {
BotCommandsCategory,
BotCommandsModule,
BOT_COMMANDS_PROVIDER,
} from '@kaufman-bot/core-server';
import { DynamicModule, Module } from '@nestjs/common';
import { getText } from 'class-validator-multi-lang';
import { TranslatesModule } from 'nestjs-translates';
import {
<%= className %>Service,
<%= className %>Config,
<%= constantName %>_CONFIG,
} from './<%= fileName %>.service';
@Module({
imports: [TranslatesModule, BotCommandsModule],
exports: [TranslatesModule, BotCommandsModule],
})
export class <%= className %>Module {
static forRoot(): DynamicModule {
return {
module: <%= className %>Module,
providers: [
{
provide: <%= constantName %>_CONFIG,
useValue: <<%= className %>Config>{
title: getText('<%= TitleName %> commands'),
name: '<%= propertyName %>',
usage: [
getText('<%= propertyName %> ping'),
getText('<%= propertyName %> help'),
],
descriptions: getText(
'Commands for <%= titleName %>'
),
spyWords: [getText('<%= propertyName %>')],
category: BotCommandsCategory.user,
},
},
{
provide: BOT_COMMANDS_PROVIDER,
useClass: <%= className %>Service,
},
],
exports: [<%= constantName %>_CONFIG],
};
}
}
Add custom schematics helpers
libs/schematics/src/generators/init/lib/add-custom.ts
import type { Tree } from '@nrwl/devkit';
import { updateJson } from '@nrwl/devkit';
export function updateTsConfig(tree: Tree) {
if (tree.exists('tsconfig.base.json')) {
updateJson(tree, 'tsconfig.base.json', (json) => {
json['compilerOptions'] = {
...json['compilerOptions'],
allowSyntheticDefaultImports: true,
strictNullChecks: true,
noImplicitOverride: true,
strictPropertyInitialization: true,
noImplicitReturns: true,
noFallthroughCasesInSwitch: true,
esModuleInterop: true,
noImplicitAny: false,
};
return json;
});
}
}
export function addScript(tree: Tree, projectName: string) {
updateJson(tree, 'package.json', (json) => {
json['scripts'] = {
...json['scripts'],
rucken: 'rucken',
nx: 'nx',
};
if (!json['scripts'][`serve:${projectName}-local`]) {
json['scripts'][
`serve:${projectName}-local`
] = `export $(xargs < ./.env.local) > /dev/null 2>&1 && npm run nx -- serve ${projectName}`;
}
return json;
});
}
export function addGitIgnoreEntry(host: Tree) {
if (host.exists('.gitignore')) {
let content = host.read('.gitignore', 'utf-8');
if (!content?.includes('*.env.*')) {
content = `${content}\n*.env.*\n`;
}
host.write('.gitignore', content);
} else {
host.write('.gitignore', `*.env.*\n`);
}
}
export function addEnvFilesEntry(host: Tree, botName: string) {
append('.env.local');
append('.env-example.local');
function append(filename: string) {
let content = '';
if (host.exists(filename)) {
content = host.read(filename, 'utf-8') || '';
}
const contentRows = content.split('\n');
const newRows: string[] = [];
const rows = [
`TELEGRAM_BOT_TOKEN=`,
`TELEGRAM_BOT_WEB_HOOKS_DOMAIN=`,
`TELEGRAM_BOT_WEB_HOOKS_PATH=`,
`TELEGRAM_BOT_ADMINS=`,
`BOT_NAMES=${botName}`,
];
for (
let contentRowindex = 0;
contentRowindex < contentRows.length;
contentRowindex++
) {
const contentRow = contentRows[contentRowindex];
for (let rowIndex = 0; rowIndex < rows.length; rowIndex++) {
const row = rows[rowIndex];
if ((contentRow || '').split('=')[0] !== (row || '').split('=')[0]) {
newRows.push(row);
}
}
}
host.write(
filename,
[
...(contentRows.length === 1 && !contentRows[0] ? [] : contentRows),
...newRows,
].join('\n')
);
}
}
Update index file of init generator
libs/schematics/src/generators/init/lib/index.ts
export * from './add-custom';
export * from './add-dependencies';
export * from './normalize-options';
Update versions file
libs/schematics/src/utils/versions.ts
export const nxVersion = '*';
export const nestJsVersion7 = '^7.0.0';
export const nestJsVersion8 = '^8.0.0';
export const nestJsSchematicsVersion = '^8.0.0';
export const rxjsVersion6 = '~6.6.3';
export const rxjsVersion7 = '^7.0.0';
export const reflectMetadataVersion = '^0.1.13';
export const kaufmanBotVersion = '^2.2.2';
Update dependencies helper
libs/schematics/src/generators/init/lib/add-dependencies.ts
import type { GeneratorCallback, Tree } from '@nrwl/devkit';
import { addDependenciesToPackageJson, readJson } from '@nrwl/devkit';
import { satisfies } from 'semver';
import {
kaufmanBotVersion,
nestJsSchematicsVersion,
nestJsVersion7,
nestJsVersion8,
nxVersion,
reflectMetadataVersion,
rxjsVersion6,
rxjsVersion7,
} from '../../../utils/versions';
export function addDependencies(tree: Tree): GeneratorCallback {
// Old nest 7 and rxjs 6 by default
let NEST_VERSION = nestJsVersion7;
let RXJS = rxjsVersion6;
const packageJson = readJson(tree, 'package.json');
if (packageJson.dependencies['@angular/core']) {
let rxjs = packageJson.dependencies['rxjs'];
if (rxjs.startsWith('~') || rxjs.startsWith('^')) {
rxjs = rxjs.substring(1);
}
if (satisfies(rxjs, rxjsVersion7)) {
NEST_VERSION = nestJsVersion8;
RXJS = packageJson.dependencies['rxjs'];
}
} else {
NEST_VERSION = nestJsVersion8;
RXJS = rxjsVersion7;
}
return addDependenciesToPackageJson(
tree,
{
'@nestjs/common': NEST_VERSION,
'@nestjs/core': NEST_VERSION,
'@nestjs/platform-express': NEST_VERSION,
'reflect-metadata': reflectMetadataVersion,
'@kaufman-bot/bot-in-groups-server': kaufmanBotVersion,
'@kaufman-bot/core-server': kaufmanBotVersion,
'@kaufman-bot/debug-messages-server': kaufmanBotVersion,
'@kaufman-bot/language-swither-server': kaufmanBotVersion,
'@kaufman-bot/short-commands-server': kaufmanBotVersion,
'@kaufman-bot/html-scraper-server': kaufmanBotVersion,
'@kaufman-bot/facts-generator-server': kaufmanBotVersion,
'@ngneat/transloco': '^4.0.0',
'@ngneat/transloco-locale': '^4.0.0',
'class-validator-multi-lang': '^0.130.201',
'class-transformer': '^0.5.1',
'class-transformer-global-storage': '^0.4.1-1',
'env-var': '^7.1.1',
'nestjs-telegraf': '^2.4.0',
'nestjs-translates': '^1.0.3',
rxjs: RXJS,
tslib: '^2.0.0',
},
{
'@nestjs/schematics': nestJsSchematicsVersion,
'@nestjs/testing': NEST_VERSION,
'@nrwl/nest': nxVersion,
'@ngneat/transloco-keys-manager': '^3.4.1',
"source-map-support": "^0.5.21",
rucken: '^3.5.3',
}
);
}
Update application generator helper
libs/schematics/src/generators/application/application.ts
import type { GeneratorCallback, Tree } from '@nrwl/devkit';
import { convertNxGenerator, formatFiles } from '@nrwl/devkit';
import { applicationGenerator as nodeApplicationGenerator } from '@nrwl/node';
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
import { initGenerator } from '../init/init';
import { addScript } from '../init/lib';
import {
createFiles,
normalizeOptions,
toNodeApplicationGeneratorOptions,
updateTsConfig,
} from './lib';
import type { ApplicationGeneratorOptions } from './schema';
export async function applicationGenerator(
tree: Tree,
rawOptions: ApplicationGeneratorOptions
): Promise<GeneratorCallback> {
const options = normalizeOptions(tree, rawOptions);
addScript(tree, rawOptions.name);
const initTask = await initGenerator(tree, {
botName: options.botName,
unitTestRunner: options.unitTestRunner,
skipFormat: true,
});
const nodeApplicationTask = await nodeApplicationGenerator(tree, {
...toNodeApplicationGeneratorOptions(options),
});
createFiles(tree, options);
updateTsConfig(tree, options);
if (!options.skipFormat) {
await formatFiles(tree);
}
return runTasksInSerial(initTask, nodeApplicationTask);
}
export default applicationGenerator;
export const applicationSchematic = convertNxGenerator(applicationGenerator);
Update init helper
libs/schematics/src/generators/init/init.ts
import type { GeneratorCallback, Tree } from '@nrwl/devkit';
import { convertNxGenerator, formatFiles } from '@nrwl/devkit';
import { initGenerator as nodeInitGenerator } from '@nrwl/node';
import { runTasksInSerial } from '@nrwl/workspace/src/utilities/run-tasks-in-serial';
import { setDefaultCollection } from '@nrwl/workspace/src/utilities/set-default-collection';
import {
addDependencies,
addEnvFilesEntry,
addGitIgnoreEntry,
normalizeOptions,
updateTsConfig,
} from './lib';
import type { InitGeneratorOptions } from './schema';
export async function initGenerator(
tree: Tree,
rawOptions: InitGeneratorOptions
): Promise<GeneratorCallback> {
const options = normalizeOptions(rawOptions);
setDefaultCollection(tree, '@kaufman-bot/schematics');
updateTsConfig(tree);
addGitIgnoreEntry(tree);
addEnvFilesEntry(tree, options.botName);
const nodeInitTask = await nodeInitGenerator(tree, options);
const installPackagesTask = addDependencies(tree);
if (!options.skipFormat) {
await formatFiles(tree);
}
return runTasksInSerial(nodeInitTask, installPackagesTask);
}
export default initGenerator;
export const initSchematic = convertNxGenerator(initGenerator);
Create public.ts
libs/schematics/src/public.ts
export { applicationGenerator } from './generators/application/application';
export { initGenerator } from './generators/init/init';
export { libraryGenerator } from './generators/library/library';
Update package.json
libs/schematics/package.json
{
"name": "@kaufman-bot/schematics",
"description": "The Nx Plugin for Nest that contains executors and generators for allowing your workspace to create KaufmanBot APIs.",
"license": "MIT",
"author": "EndyKaufman <admin@site15.ru>",
"keywords": [
"Monorepo",
"Node",
"Nest",
"CLI",
"kaufman-bot",
"nx",
"schematics",
"nests",
"telegram"
],
"bugs": {
"url": "https://github.com/EndyKaufman/kaufman-bot/issues"
},
"homepage": "https://github.com/EndyKaufman/kaufman-bot",
"repository": {
"type": "git",
"url": "git+https://github.com/EndyKaufman/kaufman-bot.git"
},
"maintainers": [
{
"name": "EndyKaufman",
"email": "admin@site15.ru"
}
],
"readme": "README.md",
"main": "./index.js",
"typings": "./index.d.ts",
"schematics": "./generators.json",
"dependencies": {
"@nrwl/devkit": "13.8.1",
"@nrwl/linter": "13.8.1",
"@nrwl/node": "13.8.1",
"@nrwl/js": "^13.10.2",
"@nrwl/jest": "13.8.1",
"@nestjs/schematics": "^8.0.0"
},
"version": "2.2.2",
"i18n": [
{
"scope": "schematics",
"path": "src/i18n",
"strategy": "join"
}
]
}
Update project.json file
libs/schematics/project.json
{
"root": "libs/schematics",
"sourceRoot": "libs/schematics",
"projectType": "library",
"targets": {
"test": {
"executor": "@nrwl/jest:jest",
"options": {
"jestConfig": "libs/schematics/jest.config.js",
"passWithNoTests": true
},
"outputs": ["coverage/libs/schematics"]
},
"build": {
"executor": "@nrwl/js:tsc",
"options": {
"outputPath": "dist/libs/schematics",
"tsConfig": "libs/schematics/tsconfig.lib.json",
"main": "libs/schematics/index.ts",
"updateBuildableProjectDepsInPackageJson": false,
"assets": [
{
"input": "libs/schematics",
"glob": "**/files/**",
"output": "/"
},
{
"input": "libs/schematics",
"glob": "**/files/**/.gitkeep",
"output": "/"
},
{
"input": "libs/schematics",
"glob": "**/*.json",
"ignore": ["**/tsconfig*.json", "project.json"],
"output": "/"
},
{
"input": "libs/schematics",
"glob": "**/*.js",
"ignore": ["**/jest.config.js"],
"output": "/"
},
{
"input": "libs/schematics",
"glob": "**/*.d.ts",
"output": "/"
},
{
"input": "",
"glob": "LICENSE",
"output": "/"
},
{
"input": "libs/schematics",
"glob": "**/*.md",
"output": "/"
}
]
},
"outputs": ["{options.outputPath}"]
},
"lint": {
"executor": "@nrwl/linter:eslint",
"options": {
"lintFilePatterns": [
"libs/schematics/**/*.ts",
"libs/schematics/**/*.spec.ts",
"libs/schematics/**/*_spec.ts",
"libs/schematics/**/*.spec.tsx",
"libs/schematics/**/*.spec.js",
"libs/schematics/**/*.spec.jsx",
"libs/schematics/**/*.d.ts"
]
},
"outputs": ["{options.outputFile}"]
}
}
}
Install need dependencies in root project
npm i --save-dev @nrwl/js @nrwl/devkit
Add custom config for rucken tools for exclude folders from generated index file
rucken.json
{
"makeTsList": {
"indexFileName": "index",
"excludes": [
"*node_modules*",
"*public_api.ts*",
"*.spec*",
"environment*",
"*test*",
"*e2e*",
"*.stories.ts",
"*.d.ts",
"*files*",
"*generators*",
"*utils*"
]
}
}
Update options of init generator
libs/schematics/src/generators/init/lib/normalize-options.ts
import type { InitGeneratorOptions } from '../schema';
export function normalizeOptions(
options: InitGeneratorOptions
): InitGeneratorOptions & Pick<Required<InitGeneratorOptions>, 'botName'> {
return {
...options,
unitTestRunner: options.unitTestRunner ?? 'jest',
botName: options.botName ?? 'Bot',
};
}
Update schema types of init generator
libs/schematics/src/generators/init/schema.d.ts
import { UnitTestRunner } from '../utils';
export interface InitGeneratorOptions {
botName?: string;
skipFormat?: boolean;
unitTestRunner?: UnitTestRunner;
}
Update schema json on init generator
libs/schematics/src/generators/init/schema.json
{
"$schema": "http://json-schema.org/schema",
"$id": "NxNestInitGenerator",
"title": "Init Nest Plugin",
"description": "Init Nest Plugin.",
"cli": "nx",
"type": "object",
"properties": {
"botName": {
"description": "Bot name.",
"type": "string",
"default": "Bot"
},
"unitTestRunner": {
"description": "Adds the specified unit test runner.",
"type": "string",
"enum": ["jest", "none"],
"default": "jest"
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false
}
},
"additionalProperties": false,
"required": []
}
Update application options
libs/schematics/src/generators/application/lib/normalize-options.ts
import type { Tree } from '@nrwl/devkit';
import { getWorkspaceLayout, joinPathFragments, names } from '@nrwl/devkit';
import { Linter } from '@nrwl/linter';
import type { Schema as NodeApplicationGeneratorOptions } from '@nrwl/node/src/generators/application/schema';
import type { ApplicationGeneratorOptions, NormalizedOptions } from '../schema';
export function normalizeOptions(
tree: Tree,
options: ApplicationGeneratorOptions
): NormalizedOptions {
const appDirectory = options.directory
? `${names(options.directory).fileName}/${names(options.name).fileName}`
: names(options.name).fileName;
const appProjectRoot = joinPathFragments(
getWorkspaceLayout(tree).appsDir,
appDirectory
);
return {
...options,
appProjectRoot,
linter: options.linter ?? Linter.EsLint,
unitTestRunner: options.unitTestRunner ?? 'jest',
botName: options.botName,
};
}
export function toNodeApplicationGeneratorOptions(
options: NormalizedOptions
): NodeApplicationGeneratorOptions {
return {
name: options.name,
directory: options.directory,
frontendProject: options.frontendProject,
linter: options.linter,
skipFormat: true,
skipPackageJson: options.skipPackageJson,
standaloneConfig: options.standaloneConfig,
tags: options.tags,
unitTestRunner: options.unitTestRunner,
setParserOptionsProject: options.setParserOptionsProject,
};
}
Update application schema type
libs/schematics/src/generators/application/schema.d.ts
import { Linter } from '@nrwl/linter';
import { UnitTestRunner } from '../../utils/test-runners';
export interface ApplicationGeneratorOptions {
name: string;
directory?: string;
frontendProject?: string;
linter?: Exclude<Linter, Linter.TsLint>;
skipFormat?: boolean;
skipPackageJson?: boolean;
standaloneConfig?: boolean;
tags?: string;
unitTestRunner?: UnitTestRunner;
setParserOptionsProject?: boolean;
botName?: string;
}
interface NormalizedOptions extends ApplicationGeneratorOptions {
appProjectRoot: Path;
}
Update application schema json
libs/schematics/src/generators/application/schema.json
{
"$schema": "http://json-schema.org/schema",
"$id": "NxNestKaufmanBotApplicationGenerator",
"title": "Nx Nest KaufmanBot Application Options Schema",
"description": "Nx Nest KaufmanBot Application Options Schema.",
"cli": "nx",
"type": "object",
"properties": {
"name": {
"description": "The name of the application.",
"type": "string",
"$default": {
"$source": "argv",
"index": 0
},
"x-prompt": "What name would you like to use for the node application?"
},
"botName": {
"description": "Bot name.",
"type": "string",
"default": "Bot"
},
"directory": {
"description": "The directory of the new application.",
"type": "string"
},
"skipFormat": {
"description": "Skip formatting files.",
"type": "boolean",
"default": false
},
"skipPackageJson": {
"description": "Do not add dependencies to `package.json`.",
"type": "boolean",
"default": false
},
"linter": {
"description": "The tool to use for running lint checks.",
"type": "string",
"enum": ["eslint", "none"],
"default": "eslint"
},
"unitTestRunner": {
"description": "Test runner to use for unit tests.",
"type": "string",
"enum": ["jest", "none"],
"default": "jest"
},
"tags": {
"description": "Add tags to the application (used for linting).",
"type": "string"
},
"frontendProject": {
"description": "Frontend project that needs to access this application. This sets up proxy configuration.",
"type": "string"
},
"standaloneConfig": {
"description": "Split the project configuration into `<projectRoot>/project.json` rather than including it inside `workspace.json`.",
"type": "boolean"
},
"setParserOptionsProject": {
"type": "boolean",
"description": "Whether or not to configure the ESLint `parserOptions.project` option. We do not do this by default for lint performance reasons.",
"default": false
}
},
"additionalProperties": false,
"required": ["name"]
}
Update export to barrel
libs/schematics/src/generators/library/lib/add-exports-to-barrel.ts
import type { Tree } from '@nrwl/devkit';
import {
addGlobal,
removeChange,
} from '@nrwl/workspace/src/utilities/ast-utils';
import * as ts from 'typescript';
import type { NormalizedOptions } from '../schema';
export function addExportsToBarrelFile(
tree: Tree,
options: NormalizedOptions
): void {
const indexPath = `${options.projectRoot}/src/index.ts`;
const indexContent = tree.read(indexPath, 'utf-8');
let sourceFile = ts.createSourceFile(
indexPath,
indexContent || '',
ts.ScriptTarget.Latest,
true
);
sourceFile = removeChange(
tree,
sourceFile,
indexPath,
0,
`export * from './lib/${options.fileName}';`
);
sourceFile = addGlobal(
tree,
// eslint-disable-next-line @typescript-eslint/no-unused-vars
sourceFile,
indexPath,
`export * from './lib/${options.fileName}.module';`
);
}
Update create files
libs/schematics/src/generators/library/lib/create-files.ts
import type { Tree } from '@nrwl/devkit';
import {
generateFiles,
joinPathFragments,
names,
offsetFromRoot,
} from '@nrwl/devkit';
import type { NormalizedOptions } from '../schema';
function capitalizeFirstLetter(text: string | undefined, locale: string) {
const [first, ...rest] = (text || '').trim();
return (first || '').toLocaleUpperCase(locale) + rest.join('');
}
export function createFiles(tree: Tree, options: NormalizedOptions): void {
const substitutions = {
...options,
...names(options.projectName),
titleName: names(options.projectName).fileName.split('-').join(' '),
TitleName: capitalizeFirstLetter(
names(options.projectName).fileName.split('-').join(' '),
'en'
),
tmpl: '',
offsetFromRoot: offsetFromRoot(options.projectRoot),
};
generateFiles(
tree,
joinPathFragments(__dirname, '..', 'files', 'common'),
options.projectRoot,
substitutions
);
generateFiles(
tree,
joinPathFragments(__dirname, '..', 'files', 'service'),
options.projectRoot,
substitutions
);
}
Update options
libs/schematics/src/generators/library/lib/normalize-options.ts
import type { Tree } from '@nrwl/devkit';
import {
generateFiles,
joinPathFragments,
names,
offsetFromRoot,
} from '@nrwl/devkit';
import type { NormalizedOptions } from '../schema';
function capitalizeFirstLetter(text: string | undefined, locale: string) {
const [first, ...rest] = (text || '').trim();
return (first || '').toLocaleUpperCase(locale) + rest.join('');
}
export function createFiles(tree: Tree, options: NormalizedOptions): void {
const substitutions = {
...options,
...names(options.projectName),
titleName: names(options.projectName).fileName.split('-').join(' '),
TitleName: capitalizeFirstLetter(
names(options.projectName).fileName.split('-').join(' '),
'en'
),
tmpl: '',
offsetFromRoot: offsetFromRoot(options.projectRoot),
};
generateFiles(
tree,
joinPathFragments(__dirname, '..', 'files', 'common'),
options.projectRoot,
substitutions
);
generateFiles(
tree,
joinPathFragments(__dirname, '..', 'files', 'service'),
options.projectRoot,
substitutions
);
}
Update library schema types
libs/schematics/src/generators/library/schema.d.ts
import { Linter } from '@nrwl/linter';
import { UnitTestRunner } from '../utils';
export interface LibraryGeneratorOptions {
name: string;
buildable?: boolean;
directory?: string;
importPath?: string;
linter?: Exclude<Linter, Linter.TsLint>;
publishable?: boolean;
skipFormat?: boolean;
skipTsConfig?: boolean;
strict?: boolean;
tags?: string;
target?:
| 'es5'
| 'es6'
| 'esnext'
| 'es2015'
| 'es2016'
| 'es2017'
| 'es2018'
| 'es2019'
| 'es2020';
testEnvironment?: 'jsdom' | 'node';
unitTestRunner?: UnitTestRunner;
standaloneConfig?: boolean;
setParserOptionsProject?: boolean;
}
export interface NormalizedOptions extends LibraryGeneratorOptions {
fileName: string;
parsedTags: string[];
prefix: string;
projectDirectory: string;
projectName: string;
projectRoot: Path;
}
Update all index files and translate
npm run generate
Check logic of work with @kaufman-bot/schematics
Create empty nx project
npx -y create-nx-workspace@13.8.1 --name=kaufman-bot-generated --preset=empty --interactive=false --nx-cloud=false
endy@endy-virtual-machine:~/Projects/current$ npx -y create-nx-workspace@13.8.1 --name=kaufman-bot-generated --preset=empty --interactive=false --nx-cloud=false
> NX Nx is creating your v13.8.1 workspace.
To make sure the command works reliably in all environments, and that the preset is applied correctly,
Nx will run "npm install" several times. Please wait.
✔ Installing dependencies with npm
✔ Nx has successfully created the workspace.
———————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————————
> NX First time using Nx? Check out this interactive Nx tutorial.
https://nx.dev/getting-started/nx-core
Go to created project
cd kaufman-bot-generated
Add all need schematics
npm install -D @nrwl/nest@13.8.1 @kaufman-bot/schematics@2.4.0
endy@endy-virtual-machine:~/Projects/current/kaufman-bot-generated$ npm install -D @nrwl/nest@13.8.1 @kaufman-bot/schematics@2.4.0
added 162 packages, and audited 567 packages in 12s
54 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Create kaufman-bot application
npx -y nx@13.8.1 g @kaufman-bot/schematics:app adam-bot --bot-name adam
endy@endy-virtual-machine:~/Projects/current/kaufman-bot-generated$ npx -y nx@13.8.1 g @kaufman-bot/schematics:app adam-bot --bot-name adam
UPDATE package.json
UPDATE nx.json
UPDATE tsconfig.base.json
UPDATE .gitignore
CREATE .env.local
CREATE .env-example.local
CREATE jest.config.js
CREATE jest.preset.js
UPDATE .vscode/extensions.json
CREATE apps/adam-bot/src/app/.gitkeep
CREATE apps/adam-bot/src/assets/.gitkeep
CREATE apps/adam-bot/src/environments/environment.prod.ts
CREATE apps/adam-bot/src/environments/environment.ts
CREATE apps/adam-bot/src/main.ts
CREATE apps/adam-bot/tsconfig.app.json
CREATE apps/adam-bot/tsconfig.json
CREATE apps/adam-bot/project.json
UPDATE workspace.json
CREATE .eslintrc.json
CREATE apps/adam-bot/.eslintrc.json
CREATE apps/adam-bot/jest.config.js
CREATE apps/adam-bot/tsconfig.spec.json
CREATE apps/adam-bot/src/app/app.module.ts
CREATE apps/adam-bot/src/app/app.service.ts
added 343 packages, removed 1 package, changed 1 package, and audited 909 packages in 37s
102 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
up to date, audited 909 packages in 2s
102 packages are looking for funding
run `npm fund` for details
found 0 vulnerabilities
Create telegram bot in @botfather
Append token to env file
.env.local
TELEGRAM_BOT_TOKEN=5384981645:AAEKAfqNpZmoN1w5eQL2QxJtvY5h3O-71Zs
TELEGRAM_BOT_WEB_HOOKS_DOMAIN=
TELEGRAM_BOT_WEB_HOOKS_PATH=
TELEGRAM_BOT_ADMINS=
BOT_NAMES=adam
Check from telegram
npm run serve:adam-bot-local
endy@endy-virtual-machine:~/Projects/current/kaufman-bot-generated$ npm run serve:adam-bot-local
> kaufman-bot-generated@0.0.0 serve:adam-bot-local
> export $(xargs < ./.env.local) > /dev/null 2>&1 && npm run nx -- serve adam-bot
> kaufman-bot-generated@0.0.0 nx
> nx "serve" "adam-bot"
> nx run adam-bot:serve
chunk (runtime: main) main.js (main) 10.1 KiB [entry] [rendered]
webpack compiled successfully (3e915c7195348378)
Debugger listening on ws://localhost:9229/045c9820-61d9-42b1-a3b5-57dc00299eea
For help, see: https://nodejs.org/en/docs/inspector
Issues checking in progress...
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [NestFactory] Starting Nest application...
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] TelegrafModule dependencies initialized +49ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] DebugMessagesModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] DebugMessagesModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] TranslatesModule dependencies initialized +1ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] CustomInjectorModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] LanguageSwitherModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] LanguageSwitherModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] FactsGeneratorModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] DiscoveryModule dependencies initialized +1ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] CustomInjectorCoreModule dependencies initialized +1ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] TranslatesModuleCore dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] TranslatesModule dependencies initialized +1ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] BotCommandsModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] BotCommandsModule dependencies initialized +1ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] ShortCommandsModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] ScraperModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] ScraperModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] CustomInjectorModule dependencies initialized +1ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] CustomInjectorModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] ShortCommandsModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] BotInGroupsModule dependencies initialized +1ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] CustomInjectorModule dependencies initialized +0ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] CustomInjectorModule dependencies initialized +1ms
[Nest] 1363135 - 04/22/2022, 1:32:02 PM LOG [InstanceLoader] AppModule dependencies initialized +0ms
No issues found.
[Nest] 1363135 - 04/22/2022, 1:32:05 PM LOG [InstanceLoader] TelegrafCoreModule dependencies initialized +2985ms
[Nest] 1363135 - 04/22/2022, 1:32:05 PM LOG [TranslatesBootstrapService] onModuleInit
[Nest] 1363135 - 04/22/2022, 1:32:05 PM LOG [TranslatesStorage] Add 1 translates for locale: en
[Nest] 1363135 - 04/22/2022, 1:32:05 PM LOG [NestApplication] Nest application successfully started +2ms
[Nest] 1363135 - 04/22/2022, 1:32:05 PM LOG [Application] 🚀 Application is running on: http://localhost:3333
Create new command
npm run nx -- g @kaufman-bot/schematics:lib super
endy@endy-virtual-machine:~/Projects/current/kaufman-bot-generated$ npm run nx -- g @kaufman-bot/schematics:lib super
> kaufman-bot-generated@0.0.0 nx
> nx "g" "@kaufman-bot/schematics:lib" "super"
CREATE libs/super/README.md
CREATE libs/super/src/index.ts
CREATE libs/super/tsconfig.json
CREATE libs/super/tsconfig.lib.json
CREATE libs/super/project.json
UPDATE workspace.json
UPDATE tsconfig.base.json
CREATE libs/super/.eslintrc.json
CREATE libs/super/jest.config.js
CREATE libs/super/tsconfig.spec.json
CREATE libs/super/src/lib/super.module.ts
CREATE libs/super/src/lib/super.service.ts
Update app module
apps/adam-bot/src/app/app.module.ts
import { SuperModule } from '@kaufman-bot-generated/super';
...
@Module({
imports: [
...
SuperModule.forRoot(),
],
providers: [AppService],
})
export class AppModule {}
Restart application and check work in telegram
Generated commands service
libs/super/src/lib/super.service.ts
import {
BotCommandsEnum,
BotCommandsProvider,
BotCommandsProviderActionMsg,
BotCommandsProviderActionResultType,
BotCommandsToolsService,
} from '@kaufman-bot/core-server';
import { Inject, Injectable, Logger } from '@nestjs/common';
import { getText } from 'class-validator-multi-lang';
import { TranslatesService } from 'nestjs-translates';
export const SUPER_CONFIG = 'SUPER_CONFIG';
export interface SuperConfig {
title: string;
name: string;
descriptions: string;
usage: string[];
spyWords: string[];
category: string;
}
@Injectable()
export class SuperService implements BotCommandsProvider {
private readonly logger = new Logger(SuperService.name);
constructor(
@Inject(SUPER_CONFIG)
private readonly superConfig: SuperConfig,
private readonly translatesService: TranslatesService,
private readonly commandToolsService: BotCommandsToolsService,
private readonly botCommandsToolsService: BotCommandsToolsService
) {}
async onHelp<
TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
>(msg: TMsg): Promise<BotCommandsProviderActionResultType<TMsg>> {
return await this.onMessage({
...msg,
text: `${this.superConfig.name} ${BotCommandsEnum.help}`,
});
}
async onMessage<
TMsg extends BotCommandsProviderActionMsg = BotCommandsProviderActionMsg
>(msg: TMsg): Promise<BotCommandsProviderActionResultType<TMsg>> {
const locale = this.botCommandsToolsService.getLocale(msg, 'en');
const spyWord = this.superConfig.spyWords.find((spyWord) =>
this.commandToolsService.checkCommands(msg.text, [spyWord], locale)
);
if (spyWord) {
if (
this.commandToolsService.checkCommands(
msg.text,
[BotCommandsEnum.help],
locale
)
) {
return {
type: 'markdown',
message: msg,
markdown: this.commandToolsService.generateHelpMessage(msg, {
locale,
name: this.superConfig.title,
descriptions: this.superConfig.descriptions,
usage: this.superConfig.usage,
category: this.superConfig.category,
}),
};
}
const processedMsg = await this.process(msg, locale);
if (typeof processedMsg === 'string') {
return {
type: 'text',
message: msg,
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) {
if (
this.commandToolsService.checkCommands(
msg.text,
[getText('ping')],
locale
)
) {
return this.translatesService.translate(getText('pong'), locale);
}
return null;
}
}
Generated commands module
libs/super/src/lib/super.module.ts
import {
BotCommandsCategory,
BotCommandsModule,
BOT_COMMANDS_PROVIDER,
} from '@kaufman-bot/core-server';
import { DynamicModule, Module } from '@nestjs/common';
import { getText } from 'class-validator-multi-lang';
import { TranslatesModule } from 'nestjs-translates';
import { SuperService, SuperConfig, SUPER_CONFIG } from './super.service';
@Module({
imports: [TranslatesModule, BotCommandsModule],
exports: [TranslatesModule, BotCommandsModule],
})
export class SuperModule {
static forRoot(): DynamicModule {
return {
module: SuperModule,
providers: [
{
provide: SUPER_CONFIG,
useValue: <SuperConfig>{
title: getText('Super commands'),
name: 'super',
usage: [getText('super ping'), getText('super help')],
descriptions: getText('Commands for super'),
spyWords: [getText('super')],
category: BotCommandsCategory.user,
},
},
{
provide: BOT_COMMANDS_PROVIDER,
useClass: SuperService,
},
],
exports: [SUPER_CONFIG],
};
}
}
Generated files your may look in https://github.com/kaufman-bot/schematics-example
In next post I append menu for quick run commands for bot...
Top comments (0)