2020-01-12 01:58:34 +03:00
|
|
|
import { Injectable, Inject, Logger } from '@nestjs/common'
|
2019-02-28 11:29:26 +03:00
|
|
|
import { ModuleRef } from '@nestjs/core'
|
|
|
|
import Telegraf, { ContextMessageUpdate } from 'telegraf'
|
|
|
|
import { flatten, head } from 'lodash'
|
|
|
|
|
2020-01-12 02:41:27 +03:00
|
|
|
import { TelegramCatch, TelegramActionHandler } from './decorators'
|
|
|
|
import { TokenInjectionToken } from './telegraf.constants'
|
|
|
|
import {
|
|
|
|
TelegrafOptionsFactory,
|
|
|
|
TelegramErrorHandler,
|
|
|
|
Handler,
|
|
|
|
ContextTransformer,
|
|
|
|
} from './interfaces'
|
|
|
|
import { InvalidConfigurationException } from './exeptions'
|
2019-02-28 11:29:26 +03:00
|
|
|
|
|
|
|
@Injectable()
|
2020-01-12 10:53:45 +03:00
|
|
|
export class TelegrafService {
|
|
|
|
private readonly logger = new Logger(TelegrafService.name, true)
|
2019-03-03 15:43:30 +03:00
|
|
|
private readonly sitePublicUrl?: string
|
2020-01-23 21:10:53 +03:00
|
|
|
public readonly bot: Telegraf<ContextMessageUpdate>
|
2019-02-28 11:29:26 +03:00
|
|
|
private ref: ModuleRef
|
|
|
|
|
|
|
|
public constructor(
|
2020-01-15 17:30:47 +03:00
|
|
|
@Inject(TokenInjectionToken) options: TelegrafOptionsFactory
|
2019-02-28 11:29:26 +03:00
|
|
|
) {
|
2020-01-12 02:41:27 +03:00
|
|
|
const { token, sitePublicUrl } = options.createTelegrafOptions()
|
2019-03-03 15:43:30 +03:00
|
|
|
this.sitePublicUrl = sitePublicUrl
|
2019-03-03 19:37:18 +03:00
|
|
|
this.bot = new Telegraf(token)
|
2019-02-28 11:29:26 +03:00
|
|
|
}
|
|
|
|
|
2019-07-16 18:56:02 +03:00
|
|
|
public init(ref: ModuleRef, devMode: boolean = false) {
|
2019-02-28 11:29:26 +03:00
|
|
|
this.ref = ref
|
|
|
|
|
|
|
|
const handlers = this.createHandlers()
|
|
|
|
|
2019-03-03 19:37:18 +03:00
|
|
|
this.setupOnStart(handlers)
|
2019-05-20 07:56:11 +03:00
|
|
|
this.setupOnMessage(handlers)
|
2019-03-03 19:37:18 +03:00
|
|
|
this.setupOnCommand(handlers)
|
2020-01-15 17:30:47 +03:00
|
|
|
this.setupActions(handlers)
|
2019-07-16 18:56:02 +03:00
|
|
|
|
|
|
|
if (devMode) {
|
|
|
|
this.startPolling()
|
|
|
|
}
|
2019-02-28 11:29:26 +03:00
|
|
|
}
|
|
|
|
|
2019-03-03 15:43:30 +03:00
|
|
|
public getMiddleware(path: string) {
|
|
|
|
if (!this.sitePublicUrl) {
|
|
|
|
throw new InvalidConfigurationException(
|
|
|
|
'sitePublicUrl',
|
2020-01-15 17:30:47 +03:00
|
|
|
'does not exist, but webook used'
|
2019-03-03 15:43:30 +03:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-07-26 10:50:31 +03:00
|
|
|
const url = `${this.sitePublicUrl}/${path}`
|
|
|
|
|
2019-03-03 15:43:30 +03:00
|
|
|
this.bot.telegram
|
2019-07-26 10:50:31 +03:00
|
|
|
.setWebhook(url)
|
2020-01-12 02:41:27 +03:00
|
|
|
.then(() => this.logger.log(`Webhook set success @ ${url}`))
|
2019-03-03 15:43:30 +03:00
|
|
|
|
2019-03-03 20:00:03 +03:00
|
|
|
return this.bot.webhookCallback(`/${path}`)
|
2019-03-03 15:43:30 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
public startPolling() {
|
2019-07-26 10:50:31 +03:00
|
|
|
this.bot.telegram.deleteWebhook().then(
|
|
|
|
() => this.bot.startPolling(),
|
|
|
|
() => {
|
|
|
|
// okay, never mind
|
2020-01-15 17:30:47 +03:00
|
|
|
}
|
2019-07-26 10:50:31 +03:00
|
|
|
)
|
2019-02-28 11:29:26 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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,
|
2020-01-15 17:30:47 +03:00
|
|
|
})
|
2019-02-28 11:29:26 +03:00
|
|
|
)
|
2020-01-15 17:30:47 +03:00
|
|
|
}
|
|
|
|
)
|
2019-02-28 11:29:26 +03:00
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2019-03-03 19:37:18 +03:00
|
|
|
private setupOnStart(handlers: Handler[]): void {
|
2019-02-28 11:29:26 +03:00
|
|
|
const onStart = handlers.filter(({ config }) => config.onStart)
|
|
|
|
|
|
|
|
if (onStart.length !== 1) {
|
|
|
|
throw new Error()
|
|
|
|
}
|
|
|
|
|
2019-03-03 19:37:18 +03:00
|
|
|
this.bot.start(this.adoptHandle(head(onStart)))
|
2019-02-28 11:29:26 +03:00
|
|
|
}
|
|
|
|
|
2019-05-20 07:56:11 +03:00
|
|
|
private setupOnMessage(handlers: Handler[]): void {
|
|
|
|
const onMessageHandlers = handlers.filter(({ config }) => config.message)
|
|
|
|
|
|
|
|
onMessageHandlers.forEach(handler => {
|
|
|
|
this.bot.hears(handler.config.message, this.adoptHandle(handler))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-03-03 19:37:18 +03:00
|
|
|
private setupOnCommand(handlers: Handler[]): void {
|
2019-02-28 11:29:26 +03:00
|
|
|
const commandHandlers = handlers.filter(({ config }) => config.command)
|
|
|
|
|
|
|
|
commandHandlers.forEach(handler => {
|
2019-03-03 19:37:18 +03:00
|
|
|
this.bot.command(handler.config.command, this.adoptHandle(handler))
|
2019-02-28 11:29:26 +03:00
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2020-01-15 17:30:47 +03:00
|
|
|
private setupActions(handlers: Handler[]): void {
|
|
|
|
const commandHandlers = handlers.filter(({ config }) => config.action)
|
|
|
|
|
|
|
|
commandHandlers.forEach(handler => {
|
|
|
|
this.bot.action(handler.config.action, this.adoptHandle(handler))
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
2019-02-28 11:29:26 +03:00
|
|
|
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 })
|
2020-01-15 17:30:47 +03:00
|
|
|
.transform(ctx)
|
|
|
|
)
|
2019-02-28 11:29:26 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
return handle(ctx, ...args).catch(errorHandler(ctx))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private createCatch() {
|
|
|
|
const handlers = Array.from(
|
2020-01-15 17:30:47 +03:00
|
|
|
(TelegramCatch.handlers || new Map()).entries()
|
2019-02-28 11:29:26 +03:00
|
|
|
).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
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|