mirror of
https://github.com/Maks1mS/nestjs-telegraf.git
synced 2024-12-24 15:04:38 +03:00
parent
2d23eba148
commit
43816099a6
26
.eslintrc.js
Normal file
26
.eslintrc.js
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
project: 'tsconfig.json',
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||||
|
extends: [
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:prettier/recommended',
|
||||||
|
],
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
ignorePatterns: ['.eslintrc.js'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/interface-name-prefix': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
'@typescript-eslint/no-use-before-define': 'off',
|
||||||
|
'@typescript-eslint/no-non-null-assertion': 'off',
|
||||||
|
'@typescript-eslint/ban-types': 'off',
|
||||||
|
},
|
||||||
|
};
|
@ -1,3 +1,5 @@
|
|||||||
export * from './update.decorator';
|
export * from './update.decorator';
|
||||||
|
export * from './scene.decorator';
|
||||||
|
export * from './wizard.decorator';
|
||||||
export * from './inject-bot.decorator';
|
export * from './inject-bot.decorator';
|
||||||
export * from './inject-all-bots.decorator';
|
export * from './inject-all-bots.decorator';
|
@ -1,9 +1,9 @@
|
|||||||
import { Inject } from '@nestjs/common';
|
import { Inject } from '@nestjs/common';
|
||||||
import { Bot } from 'grammy';
|
import { Telegraf } from 'telegraf';
|
||||||
|
|
||||||
import { getAllBotsToken } from '../../utils/get-all-bots-token.util';
|
import { getAllBotsToken } from '../../utils/get-all-bots-token.util';
|
||||||
|
|
||||||
export type AllBotsMap = Map<string, Bot<any>>;
|
export type AllBotsMap = Map<string, Telegraf<any>>;
|
||||||
|
|
||||||
export const InjectAllBots = (): ParameterDecorator =>
|
export const InjectAllBots = (): ParameterDecorator =>
|
||||||
Inject(getAllBotsToken());
|
Inject(getAllBotsToken());
|
14
lib/decorators/core/scene.decorator.ts
Normal file
14
lib/decorators/core/scene.decorator.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { SceneOptions } from 'telegraf/typings/scenes/base';
|
||||||
|
import { SceneMetadata } from '../../interfaces';
|
||||||
|
import { SCENE_METADATA } from '../../telegraf.constants';
|
||||||
|
|
||||||
|
export const Scene = (
|
||||||
|
sceneId: string,
|
||||||
|
options?: SceneOptions<any>,
|
||||||
|
): ClassDecorator =>
|
||||||
|
SetMetadata<string, SceneMetadata>(SCENE_METADATA, {
|
||||||
|
sceneId,
|
||||||
|
type: 'base',
|
||||||
|
options,
|
||||||
|
});
|
14
lib/decorators/core/wizard.decorator.ts
Normal file
14
lib/decorators/core/wizard.decorator.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { SceneOptions } from 'telegraf/typings/scenes/base';
|
||||||
|
import { SceneMetadata } from '../../interfaces';
|
||||||
|
import { SCENE_METADATA } from '../../telegraf.constants';
|
||||||
|
|
||||||
|
export const Wizard = (
|
||||||
|
sceneId: string,
|
||||||
|
options?: SceneOptions<any>,
|
||||||
|
): ClassDecorator =>
|
||||||
|
SetMetadata<string, SceneMetadata>(SCENE_METADATA, {
|
||||||
|
sceneId,
|
||||||
|
type: 'wizard',
|
||||||
|
options,
|
||||||
|
});
|
@ -1,3 +1,4 @@
|
|||||||
export * from './core';
|
export * from './core';
|
||||||
export * from './listeners';
|
export * from './listeners';
|
||||||
|
export * from './scene';
|
||||||
export * from './params';
|
export * from './params';
|
8
lib/decorators/listeners/action.decorator.ts
Normal file
8
lib/decorators/listeners/action.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for handling callback_data actions with regular expressions.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=action
|
||||||
|
*/
|
||||||
|
export const Action = createListenerDecorator('action');
|
8
lib/decorators/listeners/cashtag.decorator.ts
Normal file
8
lib/decorators/listeners/cashtag.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cashtag handling.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=cashtag
|
||||||
|
*/
|
||||||
|
export const Cashtag = createListenerDecorator('cashtag');
|
8
lib/decorators/listeners/email.decorator.ts
Normal file
8
lib/decorators/listeners/email.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for handling messages with email entity.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=telegraf-email
|
||||||
|
*/
|
||||||
|
export const Email = createListenerDecorator('email');
|
8
lib/decorators/listeners/hashtag.decorator.ts
Normal file
8
lib/decorators/listeners/hashtag.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hashtag handling.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=hashtag
|
||||||
|
*/
|
||||||
|
export const Hashtag = createListenerDecorator('hashtag');
|
8
lib/decorators/listeners/help.decorator.ts
Normal file
8
lib/decorators/listeners/help.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for /help command.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=help
|
||||||
|
*/
|
||||||
|
export const Help = createListenerDecorator('help');
|
18
lib/decorators/listeners/index.ts
Normal file
18
lib/decorators/listeners/index.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
export * from './on.decorator';
|
||||||
|
export * from './use.decorator';
|
||||||
|
export * from './action.decorator';
|
||||||
|
export * from './cashtag.decorator';
|
||||||
|
export * from './command.decorator';
|
||||||
|
export * from './game-query.decorator';
|
||||||
|
export * from './hashtag.decorator';
|
||||||
|
export * from './hears.decorator';
|
||||||
|
export * from './help.decorator';
|
||||||
|
export * from './inline-query.decorator';
|
||||||
|
export * from './mention.decorator';
|
||||||
|
export * from './phone.decorator';
|
||||||
|
export * from './settings.decorator';
|
||||||
|
export * from './start.decorator';
|
||||||
|
export * from './email.decorator';
|
||||||
|
export * from './url.decorator';
|
||||||
|
export * from './text-link.decorator';
|
||||||
|
export * from './text-mention.decorator';
|
8
lib/decorators/listeners/mention.decorator.ts
Normal file
8
lib/decorators/listeners/mention.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mention handling.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=mention
|
||||||
|
*/
|
||||||
|
export const Mention = createListenerDecorator('mention');
|
8
lib/decorators/listeners/phone.decorator.ts
Normal file
8
lib/decorators/listeners/phone.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phone number handling.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=phone
|
||||||
|
*/
|
||||||
|
export const Phone = createListenerDecorator('phone');
|
8
lib/decorators/listeners/settings.decorator.ts
Normal file
8
lib/decorators/listeners/settings.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for /settings command.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=settings
|
||||||
|
*/
|
||||||
|
export const Settings = createListenerDecorator('settings');
|
8
lib/decorators/listeners/start.decorator.ts
Normal file
8
lib/decorators/listeners/start.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for /start command.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=start
|
||||||
|
*/
|
||||||
|
export const Start = createListenerDecorator('start');
|
8
lib/decorators/listeners/text-link.decorator.ts
Normal file
8
lib/decorators/listeners/text-link.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for handling messages with text_link entity.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=telegraf-textlink
|
||||||
|
*/
|
||||||
|
export const TextLink = createListenerDecorator('textLink');
|
8
lib/decorators/listeners/text-mention.decorator.ts
Normal file
8
lib/decorators/listeners/text-mention.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for handling messages with text_mention entity.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=telegraf-textlink
|
||||||
|
*/
|
||||||
|
export const TextMention = createListenerDecorator('textMention');
|
8
lib/decorators/listeners/url.decorator.ts
Normal file
8
lib/decorators/listeners/url.decorator.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for handling messages with url entity.
|
||||||
|
*
|
||||||
|
* @see https://telegraf.js.org/#/?id=telegraf-url
|
||||||
|
*/
|
||||||
|
export const Url = createListenerDecorator('url');
|
3
lib/decorators/scene/index.ts
Normal file
3
lib/decorators/scene/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './scene-enter.decorator';
|
||||||
|
export * from './scene-leave.decorator';
|
||||||
|
export * from './wizard-step.decorator';
|
5
lib/decorators/scene/scene-enter.decorator.ts
Normal file
5
lib/decorators/scene/scene-enter.decorator.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
import { Scenes } from 'telegraf';
|
||||||
|
|
||||||
|
export const SceneEnter =
|
||||||
|
createListenerDecorator<Scenes.BaseScene<never>>('enter');
|
5
lib/decorators/scene/scene-leave.decorator.ts
Normal file
5
lib/decorators/scene/scene-leave.decorator.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
import { createListenerDecorator } from '../../utils';
|
||||||
|
import { Scenes } from 'telegraf';
|
||||||
|
|
||||||
|
export const SceneLeave =
|
||||||
|
createListenerDecorator<Scenes.BaseScene<never>>('leave');
|
6
lib/decorators/scene/wizard-step.decorator.ts
Normal file
6
lib/decorators/scene/wizard-step.decorator.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { WizardStepMetadata } from '../../interfaces';
|
||||||
|
import { WIZARD_STEP_METADATA } from '../../telegraf.constants';
|
||||||
|
|
||||||
|
export const WizardStep = (step: number) =>
|
||||||
|
SetMetadata<string, WizardStepMetadata>(WIZARD_STEP_METADATA, { step });
|
@ -1,6 +1,6 @@
|
|||||||
import { ParamData } from '@nestjs/common';
|
import { ParamData } from '@nestjs/common';
|
||||||
import { ParamsFactory } from '@nestjs/core/helpers/external-context-creator';
|
import { ParamsFactory } from '@nestjs/core/helpers/external-context-creator';
|
||||||
import { Context } from 'grammy';
|
import { Context } from 'telegraf';
|
||||||
import { TelegrafParamtype } from '../enums/telegraf-paramtype.enum';
|
import { TelegrafParamtype } from '../enums/telegraf-paramtype.enum';
|
||||||
|
|
||||||
export class TelegrafParamsFactory implements ParamsFactory {
|
export class TelegrafParamsFactory implements ParamsFactory {
|
@ -1,3 +1,4 @@
|
|||||||
export * from './telegraf-options.interface';
|
export * from './telegraf-options.interface';
|
||||||
export * from './listener-metadata.interface';
|
export * from './listener-metadata.interface';
|
||||||
|
export * from './scene-metadata.interface';
|
||||||
export * from './telegraf-exception-filter.interface';
|
export * from './telegraf-exception-filter.interface';
|
11
lib/interfaces/scene-metadata.interface.ts
Normal file
11
lib/interfaces/scene-metadata.interface.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SceneOptions } from 'telegraf/typings/scenes/base';
|
||||||
|
|
||||||
|
export interface SceneMetadata {
|
||||||
|
sceneId: string;
|
||||||
|
type: 'base' | 'wizard';
|
||||||
|
options?: SceneOptions<any>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WizardStepMetadata {
|
||||||
|
step: number;
|
||||||
|
}
|
@ -1,11 +1,11 @@
|
|||||||
import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
|
import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
|
||||||
import { Middleware, PollingOptions, BotConfig } from 'grammy';
|
import { Middleware, Telegraf } from 'telegraf';
|
||||||
|
|
||||||
export interface TelegrafModuleOptions {
|
export interface TelegrafModuleOptions {
|
||||||
token: string;
|
token: string;
|
||||||
config?: BotConfig<any>;
|
|
||||||
pollingOptions?: PollingOptions | false;
|
|
||||||
botName?: string;
|
botName?: string;
|
||||||
|
options?: Partial<Telegraf.Options<any>>;
|
||||||
|
launchOptions?: Telegraf.LaunchOptions | false;
|
||||||
include?: Function[];
|
include?: Function[];
|
||||||
middlewares?: ReadonlyArray<Middleware<any>>;
|
middlewares?: ReadonlyArray<Middleware<any>>;
|
||||||
}
|
}
|
@ -5,7 +5,7 @@ import { MetadataScanner } from '@nestjs/core/metadata-scanner';
|
|||||||
import { Module } from '@nestjs/core/injector/module';
|
import { Module } from '@nestjs/core/injector/module';
|
||||||
import { ParamMetadata } from '@nestjs/core/helpers/interfaces';
|
import { ParamMetadata } from '@nestjs/core/helpers/interfaces';
|
||||||
import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator';
|
import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator';
|
||||||
import { Composer, Context, Bot } from 'grammy';
|
import { Composer, Context, Scenes, Telegraf } from 'telegraf';
|
||||||
|
|
||||||
import { MetadataAccessorService } from './metadata-accessor.service';
|
import { MetadataAccessorService } from './metadata-accessor.service';
|
||||||
import {
|
import {
|
||||||
@ -25,9 +25,11 @@ export class ListenersExplorerService
|
|||||||
implements OnModuleInit
|
implements OnModuleInit
|
||||||
{
|
{
|
||||||
private readonly telegrafParamsFactory = new TelegrafParamsFactory();
|
private readonly telegrafParamsFactory = new TelegrafParamsFactory();
|
||||||
private bot: Bot;
|
private bot: Telegraf<any>;
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@Inject(TELEGRAF_STAGE)
|
||||||
|
private readonly stage: Scenes.Stage<any>,
|
||||||
@Inject(TELEGRAF_MODULE_OPTIONS)
|
@Inject(TELEGRAF_MODULE_OPTIONS)
|
||||||
private readonly telegrafOptions: TelegrafModuleOptions,
|
private readonly telegrafOptions: TelegrafModuleOptions,
|
||||||
@Inject(TELEGRAF_BOT_NAME)
|
@Inject(TELEGRAF_BOT_NAME)
|
||||||
@ -43,9 +45,10 @@ export class ListenersExplorerService
|
|||||||
}
|
}
|
||||||
|
|
||||||
onModuleInit(): void {
|
onModuleInit(): void {
|
||||||
this.bot = this.moduleRef.get<Bot>(this.botName, {
|
this.bot = this.moduleRef.get<Telegraf<any>>(this.botName, {
|
||||||
strict: false,
|
strict: false,
|
||||||
});
|
});
|
||||||
|
this.bot.use(this.stage.middleware());
|
||||||
|
|
||||||
this.explore();
|
this.explore();
|
||||||
}
|
}
|
||||||
@ -57,6 +60,7 @@ export class ListenersExplorerService
|
|||||||
);
|
);
|
||||||
|
|
||||||
this.registerUpdates(modules);
|
this.registerUpdates(modules);
|
||||||
|
this.registerScenes(modules);
|
||||||
}
|
}
|
||||||
|
|
||||||
private registerUpdates(modules: Module[]): void {
|
private registerUpdates(modules: Module[]): void {
|
||||||
@ -66,6 +70,28 @@ export class ListenersExplorerService
|
|||||||
updates.forEach((wrapper) => this.registerListeners(this.bot, wrapper));
|
updates.forEach((wrapper) => this.registerListeners(this.bot, wrapper));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private registerScenes(modules: Module[]): void {
|
||||||
|
const scenes = this.flatMap<InstanceWrapper>(modules, (wrapper) =>
|
||||||
|
this.filterScenes(wrapper),
|
||||||
|
);
|
||||||
|
scenes.forEach((wrapper) => {
|
||||||
|
const { sceneId, type, options } = this.metadataAccessor.getSceneMetadata(
|
||||||
|
wrapper.instance.constructor,
|
||||||
|
);
|
||||||
|
const scene =
|
||||||
|
type === 'base'
|
||||||
|
? new Scenes.BaseScene<any>(sceneId, options || ({} as any))
|
||||||
|
: new Scenes.WizardScene<any>(sceneId, options || ({} as any));
|
||||||
|
this.stage.register(scene);
|
||||||
|
|
||||||
|
if (type === 'base') {
|
||||||
|
this.registerListeners(scene, wrapper);
|
||||||
|
} else {
|
||||||
|
this.registerWizardListeners(scene as Scenes.WizardScene<any>, wrapper);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private filterUpdates(wrapper: InstanceWrapper): InstanceWrapper<unknown> {
|
private filterUpdates(wrapper: InstanceWrapper): InstanceWrapper<unknown> {
|
||||||
const { instance } = wrapper;
|
const { instance } = wrapper;
|
||||||
if (!instance) return undefined;
|
if (!instance) return undefined;
|
||||||
@ -76,6 +102,16 @@ export class ListenersExplorerService
|
|||||||
return wrapper;
|
return wrapper;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private filterScenes(wrapper: InstanceWrapper): InstanceWrapper<unknown> {
|
||||||
|
const { instance } = wrapper;
|
||||||
|
if (!instance) return undefined;
|
||||||
|
|
||||||
|
const isScene = this.metadataAccessor.isScene(wrapper.metatype);
|
||||||
|
if (!isScene) return undefined;
|
||||||
|
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
private registerListeners(
|
private registerListeners(
|
||||||
composer: Composer<any>,
|
composer: Composer<any>,
|
||||||
wrapper: InstanceWrapper<unknown>,
|
wrapper: InstanceWrapper<unknown>,
|
||||||
@ -87,6 +123,60 @@ export class ListenersExplorerService
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private registerWizardListeners(
|
||||||
|
wizard: Scenes.WizardScene<any>,
|
||||||
|
wrapper: InstanceWrapper<unknown>,
|
||||||
|
): void {
|
||||||
|
const { instance } = wrapper;
|
||||||
|
const prototype = Object.getPrototypeOf(instance);
|
||||||
|
|
||||||
|
type WizardMetadata = { step: number; methodName: string };
|
||||||
|
const wizardSteps: WizardMetadata[] = [];
|
||||||
|
const basicListeners = [];
|
||||||
|
|
||||||
|
this.metadataScanner.scanFromPrototype(
|
||||||
|
instance,
|
||||||
|
prototype,
|
||||||
|
(methodName) => {
|
||||||
|
const methodRef = prototype[methodName];
|
||||||
|
const metadata = this.metadataAccessor.getWizardStepMetadata(methodRef);
|
||||||
|
if (!metadata) {
|
||||||
|
basicListeners.push(methodName);
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
wizardSteps.push({ step: metadata.step, methodName });
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
for (const methodName of basicListeners) {
|
||||||
|
this.registerIfListener(wizard, instance, prototype, methodName);
|
||||||
|
}
|
||||||
|
|
||||||
|
const group = wizardSteps
|
||||||
|
.sort((a, b) => a.step - b.step)
|
||||||
|
.reduce<{ [key: number]: WizardMetadata[] }>(
|
||||||
|
(prev, cur) => ({
|
||||||
|
...prev,
|
||||||
|
[cur.step]: [...(prev[cur.step] || []), cur],
|
||||||
|
}),
|
||||||
|
{},
|
||||||
|
);
|
||||||
|
|
||||||
|
wizard.steps = Object.values(group).map((stepsMetadata) => {
|
||||||
|
const composer = new Composer();
|
||||||
|
stepsMetadata.forEach((stepMethod) => {
|
||||||
|
this.registerIfListener(
|
||||||
|
composer,
|
||||||
|
instance,
|
||||||
|
prototype,
|
||||||
|
stepMethod.methodName,
|
||||||
|
[{ method: 'use', args: [] }],
|
||||||
|
);
|
||||||
|
});
|
||||||
|
return composer.middleware();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
private registerIfListener(
|
private registerIfListener(
|
||||||
composer: Composer<any>,
|
composer: Composer<any>,
|
||||||
instance: any,
|
instance: any,
|
40
lib/services/metadata-accessor.service.ts
Normal file
40
lib/services/metadata-accessor.service.ts
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import {
|
||||||
|
SCENE_METADATA,
|
||||||
|
LISTENERS_METADATA,
|
||||||
|
UPDATE_METADATA,
|
||||||
|
WIZARD_STEP_METADATA,
|
||||||
|
} from '../telegraf.constants';
|
||||||
|
import {
|
||||||
|
ListenerMetadata,
|
||||||
|
SceneMetadata,
|
||||||
|
WizardStepMetadata,
|
||||||
|
} from '../interfaces';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MetadataAccessorService {
|
||||||
|
constructor(private readonly reflector: Reflector) {}
|
||||||
|
|
||||||
|
isUpdate(target: Function): boolean {
|
||||||
|
if (!target) return false;
|
||||||
|
return !!this.reflector.get(UPDATE_METADATA, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
isScene(target: Function): boolean {
|
||||||
|
if (!target) return false;
|
||||||
|
return !!this.reflector.get(SCENE_METADATA, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
getListenerMetadata(target: Function): ListenerMetadata[] | undefined {
|
||||||
|
return this.reflector.get(LISTENERS_METADATA, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
getSceneMetadata(target: Function): SceneMetadata | undefined {
|
||||||
|
return this.reflector.get(SCENE_METADATA, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
getWizardStepMetadata(target: Function): WizardStepMetadata | undefined {
|
||||||
|
return this.reflector.get(WIZARD_STEP_METADATA, target);
|
||||||
|
}
|
||||||
|
}
|
8
lib/stage.provider.ts
Normal file
8
lib/stage.provider.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Provider } from '@nestjs/common';
|
||||||
|
import { Scenes } from 'telegraf';
|
||||||
|
import { TELEGRAF_STAGE } from './telegraf.constants';
|
||||||
|
|
||||||
|
export const telegrafStageProvider: Provider = {
|
||||||
|
provide: TELEGRAF_STAGE,
|
||||||
|
useClass: Scenes.Stage,
|
||||||
|
};
|
@ -1,8 +1,8 @@
|
|||||||
import { Provider } from '@nestjs/common';
|
import { Provider } from '@nestjs/common';
|
||||||
import { Bot } from 'grammy';
|
import { Telegraf } from 'telegraf';
|
||||||
import { TELEGRAF_ALL_BOTS } from './telegraf.constants';
|
import { TELEGRAF_ALL_BOTS } from './telegraf.constants';
|
||||||
|
|
||||||
export const allBotsMap = new Map<string, Bot<any>>();
|
export const allBotsMap = new Map<string, Telegraf<any>>();
|
||||||
|
|
||||||
export const telegrafAllBotsProvider: Provider = {
|
export const telegrafAllBotsProvider: Provider = {
|
||||||
provide: TELEGRAF_ALL_BOTS,
|
provide: TELEGRAF_ALL_BOTS,
|
@ -18,6 +18,7 @@ import {
|
|||||||
TELEGRAF_MODULE_OPTIONS,
|
TELEGRAF_MODULE_OPTIONS,
|
||||||
} from './telegraf.constants';
|
} from './telegraf.constants';
|
||||||
import { ListenersExplorerService, MetadataAccessorService } from './services';
|
import { ListenersExplorerService, MetadataAccessorService } from './services';
|
||||||
|
import { telegrafStageProvider } from './stage.provider';
|
||||||
import {
|
import {
|
||||||
allBotsMap,
|
allBotsMap,
|
||||||
telegrafAllBotsProvider,
|
telegrafAllBotsProvider,
|
||||||
@ -60,11 +61,13 @@ export class TelegrafCoreModule implements OnApplicationShutdown {
|
|||||||
provide: TELEGRAF_MODULE_OPTIONS,
|
provide: TELEGRAF_MODULE_OPTIONS,
|
||||||
useValue: options,
|
useValue: options,
|
||||||
},
|
},
|
||||||
|
telegrafStageProvider,
|
||||||
telegrafBotNameProvider,
|
telegrafBotNameProvider,
|
||||||
telegrafBotProvider,
|
telegrafBotProvider,
|
||||||
telegrafAllBotsProvider,
|
telegrafAllBotsProvider,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
|
telegrafStageProvider,
|
||||||
telegrafBotNameProvider,
|
telegrafBotNameProvider,
|
||||||
telegrafBotProvider,
|
telegrafBotProvider,
|
||||||
telegrafAllBotsProvider,
|
telegrafAllBotsProvider,
|
||||||
@ -98,11 +101,13 @@ export class TelegrafCoreModule implements OnApplicationShutdown {
|
|||||||
imports: options.imports,
|
imports: options.imports,
|
||||||
providers: [
|
providers: [
|
||||||
...asyncProviders,
|
...asyncProviders,
|
||||||
|
telegrafStageProvider,
|
||||||
telegrafBotNameProvider,
|
telegrafBotNameProvider,
|
||||||
telegrafBotProvider,
|
telegrafBotProvider,
|
||||||
telegrafAllBotsProvider,
|
telegrafAllBotsProvider,
|
||||||
],
|
],
|
||||||
exports: [
|
exports: [
|
||||||
|
telegrafStageProvider,
|
||||||
telegrafBotNameProvider,
|
telegrafBotNameProvider,
|
||||||
telegrafBotProvider,
|
telegrafBotProvider,
|
||||||
telegrafAllBotsProvider,
|
telegrafAllBotsProvider,
|
@ -6,10 +6,10 @@ import {
|
|||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
|
|
||||||
@Module({})
|
@Module({})
|
||||||
export class GrammyModule {
|
export class TelegrafModule {
|
||||||
public static forRoot(options: TelegrafModuleOptions): DynamicModule {
|
public static forRoot(options: TelegrafModuleOptions): DynamicModule {
|
||||||
return {
|
return {
|
||||||
module: GrammyModule,
|
module: TelegrafModule,
|
||||||
imports: [TelegrafCoreModule.forRoot(options)],
|
imports: [TelegrafCoreModule.forRoot(options)],
|
||||||
exports: [TelegrafCoreModule],
|
exports: [TelegrafCoreModule],
|
||||||
};
|
};
|
||||||
@ -19,7 +19,7 @@ export class GrammyModule {
|
|||||||
options: TelegrafModuleAsyncOptions,
|
options: TelegrafModuleAsyncOptions,
|
||||||
): DynamicModule {
|
): DynamicModule {
|
||||||
return {
|
return {
|
||||||
module: GrammyModule,
|
module: TelegrafModule,
|
||||||
imports: [TelegrafCoreModule.forRootAsync(options)],
|
imports: [TelegrafCoreModule.forRootAsync(options)],
|
||||||
exports: [TelegrafCoreModule],
|
exports: [TelegrafCoreModule],
|
||||||
};
|
};
|
@ -1,4 +1,4 @@
|
|||||||
import { Composer, Middleware } from 'grammy';
|
import { Composer, Middleware } from 'telegraf';
|
||||||
|
|
||||||
export type Filter<T extends any[], F> = T extends []
|
export type Filter<T extends any[], F> = T extends []
|
||||||
? []
|
? []
|
16
lib/utils/create-bot-factory.util.ts
Normal file
16
lib/utils/create-bot-factory.util.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { Telegraf } from 'telegraf';
|
||||||
|
import { TelegrafModuleOptions } from '../interfaces';
|
||||||
|
|
||||||
|
export async function createBotFactory(
|
||||||
|
options: TelegrafModuleOptions,
|
||||||
|
): Promise<Telegraf<any>> {
|
||||||
|
const bot = new Telegraf<any>(options.token, options.options);
|
||||||
|
|
||||||
|
bot.use(...(options.middlewares ?? []));
|
||||||
|
|
||||||
|
if (options.launchOptions !== false) {
|
||||||
|
await bot.launch(options.launchOptions);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bot;
|
||||||
|
}
|
@ -1,4 +1,4 @@
|
|||||||
import { Composer } from 'grammy';
|
import { Composer } from 'telegraf';
|
||||||
import { ComposerMethodArgs, OnlyFunctionPropertyNames } from '../types';
|
import { ComposerMethodArgs, OnlyFunctionPropertyNames } from '../types';
|
||||||
import { LISTENERS_METADATA } from '../telegraf.constants';
|
import { LISTENERS_METADATA } from '../telegraf.constants';
|
||||||
import { ListenerMetadata } from '../interfaces';
|
import { ListenerMetadata } from '../interfaces';
|
2
sample/01-complete-app/.env.example
Normal file
2
sample/01-complete-app/.env.example
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
ECHO_BOT_TOKEN=
|
||||||
|
GREETER_BOT_TOKEN=
|
24
sample/01-complete-app/.eslintrc.js
Normal file
24
sample/01-complete-app/.eslintrc.js
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
module.exports = {
|
||||||
|
parser: '@typescript-eslint/parser',
|
||||||
|
parserOptions: {
|
||||||
|
project: 'tsconfig.json',
|
||||||
|
sourceType: 'module',
|
||||||
|
},
|
||||||
|
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||||
|
extends: [
|
||||||
|
'plugin:@typescript-eslint/recommended',
|
||||||
|
'plugin:prettier/recommended',
|
||||||
|
],
|
||||||
|
root: true,
|
||||||
|
env: {
|
||||||
|
node: true,
|
||||||
|
jest: true,
|
||||||
|
},
|
||||||
|
ignorePatterns: ['.eslintrc.js'],
|
||||||
|
rules: {
|
||||||
|
'@typescript-eslint/interface-name-prefix': 'off',
|
||||||
|
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||||
|
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||||
|
'@typescript-eslint/no-explicit-any': 'off',
|
||||||
|
},
|
||||||
|
};
|
398
sample/01-complete-app/.gitignore
vendored
Normal file
398
sample/01-complete-app/.gitignore
vendored
Normal file
@ -0,0 +1,398 @@
|
|||||||
|
# Created by .ignore support plugin (hsz.mobi)
|
||||||
|
### JetBrains template
|
||||||
|
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
|
||||||
|
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
|
||||||
|
|
||||||
|
# User-specific stuff:
|
||||||
|
.idea/**/workspace.xml
|
||||||
|
.idea/**/tasks.xml
|
||||||
|
.idea/dictionaries
|
||||||
|
|
||||||
|
# Sensitive or high-churn files:
|
||||||
|
.idea/**/dataSources/
|
||||||
|
.idea/**/dataSources.ids
|
||||||
|
.idea/**/dataSources.xml
|
||||||
|
.idea/**/dataSources.local.xml
|
||||||
|
.idea/**/sqlDataSources.xml
|
||||||
|
.idea/**/dynamic.xml
|
||||||
|
.idea/**/uiDesigner.xml
|
||||||
|
|
||||||
|
# Gradle:
|
||||||
|
.idea/**/gradle.xml
|
||||||
|
.idea/**/libraries
|
||||||
|
|
||||||
|
# CMake
|
||||||
|
cmake-build-debug/
|
||||||
|
|
||||||
|
# Mongo Explorer plugin:
|
||||||
|
.idea/**/mongoSettings.xml
|
||||||
|
|
||||||
|
## File-based project format:
|
||||||
|
*.iws
|
||||||
|
|
||||||
|
## Plugin-specific files:
|
||||||
|
|
||||||
|
# IntelliJ
|
||||||
|
out/
|
||||||
|
|
||||||
|
# mpeltonen/sbt-idea plugin
|
||||||
|
.idea_modules/
|
||||||
|
|
||||||
|
# JIRA plugin
|
||||||
|
atlassian-ide-plugin.xml
|
||||||
|
|
||||||
|
# Cursive Clojure plugin
|
||||||
|
.idea/replstate.xml
|
||||||
|
|
||||||
|
# Crashlytics plugin (for Android Studio and IntelliJ)
|
||||||
|
com_crashlytics_export_strings.xml
|
||||||
|
crashlytics.properties
|
||||||
|
crashlytics-build.properties
|
||||||
|
fabric.properties
|
||||||
|
### VisualStudio template
|
||||||
|
## Ignore Visual Studio temporary files, build results, and
|
||||||
|
## files generated by popular Visual Studio add-ons.
|
||||||
|
##
|
||||||
|
## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore
|
||||||
|
|
||||||
|
# User-specific files
|
||||||
|
*.suo
|
||||||
|
*.user
|
||||||
|
*.userosscache
|
||||||
|
*.sln.docstates
|
||||||
|
|
||||||
|
# User-specific files (MonoDevelop/Xamarin Studio)
|
||||||
|
*.userprefs
|
||||||
|
|
||||||
|
# Build results
|
||||||
|
[Dd]ebug/
|
||||||
|
[Dd]ebugPublic/
|
||||||
|
[Rr]elease/
|
||||||
|
[Rr]eleases/
|
||||||
|
x64/
|
||||||
|
x86/
|
||||||
|
bld/
|
||||||
|
[Bb]in/
|
||||||
|
[Oo]bj/
|
||||||
|
[Ll]og/
|
||||||
|
|
||||||
|
# Visual Studio 2015 cache/options directory
|
||||||
|
.vs/
|
||||||
|
# Uncomment if you have tasks that create the project's static files in wwwroot
|
||||||
|
#wwwroot/
|
||||||
|
|
||||||
|
# MSTest test Results
|
||||||
|
[Tt]est[Rr]esult*/
|
||||||
|
[Bb]uild[Ll]og.*
|
||||||
|
|
||||||
|
# NUNIT
|
||||||
|
*.VisualState.xml
|
||||||
|
TestResult.xml
|
||||||
|
|
||||||
|
# Build Results of an ATL Project
|
||||||
|
[Dd]ebugPS/
|
||||||
|
[Rr]eleasePS/
|
||||||
|
dlldata.c
|
||||||
|
|
||||||
|
# Benchmark Results
|
||||||
|
BenchmarkDotNet.Artifacts/
|
||||||
|
|
||||||
|
# .NET Core
|
||||||
|
project.lock.json
|
||||||
|
project.fragment.lock.json
|
||||||
|
artifacts/
|
||||||
|
**/Properties/launchSettings.json
|
||||||
|
|
||||||
|
*_i.c
|
||||||
|
*_p.c
|
||||||
|
*_i.h
|
||||||
|
*.ilk
|
||||||
|
*.meta
|
||||||
|
*.obj
|
||||||
|
*.pch
|
||||||
|
*.pdb
|
||||||
|
*.pgc
|
||||||
|
*.pgd
|
||||||
|
*.rsp
|
||||||
|
*.sbr
|
||||||
|
*.tlb
|
||||||
|
*.tli
|
||||||
|
*.tlh
|
||||||
|
*.tmp
|
||||||
|
*.tmp_proj
|
||||||
|
*.log
|
||||||
|
*.vspscc
|
||||||
|
*.vssscc
|
||||||
|
.builds
|
||||||
|
*.pidb
|
||||||
|
*.svclog
|
||||||
|
*.scc
|
||||||
|
|
||||||
|
# Chutzpah Test files
|
||||||
|
_Chutzpah*
|
||||||
|
|
||||||
|
# Visual C++ cache files
|
||||||
|
ipch/
|
||||||
|
*.aps
|
||||||
|
*.ncb
|
||||||
|
*.opendb
|
||||||
|
*.opensdf
|
||||||
|
*.sdf
|
||||||
|
*.cachefile
|
||||||
|
*.VC.db
|
||||||
|
*.VC.VC.opendb
|
||||||
|
|
||||||
|
# Visual Studio profiler
|
||||||
|
*.psess
|
||||||
|
*.vsp
|
||||||
|
*.vspx
|
||||||
|
*.sap
|
||||||
|
|
||||||
|
# Visual Studio Trace Files
|
||||||
|
*.e2e
|
||||||
|
|
||||||
|
# TFS 2012 Local Workspace
|
||||||
|
$tf/
|
||||||
|
|
||||||
|
# Guidance Automation Toolkit
|
||||||
|
*.gpState
|
||||||
|
|
||||||
|
# ReSharper is a .NET coding add-in
|
||||||
|
_ReSharper*/
|
||||||
|
*.[Rr]e[Ss]harper
|
||||||
|
*.DotSettings.user
|
||||||
|
|
||||||
|
# JustCode is a .NET coding add-in
|
||||||
|
.JustCode
|
||||||
|
|
||||||
|
# TeamCity is a build add-in
|
||||||
|
_TeamCity*
|
||||||
|
|
||||||
|
# DotCover is a Code Coverage Tool
|
||||||
|
*.dotCover
|
||||||
|
|
||||||
|
# AxoCover is a Code Coverage Tool
|
||||||
|
.axoCover/*
|
||||||
|
!.axoCover/settings.json
|
||||||
|
|
||||||
|
# Visual Studio code coverage results
|
||||||
|
*.coverage
|
||||||
|
*.coveragexml
|
||||||
|
|
||||||
|
# NCrunch
|
||||||
|
_NCrunch_*
|
||||||
|
.*crunch*.local.xml
|
||||||
|
nCrunchTemp_*
|
||||||
|
|
||||||
|
# MightyMoose
|
||||||
|
*.mm.*
|
||||||
|
AutoTest.Net/
|
||||||
|
|
||||||
|
# Web workbench (sass)
|
||||||
|
.sass-cache/
|
||||||
|
|
||||||
|
# Installshield output folder
|
||||||
|
[Ee]xpress/
|
||||||
|
|
||||||
|
# DocProject is a documentation generator add-in
|
||||||
|
DocProject/buildhelp/
|
||||||
|
DocProject/Help/*.HxT
|
||||||
|
DocProject/Help/*.HxC
|
||||||
|
DocProject/Help/*.hhc
|
||||||
|
DocProject/Help/*.hhk
|
||||||
|
DocProject/Help/*.hhp
|
||||||
|
DocProject/Help/Html2
|
||||||
|
DocProject/Help/html
|
||||||
|
|
||||||
|
# Click-Once directory
|
||||||
|
publish/
|
||||||
|
|
||||||
|
# Publish Web Output
|
||||||
|
*.[Pp]ublish.xml
|
||||||
|
*.azurePubxml
|
||||||
|
# Note: Comment the next line if you want to checkin your web deploy settings,
|
||||||
|
# but database connection strings (with potential passwords) will be unencrypted
|
||||||
|
*.pubxml
|
||||||
|
*.publishproj
|
||||||
|
|
||||||
|
# Microsoft Azure Web App publish settings. Comment the next line if you want to
|
||||||
|
# checkin your Azure Web App publish settings, but sensitive information contained
|
||||||
|
# in these scripts will be unencrypted
|
||||||
|
PublishScripts/
|
||||||
|
|
||||||
|
# NuGet Packages
|
||||||
|
*.nupkg
|
||||||
|
# The packages folder can be ignored because of Package Restore
|
||||||
|
**/[Pp]ackages/*
|
||||||
|
# except build/, which is used as an MSBuild target.
|
||||||
|
!**/[Pp]ackages/build/
|
||||||
|
# Uncomment if necessary however generally it will be regenerated when needed
|
||||||
|
#!**/[Pp]ackages/repositories.config
|
||||||
|
# NuGet v3's project.json files produces more ignorable files
|
||||||
|
*.nuget.props
|
||||||
|
*.nuget.targets
|
||||||
|
|
||||||
|
# Microsoft Azure Build Output
|
||||||
|
csx/
|
||||||
|
*.build.csdef
|
||||||
|
|
||||||
|
# Microsoft Azure Emulator
|
||||||
|
ecf/
|
||||||
|
rcf/
|
||||||
|
|
||||||
|
# Windows Store app package directories and files
|
||||||
|
AppPackages/
|
||||||
|
BundleArtifacts/
|
||||||
|
Package.StoreAssociation.xml
|
||||||
|
_pkginfo.txt
|
||||||
|
*.appx
|
||||||
|
|
||||||
|
# Visual Studio cache files
|
||||||
|
# files ending in .cache can be ignored
|
||||||
|
*.[Cc]ache
|
||||||
|
# but keep track of directories ending in .cache
|
||||||
|
!*.[Cc]ache/
|
||||||
|
|
||||||
|
# Others
|
||||||
|
ClientBin/
|
||||||
|
~$*
|
||||||
|
*~
|
||||||
|
*.dbmdl
|
||||||
|
*.dbproj.schemaview
|
||||||
|
*.jfm
|
||||||
|
*.pfx
|
||||||
|
*.publishsettings
|
||||||
|
orleans.codegen.cs
|
||||||
|
|
||||||
|
# Since there are multiple workflows, uncomment next line to ignore bower_components
|
||||||
|
# (https://github.com/github/gitignore/pull/1529#issuecomment-104372622)
|
||||||
|
#bower_components/
|
||||||
|
|
||||||
|
# RIA/Silverlight projects
|
||||||
|
Generated_Code/
|
||||||
|
|
||||||
|
# Backup & report files from converting an old project file
|
||||||
|
# to a newer Visual Studio version. Backup files are not needed,
|
||||||
|
# because we have git ;-)
|
||||||
|
_UpgradeReport_Files/
|
||||||
|
Backup*/
|
||||||
|
UpgradeLog*.XML
|
||||||
|
UpgradeLog*.htm
|
||||||
|
|
||||||
|
# SQL Server files
|
||||||
|
*.mdf
|
||||||
|
*.ldf
|
||||||
|
*.ndf
|
||||||
|
|
||||||
|
# Business Intelligence projects
|
||||||
|
*.rdl.data
|
||||||
|
*.bim.layout
|
||||||
|
*.bim_*.settings
|
||||||
|
|
||||||
|
# Microsoft Fakes
|
||||||
|
FakesAssemblies/
|
||||||
|
|
||||||
|
# GhostDoc plugin setting file
|
||||||
|
*.GhostDoc.xml
|
||||||
|
|
||||||
|
# Node.js Tools for Visual Studio
|
||||||
|
.ntvs_analysis.dat
|
||||||
|
node_modules/
|
||||||
|
|
||||||
|
# Typescript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# Visual Studio 6 build log
|
||||||
|
*.plg
|
||||||
|
|
||||||
|
# Visual Studio 6 workspace options file
|
||||||
|
*.opt
|
||||||
|
|
||||||
|
# Visual Studio 6 auto-generated workspace file (contains which files were open etc.)
|
||||||
|
*.vbw
|
||||||
|
|
||||||
|
# Visual Studio LightSwitch build output
|
||||||
|
**/*.HTMLClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/GeneratedArtifacts
|
||||||
|
**/*.DesktopClient/ModelManifest.xml
|
||||||
|
**/*.Server/GeneratedArtifacts
|
||||||
|
**/*.Server/ModelManifest.xml
|
||||||
|
_Pvt_Extensions
|
||||||
|
|
||||||
|
# Paket dependency manager
|
||||||
|
.paket/paket.exe
|
||||||
|
paket-files/
|
||||||
|
|
||||||
|
# FAKE - F# Make
|
||||||
|
.fake/
|
||||||
|
|
||||||
|
# JetBrains Rider
|
||||||
|
.idea/
|
||||||
|
*.sln.iml
|
||||||
|
|
||||||
|
# IDE - VSCode
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/settings.json
|
||||||
|
!.vscode/tasks.json
|
||||||
|
!.vscode/launch.json
|
||||||
|
!.vscode/extensions.json
|
||||||
|
|
||||||
|
# CodeRush
|
||||||
|
.cr/
|
||||||
|
|
||||||
|
# Python Tools for Visual Studio (PTVS)
|
||||||
|
__pycache__/
|
||||||
|
*.pyc
|
||||||
|
|
||||||
|
# Cake - Uncomment if you are using it
|
||||||
|
# tools/**
|
||||||
|
# !tools/packages.config
|
||||||
|
|
||||||
|
# Tabs Studio
|
||||||
|
*.tss
|
||||||
|
|
||||||
|
# Telerik's JustMock configuration file
|
||||||
|
*.jmconfig
|
||||||
|
|
||||||
|
# BizTalk build output
|
||||||
|
*.btp.cs
|
||||||
|
*.btm.cs
|
||||||
|
*.odx.cs
|
||||||
|
*.xsd.cs
|
||||||
|
|
||||||
|
# OpenCover UI analysis results
|
||||||
|
OpenCover/
|
||||||
|
coverage/
|
||||||
|
|
||||||
|
### macOS template
|
||||||
|
# General
|
||||||
|
.DS_Store
|
||||||
|
.AppleDouble
|
||||||
|
.LSOverride
|
||||||
|
|
||||||
|
# Icon must end with two \r
|
||||||
|
Icon
|
||||||
|
|
||||||
|
# Thumbnails
|
||||||
|
._*
|
||||||
|
|
||||||
|
# Files that might appear in the root of a volume
|
||||||
|
.DocumentRevisions-V100
|
||||||
|
.fseventsd
|
||||||
|
.Spotlight-V100
|
||||||
|
.TemporaryItems
|
||||||
|
.Trashes
|
||||||
|
.VolumeIcon.icns
|
||||||
|
.com.apple.timemachine.donotpresent
|
||||||
|
|
||||||
|
# Directories potentially created on remote AFP share
|
||||||
|
.AppleDB
|
||||||
|
.AppleDesktop
|
||||||
|
Network Trash Folder
|
||||||
|
Temporary Items
|
||||||
|
.apdisk
|
||||||
|
|
||||||
|
=======
|
||||||
|
# Local
|
||||||
|
.env
|
||||||
|
dist
|
14
sample/01-complete-app/jest.json
Normal file
14
sample/01-complete-app/jest.json
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"ts",
|
||||||
|
"tsx",
|
||||||
|
"js",
|
||||||
|
"json"
|
||||||
|
],
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.tsx?$": "ts-jest"
|
||||||
|
},
|
||||||
|
"testRegex": "/src/.*\\.(test|spec).(ts|tsx|js)$",
|
||||||
|
"collectCoverageFrom" : ["src/**/*.{js,jsx,tsx,ts}", "!**/node_modules/**", "!**/vendor/**"],
|
||||||
|
"coverageReporters": ["json", "lcov"]
|
||||||
|
}
|
15862
sample/01-complete-app/package-lock.json
generated
Normal file
15862
sample/01-complete-app/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
71
sample/01-complete-app/package.json
Normal file
71
sample/01-complete-app/package.json
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
{
|
||||||
|
"name": "nest-typescript-starter",
|
||||||
|
"private": true,
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "Nest TypeScript starter repository",
|
||||||
|
"license": "MIT",
|
||||||
|
"scripts": {
|
||||||
|
"prebuild": "rimraf dist",
|
||||||
|
"build": "nest build",
|
||||||
|
"format": "prettier --write \"src/**/*.ts\" \"test/**/*.ts\"",
|
||||||
|
"start": "nest start",
|
||||||
|
"start:dev": "nest start --watch",
|
||||||
|
"start:debug": "nest start --debug --watch",
|
||||||
|
"start:prod": "node dist/main",
|
||||||
|
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix",
|
||||||
|
"test": "jest",
|
||||||
|
"test:watch": "jest --watch",
|
||||||
|
"test:cov": "jest --coverage",
|
||||||
|
"test:debug": "node --inspect-brk -r tsconfig-paths/register -r ts-node/register node_modules/.bin/jest --runInBand",
|
||||||
|
"test:e2e": "jest --config ./test/jest-e2e.json"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@nestjs/common": "8.0.9",
|
||||||
|
"@nestjs/core": "8.0.9",
|
||||||
|
"@nestjs/platform-express": "8.0.9",
|
||||||
|
"dotenv": "10.0.0",
|
||||||
|
"nestjs-telegraf": "2.4.0",
|
||||||
|
"reflect-metadata": "0.1.13",
|
||||||
|
"rimraf": "3.0.2",
|
||||||
|
"telegraf": "4.4.2"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@nestjs/cli": "8.2.8",
|
||||||
|
"@nestjs/schematics": "8.0.5",
|
||||||
|
"@nestjs/testing": "8.0.9",
|
||||||
|
"@types/express": "4.17.13",
|
||||||
|
"@types/jest": "27.4.0",
|
||||||
|
"@types/node": "16.11.22",
|
||||||
|
"@types/supertest": "2.0.11",
|
||||||
|
"@typescript-eslint/eslint-plugin": "5.11.0",
|
||||||
|
"@typescript-eslint/parser": "5.11.0",
|
||||||
|
"eslint": "8.8.0",
|
||||||
|
"eslint-config-prettier": "8.3.0",
|
||||||
|
"eslint-plugin-prettier": "4.0.0",
|
||||||
|
"jest": "27.5.1",
|
||||||
|
"prettier": "2.5.1",
|
||||||
|
"supertest": "6.2.2",
|
||||||
|
"ts-jest": "27.1.3",
|
||||||
|
"ts-loader": "9.2.6",
|
||||||
|
"ts-node": "10.5.0",
|
||||||
|
"tsconfig-paths": "3.12.0",
|
||||||
|
"typescript": "4.4.4"
|
||||||
|
},
|
||||||
|
"jest": {
|
||||||
|
"moduleFileExtensions": [
|
||||||
|
"js",
|
||||||
|
"json",
|
||||||
|
"ts"
|
||||||
|
],
|
||||||
|
"rootDir": "src",
|
||||||
|
"testRegex": ".*\\.spec\\.ts$",
|
||||||
|
"transform": {
|
||||||
|
"^.+\\.(t|j)s$": "ts-jest"
|
||||||
|
},
|
||||||
|
"collectCoverageFrom": [
|
||||||
|
"**/*.(t|j)s"
|
||||||
|
],
|
||||||
|
"coverageDirectory": "../coverage",
|
||||||
|
"testEnvironment": "node"
|
||||||
|
}
|
||||||
|
}
|
3
sample/01-complete-app/src/app.constants.ts
Normal file
3
sample/01-complete-app/src/app.constants.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export const HELLO_SCENE_ID = 'HELLO_SCENE_ID';
|
||||||
|
|
||||||
|
export const GreeterBotName = 'greeter';
|
26
sample/01-complete-app/src/app.module.ts
Normal file
26
sample/01-complete-app/src/app.module.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { TelegrafModule } from 'nestjs-telegraf';
|
||||||
|
import { EchoModule } from './echo/echo.module';
|
||||||
|
import { GreeterModule } from './greeter/greeter.module';
|
||||||
|
import { sessionMiddleware } from './middleware/session.middleware';
|
||||||
|
import { GreeterBotName } from './app.constants';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [
|
||||||
|
TelegrafModule.forRoot({
|
||||||
|
token: process.env.ECHO_BOT_TOKEN,
|
||||||
|
include: [EchoModule],
|
||||||
|
}),
|
||||||
|
TelegrafModule.forRootAsync({
|
||||||
|
botName: GreeterBotName,
|
||||||
|
useFactory: () => ({
|
||||||
|
token: process.env.GREETER_BOT_TOKEN,
|
||||||
|
middlewares: [sessionMiddleware],
|
||||||
|
include: [GreeterModule],
|
||||||
|
}),
|
||||||
|
}),
|
||||||
|
EchoModule,
|
||||||
|
GreeterModule,
|
||||||
|
],
|
||||||
|
})
|
||||||
|
export class AppModule {}
|
@ -0,0 +1,7 @@
|
|||||||
|
import { createParamDecorator, ExecutionContext } from '@nestjs/common';
|
||||||
|
import { TelegrafExecutionContext } from 'nestjs-telegraf';
|
||||||
|
|
||||||
|
export const UpdateType = createParamDecorator(
|
||||||
|
(_, ctx: ExecutionContext) =>
|
||||||
|
TelegrafExecutionContext.create(ctx).getContext().updateType,
|
||||||
|
);
|
@ -0,0 +1,13 @@
|
|||||||
|
import { ArgumentsHost, Catch, ExceptionFilter } from '@nestjs/common';
|
||||||
|
import { TelegrafArgumentsHost } from 'nestjs-telegraf';
|
||||||
|
import { Context } from '../../interfaces/context.interface';
|
||||||
|
|
||||||
|
@Catch()
|
||||||
|
export class TelegrafExceptionFilter implements ExceptionFilter {
|
||||||
|
async catch(exception: Error, host: ArgumentsHost): Promise<void> {
|
||||||
|
const telegrafHost = TelegrafArgumentsHost.create(host);
|
||||||
|
const ctx = telegrafHost.getContext<Context>();
|
||||||
|
|
||||||
|
await ctx.replyWithHTML(`<b>Error</b>: ${exception.message}`);
|
||||||
|
}
|
||||||
|
}
|
20
sample/01-complete-app/src/common/guards/admin.guard.ts
Normal file
20
sample/01-complete-app/src/common/guards/admin.guard.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { CanActivate, ExecutionContext, Injectable } from '@nestjs/common';
|
||||||
|
import { TelegrafExecutionContext, TelegrafException } from 'nestjs-telegraf';
|
||||||
|
import { Context } from '../../interfaces/context.interface';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AdminGuard implements CanActivate {
|
||||||
|
private readonly ADMIN_IDS = [];
|
||||||
|
|
||||||
|
canActivate(context: ExecutionContext): boolean {
|
||||||
|
const ctx = TelegrafExecutionContext.create(context);
|
||||||
|
const { from } = ctx.getContext<Context>();
|
||||||
|
|
||||||
|
const isAdmin = this.ADMIN_IDS.includes(from.id);
|
||||||
|
if (!isAdmin) {
|
||||||
|
throw new TelegrafException('You are not admin 😡');
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,20 @@
|
|||||||
|
import {
|
||||||
|
CallHandler,
|
||||||
|
ExecutionContext,
|
||||||
|
Injectable,
|
||||||
|
NestInterceptor,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import { Observable } from 'rxjs';
|
||||||
|
import { tap } from 'rxjs/operators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ResponseTimeInterceptor implements NestInterceptor {
|
||||||
|
intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
|
||||||
|
console.log('Before...');
|
||||||
|
|
||||||
|
const start = Date.now();
|
||||||
|
return next
|
||||||
|
.handle()
|
||||||
|
.pipe(tap(() => console.log(`Response time: ${Date.now() - start}ms`)));
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,8 @@
|
|||||||
|
import { Injectable, PipeTransform } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class ReverseTextPipe implements PipeTransform {
|
||||||
|
transform(value: string): string {
|
||||||
|
return value.split('').reverse().join('');
|
||||||
|
}
|
||||||
|
}
|
9
sample/01-complete-app/src/echo/echo.module.ts
Normal file
9
sample/01-complete-app/src/echo/echo.module.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { EchoUpdate } from './echo.update';
|
||||||
|
import { EchoService } from './echo.service';
|
||||||
|
import { RandomNumberScene } from '../greeter/scenes/random-number.scene';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [EchoUpdate, EchoService, RandomNumberScene],
|
||||||
|
})
|
||||||
|
export class EchoModule {}
|
8
sample/01-complete-app/src/echo/echo.service.ts
Normal file
8
sample/01-complete-app/src/echo/echo.service.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class EchoService {
|
||||||
|
echo(text: string): string {
|
||||||
|
return `Echo: ${text}`;
|
||||||
|
}
|
||||||
|
}
|
53
sample/01-complete-app/src/echo/echo.update.ts
Normal file
53
sample/01-complete-app/src/echo/echo.update.ts
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
import { UseFilters, UseGuards, UseInterceptors } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
Help,
|
||||||
|
InjectBot,
|
||||||
|
On,
|
||||||
|
Message,
|
||||||
|
Start,
|
||||||
|
Update,
|
||||||
|
Command,
|
||||||
|
} from 'nestjs-telegraf';
|
||||||
|
import { Telegraf } from 'telegraf';
|
||||||
|
import { EchoService } from './echo.service';
|
||||||
|
import { GreeterBotName } from '../app.constants';
|
||||||
|
import { Context } from '../interfaces/context.interface';
|
||||||
|
import { ReverseTextPipe } from '../common/pipes/reverse-text.pipe';
|
||||||
|
import { ResponseTimeInterceptor } from '../common/interceptors/response-time.interceptor';
|
||||||
|
import { AdminGuard } from '../common/guards/admin.guard';
|
||||||
|
import { TelegrafExceptionFilter } from '../common/filters/telegraf-exception.filter';
|
||||||
|
|
||||||
|
@Update()
|
||||||
|
@UseInterceptors(ResponseTimeInterceptor)
|
||||||
|
@UseFilters(TelegrafExceptionFilter)
|
||||||
|
export class EchoUpdate {
|
||||||
|
constructor(
|
||||||
|
@InjectBot(GreeterBotName)
|
||||||
|
private readonly bot: Telegraf<Context>,
|
||||||
|
private readonly echoService: EchoService,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
@Start()
|
||||||
|
async onStart(): Promise<string> {
|
||||||
|
const me = await this.bot.telegram.getMe();
|
||||||
|
return `Hey, I'm ${me.first_name}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Help()
|
||||||
|
async onHelp(): Promise<string> {
|
||||||
|
return 'Send me any text';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('admin')
|
||||||
|
@UseGuards(AdminGuard)
|
||||||
|
onAdminCommand(): string {
|
||||||
|
return 'Welcome judge';
|
||||||
|
}
|
||||||
|
|
||||||
|
@On('text')
|
||||||
|
onMessage(
|
||||||
|
@Message('text', new ReverseTextPipe()) reversedText: string,
|
||||||
|
): string {
|
||||||
|
return this.echoService.echo(reversedText);
|
||||||
|
}
|
||||||
|
}
|
8
sample/01-complete-app/src/greeter/greeter.module.ts
Normal file
8
sample/01-complete-app/src/greeter/greeter.module.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { Module } from '@nestjs/common';
|
||||||
|
import { GreeterUpdate } from './greeter.update';
|
||||||
|
import { RandomNumberScene } from './scenes/random-number.scene';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
providers: [GreeterUpdate, RandomNumberScene],
|
||||||
|
})
|
||||||
|
export class GreeterModule {}
|
26
sample/01-complete-app/src/greeter/greeter.update.ts
Normal file
26
sample/01-complete-app/src/greeter/greeter.update.ts
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import { Command, Ctx, Hears, Start, Update, Sender } from 'nestjs-telegraf';
|
||||||
|
import { UpdateType as TelegrafUpdateType } from 'telegraf/typings/telegram-types';
|
||||||
|
import { Context } from '../interfaces/context.interface';
|
||||||
|
import { HELLO_SCENE_ID } from '../app.constants';
|
||||||
|
import { UpdateType } from '../common/decorators/update-type.decorator';
|
||||||
|
|
||||||
|
@Update()
|
||||||
|
export class GreeterUpdate {
|
||||||
|
@Start()
|
||||||
|
onStart(): string {
|
||||||
|
return 'Say hello to me';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Hears(['hi', 'hello', 'hey', 'qq'])
|
||||||
|
onGreetings(
|
||||||
|
@UpdateType() updateType: TelegrafUpdateType,
|
||||||
|
@Sender('first_name') firstName: string,
|
||||||
|
): string {
|
||||||
|
return `Hey ${firstName}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('scene')
|
||||||
|
async onSceneCommand(@Ctx() ctx: Context): Promise<void> {
|
||||||
|
await ctx.scene.enter(HELLO_SCENE_ID);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,29 @@
|
|||||||
|
import { Scene, SceneEnter, SceneLeave, Command } from 'nestjs-telegraf';
|
||||||
|
import { HELLO_SCENE_ID } from '../../app.constants';
|
||||||
|
import { Context } from '../../interfaces/context.interface';
|
||||||
|
|
||||||
|
@Scene(HELLO_SCENE_ID)
|
||||||
|
export class RandomNumberScene {
|
||||||
|
@SceneEnter()
|
||||||
|
onSceneEnter(): string {
|
||||||
|
console.log('Enter to scene');
|
||||||
|
return 'Welcome on scene ✋';
|
||||||
|
}
|
||||||
|
|
||||||
|
@SceneLeave()
|
||||||
|
onSceneLeave(): string {
|
||||||
|
console.log('Leave from scene');
|
||||||
|
return 'Bye Bye 👋';
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command(['rng', 'random'])
|
||||||
|
onRandomCommand(): number {
|
||||||
|
console.log('Use "random" command');
|
||||||
|
return Math.floor(Math.random() * 11);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Command('leave')
|
||||||
|
async onLeaveCommand(ctx: Context): Promise<void> {
|
||||||
|
await ctx.scene.leave();
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,4 @@
|
|||||||
|
import { Scenes } from 'telegraf';
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-empty-interface
|
||||||
|
export interface Context extends Scenes.SceneContext {}
|
8
sample/01-complete-app/src/main.ts
Normal file
8
sample/01-complete-app/src/main.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import 'dotenv/config';
|
||||||
|
import { NestFactory } from '@nestjs/core';
|
||||||
|
import { AppModule } from './app.module';
|
||||||
|
|
||||||
|
async function bootstrap() {
|
||||||
|
await NestFactory.createApplicationContext(AppModule);
|
||||||
|
}
|
||||||
|
bootstrap();
|
@ -0,0 +1,3 @@
|
|||||||
|
import { session } from 'telegraf';
|
||||||
|
|
||||||
|
export const sessionMiddleware = session();
|
4
sample/01-complete-app/tsconfig.build.json
Normal file
4
sample/01-complete-app/tsconfig.build.json
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
"extends": "./tsconfig.json",
|
||||||
|
"exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
|
||||||
|
}
|
16
sample/01-complete-app/tsconfig.json
Normal file
16
sample/01-complete-app/tsconfig.json
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"module": "commonjs",
|
||||||
|
"declaration": true,
|
||||||
|
"removeComments": true,
|
||||||
|
"emitDecoratorMetadata": true,
|
||||||
|
"experimentalDecorators": true,
|
||||||
|
"allowSyntheticDefaultImports": true,
|
||||||
|
"target": "es2017",
|
||||||
|
"sourceMap": true,
|
||||||
|
"outDir": "./dist",
|
||||||
|
"baseUrl": "./src",
|
||||||
|
"incremental": true
|
||||||
|
},
|
||||||
|
"include": ["src/**/*"]
|
||||||
|
}
|
@ -1,6 +0,0 @@
|
|||||||
export * from './on.decorator';
|
|
||||||
export * from './use.decorator';
|
|
||||||
export * from './command.decorator';
|
|
||||||
export * from './game-query.decorator';
|
|
||||||
export * from './hears.decorator';
|
|
||||||
export * from './inline-query.decorator';
|
|
@ -1,18 +0,0 @@
|
|||||||
import { Injectable } from '@nestjs/common';
|
|
||||||
import { Reflector } from '@nestjs/core';
|
|
||||||
import { LISTENERS_METADATA, UPDATE_METADATA } from '../telegraf.constants';
|
|
||||||
import { ListenerMetadata } from '../interfaces';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class MetadataAccessorService {
|
|
||||||
constructor(private readonly reflector: Reflector) {}
|
|
||||||
|
|
||||||
isUpdate(target: Function): boolean {
|
|
||||||
if (!target) return false;
|
|
||||||
return !!this.reflector.get(UPDATE_METADATA, target);
|
|
||||||
}
|
|
||||||
|
|
||||||
getListenerMetadata(target: Function): ListenerMetadata[] | undefined {
|
|
||||||
return this.reflector.get(LISTENERS_METADATA, target);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,16 +0,0 @@
|
|||||||
import { Bot } from 'grammy';
|
|
||||||
import { TelegrafModuleOptions } from '../interfaces';
|
|
||||||
|
|
||||||
export async function createBotFactory(
|
|
||||||
options: TelegrafModuleOptions,
|
|
||||||
): Promise<Bot> {
|
|
||||||
const bot = new Bot(options.token, options.config);
|
|
||||||
|
|
||||||
bot.use(...(options.middlewares ?? []));
|
|
||||||
|
|
||||||
if (options.pollingOptions !== false) {
|
|
||||||
bot.start(options.pollingOptions);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bot;
|
|
||||||
}
|
|
@ -1,9 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": "../../tsconfig.json",
|
|
||||||
"compilerOptions": {
|
|
||||||
"declaration": true,
|
|
||||||
"outDir": "../../dist/libs/grammy"
|
|
||||||
},
|
|
||||||
"include": ["src/**/*"],
|
|
||||||
"exclude": ["node_modules", "dist", "test", "**/*spec.ts"]
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user