feat(): add middleware support & refactor

This commit is contained in:
Arthur 2020-12-30 01:49:09 +03:00
parent 306f239603
commit 3e585859d0
35 changed files with 105 additions and 81 deletions

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers middleware for handling callback_data actions with regular expressions. * Registers middleware for handling callback_data actions with regular expressions.
* *
* @see https://telegraf.js.org/#/?id=action * @see https://telegraf.js.org/#/?id=action
*/ */
export const Action = createUpdateDecorator('action'); export const Action = createUpdateListenerDecorator('action');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Cashtag handling. * Cashtag handling.
* *
* @see https://telegraf.js.org/#/?id=cashtag * @see https://telegraf.js.org/#/?id=cashtag
*/ */
export const Cashtag = createUpdateDecorator('cashtag'); export const Cashtag = createUpdateListenerDecorator('cashtag');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Command handling. * Command handling.
* *
* @see https://telegraf.js.org/#/?id=command * @see https://telegraf.js.org/#/?id=command
*/ */
export const Command = createUpdateDecorator('command'); export const Command = createUpdateListenerDecorator('command');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers middleware for handling messages with email entity. * Registers middleware for handling messages with email entity.
* *
* @see https://telegraf.js.org/#/?id=telegraf-email * @see https://telegraf.js.org/#/?id=telegraf-email
*/ */
export const Email = createUpdateDecorator('email'); export const Email = createUpdateListenerDecorator('email');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers middleware for handling callback_data actions with game query. * Registers middleware for handling callback_data actions with game query.
* *
* @see https://telegraf.js.org/#/?id=inlinequery * @see https://telegraf.js.org/#/?id=inlinequery
*/ */
export const GameQuery = createUpdateDecorator('gameQuery'); export const GameQuery = createUpdateListenerDecorator('gameQuery');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Hashtag handling. * Hashtag handling.
* *
* @see https://telegraf.js.org/#/?id=hashtag * @see https://telegraf.js.org/#/?id=hashtag
*/ */
export const Hashtag = createUpdateDecorator('hashtag'); export const Hashtag = createUpdateListenerDecorator('hashtag');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers middleware for handling text messages. * Registers middleware for handling text messages.
* *
* @see https://telegraf.js.org/#/?id=hears * @see https://telegraf.js.org/#/?id=hears
*/ */
export const Hears = createUpdateDecorator('hears'); export const Hears = createUpdateListenerDecorator('hears');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Handler for /help command. * Handler for /help command.
* *
* @see https://telegraf.js.org/#/?id=help * @see https://telegraf.js.org/#/?id=help
*/ */
export const Help = createUpdateDecorator('help'); export const Help = createUpdateListenerDecorator('help');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers middleware for handling inline_query actions with regular expressions. * Registers middleware for handling inline_query actions with regular expressions.
* *
* @see https://telegraf.js.org/#/?id=inlinequery * @see https://telegraf.js.org/#/?id=inlinequery
*/ */
export const InlineQuery = createUpdateDecorator('inlineQuery'); export const InlineQuery = createUpdateListenerDecorator('inlineQuery');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Mention handling. * Mention handling.
* *
* @see https://telegraf.js.org/#/?id=mention * @see https://telegraf.js.org/#/?id=mention
*/ */
export const Mention = createUpdateDecorator('mention'); export const Mention = createUpdateListenerDecorator('mention');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers middleware for provided update type. * Registers middleware for provided update type.
* *
* @see https://telegraf.js.org/#/?id=on * @see https://telegraf.js.org/#/?id=on
*/ */
export const On = createUpdateDecorator('on'); export const On = createUpdateListenerDecorator('on');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Phone number handling. * Phone number handling.
* *
* @see https://telegraf.js.org/#/?id=phone * @see https://telegraf.js.org/#/?id=phone
*/ */
export const Phone = createUpdateDecorator('phone'); export const Phone = createUpdateListenerDecorator('phone');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Handler for /settings command. * Handler for /settings command.
* *
* @see https://telegraf.js.org/#/?id=settings * @see https://telegraf.js.org/#/?id=settings
*/ */
export const Settings = createUpdateDecorator('settings'); export const Settings = createUpdateListenerDecorator('settings');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Handler for /start command. * Handler for /start command.
* *
* @see https://telegraf.js.org/#/?id=start * @see https://telegraf.js.org/#/?id=start
*/ */
export const Start = createUpdateDecorator('start'); export const Start = createUpdateListenerDecorator('start');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers middleware for handling messages with text_link entity. * Registers middleware for handling messages with text_link entity.
* *
* @see https://telegraf.js.org/#/?id=telegraf-textlink * @see https://telegraf.js.org/#/?id=telegraf-textlink
*/ */
export const TextLink = createUpdateDecorator('textLink'); export const TextLink = createUpdateListenerDecorator('textLink');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers middleware for handling messages with text_mention entity. * Registers middleware for handling messages with text_mention entity.
* *
* @see https://telegraf.js.org/#/?id=telegraf-textlink * @see https://telegraf.js.org/#/?id=telegraf-textlink
*/ */
export const TextMention = createUpdateDecorator('textMention'); export const TextMention = createUpdateListenerDecorator('textMention');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers middleware for handling messages with url entity. * Registers middleware for handling messages with url entity.
* *
* @see https://telegraf.js.org/#/?id=telegraf-url * @see https://telegraf.js.org/#/?id=telegraf-url
*/ */
export const Url = createUpdateDecorator('url'); export const Url = createUpdateListenerDecorator('url');

View File

@ -1,8 +1,8 @@
import { createUpdateDecorator } from '../../helpers/create-update-decorator.helper'; import { createUpdateListenerDecorator } from '../../helpers';
/** /**
* Registers a middleware. * Registers a middleware.
* *
* @see https://telegraf.js.org/#/?id=use * @see https://telegraf.js.org/#/?id=use
*/ */
export const Use = createUpdateDecorator('use'); export const Use = createUpdateListenerDecorator('use');

View File

@ -1,6 +1,3 @@
import { SetMetadata } from '@nestjs/common'; import { createSceneListenerDecorator } from '../../helpers';
import { SCENE_LISTENER_METADATA } from '../../telegraf.constants';
import { SceneEventType } from '../../enums/scene-event-type.enum';
export const SceneEnter = (): MethodDecorator => export const SceneEnter = createSceneListenerDecorator('enter');
SetMetadata(SCENE_LISTENER_METADATA, SceneEventType.Enter);

View File

@ -1,6 +1,3 @@
import { SetMetadata } from '@nestjs/common'; import { createSceneListenerDecorator } from '../../helpers';
import { SCENE_LISTENER_METADATA } from '../../telegraf.constants';
import { SceneEventType } from '../../enums/scene-event-type.enum';
export const SceneLeave = (): MethodDecorator => export const SceneLeave = createSceneListenerDecorator('leave');
SetMetadata(SCENE_LISTENER_METADATA, SceneEventType.Leave);

View File

@ -1,4 +0,0 @@
export enum SceneEventType {
Enter = 'enter',
Leave = 'leave',
}

View File

@ -3,17 +3,21 @@ import { DiscoveryService } 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 { BaseScene as Scene, Stage, Telegraf } from 'telegraf'; import { BaseScene as Scene, Stage, Telegraf } from 'telegraf';
import { TelegrafMetadataAccessor } from './telegraf.metadata-accessor'; import { TelegrafMetadataAccessor } from '../telegraf.metadata-accessor';
@Injectable() @Injectable()
export class TelegrafSceneExplorer implements OnModuleInit { export class TelegrafSceneExplorer implements OnModuleInit {
private readonly stage = new Stage();
constructor( constructor(
@Inject(Telegraf) @Inject(Telegraf)
private readonly telegraf: Telegraf, private readonly telegraf: Telegraf,
private readonly discoveryService: DiscoveryService, private readonly discoveryService: DiscoveryService,
private readonly metadataAccessor: TelegrafMetadataAccessor, private readonly metadataAccessor: TelegrafMetadataAccessor,
private readonly metadataScanner: MetadataScanner, private readonly metadataScanner: MetadataScanner,
) {} ) {
this.telegraf.use(this.stage.middleware());
}
onModuleInit(): void { onModuleInit(): void {
this.explore(); this.explore();
@ -21,7 +25,6 @@ export class TelegrafSceneExplorer implements OnModuleInit {
private explore(): void { private explore(): void {
const sceneClasses = this.filterSceneClasses(); const sceneClasses = this.filterSceneClasses();
const stage = new Stage();
sceneClasses.forEach((wrapper) => { sceneClasses.forEach((wrapper) => {
const { instance } = wrapper; const { instance } = wrapper;
@ -30,7 +33,7 @@ export class TelegrafSceneExplorer implements OnModuleInit {
instance.constructor, instance.constructor,
); );
const scene = new Scene(sceneId); const scene = new Scene(sceneId);
stage.register(scene); this.stage.register(scene);
const prototype = Object.getPrototypeOf(instance); const prototype = Object.getPrototypeOf(instance);
this.metadataScanner.scanFromPrototype( this.metadataScanner.scanFromPrototype(
@ -39,11 +42,7 @@ export class TelegrafSceneExplorer implements OnModuleInit {
(methodKey: string) => (methodKey: string) =>
this.registerIfListener(scene, instance, methodKey), this.registerIfListener(scene, instance, methodKey),
); );
stage.register(scene);
}); });
this.telegraf.use(stage.middleware());
} }
private filterSceneClasses(): InstanceWrapper[] { private filterSceneClasses(): InstanceWrapper[] {

View File

@ -3,7 +3,7 @@ import { DiscoveryService } 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 { Telegraf } from 'telegraf'; import { Telegraf } from 'telegraf';
import { TelegrafMetadataAccessor } from './telegraf.metadata-accessor'; import { TelegrafMetadataAccessor } from '../telegraf.metadata-accessor';
@Injectable() @Injectable()
export class TelegrafUpdateExplorer implements OnModuleInit { export class TelegrafUpdateExplorer implements OnModuleInit {

View File

@ -0,0 +1,18 @@
import { SetMetadata } from '@nestjs/common';
import { BaseScene as Scene } from 'telegraf';
import { ComposerMethodArgs, SceneMethods } from '../telegraf.types';
import { UPDATE_LISTENER_METADATA } from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces';
export function createSceneListenerDecorator<Method extends SceneMethods>(
method: Method,
) {
return (
...args: ComposerMethodArgs<Scene<never>, Method>
): MethodDecorator => {
return SetMetadata(UPDATE_LISTENER_METADATA, {
method,
args,
} as ListenerMetadata);
};
}

View File

@ -1,12 +1,15 @@
import { SetMetadata } from '@nestjs/common'; import { SetMetadata } from '@nestjs/common';
import { UpdateMethodArgs, UpdateMethods } from '../telegraf.types'; import { Composer } from 'telegraf';
import { ComposerMethodArgs, UpdateMethods } from '../telegraf.types';
import { UPDATE_LISTENER_METADATA } from '../telegraf.constants'; import { UPDATE_LISTENER_METADATA } from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces'; import { ListenerMetadata } from '../interfaces';
export function createUpdateDecorator<Method extends UpdateMethods>( export function createUpdateListenerDecorator<Method extends UpdateMethods>(
method: Method, method: Method,
) { ) {
return (...args: UpdateMethodArgs<Method>): MethodDecorator => { return (
...args: ComposerMethodArgs<Composer<never>, Method>
): MethodDecorator => {
return SetMetadata(UPDATE_LISTENER_METADATA, { return SetMetadata(UPDATE_LISTENER_METADATA, {
method, method,
args, args,

View File

@ -1 +1,2 @@
export * from './create-update-decorator.helper'; export * from './create-update-listener-decorator.helper';
export * from './create-scene-listener-decorator.helper';

View File

@ -1,6 +1,4 @@
import { UpdateMethods } from '../telegraf.types';
export interface ListenerMetadata { export interface ListenerMetadata {
method: UpdateMethods; method: string;
args: unknown[]; args: unknown[];
} }

View File

@ -1,10 +1,12 @@
import { ModuleMetadata, Type } from '@nestjs/common/interfaces'; import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
import { Middleware, Context } from 'telegraf';
import { TelegrafLaunchOption, TelegrafOption } from '../telegraf.types'; import { TelegrafLaunchOption, TelegrafOption } from '../telegraf.types';
export interface TelegrafModuleOptions { export interface TelegrafModuleOptions<C extends Context = Context> {
token: string; token: string;
options?: TelegrafOption; options?: TelegrafOption;
launchOptions?: TelegrafLaunchOption; launchOptions?: TelegrafLaunchOption;
middlewares?: Middleware<C>[];
} }
export interface TelegrafOptionsFactory { export interface TelegrafOptionsFactory {

View File

@ -4,4 +4,3 @@ export const UPDATE_METADATA = 'UPDATE_METADATA';
export const UPDATE_LISTENER_METADATA = 'UPDATE_LISTENER_METADATA'; export const UPDATE_LISTENER_METADATA = 'UPDATE_LISTENER_METADATA';
export const SCENE_METADATA = 'SCENE_METADATA'; export const SCENE_METADATA = 'SCENE_METADATA';
export const SCENE_LISTENER_METADATA = 'SCENE_LISTENER_METADATA';

View File

@ -15,8 +15,8 @@ import {
} from './interfaces'; } from './interfaces';
import { TELEGRAF_MODULE_OPTIONS } from './telegraf.constants'; import { TELEGRAF_MODULE_OPTIONS } from './telegraf.constants';
import { TelegrafMetadataAccessor } from './telegraf.metadata-accessor'; import { TelegrafMetadataAccessor } from './telegraf.metadata-accessor';
import { TelegrafUpdateExplorer } from './telegraf-update.explorer'; import { TelegrafUpdateExplorer } from './explorers/telegraf-update.explorer';
import { TelegrafSceneExplorer } from './telegraf-scene.explorer'; import { TelegrafSceneExplorer } from './explorers/telegraf-scene.explorer';
import { createProviders, TelegrafProvider } from './telegraf.providers'; import { createProviders, TelegrafProvider } from './telegraf.providers';
@Module({ @Module({

View File

@ -5,11 +5,12 @@ import { TelegrafModuleOptions } from './interfaces';
export const TelegrafProvider = { export const TelegrafProvider = {
provide: Telegraf, provide: Telegraf,
inject: [TELEGRAF_MODULE_OPTIONS],
useFactory: (options: TelegrafModuleOptions) => { useFactory: (options: TelegrafModuleOptions) => {
const telegraf = new Telegraf(options.token, options.options); const telegraf = new Telegraf(options.token, options.options);
telegraf.use(...options.middlewares);
return telegraf; return telegraf;
}, },
inject: [TELEGRAF_MODULE_OPTIONS],
}; };
export function createProviders(options: TelegrafModuleOptions): Provider[] { export function createProviders(options: TelegrafModuleOptions): Provider[] {

View File

@ -1,4 +1,4 @@
import { Composer, Middleware, Telegraf } from 'telegraf'; import { Composer, Middleware, BaseScene, Telegraf } from 'telegraf';
export type Filter<T extends any[], F> = T extends [] export type Filter<T extends any[], F> = T extends []
? [] ? []
@ -8,14 +8,22 @@ export type Filter<T extends any[], F> = T extends []
: [Head, ...Filter<Tail, F>] : [Head, ...Filter<Tail, F>]
: []; : [];
export type UpdateMethods = Exclude< export type OnlyFunctionPropertyNames<T> = {
keyof Composer<never>, [K in keyof T]: T[K] extends (...args: any) => any ? K : never;
'middleware' | 'guard' | 'filter' | 'drop' }[keyof T];
>;
export type UpdateMethodArgs<T extends UpdateMethods> = Filter< // export type FilterComposerMethods<T extends string> = Exclude<
Parameters<Composer<never>[T]>, // T,
Middleware<any> // 'middleware' | 'guard' | 'filter' | 'drop'
>; // >;
export type ComposerMethodArgs<
T extends Composer<never>,
U extends OnlyFunctionPropertyNames<T> = OnlyFunctionPropertyNames<T>
> = Filter<Parameters<T[U]>, Middleware<never>>;
export type UpdateMethods = OnlyFunctionPropertyNames<Composer<never>>;
export type SceneMethods = OnlyFunctionPropertyNames<BaseScene<never>>;
export type TelegrafOption = ConstructorParameters<typeof Telegraf>[1]; export type TelegrafOption = ConstructorParameters<typeof Telegraf>[1];
export type TelegrafLaunchOption = Parameters<Telegraf['launch']>[0]; export type TelegrafLaunchOption = Parameters<Telegraf['launch']>[0];

View File

@ -3,11 +3,13 @@ import { TelegrafModule } from '../lib';
import { EchoService } from './echo.service'; import { EchoService } from './echo.service';
import { AppUpdate } from './app.update'; import { AppUpdate } from './app.update';
import { HelloScene } from './scenes/hello.scene'; import { HelloScene } from './scenes/hello.scene';
import { sessionMiddleware } from './middleware/session.middleware';
@Module({ @Module({
imports: [ imports: [
TelegrafModule.forRoot({ TelegrafModule.forRoot({
token: '1467731595:AAHCvH65H9VQYKF9jE-E8c2rXsQBVAYseg8', // Don't steal >:( token: '1467731595:AAHCvH65H9VQYKF9jE-E8c2rXsQBVAYseg8', // Don't steal >:(
middlewares: [sessionMiddleware],
}), }),
], ],
providers: [EchoService, AppUpdate, HelloScene], providers: [EchoService, AppUpdate, HelloScene],

View File

@ -0,0 +1,3 @@
import { session } from 'telegraf';
export const sessionMiddleware = session();

View File

@ -11,7 +11,7 @@ export class HelloScene {
} }
@SceneLeave() @SceneLeave()
async onSceneLeave(): Promise<void> { async onSceneLeave(ctx: Context): Promise<void> {
console.log('Leave from scene'); console.log('Leave from scene');
await ctx.reply('Bye Bye 👋'); await ctx.reply('Bye Bye 👋');
} }