mirror of
https://github.com/Maks1mS/nestjs-telegraf.git
synced 2025-03-15 22:43:45 +03:00
chore(release): v1.0.0-alpha.1 (#39)
This commit is contained in:
commit
29e489f619
@ -1,5 +1,4 @@
|
|||||||
{
|
{
|
||||||
"trailingComma": "es5",
|
"trailingComma": "all",
|
||||||
"singleQuote": true,
|
"singleQuote": true
|
||||||
"semi": false
|
|
||||||
}
|
}
|
||||||
|
239
README.md
239
README.md
@ -27,213 +27,126 @@
|
|||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
$ npm i nestjs-telegraf telegraf
|
$ npm i nestjs-telegraf
|
||||||
```
|
```
|
||||||
|
|
||||||
## Usage
|
Once the installation process is complete, we can import the TelegrafModule into the root AppModule.
|
||||||
|
|
||||||
### An example of package usage
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
/* bot.module.ts */
|
/* app.module.ts */
|
||||||
|
|
||||||
import { Module, OnModuleInit, Logger } from '@nestjs/common'
|
import { Module } from '@nestjs/common';
|
||||||
import { ModuleRef } from '@nestjs/core'
|
import { TelegrafModule } from 'nestjs-telegraf';
|
||||||
import { ConfigModule } from '@nestjs/config'
|
|
||||||
import { TelegrafModule, TelegrafService } from 'nestjs-telegraf'
|
|
||||||
import botConfig from './bot.config'
|
|
||||||
import { TelegrafConfigService } from './telegraf-config.service'
|
|
||||||
import { BotService } from './bot.service'
|
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TelegrafModule.fromFactory({
|
TelegrafModule.forRoot({
|
||||||
imports: [ConfigModule.forFeature(botConfig)],
|
token: 'TELEGRAM_BOT_TOKEN',
|
||||||
useClass: TelegrafConfigService,
|
})
|
||||||
}),
|
|
||||||
],
|
],
|
||||||
exports: [TelegrafModule],
|
|
||||||
providers: [BotService],
|
|
||||||
})
|
})
|
||||||
export class BotModule implements OnModuleInit {
|
export class AppModule {}
|
||||||
constructor(
|
|
||||||
private readonly moduleRef: ModuleRef,
|
|
||||||
private readonly telegrafService: TelegrafService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
onModuleInit() {
|
|
||||||
this.telegrafService.init(this.moduleRef)
|
|
||||||
this.telegrafService.startPolling()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
```typescript
|
The `forRoot()` method accepts the same configuration object as Telegraf class constructor from the Telegraf package, as described [here](https://telegraf.js.org/#/?id=constructor).
|
||||||
/* telegraf-config.service.ts */
|
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common'
|
## Telegraf methods
|
||||||
import { TelegrafOptionsFactory, TelegrafModuleOptions } from 'nestjs-telegraf'
|
|
||||||
import { ConfigService } from '@nestjs/config'
|
Each Telegraf instance method described [here](https://telegraf.js.org/#/?id=telegraf) has own decorator in `nestjs-telegraf` package. The name of the decorator corresponds to the name of the Telegraf method and starts with `Telegraf`. For example [`@TelegrafHears`](https://telegraf.js.org/#/?id=hears), [`@TelegrafOn`](https://telegraf.js.org/#/?id=on), [`@TelegrafAction`](https://telegraf.js.org/#/?id=action) and so on.
|
||||||
|
|
||||||
|
Now let's try to repeat the example from the Telegraf [documentation page](https://telegraf.js.org/#/?id=example).
|
||||||
|
|
||||||
|
```typescript
|
||||||
|
/* app.service.ts */
|
||||||
|
|
||||||
|
import { Injectable } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
TelegrafStart,
|
||||||
|
TelegrafHelp,
|
||||||
|
TelegrafOn,
|
||||||
|
TelegrafHears,
|
||||||
|
ContextMessageUpdate,
|
||||||
|
} from 'nestjs-telegraf';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TelegrafConfigService implements TelegrafOptionsFactory {
|
export class AppService {
|
||||||
constructor(private readonly configService: ConfigService) {}
|
@TelegrafStart()
|
||||||
|
start(ctx: ContextMessageUpdate) {
|
||||||
createTelegrafOptions(): TelegrafModuleOptions {
|
ctx.reply('Welcome');
|
||||||
return {
|
|
||||||
token: this.configService.get('bot.token'),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
/* bot.config.ts */
|
|
||||||
|
|
||||||
import { registerAs } from '@nestjs/config'
|
|
||||||
|
|
||||||
interface Config {
|
|
||||||
token: string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default registerAs(
|
@TelegrafHelp()
|
||||||
'bot',
|
help(ctx: ContextMessageUpdate) {
|
||||||
(): Config => ({
|
ctx.reply('Send me a sticker');
|
||||||
token: process.env.TELEGRAM_BOT_TOKEN,
|
}
|
||||||
})
|
|
||||||
)
|
|
||||||
```
|
|
||||||
|
|
||||||
### Telegraf
|
@TelegrafOn('sticker')
|
||||||
|
on(ctx: ContextMessageUpdate) {
|
||||||
|
ctx.reply('👍');
|
||||||
|
}
|
||||||
|
|
||||||
#### Telegraf methods usage
|
@TelegrafHears('hi')
|
||||||
You can decorate any `Telegraf` method with `@TelegramActionHandler` decorator.
|
hears(ctx: ContextMessageUpdate) {
|
||||||
|
ctx.reply('Hey there');
|
||||||
```typescript
|
|
||||||
/* bot.service.ts */
|
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common'
|
|
||||||
import { TelegrafTelegramService } from 'nestjs-telegraf'
|
|
||||||
import { ContextMessageUpdate } from 'telegraf'
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class BotService {
|
|
||||||
/* This decorator handle /start command */
|
|
||||||
@TelegramActionHandler({ onStart: true })
|
|
||||||
async onStart(ctx: ContextMessageUpdate) {
|
|
||||||
await ctx.reply('/start command reply')
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
##### Today available actions for decorator:
|
## Async configuration
|
||||||
|
When you need to pass module options asynchronously instead of statically, use the forRootAsync() method. As with most dynamic modules, Nest provides several techniques to deal with async configuration.
|
||||||
|
|
||||||
- [`onStart`](https://telegraf.js.org/#/?id=start) Handler for /start command.
|
One technique is to use a factory function:
|
||||||
|
|
||||||
- [`command`](https://telegraf.js.org/#/?id=command) Command handling.
|
|
||||||
|
|
||||||
- [`message`](https://telegraf.js.org/#/?id=hears) Registers middleware for handling text messages.
|
|
||||||
|
|
||||||
- [`action`](https://telegraf.js.org/#/?id=action) Registers middleware for handling `callback_data` actions with regular expressions.
|
|
||||||
|
|
||||||
#### Telegraf middlewares usage
|
|
||||||
|
|
||||||
See https://github.com/bukhalo/nestjs-telegraf/issues/7#issuecomment-577582322
|
|
||||||
|
|
||||||
#### Telegraf proxy usage
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
TelegrafModule.forRootAsync({
|
||||||
/* bot.config.ts */
|
useFactory: () => ({
|
||||||
|
token: 'TELEGRAM_BOT_TOKEN',
|
||||||
import { registerAs } from '@nestjs/config'
|
|
||||||
|
|
||||||
interface Config {
|
|
||||||
token: string
|
|
||||||
socksHost: string
|
|
||||||
socksPort: string | number
|
|
||||||
socksUser: string
|
|
||||||
socksPassword: string
|
|
||||||
}
|
|
||||||
|
|
||||||
export default registerAs(
|
|
||||||
'bot',
|
|
||||||
(): Config => ({
|
|
||||||
token: process.env.TELEGRAM_BOT_TOKEN,
|
|
||||||
socksHost: process.env.TELEGRAM_BOT_SOCKS_HOST,
|
|
||||||
socksPort: process.env.TELEGRAM_BOT_SOCKS_PORT,
|
|
||||||
socksUser: process.env.TELEGRAM_BOT_SOCKS_USER,
|
|
||||||
socksPassword: process.env.TELEGRAM_BOT_SOCKS_PASS,
|
|
||||||
}),
|
}),
|
||||||
);
|
});
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
|
Like other [factory providers](https://docs.nestjs.com/fundamentals/custom-providers#factory-providers-usefactory), our factory function can be async and can inject dependencies through inject.
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
|
TelegrafModule.forRootAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
|
useFactory: async (configService: ConfigService) => ({
|
||||||
|
token: configService.getString('TELEGRAM_BOT_TOKEN'),
|
||||||
|
}),
|
||||||
|
inject: [ConfigService],
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
/* telegraf-config.service.ts */
|
Alternatively, you can configure the TelegrafModule using a class instead of a factory, as shown below:
|
||||||
|
|
||||||
import { Injectable } from '@nestjs/common'
|
```typescript
|
||||||
import { ConfigService } from '@nestjs/config'
|
TelegrafModule.forRootAsync({
|
||||||
import { TelegrafModuleOptions, TelegrafOptionsFactory } from 'nestjs-telegraf'
|
useClass: TelegrafConfigService,
|
||||||
import { SocksProxyAgent } from 'socks-proxy-agent'
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
The construction above instantiates `TelegrafConfigService` inside `TelegrafModule`, using it to create the required options object. Note that in this example, the `TelegrafConfigService` has to implement the `TelegrafOptionsFactory` interface, as shown below. The `TelegrafModule` will call the `createTelegrafOptions()` method on the instantiated object of the supplied class.
|
||||||
|
|
||||||
|
```typescript
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class TelegrafConfigService implements TelegrafOptionsFactory {
|
class TelegrafConfigService implements TelegrafOptionsFactory {
|
||||||
private agent
|
createMongooseOptions(): TelegrafModuleOptions {
|
||||||
|
|
||||||
constructor(private readonly configService: ConfigService) {}
|
|
||||||
|
|
||||||
createTelegrafOptions(): TelegrafModuleOptions {
|
|
||||||
const proxyConfig = {
|
|
||||||
host: this.configService.get('bot.socksHost'),
|
|
||||||
port: this.configService.get('bot.socksPort'),
|
|
||||||
userId: this.configService.get('bot.socksUser'),
|
|
||||||
password: this.configService.get('bot.socksPassword'),
|
|
||||||
}
|
|
||||||
this.agent = new SocksProxyAgent(proxyConfig)
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
token: this.configService.get('bot.token'),
|
token: 'TELEGRAM_BOT_TOKEN',
|
||||||
telegrafOptions: { telegram: { agent: this.agent } },
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Telegram
|
If you want to reuse an existing options provider instead of creating a private copy inside the `TelegrafModule`, use the `useExisting` syntax.
|
||||||
|
|
||||||
#### Telegram methods usage
|
|
||||||
|
|
||||||
Inject `TelegrafTelegramService` from `nestjs-telegraf` package for use [Telegram instance](https://telegraf.js.org/#/?id=telegram) from `telegraf` package.
|
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
/* bot.service.ts */
|
TelegrafModule.forRootAsync({
|
||||||
|
imports: [ConfigModule],
|
||||||
import { Injectable } from '@nestjs/common'
|
useExisting: ConfigService,
|
||||||
import { TelegrafTelegramService, TelegramActionHandler } from 'nestjs-telegraf'
|
});
|
||||||
import { ContextMessageUpdate } from 'telegraf'
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class BotService {
|
|
||||||
constructor(
|
|
||||||
private readonly telegrafTelegramService: TelegrafTelegramService
|
|
||||||
) {}
|
|
||||||
|
|
||||||
@TelegramActionHandler({ onStart: true })
|
|
||||||
async start(ctx: ContextMessageUpdate) {
|
|
||||||
const me = await this.telegrafTelegramService.getMe()
|
|
||||||
console.log(me)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
You can see the basic use of the package in this repository:
|
|
||||||
https://github.com/bukhalo/nestjs-telegraf-sample
|
|
||||||
|
|
||||||
## Support
|
## Support
|
||||||
|
|
||||||
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
Nest is an MIT-licensed open source project. It can grow thanks to the sponsors and support by the amazing backers. If you'd like to join them, please [read more here](https://docs.nestjs.com/support).
|
||||||
|
@ -1,3 +1,15 @@
|
|||||||
export * from './pipe-context.decorator'
|
export * from './telegraf-use.decorator';
|
||||||
export * from './telegram-action-handler.decorator'
|
export * from './telegraf-on.decorator';
|
||||||
export * from './telegram-catch.decorator'
|
export * from './telegraf-hears.decorator';
|
||||||
|
export * from './telegraf-command.decorator';
|
||||||
|
export * from './telegraf-start.decorator';
|
||||||
|
export * from './telegraf-help.decorator';
|
||||||
|
export * from './telegraf-settings.decorator';
|
||||||
|
export * from './telegraf-entity.decorator';
|
||||||
|
export * from './telegraf-mention.decorator';
|
||||||
|
export * from './telegraf-phone.decorator';
|
||||||
|
export * from './telegraf-hashtag.decorator';
|
||||||
|
export * from './telegraf-cashtag.decorator';
|
||||||
|
export * from './telegraf-action.decorator';
|
||||||
|
export * from './telegraf-inline-query.decorator';
|
||||||
|
export * from './telegraf-game-query.decorator';
|
||||||
|
@ -1,18 +0,0 @@
|
|||||||
import { Type } from '@nestjs/common'
|
|
||||||
import { ContextTransformer } from '../interfaces'
|
|
||||||
import { addHandlerToStore } from './'
|
|
||||||
|
|
||||||
export const PipeContext = <T>(transform: Type<ContextTransformer<T>>) => (
|
|
||||||
target: Object,
|
|
||||||
propertyKey: string,
|
|
||||||
parameterIndex: number,
|
|
||||||
) => {
|
|
||||||
addHandlerToStore(target, propertyKey, {
|
|
||||||
transformations: [
|
|
||||||
{
|
|
||||||
index: parameterIndex,
|
|
||||||
transform,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
21
lib/decorators/telegraf-action.decorator.ts
Normal file
21
lib/decorators/telegraf-action.decorator.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
import { HearsTriggers } from 'telegraf';
|
||||||
|
|
||||||
|
export type TelegrafActionTriggers = HearsTriggers;
|
||||||
|
|
||||||
|
export interface TelegrafActionMetadata {
|
||||||
|
triggers: TelegrafActionTriggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for handling callback_data actions with regular expressions.
|
||||||
|
* @param triggers Triggers
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=action
|
||||||
|
*/
|
||||||
|
export function TelegrafAction(
|
||||||
|
triggers: TelegrafActionTriggers,
|
||||||
|
): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.ACTION, { triggers });
|
||||||
|
}
|
20
lib/decorators/telegraf-cashtag.decorator.ts
Normal file
20
lib/decorators/telegraf-cashtag.decorator.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
export type TelegrafCashtagCashtag = string | string[];
|
||||||
|
|
||||||
|
export interface TelegrafCashtagMetadata {
|
||||||
|
cashtag: TelegrafCashtagCashtag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cashtag handling.
|
||||||
|
* @param cashtag Cashtag
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=cashtag
|
||||||
|
*/
|
||||||
|
export function TelegrafCashtag(
|
||||||
|
cashtag: TelegrafCashtagCashtag,
|
||||||
|
): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.CASHTAG, { cashtag });
|
||||||
|
}
|
20
lib/decorators/telegraf-command.decorator.ts
Normal file
20
lib/decorators/telegraf-command.decorator.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
export type TelegrafCommandCommands = string | string[];
|
||||||
|
|
||||||
|
export interface TelegrafCommandMetadata {
|
||||||
|
commands: TelegrafCommandCommands;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command handling.
|
||||||
|
* @param commands Commands
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=command
|
||||||
|
*/
|
||||||
|
export function TelegrafCommand(
|
||||||
|
commands: TelegrafCommandCommands,
|
||||||
|
): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.COMMAND, { commands });
|
||||||
|
}
|
23
lib/decorators/telegraf-entity.decorator.ts
Normal file
23
lib/decorators/telegraf-entity.decorator.ts
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
export type TelegrafEntityEntity =
|
||||||
|
| string
|
||||||
|
| string[]
|
||||||
|
| RegExp
|
||||||
|
| RegExp[]
|
||||||
|
| Function;
|
||||||
|
|
||||||
|
export interface TelegrafEntityMetadata {
|
||||||
|
entity: TelegrafEntityEntity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Entity handling.
|
||||||
|
* @param entity Entity name
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=entity
|
||||||
|
*/
|
||||||
|
export function TelegrafEntity(entity: TelegrafEntityEntity): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.ENTITY, { entity });
|
||||||
|
}
|
11
lib/decorators/telegraf-game-query.decorator.ts
Normal file
11
lib/decorators/telegraf-game-query.decorator.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for handling callback_data actions with game query.
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=inlinequery
|
||||||
|
*/
|
||||||
|
export function TelegrafGameQuery(): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.GAME_QUERY, {});
|
||||||
|
}
|
20
lib/decorators/telegraf-hashtag.decorator.ts
Normal file
20
lib/decorators/telegraf-hashtag.decorator.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
export type TelegrafHashtagHashtag = string | string[];
|
||||||
|
|
||||||
|
export interface TelegrafHashtagMetadata {
|
||||||
|
hashtag: TelegrafHashtagHashtag;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Hashtag handling.
|
||||||
|
* @param hashtag Hashtag
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=hashtag
|
||||||
|
*/
|
||||||
|
export function TelegrafHashtag(
|
||||||
|
hashtag: TelegrafHashtagHashtag,
|
||||||
|
): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.HASHTAG, { hashtag });
|
||||||
|
}
|
21
lib/decorators/telegraf-hears.decorator.ts
Normal file
21
lib/decorators/telegraf-hears.decorator.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
import { HearsTriggers } from 'telegraf';
|
||||||
|
|
||||||
|
export type TelegrafHearsTriggers = HearsTriggers;
|
||||||
|
|
||||||
|
export interface TelegrafHearsMetadata {
|
||||||
|
triggers: TelegrafHearsTriggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for handling text messages.
|
||||||
|
* @param triggers Triggers
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=hears
|
||||||
|
*/
|
||||||
|
export function TelegrafHears(
|
||||||
|
triggers: TelegrafHearsTriggers,
|
||||||
|
): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.HEARS, { triggers: triggers });
|
||||||
|
}
|
11
lib/decorators/telegraf-help.decorator.ts
Normal file
11
lib/decorators/telegraf-help.decorator.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for /help command.
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=help
|
||||||
|
*/
|
||||||
|
export function TelegrafHelp(): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.HELP, {});
|
||||||
|
}
|
20
lib/decorators/telegraf-inline-query.decorator.ts
Normal file
20
lib/decorators/telegraf-inline-query.decorator.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
export type TelegrafInlineQueryTriggers = string | string[] | RegExp | RegExp[];
|
||||||
|
|
||||||
|
export interface TelegrafInlineQueryMetadata {
|
||||||
|
triggers: TelegrafInlineQueryTriggers;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for handling inline_query actions with regular expressions.
|
||||||
|
* @param triggers Triggers
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=inlinequery
|
||||||
|
*/
|
||||||
|
export function TelegrafInlineQuery(
|
||||||
|
triggers: TelegrafInlineQueryTriggers,
|
||||||
|
): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.INLINE_QUERY, { triggers });
|
||||||
|
}
|
20
lib/decorators/telegraf-mention.decorator.ts
Normal file
20
lib/decorators/telegraf-mention.decorator.ts
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
export type TelegrafMentionUsername = string | string[];
|
||||||
|
|
||||||
|
export interface TelegrafMentionMetadata {
|
||||||
|
username: TelegrafMentionUsername;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Mention handling.
|
||||||
|
* @param username Username
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=mention
|
||||||
|
*/
|
||||||
|
export function TelegrafMention(
|
||||||
|
username: TelegrafMentionUsername,
|
||||||
|
): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.MENTION, { username });
|
||||||
|
}
|
25
lib/decorators/telegraf-on.decorator.ts
Normal file
25
lib/decorators/telegraf-on.decorator.ts
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
import { UpdateType, MessageSubTypes } from 'telegraf/typings/telegram-types';
|
||||||
|
|
||||||
|
export type TelegrafOnUpdateTypes =
|
||||||
|
| UpdateType
|
||||||
|
| UpdateType[]
|
||||||
|
| MessageSubTypes
|
||||||
|
| MessageSubTypes[];
|
||||||
|
|
||||||
|
export interface TelegrafOnMetadata {
|
||||||
|
updateTypes: TelegrafOnUpdateTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers middleware for provided update type.
|
||||||
|
* @param updateTypes Update type
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=on
|
||||||
|
*/
|
||||||
|
export function TelegrafOn(
|
||||||
|
updateTypes: TelegrafOnUpdateTypes,
|
||||||
|
): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.ON, { updateTypes: updateTypes });
|
||||||
|
}
|
18
lib/decorators/telegraf-phone.decorator.ts
Normal file
18
lib/decorators/telegraf-phone.decorator.ts
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
export type TelegrafPhonePhone = string | string[];
|
||||||
|
|
||||||
|
export interface TelegrafPhoneMetadata {
|
||||||
|
phone: TelegrafPhonePhone;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Phone number handling.
|
||||||
|
* @param phone Phone number
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=phone
|
||||||
|
*/
|
||||||
|
export function TelegrafPhone(phone: TelegrafPhonePhone): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.PHONE, { phone });
|
||||||
|
}
|
11
lib/decorators/telegraf-settings.decorator.ts
Normal file
11
lib/decorators/telegraf-settings.decorator.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for /settings command.
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=settings
|
||||||
|
*/
|
||||||
|
export function TelegrafSettings(): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.SETTINGS, {});
|
||||||
|
}
|
11
lib/decorators/telegraf-start.decorator.ts
Normal file
11
lib/decorators/telegraf-start.decorator.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handler for /start command.
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=start
|
||||||
|
*/
|
||||||
|
export function TelegrafStart(): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.START, {});
|
||||||
|
}
|
11
lib/decorators/telegraf-use.decorator.ts
Normal file
11
lib/decorators/telegraf-use.decorator.ts
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
import { SetMetadata } from '@nestjs/common';
|
||||||
|
import { DECORATORS } from '../telegraf.constants';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers a middleware.
|
||||||
|
*
|
||||||
|
* https://telegraf.js.org/#/?id=use
|
||||||
|
*/
|
||||||
|
export function TelegrafUse(): MethodDecorator {
|
||||||
|
return SetMetadata(DECORATORS.USE, {});
|
||||||
|
}
|
@ -1,44 +0,0 @@
|
|||||||
import { HandleParameters } from '../interfaces'
|
|
||||||
|
|
||||||
type Decorator = (
|
|
||||||
params: HandleParameters,
|
|
||||||
) => (target: any, propertyKey: string, descriptor: PropertyDescriptor) => void
|
|
||||||
|
|
||||||
type HandlerDecorator = Decorator & {
|
|
||||||
handlers?: Map<any, Map<string, HandleParameters>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TelegramActionHandler: HandlerDecorator = (
|
|
||||||
parameters: HandleParameters,
|
|
||||||
) => (target: any, propertyKey: string) => {
|
|
||||||
// eslint-disable-next-line no-use-before-define
|
|
||||||
addHandlerToStore(target, propertyKey, parameters)
|
|
||||||
}
|
|
||||||
|
|
||||||
export const addHandlerToStore = (
|
|
||||||
instance: Object,
|
|
||||||
name: string,
|
|
||||||
config: HandleParameters,
|
|
||||||
) => {
|
|
||||||
const handlerClass = instance.constructor
|
|
||||||
|
|
||||||
if (!TelegramActionHandler.handlers) {
|
|
||||||
TelegramActionHandler.handlers = new Map()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TelegramActionHandler.handlers.get(handlerClass)) {
|
|
||||||
TelegramActionHandler.handlers.set(handlerClass, new Map())
|
|
||||||
}
|
|
||||||
|
|
||||||
const oldParameters =
|
|
||||||
TelegramActionHandler.handlers.get(handlerClass).get(name) || {}
|
|
||||||
|
|
||||||
TelegramActionHandler.handlers.get(handlerClass).set(name, {
|
|
||||||
...oldParameters,
|
|
||||||
...config,
|
|
||||||
transformations: [
|
|
||||||
...(oldParameters.transformations || []),
|
|
||||||
...(config.transformations || []),
|
|
||||||
],
|
|
||||||
})
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
import { Type } from '@nestjs/common'
|
|
||||||
import { TelegramErrorHandler } from '../interfaces'
|
|
||||||
|
|
||||||
type Decorator = (error: any) => ClassDecorator
|
|
||||||
|
|
||||||
type HandlerDecorator = Decorator & {
|
|
||||||
handlers?: Map<Error, Type<TelegramErrorHandler>>
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TelegramCatch: HandlerDecorator = error => target => {
|
|
||||||
if (!TelegramCatch.handlers) {
|
|
||||||
TelegramCatch.handlers = new Map()
|
|
||||||
}
|
|
||||||
|
|
||||||
TelegramCatch.handlers.set(error, target as any)
|
|
||||||
|
|
||||||
return target
|
|
||||||
}
|
|
@ -1 +0,0 @@
|
|||||||
export * from './invalid-configuration.exeption'
|
|
@ -1,10 +0,0 @@
|
|||||||
export class InvalidConfigurationException extends Error {
|
|
||||||
public constructor(
|
|
||||||
public readonly invalidField,
|
|
||||||
public readonly invalidCause,
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
`Options validation failed, "${invalidField}" invalid — ${invalidCause}`,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,5 +1,3 @@
|
|||||||
export * from './telegraf.module'
|
export * from './telegraf.module';
|
||||||
export * from './interfaces'
|
export * from './interfaces';
|
||||||
export * from './decorators'
|
export * from './decorators';
|
||||||
export * from './telegraf.service'
|
|
||||||
export * from './telegraf-telegram.service'
|
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { ContextMessageUpdate } from 'telegraf'
|
|
||||||
|
|
||||||
export interface ContextTransformer<T = any> {
|
|
||||||
transform: (ctx: ContextMessageUpdate) => Promise<T>
|
|
||||||
}
|
|
@ -1,18 +0,0 @@
|
|||||||
import { ContextTransformer } from './'
|
|
||||||
import { HearsTriggers } from 'telegraf'
|
|
||||||
import { UpdateType, MessageSubTypes } from 'telegraf/typings/telegram-types'
|
|
||||||
import { Type } from '@nestjs/common'
|
|
||||||
|
|
||||||
interface ArgumentTransformation {
|
|
||||||
index: number
|
|
||||||
transform: Type<ContextTransformer>
|
|
||||||
}
|
|
||||||
|
|
||||||
export interface HandleParameters {
|
|
||||||
onStart?: boolean
|
|
||||||
on?: UpdateType | UpdateType[] | MessageSubTypes | MessageSubTypes[]
|
|
||||||
command?: string
|
|
||||||
message?: string | RegExp
|
|
||||||
action?: HearsTriggers
|
|
||||||
transformations?: ArgumentTransformation[]
|
|
||||||
}
|
|
@ -1,6 +0,0 @@
|
|||||||
import { HandleParameters } from './'
|
|
||||||
|
|
||||||
export interface Handler {
|
|
||||||
handle: (...args: any[]) => Promise<void>
|
|
||||||
config: HandleParameters
|
|
||||||
}
|
|
@ -1,5 +1,2 @@
|
|||||||
export * from './telegraf-options.interface'
|
export { ContextMessageUpdate } from 'telegraf';
|
||||||
export * from './handler.interface'
|
export * from './telegraf-options.interface';
|
||||||
export * from './handle-parameters.interface'
|
|
||||||
export * from './telegram-error-handler.interface'
|
|
||||||
export * from './context-transformer.interface'
|
|
||||||
|
@ -1,22 +1,21 @@
|
|||||||
import { ModuleMetadata, Type } from '@nestjs/common/interfaces'
|
import { ModuleMetadata, Type } from '@nestjs/common/interfaces';
|
||||||
import { TelegrafOptions } from 'telegraf'
|
import { TelegrafOptions } from 'telegraf';
|
||||||
|
|
||||||
export interface TelegrafModuleOptions {
|
export interface TelegrafModuleOptions {
|
||||||
token: string
|
token: string;
|
||||||
sitePublicUrl?: string
|
options?: TelegrafOptions;
|
||||||
telegrafOptions?: TelegrafOptions
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TelegrafOptionsFactory {
|
export interface TelegrafOptionsFactory {
|
||||||
createTelegrafOptions(): TelegrafModuleOptions
|
createTelegrafOptions(): TelegrafModuleOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface TelegrafModuleAsyncOptions
|
export interface TelegrafModuleAsyncOptions
|
||||||
extends Pick<ModuleMetadata, 'imports'> {
|
extends Pick<ModuleMetadata, 'imports'> {
|
||||||
useExisting?: Type<TelegrafOptionsFactory>
|
useExisting?: Type<TelegrafOptionsFactory>;
|
||||||
useClass?: Type<TelegrafOptionsFactory>
|
useClass?: Type<TelegrafOptionsFactory>;
|
||||||
useFactory?: (
|
useFactory?: (
|
||||||
...args: any[]
|
...args: any[]
|
||||||
) => Promise<TelegrafModuleOptions> | TelegrafModuleOptions
|
) => Promise<TelegrafModuleOptions> | TelegrafModuleOptions;
|
||||||
inject?: any[]
|
inject?: any[];
|
||||||
}
|
}
|
||||||
|
@ -1,5 +0,0 @@
|
|||||||
import { ContextMessageUpdate } from 'telegraf'
|
|
||||||
|
|
||||||
export interface TelegramErrorHandler<E = any> {
|
|
||||||
catch(ctx: ContextMessageUpdate, error: E): Promise<void>
|
|
||||||
}
|
|
91
lib/telegraf-core.module.ts
Normal file
91
lib/telegraf-core.module.ts
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
import { Module, DynamicModule, Provider, Type } from '@nestjs/common';
|
||||||
|
import {
|
||||||
|
TelegrafModuleOptions,
|
||||||
|
TelegrafModuleAsyncOptions,
|
||||||
|
TelegrafOptionsFactory,
|
||||||
|
} from './interfaces';
|
||||||
|
import {
|
||||||
|
TELEGRAF_MODULE_OPTIONS,
|
||||||
|
TELEGRAF_PROVIDER,
|
||||||
|
} from './telegraf.constants';
|
||||||
|
import { TelegrafMetadataAccessor } from './telegraf-metadata.accessor';
|
||||||
|
import { TelegrafExplorer } from './telegraf.explorer';
|
||||||
|
import { DiscoveryModule } from '@nestjs/core';
|
||||||
|
import { TelegrafProvider } from './telegraf.provider';
|
||||||
|
|
||||||
|
@Module({
|
||||||
|
imports: [DiscoveryModule],
|
||||||
|
providers: [TelegrafMetadataAccessor, TelegrafExplorer],
|
||||||
|
})
|
||||||
|
export class TelegrafCoreModule {
|
||||||
|
public static forRoot(options: TelegrafModuleOptions): DynamicModule {
|
||||||
|
const telegrafProvider = {
|
||||||
|
provide: TELEGRAF_PROVIDER,
|
||||||
|
useClass: TelegrafProvider,
|
||||||
|
inject: [TELEGRAF_MODULE_OPTIONS],
|
||||||
|
};
|
||||||
|
return {
|
||||||
|
module: TelegrafCoreModule,
|
||||||
|
providers: [
|
||||||
|
{ provide: TELEGRAF_MODULE_OPTIONS, useValue: options },
|
||||||
|
telegrafProvider,
|
||||||
|
],
|
||||||
|
exports: [telegrafProvider],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static forRootAsync(
|
||||||
|
options: TelegrafModuleAsyncOptions,
|
||||||
|
): DynamicModule {
|
||||||
|
const telegrafProvider = {
|
||||||
|
provide: TELEGRAF_PROVIDER,
|
||||||
|
useClass: TelegrafProvider,
|
||||||
|
inject: [TELEGRAF_MODULE_OPTIONS],
|
||||||
|
};
|
||||||
|
const asyncProviders = this.createAsyncProviders(options);
|
||||||
|
return {
|
||||||
|
module: TelegrafCoreModule,
|
||||||
|
imports: options.imports,
|
||||||
|
providers: [...asyncProviders, telegrafProvider],
|
||||||
|
exports: [telegrafProvider],
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
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,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
186
lib/telegraf-metadata.accessor.ts
Normal file
186
lib/telegraf-metadata.accessor.ts
Normal file
@ -0,0 +1,186 @@
|
|||||||
|
import { Injectable, Type } from '@nestjs/common';
|
||||||
|
import { Reflector } from '@nestjs/core';
|
||||||
|
import {
|
||||||
|
TelegrafActionMetadata,
|
||||||
|
TelegrafCashtagMetadata,
|
||||||
|
TelegrafCommandMetadata,
|
||||||
|
TelegrafEntityMetadata,
|
||||||
|
TelegrafHashtagMetadata,
|
||||||
|
TelegrafHearsMetadata,
|
||||||
|
TelegrafInlineQueryMetadata,
|
||||||
|
TelegrafMentionMetadata,
|
||||||
|
TelegrafOnMetadata,
|
||||||
|
TelegrafPhoneMetadata,
|
||||||
|
TelegrafStart,
|
||||||
|
} from './decorators';
|
||||||
|
import { DECORATORS } from './telegraf.constants';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TelegrafMetadataAccessor {
|
||||||
|
constructor(private readonly reflector: Reflector) {}
|
||||||
|
|
||||||
|
isTelegrafUse(target: Type<any> | Function): boolean {
|
||||||
|
if (!target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !!this.reflector.get(DECORATORS.USE, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
isTelegrafOn(target: Type<any> | Function): boolean {
|
||||||
|
if (!target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !!this.reflector.get(DECORATORS.ON, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTelegrafOnMetadata(
|
||||||
|
target: Type<any> | Function,
|
||||||
|
): TelegrafOnMetadata | undefined {
|
||||||
|
return this.reflector.get(DECORATORS.ON, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
isTelegrafHears(target: Type<any> | Function): boolean {
|
||||||
|
if (!target) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return !!this.reflector.get(DECORATORS.HEARS, target);
|
||||||
|
}
|
||||||
|
|
||||||
|
getTelegrafHearsMetadata(
|
||||||
|
target: Type<any> | Function,
|
||||||
|
): TelegrafHearsMetadata | 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,
|
||||||
|
): TelegrafCommandMetadata | 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,
|
||||||
|
): TelegrafEntityMetadata | 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,
|
||||||
|
): TelegrafMentionMetadata | 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,
|
||||||
|
): TelegrafPhoneMetadata | 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,
|
||||||
|
): TelegrafHashtagMetadata | 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,
|
||||||
|
): TelegrafCashtagMetadata | 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,
|
||||||
|
): TelegrafActionMetadata | 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,
|
||||||
|
): TelegrafInlineQueryMetadata | 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);
|
||||||
|
}
|
||||||
|
}
|
@ -1,13 +0,0 @@
|
|||||||
import { Injectable, Inject } from '@nestjs/common';
|
|
||||||
const Telegram = require('telegraf/telegram');
|
|
||||||
import { Telegram as TelegramClient } from 'telegraf';
|
|
||||||
|
|
||||||
import { TELEGRAF_MODULE_OPTIONS } from './telegraf.constants';
|
|
||||||
import { TelegrafModuleOptions } from './interfaces';
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TelegrafTelegramService extends TelegramClient {
|
|
||||||
constructor(@Inject(TELEGRAF_MODULE_OPTIONS) options: TelegrafModuleOptions) {
|
|
||||||
super(options.token, {});
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,2 +1,21 @@
|
|||||||
export const TELEGRAF_MODULE_OPTIONS = 'TELEGRAF_MODULE_OPTIONS'
|
export const TELEGRAF_MODULE_OPTIONS = 'TELEGRAF_MODULE_OPTIONS';
|
||||||
export const TokenInjectionToken = Symbol('TokenInjectionToken')
|
export const TELEGRAF_PROVIDER = 'TELEGRAF_PROVIDER';
|
||||||
|
|
||||||
|
export const DECORATORS_PREFIX = 'TELEGRAF';
|
||||||
|
export const DECORATORS = {
|
||||||
|
USE: `${DECORATORS_PREFIX}/USE`,
|
||||||
|
ON: `${DECORATORS_PREFIX}/ON`,
|
||||||
|
HEARS: `${DECORATORS_PREFIX}/HEARS`,
|
||||||
|
COMMAND: `${DECORATORS_PREFIX}/COMMAND`,
|
||||||
|
START: `${DECORATORS_PREFIX}/START`,
|
||||||
|
HELP: `${DECORATORS_PREFIX}/HELP`,
|
||||||
|
SETTINGS: `${DECORATORS_PREFIX}/SETTINGS`,
|
||||||
|
ENTITY: `${DECORATORS_PREFIX}/ENTITY`,
|
||||||
|
MENTION: `${DECORATORS_PREFIX}/MENTION`,
|
||||||
|
PHONE: `${DECORATORS_PREFIX}/PHONE`,
|
||||||
|
HASHTAG: `${DECORATORS_PREFIX}/HASHTAG`,
|
||||||
|
CASHTAG: `${DECORATORS_PREFIX}/CASHTAG`,
|
||||||
|
ACTION: `${DECORATORS_PREFIX}/ACTION`,
|
||||||
|
INLINE_QUERY: `${DECORATORS_PREFIX}/INLINE_QUERY`,
|
||||||
|
GAME_QUERY: `${DECORATORS_PREFIX}/GAME_QUERY`,
|
||||||
|
};
|
||||||
|
258
lib/telegraf.explorer.ts
Normal file
258
lib/telegraf.explorer.ts
Normal file
@ -0,0 +1,258 @@
|
|||||||
|
import { Injectable, OnModuleInit } from '@nestjs/common';
|
||||||
|
import { DiscoveryService, ModuleRef } from '@nestjs/core';
|
||||||
|
import { InstanceWrapper } from '@nestjs/core/injector/instance-wrapper';
|
||||||
|
import { MetadataScanner } from '@nestjs/core/metadata-scanner';
|
||||||
|
import Telegraf from 'telegraf';
|
||||||
|
import { TelegrafMetadataAccessor } from './telegraf-metadata.accessor';
|
||||||
|
import { TelegrafProvider } from './telegraf.provider';
|
||||||
|
import { TELEGRAF_PROVIDER } from './telegraf.constants';
|
||||||
|
import { ContextMessageUpdate } from 'telegraf';
|
||||||
|
import {
|
||||||
|
TelegrafActionMetadata,
|
||||||
|
TelegrafCashtagMetadata,
|
||||||
|
TelegrafCommandMetadata,
|
||||||
|
TelegrafEntityMetadata,
|
||||||
|
TelegrafHashtagMetadata,
|
||||||
|
TelegrafHearsMetadata,
|
||||||
|
TelegrafInlineQueryMetadata,
|
||||||
|
TelegrafMentionMetadata,
|
||||||
|
TelegrafOnMetadata,
|
||||||
|
TelegrafPhoneMetadata,
|
||||||
|
} from './decorators';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class TelegrafExplorer implements OnModuleInit {
|
||||||
|
constructor(
|
||||||
|
private readonly moduleRef: ModuleRef,
|
||||||
|
private readonly discoveryService: DiscoveryService,
|
||||||
|
private readonly metadataAccessor: TelegrafMetadataAccessor,
|
||||||
|
private readonly metadataScanner: MetadataScanner,
|
||||||
|
) {}
|
||||||
|
|
||||||
|
onModuleInit() {
|
||||||
|
this.explore();
|
||||||
|
}
|
||||||
|
|
||||||
|
explore() {
|
||||||
|
const providers: InstanceWrapper[] = this.discoveryService.getProviders();
|
||||||
|
providers.forEach((wrapper: InstanceWrapper) => {
|
||||||
|
const { instance } = wrapper;
|
||||||
|
|
||||||
|
if (!instance) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const telegraf = this.moduleRef.get<TelegrafProvider<any>>(
|
||||||
|
TELEGRAF_PROVIDER,
|
||||||
|
{ strict: false },
|
||||||
|
);
|
||||||
|
|
||||||
|
this.metadataScanner.scanFromPrototype(
|
||||||
|
instance,
|
||||||
|
Object.getPrototypeOf(instance),
|
||||||
|
(key: string) => {
|
||||||
|
if (this.metadataAccessor.isTelegrafUse(instance[key])) {
|
||||||
|
this.handleTelegrafUse(instance, key, telegraf);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafOn(instance[key])) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafOnMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafOn(instance, key, telegraf, metadata);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafHears(instance[key])) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafHearsMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafHears(instance, key, telegraf, metadata);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafCommand(instance[key])) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafCommandMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafCommand(instance, key, telegraf, metadata);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafStart(instance[key])) {
|
||||||
|
this.handleTelegrafStart(instance, key, telegraf);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafHelp(instance[key])) {
|
||||||
|
this.handleTelegrafHelp(instance, key, telegraf);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafSettings(instance[key])) {
|
||||||
|
this.handleTelegrafSettings(instance, key, telegraf);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafEntity(instance[key])) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafEntityMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafEntity(instance, key, telegraf, metadata);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafMention(instance[key])) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafMentionMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafMention(instance, key, telegraf, metadata);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafPhone(instance[key])) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafPhoneMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafPhone(instance, key, telegraf, metadata);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafHashtag(instance[key])) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafHashtagMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafHashtag(instance, key, telegraf, metadata);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafCashtag(instance[key])) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafCashtagMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafCashtag(instance, key, telegraf, metadata);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafAction(instance[key])) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafActionMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafAction(instance, key, telegraf, metadata);
|
||||||
|
} else if (
|
||||||
|
this.metadataAccessor.isTelegrafInlineQuery(instance[key])
|
||||||
|
) {
|
||||||
|
const metadata = this.metadataAccessor.getTelegrafInlineQueryMetadata(
|
||||||
|
instance[key],
|
||||||
|
);
|
||||||
|
this.handleTelegrafInlineQuery(instance, key, telegraf, metadata);
|
||||||
|
} else if (this.metadataAccessor.isTelegrafGameQuery(instance[key])) {
|
||||||
|
this.handleTelegrafGameQuery(instance, key, telegraf);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafUse(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
) {
|
||||||
|
telegraf.use(instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafOn(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafOnMetadata,
|
||||||
|
) {
|
||||||
|
telegraf.on(metadata.updateTypes, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafHears(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafHearsMetadata,
|
||||||
|
) {
|
||||||
|
telegraf.hears(metadata.triggers, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafCommand(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafCommandMetadata,
|
||||||
|
) {
|
||||||
|
telegraf.command(metadata.commands, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafStart(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
) {
|
||||||
|
telegraf.start(instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafHelp(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
) {
|
||||||
|
telegraf.help(instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafSettings(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
telegraf.settings(instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafEntity(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafEntityMetadata,
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
telegraf.entity(metadata.entity, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafMention(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafMentionMetadata,
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
telegraf.mention(metadata.username, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafPhone(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafPhoneMetadata,
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
telegraf.phone(metadata.phone, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafHashtag(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafHashtagMetadata,
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
telegraf.hashtag(metadata.hashtag, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafCashtag(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafCashtagMetadata,
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
telegraf.cashtag(metadata.cashtag, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafAction(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafActionMetadata,
|
||||||
|
) {
|
||||||
|
telegraf.action(metadata.triggers, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafInlineQuery(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
metadata: TelegrafInlineQueryMetadata,
|
||||||
|
) {
|
||||||
|
// @ts-ignore
|
||||||
|
telegraf.inlineQuery(metadata.triggers, instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
|
||||||
|
handleTelegrafGameQuery(
|
||||||
|
instance: object,
|
||||||
|
key: string,
|
||||||
|
telegraf: Telegraf<ContextMessageUpdate>,
|
||||||
|
) {
|
||||||
|
telegraf.gameQuery(instance[key].bind(instance));
|
||||||
|
}
|
||||||
|
}
|
@ -1,63 +1,25 @@
|
|||||||
import { Module, DynamicModule, Provider } from '@nestjs/common';
|
import { Module, DynamicModule } from '@nestjs/common';
|
||||||
|
import { TelegrafCoreModule } from './telegraf-core.module';
|
||||||
import {
|
import {
|
||||||
|
TelegrafModuleOptions,
|
||||||
TelegrafModuleAsyncOptions,
|
TelegrafModuleAsyncOptions,
|
||||||
TelegrafOptionsFactory
|
|
||||||
} from './interfaces';
|
} from './interfaces';
|
||||||
import {
|
|
||||||
TELEGRAF_MODULE_OPTIONS,
|
|
||||||
TokenInjectionToken
|
|
||||||
} from './telegraf.constants';
|
|
||||||
import { TelegrafService, TelegrafTelegramService } from './';
|
|
||||||
|
|
||||||
@Module({})
|
@Module({})
|
||||||
export class TelegrafModule {
|
export class TelegrafModule {
|
||||||
static fromFactory(options: TelegrafModuleAsyncOptions): DynamicModule {
|
public static forRoot(options?: TelegrafModuleOptions): DynamicModule {
|
||||||
return {
|
return {
|
||||||
module: TelegrafModule,
|
module: TelegrafModule,
|
||||||
imports: options.imports || [],
|
imports: [TelegrafCoreModule.forRoot(options)],
|
||||||
providers: [
|
|
||||||
...this.createAsyncProviders(options),
|
|
||||||
TelegrafService,
|
|
||||||
TelegrafTelegramService,
|
|
||||||
{
|
|
||||||
provide: TokenInjectionToken,
|
|
||||||
useClass: options.useClass
|
|
||||||
}
|
|
||||||
],
|
|
||||||
exports: [TelegrafService, TelegrafTelegramService]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
private static createAsyncProviders(
|
public static forRootAsync(
|
||||||
options: TelegrafModuleAsyncOptions
|
options: TelegrafModuleAsyncOptions,
|
||||||
): Provider[] {
|
): DynamicModule {
|
||||||
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 {
|
return {
|
||||||
provide: TELEGRAF_MODULE_OPTIONS,
|
module: TelegrafModule,
|
||||||
useFactory: options.useFactory,
|
imports: [TelegrafCoreModule.forRootAsync(options)],
|
||||||
inject: options.inject || []
|
|
||||||
};
|
|
||||||
}
|
|
||||||
return {
|
|
||||||
provide: TELEGRAF_MODULE_OPTIONS,
|
|
||||||
useFactory: async (optionsFactory: TelegrafOptionsFactory) =>
|
|
||||||
await optionsFactory.createTelegrafOptions(),
|
|
||||||
inject: [options.useExisting || options.useClass]
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
33
lib/telegraf.provider.ts
Normal file
33
lib/telegraf.provider.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {
|
||||||
|
Injectable,
|
||||||
|
Inject,
|
||||||
|
OnApplicationBootstrap,
|
||||||
|
Logger,
|
||||||
|
OnApplicationShutdown,
|
||||||
|
} from '@nestjs/common';
|
||||||
|
import Telegraf, { ContextMessageUpdate } from 'telegraf';
|
||||||
|
import { TELEGRAF_MODULE_OPTIONS } from './telegraf.constants';
|
||||||
|
import { TelegrafModuleOptions } from './interfaces';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
// @ts-ignore
|
||||||
|
export class TelegrafProvider<TContext extends ContextMessageUpdate>
|
||||||
|
extends Telegraf<TContext>
|
||||||
|
implements OnApplicationBootstrap, OnApplicationShutdown {
|
||||||
|
private logger = new Logger('Telegraf');
|
||||||
|
|
||||||
|
constructor(@Inject(TELEGRAF_MODULE_OPTIONS) options: TelegrafModuleOptions) {
|
||||||
|
super(options.token, options.options);
|
||||||
|
}
|
||||||
|
|
||||||
|
onApplicationBootstrap() {
|
||||||
|
this.catch(e => {
|
||||||
|
this.logger.error(e);
|
||||||
|
});
|
||||||
|
this.startPolling();
|
||||||
|
}
|
||||||
|
|
||||||
|
async onApplicationShutdown(signal?: string) {
|
||||||
|
await this.stop();
|
||||||
|
}
|
||||||
|
}
|
@ -1,183 +0,0 @@
|
|||||||
import { Inject, Injectable, Logger } from '@nestjs/common'
|
|
||||||
import { ModuleRef } from '@nestjs/core'
|
|
||||||
import { flatten, head } from 'lodash'
|
|
||||||
import Telegraf, { ContextMessageUpdate } from 'telegraf'
|
|
||||||
import { TelegramActionHandler, TelegramCatch } from './decorators'
|
|
||||||
import { InvalidConfigurationException } from './exeptions'
|
|
||||||
import {
|
|
||||||
ContextTransformer,
|
|
||||||
Handler,
|
|
||||||
TelegrafOptionsFactory,
|
|
||||||
TelegramErrorHandler,
|
|
||||||
} from './interfaces'
|
|
||||||
import { TokenInjectionToken } from './telegraf.constants'
|
|
||||||
|
|
||||||
@Injectable()
|
|
||||||
export class TelegrafService {
|
|
||||||
private readonly logger = new Logger(TelegrafService.name, true)
|
|
||||||
private readonly sitePublicUrl?: string
|
|
||||||
public readonly bot: Telegraf<ContextMessageUpdate>
|
|
||||||
private ref: ModuleRef
|
|
||||||
|
|
||||||
public constructor(
|
|
||||||
@Inject(TokenInjectionToken) options: TelegrafOptionsFactory
|
|
||||||
) {
|
|
||||||
const {
|
|
||||||
token,
|
|
||||||
sitePublicUrl,
|
|
||||||
telegrafOptions,
|
|
||||||
} = options.createTelegrafOptions()
|
|
||||||
this.sitePublicUrl = sitePublicUrl
|
|
||||||
this.bot = new Telegraf(token, telegrafOptions)
|
|
||||||
}
|
|
||||||
|
|
||||||
public init(ref: ModuleRef, devMode: boolean = false) {
|
|
||||||
this.ref = ref
|
|
||||||
|
|
||||||
const handlers = this.createHandlers()
|
|
||||||
|
|
||||||
this.setupOnStart(handlers)
|
|
||||||
this.setupOn(handlers)
|
|
||||||
this.setupOnMessage(handlers)
|
|
||||||
this.setupOnCommand(handlers)
|
|
||||||
this.setupActions(handlers)
|
|
||||||
|
|
||||||
if (devMode) {
|
|
||||||
this.startPolling()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public getMiddleware(path: string) {
|
|
||||||
if (!this.sitePublicUrl) {
|
|
||||||
throw new InvalidConfigurationException(
|
|
||||||
'sitePublicUrl',
|
|
||||||
'does not exist, but webook used'
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = `${this.sitePublicUrl}/${path}`
|
|
||||||
|
|
||||||
this.bot.telegram
|
|
||||||
.setWebhook(url)
|
|
||||||
.then(() => this.logger.log(`Webhook set success @ ${url}`))
|
|
||||||
|
|
||||||
return this.bot.webhookCallback(`/${path}`)
|
|
||||||
}
|
|
||||||
|
|
||||||
public startPolling() {
|
|
||||||
this.bot.telegram.deleteWebhook().then(
|
|
||||||
() => this.bot.startPolling(),
|
|
||||||
() => {
|
|
||||||
// okay, never mind
|
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private createHandlers(): Handler[] {
|
|
||||||
return flatten(
|
|
||||||
Array.from((TelegramActionHandler.handlers || new Map()).entries()).map(
|
|
||||||
([handlerClass, classConfig]) => {
|
|
||||||
const handlerInstance = this.ref.get(handlerClass, { strict: false })
|
|
||||||
|
|
||||||
return Array.from(classConfig.entries()).map(
|
|
||||||
([methodName, methodCondig]) => ({
|
|
||||||
handle: handlerInstance[methodName].bind(handlerInstance),
|
|
||||||
config: methodCondig,
|
|
||||||
})
|
|
||||||
)
|
|
||||||
}
|
|
||||||
)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupOnStart(handlers: Handler[]): void {
|
|
||||||
const onStart = handlers.filter(({ config }) => config.onStart)
|
|
||||||
|
|
||||||
if (onStart.length !== 1) {
|
|
||||||
throw new Error()
|
|
||||||
}
|
|
||||||
|
|
||||||
this.bot.start(this.adoptHandle(head(onStart)))
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupOn(handlers: Handler[]): void {
|
|
||||||
const onHandlers = handlers.filter(({ config }) => config.on)
|
|
||||||
|
|
||||||
onHandlers.forEach(handler => {
|
|
||||||
this.bot.on(handler.config.on, this.adoptHandle(handler))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupOnMessage(handlers: Handler[]): void {
|
|
||||||
const onMessageHandlers = handlers.filter(
|
|
||||||
({ config }) => config.message !== undefined
|
|
||||||
)
|
|
||||||
|
|
||||||
onMessageHandlers.forEach(handler => {
|
|
||||||
if (handler.config.message) {
|
|
||||||
this.bot.hears(handler.config.message, this.adoptHandle(handler))
|
|
||||||
} else {
|
|
||||||
this.bot.on('message', this.adoptHandle(handler))
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupOnCommand(handlers: Handler[]): void {
|
|
||||||
const commandHandlers = handlers.filter(({ config }) => config.command)
|
|
||||||
|
|
||||||
commandHandlers.forEach(handler => {
|
|
||||||
this.bot.command(handler.config.command, this.adoptHandle(handler))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private setupActions(handlers: Handler[]): void {
|
|
||||||
const commandHandlers = handlers.filter(({ config }) => config.action)
|
|
||||||
|
|
||||||
commandHandlers.forEach(handler => {
|
|
||||||
this.bot.action(handler.config.action, this.adoptHandle(handler))
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
private adoptHandle({ handle, config }: Handler) {
|
|
||||||
const errorHandler = this.createCatch()
|
|
||||||
|
|
||||||
return async (ctx: ContextMessageUpdate) => {
|
|
||||||
const args = await Promise.all(
|
|
||||||
(config.transformations || [])
|
|
||||||
.sort((a, b) => a.index - b.index)
|
|
||||||
.map(({ transform }) =>
|
|
||||||
this.ref
|
|
||||||
.get<ContextTransformer>(transform, { strict: false })
|
|
||||||
.transform(ctx)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
|
|
||||||
return handle(ctx, ...args).catch(errorHandler(ctx))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private createCatch() {
|
|
||||||
const handlers = Array.from(
|
|
||||||
(TelegramCatch.handlers || new Map()).entries()
|
|
||||||
).map(([errorType, handlerType]) => {
|
|
||||||
const handler = this.ref.get<TelegramErrorHandler>(handlerType, {
|
|
||||||
strict: false,
|
|
||||||
})
|
|
||||||
|
|
||||||
return {
|
|
||||||
errorType,
|
|
||||||
handler,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return (ctx: ContextMessageUpdate) => (e: any) => {
|
|
||||||
for (const { errorType, handler } of handlers) {
|
|
||||||
if (e instanceof (errorType as any)) {
|
|
||||||
return handler.catch(ctx, e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
throw e
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
44
package-lock.json
generated
44
package-lock.json
generated
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nestjs-telegraf",
|
"name": "nestjs-telegraf",
|
||||||
"version": "0.7.3",
|
"version": "1.0.0-alpha.1",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
@ -102,17 +102,10 @@
|
|||||||
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
"integrity": "sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"@types/lodash": {
|
|
||||||
"version": "4.14.149",
|
|
||||||
"resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.149.tgz",
|
|
||||||
"integrity": "sha512-ijGqzZt/b7BfzcK9vTrS6MFljQRPn5BFWOx8oE0GYxribu6uV+aA9zZuXI1zc/etK9E8nrgdoF2+LgUw7+9tJQ==",
|
|
||||||
"dev": true
|
|
||||||
},
|
|
||||||
"@types/node": {
|
"@types/node": {
|
||||||
"version": "13.1.6",
|
"version": "13.9.2",
|
||||||
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.1.6.tgz",
|
"resolved": "https://registry.npmjs.org/@types/node/-/node-13.9.2.tgz",
|
||||||
"integrity": "sha512-Jg1F+bmxcpENHP23sVKkNuU3uaxPnsBMW0cLjleiikFKomJQbsn0Cqk2yDvQArqzZN6ABfBkZ0To7pQ8sLdWDg==",
|
"integrity": "sha512-bnoqK579sAYrQbp73wwglccjJ4sfRdKU7WNEZ5FW4K2U6Kc0/eZ5kvXG0JKsEKFB50zrFmfFt52/cvBbZa7eXg=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"@types/parse-json": {
|
"@types/parse-json": {
|
||||||
"version": "4.0.0",
|
"version": "4.0.0",
|
||||||
@ -902,11 +895,6 @@
|
|||||||
"p-locate": "^4.1.0"
|
"p-locate": "^4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"lodash": {
|
|
||||||
"version": "4.17.15",
|
|
||||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.15.tgz",
|
|
||||||
"integrity": "sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A=="
|
|
||||||
},
|
|
||||||
"log-symbols": {
|
"log-symbols": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-3.0.0.tgz",
|
||||||
@ -975,16 +963,14 @@
|
|||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
"minimist": {
|
"minimist": {
|
||||||
"version": "1.2.0",
|
"version": "1.2.5",
|
||||||
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
|
||||||
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
|
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"module-alias": {
|
"module-alias": {
|
||||||
"version": "2.2.2",
|
"version": "2.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/module-alias/-/module-alias-2.2.2.tgz",
|
||||||
"integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q==",
|
"integrity": "sha512-A/78XjoX2EmNvppVWEhM2oGk3x4lLxnkEA4jTbaK97QKSDjkIoOsKQlfylt/d3kKKi596Qy3NP5XrXJ6fZIC9Q=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
@ -1001,8 +987,7 @@
|
|||||||
"node-fetch": {
|
"node-fetch": {
|
||||||
"version": "2.6.0",
|
"version": "2.6.0",
|
||||||
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
|
||||||
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA==",
|
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"normalize-path": {
|
"normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
@ -1239,8 +1224,7 @@
|
|||||||
"sandwich-stream": {
|
"sandwich-stream": {
|
||||||
"version": "2.0.2",
|
"version": "2.0.2",
|
||||||
"resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz",
|
"resolved": "https://registry.npmjs.org/sandwich-stream/-/sandwich-stream-2.0.2.tgz",
|
||||||
"integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ==",
|
"integrity": "sha512-jLYV0DORrzY3xaz/S9ydJL6Iz7essZeAfnAavsJ+zsJGZ1MOnsS52yRjU3uF3pJa/lla7+wisp//fxOwOH8SKQ=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"semver-compare": {
|
"semver-compare": {
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
@ -1349,7 +1333,6 @@
|
|||||||
"version": "3.36.0",
|
"version": "3.36.0",
|
||||||
"resolved": "https://registry.npmjs.org/telegraf/-/telegraf-3.36.0.tgz",
|
"resolved": "https://registry.npmjs.org/telegraf/-/telegraf-3.36.0.tgz",
|
||||||
"integrity": "sha512-9o6AJKRiTm5vMWYI6WpTfBHzu4FMpWBNKxvnMxRds/cbMY9RnsVVjdi8i4bFFlfd+xbi73EbrnI3dybayryICA==",
|
"integrity": "sha512-9o6AJKRiTm5vMWYI6WpTfBHzu4FMpWBNKxvnMxRds/cbMY9RnsVVjdi8i4bFFlfd+xbi73EbrnI3dybayryICA==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"@types/node": "^13.1.0",
|
"@types/node": "^13.1.0",
|
||||||
"debug": "^4.0.1",
|
"debug": "^4.0.1",
|
||||||
@ -1364,7 +1347,6 @@
|
|||||||
"version": "4.1.1",
|
"version": "4.1.1",
|
||||||
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
|
||||||
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
|
||||||
"dev": true,
|
|
||||||
"requires": {
|
"requires": {
|
||||||
"ms": "^2.1.1"
|
"ms": "^2.1.1"
|
||||||
}
|
}
|
||||||
@ -1372,16 +1354,14 @@
|
|||||||
"ms": {
|
"ms": {
|
||||||
"version": "2.1.2",
|
"version": "2.1.2",
|
||||||
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
|
||||||
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
|
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
|
||||||
"dev": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"telegram-typings": {
|
"telegram-typings": {
|
||||||
"version": "3.6.1",
|
"version": "3.6.1",
|
||||||
"resolved": "https://registry.npmjs.org/telegram-typings/-/telegram-typings-3.6.1.tgz",
|
"resolved": "https://registry.npmjs.org/telegram-typings/-/telegram-typings-3.6.1.tgz",
|
||||||
"integrity": "sha512-njVv1EAhIZnmQVLocZEADYUyqA1WIXuVcDYlsp+mXua/XB0pxx+PKtMSPeZ/EE4wPWTw9h/hA9ASTT6yQelkiw==",
|
"integrity": "sha512-njVv1EAhIZnmQVLocZEADYUyqA1WIXuVcDYlsp+mXua/XB0pxx+PKtMSPeZ/EE4wPWTw9h/hA9ASTT6yQelkiw=="
|
||||||
"dev": true
|
|
||||||
},
|
},
|
||||||
"timers-ext": {
|
"timers-ext": {
|
||||||
"version": "0.1.7",
|
"version": "0.1.7",
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "nestjs-telegraf",
|
"name": "nestjs-telegraf",
|
||||||
"version": "0.7.3",
|
"version": "1.0.0-alpha.1",
|
||||||
"description": "Telegraf module for Nest framework",
|
"description": "Telegraf module for Nest framework",
|
||||||
"keywords": [
|
"keywords": [
|
||||||
"nest",
|
"nest",
|
||||||
@ -34,24 +34,21 @@
|
|||||||
"publish:npm": "npm publish --access public"
|
"publish:npm": "npm publish --access public"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"lodash": "4.17.15"
|
"telegraf": "^3.36.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@nestjs/common": "6.11.11",
|
"@nestjs/common": "6.11.11",
|
||||||
"@nestjs/core": "6.11.11",
|
"@nestjs/core": "6.11.11",
|
||||||
"@types/lodash": "4.14.149",
|
|
||||||
"husky": "4.2.3",
|
"husky": "4.2.3",
|
||||||
"lint-staged": "10.0.8",
|
"lint-staged": "10.0.8",
|
||||||
"prettier": "1.19.1",
|
"prettier": "1.19.1",
|
||||||
"reflect-metadata": "0.1.13",
|
"reflect-metadata": "0.1.13",
|
||||||
"telegraf": "3.36.0",
|
|
||||||
"typescript": "3.8.3"
|
"typescript": "3.8.3"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
"@nestjs/common": "^6.7.0",
|
"@nestjs/common": "^6.7.0",
|
||||||
"@nestjs/core": "^6.7.0",
|
"@nestjs/core": "^6.7.0",
|
||||||
"reflect-metadata": "^0.1.13",
|
"reflect-metadata": "^0.1.13"
|
||||||
"telegraf": "^3.35.0"
|
|
||||||
},
|
},
|
||||||
"husky": {
|
"husky": {
|
||||||
"hooks": {
|
"hooks": {
|
||||||
|
Loading…
Reference in New Issue
Block a user