📜 ⬆️ ⬇️

Telegram bot framework

It just so happened that the line of my work is closely intertwined with the creation of bots for Telegram. I started writing them immediately after the Telegram Bot API appeared , then there were no tools for this. I had to write the library myself to work with the API, as I partially already mentioned in my previous article . Over time, the library was rewritten several times and eventually overgrown with different chips. In the article I will try to talk about how to write bots with it.




')
To work with the API, you first need a token, to get it, just write to this bot and follow its instructions.

I'll start right away with an example of a simple bot:

'use strict' var tg = require('telegram-node-bot')('YOUR_TOKEN') tg.router. when(['ping'], 'PingController') tg.controller('PingController', ($) => { tg.for('ping', () => { $.sendMessage('pong') }) }) 


Works!



And now let's look at what is written there:

 var tg = require('telegram-node-bot')('YOUR_TOKEN') 

Everything is clear, just declare the module and give it our token.

 tg.router. when(['ping'], 'PingController') 

Next, we announce the commands and routers that are responsible for these commands.

 tg.controller('PingController', ($) => { tg.for('ping', () => { $.sendMessage('pong') }) }) 

After we create the PingController controller and declare a ping command handler in it.

When the bot receives the command from the user, it will understand that the PingController controller is responsible for the ping command and will execute the ping command handler.

This can close the article and start writing simple bots, and I will continue to tell.

Router


As I wrote above, the router is responsible for communicating the commands and controllers that these commands handle.
Obviously, one controller can handle multiple commands.
Also, the router has a otherwise function, with which you can declare a controller that will process unsolicited commands:

 tg.router. when(['test', 'test2'], 'TestController'). // "TestController"     test,    test2 when(['ping'], 'PingController'). otherwise('OtherController') //"OtherController"      . 



Controller


When a bot receives a command from a user, controllers come into play. As you can see from the example above, the controllers process user commands. Within one controller, several commands can be processed:

 tg.controller('TestController', ($) => { tg.for('test', () => { $.sendMessage('test') }) tg.for('test2', () => { $.sendMessage('test2') }) }) 

In the controller, you can also write any of its functions, variables.

Scope


Each controller accepts a special $ - scope variable.
It contains all that we need to know about the request:




You may have noticed that, for example, we called the sendMessage function using scope . The fact is that in addition to the fields described above, the scope also contains all the functions of the library with the chatId already written for a particular chat. The same function sendMessage can be called directly:

 tg.controller('TestController', ($) => { tg.for('test', () => { tg.sendMessage($.chatId, 'test') }) tg.for('test2', () => { tg.sendMessage($.chatId, 'test2') }) }) 

Agree, it is not very convenient to write the chat id, considering that we are writing to the same chat where the message came from.

Call chain


Sometimes we ask something from the user and wait for any information from him. How to implement it? Of course, we can store the state of users and, depending on it, process it in “OtherController”, but this is not very nice, and breaks the structure and readability.
For such cases, the scope has a waitForRequest function:
 tg.controller('TestController', ($) => { tg.for('/reg', ($) => { $.sendMessage('Send me your name!') $.waitForRequest(($) => { $.sendMessage('Hi ' + $.message.text + '!') }) }) }) 

The waitForRequest function takes one argument, a callback , which it will call when the user sends the next message. A new scope is transferred to this callback . As you can see from the example above, we ask the user to enter his name, wait for his next message and welcome it.

Navigation



Suppose our bot has authorization, in the main controller we need to somehow check whether the user is authorized and redirect him to the login, but how? To do this, there is a routeTo function that accepts the command and executes it as if the user sent it to us:
 tg.controller('StartController', ($) => { tg.for('/profile', ($) => { if(!logined){ //     $.routeTo("/login") //   } }) }) 


Forms


It often happens that you need to find out some information from the user, for this there is a form generator:
 var form = { name: { q: 'Send me your name', error: 'sorry, wrong input', validator: (input, callback) => { if(input['text']) { callback(true) return } callback(false) } }, age: { q: 'Send me your age', error: 'sorry, wrong input', validator: (input, callback) => { if(input['text'] && IsNumeric(input['text'])) { callback(true) return } callback(false) } }, sex: { q: 'Select your sex', keyboard: [['male'],['famale'], ['UFO']], error: 'sorry, wrong input', validator: (input, callback) => { if(input['text'] && ['male', 'famale', 'UFO'].indexOf(input['text']) > -1) { callback(true) return } callback(false) } }, } $.runForm(form, (result) => { console.log(result) }) 

As can be seen from the code, each field has a message that is being poisoned to the user, a validator that receives a message from the user, and an error message in case of incorrect data. You can also send a keyboard.
The runForm function will return an object with the same fields as we wrote in the form itself, in our case it is name and age.

Menu



There is a similar tool for the menu:
 $.runMenu({ message: 'Select:', 'Exit': { message: 'Do you realy want to exit?', 'yes': () => { }, 'no': () => { } } }) 

The menu automatically creates a keyboard with field names and sends it along with the message that we specify in the message field.
A menu item can be both an object and a function. If it is an object, the user will receive a submenu, and if the function, then it will be called and allow us to process the user's request.

It is also possible to set the location of the buttons in the menu keypad; the layout field is responsible for this; if it is not transmitted at all, then there will be one button on each line. You can pass the maximum number of buttons per line or an array of the number of buttons for each line:
 $.runMenu({ message: 'Select:', layout: 2, 'test1': () => {}, //    'test2': () => {}, //    'test3': () => {}, //    'test4': () => {}, //    'test5': () => {}, //    }) 


 $.runMenu({ message: 'Select:', layout: [1, 2, 1, 1], 'test1': () => {}, //    'test2': () => {}, //    'test3': () => {}, //    'test4': () => {}, //    'test5': () => {}, ///    }) 


API functions


All functions for working with API have both obligatory parameters and optional ones. For example, the function sendMessage - according to the documentation it has for the required parameter (chatId, photo), but we can send any additional ones:
 var options = { reply_markup: JSON.stringify({ one_time_keyboard: true, keyboard: [['test']] }) } $.sendMessage('test', options) 


In this case, the last parameter is always callback.

Here is a list of currently supported API functions with required parameters (recall that if you call them from scope , the chatId parameter is not needed):



Examples of calling some functions:
 var doc = { value: fs.createReadStream('file.png'), //stream filename: 'photo.png', contentType: 'image/png' } $.sendDocument(doc) $.sendPhoto(fs.createReadStream('photo.jpeg')) $.sendAudio(fs.createReadStream('audio.mp3')) $.sendVoice(fs.createReadStream('voice.ogg')) $.sendVideo(fs.createReadStream('video.mp4')) $.sendSticker(fs.createReadStream('sticker.webp')) 



That's all, thanks to everyone who mastered the article, and here is the link to GitHub - github.com/Naltox/telegram-node-bot and NPM: npmjs.com/package/telegram-node-bot

Source: https://habr.com/ru/post/276389/


All Articles