Chain of Responsibility — How to pass the limits of Telegram bot ?
Summary
Avoid coupling the sender of a request to its receiver by giving more than one object a chance to handle the request. Chain the receiving objects and pass the request along the chain until an object handles it.

Structure

Participants
Handler (Approver) : defines an interface for handling the requests
ConcreteHandler : handles requests it is responsible for
can access its successor
if the ConcreteHandler can handle the request, it does so; otherwise it forwards the request to its successor
Client (ChainApp): initiates the request to a ConcreteHandler object on the chain
Typescript Code in Examples
Issue: How to pass the limits of Telegram bot ?
https://core.telegram.org/bots/faq#my-bot-is-hitting-limits-how-do-i-avoid-this
My bot is hitting limits, how do I avoid this?
When sending messages inside a particular chat, avoid sending more than one message per second. We may allow short bursts that go over this limit, but eventually you’ll begin receiving 429 errors.
If you’re sending bulk notifications to multiple users, the API will not allow more than 30 messages per second or so. Consider spreading out notifications over large intervals of 8–12 hours for best results.
Also note that your bot will not be able to send more than 20 messages per minute to the same group.
Code solution
telegraf.chain-of-responsibility.handler.interface.ts
export interface TelegrafChainOfResponsibilityHandlerInterface {
setNext(handler: TelegrafChainOfResponsibilityHandlerInterface): TelegrafChainOfResponsibilityHandlerInterface;
handle(request: string): void;
}
telegraf.chain-of-responsibility.abstract.handler.ts
import { TelegrafChainOfResponsibilityHandlerInterface } from './telegraf.chain-of-responsibility.handler.interface';
export abstract class TelegrafChainOfResponsibilityAbstractHandler implements TelegrafChainOfResponsibilityHandlerInterface {
private nextHandler: TelegrafChainOfResponsibilityAbstractHandler;
public setNext(handler: TelegrafChainOfResponsibilityAbstractHandler): TelegrafChainOfResponsibilityAbstractHandler {
this.nextHandler = handler;
return handler;
}
public async handle(request: string): Promise<TelegrafChainOfResponsibilityAbstractHandler> {
if (this.nextHandler) {
await this.nextHandler.handle(request);
return this.getNextHandler();
}
return null;
}
public getNextHandler(): TelegrafChainOfResponsibilityAbstractHandler {
return this.nextHandler;
}
}
telegraf.chain-of-responsibility.service.handler.ts
import { Logger } from '@nestjs/common';
import { Telegraf } from 'telegraf';
import { TelegrafContext } from 'telegraf/typings/context';
import { TelegrafBotErrorService } from '../telegraf.bot.error.service';
import { TelegrafChainOfResponsibilityAbstractHandler } from './telegraf.chain-of-responsibility.abstract.handler';
const helpersText = [
'Welcome to Chain Of Responsibility bot by nasterblue',
'/help',
'/start',
'/stop',
].join('\n');
export class TelegrafChainOfResponsibilityServiceHandler extends TelegrafChainOfResponsibilityAbstractHandler {
private readonly logger = new Logger(TelegrafChainOfResponsibilityServiceHandler.name);
private telegrafInstance: Telegraf<TelegrafContext>;
private telegrafContext: TelegrafContext;
private token: string;
public constructor(token: string) {
super();
this.token = token;
(async () => {
await this.initCommands(token);
})();
}
public handle(request: string): void {
// this.logger.debug(request);
try {
this.telegrafContext?.replyWithHTML(request)
.catch((err) => {
// this.logger.debug(`replyWithHTML failed token ${this.token}`);
// this.logger.debug(`replyWithHTML failed, transfer this handle to next handler`);
TelegrafBotErrorService.getInstance().replyWithHTML(err.description);
return super.handle(request);
});
// this.logger.debug(`replyWithHTML with token ${this.token}`);
} catch (err) {
// this.logger.debug(`replyWithHTML failed token ${this.token}`);
// this.logger.debug(`replyWithHTML failed, transfer this handle to next handler`);
TelegrafBotErrorService.getInstance().replyWithHTML(err.description);
return super.handle(request);
}
}
public setBotContext(telegrafContext: TelegrafContext): void {
this.telegrafContext = telegrafContext;
}
public getBotContext(): TelegrafContext {
return this.telegrafContext;
}
async initCommands(token): Promise<any> {
this.logger.debug(`Starting ${TelegrafChainOfResponsibilityServiceHandler.name} with token : ${token} ....`);
this.telegrafInstance = new Telegraf(token);
this.telegrafInstance.use((ctx: TelegrafContext, next) => {
this.logger.debug(`Starting ${TelegrafChainOfResponsibilityServiceHandler.name} with token : ${token} ....`);
this.logger.debug(`Starting ${TelegrafChainOfResponsibilityServiceHandler.name} with middlewares`);
this.setBotContext(ctx);
return next();
});
this.telegrafInstance.start(async (ctx: TelegrafContext) => {
ctx.replyWithHTML(helpersText);
});
this.telegrafInstance.help((ctx) => {
ctx.replyWithHTML(helpersText);
});
this.telegrafInstance.catch((err, ctx) => {
this.logger.error(JSON.stringify(err));
TelegrafBotErrorService.getInstance().replyWithHTML(err.description);
});
await this.telegrafInstance.launch();
this.logger.debug(`Started ${TelegrafChainOfResponsibilityServiceHandler.name} with token : ${token} ....`);
}
}
telegraf.util.service.ts
export function fGetTelegrafChainOfResponsibilityToken(): string[] {
const tokens: string[] = [];
Object.keys(process.env).forEach((token: string) => {
if (token && token.startsWith('TELEGRAF_BOT_CHAIN_OF_RESPONSIBILITY')) {
tokens.push(process.env[token]);
}
});
return tokens;
}
telegraf.chain-of-responsibility.service.ts
import { Logger } from '@nestjs/common';
import { fGetTelegrafChainOfResponsibilityToken } from '../telegraf.util.service';
import { fGetCoreThreadsNumbers } from '../../../shared/utils';
import { TelegrafChainOfResponsibilityServiceHandler } from './telegraf.chain-of-responsibility.service.handler';
const PromisePool = require('@supercharge/promise-pool');
export class TelegrafChainOfResponsibilityService {
private readonly logger = new Logger(TelegrafChainOfResponsibilityService.name);
private static instance: TelegrafChainOfResponsibilityService;
public serviceHandler: TelegrafChainOfResponsibilityServiceHandler;
private constructor() {
(async () => {
await this.initCommands();
})();
}
public static getInstance(): TelegrafChainOfResponsibilityService {
if (!TelegrafChainOfResponsibilityService.instance) {
TelegrafChainOfResponsibilityService.instance = new TelegrafChainOfResponsibilityService();
}
return TelegrafChainOfResponsibilityService.instance;
}
private async initCommands(): Promise<any> {
const results: { results: TelegrafChainOfResponsibilityServiceHandler[] } = await PromisePool
.withConcurrency(fGetCoreThreadsNumbers())
.for(fGetTelegrafChainOfResponsibilityToken())
.process(async (token: string) => {
return new TelegrafChainOfResponsibilityServiceHandler(token);
});
const serviceHandlers = results.results;
serviceHandlers.forEach((item: TelegrafChainOfResponsibilityServiceHandler, index: number) => {
item.setNext(serviceHandlers.length - 1 == index ? serviceHandlers[0] : serviceHandlers[index + 1]);
});
this.serviceHandler = serviceHandlers.shift();
}
public async replyWithHTML(html: string): Promise<any> {
// this.logger.debug(html);
if (html && html.trim() !== '' && TelegrafChainOfResponsibilityService.getInstance().serviceHandler) {
TelegrafChainOfResponsibilityService.getInstance().serviceHandler.handle(html);
TelegrafChainOfResponsibilityService.getInstance().setServiceHandler(TelegrafChainOfResponsibilityService.getInstance().serviceHandler.getNextHandler() as any);
}
}
public setServiceHandler(serviceHandler: TelegrafChainOfResponsibilityServiceHandler): void {
TelegrafChainOfResponsibilityService.getInstance().serviceHandler = serviceHandler;
}
}
Client (ChainApp)
TelegrafChainOfResponsibilityService.getInstance().replyWithHTML(html);
Add all your bots into your Telegram Group and enjoy.
/start@development_chain_01_bot
/start@development_chain_02_bot
/start@development_chain_03_bot
...
You can not asking BotFather for more than 20 bots

