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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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.
*
* @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 { SCENE_LISTENER_METADATA } from '../../telegraf.constants';
import { SceneEventType } from '../../enums/scene-event-type.enum';
import { createSceneListenerDecorator } from '../../helpers';
export const SceneEnter = (): MethodDecorator =>
SetMetadata(SCENE_LISTENER_METADATA, SceneEventType.Enter);
export const SceneEnter = createSceneListenerDecorator('enter');

View File

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

View File

@ -3,7 +3,7 @@ import { DiscoveryService } from '@nestjs/core';
import { MetadataScanner } from '@nestjs/core/metadata-scanner';
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
import { Telegraf } from 'telegraf';
import { TelegrafMetadataAccessor } from './telegraf.metadata-accessor';
import { TelegrafMetadataAccessor } from '../telegraf.metadata-accessor';
@Injectable()
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 { UpdateMethodArgs, UpdateMethods } from '../telegraf.types';
import { Composer } from 'telegraf';
import { ComposerMethodArgs, UpdateMethods } from '../telegraf.types';
import { UPDATE_LISTENER_METADATA } from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces';
export function createUpdateDecorator<Method extends UpdateMethods>(
export function createUpdateListenerDecorator<Method extends UpdateMethods>(
method: Method,
) {
return (...args: UpdateMethodArgs<Method>): MethodDecorator => {
return (
...args: ComposerMethodArgs<Composer<never>, Method>
): MethodDecorator => {
return SetMetadata(UPDATE_LISTENER_METADATA, {
method,
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 {
method: UpdateMethods;
method: string;
args: unknown[];
}

View File

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

View File

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

View File

@ -5,11 +5,12 @@ import { TelegrafModuleOptions } from './interfaces';
export const TelegrafProvider = {
provide: Telegraf,
inject: [TELEGRAF_MODULE_OPTIONS],
useFactory: (options: TelegrafModuleOptions) => {
const telegraf = new Telegraf(options.token, options.options);
telegraf.use(...options.middlewares);
return telegraf;
},
inject: [TELEGRAF_MODULE_OPTIONS],
};
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 []
? []
@ -8,14 +8,22 @@ export type Filter<T extends any[], F> = T extends []
: [Head, ...Filter<Tail, F>]
: [];
export type UpdateMethods = Exclude<
keyof Composer<never>,
'middleware' | 'guard' | 'filter' | 'drop'
>;
export type UpdateMethodArgs<T extends UpdateMethods> = Filter<
Parameters<Composer<never>[T]>,
Middleware<any>
>;
export type OnlyFunctionPropertyNames<T> = {
[K in keyof T]: T[K] extends (...args: any) => any ? K : never;
}[keyof T];
// export type FilterComposerMethods<T extends string> = Exclude<
// T,
// '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 TelegrafLaunchOption = Parameters<Telegraf['launch']>[0];

View File

@ -3,11 +3,13 @@ import { TelegrafModule } from '../lib';
import { EchoService } from './echo.service';
import { AppUpdate } from './app.update';
import { HelloScene } from './scenes/hello.scene';
import { sessionMiddleware } from './middleware/session.middleware';
@Module({
imports: [
TelegrafModule.forRoot({
token: '1467731595:AAHCvH65H9VQYKF9jE-E8c2rXsQBVAYseg8', // Don't steal >:(
middlewares: [sessionMiddleware],
}),
],
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()
async onSceneLeave(): Promise<void> {
async onSceneLeave(ctx: Context): Promise<void> {
console.log('Leave from scene');
await ctx.reply('Bye Bye 👋');
}