From 75ff810c34b713133f73c637724c848f56207d65 Mon Sep 17 00:00:00 2001 From: Morb0 Date: Tue, 5 Jan 2021 12:37:04 +0300 Subject: [PATCH] feat(): wip --- lib/explorers/telegraf-update.explorer.ts | 8 +- lib/factories/telegraf-params-factory.ts | 19 +++-- lib/services/listeners-explorer.service.ts | 92 +++++++++++++++++---- lib/services/metadata-accessor.service.ts | 4 +- lib/telegraf.constants.ts | 3 +- lib/utils/create-listener-decorator.util.ts | 6 +- lib/utils/param-decorator.util.ts | 36 -------- 7 files changed, 98 insertions(+), 70 deletions(-) diff --git a/lib/explorers/telegraf-update.explorer.ts b/lib/explorers/telegraf-update.explorer.ts index 1246509..9a7ac43 100644 --- a/lib/explorers/telegraf-update.explorer.ts +++ b/lib/explorers/telegraf-update.explorer.ts @@ -1,17 +1,19 @@ import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; -import { Injectable as IInjectable } from '@nestjs/common/interfaces/injectable.interface'; import { DiscoveryService, ModuleRef, ModulesContainer } from '@nestjs/core'; import { MetadataScanner } from '@nestjs/core/metadata-scanner'; import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; +import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator'; import { fromPromise } from 'rxjs/internal-compatibility'; import { filter, mergeAll } from 'rxjs/operators'; import { Observable, of } from 'rxjs'; import { Context, Telegraf } from 'telegraf'; + import { TelegrafMetadataAccessor } from '../telegraf.metadata-accessor'; -import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator'; import { TelegrafParamsFactory } from '../factories/telegraf-params-factory'; +// TODO: DELETE THIS CLASS + @Injectable() export class TelegrafUpdateExplorer implements OnModuleInit { private readonly telegrafParamsFactory = new TelegrafParamsFactory(); @@ -37,7 +39,7 @@ export class TelegrafUpdateExplorer implements OnModuleInit { } private exploreProviders( - providers: Map>, + providers: Map>, moduleName: string, ): void { [...providers.values()] diff --git a/lib/factories/telegraf-params-factory.ts b/lib/factories/telegraf-params-factory.ts index 7f9adbf..df4b7e0 100644 --- a/lib/factories/telegraf-params-factory.ts +++ b/lib/factories/telegraf-params-factory.ts @@ -1,15 +1,18 @@ +import { ParamsFactory } from '@nestjs/core/helpers/external-context-creator'; +import { Context } from 'telegraf'; import { TelegrafParamtype } from '../enums/telegraf-paramtype.enum'; -export class TelegrafParamsFactory { - exchangeKeyForValue< - TContext extends Record = any, - TResult = any - >(type: number, ctx: TContext, next: Function): TResult { - switch (type as TelegrafParamtype) { +export class TelegrafParamsFactory implements ParamsFactory { + exchangeKeyForValue( + type: TelegrafParamtype, + ctx: Context, + next: Function, + ): unknown { + switch (type) { case TelegrafParamtype.CONTEXT: - return ctx as any; + return ctx; case TelegrafParamtype.NEXT: - return next as any; + return next; case TelegrafParamtype.SENDER: return ctx.from; case TelegrafParamtype.MESSAGE: diff --git a/lib/services/listeners-explorer.service.ts b/lib/services/listeners-explorer.service.ts index eb0b789..05abc1e 100644 --- a/lib/services/listeners-explorer.service.ts +++ b/lib/services/listeners-explorer.service.ts @@ -6,17 +6,30 @@ import { Module } from '@nestjs/core/injector/module'; import { BaseScene, Composer, Stage, Telegraf } from 'telegraf'; import { MetadataAccessorService } from './metadata-accessor.service'; -import { TELEGRAF_MODULE_OPTIONS } from '../telegraf.constants'; -import { TelegrafModuleOptions } from '../interfaces'; +import { + PARAM_ARGS_METADATA, + TELEGRAF_MODULE_OPTIONS, +} from '../telegraf.constants'; +import { ListenerMetadata, TelegrafModuleOptions } from '../interfaces'; import { BaseExplorerService } from './base-explorer.service'; import { getBotToken } from '../utils'; +import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator'; +import { TelegrafParamsFactory } from '../factories/telegraf-params-factory'; +import { TelegrafContextType } from '../execution-context/telegraf-execution-context'; +import { ParamMetadata } from '@nestjs/core/helpers/interfaces'; + +interface ListenerCallbackMetadata { + methodName: string; + metadata: ListenerMetadata; +} @Injectable() export class ListenersExplorerService extends BaseExplorerService implements OnModuleInit { + private readonly telegrafParamsFactory = new TelegrafParamsFactory(); private readonly stage = new Stage([]); - private bot: Telegraf; + private bot: Telegraf; constructor( @Inject(TELEGRAF_MODULE_OPTIONS) @@ -26,6 +39,7 @@ export class ListenersExplorerService private readonly metadataAccessor: MetadataAccessorService, private readonly metadataScanner: MetadataScanner, private readonly modulesContainer: ModulesContainer, + private readonly externalContextCreator: ExternalContextCreator, ) { super(); } @@ -58,17 +72,18 @@ export class ListenersExplorerService } private registerScenes(modules: Module[]): void { - const scenes = this.flatMap(modules, (instance) => - this.filterScenes(instance), + const scenes = this.flatMap( + modules, + (instance, moduleRef) => this.filterScenes(instance), ); - scenes.forEach(({ instance }) => { + scenes.forEach((wrapper) => { const sceneId = this.metadataAccessor.getSceneMetadata( instance.constructor, ); const scene = new BaseScene(sceneId); this.stage.register(scene); - this.registerInstanceMethodListeners(scene, instance); + this.registerInstanceMethodListeners(scene, wrapper); }); } @@ -94,18 +109,63 @@ export class ListenersExplorerService private registerInstanceMethodListeners( composer: Composer, - instance: Record, + wrapper: InstanceWrapper, ): void { + const { instance } = wrapper; const prototype = Object.getPrototypeOf(instance); - this.metadataScanner.scanFromPrototype(instance, prototype, (name) => { - const methodRef = instance[name]; + const listenersMetadata = this.metadataScanner.scanFromPrototype( + instance, + prototype, + (name): ListenerCallbackMetadata => + this.extractListenerCallbackMetadata(prototype, name), + ); - const metadata = this.metadataAccessor.getListenerMetadata(methodRef); - if (!metadata) return; + const contextCallbackFn = this.createContextCallback( + instance, + prototype, + wrapper, + ); + } - const middlewareFn = methodRef.bind(instance); - const { method, args } = metadata; - composer[method](...args, middlewareFn); - }); + private extractListenerCallbackMetadata( + prototype: any, + methodName: string, + ): ListenerCallbackMetadata { + const callback = prototype[methodName]; + const metadata = this.metadataAccessor.getListenerMetadata(callback); + + if (!metadata) { + return undefined; + } + + return { + methodName, + metadata: metadata, + }; + } + + createContextCallback>( + instance: T, + prototype: unknown, + wrapper: InstanceWrapper, + moduleRef: Module, + listener: ListenerMetadata, + ) { + const paramsFactory = this.telegrafParamsFactory; + const resolverCallback = this.externalContextCreator.create< + Record, + TelegrafContextType + >( + instance, + prototype[listener.methodName], + listener.methodName, + PARAM_ARGS_METADATA, + paramsFactory, + undefined, + undefined, + undefined, + 'telegraf', + ); + return resolverCallback; } } diff --git a/lib/services/metadata-accessor.service.ts b/lib/services/metadata-accessor.service.ts index 322899a..62a785d 100644 --- a/lib/services/metadata-accessor.service.ts +++ b/lib/services/metadata-accessor.service.ts @@ -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'; @@ -27,7 +27,7 @@ export class MetadataAccessorService { } 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 { diff --git a/lib/telegraf.constants.ts b/lib/telegraf.constants.ts index bbff974..b7d4c7d 100644 --- a/lib/telegraf.constants.ts +++ b/lib/telegraf.constants.ts @@ -4,8 +4,7 @@ export const TELEGRAF_MODULE_OPTIONS = 'TELEGRAF_MODULE_OPTIONS'; 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; diff --git a/lib/utils/create-listener-decorator.util.ts b/lib/utils/create-listener-decorator.util.ts index de482fd..b880fcb 100644 --- a/lib/utils/create-listener-decorator.util.ts +++ b/lib/utils/create-listener-decorator.util.ts @@ -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( @@ -10,7 +10,7 @@ export function createListenerDecorator( return ( ...args: ComposerMethodArgs, TMethod> ): MethodDecorator => { - return SetMetadata(UPDATE_LISTENER_METADATA, { + return SetMetadata(LISTENER_METADATA, { method, args, } as ListenerMetadata); @@ -21,7 +21,7 @@ export function createMissedListenerDecorator( method: string, ) { return (...args: TArgs): MethodDecorator => { - return SetMetadata(UPDATE_LISTENER_METADATA, { + return SetMetadata(LISTENER_METADATA, { method, args, } as ListenerMetadata); diff --git a/lib/utils/param-decorator.util.ts b/lib/utils/param-decorator.util.ts index 986ab90..6a2d261 100644 --- a/lib/utils/param-decorator.util.ts +++ b/lib/utils/param-decorator.util.ts @@ -48,39 +48,3 @@ export const addPipesMetadata = ( key, ); }; - -// export function createTelegrafParamDecorator( -// paramtype: TelegrafParamtype, -// ): (...pipes: (Type | PipeTransform)[]) => ParameterDecorator { -// return (...pipes: (Type | PipeTransform)[]) => ( -// target, -// key, -// index, -// ) => { -// const args = -// Reflect.getMetadata(LISTENER_ARGS_METADATA, target.constructor, key) || -// {}; -// Reflect.defineMetadata( -// LISTENER_ARGS_METADATA, -// assignMetadata(args, paramtype, index, undefined, ...pipes), -// target.constructor, -// key, -// ); -// }; -// } -// -// export const createPipesTelegrafParamDecorator = ( -// paramtype: TelegrafParamtype, -// ) => ( -// ...pipes: (Type | PipeTransform)[] -// ): ParameterDecorator => (target, key, index) => { -// const args = -// Reflect.getMetadata(LISTENER_ARGS_METADATA, target.constructor, key) || {}; -// -// Reflect.defineMetadata( -// LISTENER_ARGS_METADATA, -// assignMetadata(args, paramtype, index, undefined, ...pipes), -// target.constructor, -// key, -// ); -// };