Merge pull request #190 from Morb0/feature/execution-context

feat(): custom execution context
This commit is contained in:
Alexander Bukhalo 2021-01-07 03:35:46 +03:00 committed by GitHub
commit 6c1aa8e2cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 483 additions and 665 deletions

View File

@ -6,10 +6,9 @@ module.exports = {
},
plugins: ['@typescript-eslint/eslint-plugin'],
extends: [
'plugin:@typescript-eslint/eslint-recommended',
'plugin:@typescript-eslint/recommended',
'prettier',
'prettier/@typescript-eslint',
'plugin:prettier/recommended',
],
root: true,
env: {

View File

@ -1,3 +1,4 @@
export * from './core';
export * from './listeners';
export * from './scene';
export * from './params';

View File

@ -5,4 +5,6 @@ import { createMissedListenerDecorator } from '../../utils';
*
* @see https://telegraf.js.org/#/?id=cashtag
*/
export const Cashtag = createMissedListenerDecorator<[string | string[]]>('cashtag');
export const Cashtag = createMissedListenerDecorator<[string | string[]]>(
'cashtag',
);

View File

@ -5,4 +5,6 @@ import { createMissedListenerDecorator } from '../../utils';
*
* @see https://telegraf.js.org/#/?id=telegraf-email
*/
export const Email = createMissedListenerDecorator<[string | string[]]>('email');
export const Email = createMissedListenerDecorator<[string | string[]]>(
'email',
);

View File

@ -5,4 +5,6 @@ import { createMissedListenerDecorator } from '../../utils';
*
* @see https://telegraf.js.org/#/?id=hashtag
*/
export const Hashtag = createMissedListenerDecorator<[string | string[]]>('hashtag');
export const Hashtag = createMissedListenerDecorator<[string | string[]]>(
'hashtag',
);

View File

@ -6,4 +6,6 @@ import { HearsTriggers } from 'telegraf/typings/composer';
*
* @see https://telegraf.js.org/#/?id=inlinequery
*/
export const InlineQuery = createMissedListenerDecorator<[HearsTriggers<unknown>]>('inlineQuery');
export const InlineQuery = createMissedListenerDecorator<
[HearsTriggers<unknown>]
>('inlineQuery');

View File

@ -5,4 +5,6 @@ import { createMissedListenerDecorator } from '../../utils';
*
* @see https://telegraf.js.org/#/?id=mention
*/
export const Mention = createMissedListenerDecorator<[string | string[]]>('mention');
export const Mention = createMissedListenerDecorator<[string | string[]]>(
'mention',
);

View File

@ -5,4 +5,6 @@ import { createMissedListenerDecorator } from '../../utils';
*
* @see https://telegraf.js.org/#/?id=phone
*/
export const Phone = createMissedListenerDecorator<[string | string[]]>('phone');
export const Phone = createMissedListenerDecorator<[string | string[]]>(
'phone',
);

View File

@ -5,4 +5,6 @@ import { createMissedListenerDecorator } from '../../utils';
*
* @see https://telegraf.js.org/#/?id=telegraf-textlink
*/
export const TextLink = createMissedListenerDecorator<[string | string[]]>('textLink');
export const TextLink = createMissedListenerDecorator<[string | string[]]>(
'textLink',
);

View File

@ -5,4 +5,6 @@ import { createMissedListenerDecorator } from '../../utils';
*
* @see https://telegraf.js.org/#/?id=telegraf-textlink
*/
export const TextMention = createMissedListenerDecorator<[string | string[]]>('textMention');
export const TextMention = createMissedListenerDecorator<[string | string[]]>(
'textMention',
);

View File

@ -0,0 +1,8 @@
import { createTelegrafParamDecorator } from '../../utils/param-decorator.util';
import { TelegrafParamtype } from '../../enums/telegraf-paramtype.enum';
export const Context: () => ParameterDecorator = createTelegrafParamDecorator(
TelegrafParamtype.CONTEXT,
);
export const Ctx = Context;

View File

@ -0,0 +1,4 @@
export * from './context.decorator';
export * from './next.decorator';
export * from './message.decorator';
export * from './sender.decorator';

View File

@ -0,0 +1,21 @@
import { PipeTransform, Type } from '@nestjs/common';
import { createTelegrafPipesParamDecorator } from '../../utils/param-decorator.util';
import { TelegrafParamtype } from '../../enums/telegraf-paramtype.enum';
export function Message(): ParameterDecorator;
export function Message(
...pipes: (Type<PipeTransform> | PipeTransform)[]
): ParameterDecorator;
export function Message(
property: string,
...pipes: (Type<PipeTransform> | PipeTransform)[]
): ParameterDecorator;
export function Message(
property?: string | (Type<PipeTransform> | PipeTransform),
...pipes: (Type<PipeTransform> | PipeTransform)[]
) {
return createTelegrafPipesParamDecorator(TelegrafParamtype.MESSAGE)(
property,
...pipes,
);
}

View File

@ -0,0 +1,6 @@
import { createTelegrafParamDecorator } from '../../utils/param-decorator.util';
import { TelegrafParamtype } from '../../enums/telegraf-paramtype.enum';
export const Next: () => ParameterDecorator = createTelegrafParamDecorator(
TelegrafParamtype.NEXT,
);

View File

@ -0,0 +1,21 @@
import { PipeTransform, Type } from '@nestjs/common';
import { createTelegrafPipesParamDecorator } from '../../utils/param-decorator.util';
import { TelegrafParamtype } from '../../enums/telegraf-paramtype.enum';
export function Sender(): ParameterDecorator;
export function Sender(
...pipes: (Type<PipeTransform> | PipeTransform)[]
): ParameterDecorator;
export function Sender(
property: string,
...pipes: (Type<PipeTransform> | PipeTransform)[]
): ParameterDecorator;
export function Sender(
property?: string | (Type<PipeTransform> | PipeTransform),
...pipes: (Type<PipeTransform> | PipeTransform)[]
) {
return createTelegrafPipesParamDecorator(TelegrafParamtype.SENDER)(
property,
...pipes,
);
}

View File

@ -0,0 +1,7 @@
export enum TelegrafParamtype {
CONTEXT,
NEXT,
SENDER,
MESSAGE,
// TODO-Possible-Feature: Add more paramtypes
}

1
lib/errors/index.ts Normal file
View File

@ -0,0 +1 @@
export * from './telegraf.exception';

View File

@ -0,0 +1 @@
export class TelegrafException extends Error {}

View File

@ -0,0 +1,3 @@
export * from './tg-arguments-host.interace';
export * from './telegraf-arguments-host';
export * from './telegraf-execution-context';

View File

@ -0,0 +1,22 @@
import { ArgumentsHost } from '@nestjs/common';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
import { TgArgumentsHost } from './tg-arguments-host.interace';
export class TelegrafArgumentsHost
extends ExecutionContextHost
implements TgArgumentsHost {
static create(context: ArgumentsHost): TelegrafArgumentsHost {
const type = context.getType();
const tgContext = new TelegrafArgumentsHost(context.getArgs());
tgContext.setType(type);
return tgContext;
}
getContext<T = any>(): T {
return this.getArgByIndex(0);
}
getNext<T = any>(): T {
return this.getArgByIndex(1);
}
}

View File

@ -0,0 +1,32 @@
import { ContextType, ExecutionContext } from '@nestjs/common';
import { ExecutionContextHost } from '@nestjs/core/helpers/execution-context-host';
import { TgArgumentsHost } from './tg-arguments-host.interace';
export type TelegrafContextType = 'telegraf' | ContextType;
export class TelegrafExecutionContext
extends ExecutionContextHost
implements TgArgumentsHost {
static create(context: ExecutionContext): TelegrafExecutionContext {
const type = context.getType();
const tgContext = new TelegrafExecutionContext(
context.getArgs(),
context.getClass(),
context.getHandler(),
);
tgContext.setType(type);
return tgContext;
}
getType<TContext extends string = TelegrafContextType>(): TContext {
return super.getType();
}
getContext<T = any>(): T {
return this.getArgByIndex(0);
}
getNext<T = any>(): T {
return this.getArgByIndex(0);
}
}

View File

@ -0,0 +1,6 @@
import { ArgumentsHost } from '@nestjs/common';
export interface TgArgumentsHost extends ArgumentsHost {
getContext<T = any>(): T;
getNext<T = any>(): T;
}

View File

@ -0,0 +1,28 @@
import { ParamData } from '@nestjs/common';
import { ParamsFactory } from '@nestjs/core/helpers/external-context-creator';
import { Context } from 'telegraf';
import { TelegrafParamtype } from '../enums/telegraf-paramtype.enum';
export class TelegrafParamsFactory implements ParamsFactory {
exchangeKeyForValue(
type: TelegrafParamtype,
data: ParamData,
args: unknown[],
): unknown {
const ctx = args[0] as Context;
const next = args[1] as Function;
switch (type) {
case TelegrafParamtype.CONTEXT:
return ctx;
case TelegrafParamtype.NEXT:
return next;
case TelegrafParamtype.SENDER:
return ctx.from ? ctx.from[data as string] : ctx.from;
case TelegrafParamtype.MESSAGE:
return ctx.message ? ctx.message[data as string] : ctx.message;
default:
return null;
}
}
}

View File

@ -2,5 +2,8 @@ export * from './decorators';
export * from './interfaces';
export * from './utils';
export * from './types';
export * from './services';
export * from './errors';
export * from './execution-context';
export * from './telegraf.constants';
export * from './telegraf.module';

View File

@ -0,0 +1,5 @@
import { ArgumentsHost } from '@nestjs/common';
export interface TelegrafExceptionFilter<T = any> {
catch(exception: T, host: ArgumentsHost): any;
}

View File

@ -19,9 +19,7 @@ export class BaseExplorerService {
include: Function[],
): Module[] {
const modules = [...modulesContainer.values()];
return modules.filter(({ metatype }) =>
include.includes(metatype),
);
return modules.filter(({ metatype }) => include.includes(metatype));
}
flatMap<T>(

View File

@ -3,33 +3,40 @@ import { DiscoveryService, ModuleRef, ModulesContainer } from '@nestjs/core';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import { MetadataScanner } from '@nestjs/core/metadata-scanner';
import { Module } from '@nestjs/core/injector/module';
import { BaseScene, Composer, Stage, Telegraf } from 'telegraf';
import { ParamMetadata } from '@nestjs/core/helpers/interfaces';
import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator';
import { BaseScene, Composer, Context, Stage, Telegraf } from 'telegraf';
import { MetadataAccessorService } from './metadata-accessor.service';
import {
PARAM_ARGS_METADATA,
TELEGRAF_BOT_NAME,
TELEGRAF_MODULE_OPTIONS,
} from '../telegraf.constants';
import { TelegrafModuleOptions } from '../interfaces';
import { BaseExplorerService } from './base-explorer.service';
import { TelegrafParamsFactory } from '../factories/telegraf-params-factory';
import { TelegrafContextType } from '../execution-context';
import { TelegrafModuleOptions } from '../interfaces';
@Injectable()
export class ListenersExplorerService
extends BaseExplorerService
implements OnModuleInit {
private readonly telegrafParamsFactory = new TelegrafParamsFactory();
private readonly stage = new Stage([]);
private bot: Telegraf<any>;
constructor(
@Inject(TELEGRAF_BOT_NAME)
private readonly botName: string,
@Inject(TELEGRAF_MODULE_OPTIONS)
private readonly telegrafOptions: TelegrafModuleOptions,
@Inject(TELEGRAF_BOT_NAME)
private readonly botName: string,
private readonly moduleRef: ModuleRef,
private readonly discoveryService: DiscoveryService,
private readonly metadataAccessor: MetadataAccessorService,
private readonly metadataScanner: MetadataScanner,
private readonly modulesContainer: ModulesContainer,
private readonly externalContextCreator: ExternalContextCreator,
) {
super();
}
@ -57,23 +64,21 @@ export class ListenersExplorerService
const updates = this.flatMap<InstanceWrapper>(modules, (instance) =>
this.filterUpdates(instance),
);
updates.forEach(({ instance }) =>
this.registerInstanceMethodListeners(this.bot, instance),
);
updates.forEach((wrapper) => this.registerListeners(this.bot, wrapper));
}
private registerScenes(modules: Module[]): void {
const scenes = this.flatMap<InstanceWrapper>(modules, (instance) =>
this.filterScenes(instance),
const scenes = this.flatMap<InstanceWrapper>(modules, (wrapper) =>
this.filterScenes(wrapper),
);
scenes.forEach(({ instance }) => {
scenes.forEach((wrapper) => {
const sceneId = this.metadataAccessor.getSceneMetadata(
instance.constructor,
wrapper.instance.constructor,
);
const scene = new BaseScene(sceneId);
this.stage.register(scene);
this.registerInstanceMethodListeners(scene, instance);
this.registerListeners(scene, wrapper);
});
}
@ -97,20 +102,73 @@ export class ListenersExplorerService
return wrapper;
}
private registerInstanceMethodListeners(
composer: Composer<never>,
instance: Record<string, Function>,
private registerListeners(
composer: Composer<any>,
wrapper: InstanceWrapper<unknown>,
): void {
const { instance } = wrapper;
const prototype = Object.getPrototypeOf(instance);
this.metadataScanner.scanFromPrototype(instance, prototype, (name) => {
const methodRef = instance[name];
this.metadataScanner.scanFromPrototype(instance, prototype, (name) =>
this.registerIfListener(composer, instance, prototype, name),
);
}
const metadata = this.metadataAccessor.getListenerMetadata(methodRef);
if (!metadata) return;
private registerIfListener(
composer: Composer<any>,
instance: any,
prototype: any,
methodName: string,
): void {
const methodRef = prototype[methodName];
const metadata = this.metadataAccessor.getListenerMetadata(methodRef);
if (!metadata) {
return undefined;
}
const middlewareFn = methodRef.bind(instance);
const { method, args } = metadata;
composer[method](...args, middlewareFn);
});
const listenerCallbackFn = this.createContextCallback(
instance,
prototype,
methodName,
);
const { method, args } = metadata;
/* Basic callback */
// composer[method](...args, listenerCallbackFn);
/* Complex callback return value handing */
composer[method](
...args,
async (ctx: Context, next: Function): Promise<void> => {
const result = await listenerCallbackFn(ctx, next);
if (result) {
await ctx.reply(String(result));
}
// TODO-Possible-Feature: Add more supported return types
},
);
}
createContextCallback<T extends Record<string, unknown>>(
instance: T,
prototype: unknown,
methodName: string,
) {
const paramsFactory = this.telegrafParamsFactory;
const resolverCallback = this.externalContextCreator.create<
Record<number, ParamMetadata>,
TelegrafContextType
>(
instance,
prototype[methodName],
methodName,
PARAM_ARGS_METADATA,
paramsFactory,
undefined,
undefined,
undefined,
'telegraf',
);
return resolverCallback;
}
}

View File

@ -2,7 +2,7 @@ import { Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import {
SCENE_METADATA,
UPDATE_LISTENER_METADATA,
LISTENER_METADATA,
UPDATE_METADATA,
} from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces';
@ -12,21 +12,17 @@ export class MetadataAccessorService {
constructor(private readonly reflector: Reflector) {}
isUpdate(target: Function): boolean {
if (!target) {
return false;
}
if (!target) return false;
return !!this.reflector.get(UPDATE_METADATA, target);
}
isScene(target: Function): boolean {
if (!target) {
return false;
}
if (!target) return false;
return !!this.reflector.get(SCENE_METADATA, target);
}
getListenerMetadata(target: Function): ListenerMetadata | undefined {
return this.reflector.get(UPDATE_LISTENER_METADATA, target);
return this.reflector.get(LISTENER_METADATA, target);
}
getSceneMetadata(target: Function): string | undefined {

View File

@ -1,8 +1,11 @@
import { ROUTE_ARGS_METADATA } from '@nestjs/common/constants';
export const TELEGRAF_MODULE_OPTIONS = 'TELEGRAF_MODULE_OPTIONS';
export const TELEGRAF_BOT_NAME = 'TELEGRAF_BOT_NAME';
export const DEFAULT_BOT_NAME = 'DEFAULT_BOT_NAME';
export const UPDATE_METADATA = 'UPDATE_METADATA';
export const UPDATE_LISTENER_METADATA = 'UPDATE_LISTENER_METADATA';
export const SCENE_METADATA = 'SCENE_METADATA';
export const LISTENER_METADATA = 'LISTENER_METADATA';
export const PARAM_ARGS_METADATA = ROUTE_ARGS_METADATA;

View File

@ -1,7 +1,7 @@
import { SetMetadata } from '@nestjs/common';
import { BaseScene as Scene } from 'telegraf';
import { ComposerMethodArgs, SceneMethods } from '../types';
import { UPDATE_LISTENER_METADATA } from '../telegraf.constants';
import { LISTENER_METADATA } from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces';
export function createListenerDecorator<TMethod extends SceneMethods>(
@ -10,7 +10,7 @@ export function createListenerDecorator<TMethod extends SceneMethods>(
return (
...args: ComposerMethodArgs<Scene<never>, TMethod>
): MethodDecorator => {
return SetMetadata(UPDATE_LISTENER_METADATA, {
return SetMetadata(LISTENER_METADATA, {
method,
args,
} as ListenerMetadata);
@ -20,10 +20,8 @@ export function createListenerDecorator<TMethod extends SceneMethods>(
export function createMissedListenerDecorator<TArgs extends any[]>(
method: string,
) {
return (
...args: TArgs
): MethodDecorator => {
return SetMetadata(UPDATE_LISTENER_METADATA, {
return (...args: TArgs): MethodDecorator => {
return SetMetadata(LISTENER_METADATA, {
method,
args,
} as ListenerMetadata);

View File

@ -0,0 +1,50 @@
import { assignMetadata, PipeTransform, Type } from '@nestjs/common';
import { isNil, isString } from '@nestjs/common/utils/shared.utils';
import { TelegrafParamtype } from '../enums/telegraf-paramtype.enum';
import { PARAM_ARGS_METADATA } from '../telegraf.constants';
export type ParamData = object | string | number;
export const createTelegrafParamDecorator = (paramtype: TelegrafParamtype) => {
return (data?: ParamData): ParameterDecorator => (target, key, index) => {
const args =
Reflect.getMetadata(PARAM_ARGS_METADATA, target.constructor, key) || {};
Reflect.defineMetadata(
PARAM_ARGS_METADATA,
assignMetadata(args, paramtype, index, data),
target.constructor,
key,
);
};
};
export const createTelegrafPipesParamDecorator = (
paramtype: TelegrafParamtype,
) => (
data?: any,
...pipes: (Type<PipeTransform> | PipeTransform)[]
): ParameterDecorator => (target, key, index) => {
addPipesMetadata(paramtype, data, pipes, target, key, index);
};
export const addPipesMetadata = (
paramtype: TelegrafParamtype,
data: any,
pipes: (Type<PipeTransform> | PipeTransform)[],
target: Record<string, any>,
key: string | symbol,
index: number,
) => {
const args =
Reflect.getMetadata(PARAM_ARGS_METADATA, target.constructor, key) || {};
const hasParamData = isNil(data) || isString(data);
const paramData = hasParamData ? data : undefined;
const paramPipes = hasParamData ? pipes : [data, ...pipes];
Reflect.defineMetadata(
PARAM_ARGS_METADATA,
assignMetadata(args, paramtype, index, paramData, ...paramPipes),
target.constructor,
key,
);
};

590
package-lock.json generated
View File

@ -4006,12 +4006,6 @@
"integrity": "sha512-3c+yGKvVP5Y9TYBEibGNR+kLtijnj7mYrXRg+WpFb2X9xm04g/DXYkfg4hmzJQosc9snFNUPkbYIhu+KAm6jJw==",
"dev": true
},
"@types/json5": {
"version": "0.0.29",
"resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz",
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true
},
"@types/lodash": {
"version": "4.14.167",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.167.tgz",
@ -4190,33 +4184,12 @@
"sprintf-js": "~1.0.2"
}
},
"array-includes": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.1.1.tgz",
"integrity": "sha512-c2VXaCHl7zPsvpkFsw4nxvFie4fh1ur9bpcgsVkIjqn0H/Xwdg+7fv3n2r/isyS8EBj5b06M9kHyZuIr4El6WQ==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0",
"is-string": "^1.0.5"
}
},
"array-union": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
"integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
"dev": true
},
"array.prototype.flat": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/array.prototype.flat/-/array.prototype.flat-1.2.3.tgz",
"integrity": "sha512-gBlRZV0VSmfPIeWfuuy56XZMvbVfbEUnOXUvt3F/eUUUSyzlgLxhEX4YAEpxNAogRGehPSnfXyPtYyKAhkzQhQ==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1"
}
},
"astral-regex": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
@ -4356,12 +4329,6 @@
"integrity": "sha512-vlcSGgdYS26mPf7qNi+dCisbhiyDnrN1zaRbw3CSuc2wGOMEGGPsp46PdRG5gqXwgtJfjxDkxRNAgRPr1B77vQ==",
"dev": true
},
"contains-path": {
"version": "0.1.0",
"resolved": "https://registry.npmjs.org/contains-path/-/contains-path-0.1.0.tgz",
"integrity": "sha1-/ozxhP9mcLa67wGp1IYaXL7EEgo=",
"dev": true
},
"cosmiconfig": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.0.0.tgz",
@ -4415,15 +4382,6 @@
"integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
"dev": true
},
"define-properties": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
"integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==",
"dev": true,
"requires": {
"object-keys": "^1.0.12"
}
},
"dir-glob": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
@ -4475,36 +4433,6 @@
"is-arrayish": "^0.2.1"
}
},
"es-abstract": {
"version": "1.17.6",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz",
"integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.0",
"is-regex": "^1.1.0",
"object-inspect": "^1.7.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.0",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
},
"es-to-primitive": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz",
"integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==",
"dev": true,
"requires": {
"is-callable": "^1.1.4",
"is-date-object": "^1.0.1",
"is-symbol": "^1.0.2"
}
},
"escape-string-regexp": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@ -4619,146 +4547,13 @@
"integrity": "sha512-9sm5/PxaFG7qNJvJzTROMM1Bk1ozXVTKI0buKOyb0Bsr1hrwi0H/TzxF/COtf1uxikIK8SwhX7K6zg78jAzbeA==",
"dev": true
},
"eslint-import-resolver-node": {
"version": "0.3.4",
"resolved": "https://registry.npmjs.org/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.4.tgz",
"integrity": "sha512-ogtf+5AB/O+nM6DIeBUNr2fuT7ot9Qg/1harBfBtaP13ekEWFQEEMP94BCB7zaNW3gyY+8SHYF00rnqYwXKWOA==",
"eslint-plugin-prettier": {
"version": "3.3.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.3.1.tgz",
"integrity": "sha512-Rq3jkcFY8RYeQLgk2cCwuc0P7SEFwDravPhsJZOQ5N4YI4DSg50NyqJ/9gdZHzQlHf8MvafSesbNJCcP/FF6pQ==",
"dev": true,
"requires": {
"debug": "^2.6.9",
"resolve": "^1.13.1"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
}
}
},
"eslint-module-utils": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/eslint-module-utils/-/eslint-module-utils-2.6.0.tgz",
"integrity": "sha512-6j9xxegbqe8/kZY8cYpcp0xhbK0EgJlg3g9mib3/miLaExuuwc3n5UEfSnU6hWMbT0FAYVvDbL9RrRgpUeQIvA==",
"dev": true,
"requires": {
"debug": "^2.6.9",
"pkg-dir": "^2.0.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
"dev": true,
"requires": {
"locate-path": "^2.0.0"
}
},
"locate-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
"integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
"dev": true,
"requires": {
"p-locate": "^2.0.0",
"path-exists": "^3.0.0"
}
},
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
"dev": true,
"requires": {
"p-try": "^1.0.0"
}
},
"p-locate": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
"dev": true,
"requires": {
"p-limit": "^1.1.0"
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
"dev": true
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
},
"pkg-dir": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-2.0.0.tgz",
"integrity": "sha1-9tXREJ4Z1j7fQo4L1X4Sd3YVM0s=",
"dev": true,
"requires": {
"find-up": "^2.1.0"
}
}
}
},
"eslint-plugin-import": {
"version": "2.22.1",
"resolved": "https://registry.npmjs.org/eslint-plugin-import/-/eslint-plugin-import-2.22.1.tgz",
"integrity": "sha512-8K7JjINHOpH64ozkAhpT3sd+FswIZTfMZTjdx052pnWrgRCVfp8op9tbjpAk3DdUeI/Ba4C8OjdC0r90erHEOw==",
"dev": true,
"requires": {
"array-includes": "^3.1.1",
"array.prototype.flat": "^1.2.3",
"contains-path": "^0.1.0",
"debug": "^2.6.9",
"doctrine": "1.5.0",
"eslint-import-resolver-node": "^0.3.4",
"eslint-module-utils": "^2.6.0",
"has": "^1.0.3",
"minimatch": "^3.0.4",
"object.values": "^1.1.1",
"read-pkg-up": "^2.0.0",
"resolve": "^1.17.0",
"tsconfig-paths": "^3.9.0"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"dev": true,
"requires": {
"ms": "2.0.0"
}
},
"doctrine": {
"version": "1.5.0",
"resolved": "https://registry.npmjs.org/doctrine/-/doctrine-1.5.0.tgz",
"integrity": "sha1-N53Ocw9hZvds76TmcHoVmwLFpvo=",
"dev": true,
"requires": {
"esutils": "^2.0.2",
"isarray": "^1.0.0"
}
}
"prettier-linter-helpers": "^1.0.0"
}
},
"eslint-scope": {
@ -4872,6 +4667,12 @@
"integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
"dev": true
},
"fast-diff": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
"integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
"dev": true
},
"fast-glob": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
@ -4999,12 +4800,6 @@
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
"dev": true
},
"function-bind": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
"integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==",
"dev": true
},
"functional-red-black-tree": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz",
@ -5099,33 +4894,12 @@
"wordwrap": "^1.0.0"
}
},
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
"integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==",
"dev": true,
"requires": {
"function-bind": "^1.1.1"
}
},
"has-flag": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=",
"dev": true
},
"has-symbols": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz",
"integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==",
"dev": true
},
"hosted-git-info": {
"version": "2.8.8",
"resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.8.tgz",
"integrity": "sha512-f/wzC2QaWBs7t9IYqB4T3sR1xviIViXJRJTWBlx2Gf3g0Xi5vI7Yy4koXQ1c9OYDGHN9sBy1DQ2AB8fqZBWhUg==",
"dev": true
},
"human-signals": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-1.1.1.tgz",
@ -5258,18 +5032,6 @@
"integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=",
"dev": true
},
"is-callable": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.2.tgz",
"integrity": "sha512-dnMqspv5nU3LoewK2N/y7KLtxtakvTuaCsU9FU50/QDmdbHNy/4/JuRtMHqRU22o3q+W89YQndQEeCVwK+3qrA==",
"dev": true
},
"is-date-object": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz",
"integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==",
"dev": true
},
"is-extglob": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
@ -5291,12 +5053,6 @@
"is-extglob": "^2.1.1"
}
},
"is-negative-zero": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.0.tgz",
"integrity": "sha1-lVOxIbD6wohp2p7UWeIMdUN4hGE=",
"dev": true
},
"is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
@ -5309,15 +5065,6 @@
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"dev": true
},
"is-regex": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz",
"integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==",
"dev": true,
"requires": {
"has-symbols": "^1.0.1"
}
},
"is-regexp": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-regexp/-/is-regexp-1.0.0.tgz",
@ -5330,27 +5077,6 @@
"integrity": "sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==",
"dev": true
},
"is-string": {
"version": "1.0.5",
"resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz",
"integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==",
"dev": true
},
"is-symbol": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz",
"integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==",
"dev": true,
"requires": {
"has-symbols": "^1.0.1"
}
},
"isarray": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
"integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=",
"dev": true
},
"isexe": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
@ -5397,15 +5123,6 @@
"integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=",
"dev": true
},
"json5": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz",
"integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==",
"dev": true,
"requires": {
"minimist": "^1.2.0"
}
},
"jsonfile": {
"version": "6.1.0",
"resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz",
@ -5596,29 +5313,6 @@
}
}
},
"load-json-file": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-2.0.0.tgz",
"integrity": "sha1-eUfkIUmvgNaWy/eXvKq8/h/inKg=",
"dev": true,
"requires": {
"graceful-fs": "^4.1.2",
"parse-json": "^2.2.0",
"pify": "^2.0.0",
"strip-bom": "^3.0.0"
},
"dependencies": {
"parse-json": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz",
"integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck=",
"dev": true,
"requires": {
"error-ex": "^1.2.0"
}
}
}
},
"locate-path": {
"version": "5.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
@ -5811,12 +5505,6 @@
"resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz",
"integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q=="
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
"dev": true
},
"natural-compare": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
@ -5834,26 +5522,6 @@
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.1.tgz",
"integrity": "sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw=="
},
"normalize-package-data": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
"integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
"dev": true,
"requires": {
"hosted-git-info": "^2.1.4",
"resolve": "^1.10.0",
"semver": "2 || 3 || 4 || 5",
"validate-npm-package-license": "^3.0.1"
},
"dependencies": {
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
"integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==",
"dev": true
}
}
},
"normalize-path": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
@ -5875,64 +5543,6 @@
"integrity": "sha512-VOJmgmS+7wvXf8CjbQmimtCnEx3IAoLxI3fp2fbWehxrWBcAQFbk+vcwb6vzR0VZv/eNCJ/27j151ZTwqW/JeQ==",
"dev": true
},
"object-inspect": {
"version": "1.8.0",
"resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.8.0.tgz",
"integrity": "sha512-jLdtEOB112fORuypAyl/50VRVIBIdVQOSUUGQHzJ4xBSbit81zRarz7GThkEFZy1RceYrWYcPcBFPQwHyAc1gA==",
"dev": true
},
"object-keys": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==",
"dev": true
},
"object.assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.1.tgz",
"integrity": "sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.18.0-next.0",
"has-symbols": "^1.0.1",
"object-keys": "^1.1.1"
},
"dependencies": {
"es-abstract": {
"version": "1.18.0-next.0",
"resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.18.0-next.0.tgz",
"integrity": "sha512-elZXTZXKn51hUBdJjSZGYRujuzilgXo8vSPQzjGYXLvSlGiCo8VO8ZGV3kjo9a0WNJJ57hENagwbtlRuHuzkcQ==",
"dev": true,
"requires": {
"es-to-primitive": "^1.2.1",
"function-bind": "^1.1.1",
"has": "^1.0.3",
"has-symbols": "^1.0.1",
"is-callable": "^1.2.0",
"is-negative-zero": "^2.0.0",
"is-regex": "^1.1.1",
"object-inspect": "^1.8.0",
"object-keys": "^1.1.1",
"object.assign": "^4.1.0",
"string.prototype.trimend": "^1.0.1",
"string.prototype.trimstart": "^1.0.1"
}
}
}
},
"object.values": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.1.tgz",
"integrity": "sha512-WTa54g2K8iu0kmS/us18jEmdv1a4Wi//BZ/DTVYEcH0XhLM5NYdpDHja3gt57VrZLcNAO2WGA+KpWsDBaHt6eA==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.0-next.1",
"function-bind": "^1.1.1",
"has": "^1.0.3"
}
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -6076,12 +5686,6 @@
"integrity": "sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==",
"dev": true
},
"pify": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz",
"integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=",
"dev": true
},
"pkg-dir": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
@ -6112,6 +5716,15 @@
"integrity": "sha512-PqyhM2yCjg/oKkFPtTGUojv7gnZAoG80ttl45O6x2Ug/rMJw4wcc9k6aaf2hibP7BGVCCM33gZoGjyvt9mm16Q==",
"dev": true
},
"prettier-linter-helpers": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
"integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
"dev": true,
"requires": {
"fast-diff": "^1.1.2"
}
},
"progress": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz",
@ -6134,89 +5747,6 @@
"integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==",
"dev": true
},
"read-pkg": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-2.0.0.tgz",
"integrity": "sha1-jvHAYjxqbbDcZxPEv6xGMysjaPg=",
"dev": true,
"requires": {
"load-json-file": "^2.0.0",
"normalize-package-data": "^2.3.2",
"path-type": "^2.0.0"
},
"dependencies": {
"path-type": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/path-type/-/path-type-2.0.0.tgz",
"integrity": "sha1-8BLMuEFbcJb8LaoQVMPXI4lZTHM=",
"dev": true,
"requires": {
"pify": "^2.0.0"
}
}
}
},
"read-pkg-up": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-2.0.0.tgz",
"integrity": "sha1-a3KoBImE4MQeeVEP1en6mbO1Sb4=",
"dev": true,
"requires": {
"find-up": "^2.0.0",
"read-pkg": "^2.0.0"
},
"dependencies": {
"find-up": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
"integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
"dev": true,
"requires": {
"locate-path": "^2.0.0"
}
},
"locate-path": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz",
"integrity": "sha1-K1aLJl7slExtnA3pw9u7ygNUzY4=",
"dev": true,
"requires": {
"p-locate": "^2.0.0",
"path-exists": "^3.0.0"
}
},
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
"dev": true,
"requires": {
"p-try": "^1.0.0"
}
},
"p-locate": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz",
"integrity": "sha1-IKAQOyIqcMj9OcwuWAaA893l7EM=",
"dev": true,
"requires": {
"p-limit": "^1.1.0"
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
"dev": true
},
"path-exists": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
"integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
"dev": true
}
}
},
"rechoir": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz",
@ -6439,38 +5969,6 @@
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"spdx-correct": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
"integrity": "sha512-cOYcUWwhCuHCXi49RhFRCyJEK3iPj1Ziz9DpViV3tbZOwXD49QzIN3MpOLJNxh2qwq2lJJZaKMVw9qNi4jTC0w==",
"dev": true,
"requires": {
"spdx-expression-parse": "^3.0.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-exceptions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.3.0.tgz",
"integrity": "sha512-/tTrYOC7PPI1nUAgx34hUpqXuyJG+DTHJTnIULG4rDygi4xu/tfgmq1e1cIRwRzwZgo4NLySi+ricLkZkw4i5A==",
"dev": true
},
"spdx-expression-parse": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz",
"integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==",
"dev": true,
"requires": {
"spdx-exceptions": "^2.1.0",
"spdx-license-ids": "^3.0.0"
}
},
"spdx-license-ids": {
"version": "3.0.6",
"resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.6.tgz",
"integrity": "sha512-+orQK83kyMva3WyPf59k1+Y525csj5JejicWut55zeTWANuN17qSiSLUXWtzHeNWORSvT7GLDJ/E/XiIWoXBTw==",
"dev": true
},
"sprintf-js": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
@ -6494,26 +5992,6 @@
"strip-ansi": "^6.0.0"
}
},
"string.prototype.trimend": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz",
"integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
}
},
"string.prototype.trimstart": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz",
"integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==",
"dev": true,
"requires": {
"define-properties": "^1.1.3",
"es-abstract": "^1.17.5"
}
},
"stringify-object": {
"version": "3.3.0",
"resolved": "https://registry.npmjs.org/stringify-object/-/stringify-object-3.3.0.tgz",
@ -6542,12 +6020,6 @@
}
}
},
"strip-bom": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-3.0.0.tgz",
"integrity": "sha1-IzTBjpx1n3vdVv3vfprj1YjmjtM=",
"dev": true
},
"strip-final-newline": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz",
@ -6672,18 +6144,6 @@
"is-number": "^7.0.0"
}
},
"tsconfig-paths": {
"version": "3.9.0",
"resolved": "https://registry.npmjs.org/tsconfig-paths/-/tsconfig-paths-3.9.0.tgz",
"integrity": "sha512-dRcuzokWhajtZWkQsDVKbWyY+jgcLC5sqJhg2PSgf4ZkH2aHPvaOY8YWGhmjb68b5qqTfasSsDO9k7RUiEmZAw==",
"dev": true,
"requires": {
"@types/json5": "^0.0.29",
"json5": "^1.0.1",
"minimist": "^1.2.0",
"strip-bom": "^3.0.0"
}
},
"tslib": {
"version": "1.11.1",
"resolved": "https://registry.npmjs.org/tslib/-/tslib-1.11.1.tgz",
@ -6779,16 +6239,6 @@
"integrity": "sha512-gTpR5XQNKFwOd4clxfnhaqvfqMpqEwr4tOtCyz4MtYZX2JYhfr1JvBFKdS+7K/9rfpZR3VLX+YWBbKoxCgS43Q==",
"dev": true
},
"validate-npm-package-license": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz",
"integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==",
"dev": true,
"requires": {
"spdx-correct": "^3.0.0",
"spdx-expression-parse": "^3.0.0"
}
},
"vscode-textmate": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-5.2.0.tgz",

View File

@ -54,7 +54,7 @@
"@typescript-eslint/parser": "4.12.0",
"eslint": "7.17.0",
"eslint-config-prettier": "7.1.0",
"eslint-plugin-import": "2.22.1",
"eslint-plugin-prettier": "^3.3.1",
"husky": "4.3.6",
"lint-staged": "10.5.3",
"lodash": "4.17.20",

View File

@ -9,7 +9,6 @@ import { GreeterBotName } from './app.constants';
imports: [
TelegrafModule.forRoot({
token: process.env.ECHO_BOT_TOKEN,
middlewares: [sessionMiddleware],
include: [EchoModule],
}),
TelegrafModule.forRootAsync({

View File

@ -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,
);

View File

@ -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}`);
}
}

View 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;
}
}

View File

@ -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`)));
}
}

View File

@ -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('');
}
}

View File

@ -1,9 +1,9 @@
import { Module } from '@nestjs/common';
import { EchoUpdate } from './echo.update';
import { EchoService } from './echo.service';
import { HelloScene } from './scenes/hello.scene';
import { RandomNumberScene } from '../greeter/scenes/random-number.scene';
@Module({
providers: [EchoUpdate, EchoService, HelloScene],
providers: [EchoUpdate, EchoService, RandomNumberScene],
})
export class EchoModule {}

View File

@ -1,18 +1,25 @@
import { Telegraf } from 'telegraf';
import { UseFilters, UseGuards, UseInterceptors } from '@nestjs/common';
import {
Command,
getBotToken,
Help,
InjectBot,
On,
Message,
Start,
Update,
Command,
} from 'nestjs-telegraf';
import { Telegraf } from 'telegraf';
import { EchoService } from './echo.service';
import { GreeterBotName, HELLO_SCENE_ID } from '../app.constants';
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)
@ -21,31 +28,26 @@ export class EchoUpdate {
) {}
@Start()
async onStart(ctx: Context): Promise<void> {
async onStart(): Promise<string> {
const me = await this.bot.telegram.getMe();
await ctx.reply(`Hey, I'm ${me.first_name}`);
return `Hey, I'm ${me.first_name}`;
}
@Help()
async onHelp(ctx: Context): Promise<void> {
await ctx.reply('Send me any text');
async onHelp(): Promise<string> {
return 'Send me any text';
}
@Command('scene')
async onSceneCommand(ctx: Context): Promise<void> {
await ctx.scene.enter(HELLO_SCENE_ID);
@Command('admin')
@UseGuards(AdminGuard)
onAdminCommand(): string {
return 'Welcome judge';
}
@On('message')
async onMessage(ctx: Context): Promise<void> {
console.log('New message received');
if ('text' in ctx.message) {
const messageText = ctx.message.text;
const echoText = this.echoService.echo(messageText);
await ctx.reply(echoText);
} else {
await ctx.reply('Only text messages');
}
@On('text')
onMessage(
@Message('text', new ReverseTextPipe()) reversedText: string,
): string {
return this.echoService.echo(reversedText);
}
}

View File

@ -1,7 +1,8 @@
import { Module } from '@nestjs/common';
import { GreeterUpdate } from './greeter.update';
import { RandomNumberScene } from './scenes/random-number.scene';
@Module({
providers: [GreeterUpdate],
providers: [GreeterUpdate, RandomNumberScene],
})
export class GreeterModule {}

View File

@ -1,16 +1,26 @@
import { Hears, Start, Update } from '../../../../lib';
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()
async onStart(ctx: Context): Promise<void> {
await ctx.reply('Say hello to me');
onStart(): string {
return 'Say hello to me';
}
@Hears(['hi', 'hello', 'hey', 'qq'])
async onGreetings(ctx: Context): Promise<void> {
const { first_name } = ctx.from;
await ctx.reply(`Hey ${first_name}`);
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);
}
}

View File

@ -3,23 +3,23 @@ import { HELLO_SCENE_ID } from '../../app.constants';
import { Context } from '../../interfaces/context.interface';
@Scene(HELLO_SCENE_ID)
export class HelloScene {
export class RandomNumberScene {
@SceneEnter()
async onSceneEnter(ctx: Context): Promise<void> {
onSceneEnter(): string {
console.log('Enter to scene');
await ctx.reply('Welcome on scene ✋');
return 'Welcome on scene ✋';
}
@SceneLeave()
async onSceneLeave(ctx: Context): Promise<void> {
onSceneLeave(): string {
console.log('Leave from scene');
await ctx.reply('Bye Bye 👋');
return 'Bye Bye 👋';
}
@Command('hello')
async onHelloCommand(ctx: Context): Promise<void> {
console.log('Use say hello');
await ctx.reply('Hi');
@Command(['rng', 'random'])
onRandomCommand(): number {
console.log('Use "random" command');
return Math.floor(Math.random() * 11);
}
@Command('leave')

View File

@ -12,6 +12,7 @@ The described functionality is under development, the functionality has not been
### Update
`@Update` class decorator, it's like NestJS [`@Controller`](https://docs.nestjs.com/controllers) decorator, but for [Telegram Bot API updates](https://core.telegram.org/bots/api#getting-updates).
It is required for the class that will receive updates from Telegram.
```typescript {3}
import { Update, Context } from 'nestjs-telegraf';

View File

@ -18,8 +18,8 @@ import {
Help,
On,
Hears,
Context,
} from 'nestjs-telegraf';
import { Context } from 'telegraf';
@Injectable()
export class AppService {