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

--

--

--

Stupid as stupid does — https://www.linkedin.com/in/nasterblue

Love podcasts or audiobooks? Learn on the go with our new app.

Angular 5.0 new ship arrives.

🔸You can use whenever, just need access of the Internet and device.

Functional JS #3: State

PWA-Solutions compared

Create Singly Linked Lists using Javascript

5 JavaScript Features That Are Introduced In ES2021

ES 2021

JavaScript Crash Course for Beginners -1

9 Ways to Manipulate and Work With Components in React

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store
nasterblue

nasterblue

Stupid as stupid does — https://www.linkedin.com/in/nasterblue

More from Medium

Ember Query Params

A Guide to Koa js-First Steps

Everything you need to know unit testing:

CS373 Spring 2022: Maria Gu