feat(): wip

This commit is contained in:
Morb0 2021-01-05 12:37:04 +03:00
parent 320ee3077e
commit 75ff810c34
7 changed files with 98 additions and 70 deletions

View File

@ -1,17 +1,19 @@
import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; 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 { DiscoveryService, ModuleRef, ModulesContainer } from '@nestjs/core';
import { MetadataScanner } from '@nestjs/core/metadata-scanner'; import { MetadataScanner } from '@nestjs/core/metadata-scanner';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper'; import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import { isFunction, isNil } from '@nestjs/common/utils/shared.utils'; 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 { fromPromise } from 'rxjs/internal-compatibility';
import { filter, mergeAll } from 'rxjs/operators'; import { filter, mergeAll } from 'rxjs/operators';
import { Observable, of } from 'rxjs'; import { Observable, of } from 'rxjs';
import { Context, Telegraf } from 'telegraf'; import { Context, Telegraf } from 'telegraf';
import { TelegrafMetadataAccessor } from '../telegraf.metadata-accessor'; import { TelegrafMetadataAccessor } from '../telegraf.metadata-accessor';
import { ExternalContextCreator } from '@nestjs/core/helpers/external-context-creator';
import { TelegrafParamsFactory } from '../factories/telegraf-params-factory'; import { TelegrafParamsFactory } from '../factories/telegraf-params-factory';
// TODO: DELETE THIS CLASS
@Injectable() @Injectable()
export class TelegrafUpdateExplorer implements OnModuleInit { export class TelegrafUpdateExplorer implements OnModuleInit {
private readonly telegrafParamsFactory = new TelegrafParamsFactory(); private readonly telegrafParamsFactory = new TelegrafParamsFactory();
@ -37,7 +39,7 @@ export class TelegrafUpdateExplorer implements OnModuleInit {
} }
private exploreProviders( private exploreProviders(
providers: Map<string, InstanceWrapper<IInjectable>>, providers: Map<string, InstanceWrapper<unknown>>,
moduleName: string, moduleName: string,
): void { ): void {
[...providers.values()] [...providers.values()]

View File

@ -1,15 +1,18 @@
import { ParamsFactory } from '@nestjs/core/helpers/external-context-creator';
import { Context } from 'telegraf';
import { TelegrafParamtype } from '../enums/telegraf-paramtype.enum'; import { TelegrafParamtype } from '../enums/telegraf-paramtype.enum';
export class TelegrafParamsFactory { export class TelegrafParamsFactory implements ParamsFactory {
exchangeKeyForValue< exchangeKeyForValue(
TContext extends Record<string, any> = any, type: TelegrafParamtype,
TResult = any ctx: Context,
>(type: number, ctx: TContext, next: Function): TResult { next: Function,
switch (type as TelegrafParamtype) { ): unknown {
switch (type) {
case TelegrafParamtype.CONTEXT: case TelegrafParamtype.CONTEXT:
return ctx as any; return ctx;
case TelegrafParamtype.NEXT: case TelegrafParamtype.NEXT:
return next as any; return next;
case TelegrafParamtype.SENDER: case TelegrafParamtype.SENDER:
return ctx.from; return ctx.from;
case TelegrafParamtype.MESSAGE: case TelegrafParamtype.MESSAGE:

View File

@ -6,17 +6,30 @@ import { Module } from '@nestjs/core/injector/module';
import { BaseScene, Composer, Stage, Telegraf } from 'telegraf'; import { BaseScene, Composer, Stage, Telegraf } from 'telegraf';
import { MetadataAccessorService } from './metadata-accessor.service'; import { MetadataAccessorService } from './metadata-accessor.service';
import { TELEGRAF_MODULE_OPTIONS } from '../telegraf.constants'; import {
import { TelegrafModuleOptions } from '../interfaces'; PARAM_ARGS_METADATA,
TELEGRAF_MODULE_OPTIONS,
} from '../telegraf.constants';
import { ListenerMetadata, TelegrafModuleOptions } from '../interfaces';
import { BaseExplorerService } from './base-explorer.service'; import { BaseExplorerService } from './base-explorer.service';
import { getBotToken } from '../utils'; 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() @Injectable()
export class ListenersExplorerService export class ListenersExplorerService
extends BaseExplorerService extends BaseExplorerService
implements OnModuleInit { implements OnModuleInit {
private readonly telegrafParamsFactory = new TelegrafParamsFactory();
private readonly stage = new Stage([]); private readonly stage = new Stage([]);
private bot: Telegraf<any>; private bot: Telegraf<never>;
constructor( constructor(
@Inject(TELEGRAF_MODULE_OPTIONS) @Inject(TELEGRAF_MODULE_OPTIONS)
@ -26,6 +39,7 @@ export class ListenersExplorerService
private readonly metadataAccessor: MetadataAccessorService, private readonly metadataAccessor: MetadataAccessorService,
private readonly metadataScanner: MetadataScanner, private readonly metadataScanner: MetadataScanner,
private readonly modulesContainer: ModulesContainer, private readonly modulesContainer: ModulesContainer,
private readonly externalContextCreator: ExternalContextCreator,
) { ) {
super(); super();
} }
@ -58,17 +72,18 @@ export class ListenersExplorerService
} }
private registerScenes(modules: Module[]): void { private registerScenes(modules: Module[]): void {
const scenes = this.flatMap<InstanceWrapper>(modules, (instance) => const scenes = this.flatMap<InstanceWrapper>(
this.filterScenes(instance), modules,
(instance, moduleRef) => this.filterScenes(instance),
); );
scenes.forEach(({ instance }) => { scenes.forEach((wrapper) => {
const sceneId = this.metadataAccessor.getSceneMetadata( const sceneId = this.metadataAccessor.getSceneMetadata(
instance.constructor, instance.constructor,
); );
const scene = new BaseScene(sceneId); const scene = new BaseScene(sceneId);
this.stage.register(scene); this.stage.register(scene);
this.registerInstanceMethodListeners(scene, instance); this.registerInstanceMethodListeners(scene, wrapper);
}); });
} }
@ -94,18 +109,63 @@ export class ListenersExplorerService
private registerInstanceMethodListeners( private registerInstanceMethodListeners(
composer: Composer<never>, composer: Composer<never>,
instance: Record<string, Function>, wrapper: InstanceWrapper<unknown>,
): void { ): void {
const { instance } = wrapper;
const prototype = Object.getPrototypeOf(instance); const prototype = Object.getPrototypeOf(instance);
this.metadataScanner.scanFromPrototype(instance, prototype, (name) => { const listenersMetadata = this.metadataScanner.scanFromPrototype(
const methodRef = instance[name]; instance,
prototype,
(name): ListenerCallbackMetadata =>
this.extractListenerCallbackMetadata(prototype, name),
);
const metadata = this.metadataAccessor.getListenerMetadata(methodRef); const contextCallbackFn = this.createContextCallback(
if (!metadata) return; instance,
prototype,
wrapper,
);
}
const middlewareFn = methodRef.bind(instance); private extractListenerCallbackMetadata(
const { method, args } = metadata; prototype: any,
composer[method](...args, middlewareFn); methodName: string,
}); ): ListenerCallbackMetadata {
const callback = prototype[methodName];
const metadata = this.metadataAccessor.getListenerMetadata(callback);
if (!metadata) {
return undefined;
}
return {
methodName,
metadata: metadata,
};
}
createContextCallback<T extends Record<string, unknown>>(
instance: T,
prototype: unknown,
wrapper: InstanceWrapper,
moduleRef: Module,
listener: ListenerMetadata,
) {
const paramsFactory = this.telegrafParamsFactory;
const resolverCallback = this.externalContextCreator.create<
Record<number, ParamMetadata>,
TelegrafContextType
>(
instance,
prototype[listener.methodName],
listener.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 { Reflector } from '@nestjs/core';
import { import {
SCENE_METADATA, SCENE_METADATA,
UPDATE_LISTENER_METADATA, LISTENER_METADATA,
UPDATE_METADATA, UPDATE_METADATA,
} from '../telegraf.constants'; } from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces'; import { ListenerMetadata } from '../interfaces';
@ -27,7 +27,7 @@ export class MetadataAccessorService {
} }
getListenerMetadata(target: Function): ListenerMetadata | undefined { 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 { getSceneMetadata(target: Function): string | undefined {

View File

@ -4,8 +4,7 @@ export const TELEGRAF_MODULE_OPTIONS = 'TELEGRAF_MODULE_OPTIONS';
export const DEFAULT_BOT_NAME = 'DEFAULT_BOT_NAME'; export const DEFAULT_BOT_NAME = 'DEFAULT_BOT_NAME';
export const UPDATE_METADATA = 'UPDATE_METADATA'; export const UPDATE_METADATA = 'UPDATE_METADATA';
export const UPDATE_LISTENER_METADATA = 'UPDATE_LISTENER_METADATA';
export const SCENE_METADATA = 'SCENE_METADATA'; export const SCENE_METADATA = 'SCENE_METADATA';
export const LISTENER_METADATA = 'LISTENER_METADATA';
export const PARAM_ARGS_METADATA = ROUTE_ARGS_METADATA; export const PARAM_ARGS_METADATA = ROUTE_ARGS_METADATA;

View File

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

View File

@ -48,39 +48,3 @@ export const addPipesMetadata = (
key, key,
); );
}; };
// export function createTelegrafParamDecorator(
// paramtype: TelegrafParamtype,
// ): (...pipes: (Type<PipeTransform> | PipeTransform)[]) => ParameterDecorator {
// return (...pipes: (Type<PipeTransform> | 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> | 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,
// );
// };