Bot-brother
Node.js library to help you easy create telegram bots. Works on top of node-telegram-bot-api Supports telegram-api 2.0 inline keyboards
Main features:
- sessions
- middlewares
- localization
- templated keyboards and messages
- navigation between commands
- inline keyboards
This bots work on top of bot-brother: @weatherman_bot @zodiac_bot @my_ali_bot @delorean_bot @matchmaker_bot
Table of contents
- Install
- Simple usage
- Examples of usage
- Commands
- Middlewares
- Sessions
- Localization and texts
- Keyboards
- Api
- Bot
- Command
- Context
- Context properties
- Context methods
- context.sendMessage(text, [options])
- context.sendPhoto(photo, [options])
- context.sendAudio(audio, [options])
- context.sendDocument(A, [options])
- context.sendSticker(A, [options])
- context.sendVideo(A, [options])
- context.sendVoice(voice, [options])
- context.sendChatAction(action)
- context.getUserProfilePhotos([offset], [limit])
- context.sendLocation(latitude, longitude, [options])
Install
npm install bot-brother
Simple usage
var bb = ;var bot = ; // Let's create command '/start'.bot; // Creating command '/upload_photo'.bot;
Examples of usage
We've written simple notification bot with bot-brother
, so you can inspect code here: https://github.com/SerjoPepper/delorean_bot
You can try bot in action here:
https://telegram.me/delorean_bot
Commands
Commands can be set with strings or regexps.
bot; bot; bot;
Middlewares
Middlewares are useful for multistage command handling.
var bb = ;var bot = bot; bot;
There are following stages, sorted in order of appearance.
Name | Description |
---|---|
before | applied before all stages |
beforeInvoke | applied before invoke stage |
beforeAnswer | applied before answer stage |
invoke | same as command.invoke(...) |
answer | same as command.answer(...) |
Let's look at following example, and try to understand how and in what order they will be invoked.
bot; // This callback cathes all commands.bot; bot; bot;
Bot dialog
me > /hello
bot > bot before
bot > bot beforeInvoke
bot > rgx before
bot > rgx beforeInvoke
bot > hello before
bot > hello beforeInvoke
bot > hello invoke
me > I type something
bot > bot before
bot > bot beforeAnswer
bot > rgx before
bot > rgx beforeAnswer
bot > hello beforeAnswer
bot > bot before // We've jumped to "world" command with "ctx.go('world')""
bot > bot beforeInvoke
bot > rgx before
bot > rgx beforeInvoke
bot > world before
bot > world invoke
Predefined middlewares
There are two predefined middlewares:
botanio
- tracks each incoming message. See http://botan.io/typing
- shows typing status before each message. See https://core.telegram.org/bots/api#sendchataction
Usage:
bot;bot;
Sessions
Sessions can be implemented with Redis, with memory/fs storage or your custom storage
bot
This dialog demonstrates how it works:
me > /memory
bot > Type some string
me > 1
bot > 1
me > 2
bot > 12
me > hello
bot > 12hello
Redis storage
var bb = require('bot-brother')
bot = bb({
key: '<_TELEGRAM_BOT_TOKEN>',
sessionManager: bb.sessionManager.redis({port: '...', host: '...'}),
polling: { interval: 0, timeout: 1 }
})
With custom Redis-client
var bb = require('bot-brother')
bot = bb({
key: '<_TELEGRAM_BOT_TOKEN>',
sessionManager: bb.sessionManager.redis({client: yourCustomRedisConnection}),
polling: { interval: 0, timeout: 1 }
})
Memory storage
var bb = require('bot-brother')
bot = bb({
key: '<_TELEGRAM_BOT_TOKEN>',
// set the path where your session will be saved. You can skip this option
sessionManager: bb.sessionManager.memory({dir: '/path/to/dir'}),
polling: { interval: 0, timeout: 1 }
})
Your custom storage
var bb = require('bot-brother')
bot = bb({
key: '<_TELEGRAM_BOT_TOKEN>',
// set the path where your session will be saved. You can skip this option
sessionManager: function (bot) {
return bb.sessionManager.create({
save: function (id, session) {
// save session
// should return promise
return Promise.resolve(true)
},
get: function(id) {
// get session by key
// should return promise with {Object}
return fetchYourSessionAsync(id)
},
getMultiple: function(ids) {
// optionally method
// define it if you use expression: bot.withContexts(ids)
// should return promise with array of session objects
},
getAll: function() {
// optionally method, same as 'getMultiple'
// define it if you use bot.withAllContexts
}
})
},
polling: { interval: 0, timeout: 1 }
})
Localization and texts
Localization can be used in texts and keyboards. For templates we use ejs.
// Setting keys and values for locale 'en'.bot // Setting default localization values (used if key in certain locale did not found).bot bot; botbotbotbot
When bot-brother sends a message, it tries to interpret this message as a key from your localization set. If key's not found, it interprets the message as a template with variables and renders it via ejs.
All local variables can be set via ctx.data
.
Texts can be set for following entities:
- bot
- command
- context
bot; bot; bot; bot
Bot dialog:
me > /page1
bot > Page 1 text
me > /page2
bot > Page 2 text
me > /page3
bot > Page 3 text
Keyboards
You can set keyboard for context, command or bot.
// This keyboard is applied for any command.// Also you can use emoji in keyboard.bot bot bot bot
Going to command
You can go to any command via keyboard. First argument for go
method is a command name.
bot.keyboard([[
{'command1': {go: 'command1'}}
]])
isShown flag
isShown
flag can be used to hide keyboard buttons in certain moment.
bot.use('before', function (ctx) {
ctx.isButtonShown = Math.round() > 0.5;
}).keyboard([[
{
'text1': {
go: 'command1',
isShown: function (ctx) {
return ctx.isButtonShown;
}
}
}
]]);
Localization in keyboards
bot
Keyboard templates
You can use keyboard templates
bot bot bot bot
Keyboard answers
If you want to handle a text answer from your keyboard, use following code:
bot;
Sometimes you want user to manually enter an answer. Use following code to do this:
// Use 'compliantKeyboard' flag.bot;
Inline 2.0 keyboards
You can use inline keyboards in the same way as default keyboards
bot// set any your data to callbackData.// IMPORTANT! Try to fit your data in 60 chars, because Telegram has limit for inline buttons bot
Api
There are three base classes:
- Bot
- Command
- Context
Bot
Bot represents a bot.
var bb = require('bot-brother');
var bot = bb({
key: '<TELEGRAM_BOT_TOKEN>',
// optional
webHook: {
url: 'https://mybot.com/updates',
key: '<PEM_PRIVATE_KEY>',
cert: '<PEM_PUBLIC_KEY>',
port: 443,
https: true
}
})
Has following methods and fields:
bot.api
bot.api is an instance of node-telegram-bot-api
botapi;
bot.command
Creates a command.
bot;
bot.keyboard
bot
bot.texts
Defined texts can be used in keyboards, messages, photo captions
bot // With localization.bot
Using webHook
Webhook in telegram documentation: https://core.telegram.org/bots/api#setwebhook
If your node.js process is running behind the proxy (nginx for example) use following code.
We omit webHook.key
parameter and run node.js on 3000 unsecure port.
var bb = ;var bot =
Otherwise if your node.js server is available outside, use following code:
var bb = ;var bot =
Command
bot
Context
The context is the essence that runs through all middlewares. You can put some data in the context and use this data in the next handler. Context is passed as the first argument in all middleware handlers.
// this is handler is invokebot; bot;
You can put any property to context variable. But! You must observe the following rules:
- Property name can not start with an underscore.
ctx._myVar
- bad!,ctx.myVar
- good. - Names of properties should not overlap predefined properties or methods.
ctx.session = 'Hello'
- bad!ctx.mySession = 'Hello'
- good.
Context properties
Context has following predefined properties available for reading. Some of them are available for editing. Let's take a look at them:
context.session
You can put any data in context.session. This data will be available in commands and middlewares invoked for the same user. Important! Currently for group chats session data is shared between all users in chat.
bot; bot;
This is how it works:
me > /hello
bot > Hello! What is your name?
me > John
bot > OK! I remembered it.
me > /bye
bot > Bye John
context.data
This variable works when rendering message texts. For template rendering we use (ejs)[https://github.com/tj/ejs]. All the data you put in context.data is available in the templates.
bot.texts({
hello: {
world: {
friend: 'Hello world, <%=name%>!'
}
}
});
bot.command('hello').invoke(function (ctx) {
ctx.data.name = 'John';
ctx.sendMessage('hello.world.friend');
});
This is how it works:
me > /hello
bot > Hello world, John!
There is predefined method render
in context.data. It can be used for rendering embedded keys:
bot.texts({
hello: {
world: {
friend: 'Hello world, <%=name%>!',
bye: 'Good bye, <%=name%>',
message: '<%=render("hello.world.friend")%> <%=render("hello.world.bye")%>'
}
}
});
bot.command('hello').invoke(function (ctx) {
ctx.data.name = 'John';
ctx.sendMessage('hello.world.message');
});
Bot dialog:
me > /hello
bot > Hello world, John! Good bye, John
context.meta
context.meta contains following fields:
user
- see https://core.telegram.org/bots/api#userchat
- see https://core.telegram.org/bots/api#chatsessionId
- key name for saving session, currently it ismeta.chat.id
. So for group chats your session data is shared between all users in chat.
context.command
Represents currently handled command. Has following properties:
name
- the name of a commandargs
- arguments for a commandtype
- Can beinvoke
oranswer
. If handler is invoked with.withContext
method, type issynthetic
Suppose that we have the following code:
bot;
The result is the following dialogue:
me > /hello world dear friend
bot > Type: invoke; Name: hello; Arguments: world-dear-friend
me > bye
bot > Type: answer; Name: hello; Answer: bye
Also you can pass args in this way
me > /hello__world
bot > Type: invoke; Name: hello; Arguments: world
me > bye
bot > Type: answer; Name: hello; Answer: bye
context.answer
This is an answer for a command. Context.answer is defined only when user answers with a text message.
context.message
Represents message object. For more details see: https://core.telegram.org/bots/api#message
context.bot
Bot instance
context.isRedirected
Boolean. This flag is set to 'true' when a command was achieved via go
method (user did not type text /command
in bot).
Let's look at the following example:
bot; bot;
User was typing something like this:
me > /hello
bot > Type something
me > lol
bot > isRedirected: true
context.isSynthetic
Boolean. This flag is true when we achieve the handler with .withContext
method.
bot; bot
Dialog with bot:
me > /withcontext
bot > isSynthetic before: false
bot > hello
bot > isSynthetic before: true
bot > isSynthetic in handler: true
Context methods
Context has the following methods.
context.keyboard(keyboardDefinition)
Sets keyboard
ctx
context.hideKeyboard()
ctx
context.inlineKeyboard(keyboardDefinition)
Sets keyboard
ctx
context.render(key, data)
Returns rendered text or key
ctxctxdataname = 'John';var str = ctx;console; // outputs 'Hi, John Doe'
context.go()
Returns Promise
Goes to some command
var command1 = botvar command2 = bot
context.goParent()
Returns Promise
Goes to the parent command. A command is considered a descendant if its name begins with the parent command name, for example setting
is a parent command, settings_locale
is a descendant command.
var command1 = botvar command1Child = bot;
context.goBack()
Returns Promise
Goes to previously invoked command.
Useful in keyboard 'Back' button.
bot// orbot
context.repeat()
Returns Promise
Repeats current state, useful for handling wrong answers.
bot;
context.end()
Stops middlewares chain.
context.setLocale(locale)
Sets locale for the context. Use it if you need localization.
botbot;
context.getLocale()
Returns current locale
context.sendMessage(text, [options])
Returns Promise
Sends text message.
See: https://core.telegram.org/bots/api#sendmessage
Param | Type | Description |
---|---|---|
text | String |
Text or localization key to be sent |
[options] | Object |
Additional Telegram query options |
context.forwardMessage(fromChatId, messageId)
Returns Promise
Forwards messages of any kind.
Param | Type | Description |
---|---|---|
fromChatId | Number | String |
Unique identifier for the chat where the original message was sent |
messageId | Number | String |
Unique message identifier |
context.sendPhoto(photo, [options])
Returns Promise
Sends photo
See: https://core.telegram.org/bots/api#sendphoto
Param | Type | Description |
---|---|---|
photo | String | stream.Stream |
A file path or a Stream. Can also be a file_id previously uploaded |
[options] | Object |
Additional Telegram query options |
context.sendAudio(audio, [options])
Returns Promise
Sends audio
See: https://core.telegram.org/bots/api#sendaudio
Param | Type | Description |
---|---|---|
audio | String | stream.Stream |
A file path or a Stream. Can also be a file_id previously uploaded. |
[options] | Object |
Additional Telegram query options |
context.sendDocument(A, [options])
Returns Promise
Sends Document
See: https://core.telegram.org/bots/api#sendDocument
Param | Type | Description |
---|---|---|
A | String | stream.Stream |
file path or a Stream. Can also be a file_id previously uploaded. |
[options] | Object |
Additional Telegram query options |
context.sendSticker(A, [options])
Returns Promise
Sends .webp stickers.
See: https://core.telegram.org/bots/api#sendsticker
Param | Type | Description |
---|---|---|
A | String | stream.Stream |
file path or a Stream. Can also be a file_id previously uploaded. |
[options] | Object |
Additional Telegram query options |
context.sendVideo(A, [options])
Returns Promise
Use this method to send video files, Telegram clients support mp4 videos (other formats may be sent as Document).
See: https://core.telegram.org/bots/api#sendvideo
Param | Type | Description |
---|---|---|
A | String | stream.Stream |
file path or a Stream. Can also be a file_id previously uploaded. |
[options] | Object |
Additional Telegram query options |
context.sendVoice(voice, [options])
Returns Promise
Sends voice
Kind: instance method of TelegramBot
See: https://core.telegram.org/bots/api#sendvoice
Param | Type | Description |
---|---|---|
voice | String | stream.Stream |
A file path or a Stream. Can also be a file_id previously uploaded. |
[options] | Object |
Additional Telegram query options |
context.sendChatAction(action)
Returns Promise
Sends chat action.
typing
for text messages,
upload_photo
for photos, record_video
or upload_video
for videos,
record_audio
or upload_audio
for audio files, upload_document
for general files,
find_location
for location data.
See: https://core.telegram.org/bots/api#sendchataction
Param | Type | Description |
---|---|---|
action | String |
Type of action to broadcast. |
context.getUserProfilePhotos([offset], [limit])
Returns Promise
Use this method to get the list of profile pictures for a user.
Returns a UserProfilePhotos object.
See: https://core.telegram.org/bots/api#getuserprofilephotos
Param | Type | Description |
---|---|---|
[offset] | Number |
Sequential number of the first photo to be returned. By default, all photos are returned. |
[limit] | Number |
Limits the number of photos to be retrieved. Values between 1—100 are accepted. Defaults to 100. |
context.sendLocation(latitude, longitude, [options])
Returns Promise
Sends location.
Use this method to send point on the map.
See: https://core.telegram.org/bots/api#sendlocation
Param | Type | Description |
---|---|---|
latitude | Float |
Latitude of location |
longitude | Float |
Longitude of location |
[options] | Object |
Additional Telegram query options |