Last active
May 3, 2023 15:48
-
-
Save Aleksey-Danchin/7ab42657fc4ba04aa67221a58ee995ec to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import TelegramBot from "node-telegram-bot-api"; | |
export type UseHandler<ContextType> = ( | |
message: TelegramBot.Message | undefined, | |
metadata: TelegramBot.Metadata | undefined, | |
query: TelegramBot.CallbackQuery | undefined, | |
context: ContextType, | |
next: () => void | |
) => void; | |
export type MessageHandler<ContextType> = ( | |
message: TelegramBot.Message, | |
metadata: TelegramBot.Metadata, | |
context: ContextType, | |
next: () => void | |
) => void; | |
export type CallbackHandler<ContextType> = ( | |
query: TelegramBot.CallbackQuery, | |
context: ContextType, | |
next: () => void | |
) => void; | |
export type CommandHandler<ContextType> = ( | |
message: TelegramBot.Message, | |
match: RegExpExecArray | null, | |
context: ContextType, | |
next: () => void | |
) => void; | |
export type messageArg<ContextType> = | |
| MessageHandler<ContextType> | |
| messageArg<ContextType>[]; | |
export type callbackArg<ContextType> = | |
| CallbackHandler<ContextType> | |
| callbackArg<ContextType>[]; | |
export type commandArg<ContextType> = | |
| CommandHandler<ContextType> | |
| commandArg<ContextType>[]; | |
export class SmartTelegramBot<ContextType extends Object> extends TelegramBot { | |
private useHandlers = [] as UseHandler<ContextType>[][]; | |
private messageHandlers = [] as MessageHandler<ContextType>[][]; | |
private callbackHandlers = new Map< | |
string, | |
CallbackHandler<ContextType>[][] | |
>(); | |
private commandHandlers = new Map< | |
string, | |
CommandHandler<ContextType>[][] | |
>(); | |
private contextBuffer = new WeakMap< | |
TelegramBot.Message | TelegramBot.CallbackQuery, | |
ContextType | |
>(); | |
constructor(...args: ConstructorParameters<typeof TelegramBot>) { | |
super(...args); | |
this.on("message", async (message, metadata) => { | |
if (!this.contextBuffer.has(message)) { | |
this.contextBuffer.set(message, {} as ContextType); | |
} | |
const context = this.contextBuffer.get(message) as ContextType; | |
await this.runUse(message, metadata, undefined, context); | |
this.runMessage(message, metadata, context); | |
}); | |
this.on("callback_query", async (query) => { | |
if (!query.data) { | |
return; | |
} | |
const collection = this.callbackHandlers.get(query.data); | |
if (!collection) { | |
return; | |
} | |
if (!this.contextBuffer.has(query)) { | |
this.contextBuffer.set(query, {} as ContextType); | |
} | |
const context = this.contextBuffer.get(query) as ContextType; | |
await this.runUse(undefined, undefined, query, context); | |
this.runQuery(collection, query, context); | |
}); | |
} | |
use(...handlers: UseHandler<ContextType>[]) { | |
handlers = handlers.flat(21); // wow 0_o, 21 is deep limit | |
if (!handlers.length) { | |
throw Error("Unexpectedly empty number of handlers."); | |
} | |
this.useHandlers.push(handlers as UseHandler<ContextType>[]); | |
return this; | |
} | |
message(...handlers: messageArg<ContextType>[]) { | |
handlers = handlers.flat(21); // wow 0_o, 21 is deep limit | |
if (!handlers.length) { | |
throw Error("Unexpectedly empty number of handlers."); | |
} | |
this.messageHandlers.push(handlers as MessageHandler<ContextType>[]); | |
return this; | |
} | |
callback(name: string, ...handlers: callbackArg<ContextType>[]) { | |
handlers = handlers.flat(21); // wow 0_o, 21 is deep limit | |
if (!handlers.length) { | |
throw Error("Unexpectedly empty number of handlers."); | |
} | |
if (!this.callbackHandlers.has(name)) { | |
this.callbackHandlers.set(name, []); | |
} | |
this.callbackHandlers | |
.get(name) | |
?.push(handlers as CallbackHandler<ContextType>[]); | |
return this; | |
} | |
command(reg: RegExp, ...handlers: messageArg<ContextType>[]) { | |
handlers = handlers.flat(21); // wow 0_o, 21 is deep limit | |
if (!handlers.length) { | |
throw Error("Unexpectedly empty number of handlers."); | |
} | |
this.message((message, meta, context, next) => { | |
const { text } = message; | |
if (text && reg.test(text)) { | |
next(); | |
} | |
}, ...handlers); | |
return this; | |
} | |
private async runUse( | |
message: TelegramBot.Message | undefined, | |
metadata: TelegramBot.Metadata | undefined, | |
query: TelegramBot.CallbackQuery | undefined, | |
context: ContextType, | |
row: number = 0, | |
column: number = 0 | |
) { | |
const handler = (this.useHandlers[row] || [])[column] as | |
| UseHandler<ContextType> | |
| undefined; | |
if (!handler) { | |
return; | |
} | |
let flag = false; | |
let promise: Promise<any> | null = null; | |
const next = () => { | |
if (flag) { | |
throw Error("Unexpected double calling."); | |
} | |
flag = true; | |
const handler1 = (this.useHandlers[row] || [])[column + 1] as | |
| UseHandler<ContextType> | |
| undefined; | |
if (handler1) { | |
promise = this.runUse( | |
message, | |
metadata, | |
query, | |
context, | |
row, | |
column + 1 | |
); | |
} else { | |
promise = this.runUse( | |
message, | |
metadata, | |
query, | |
context, | |
row + 1, | |
0 | |
); | |
} | |
}; | |
await handler(message, metadata, query, context, next); | |
if (!flag && column === 0) { | |
promise = this.runUse( | |
message, | |
metadata, | |
query, | |
context, | |
row + 1, | |
0 | |
); | |
} | |
await promise; | |
} | |
private async runMessage( | |
message: TelegramBot.Message, | |
metadata: TelegramBot.Metadata, | |
context: ContextType, | |
row: number = 0, | |
column: number = 0 | |
) { | |
const handler = (this.messageHandlers[row] || [])[column] as | |
| MessageHandler<ContextType> | |
| undefined; | |
if (!handler) { | |
return; | |
} | |
let flag = false; | |
let promise: Promise<any> | null = null; | |
const next = () => { | |
if (flag) { | |
throw Error("Unexpected double calling."); | |
} | |
flag = true; | |
const handler1 = (this.messageHandlers[row] || [])[column + 1] as | |
| MessageHandler<ContextType> | |
| undefined; | |
if (handler1) { | |
promise = this.runMessage( | |
message, | |
metadata, | |
context, | |
row, | |
column + 1 | |
); | |
} else { | |
promise = this.runMessage( | |
message, | |
metadata, | |
context, | |
row + 1, | |
0 | |
); | |
} | |
}; | |
await handler(message, metadata, context, next); | |
if (!flag && column === 0) { | |
promise = this.runMessage(message, metadata, context, row + 1, 0); | |
} | |
await promise; | |
} | |
private async runQuery( | |
collection: CallbackHandler<ContextType>[][], | |
query: TelegramBot.CallbackQuery, | |
context: ContextType, | |
row: number = 0, | |
column: number = 0 | |
) { | |
const handler = (collection[row] || [])[column] as | |
| CallbackHandler<ContextType> | |
| undefined; | |
if (!handler) { | |
return; | |
} | |
let flag = false; | |
let promise: Promise<any> | null = null; | |
const next = () => { | |
if (flag) { | |
throw Error("Unexpected double calling."); | |
} | |
flag = true; | |
const handler1 = (collection[row] || [])[column + 1] as | |
| CallbackHandler<ContextType> | |
| undefined; | |
if (handler1) { | |
promise = this.runQuery( | |
collection, | |
query, | |
context, | |
row, | |
column + 1 | |
); | |
} else { | |
promise = this.runQuery(collection, query, context, row + 1, 0); | |
} | |
}; | |
await handler(query, context, next); | |
if (!flag && column === 0) { | |
promise = this.runQuery(collection, query, context, row + 1, 0); | |
} | |
await promise; | |
} | |
private async runCommand( | |
collection: CommandHandler<ContextType>[][], | |
message: TelegramBot.Message, | |
match: RegExpExecArray | null, | |
context: ContextType, | |
row: number = 0, | |
column: number = 0 | |
) { | |
const handler = (collection[row] || [])[column] as | |
| CommandHandler<ContextType> | |
| undefined; | |
if (!handler) { | |
return; | |
} | |
let flag = false; | |
let promise: Promise<any> | null = null; | |
const next = () => { | |
if (flag) { | |
throw Error("Unexpected double calling."); | |
} | |
flag = true; | |
const handler1 = (collection[row] || [])[column + 1] as | |
| CommandHandler<ContextType> | |
| undefined; | |
if (handler1) { | |
promise = this.runCommand( | |
collection, | |
message, | |
match, | |
context, | |
row, | |
column + 1 | |
); | |
} else { | |
promise = this.runCommand( | |
collection, | |
message, | |
match, | |
context, | |
row + 1, | |
0 | |
); | |
} | |
}; | |
await handler(message, match, context, next); | |
if (!flag && column === 0) { | |
promise = this.runCommand( | |
collection, | |
message, | |
match, | |
context, | |
row + 1, | |
0 | |
); | |
} | |
await promise; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment