diff --git a/README.md b/README.md index 990f90e..2671637 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ node-telegram-bot ## Changelog +- 0.1.10 add and improve methods from updated telegram bot api - 0.1.9 Improved error 409 by kaminoo - 0.1.8 Timeout defaults to 60000ms - 0.1.7 Supports splitting command target (#45) diff --git a/lib/Bot.js b/lib/Bot.js index 33e2284..392a9c8 100644 --- a/lib/Bot.js +++ b/lib/Bot.js @@ -1,15 +1,15 @@ -'use strict' - -var EventEmitter = require('events').EventEmitter - , debug = require('debug')('node-telegram-bot') - , util = require('util') - , request = require('request') - , fs = require('fs') - , path = require('path') - , qs = require('querystring') - , Q = require('q') - , botanio = require('botanio-node') - , mime = require('mime'); +'use strict'; + +var EventEmitter = require('events').EventEmitter; +var debug = require('debug')('node-telegram-bot'); +var util = require('util'); +var request = require('request'); +var fs = require('fs'); +var path = require('path'); +var qs = require('querystring'); +var Q = require('q'); +var botanio = require('botanio-node'); +var mime = require('mime'); /** * Constructor for Telegram Bot API Client. @@ -37,18 +37,21 @@ function Bot(options) { this.analytics = null; this.timeout = options.timeout ? options.timeout : 60; //specify in seconds // define the messageType's - this.NORMAL_MESSAGE = 1; - this.EDITED_MESSAGE = 2; + // this.NORMAL_MESSAGE = 1; + // this.EDITED_MESSAGE = 2; } util.inherits(Bot, EventEmitter); /** * This callback occur after client request for a certain webservice. + * @func _get + * @param {Object} options + * @param {Bot~requestCallback} callback The callback that handles the response. * * @callback Bot~requestCallback - * @param {Error} Error during request - * @param {Object} Response from Telegram service + * @param {Error} callback.Error during request + * @param {Object} callback.Response from Telegram service */ Bot.prototype._get = function (options, callback) { var self = this; @@ -86,10 +89,13 @@ Bot.prototype._get = function (options, callback) { /** * To perform multipart request e.g. file upload - * + * @func _multipart + * @param {Object} options + * @param {Bot~requestCallback} callback The callback that handles the response. + * @callback Bot~requestCallback - * @param {Error} Error during request - * @param {Object} Response from Telegram service + * @param {Error} callback.Error during request + * @param {Object} callback.Response from Telegram service */ Bot.prototype._multipart = function (options, callback) { var self = this; @@ -164,9 +170,9 @@ Bot.prototype._multipart = function (options, callback) { /** * Temporary solution to set webhook + * @func _setWebhook + * @param {Object} webhook * - * @param {Error} Error during request - * @param {Object} Response from Telegram service */ Bot.prototype._setWebhook = function (webhook) { var self = this; @@ -176,13 +182,18 @@ Bot.prototype._setWebhook = function (webhook) { url: url, json: true }, function (err, res, body) { + /** + * @param {Object} res + * @param {Boolean} res.ok + * @param {int} res.statusCode + */ if (!err && res && res.statusCode === 200) { if (body.ok) { debug("Set webhook to " + self.webhook); - } else { - debug("Body not ok"); - debug(body); - } + } else { + debug("Body not ok"); + debug(body); + } } else if(res && res.hasOwnProperty('statusCode') && res.statusCode === 401){ debug(err); debug("Failed to set webhook with code" + res.statusCode); @@ -191,10 +202,11 @@ Bot.prototype._setWebhook = function (webhook) { debug("Failed to set webhook with unknown error"); } }); -} +}; /** * Start polling for messages + * @func _poll * * @return {Bot} Self */ @@ -217,45 +229,54 @@ Bot.prototype._poll = function () { } else if (res && res.statusCode === 200) { if (body.ok) { body.result.forEach(function (msg) { + /** + * @param {Object} msg + * @param {int} msg.update_id + * @param {Boolean} msg.edited_message + */ if (msg.update_id >= self.offset) { self.offset = msg.update_id + 1; var message; var messageType = 0; if (!!msg.message){ - message = msg.message; - messageType = 1; // We are a normal message + message = msg.message; + messageType = 1; // We are a normal message }else if (!!msg.edited_message){ - message = msg.edited_message - messageType = 2; // We are an edited message + message = msg.edited_message; + messageType = 2; // We are an edited message } if(messageType > 0) { - if (self.parseCommand) { - if (message && message.text && message.text.charAt(0) === '/') { - /** - * Split the message on space and @ - * Zero part = complete message - * First part = command with leading / - * Third part = target or empty "" - * Fourth part = arguments or empty "" - */ - var messageParts = message.text.match(/([^@ ]*)([^ ]*)[ ]?(.*)/); - - // Filter everything not alphaNum out of the command - var command = messageParts[1].replace(/[^a-zA-Z0-9 ]/g, ""); - // Target incl @ sign or null - var target = (messageParts[2] !== "" ? messageParts[2]: null); - // Optional arguments or null - var args = (messageParts[3] !== "" ? messageParts[3].split(' '): null); - - self.emit(command, message, args, target); - } - } - - if (self.analytics !== null) { - self.analytics.track(message); - } - message.messageType = messageType; - self.emit('message', message); + if (self.parseCommand) { + if (message && message.text && message.text.charAt(0) === '/') { + /** + * Split the message on space and @ + * Zero part = complete message + * First part = command with leading / + * Third part = target or empty "" + * Fourth part = arguments or empty "" + */ + var messageParts = message.text.match(/([^@ ]*)([^ ]*)[ ]?(.*)/); + + // Filter everything not alphaNum out of the command + var command = messageParts[1].replace(/[^a-zA-Z0-9 ]/g, ""); + // Target incl @ sign or null + var target = (messageParts[2] !== "" ? messageParts[2]: null); + // Optional arguments or null + var args = (messageParts[3] !== "" ? messageParts[3].split(' '): null); + + self.emit(command, message, args, target); + } + } + + if (self.analytics !== null) { + self.analytics.track(message); + } + message.messageType = messageType; + self.emit('message', message); + } + + if (msg.callback_query) { + self.emit('callback_query', msg.callback_query); } } }); @@ -282,6 +303,7 @@ Bot.prototype._poll = function () { /** * Bot start receiving activities + * @func start * * @return {Bot} Self */ @@ -298,6 +320,7 @@ Bot.prototype.start = function () { /** * End polling for messages + * @func stop * * @return {Bot} Self */ @@ -312,31 +335,39 @@ Bot.prototype.stop = function () { /** * Returns basic information about the bot in form of a User object. + * @func getMe * * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#getme */ Bot.prototype.getMe = function (callback) { - var self = this - , deferred = Q.defer(); - - this._get({ method: 'getMe' }, function (err, res) { - if (err) { - return deferred.reject(err); - } + var self = this; + var deferred = Q.defer(); + + this._get({ method: 'getMe' }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { + if (err) { + return deferred.reject(err); + } - if (res.ok) { - self.id = res.result.id; - self.first_name = res.result.first_name; - self.username = res.result.username; + if (res.ok) { + self.id = res.result.id; + self.first_name = res.result.first_name; + self.username = res.result.username; - deferred.resolve(res.result); - } else { - deferred.reject(res); - } - }); + deferred.resolve(res.result); + } else { + deferred.reject(res); + } + }); return deferred.promise.nodeify(callback); }; @@ -344,18 +375,17 @@ Bot.prototype.getMe = function (callback) { /** * Use this method to get a list of profile pictures for a user. * - * @param {Object} options Options - * @param {Integer} options.user_id Unique identifier of the target user - * @param {String=} options.offset Sequential number of the first photo to be returned. By default, all photos are returned. - * @param {Integer=} options.limit Limits the number of photos to be retrieved. Values between 1—100 are accepted. Defaults to 100. + * @param {Object} options Options + * @param {int} options.user_id Unique identifier of the target user + * @param {String=} options.offset Sequential number of the first photo to be returned. By default, all photos are returned. + * @param {int=} options.limit Limits the number of photos to be retrieved. Values between 1—100 are accepted. Defaults to 100. * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#getuserprofilephotos */ Bot.prototype.getUserProfilePhotos = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); this._get({ method: 'getUserProfilePhotos', @@ -364,7 +394,14 @@ Bot.prototype.getUserProfilePhotos = function (options, callback) { offset: options.offset, limit: options.limit } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -382,24 +419,32 @@ Bot.prototype.getUserProfilePhotos = function (options, callback) { /** * Use this method to get basic info about a file and prepare it for downloading. For the moment, bots can download files of up to 20MB in size. * - * @param {Object} options Options - * @param {String} options.file_id File identifier to get info about - * @param {String=} options.dir Directory the file to be stored (if it is not specified, no file willbe downloaded) + * @param {Object} options Options + * @param {String} options.file_id File identifier to get info about + * @param {String=} options.dir Directory the file to be stored (if it is not specified, no file willbe downloaded) * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#getfile */ Bot.prototype.getFile = function (options, callback) { - var self = this - , deferred = Q.defer(); + var self = this; + var deferred = Q.defer(); this._get({ method: 'getFile', params: { file_id: options.file_id } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + * @param {string} res.result.file_path + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -438,8 +483,8 @@ Bot.prototype.getFile = function (options, callback) { /** * Use this method to get the download URL for a Telegram file when file_path is provided along file_id. Usually happens with forwarded messages to which you already called getFile. * - * @param {String} file_path File path provided by Telegram - * @return {String} Download URL + * @param {String} file_path File path provided by Telegram + * @return {String} Download URL * */ Bot.prototype.getFileURLFromFilePath = function (file_path) { @@ -447,25 +492,23 @@ Bot.prototype.getFileURLFromFilePath = function (file_path) { return self.base_url + 'file/bot' + self.token + '/' + file_path; }; - /** * Use this method to send text messages. * - * @param {Object} options Options - * @param {Integer} options.chat_id Unique identifier for the message recipient — User or GroupChat id - * @param {String} options.text Text of the message to be sent - * @param {String} options.parse_mode Send Markdown, if you want Telegram apps to show bold, italic and inline URLs in your bot's message. - * @param {Boolean=} options.disable_web_page_preview Disables link previews for links in this message - * @param {Integer=} options.reply_to_message_id If the message is a reply, ID of the original message - * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {String} options.text Text of the message to be sent + * @param {String} options.parse_mode Send Markdown, if you want Telegram apps to show bold, italic and inline URLs in your bot's message. + * @param {Boolean=} options.disable_web_page_preview Disables link previews for links in this message + * @param {int=} options.reply_to_message_id If the message is a reply, ID of the original message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#sendmessage */ Bot.prototype.sendMessage = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); this._get({ method: 'sendMessage', @@ -477,7 +520,14 @@ Bot.prototype.sendMessage = function (options, callback) { reply_to_message_id: options.reply_to_message_id, reply_markup: JSON.stringify(options.reply_markup) } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -492,10 +542,18 @@ Bot.prototype.sendMessage = function (options, callback) { return deferred.promise.nodeify(callback); }; -// https://tlgrm.ru/docs/bots/api#sendcontact +/** + * Use this method to send phone contacts. On success, the sent Message is returned. + * + * @func sendContact + * @param {Object} options + * @param callback + * @returns {*} + * + * @see https://tlgrm.ru/docs/bots/api#sendcontact + */ Bot.prototype.sendContact = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); this._get({ method: 'sendContact', @@ -508,7 +566,14 @@ Bot.prototype.sendContact = function (options, callback) { reply_to_message_id: options.reply_to_message_id, reply_markup: JSON.stringify(options.reply_markup) } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -526,27 +591,35 @@ Bot.prototype.sendContact = function (options, callback) { /** * Use this method to forward messages of any kind. * - * @param {Object} options Options - * @param {Integer} options.chat_id Unique identifier for the message recipient — User or GroupChat id - * @param {Integer} options.from_chat_id Unique identifier for the chat where the original message was sent — User or GroupChat id - * @param {Integer} options.message_id Unique message identifier + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {int} options.from_chat_id Unique identifier for the chat where the original message was sent — User or GroupChat id + * @param {Boolean} options.disable_notification Optional Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound. + * @param {int} options.message_id Unique message identifier * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#forwardmessage */ Bot.prototype.forwardMessage = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); this._get({ method: 'forwardMessage', params: { chat_id: options.chat_id, from_chat_id: options.from_chat_id, + disable_notification: options.disable_notification, message_id: options.message_id } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -564,33 +637,42 @@ Bot.prototype.forwardMessage = function (options, callback) { /** * Use this method to send photos. * - * @param {Object} options Options - * @param {Integer} options.chat_id Unique identifier for the message recipient — User or GroupChat id - * @param {String} options.photo Path to photo file (Library will create a stream if the path exist) - * @param {String=} options.file_id If file_id is passed, method will use this instead - * @param {String=} options.caption Photo caption (may also be used when resending photos by file_id). - * @param {Integer=} options.reply_to_message_id If the message is a reply, ID of the original message - * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {String} options.photo Path to photo file (Library will create a stream if the path exist) + * @param {String=} options.file_id If file_id is passed, method will use this instead + * @param {String=} options.caption Photo caption (may also be used when resending photos by file_id). + * @param {Boolean} options.disable_notification Optional Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound. + * @param {int=} options.reply_to_message_id If the message is a reply, ID of the original message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options.files * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#sendphoto */ Bot.prototype.sendPhoto = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); if (options.file_id) { this._get({ method: 'sendPhoto', params: { chat_id: options.chat_id, - caption: options.caption, photo: options.file_id, + caption: options.caption, + disable_notification: options.disable_notification, reply_to_message_id: options.reply_to_message_id, reply_markup: JSON.stringify(options.reply_markup) } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -625,7 +707,14 @@ Bot.prototype.sendPhoto = function (options, callback) { reply_markup: JSON.stringify(options.reply_markup) }, files: files - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -644,20 +733,25 @@ Bot.prototype.sendPhoto = function (options, callback) { /** * Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. * - * @param {Object} options Options - * @param {Integer} options.chat_id Unique identifier for the message recipient — User or GroupChat id - * @param {String} options.audio Path to audio file (Library will create a stream if the path exist) - * @param {String=} options.file_id If file_id is passed, method will use this instead - * @param {Integer=} options.reply_to_message_id If the message is a reply, ID of the original message - * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {String} options.audio Path to audio file (Library will create a stream if the path exist) + * @param {String=} options.file_id If file_id is passed, method will use this instead + * @param {String} options.caption Optional Audio caption, 0-200 characters + * @param {int} options.duration Optional Duration of the audio in seconds + * @param {String} options.performer Optional Performer + * @param {String} options.title Optional Track name + * @param {Boolean} options.disable_notification Optional Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound. + * @param {int=} options.reply_to_message_id If the message is a reply, ID of the original message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options.files * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#sendaudio */ Bot.prototype.sendAudio = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); if (options.file_id) { this._get({ @@ -665,10 +759,22 @@ Bot.prototype.sendAudio = function (options, callback) { params: { chat_id: options.chat_id, audio: options.file_id, + caption: options.caption, + duration: options.duration, + performer: options.performer, + title: options.title, + disable_notification: options.disable_notification, reply_to_message_id: options.reply_to_message_id, reply_markup: JSON.stringify(options.reply_markup) } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -705,7 +811,14 @@ Bot.prototype.sendAudio = function (options, callback) { reply_markup: JSON.stringify(options.reply_markup) }, files: files - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -724,20 +837,22 @@ Bot.prototype.sendAudio = function (options, callback) { /** * Use this method to send general files. * - * @param {Object} options Options - * @param {Integer} options.chat_id Unique identifier for the message recipient — User or GroupChat id - * @param {String} options.document Path to document file (Library will create a stream if the path exist) - * @param {String=} options.file_id If file_id is passed, method will use this instead - * @param {Integer=} options.reply_to_message_id If the message is a reply, ID of the original message - * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {String} options.file_id file_id as String + * @param {String} options.document Path to document file (Library will create a stream if the path exist) + * @param {String} options.caption Optional New caption of the message + * @param {Boolean} options.disable_notification Optional Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound. + * @param {int=} options.reply_to_message_id If the message is a reply, ID of the original message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options.files * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#senddocument */ Bot.prototype.sendDocument = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); if (options.file_id) { this._get({ @@ -745,10 +860,19 @@ Bot.prototype.sendDocument = function (options, callback) { params: { chat_id: options.chat_id, document: options.file_id, + caption: options.caption, + disable_notification: options.disable_notification, reply_to_message_id: options.reply_to_message_id, reply_markup: JSON.stringify(options.reply_markup) } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -782,7 +906,14 @@ Bot.prototype.sendDocument = function (options, callback) { reply_markup: JSON.stringify(options.reply_markup) }, files: files - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -801,20 +932,21 @@ Bot.prototype.sendDocument = function (options, callback) { /** * Use this method to send .webp stickers. * - * @param {Object} options Options - * @param {Integer} options.chat_id Unique identifier for the message recipient — User or GroupChat id - * @param {String} options.sticker Path to sticker file (Library will create a stream if the path exist) - * @param {String=} options.file_id If file_id is passed, method will use this instead - * @param {Integer=} options.reply_to_message_id If the message is a reply, ID of the original message - * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {String} options.sticker Path to sticker file (Library will create a stream if the path exist) + * @param {String=} options.file_id If file_id is passed, method will use this instead + * @param {Boolean} options.disable_notification Optional Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound. + * @param {int=} options.reply_to_message_id If the message is a reply, ID of the original message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options.files * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#sendsticker */ Bot.prototype.sendSticker = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); if (options.file_id) { this._get({ @@ -822,10 +954,18 @@ Bot.prototype.sendSticker = function (options, callback) { params: { chat_id: options.chat_id, sticker: options.file_id, + disable_notification: options.disable_notification, reply_to_message_id: options.reply_to_message_id, reply_markup: JSON.stringify(options.reply_markup) } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -852,7 +992,14 @@ Bot.prototype.sendSticker = function (options, callback) { files: { sticker: options.files.sticker } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -871,20 +1018,25 @@ Bot.prototype.sendSticker = function (options, callback) { /** * Use this method to send video files, Telegram clients support mp4 video. * - * @param {Object} options Options - * @param {Integer} options.chat_id Unique identifier for the message recipient — User or GroupChat id - * @param {String} options.video Path to video file (Library will create a stream if the path exist) - * @param {String=} options.file_id If file_id is passed, method will use this instead - * @param {Integer=} options.reply_to_message_id If the message is a reply, ID of the original message - * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {String} options.video Path to video file (Library will create a stream if the path exist) + * @param {String=} options.file_id If file_id is passed, method will use this instead + * @param {int} options.duration Optional Duration of sent video in seconds + * @param {int} options.width Optional Video width + * @param {int} options.height Optional Video height + * @param {String} options.caption Optional Video caption (may also be used when resending videos by file_id), 0-200 characters + * @param {Boolean} options.disable_notification Optional Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound. + * @param {int=} options.reply_to_message_id If the message is a reply, ID of the original message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options.files * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#sendvideo */ Bot.prototype.sendVideo = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); if (options.file_id) { this._get({ @@ -892,10 +1044,22 @@ Bot.prototype.sendVideo = function (options, callback) { params: { chat_id: options.chat_id, video: options.file_id, + duration: options.duration, + width: options.width, + height: options.height, + caption: options.caption, + disable_notification: options.disable_notification, reply_to_message_id: options.reply_to_message_id, reply_markup: JSON.stringify(options.reply_markup) } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -932,7 +1096,114 @@ Bot.prototype.sendVideo = function (options, callback) { reply_markup: JSON.stringify(options.reply_markup) }, files: files - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { + if (err) { + return deferred.reject(err); + } + + if (res.ok) { + deferred.resolve(res.result); + } else { + deferred.reject(res); + } + }); + } + + return deferred.promise.nodeify(callback); +}; + +/** + * Use this method to send audio files, if you want Telegram clients to display the file as a playable voice message. + * + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {String} options.audio Path to audio file (Library will create a stream if the path exist) + * @param {String} options.caption Optional Voice message caption, 0-200 characters + * @param {int} options.duration Optional Duration of the voice message in seconds + * @param {String=} options.file_id If file_id is passed, method will use this instead + * @param {Boolean} options.disable_notification Optional Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound. + * @param {int=} options.reply_to_message_id If the message is a reply, ID of the original message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options.files + * @param {Bot~requestCallback} callback The callback that handles the response. + * @return {promise} Q Promise + * + * @see https://core.telegram.org/bots/api#sendvoice + */ +Bot.prototype.sendVoice = function (options, callback) { + var deferred = Q.defer(); + + if (options.file_id) { + this._get({ + method: 'sendVoice', + params: { + chat_id: options.chat_id, + audio: options.file_id, + caption: options.caption, + duration: options.duration, + disable_notification: options.disable_notification, + reply_to_message_id: options.reply_to_message_id, + reply_markup: JSON.stringify(options.reply_markup) + } + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { + if (err) { + return deferred.reject(err); + } + + if (res.ok) { + deferred.resolve(res.result); + } else { + deferred.reject(res); + } + }); + } else { + var files; + if (options.files.stream) { + files = { + type: 'audio', + filename: options.files.filename, + contentType: options.files.contentType, + stream: options.files.stream + } + } else if (mime.lookup(options.files.audio) !== 'audio/ogg') { + return Q.reject(new Error('Invalid file type')) + .nodeify(callback); + } else { + files = { + audio: options.files.audio + } + } + + this._multipart({ + method: 'sendVoice', + params: { + chat_id: options.chat_id, + reply_to_message_id: options.reply_to_message_id, + reply_markup: JSON.stringify(options.reply_markup) + }, + files: files + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -951,20 +1222,20 @@ Bot.prototype.sendVideo = function (options, callback) { /** * Use this method to send point on the map. * - * @param {Object} options Options - * @param {Integer} options.chat_id Unique identifier for the message recipient — User or GroupChat id - * @param {Float} options.latitude Latitude of location - * @param {Float} options.longitude Longitude of location - * @param {Integer=} options.reply_to_message_id If the message is a reply, ID of the original message - * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {float} options.latitude Latitude of location + * @param {float} options.longitude Longitude of location + * @param {Boolean} options.disable_notification Optional Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound. + * @param {int=} options.reply_to_message_id If the message is a reply, ID of the original message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#sendlocation */ Bot.prototype.sendLocation = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); this._get({ method: 'sendLocation', @@ -972,10 +1243,75 @@ Bot.prototype.sendLocation = function (options, callback) { chat_id: options.chat_id, latitude: options.latitude, longitude: options.longitude, + disable_notification: options.disable_notification, reply_to_message_id: options.reply_to_message_id, reply_markup: JSON.stringify(options.reply_markup) } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { + if (err) { + return deferred.reject(err); + } + + if (res.ok) { + deferred.resolve(res.result); + } else { + deferred.reject(res); + } + }); + + return deferred.promise.nodeify(callback); +}; + + +/** + * Use this method to send point on the map. + * + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {float} options.latitude Latitude of location + * @param {float} options.longitude Longitude of location + * @param {String} options.title Yes Name of the venue + * @param {String} options.address Yes Address of the venue + * @param {String} options.foursquare_id Optional Foursquare identifier of the venue + * @param {Boolean} options.disable_notification Optional Sends the message silently. iOS users will not receive a notification, Android users will receive a notification with no sound. + * @param {int=} options.reply_to_message_id If the message is a reply, ID of the original message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Bot~requestCallback} callback The callback that handles the response. + * @return {promise} Q Promise + * + * @see https://core.telegram.org/bots/api#sendvenue + */ +Bot.prototype.sendVenue = function (options, callback) { + var deferred = Q.defer(); + + this._get({ + method: 'sendVenue', + params: { + chat_id: options.chat_id, + latitude: options.latitude, + longitude: options.longitude, + title: options.title, + address: options.address, + foursquare_id: options.foursquare_id, + disable_notification: options.disable_notification, + reply_to_message_id: options.reply_to_message_id, + reply_markup: JSON.stringify(options.reply_markup) + } + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -993,17 +1329,16 @@ Bot.prototype.sendLocation = function (options, callback) { /** * Use this method when you need to tell the user that something is happening on the bot's side. * - * @param {Object} options Options - * @param {Integer} options.chat_id Unique identifier for the message recipient — User or GroupChat id - * @param {String} options.action Type of action to broadcast. + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {String} options.action Type of action to broadcast. * @param {Bot~requestCallback} callback The callback that handles the response. - * @return {Promise} Q Promise + * @return {promise} Q Promise * * @see https://core.telegram.org/bots/api#sendchataction */ Bot.prototype.sendChatAction = function (options, callback) { - var self = this - , deferred = Q.defer(); + var deferred = Q.defer(); this._get({ method: 'sendChatAction', @@ -1011,7 +1346,70 @@ Bot.prototype.sendChatAction = function (options, callback) { chat_id: options.chat_id, action: options.action } - }, function (err, res) { + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { + if (err) { + return deferred.reject(err); + } + + if (res.ok) { + deferred.resolve(res.result); + } else { + deferred.reject(res); + } + }); + + return deferred.promise.nodeify(callback); +}; + +/** + * Use this method to edit text messages sent by the bot or via the bot (for inline bots). + * On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + * @func editMessageText + * + * @param {Object} options Options + + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {int} options.message_id Optional Required if inline_message_id is not specified. Unique identifier of the sent message + * @param {String} options.inline_message_id Optional Required if chat_id and message_id are not specified. Identifier of the inline message + * @param {String} options.text Text of the message to be sent + * @param {String} options.parse_mode Send Markdown, if you want Telegram apps to show bold, italic and inline URLs in your bot's message. + * @param {Boolean=} options.disable_web_page_preview Disables link previews for links in this message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + + * @param {Bot~requestCallback} callback The callback that handles the response. + * @return {promise} Q Promise + * + * @see https://core.telegram.org/bots/api#editmessagetext + */ +Bot.prototype.editMessageText = function (options, callback) { + var deferred = Q.defer(); + + this._get({ + method: 'editMessageText', + params: { + chat_id: options.chat_id, + message_id: options.message_id, + inline_message_id: options.inline_message_id, + text: options.text, + parse_mode: options.parse_mode, + disable_web_page_preview: options.disable_web_page_preview, + reply_markup: JSON.stringify(options.reply_markup) + } + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { if (err) { return deferred.reject(err); } @@ -1026,13 +1424,126 @@ Bot.prototype.sendChatAction = function (options, callback) { return deferred.promise.nodeify(callback); }; + +/** + * Use this method to edit captions of messages sent by the bot or via the bot (for inline bots). + * On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + * @func editMessageCaption + * + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {int} options.message_id Optional Required if inline_message_id is not specified. Unique identifier of the sent message + * @param {String} options.inline_message_id Optional Required if chat_id and message_id are not specified. Identifier of the inline message + * @param {String} options.caption Optional New caption of the message + * @param {String} options.text Text of the message to be sent + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Bot~requestCallback} callback The callback that handles the response. + * @return {promise} Q Promise + * + * @see https://core.telegram.org/bots/api#editmessagecaption + */ +Bot.prototype.editMessageCaption = function (options, callback) { + var deferred = Q.defer(); + + this._get({ + method: 'editMessageCaption', + params: { + chat_id: options.chat_id, + message_id: options.message_id, + inline_message_id: options.inline_message_id, + caption: options.caption, + text: options.text, + reply_markup: JSON.stringify(options.reply_markup) + } + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { + if (err) { + return deferred.reject(err); + } + + if (res.ok) { + deferred.resolve(res.result); + } else { + deferred.reject(res); + } + }); + + return deferred.promise.nodeify(callback); +}; + + + +/** + * Use this method to edit captions of messages sent by the bot or via the bot (for inline bots). + * On success, if edited message is sent by the bot, the edited Message is returned, otherwise True is returned. + * @func editMessageReplyMarkup + * + * @param {Object} options Options + * @param {int} options.chat_id Unique identifier for the message recipient — User or GroupChat id + * @param {int} options.message_id Optional Required if inline_message_id is not specified. Unique identifier of the sent message + * @param {String} options.inline_message_id Optional Required if chat_id and message_id are not specified. Identifier of the inline message + * @param {Object=} options.reply_markup Additional interface options. {@link https://core.telegram.org/bots/api/#replykeyboardmarkup| ReplyKeyboardMarkup} + * @param {Bot~requestCallback} callback The callback that handles the response. + * @return {promise} Q Promise + * + * @see https://core.telegram.org/bots/api#editmessagereplymarkup + */ +Bot.prototype.editMessageReplyMarkup = function (options, callback) { + /** + * @param {Object} Q + * @param {Function} Q.defer + * @param {Object} deferred + * @param {Function} deferred.reject + * @param {Function} deferred.resolve + * @param {Object} deferred.promise + * @param {Function} deferred.promise.nodeify + */ + var deferred = Q.defer(); + + this._get({ + method: 'editMessageReplyMarkup', + params: { + chat_id: options.chat_id, + message_id: options.message_id, + inline_message_id: options.inline_message_id, + reply_markup: JSON.stringify(options.reply_markup) + } + }, + /** + * @param {Error} err + * @param {Object} res + * @param {Boolean} res.ok + * @param {Object} res.result + */ + function (err, res) { + if (err) { + return deferred.reject(err); + } + + if (res.ok) { + deferred.resolve(res.result); + } else { + deferred.reject(res); + } + }); + + return deferred.promise.nodeify(callback); +}; + + /** * Analytics from http://botan.io/ * Allows all incoming messages, and you can make tagging, for specific messages * bot.analytics.track(message, 'Specific tag'); * * @param {String} token You can take this token here: https://appmetrica.yandex.com/ - * @return {Bot} Self + * @return {Bot} Self * * @see https://github.com/botanio/sdk */