fix(): working after merge

This commit is contained in:
Alexander Bukhalo 2021-01-02 21:40:13 +03:00
parent b808fb646a
commit 832ddaf15c
21 changed files with 167 additions and 706 deletions

View File

@ -1,5 +1,6 @@
# source # source
lib lib
index.ts
package-lock.json package-lock.json
tsconfig.json tsconfig.json
.prettierrc .prettierrc

View File

@ -1,4 +1,5 @@
import { Inject } from '@nestjs/common'; import { Inject } from '@nestjs/common';
import { Telegraf } from 'telegraf'; import { getBotToken } from '../../utils';
export const InjectBot = (): ParameterDecorator => Inject(Telegraf); export const InjectBot = (name?: string): ParameterDecorator =>
Inject(getBotToken(name));

View File

@ -1,5 +0,0 @@
import { Inject } from '@nestjs/common';
import { getBotToken } from '../utils';
export const InjectBot = (name?: string): ParameterDecorator =>
Inject(getBotToken(name));

View File

@ -1,73 +0,0 @@
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
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';
@Injectable()
export class TelegrafSceneExplorer implements OnModuleInit {
private readonly stage = new Stage([]);
constructor(
@Inject(Telegraf)
private readonly telegraf: Telegraf<never>,
private readonly discoveryService: DiscoveryService,
private readonly metadataAccessor: TelegrafMetadataAccessor,
private readonly metadataScanner: MetadataScanner,
) {
this.telegraf.use(this.stage.middleware());
}
onModuleInit(): void {
this.explore();
}
private explore(): void {
const sceneClasses = this.filterSceneClasses();
sceneClasses.forEach((wrapper) => {
const { instance } = wrapper;
const sceneId = this.metadataAccessor.getSceneMetadata(
instance.constructor,
);
const scene = new Scene(sceneId);
this.stage.register(scene);
const prototype = Object.getPrototypeOf(instance);
this.metadataScanner.scanFromPrototype(
instance,
prototype,
(methodKey: string) =>
this.registerIfListener(scene, instance, methodKey),
);
});
}
private filterSceneClasses(): InstanceWrapper[] {
return this.discoveryService
.getProviders()
.filter((wrapper) => wrapper.instance)
.filter((wrapper) =>
this.metadataAccessor.isScene(wrapper.instance.constructor),
);
}
private registerIfListener(
scene: Scene<never>,
instance: Record<string, Function>,
methodKey: string,
): void {
const methodRef = instance[methodKey];
const middlewareFn = methodRef.bind(instance);
const listenerMetadata = this.metadataAccessor.getListenerMetadata(
methodRef,
);
if (!listenerMetadata) return;
const { method, args } = listenerMetadata;
(scene[method] as any)(...args, middlewareFn);
}
}

View File

@ -1,63 +0,0 @@
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
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';
@Injectable()
export class TelegrafUpdateExplorer implements OnModuleInit {
constructor(
@Inject(Telegraf)
private readonly telegraf: Telegraf<never>,
private readonly discoveryService: DiscoveryService,
private readonly metadataAccessor: TelegrafMetadataAccessor,
private readonly metadataScanner: MetadataScanner,
) {}
onModuleInit(): void {
this.explore();
}
private explore(): void {
const updateClasses = this.filterUpdateClasses();
updateClasses.forEach((wrapper) => {
const { instance } = wrapper;
const prototype = Object.getPrototypeOf(instance);
this.metadataScanner.scanFromPrototype(
instance,
prototype,
(methodKey: string) => this.registerIfListener(instance, methodKey),
);
});
}
private filterUpdateClasses(): InstanceWrapper[] {
return this.discoveryService
.getProviders()
.filter((wrapper) => wrapper.instance)
.filter((wrapper) =>
this.metadataAccessor.isUpdate(wrapper.instance.constructor),
);
}
private registerIfListener(
instance: Record<string, Function>,
methodKey: string,
): void {
const methodRef = instance[methodKey];
const middlewareFn = methodRef.bind(instance);
const listenerMetadata = this.metadataAccessor.getListenerMetadata(
methodRef,
);
if (!listenerMetadata) return;
const { method, args } = listenerMetadata;
// NOTE: Use "any" to disable "Expected at least 1 arguments, but got 1 or more." error.
// Use telegraf instance for non-scene listeners
(this.telegraf[method] as any)(...args, middlewareFn);
}
}

View File

@ -1,6 +1,6 @@
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 '../telegraf.types'; import { ComposerMethodArgs, SceneMethods } from '../types';
import { UPDATE_LISTENER_METADATA } from '../telegraf.constants'; import { UPDATE_LISTENER_METADATA } from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces'; import { ListenerMetadata } from '../interfaces';

View File

@ -1,6 +1,6 @@
import { SetMetadata } from '@nestjs/common'; import { SetMetadata } from '@nestjs/common';
import { Composer } from 'telegraf'; import { Composer } from 'telegraf';
import { ComposerMethodArgs, UpdateMethods } from '../telegraf.types'; import { ComposerMethodArgs, UpdateMethods } from '../types';
import { UPDATE_LISTENER_METADATA } from '../telegraf.constants'; import { UPDATE_LISTENER_METADATA } from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces'; import { ListenerMetadata } from '../interfaces';

View File

@ -11,5 +11,5 @@ export * from './interfaces';
export * from './helpers'; export * from './helpers';
export * from './utils'; export * from './utils';
export * from './telegraf.module'; export * from './telegraf.module';
export * from './telegraf.types'; export * from './types';
export { Telegraf } from 'telegraf'; export { Telegraf } from 'telegraf';

View File

@ -1,11 +0,0 @@
import { TelegrafContext } from 'telegraf/typings/context';
export interface Context extends TelegrafContext {
[key: string]: any; // TBD
}
/**
* Removed type from Telegraf v3.38.0, added for backward compatibility.
* TODO: remove on next major release
*/
export interface ContextMessageUpdate extends Context {}

View File

@ -1,4 +1,3 @@
export * from './context.interface';
export * from './telegraf-options.interface'; export * from './telegraf-options.interface';
export * from './listener-metadata.interface'; export * from './listener-metadata.interface';
export * from './update-metadata.interface'; export * from './update-metadata.interface';

View File

@ -4,10 +4,7 @@ import {
TelegrafOptions, TelegrafOptions,
LaunchPollingOptions, LaunchPollingOptions,
LaunchWebhookOptions, LaunchWebhookOptions,
TelegrafOptions,
} from 'telegraf/typings/telegraf'; } from 'telegraf/typings/telegraf';
import { Middleware } from 'telegraf/typings/composer';
import { Context } from './context.interface';
export interface TelegrafModuleOptions<C extends Context = Context> { export interface TelegrafModuleOptions<C extends Context = Context> {
token: string; token: string;
@ -18,9 +15,7 @@ export interface TelegrafModuleOptions<C extends Context = Context> {
}; };
botName?: string; botName?: string;
include?: Function[]; include?: Function[];
middlewares?: ReadonlyArray<Middleware<Context>>; middlewares?: ReadonlyArray<Middleware<C>>;
disableGlobalCatch?: boolean;
middlewares?: Middleware<C>[];
} }
export interface TelegrafOptionsFactory { export interface TelegrafOptionsFactory {

View File

@ -1,2 +1,4 @@
export * from './updates-explorer.service'; export * from './updates-explorer.service';
export * from './metadata-accessor.service'; export * from './metadata-accessor.service';
export * from './scenes-explorer.service';
export * from './updates-explorer.service';

View File

@ -1,204 +1,42 @@
import { Injectable, Type } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import { Reflector } from '@nestjs/core'; import { Reflector } from '@nestjs/core';
import { import {
ActionOptions, SCENE_METADATA,
CashtagOptions, UPDATE_LISTENER_METADATA,
CommandOptions, UPDATE_METADATA,
EntityOptions, } from '../telegraf.constants';
HashtagOptions, import { ListenerMetadata } from '../interfaces';
HearsOptions,
InlineQueryOptions,
MentionOptions,
OnOptions,
PhoneOptions,
UpdateHookOptions,
} from '../decorators';
import { DECORATORS } from '../telegraf.constants';
@Injectable() @Injectable()
export class MetadataAccessorService { export class MetadataAccessorService {
constructor(private readonly reflector: Reflector) {} constructor(private readonly reflector: Reflector) {}
isUpdate(target: Type<any> | Function): boolean { isUpdate(target: Function): boolean {
if (!target) { if (!target) {
return false; return false;
} }
return !!this.reflector.get(DECORATORS.UPDATE, target); return !!this.reflector.get(UPDATE_METADATA, target);
} }
isUpdateHook(target: Type<any> | Function): boolean { isUpdateListener(target: Function) {
if (!target) { if (!target) {
return false; return false;
} }
return !!this.reflector.get(DECORATORS.UPDATE_HOOK, target); return !!this.reflector.get(UPDATE_LISTENER_METADATA, target);
} }
getUpdateHookMetadata( isScene(target: Function): boolean {
target: Type<any> | Function,
): UpdateHookOptions | undefined {
return this.reflector.get(DECORATORS.UPDATE_HOOK, target);
}
isTelegrafUse(target: Type<any> | Function): boolean {
if (!target) { if (!target) {
return false; return false;
} }
return !!this.reflector.get(DECORATORS.USE, target); return !!this.reflector.get(SCENE_METADATA, target);
} }
isTelegrafOn(target: Type<any> | Function): boolean { getListenerMetadata(target: Function): ListenerMetadata | undefined {
if (!target) { return this.reflector.get(UPDATE_LISTENER_METADATA, target);
return false;
}
return !!this.reflector.get(DECORATORS.ON, target);
} }
getTelegrafOnMetadata(target: Type<any> | Function): OnOptions | undefined { getSceneMetadata(target: Function): string | undefined {
return this.reflector.get(DECORATORS.ON, target); return this.reflector.get(SCENE_METADATA, target);
}
isTelegrafHears(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.HEARS, target);
}
getTelegrafHearsMetadata(
target: Type<any> | Function,
): HearsOptions | undefined {
return this.reflector.get(DECORATORS.HEARS, target);
}
isTelegrafCommand(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.COMMAND, target);
}
getTelegrafCommandMetadata(
target: Type<any> | Function,
): CommandOptions | undefined {
return this.reflector.get(DECORATORS.COMMAND, target);
}
isTelegrafStart(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.START, target);
}
isTelegrafHelp(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.HELP, target);
}
isTelegrafSettings(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.SETTINGS, target);
}
isTelegrafEntity(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.ENTITY, target);
}
getTelegrafEntityMetadata(
target: Type<any> | Function,
): EntityOptions | undefined {
return this.reflector.get(DECORATORS.ENTITY, target);
}
isTelegrafMention(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.MENTION, target);
}
getTelegrafMentionMetadata(
target: Type<any> | Function,
): MentionOptions | undefined {
return this.reflector.get(DECORATORS.MENTION, target);
}
isTelegrafPhone(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.PHONE, target);
}
getTelegrafPhoneMetadata(
target: Type<any> | Function,
): PhoneOptions | undefined {
return this.reflector.get(DECORATORS.PHONE, target);
}
isTelegrafHashtag(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.HASHTAG, target);
}
getTelegrafHashtagMetadata(
target: Type<any> | Function,
): HashtagOptions | undefined {
return this.reflector.get(DECORATORS.HASHTAG, target);
}
isTelegrafCashtag(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.CASHTAG, target);
}
getTelegrafCashtagMetadata(
target: Type<any> | Function,
): CashtagOptions | undefined {
return this.reflector.get(DECORATORS.CASHTAG, target);
}
isTelegrafAction(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.ACTION, target);
}
getTelegrafActionMetadata(
target: Type<any> | Function,
): ActionOptions | undefined {
return this.reflector.get(DECORATORS.ACTION, target);
}
isTelegrafInlineQuery(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.INLINE_QUERY, target);
}
getTelegrafInlineQueryMetadata(
target: Type<any> | Function,
): InlineQueryOptions | undefined {
return this.reflector.get(DECORATORS.INLINE_QUERY, target);
}
isTelegrafGameQuery(target: Type<any> | Function): boolean {
if (!target) {
return false;
}
return !!this.reflector.get(DECORATORS.GAME_QUERY, target);
} }
} }

View File

@ -0,0 +1,98 @@
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { DiscoveryService, ModuleRef, ModulesContainer } 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 { MetadataAccessorService } from './metadata-accessor.service';
import { BaseExplorerService } from './base-explorer.service';
import {
TELEGRAF_BOT_NAME,
TELEGRAF_MODULE_OPTIONS,
} from '../telegraf.constants';
import { TelegrafModuleOptions } from '../interfaces';
import { Module } from '@nestjs/core/injector/module';
@Injectable()
export class ScenesExplorerService
extends BaseExplorerService
implements OnModuleInit {
private readonly stage = new Stage([]);
constructor(
@Inject(TELEGRAF_BOT_NAME)
private readonly botName: string,
@Inject(TELEGRAF_MODULE_OPTIONS)
private readonly telegrafModuleOptions: TelegrafModuleOptions,
private readonly moduleRef: ModuleRef,
private readonly discoveryService: DiscoveryService,
private readonly metadataAccessor: MetadataAccessorService,
private readonly metadataScanner: MetadataScanner,
private readonly modulesContainer: ModulesContainer,
) {
super();
}
private bot: Telegraf<any>;
onModuleInit(): void {
this.bot = this.moduleRef.get<Telegraf<any>>(this.botName, {
strict: false,
});
this.bot.use(this.stage.middleware());
this.explore();
}
private explore(): void {
const modules = this.getModules(
this.modulesContainer,
this.telegrafModuleOptions.include || [],
);
const scenes = this.flatMap(modules, (instance, moduleRef) =>
this.applyScenes(instance, moduleRef),
);
}
private applyScenes(wrapper: InstanceWrapper, moduleRef: Module) {
const { instance } = wrapper;
if (!instance) {
return undefined;
}
const prototype = Object.getPrototypeOf(instance);
const providers: InstanceWrapper[] = this.discoveryService.getProviders();
const sceneProviders: InstanceWrapper[] = providers.filter(
(wrapper: InstanceWrapper) =>
this.metadataAccessor.isScene(wrapper.metatype),
);
sceneProviders.forEach((wrapper) => {
const { instance } = wrapper;
if (!instance) {
return undefined;
}
const sceneId = this.metadataAccessor.getSceneMetadata(
instance.constructor,
);
const scene = new Scene(sceneId);
this.stage.register(scene);
this.metadataScanner.scanFromPrototype(
instance,
prototype,
(methodKey: string) => {
const methodRef = instance[methodKey];
if (this.metadataAccessor.isUpdateListener(methodRef)) {
const metadata = this.metadataAccessor.getListenerMetadata(
methodRef,
);
const middlewareFn = methodRef.bind(instance);
const { method, args } = metadata;
(scene[method] as any)(...args, middlewareFn);
}
},
);
});
}
}

View File

@ -7,19 +7,6 @@ import {
TELEGRAF_BOT_NAME, TELEGRAF_BOT_NAME,
TELEGRAF_MODULE_OPTIONS, TELEGRAF_MODULE_OPTIONS,
} from '../telegraf.constants'; } from '../telegraf.constants';
import {
ActionOptions,
CashtagOptions,
CommandOptions,
EntityOptions,
HashtagOptions,
HearsOptions,
InlineQueryOptions,
MentionOptions,
OnOptions,
PhoneOptions,
UpdateHookOptions,
} from '../decorators';
import { Telegraf } from 'telegraf'; import { Telegraf } from 'telegraf';
import { TelegrafModuleOptions } from '../interfaces'; import { TelegrafModuleOptions } from '../interfaces';
import { BaseExplorerService } from './base-explorer.service'; import { BaseExplorerService } from './base-explorer.service';
@ -81,184 +68,16 @@ export class UpdatesExplorerService
return undefined; return undefined;
} }
this.metadataScanner.scanFromPrototype(instance, prototype, (name) => { this.metadataScanner.scanFromPrototype(instance, prototype, (name) => {
if (this.metadataAccessor.isUpdateHook(instance[name])) { const methodRef = instance[name];
const metadata = this.metadataAccessor.getUpdateHookMetadata( if (this.metadataAccessor.isUpdateListener(methodRef)) {
instance[name], const metadata = this.metadataAccessor.getListenerMetadata(methodRef);
); const middlewareFn = methodRef.bind(instance);
this.handleUpdateHook(instance, name, metadata); const { method, args } = metadata;
// NOTE: Use "any" to disable "Expected at least 1 arguments, but got 1 or more." error.
// Use telegraf instance for non-scene listeners
(this.bot[method] as any)(...args, middlewareFn);
} }
}); });
}); });
providers.forEach((wrapper: InstanceWrapper) => {
const { instance } = wrapper;
if (!instance) {
return undefined;
}
this.metadataScanner.scanFromPrototype(
instance,
prototype,
(key: string) => {
if (this.metadataAccessor.isTelegrafUse(instance[key])) {
this.handleTelegrafUse(instance, key);
} else if (this.metadataAccessor.isTelegrafOn(instance[key])) {
const metadata = this.metadataAccessor.getTelegrafOnMetadata(
instance[key],
);
this.handleTelegrafOn(instance, key, metadata);
} else if (this.metadataAccessor.isTelegrafHears(instance[key])) {
const metadata = this.metadataAccessor.getTelegrafHearsMetadata(
instance[key],
);
this.handleTelegrafHears(instance, key, metadata);
} else if (this.metadataAccessor.isTelegrafCommand(instance[key])) {
const metadata = this.metadataAccessor.getTelegrafCommandMetadata(
instance[key],
);
this.handleTelegrafCommand(instance, key, metadata);
} else if (this.metadataAccessor.isTelegrafStart(instance[key])) {
this.handleTelegrafStart(instance, key);
} else if (this.metadataAccessor.isTelegrafHelp(instance[key])) {
this.handleTelegrafHelp(instance, key);
} else if (this.metadataAccessor.isTelegrafSettings(instance[key])) {
this.handleTelegrafSettings(instance, key);
} else if (this.metadataAccessor.isTelegrafEntity(instance[key])) {
const metadata = this.metadataAccessor.getTelegrafEntityMetadata(
instance[key],
);
this.handleTelegrafEntity(instance, key, metadata);
} else if (this.metadataAccessor.isTelegrafMention(instance[key])) {
const metadata = this.metadataAccessor.getTelegrafMentionMetadata(
instance[key],
);
this.handleTelegrafMention(instance, key, metadata);
} else if (this.metadataAccessor.isTelegrafPhone(instance[key])) {
const metadata = this.metadataAccessor.getTelegrafPhoneMetadata(
instance[key],
);
this.handleTelegrafPhone(instance, key, metadata);
} else if (this.metadataAccessor.isTelegrafHashtag(instance[key])) {
const metadata = this.metadataAccessor.getTelegrafHashtagMetadata(
instance[key],
);
this.handleTelegrafHashtag(instance, key, metadata);
} else if (this.metadataAccessor.isTelegrafCashtag(instance[key])) {
const metadata = this.metadataAccessor.getTelegrafCashtagMetadata(
instance[key],
);
this.handleTelegrafCashtag(instance, key, metadata);
} else if (this.metadataAccessor.isTelegrafAction(instance[key])) {
const metadata = this.metadataAccessor.getTelegrafActionMetadata(
instance[key],
);
this.handleTelegrafAction(instance, key, metadata);
} else if (
this.metadataAccessor.isTelegrafInlineQuery(instance[key])
) {
const metadata = this.metadataAccessor.getTelegrafInlineQueryMetadata(
instance[key],
);
this.handleTelegrafInlineQuery(instance, key, metadata);
} else if (this.metadataAccessor.isTelegrafGameQuery(instance[key])) {
this.handleTelegrafGameQuery(instance, key);
}
},
);
});
}
handleUpdateHook(instance: object, key: string, metadata: UpdateHookOptions) {
this.bot.on(metadata.updateType, instance[key].bind(instance));
}
handleTelegrafUse(instance: object, key: string) {
this.bot.use(instance[key].bind(instance));
}
handleTelegrafOn(instance: object, key: string, metadata: OnOptions) {
this.bot.on(metadata.updateTypes, instance[key].bind(instance));
}
handleTelegrafHears(instance: object, key: string, metadata: HearsOptions) {
this.bot.hears(metadata.triggers, instance[key].bind(instance));
}
handleTelegrafCommand(
instance: object,
key: string,
metadata: CommandOptions,
) {
this.bot.command(metadata.commands, instance[key].bind(instance));
}
handleTelegrafStart(instance: object, key: string) {
this.bot.start(instance[key].bind(instance));
}
handleTelegrafHelp(instance: object, key: string) {
this.bot.help(instance[key].bind(instance));
}
handleTelegrafSettings(instance: object, key: string) {
// @ts-ignore
this.bot.settings(instance[key].bind(instance));
}
handleTelegrafEntity(instance: object, key: string, metadata: EntityOptions) {
// @ts-ignore
this.bot.entity(metadata.entity, instance[key].bind(instance));
}
handleTelegrafMention(
instance: object,
key: string,
metadata: MentionOptions,
) {
// @ts-ignore
this.bot.mention(metadata.username, instance[key].bind(instance));
}
handleTelegrafPhone(instance: object, key: string, metadata: PhoneOptions) {
// @ts-ignore
this.bot.phone(metadata.phone, instance[key].bind(instance));
}
handleTelegrafHashtag(
instance: object,
key: string,
metadata: HashtagOptions,
) {
// @ts-ignore
this.bot.hashtag(metadata.hashtag, instance[key].bind(instance));
}
handleTelegrafCashtag(
instance: object,
key: string,
metadata: CashtagOptions,
) {
// @ts-ignore
this.bot.cashtag(metadata.cashtag, instance[key].bind(instance));
}
handleTelegrafAction(instance: object, key: string, metadata: ActionOptions) {
this.bot.action(metadata.triggers, instance[key].bind(instance));
}
handleTelegrafInlineQuery(
instance: object,
key: string,
metadata: InlineQueryOptions,
) {
if (metadata.triggers) {
// @ts-ignore
this.bot.inlineQuery(metadata.triggers, instance[key].bind(instance));
} else {
this.bot.on(metadata.updateType, instance[key].bind(instance));
}
}
handleTelegrafGameQuery(instance: object, key: string) {
this.bot.gameQuery(instance[key].bind(instance));
} }
} }

View File

@ -13,13 +13,16 @@ import {
TelegrafModuleOptions, TelegrafModuleOptions,
TelegrafModuleAsyncOptions, TelegrafModuleAsyncOptions,
TelegrafOptionsFactory, TelegrafOptionsFactory,
Context,
} from './interfaces'; } from './interfaces';
import { import {
TELEGRAF_BOT_NAME, TELEGRAF_BOT_NAME,
TELEGRAF_MODULE_OPTIONS, TELEGRAF_MODULE_OPTIONS,
} from './telegraf.constants'; } from './telegraf.constants';
import { MetadataAccessorService, UpdatesExplorerService } from './services'; import {
MetadataAccessorService,
ScenesExplorerService,
UpdatesExplorerService,
} from './services';
import { getBotToken } from './utils'; import { getBotToken } from './utils';
import { Telegraf } from 'telegraf'; import { Telegraf } from 'telegraf';
import { defer } from 'rxjs'; import { defer } from 'rxjs';
@ -27,7 +30,11 @@ import { defer } from 'rxjs';
@Global() @Global()
@Module({ @Module({
imports: [DiscoveryModule], imports: [DiscoveryModule],
providers: [UpdatesExplorerService, MetadataAccessorService], providers: [
UpdatesExplorerService,
ScenesExplorerService,
MetadataAccessorService,
],
}) })
export class TelegrafCoreModule implements OnApplicationShutdown { export class TelegrafCoreModule implements OnApplicationShutdown {
private static logger = new Logger(TelegrafCoreModule.name); private static logger = new Logger(TelegrafCoreModule.name);
@ -44,22 +51,8 @@ export class TelegrafCoreModule implements OnApplicationShutdown {
provide: telegrafBotName, provide: telegrafBotName,
useFactory: async (): Promise<any> => useFactory: async (): Promise<any> =>
await defer(async () => { await defer(async () => {
const bot = new Telegraf<Context>(options.token); const bot = new Telegraf<any>(options.token);
this.applyBotMiddlewares(bot, options.middlewares); this.applyBotMiddlewares(bot, options.middlewares);
/**
* Backward compatibility with versions < 1.4.0,
* TODO: remove that on next major release,
* after exception filters has been added
*/
if (!options.disableGlobalCatch) {
bot.catch((err, ctx: Context) => {
this.logger.error(
`Encountered an error for ${ctx.updateType} update type`,
err,
);
});
}
await bot.launch(options.launchOptions); await bot.launch(options.launchOptions);
return bot; return bot;
}).toPromise(), }).toPromise(),
@ -95,22 +88,8 @@ export class TelegrafCoreModule implements OnApplicationShutdown {
const { botName, ...telegrafOptions } = telegrafModuleOptions; const { botName, ...telegrafOptions } = telegrafModuleOptions;
return await defer(async () => { return await defer(async () => {
const bot = new Telegraf<Context>(telegrafOptions.token); const bot = new Telegraf<any>(telegrafOptions.token);
this.applyBotMiddlewares(bot, telegrafOptions.middlewares); this.applyBotMiddlewares(bot, telegrafOptions.middlewares);
/**
* Backward compatibility with versions < 1.4.0,
* TODO: remove that on next major release,
* after exception filters has been added
*/
if (!telegrafOptions.disableGlobalCatch) {
bot.catch((err, ctx: Context) => {
this.logger.error(
`Encountered an error for ${ctx.updateType} update type`,
err,
);
});
}
await bot.launch(telegrafOptions.launchOptions); await bot.launch(telegrafOptions.launchOptions);
return bot; return bot;
}).toPromise(); }).toPromise();

View File

@ -1,29 +0,0 @@
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 TelegrafMetadataAccessor {
constructor(private readonly reflector: Reflector) {}
isUpdate(target: Function): boolean {
return !!this.reflector.get(UPDATE_METADATA, target);
}
isScene(target: Function): boolean {
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);
}
}

View File

@ -1,106 +1,27 @@
import { DiscoveryModule, ModuleRef } from '@nestjs/core'; import { Module, DynamicModule } from '@nestjs/common';
import { TelegrafCoreModule } from './telegraf-core.module';
import { import {
DynamicModule,
Inject,
Module,
OnApplicationBootstrap,
OnApplicationShutdown,
Provider,
} from '@nestjs/common';
import { Telegraf } from 'telegraf';
import {
TelegrafModuleAsyncOptions,
TelegrafModuleOptions, TelegrafModuleOptions,
TelegrafOptionsFactory, TelegrafModuleAsyncOptions,
} from './interfaces'; } from './interfaces';
import { TELEGRAF_MODULE_OPTIONS } from './telegraf.constants';
import { TelegrafMetadataAccessor } from './telegraf.metadata-accessor';
import { TelegrafUpdateExplorer } from './explorers/telegraf-update.explorer';
import { TelegrafSceneExplorer } from './explorers/telegraf-scene.explorer';
import { createProviders, TelegrafProvider } from './telegraf.providers';
@Module({
imports: [DiscoveryModule],
providers: [
TelegrafMetadataAccessor,
TelegrafSceneExplorer,
TelegrafUpdateExplorer,
],
})
export class TelegrafModule
implements OnApplicationBootstrap, OnApplicationShutdown {
constructor(
@Inject(TELEGRAF_MODULE_OPTIONS)
private readonly options: TelegrafModuleOptions,
private readonly moduleRef: ModuleRef,
) {}
async onApplicationBootstrap(): Promise<void> {
const { launchOptions } = this.options;
const telegraf = this.moduleRef.get(Telegraf);
await telegraf.launch(launchOptions);
}
async onApplicationShutdown(): Promise<void> {
const telegraf = this.moduleRef.get(Telegraf);
await telegraf.stop();
}
@Module({})
export class TelegrafModule {
public static forRoot(options: TelegrafModuleOptions): DynamicModule { public static forRoot(options: TelegrafModuleOptions): DynamicModule {
const providers = [...createProviders(options), TelegrafProvider];
return { return {
module: TelegrafModule, module: TelegrafModule,
providers, imports: [TelegrafCoreModule.forRoot(options)],
exports: providers, exports: [TelegrafCoreModule],
}; };
} }
public static forRootAsync( public static forRootAsync(
options: TelegrafModuleAsyncOptions, options: TelegrafModuleAsyncOptions,
): DynamicModule { ): DynamicModule {
const providers = [...this.createAsyncProviders(options), TelegrafProvider];
return { return {
module: TelegrafModule, module: TelegrafModule,
imports: options.imports || [], imports: [TelegrafCoreModule.forRootAsync(options)],
providers, exports: [TelegrafCoreModule],
exports: providers,
};
}
private static createAsyncProviders(
options: TelegrafModuleAsyncOptions,
): Provider[] {
if (options.useExisting || options.useFactory) {
return [this.createAsyncOptionsProvider(options)];
}
return [
this.createAsyncOptionsProvider(options),
{
provide: options.useClass,
useClass: options.useClass,
},
];
}
private static createAsyncOptionsProvider(
options: TelegrafModuleAsyncOptions,
): Provider {
if (options.useFactory) {
return {
provide: TELEGRAF_MODULE_OPTIONS,
useFactory: options.useFactory,
inject: options.inject || [],
};
}
return {
provide: TELEGRAF_MODULE_OPTIONS,
useFactory: async (optionsFactory: TelegrafOptionsFactory) =>
await optionsFactory.createTelegrafOptions(),
inject: [options.useExisting || options.useClass],
}; };
} }
} }

View File

@ -1,25 +0,0 @@
import { Provider } from '@nestjs/common';
import { Telegraf } from 'telegraf';
import { TELEGRAF_MODULE_OPTIONS } from './telegraf.constants';
import { TelegrafModuleOptions } from './interfaces';
export const TelegrafProvider = {
provide: Telegraf,
useFactory: (options: TelegrafModuleOptions) => {
const telegraf = new Telegraf(options.token, options.options);
if (options.middlewares?.length > 0) {
telegraf.use(...options.middlewares);
}
return telegraf;
},
inject: [TELEGRAF_MODULE_OPTIONS],
};
export function createProviders(options: TelegrafModuleOptions): Provider[] {
return [
{
provide: TELEGRAF_MODULE_OPTIONS,
useValue: options,
},
];
}

14
package-lock.json generated
View File

@ -13,6 +13,7 @@
"devDependencies": { "devDependencies": {
"@nestjs/common": "7.6.5", "@nestjs/common": "7.6.5",
"@nestjs/core": "7.6.5", "@nestjs/core": "7.6.5",
"@types/lodash": "^4.14.167",
"@typescript-eslint/eslint-plugin": "4.11.1", "@typescript-eslint/eslint-plugin": "4.11.1",
"@typescript-eslint/parser": "4.11.1", "@typescript-eslint/parser": "4.11.1",
"eslint": "7.17.0", "eslint": "7.17.0",
@ -20,6 +21,7 @@
"eslint-plugin-import": "2.22.1", "eslint-plugin-import": "2.22.1",
"husky": "4.3.6", "husky": "4.3.6",
"lint-staged": "10.5.3", "lint-staged": "10.5.3",
"lodash": "^4.17.20",
"prettier": "2.2.1", "prettier": "2.2.1",
"reflect-metadata": "0.1.13", "reflect-metadata": "0.1.13",
"rxjs": "6.6.3", "rxjs": "6.6.3",
@ -292,6 +294,12 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true "dev": true
}, },
"node_modules/@types/lodash": {
"version": "4.14.167",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.167.tgz",
"integrity": "sha512-w7tQPjARrvdeBkX/Rwg95S592JwxqOjmms3zWQ0XZgSyxSLdzWaYH3vErBhdVS/lRBX7F8aBYcYJYTr5TMGOzw==",
"dev": true
},
"node_modules/@types/parse-json": { "node_modules/@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
@ -3880,6 +3888,12 @@
"integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=", "integrity": "sha1-7ihweulOEdK4J7y+UnC86n8+ce4=",
"dev": true "dev": true
}, },
"@types/lodash": {
"version": "4.14.167",
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.167.tgz",
"integrity": "sha512-w7tQPjARrvdeBkX/Rwg95S592JwxqOjmms3zWQ0XZgSyxSLdzWaYH3vErBhdVS/lRBX7F8aBYcYJYTr5TMGOzw==",
"dev": true
},
"@types/parse-json": { "@types/parse-json": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",