├── .npmignore ├── test ├── fixtures │ ├── doc.txt │ ├── bluejay.ogg │ ├── pizzacat.jpg │ └── sticker.webp └── test.js ├── src ├── util.js └── apiclient.js ├── .editorconfig ├── .gitignore ├── .travis.yml ├── LICENSE ├── index.js ├── package.json └── README.md /.npmignore: -------------------------------------------------------------------------------- 1 | test/ 2 | -------------------------------------------------------------------------------- /test/fixtures/doc.txt: -------------------------------------------------------------------------------- 1 | Hello World! 2 | -------------------------------------------------------------------------------- /test/fixtures/bluejay.ogg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m90/telegram-bot-client/HEAD/test/fixtures/bluejay.ogg -------------------------------------------------------------------------------- /test/fixtures/pizzacat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m90/telegram-bot-client/HEAD/test/fixtures/pizzacat.jpg -------------------------------------------------------------------------------- /test/fixtures/sticker.webp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/m90/telegram-bot-client/HEAD/test/fixtures/sticker.webp -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | isFileId: function (el) { 3 | return (/[a-z0-9\-_]{55}/i).test(el); 4 | } 5 | }; 6 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | trim_trailing_whitespace = true 11 | 12 | # tab indentation for js, json and less 13 | [*.js, *.less, *.json] 14 | indent_style = tab 15 | 16 | # 2 space indentation for jade template files 17 | [*.jade] 18 | indent_style = space 19 | indent_size = 2 20 | 21 | # npm style indent for package.json 22 | [package.json] 23 | indent_style = space 24 | indent_size = 2 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | 29 | demo.js 30 | env.txt 31 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - node 4 | - lts/* 5 | sudo: false 6 | env: 7 | global: 8 | - TELEGRAM_ID=89149957 9 | matrix: 10 | secure: aCg+WKO5YDIHKESGGDb6Bda7SfX+nZOivTYnp1jyAiSsXcPe9LepEW0SKrst4GNbTM65NqsZYbdSTQ1zWIM5TjtEPUWLcyrTr3Sek7WrzhegLN3dUuW/rGyJ2KAbpx8fo2mu29DDEuWWnJTsu1LJQHwjW6STfxQ9A1op1jUzz8XkhISlLgK/6cupcp7KzmSpBm7CKn4DCvB8q+QXyzsP8WBKWjfvm7saKpFhAX+PVsDM+6ebuPHzSVVDuv7YAMRQt7VukxwqFL6JxDtkUsS+mGTcjpnRTuftwGtlpOuRaVoH6OL3+yE+nG9NkK9ESs7iWKZQCnelVgVO0Tdy/CG+pxj8eWKeDFCT0QDz3ZwHrF7sluN+BwBoxBkePWR/k6s4Spd1EaGV1UfeP1OqjpqT88bigF1zv2UTk71B4eiDfI/arYjxBlkIyZddTCLexP/Gc0LNCep09v13T7EqqxZjReze+Tup5D0/4P74NNIXrC/gp0IEc5O9qnUpRbGvrbFSRJNd2r+HEoHt++D1YiG9Z88FbvlIy6OmMpVKlVEWjG0oSzTLC+BIfpc/YqGjCBasaBzMHkOmVWGm+Amcp72LBms+G1I8QvGtTu3ljDjvDc7qgnjgGVFTpOQa7FsGxkysDLz1HC4G1s7pjbiSQ7lS515RyKwhkLpk+E27iO5e61I= 11 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Frederik Ring 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var ApiClient = require('./src/apiclient'); 2 | 3 | function TelegramBotClient (token, promise) { 4 | 5 | if (!token) { 6 | throw new Error('You must pass a token to the constructor!'); 7 | } 8 | 9 | promise = promise || Promise.resolve(); 10 | 11 | var apiClient = new ApiClient(token); 12 | 13 | function makeChainableMethod (method) { 14 | return function () { 15 | var args = [].slice.call(arguments); 16 | return new TelegramBotClient(token, promise.then(function () { 17 | return apiClient[method].apply(null, args); 18 | })); 19 | }; 20 | } 21 | 22 | for (var method in apiClient) { 23 | if (Object.prototype.hasOwnProperty.call(apiClient, method)) { 24 | this[method] = makeChainableMethod(method); 25 | } 26 | } 27 | 28 | this.delay = function (ms) { 29 | return new TelegramBotClient(token, promise.then(function () { 30 | return new Promise(function (resolve) { 31 | setTimeout(resolve, ms); 32 | }); 33 | })); 34 | }; 35 | 36 | this.promise = function () { 37 | return promise; 38 | }; 39 | 40 | this.then = function (handler, errHandler) { 41 | promise.then(handler, errHandler); 42 | }; 43 | 44 | this.catch = function (handler) { 45 | promise.catch(handler); 46 | }; 47 | 48 | } 49 | 50 | module.exports = TelegramBotClient; 51 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telegram-bot-client", 3 | "version": "2.1.1", 4 | "description": "Client library for the Telegram messenger Bot API", 5 | "main": "index.js", 6 | "scripts": { 7 | "pretest": "eslint .", 8 | "fix": "eslint . --fix", 9 | "test": "if-env TRAVIS_PULL_REQUEST=true && echo 'Skipping tests for pull requests...' || mocha" 10 | }, 11 | "repository": { 12 | "type": "git", 13 | "url": "git+https://github.com/m90/telegram-bot-client.git" 14 | }, 15 | "keywords": [ 16 | "telegram", 17 | "bot", 18 | "api", 19 | "client" 20 | ], 21 | "author": "Frederik Ring ", 22 | "license": "MIT", 23 | "bugs": { 24 | "url": "https://github.com/m90/telegram-bot-client/issues" 25 | }, 26 | "homepage": "https://github.com/m90/telegram-bot-client#readme", 27 | "devDependencies": { 28 | "eslint": "^6.8.0", 29 | "eslint-plugin-node": "^9.1.0", 30 | "if-env": "^1.0.4", 31 | "mocha": "^5.2.0" 32 | }, 33 | "dependencies": { 34 | "fr": "^1.0.0", 35 | "is-url": "^1.2.1", 36 | "superagent": "^3.8.3", 37 | "tempfile": "^1.1.1" 38 | }, 39 | "eslintConfig": { 40 | "extends": [ 41 | "eslint:recommended", 42 | "plugin:node/recommended" 43 | ], 44 | "env": { 45 | "mocha": true 46 | }, 47 | "rules": { 48 | "comma-style": [ 49 | 2, 50 | "last" 51 | ], 52 | "space-before-function-paren": [ 53 | 2, 54 | "always" 55 | ], 56 | "brace-style": [ 57 | 2, 58 | "1tbs" 59 | ], 60 | "space-before-blocks": 2 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # telegram-bot-client 2 | 3 | [![Build Status](https://travis-ci.org/m90/telegram-bot-client.svg?branch=master)](https://travis-ci.org/m90/telegram-bot-client) 4 | 5 | > node.js client for the [Telegram messenger Bot API](https://core.telegram.org/bots/api) 6 | 7 | ### Installation 8 | Install via npm: 9 | 10 | ```sh 11 | $ npm install telegram-bot-client --save 12 | ``` 13 | 14 | ### Usage 15 | **Important:** Please note that this client does not and will not support automated polling or listening to webhooks. This can be done much better by the application running the bot. If you are interested in how this might look like you can have a look at [telegram-bot-boilerplate](https://github.com/m90/telegram-bot-boilerplate). 16 | 17 | Instantiate a new client using your bot's token: 18 | 19 | ```js 20 | var TelegramBotClient = require('telegram-bot-client'); 21 | var client = new TelegramBotClient(MY_TOKEN); 22 | client.sendMessage(CHAT_ID, 'I\'m a bot, so what?'); 23 | ``` 24 | 25 | All methods on the client are chainable and will wait for the previous operations to finish: 26 | 27 | ```js 28 | client 29 | .sendMessage(CHAT_ID, 'Hi!') 30 | .sendMessage(CHAT_ID, 'How are you?') 31 | .sendMessage(CHAT_ID, 'Be right back!') 32 | .delay(25000) 33 | .sendMessage(CHAT_ID, 'Back!') 34 | .sendMessage(CHAT_ID, 'Wait, I want to show you something, where is it?') 35 | .delay(7500) 36 | .sendPhoto(CHAT_ID, SOME_URL_POINTING_TO_A_PHOTO) 37 | .sendMessage(CHAT_ID, 'How do you like it?') 38 | .catch(function(err){ 39 | // all errors ocurring in the above call chain 40 | // will be passed down to this handler 41 | console.log(err); 42 | }); 43 | ``` 44 | 45 | If you want to consume the response you can unwrap the chain using the `.promise()` method: 46 | 47 | ```js 48 | client 49 | .sendMessage(CHAT_ID, 'Did this really work?') 50 | .promise() 51 | .then(function(response){ 52 | console.log(response); 53 | }, function(err){ 54 | console.log(err); 55 | }); 56 | ``` 57 | 58 | ### Passing optional arguments: 59 | 60 | All methods are following the same convention: Required arguments are passed seperately, all optional parameters can be wrapped into an object and be supplied as the method's last argument: 61 | 62 | ```js 63 | var messageText = 'Look at this: https://www.youtube.com/watch?v=qb_hqexKkw8'; 64 | var opts = { disable_web_page_preview: true }; 65 | client.sendMessage(CHAT_ID, messageText, opts); 66 | ``` 67 | 68 | ### Available methods: 69 | 70 | All [methods described by the API docs](https://core.telegram.org/bots/api#available-methods) are present on the client. 71 | 72 | ##### `#getMe()` 73 | gets info on the bot 74 | 75 | ##### `#getUpdates([options])` 76 | gets updates (messages) sent to the bot 77 | 78 | ##### `#getUserProfilePhotos(userId[, options])` 79 | gets a user's profile photos 80 | - userId: the user's id 81 | 82 | ##### `#setWebhook(url)` 83 | sets or removes the webhook url to use 84 | - url: the url, to remove the webhook pass `''` 85 | 86 | ##### `#sendMessage(chatId, text[, options])` 87 | sends a message 88 | - chatId: the chat's id 89 | - text: the message to send 90 | 91 | ##### `#forwardMessage(chatId, fromChatId, messageId)` 92 | forwards a message 93 | - chatId: the chat's id 94 | - fromChatId: the id of the chat the message is forwarded from 95 | - messageId: the message's id 96 | 97 | ##### `#sendPhoto(chatId, photoReference[, options])` 98 | sends a photo 99 | - chatId: the chat's id 100 | - photoReference: this can be a local file path, a URL or a telegram file id 101 | 102 | ##### `#sendAudio(chatId, audioReference[, options])` 103 | sends audio 104 | - chatId: the chat's id 105 | - audioReference: this can be a local file path, a URL or a telegram file id 106 | 107 | ##### `#sendVoice(chatId, voiceReference[, options])` 108 | sends audio 109 | - chatId: the chat's id 110 | - voiceReference: this can be a local file path or a telegram file id 111 | 112 | ##### `#sendDocument(chatId, documentReference[, options])` 113 | sends a document 114 | - chatId: the chat's id 115 | - documentReference: this can be a local file path, a URL or a telegram file id 116 | 117 | ##### `#sendSticker(chatId, stickerReference[, options])` 118 | sends a sticker 119 | - chatId: the chat's id 120 | - stickerReference: this can be a local file path, a URL or a telegram file id 121 | 122 | ##### `#sendVideo(chatId, videoReference[, options])` 123 | sends video 124 | - chatId: the chat's id 125 | - videoReference: this can be a local file path, a URL or a telegram file id 126 | 127 | ##### `#sendLocation(chatId, lat, lon[, options])` 128 | sends a location 129 | - chatId: the chat's id 130 | - lat: latitude 131 | - lon: longitude 132 | 133 | ##### `#sendVenue(chatId, lat, lon, title, address[, options])` 134 | sends a venue 135 | - chatId: the chat's id 136 | - lat: latitude 137 | - lon: longitude 138 | - title: title of the venue 139 | - address: address of the venue 140 | 141 | ##### `#sendContact(chatId, phoneNumber, firstName[, options])` 142 | sends a contact 143 | - chatId: the chat's id 144 | - phoneNumber: the contact's phone number 145 | - firstName: the contact's first name 146 | 147 | ##### `#sendChatAction(chatId, action)` 148 | sends a chat action 149 | - chatId: the chat's id 150 | - action: the action to send 151 | 152 | ##### `#kickChatMember(chatId, userId)` 153 | kicks a user from a chat 154 | - chatId: the chat's id 155 | - userId: the user id to ban 156 | 157 | ##### `#unbanChatMember(chatId, userId)` 158 | unbans a previously kicked user 159 | - chatId: the chat's id 160 | - userId: the user id to unban 161 | 162 | ##### `#answerCallbackQuery(callbackQueryId[, options])` 163 | answer callback queries sent by inline keyboards 164 | - callbackQueryId: the id of the callback query 165 | 166 | ##### `#editMessageText([chatId,] identifier, text[, options])` 167 | edit a message's text 168 | - chatId: the chat's id 169 | - identifier: message or inline message id 170 | - text: the updated text 171 | 172 | ##### `#editMessageCaption([chatId,] identifier[, options])` 173 | edit a message's caption 174 | - chatId: the chat's id 175 | - identifier: message or inline message id 176 | 177 | ##### `#editMessageReplyMarkup([chatId,] identifier[, options])` 178 | edit a message's reply markup 179 | - chatId: the chat's id 180 | - identifier: message or inline message id 181 | 182 | ##### `#answerInlineQuery(inlineQueryId, results[, options])` 183 | answer an inline query 184 | - inlineQueryId: the inline query's id 185 | - results: a string containing a JSON version of the results in an array 186 | 187 | ##### `#getChat(chatId)` 188 | get information on a chat 189 | - chatId: the chat's id 190 | 191 | ##### `#getChatAdministrators(chatId)` 192 | get the administrators of a chat 193 | - chatId: the chat's id 194 | 195 | ##### `#getChatMembersCount(chatId)` 196 | get the number of members of a chat 197 | - chatId: the chat's id 198 | 199 | ##### `#getChatMember(chatId, memberId)` 200 | get information on a member of a chat 201 | - chatId: the chat's id 202 | - userId: the chat's id 203 | 204 | ##### `#leaveChat(chatId)` 205 | leave a chat 206 | - chatId: the chat's id 207 | 208 | ### Additional methods: 209 | 210 | ##### `#delay(duration)` 211 | pauses the queue for the give duration 212 | - duration: the time to pause in ms 213 | 214 | ##### `#promise()` 215 | unwraps the chain and returns a promise for the last operation 216 | 217 | ##### `#then(successHandler, errorHandler)` 218 | exposes the last operation's result via a promise interface 219 | - successHandler: handler being passed the result 220 | - errorHandler: handler being passed the error 221 | 222 | ##### `#catch(handler)` 223 | handles errors occured in the chain 224 | - handler: error handler function 225 | 226 | 227 | 228 | ### License 229 | MIT © [Frederik Ring](http://www.frederikring.com) 230 | -------------------------------------------------------------------------------- /test/test.js: -------------------------------------------------------------------------------- 1 | var assert = require('assert'); 2 | 3 | var TelegramBotClient = require('./../index'); 4 | 5 | var CHAT_ID = process.env.TELEGRAM_ID; 6 | var TOKEN = process.env.TELEGRAM_TOKEN; 7 | 8 | if (!TOKEN) { 9 | throw new Error('Please set TELEGRAM_TOKEN before running the tests!'); 10 | } 11 | 12 | describe('TelegramBotClient', function () { 13 | 14 | describe('Chain', function () { 15 | this.timeout(40000); 16 | var client = new TelegramBotClient(TOKEN); 17 | it('exposes all methods in an async chain', function () { 18 | return client 19 | .sendMessage(CHAT_ID, 'This is a test') 20 | .delay(12000) 21 | .sendMessage(CHAT_ID, 'I can wait') 22 | .promise(); 23 | }); 24 | it('can be unwrapped into a promise', function () { 25 | var p = client.sendMessage(CHAT_ID, 'Promises! ftw!').promise(); 26 | assert(p instanceof Promise); 27 | }); 28 | it('exposes a fake `.then`', function (done) { 29 | client.sendMessage(CHAT_ID, 'Promises! ftw!').then(function (res) { 30 | assert(res.ok); 31 | done(); 32 | }); 33 | }); 34 | it('passes errors down the chain', function (done) { 35 | client 36 | .sendMessage(CHAT_ID) 37 | .delay(99000) 38 | .sendMessage(CHAT_ID, 'I can wait') 39 | .catch(function (err) { 40 | assert(err); 41 | done(); 42 | }); 43 | }); 44 | }); 45 | 46 | describe('Standard Chat Methods', function () { 47 | 48 | describe('#sendMessage(chatId, text[, options])', function () { 49 | this.timeout(10000); 50 | var client = new TelegramBotClient(TOKEN); 51 | it('sends a message', function () { 52 | return client.sendMessage(CHAT_ID, 'Foo Bar!').promise(); 53 | }); 54 | it('rejects on missing arguments', function (done) { 55 | client.sendMessage(CHAT_ID).catch(function (err) { 56 | assert(err); 57 | done(); 58 | }); 59 | }); 60 | }); 61 | 62 | describe('#forwardMessage(chatId, fromChatId, messageId)', function () { 63 | this.timeout(10000); 64 | var client = new TelegramBotClient(TOKEN); 65 | it('forwards sent messages when passing an id', function () { 66 | return client.sendMessage(CHAT_ID, 'Forward Me!').promise().then(function (response) { 67 | return client.forwardMessage(CHAT_ID, response.result.chat.id, response.result.message_id).promise(); 68 | }); 69 | }); 70 | it('rejects on missing params', function (done) { 71 | client.forwardMessage(CHAT_ID).catch(function (err) { 72 | assert(err); 73 | done(); 74 | }); 75 | }); 76 | }); 77 | 78 | describe('#sendPhoto(chatId, photoReference[, options])', function () { 79 | this.timeout(25000); 80 | var client = new TelegramBotClient(TOKEN); 81 | it('sends a photo using a local file', function () { 82 | return client.sendPhoto(CHAT_ID, './test/fixtures/pizzacat.jpg').promise(); 83 | }); 84 | it('sends a photo using a URL', function () { 85 | return client.sendPhoto(CHAT_ID, 'https://upload.wikimedia.org/wikipedia/commons/5/50/01-Magpie.jpg').promise(); 86 | }); 87 | it('resends photos using file identifiers', function () { 88 | return client.sendPhoto(CHAT_ID, 'https://upload.wikimedia.org/wikipedia/commons/thumb/0/04/Cyanocitta-cristata-004.jpg/1280px-Cyanocitta-cristata-004.jpg').promise().then(function (res) { 89 | return client.sendPhoto(CHAT_ID, res.result.photo[0].file_id); 90 | }); 91 | }); 92 | }); 93 | 94 | describe('#sendAudio(chatId, audioReference[, options])', function () { 95 | this.timeout(25000); 96 | var client = new TelegramBotClient(TOKEN); 97 | it('sends audio using a local file', function () { 98 | return client.sendAudio(CHAT_ID, './test/fixtures/bluejay.ogg').promise(); 99 | }); 100 | it('sends audio using a URL', function () { 101 | return client.sendAudio(CHAT_ID, 'https://upload.wikimedia.org/wikipedia/commons/7/72/Cyanocitta_cristata_-_Blue_Jay_-_XC109601.ogg').promise(); 102 | }); 103 | }); 104 | 105 | describe('#sendVoice(chatId, voiceReference[, options])', function () { 106 | this.timeout(25000); 107 | var client = new TelegramBotClient(TOKEN); 108 | it('sends a voice message using a local file', function () { 109 | return client.sendVoice(CHAT_ID, './test/fixtures/bluejay.ogg').promise(); 110 | }); 111 | }); 112 | 113 | describe('#sendDocument(chatId, documentReference[, options])', function () { 114 | this.timeout(25000); 115 | var client = new TelegramBotClient(TOKEN); 116 | it('sends a document using a local file', function () { 117 | return client.sendDocument(CHAT_ID, './test/fixtures/doc.txt').promise(); 118 | }); 119 | }); 120 | 121 | describe('#sendSticker(chatId, stickerReference[, options])', function () { 122 | this.timeout(25000); 123 | var client = new TelegramBotClient(TOKEN); 124 | it('sends a sticker using a local file', function () { 125 | return client.sendSticker(CHAT_ID, './test/fixtures/sticker.webp').promise(); 126 | }); 127 | }); 128 | 129 | describe('#sendLocation(chatId, lat, lon[, options])', function () { 130 | this.timeout(10000); 131 | var client = new TelegramBotClient(TOKEN); 132 | it('sends a location by passing lat and lon', function () { 133 | return client.sendLocation(CHAT_ID, 52.520007, 13.404954).promise(); 134 | }); 135 | it('rejects on missing params', function (done) { 136 | client.sendLocation(CHAT_ID, 52.520007).catch(function (err) { 137 | assert(err); 138 | done(); 139 | }); 140 | }); 141 | }); 142 | 143 | describe('#sendVenue(chatId, lat, lon, title, address[, options])', function () { 144 | this.timeout(10000); 145 | var client = new TelegramBotClient(TOKEN); 146 | it('sends a location by passing lat, lon, title, address', function () { 147 | return client.sendVenue(CHAT_ID, 52.551110, 13.331278, 'Volkspark Rehberge', 'Windhuker Str. 52A, 13351 Berlin').promise(); 148 | }); 149 | }); 150 | 151 | describe('#sendContact(chatId, phoneNumber, firstName[, options])', function () { 152 | this.timeout(10000); 153 | var client = new TelegramBotClient(TOKEN); 154 | it('sends a contact', function () { 155 | return client.sendContact(CHAT_ID, '1-800-I-CAN-SUE', 'John', { last_name: 'Doe' }).promise(); 156 | }); 157 | }); 158 | 159 | 160 | describe('#sendChatAction(chatId, action)', function () { 161 | this.timeout(10000); 162 | var client = new TelegramBotClient(TOKEN); 163 | it('sends chat actions', function () { 164 | return client.sendChatAction(CHAT_ID, 'typing').promise(); 165 | }); 166 | }); 167 | 168 | describe('#getMe()', function () { 169 | this.timeout(10000); 170 | var client = new TelegramBotClient(TOKEN); 171 | it('gets infos about the bot', function () { 172 | return client.getMe().promise().then(function (res) { 173 | assert(res.result.id); 174 | }); 175 | }); 176 | }); 177 | 178 | describe('#getChat(chatId)', function () { 179 | this.timeout(10000); 180 | var client = new TelegramBotClient(TOKEN); 181 | it('gets infos about the bot', function () { 182 | return client.getChat(CHAT_ID).promise().then(function (res) { 183 | assert(res.result.id); 184 | }); 185 | }); 186 | }); 187 | 188 | describe('#getChatAdministrators(chatId)', function () { 189 | this.timeout(10000); 190 | var client = new TelegramBotClient(TOKEN); 191 | it('gets infos about the bot', function () { 192 | // the test chat does not have adiministrators 193 | return client.getChatAdministrators(CHAT_ID).promise().catch(function (err) { 194 | assert(err); 195 | }); 196 | }); 197 | }); 198 | 199 | describe('#getChatMembersCount(chatId)', function () { 200 | this.timeout(10000); 201 | var client = new TelegramBotClient(TOKEN); 202 | it('gets infos about the bot', function () { 203 | return client.getChatMembersCount(CHAT_ID).promise().then(function (res) { 204 | assert(res.result); 205 | assert(isFinite(res.result)); 206 | }); 207 | }); 208 | }); 209 | 210 | describe('#getUpdates([options])', function () { 211 | this.timeout(10000); 212 | var client = new TelegramBotClient(TOKEN); 213 | it('gets updates for the bot', function () { 214 | return client.getUpdates().promise().then(function (res) { 215 | assert(typeof res.result.length !== 'undefined'); 216 | }); 217 | }); 218 | }); 219 | 220 | describe('#setWebhook(url)', function () { 221 | this.timeout(10000); 222 | var client = new TelegramBotClient(TOKEN); 223 | it('sets webhook values', function () { 224 | return client.setWebhook('123.123.123.123').promise().then(function (res) { 225 | assert.strictEqual(res.result, true); 226 | }); 227 | }); 228 | it('rejects invalid values', function (done) { 229 | client.setWebhook('this is not a url').catch(function (err) { 230 | assert(err); 231 | done(); 232 | }); 233 | }); 234 | it('unsets webhook values', function () { 235 | return client.setWebhook('').promise().then(function (res) { 236 | assert.strictEqual(res.result, true); 237 | }); 238 | }); 239 | 240 | }); 241 | 242 | }); 243 | 244 | describe('Message Editing', function () { 245 | describe('#editMessageText([chat_id, ]identifier, text[, options])', function () { 246 | this.timeout(10000); 247 | var client = new TelegramBotClient(TOKEN); 248 | it('edits a sent message\'s text', function () { 249 | return client.sendMessage(CHAT_ID, 'This message should not contain tpyos').promise().then(function (response) { 250 | return client.editMessageText(CHAT_ID, response.result.message_id, 'This message should not contain typos').promise().then(function (response) { 251 | assert(response.result.text.indexOf('typo') > -1); 252 | }); 253 | }); 254 | }); 255 | }); 256 | describe('#editMessageCaption([chat_id, ]identifier[, options])', function () { 257 | this.timeout(10000); 258 | var client = new TelegramBotClient(TOKEN); 259 | it('edits a sent message\'s caption', function () { 260 | return client.sendPhoto(CHAT_ID, './test/fixtures/pizzacat.jpg', { caption: 'Pizza Cat?' }).promise().then(function (response) { 261 | return client.editMessageCaption(CHAT_ID, response.result.message_id, { caption: 'Pizza Cat!' }).promise().then(function (response) { 262 | assert(response.result.caption.indexOf('!') > -1); 263 | }); 264 | }); 265 | }); 266 | }); 267 | }); 268 | 269 | }); 270 | -------------------------------------------------------------------------------- /src/apiclient.js: -------------------------------------------------------------------------------- 1 | var request = require('superagent'); 2 | var util = require('./util'); 3 | var isUrl = require('is-url'); 4 | var fs = require('fs'); 5 | var tempfile = require('tempfile'); 6 | var format = require('fr'); 7 | 8 | var endpoint = 'https://api.telegram.org/bot{0}/{1}'; 9 | 10 | function is (type) { 11 | return function (el) { 12 | return Object.prototype.toString.call(el) === '[object ' + type + ']'; 13 | }; 14 | } 15 | 16 | function ApiClient (token) { 17 | 18 | function _post (method, payload, options) { 19 | return new Promise(function (resolve, reject) { 20 | request 21 | .post(format(endpoint, token, method)) 22 | .type('form') 23 | .send(payload || {}) 24 | .send(options || {}) 25 | .end(function (err, res) { 26 | if (err) { 27 | return reject(err); 28 | } else if (res && res.ok) { 29 | return resolve(res.body); 30 | } 31 | reject(new Error( 32 | (res && res.body && res.body.description) || 33 | 'Unknown error performing POST request' 34 | )); 35 | }); 36 | }); 37 | } 38 | 39 | function _get (method, payload, options) { 40 | return new Promise(function (resolve, reject) { 41 | request 42 | .get(format(endpoint, token, method)) 43 | .send(payload || {}) 44 | .send(options || {}) 45 | .end(function (err, res) { 46 | if (err) { 47 | return reject(err); 48 | } else if (res && res.ok) { 49 | return resolve(res.body); 50 | } 51 | reject(new Error( 52 | (res && res.body && res.body.description) || 53 | 'Unknown error performing GET request' 54 | )); 55 | }); 56 | }); 57 | } 58 | 59 | function _fetchMedia (mediaLocation) { 60 | return new Promise(function (resolve, reject) { 61 | request 62 | .get(mediaLocation) 63 | .end(function (err, res) { 64 | if (err) { 65 | reject(err); 66 | return; 67 | } 68 | 69 | if (res && res.ok) { 70 | var tmpPath = tempfile(res.header['content-type'].split('/')[1]); 71 | return fs.writeFile(tmpPath, res.body, function (err) { 72 | if (err) { 73 | reject(err); 74 | } else { 75 | resolve(tmpPath); 76 | } 77 | }); 78 | } 79 | reject(new Error( 80 | (res && res.body && res.body.description) || 81 | 'Unknown error performing retrieving requested media' 82 | )); 83 | }); 84 | }); 85 | } 86 | 87 | function _postMedia (type, payload, options, apiMethod) { 88 | return new Promise(function (resolve, reject) { 89 | if (!apiMethod) { 90 | apiMethod = format( 91 | '{0}{1}{2}', 92 | 'send', 93 | type[0].toUpperCase(), 94 | type.substr(1, type.length -1) 95 | ); 96 | } 97 | 98 | var r = request 99 | .post(format(endpoint, token, apiMethod)) 100 | .field('chat_id', payload.chat_id); 101 | 102 | var mediaData = isUrl(payload.media) 103 | ? _fetchMedia(payload.media).then(function (data) { 104 | return [data, true]; 105 | }) 106 | : Promise.resolve([payload.media, false]) 107 | 108 | if (options) { 109 | Object.keys(options).forEach(function (key) { 110 | r.field(key, options[key]); 111 | }); 112 | } 113 | 114 | mediaData.then(function (data) { 115 | var media = data[0]; 116 | var unlink = data[1]; 117 | 118 | if (util.isFileId(data)) { 119 | r.field(type, media); 120 | } else { 121 | r.attach(type, media); 122 | } 123 | 124 | r.end(function (err, res) { 125 | if (unlink) { 126 | fs.unlink(media, Function.prototype); 127 | } 128 | if (err) { 129 | return reject(err); 130 | } else if (res.ok) { 131 | return resolve(res.body); 132 | } 133 | reject(new Error( 134 | (res && res.body && res.body.description) || 135 | 'Unknown error performing POST request' 136 | )); 137 | }); 138 | }, reject); 139 | }); 140 | 141 | } 142 | 143 | this.sendMessage = function (chatId, message, options) { 144 | var payload = { 145 | chat_id: chatId, 146 | text: message 147 | }; 148 | return _post('sendMessage', payload, options); 149 | }; 150 | 151 | this.sendChatAction = function (chatId, action) { 152 | var payload = { 153 | chat_id: chatId, 154 | action: action 155 | }; 156 | return _post('sendChatAction', payload); 157 | }; 158 | 159 | this.forwardMessage = function (chatId, fromChatId, messageId) { 160 | var payload = { 161 | chat_id: chatId, 162 | from_chat_id: fromChatId, 163 | message_id: messageId 164 | }; 165 | return _post('forwardMessage', payload); 166 | }; 167 | 168 | this.sendLocation = function (chatId, lat, lon, options) { 169 | var payload = { 170 | chat_id: chatId, 171 | latitude: lat, 172 | longitude: lon 173 | }; 174 | return _post('sendLocation', payload, options); 175 | }; 176 | 177 | this.sendVenue = function (chatId, lat, lon, title, address, options) { 178 | var payload = { 179 | chat_id: chatId, 180 | latitude: lat, 181 | longitude: lon, 182 | title: title, 183 | address: address 184 | }; 185 | return _post('sendVenue', payload, options); 186 | }; 187 | 188 | this.sendPhoto = function (chatId, photo, options) { 189 | var payload = { 190 | chat_id: chatId, 191 | media: photo 192 | }; 193 | return _postMedia('photo', payload, options); 194 | }; 195 | 196 | this.sendAudio = function (chatId, audio, options) { 197 | var payload = { 198 | chat_id: chatId, 199 | media: audio 200 | }; 201 | return _postMedia('audio', payload, options); 202 | }; 203 | 204 | this.sendVoice = function (chatId, voice, options) { 205 | var payload = { 206 | chat_id: chatId, 207 | media: voice 208 | }; 209 | return _postMedia('voice', payload, options); 210 | }; 211 | 212 | this.sendSticker = function (chatId, sticker, options) { 213 | var payload = { 214 | chat_id: chatId, 215 | media: sticker 216 | }; 217 | return _postMedia('sticker', payload, options); 218 | }; 219 | 220 | this.sendDocument = function (chatId, doc, options) { 221 | var payload = { 222 | chat_id: chatId, 223 | media: doc 224 | }; 225 | return _postMedia('document', payload, options); 226 | }; 227 | 228 | this.sendVideo = function (chatId, video, options) { 229 | var payload = { 230 | chat_id: chatId, 231 | media: video 232 | }; 233 | return _postMedia('video', payload, options); 234 | }; 235 | 236 | this.sendContact = function (chatId, phoneNumber, firstName, options) { 237 | var payload = { 238 | chat_id: chatId, 239 | phone_number: phoneNumber, 240 | first_name: firstName 241 | }; 242 | return _post('sendContact', payload, options); 243 | }; 244 | 245 | this.getUserProfilePhotos = function (userId, options) { 246 | var payload = { 247 | user_id: userId 248 | }; 249 | return _get('getUserProfilePhotos', payload, options); 250 | }; 251 | 252 | this.getMe = function () { 253 | return _get('getMe', {}); 254 | }; 255 | 256 | this.getChat = function (chatId) { 257 | var payload = { 258 | chat_id: chatId 259 | }; 260 | return _get('getChat', payload); 261 | }; 262 | 263 | this.getChatAdministrators = function (chatId) { 264 | var payload = { 265 | chat_id: chatId 266 | }; 267 | return _get('getChatAdministrators', payload); 268 | }; 269 | 270 | this.getChatMembersCount = function (chatId) { 271 | var payload = { 272 | chat_id: chatId 273 | }; 274 | return _get('getChatMembersCount', payload); 275 | }; 276 | 277 | this.getChatMember = function (chatId, userId) { 278 | var payload = { 279 | chat_id: chatId, 280 | user_id: userId 281 | }; 282 | return _get('getChatMember', payload); 283 | }; 284 | 285 | this.leaveChat = function (chatId) { 286 | var payload = { 287 | chat_id: chatId 288 | }; 289 | return _get('leaveChat', payload); 290 | }; 291 | 292 | this.kickChatMember = function (chatId, userId) { 293 | var payload = { 294 | chat_id: chatId, 295 | user_id: userId 296 | }; 297 | return _post('kickChatMember', payload); 298 | }; 299 | 300 | this.unbanChatMember = function (chatId, userId) { 301 | var payload = { 302 | chat_id: chatId, 303 | user_id: userId 304 | }; 305 | return _post('unbanChatMember', payload); 306 | }; 307 | 308 | this.answerCallbackQuery = function (callbackQueryId, options) { 309 | var payload = { 310 | callback_query_id: callbackQueryId 311 | }; 312 | return _post('answerCallbackQuery', payload, options); 313 | }; 314 | 315 | this.editMessageText = function (/*[chatId,] identifier, text[, options]*/) { 316 | var args = [].slice.call(arguments); 317 | var payload = {}, options; 318 | if (args.length < 4 && is('String')(args[0]) && is('String')(args[1])) { 319 | payload.inline_message_id = args[0]; 320 | payload.text = args[1]; 321 | options = args[2]; 322 | 323 | } else if (args.length > 2 && (!args.slice(0, 3).some(is('Object')))) { 324 | payload.chat_id = args[0]; 325 | payload.message_id = args[1]; 326 | payload.text = args[2]; 327 | options = args[3]; 328 | } else { 329 | return Promise.reject(new Error('Could not handle passed arguments')); 330 | } 331 | return _post('editMessageText', payload, options); 332 | }; 333 | 334 | this.editMessageCaption = function (/*[chatId,] identifier[, options]*/) { 335 | var args = [].slice.call(arguments); 336 | var payload = {}, options; 337 | if (args.length === 2 && args.every(is('String'))) { 338 | payload.inline_message_id = args[0]; 339 | options = args[1]; 340 | } else if (args.length > 1 && (!args.slice(0, 2).some(is('Object')))) { 341 | payload.chat_id = args[0]; 342 | payload.message_id = args[1]; 343 | options = args[2]; 344 | } else { 345 | return Promise.reject(new Error('Could not handle passed arguments')); 346 | } 347 | return _post('editMessageCaption', payload, options); 348 | }; 349 | 350 | this.editMessageReplyMarkup = function (/*[chatId,] identifier[, options]*/) { 351 | var args = [].slice.call(arguments); 352 | var payload = {}, options; 353 | if (args.length === 2 && args.every(is('String'))) { 354 | payload.inline_message_id = args[0]; 355 | options = args[1]; 356 | } else if (args.length > 1 && (!args.slice(0, 2).some(is('Object')))) { 357 | payload.chat_id = args[0]; 358 | payload.message_id = args[1]; 359 | options = args[2]; 360 | } else { 361 | return Promise.reject(new Error('Could not handle passed arguments')); 362 | } 363 | return _post('editMessageReplyMarkup', payload, options); 364 | }; 365 | 366 | this.answerInlineQuery = function (inlineQueryId, results, options) { 367 | var payload = { 368 | inline_query_id: inlineQueryId, 369 | results: results 370 | }; 371 | return _post('answerInlineQuery', payload, options); 372 | }; 373 | 374 | this.setWebhook = function (url) { 375 | var payload = { 376 | url: url 377 | }; 378 | return _post('setWebhook', payload); 379 | }; 380 | 381 | this.getUpdates = function (options) { 382 | return _get('getUpdates', null, options); 383 | }; 384 | 385 | } 386 | 387 | module.exports = ApiClient; 388 | --------------------------------------------------------------------------------