mirror of
https://github.com/Maks1mS/nestjs-telegraf.git
synced 2025-09-23 09:49:06 +03:00
merge refactor/v2
This commit is contained in:
37
lib/services/base-explorer.service.ts
Normal file
37
lib/services/base-explorer.service.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
|
||||
import { Module } from '@nestjs/core/injector/module';
|
||||
import { flattenDeep, identity, isEmpty } from 'lodash';
|
||||
|
||||
export class BaseExplorerService {
|
||||
getModules(
|
||||
modulesContainer: Map<string, Module>,
|
||||
include: Function[],
|
||||
): Module[] {
|
||||
if (!include || isEmpty(include)) {
|
||||
return [...modulesContainer.values()];
|
||||
}
|
||||
const whitelisted = this.includeWhitelisted(modulesContainer, include);
|
||||
return whitelisted;
|
||||
}
|
||||
|
||||
includeWhitelisted(
|
||||
modulesContainer: Map<string, Module>,
|
||||
include: Function[],
|
||||
): Module[] {
|
||||
const modules = [...modulesContainer.values()];
|
||||
return modules.filter(({ metatype }) => include.includes(metatype));
|
||||
}
|
||||
|
||||
flatMap<T>(
|
||||
modules: Module[],
|
||||
callback: (instance: InstanceWrapper, moduleRef: Module) => T | T[],
|
||||
): T[] {
|
||||
const invokeMap = () => {
|
||||
return modules.map((moduleRef) => {
|
||||
const providers = [...moduleRef.providers.values()];
|
||||
return providers.map((wrapper) => callback(wrapper, moduleRef));
|
||||
});
|
||||
};
|
||||
return flattenDeep(invokeMap()).filter(identity);
|
||||
}
|
||||
}
|
3
lib/services/index.ts
Normal file
3
lib/services/index.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export * from './listeners-explorer.service';
|
||||
export * from './metadata-accessor.service';
|
||||
export * from './listeners-explorer.service';
|
111
lib/services/listeners-explorer.service.ts
Normal file
111
lib/services/listeners-explorer.service.ts
Normal file
@@ -0,0 +1,111 @@
|
||||
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
|
||||
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 { MetadataAccessorService } from './metadata-accessor.service';
|
||||
import { TELEGRAF_MODULE_OPTIONS } from '../telegraf.constants';
|
||||
import { TelegrafModuleOptions } from '../interfaces';
|
||||
import { BaseExplorerService } from './base-explorer.service';
|
||||
import { getBotToken } from '../utils';
|
||||
|
||||
@Injectable()
|
||||
export class ListenersExplorerService
|
||||
extends BaseExplorerService
|
||||
implements OnModuleInit {
|
||||
private readonly stage = new Stage([]);
|
||||
private bot: Telegraf<any>;
|
||||
|
||||
constructor(
|
||||
@Inject(TELEGRAF_MODULE_OPTIONS)
|
||||
private readonly telegrafOptions: TelegrafModuleOptions,
|
||||
private readonly moduleRef: ModuleRef,
|
||||
private readonly discoveryService: DiscoveryService,
|
||||
private readonly metadataAccessor: MetadataAccessorService,
|
||||
private readonly metadataScanner: MetadataScanner,
|
||||
private readonly modulesContainer: ModulesContainer,
|
||||
) {
|
||||
super();
|
||||
}
|
||||
|
||||
onModuleInit(): void {
|
||||
const botToken = getBotToken(this.telegrafOptions.name);
|
||||
this.bot = this.moduleRef.get<Telegraf<never>>(botToken);
|
||||
this.bot.use(this.stage.middleware());
|
||||
|
||||
this.explore();
|
||||
}
|
||||
|
||||
explore(): void {
|
||||
const modules = this.getModules(
|
||||
this.modulesContainer,
|
||||
this.telegrafOptions.include || [],
|
||||
);
|
||||
|
||||
this.registerUpdates(modules);
|
||||
this.registerScenes(modules);
|
||||
}
|
||||
|
||||
private registerUpdates(modules: Module[]): void {
|
||||
const updates = this.flatMap<InstanceWrapper>(modules, (instance) =>
|
||||
this.filterUpdates(instance),
|
||||
);
|
||||
updates.forEach(({ instance }) =>
|
||||
this.registerInstanceMethodListeners(this.bot, instance),
|
||||
);
|
||||
}
|
||||
|
||||
private registerScenes(modules: Module[]): void {
|
||||
const scenes = this.flatMap<InstanceWrapper>(modules, (instance) =>
|
||||
this.filterScenes(instance),
|
||||
);
|
||||
scenes.forEach(({ instance }) => {
|
||||
const sceneId = this.metadataAccessor.getSceneMetadata(
|
||||
instance.constructor,
|
||||
);
|
||||
const scene = new BaseScene(sceneId);
|
||||
this.stage.register(scene);
|
||||
|
||||
this.registerInstanceMethodListeners(scene, instance);
|
||||
});
|
||||
}
|
||||
|
||||
private filterUpdates(wrapper: InstanceWrapper): InstanceWrapper<unknown> {
|
||||
const { instance } = wrapper;
|
||||
if (!instance) return undefined;
|
||||
|
||||
const isUpdate = this.metadataAccessor.isUpdate(wrapper.metatype);
|
||||
if (!isUpdate) return undefined;
|
||||
|
||||
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 registerInstanceMethodListeners(
|
||||
composer: Composer<never>,
|
||||
instance: Record<string, Function>,
|
||||
): void {
|
||||
const prototype = Object.getPrototypeOf(instance);
|
||||
this.metadataScanner.scanFromPrototype(instance, prototype, (name) => {
|
||||
const methodRef = instance[name];
|
||||
|
||||
const metadata = this.metadataAccessor.getListenerMetadata(methodRef);
|
||||
if (!metadata) return;
|
||||
|
||||
const middlewareFn = methodRef.bind(instance);
|
||||
const { method, args } = metadata;
|
||||
composer[method](...args, middlewareFn);
|
||||
});
|
||||
}
|
||||
}
|
36
lib/services/metadata-accessor.service.ts
Normal file
36
lib/services/metadata-accessor.service.ts
Normal file
@@ -0,0 +1,36 @@
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { Reflector } from '@nestjs/core';
|
||||
import {
|
||||
SCENE_METADATA,
|
||||
UPDATE_LISTENER_METADATA,
|
||||
UPDATE_METADATA,
|
||||
} from '../telegraf.constants';
|
||||
import { ListenerMetadata } from '../interfaces';
|
||||
|
||||
@Injectable()
|
||||
export class MetadataAccessorService {
|
||||
constructor(private readonly reflector: Reflector) {}
|
||||
|
||||
isUpdate(target: Function): boolean {
|
||||
// TODO: We really need this check?
|
||||
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(UPDATE_LISTENER_METADATA, target);
|
||||
}
|
||||
|
||||
getSceneMetadata(target: Function): string | undefined {
|
||||
return this.reflector.get(SCENE_METADATA, target);
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user