Very powerful module for creating Telegram bots.
To install the stable version:
npm install --save telegram-node-bot
This assumes you are using npm as your package manager. If you don’t, you can access these files on npmcdn, download them, or point your package manager to them.
First of all you need to create your bot and get Token, you can do it right in telegram, just write to @BotFather.
Now lets write 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')
})
})
Thats it!
I'm using something like MVC, so we have router and controllers. First you need to declare your commands and which controller will handle it. Then you need to write controllers and handle specific commands in it.
For example if we want three command: /start, /stop and /restart And we want that commands to be handled by different controllers.
Router declaration code will be like this:
tg.router.
when(['/start'], 'StartController').
when(['/stop'], 'StopController').
when(['/restart'], 'RestartController')
Probably we will have a case when user send us command we didn't know, for that case router have otherwise function:
tg.router.
when(['/start'], 'StartController').
when(['/stop'], 'StopController').
when(['/restart'], 'RestartController').
otherwise('OtherwiseController')
Now all unknown commands will be handled by OtherwiseController.
Sometimes your commands have some args, you can declire them like this:
tg.router.
when(['/sum :num1 :num2'], 'SumController')
After they will be in 'query' property of scope:
tg.controller('SumController', ($) => {
tg.for('/sum :num1 :num2', ($) => {
$.sendMessage(parseInt($.query.num1) + parseInt($.query.num2))
})
})
Let's say you have some login logic in controller probably you need to route user to login 'page'. For that case you have routeTo function:
tg.controller('StartController', ($) => {
tg.for('/profile', ($) => {
if(!logined){
$.routeTo("/login")
}
})
})
Controllers are very simple:
tg.controller('ExampleController', ($) => {
//you can create any functions, variables, etc. here
tg.for('/test', ($) => {
//will handle /test command
})
tg.for('/example', ($) => {
//will handle /exaple command
})
})
Remember: if you want to handle command in controller you need to declare it in router.
Let's say you asked user for something, now you need to wait his next message and make some logic, right?
waitForRequest function well help you:
tg.controller('ExampleController', ($) => {
tg.for('/reg', ($) => {
$.sendMessage('Send me your name!')
$.waitForRequest(($) => {
$.sendMessage('Hi ' + $.message.text + '!')
})
})
})
waitForRequest will call callback and pass new context.
You can call methods two ways:
Directly from tg:
tg.sendMessage(chatId, 'Hi')
Or if you using controllers controller will pass you context '$' than already knows user id, so it's more easy to use:
$.sendMessage('Hi')
All methods have required parameters and optional parameters, you can find them in api documentation Also all methods have callback parameter, callback returns request result, callback parameter always the last one.
With $.runForm function you can create forms:
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)
}
}
}
$.runForm(form, (result) => {
console.log(result)
})
Bot will ask send the 'q' message to user, wait for message, validate it with your validator function and save the answer, if validation fails bot will ask again that question.
You can create menu with $.runMenu function:
$.runMenu({
message: 'Select:',
options: {
parse_mode: 'Markdown' // in options field you can pass some additional data, like parse_mode
},
'Exit': {
message: 'Do you realy want to exit?',
resize_keyboard: true,
'yes': () => {
},
'no': () => {
}
},
'anyMatch': () => { //will be executed at any other message
}
})
Layouting menu:
You can pass the maximum number of buttons in line like this:
$.runMenu({
message: 'Select:',
layout: 2,
'test1': () => {}, //will be on first line
'test2': () => {}, //will be on first line
'test3': () => {}, //will be on second line
'test4': () => {}, //will be on second line
'test5': () => {}, //will be on third line
})
Or you can pass an array of number of buttons for each line:
$.runMenu({
message: 'Select:',
layout: [1, 2, 1, 1],
'test1': () => {}, //will be on first line
'test2': () => {}, //will be on second line
'test3': () => {}, //will be on second line
'test4': () => {}, //will be on third line
'test5': () => {}, //will be on fourth line
})
If you pass layout
Bot will create keyboard and send it with your message, when user select if item is callback bot will call it, if it's submenu bot will send submenu.
You can crete inline menu using tg.runInlineMenu or $.runInlineMenu using scope:
tg.runInlineMenu(chatId, methodName, methodArgs... , menuData, layout)
Example:
$.runInlineMenu('sendMessage', 'Select:', {}, [
{
text: '1',
callback: ($) => {
console.log(1)
}
},
{
text: '2',
url: 'telegram.org',
callback: ($) => {
console.log('telegram.org')
}
}
], [2])
As you can see controller methods always give you scope ( $ ).
Scope have:
- all methods with already set chatId
- chatId (current chat id)
- message
- args ( if you have command '/test' and user will send you '/test 1' 'args' will contain 1
- query ( object of args )
List of supported methods with required parameters:
sendPhoto(chatId, photo)
sendDocument(chatId, document)
sendMessage(chatId, text)
sendLocation(chatId, latitude, longitude)
sendAudio(chatId, audio)
forwardMessage(chatId, fromChatId, messageId)
getFile(fileId)
sendChatAction(chatId, action)
getUserProfilePhotos(userId)
sendSticker(chatId, sticker)
sendVoice(chatId, voice)
sendVideo(chatId, video)
kickChatMember(chatId, userId)
unbanChatMember(chatId, userId)
sendVenue(chatId, latitude, longitude, title, address, options)
sendContact(chatId, phoneNumber, firstName, options)
editMessageText(text, options)
editMessageCaption(options)
editMessageReplyMarkup(options)
answerCallbackQuery(callbackQueryId)
sendPhotoFromUrl(chatId, url)
sendDocumentFromUrl(chatId, url)
sendAudioFromUrl(chatId, url)
call(method, params)
For sendDocument method document parameter need to be like this:
var doc = {
value: fs.createReadStream('file.png'), //stream
filename: 'photo.png',
contentType: 'image/png'
}
$.sendDocument(doc)
For sendPhoto method photo parameter is ReadStream object, example:
$.sendPhoto(fs.createReadStream('photo.jpeg'))
For sendAudio method audio parameter is ReadStream object, example:
$.sendAudio(fs.createReadStream('audio.mp3'))
For sendVoice method voice parameter is ReadStream object, example:
$.sendVoice(fs.createReadStream('voice.ogg'))
For sendVideo method video parameter is ReadStream object, example:
$.sendVideo(fs.createReadStream('video.mp4'))
For sendSticker method sticker parameter is ReadStream object, example:
$.sendSticker(fs.createReadStream('sticker.webp'))
You can handle inline requests with 'tg.inlineMode':
tg.inlineMode(($) => {
})
To answer request you can use 'answerInlineQuery' and 'paginatedAnswer' methods:
tg.inlineMode(($) => {
tg.answerInlineQuery($.id, [{
type: 'gif',
gif_url: 'http://thecatapi.com/api/images/get?format=src&size=med&type=gif&qwe=' + Math.random().toString(36).substring(2),
gif_width: 250,
gif_height: 250,
thumb_url: 'http://thecatapi.com/api/images/get?format=src&size=small&type=gif&qwe=' + Math.random().toString(36).substring(2)
}])
})
'paginatedAnswer' method will care about paging response for you:
tg.inlineMode(($) => {
var results = []
for(var i = 0; i < 51; i++){
results.push({
type: 'gif',
gif_url: 'http://thecatapi.com/api/images/get?format=src&size=med&type=gif&uid=' + Math.random().toString(36).substring(2),
gif_width: 250,
gif_height: 250,
thumb_url: 'http://thecatapi.com/api/images/get?format=src&size=small&type=gif&uid=' + Math.random().toString(36).substring(2)
})
}
tg.paginatedAnswer($, results, 10)
})
'inlineMode' method passes inline scope that consists of update object and 'answer' and 'paginatedAnswer' methods prepared for answer that request. So this code will be valide too:
tg.inlineMode(($) => {
var results = []
for(var i = 0; i < 51; i++){
results.push({
type: 'gif',
gif_url: 'http://thecatapi.com/api/images/get?format=src&size=med&type=gif&uid=' + Math.random().toString(36).substring(2),
gif_width: 250,
gif_height: 250,
thumb_url: 'http://thecatapi.com/api/images/get?format=src&size=small&type=gif&uid=' + Math.random().toString(36).substring(2)
})
}
$.paginatedAnswer(results, 10)
})
You can handle qallback queries with 'tg.callbackQueries':
tg.callbackQueries(($) => {
})
And you can answer them using 'tg.answerInlineQuery' method or using 'answer' method from scope:
tg.callbackQueries(($) => {
$.answer({text: 'test'})
$.answerInlineQuery($.id, {text: 'test'}) //result will be the same
})
Copyright (c) 2016 Narek Abovyan
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.