merge refactor/v2

This commit is contained in:
Morb0
2021-01-05 00:32:31 +03:00
81 changed files with 34665 additions and 4110 deletions

View File

@@ -1,4 +1,5 @@
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,8 +1,8 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createListenerDecorator } from '../../utils';
/**
* Registers middleware for handling callback_data actions with regular expressions.
*
* @see https://telegraf.js.org/#/?id=action
*/
export const Action = createUpdateListenerDecorator('action');
export const Action = createListenerDecorator('action');

View File

@@ -1,8 +1,10 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
/**
* Cashtag handling.
*
* @see https://telegraf.js.org/#/?id=cashtag
*/
export const Cashtag = createUpdateListenerDecorator('cashtag');
export const Cashtag = createMissedListenerDecorator<[string | string[]]>(
'cashtag',
);

View File

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

View File

@@ -1,8 +1,10 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
/**
* Registers middleware for handling messages with email entity.
*
* @see https://telegraf.js.org/#/?id=telegraf-email
*/
export const Email = createUpdateListenerDecorator('email');
export const Email = createMissedListenerDecorator<[string | string[]]>(
'email',
);

View File

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

View File

@@ -1,8 +1,10 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
/**
* Hashtag handling.
*
* @see https://telegraf.js.org/#/?id=hashtag
*/
export const Hashtag = createUpdateListenerDecorator('hashtag');
export const Hashtag = createMissedListenerDecorator<[string | string[]]>(
'hashtag',
);

View File

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

View File

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

View File

@@ -1,8 +1,11 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
import { HearsTriggers } from 'telegraf/typings/composer';
/**
* Registers middleware for handling inline_query actions with regular expressions.
*
* @see https://telegraf.js.org/#/?id=inlinequery
*/
export const InlineQuery = createUpdateListenerDecorator('inlineQuery');
export const InlineQuery = createMissedListenerDecorator<
[HearsTriggers<unknown>]
>('inlineQuery');

View File

@@ -1,8 +1,10 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
/**
* Mention handling.
*
* @see https://telegraf.js.org/#/?id=mention
*/
export const Mention = createUpdateListenerDecorator('mention');
export const Mention = createMissedListenerDecorator<[string | string[]]>(
'mention',
);

View File

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

View File

@@ -1,8 +1,10 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
/**
* Phone number handling.
*
* @see https://telegraf.js.org/#/?id=phone
*/
export const Phone = createUpdateListenerDecorator('phone');
export const Phone = createMissedListenerDecorator<[string | string[]]>(
'phone',
);

View File

@@ -1,8 +1,8 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
/**
* Handler for /settings command.
*
* @see https://telegraf.js.org/#/?id=settings
*/
export const Settings = createUpdateListenerDecorator('settings');
export const Settings = createMissedListenerDecorator<[]>('settings');

View File

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

View File

@@ -1,8 +1,10 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
/**
* Registers middleware for handling messages with text_link entity.
*
* @see https://telegraf.js.org/#/?id=telegraf-textlink
*/
export const TextLink = createUpdateListenerDecorator('textLink');
export const TextLink = createMissedListenerDecorator<[string | string[]]>(
'textLink',
);

View File

@@ -1,8 +1,10 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
/**
* Registers middleware for handling messages with text_mention entity.
*
* @see https://telegraf.js.org/#/?id=telegraf-textlink
*/
export const TextMention = createUpdateListenerDecorator('textMention');
export const TextMention = createMissedListenerDecorator<[string | string[]]>(
'textMention',
);

View File

@@ -1,8 +1,8 @@
import { createUpdateListenerDecorator } from '../../utils';
import { createMissedListenerDecorator } from '../../utils';
/**
* Registers middleware for handling messages with url entity.
*
* @see https://telegraf.js.org/#/?id=telegraf-url
*/
export const Url = createUpdateListenerDecorator('url');
export const Url = createMissedListenerDecorator<[string | string[]]>('url');

View File

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

View File

@@ -1,3 +1,3 @@
import { createSceneListenerDecorator } from '../../utils';
import { createListenerDecorator } from '../../utils';
export const SceneEnter = createSceneListenerDecorator('enter');
export const SceneEnter = createListenerDecorator('enter');

View File

@@ -1,3 +1,3 @@
import { createSceneListenerDecorator } from '../../utils';
import { createListenerDecorator } from '../../utils';
export const SceneLeave = createSceneListenerDecorator('leave');
export const SceneLeave = createListenerDecorator('leave');

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,7 +1,6 @@
export * from './decorators';
export * from './interfaces';
export * from './utils';
export * from './errors';
export * from './execution-context';
export * from './types';
export * from './telegraf.constants';
export * from './telegraf.module';
export * from './telegraf.types';

View File

@@ -1,19 +1,21 @@
import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
import { Middleware, Context } from 'telegraf';
import {
TelegrafOptions,
LaunchPollingOptions,
LaunchWebhookOptions,
TelegrafOptions,
} from 'telegraf/typings/telegraf';
export interface TelegrafModuleOptions<C extends Context = Context> {
token: string;
name?: string;
options?: TelegrafOptions;
launchOptions?: {
polling?: LaunchPollingOptions;
webhook?: LaunchWebhookOptions;
};
middlewares?: Middleware<C>[];
include?: Function[];
middlewares?: ReadonlyArray<Middleware<C>>;
}
export interface TelegrafOptionsFactory {
@@ -22,6 +24,7 @@ export interface TelegrafOptionsFactory {
export interface TelegrafModuleAsyncOptions
extends Pick<ModuleMetadata, 'imports'> {
botName?: string;
useExisting?: Type<TelegrafOptionsFactory>;
useClass?: Type<TelegrafOptionsFactory>;
useFactory?: (

View 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
View File

@@ -0,0 +1,3 @@
export * from './listeners-explorer.service';
export * from './metadata-accessor.service';
export * from './listeners-explorer.service';

View 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);
});
}
}

View File

@@ -4,18 +4,25 @@ import {
SCENE_METADATA,
UPDATE_LISTENER_METADATA,
UPDATE_METADATA,
} from './telegraf.constants';
import { ListenerMetadata } from './interfaces';
} from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces';
@Injectable()
export class TelegrafMetadataAccessor {
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);
}

115
lib/telegraf-core.module.ts Normal file
View File

@@ -0,0 +1,115 @@
import { DiscoveryModule, ModuleRef } from '@nestjs/core';
import {
Module,
DynamicModule,
Provider,
Type,
Global,
Inject,
OnApplicationShutdown,
} from '@nestjs/common';
import {
TelegrafModuleOptions,
TelegrafModuleAsyncOptions,
TelegrafOptionsFactory,
} from './interfaces';
import { TELEGRAF_MODULE_OPTIONS } from './telegraf.constants';
import { MetadataAccessorService, ListenersExplorerService } from './services';
import { getBotToken, createBotFactory } from './utils';
@Global()
@Module({
imports: [DiscoveryModule],
providers: [ListenersExplorerService, MetadataAccessorService],
})
export class TelegrafCoreModule implements OnApplicationShutdown {
constructor(
@Inject(TELEGRAF_MODULE_OPTIONS)
private readonly options: TelegrafModuleOptions,
private readonly moduleRef: ModuleRef,
) {}
public static forRoot(options: TelegrafModuleOptions): DynamicModule {
const telegrafBotProvider: Provider = {
provide: getBotToken(options.name),
useFactory: async () => await createBotFactory(options),
};
return {
module: TelegrafCoreModule,
providers: [
{
provide: TELEGRAF_MODULE_OPTIONS,
useValue: options,
},
telegrafBotProvider,
],
exports: [telegrafBotProvider],
};
}
public static forRootAsync(
options: TelegrafModuleAsyncOptions,
): DynamicModule {
const telegrafBotName = getBotToken(options.botName);
const telegrafBotProvider: Provider = {
provide: telegrafBotName,
useFactory: async (options: TelegrafModuleOptions) =>
await createBotFactory(options),
inject: [TELEGRAF_MODULE_OPTIONS],
};
const asyncProviders = this.createAsyncProviders(options);
return {
module: TelegrafCoreModule,
imports: options.imports,
providers: [...asyncProviders, telegrafBotProvider],
exports: [telegrafBotProvider],
};
}
async onApplicationShutdown(): Promise<void> {
const botName = getBotToken(this.options.name);
const bot = this.moduleRef.get<any>(botName);
bot && (await bot.stop());
}
private static createAsyncProviders(
options: TelegrafModuleAsyncOptions,
): Provider[] {
if (options.useExisting || options.useFactory) {
return [this.createAsyncOptionsProvider(options)];
}
const useClass = options.useClass as Type<TelegrafOptionsFactory>;
return [
this.createAsyncOptionsProvider(options),
{
provide: useClass,
useClass,
},
];
}
private static createAsyncOptionsProvider(
options: TelegrafModuleAsyncOptions,
): Provider {
if (options.useFactory) {
return {
provide: TELEGRAF_MODULE_OPTIONS,
useFactory: options.useFactory,
inject: options.inject || [],
};
}
// `as Type<TelegrafOptionsFactory>` is a workaround for microsoft/TypeScript#31603
const inject = [
(options.useClass || options.useExisting) as Type<TelegrafOptionsFactory>,
];
return {
provide: TELEGRAF_MODULE_OPTIONS,
useFactory: async (optionsFactory: TelegrafOptionsFactory) =>
await optionsFactory.createTelegrafOptions(),
inject,
};
}
}

View File

@@ -1,6 +1,7 @@
import { ROUTE_ARGS_METADATA } from '@nestjs/common/constants';
export const TELEGRAF_MODULE_OPTIONS = 'TELEGRAF_MODULE_OPTIONS';
export const DEFAULT_BOT_NAME = 'DEFAULT_BOT_NAME';
export const UPDATE_METADATA = 'UPDATE_METADATA';
export const UPDATE_LISTENER_METADATA = 'UPDATE_LISTENER_METADATA';

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 {
DynamicModule,
Inject,
Module,
OnApplicationBootstrap,
OnApplicationShutdown,
Provider,
} from '@nestjs/common';
import { Telegraf } from 'telegraf';
import {
TelegrafModuleAsyncOptions,
TelegrafModuleOptions,
TelegrafOptionsFactory,
TelegrafModuleAsyncOptions,
} 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 {
const providers = [...createProviders(options), TelegrafProvider];
return {
module: TelegrafModule,
providers,
exports: providers,
imports: [TelegrafCoreModule.forRoot(options)],
exports: [TelegrafCoreModule],
};
}
public static forRootAsync(
options: TelegrafModuleAsyncOptions,
): DynamicModule {
const providers = [...this.createAsyncProviders(options), TelegrafProvider];
return {
module: TelegrafModule,
imports: options.imports || [],
providers,
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],
imports: [TelegrafCoreModule.forRootAsync(options)],
exports: [TelegrafCoreModule],
};
}
}

View File

@@ -1,23 +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);
telegraf.use(...options.middlewares);
return telegraf;
},
inject: [TELEGRAF_MODULE_OPTIONS],
};
export function createProviders(options: TelegrafModuleOptions): Provider[] {
return [
{
provide: TELEGRAF_MODULE_OPTIONS,
useValue: options,
},
];
}

View File

@@ -17,5 +17,5 @@ export type ComposerMethodArgs<
U extends OnlyFunctionPropertyNames<T> = OnlyFunctionPropertyNames<T>
> = Filter<Parameters<T[U]>, Middleware<never>>;
export type UpdateMethods = OnlyFunctionPropertyNames<Composer<never>>;
export type ComposerMethods = OnlyFunctionPropertyNames<Composer<never>>;
export type SceneMethods = OnlyFunctionPropertyNames<BaseScene<never>>;

View File

@@ -0,0 +1,13 @@
import { Telegraf } from 'telegraf';
import { TelegrafModuleOptions } from '../interfaces';
export async function createBotFactory(
options: TelegrafModuleOptions,
): Promise<Telegraf<never>> {
const bot = new Telegraf<never>(options.token, options.options);
bot.use(...(options.middlewares ?? []));
await bot.launch(options.launchOptions);
return bot;
}

View File

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

View File

@@ -1,18 +0,0 @@
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,18 +0,0 @@
import { SetMetadata } from '@nestjs/common';
import { Composer } from 'telegraf';
import { ComposerMethodArgs, UpdateMethods } from '../telegraf.types';
import { UPDATE_LISTENER_METADATA } from '../telegraf.constants';
import { ListenerMetadata } from '../interfaces';
export function createUpdateListenerDecorator<Method extends UpdateMethods>(
method: Method,
) {
return (
...args: ComposerMethodArgs<Composer<never>, Method>
): MethodDecorator => {
return SetMetadata(UPDATE_LISTENER_METADATA, {
method,
args,
} as ListenerMetadata);
};
}

View File

@@ -0,0 +1,5 @@
import { DEFAULT_BOT_NAME } from '../telegraf.constants';
export function getBotToken(name?: string): string {
return name && name !== DEFAULT_BOT_NAME ? `${name}Bot` : DEFAULT_BOT_NAME;
}

View File

@@ -1,2 +1,3 @@
export * from './create-update-listener-decorator.util';
export * from './create-scene-listener-decorator.util';
export * from './get-bot-token.util';
export * from './create-bot-factory.util';
export * from './create-listener-decorator.util';