├── LICENSE ├── bin └── index.js ├── bower.json ├── examples ├── 1sServer.js ├── MiElPotato.jpg ├── channelPost.js ├── financeExchange.js ├── inlineQuery.js ├── lightServer.js ├── msg.json ├── proxy.js ├── sendMediaGroup.js ├── server.js ├── template.js ├── virtual.js └── yield.js ├── index.js ├── package.json ├── readme.md ├── src ├── api │ ├── index.js │ ├── proto.js │ └── request.js ├── cli.js ├── errors.js ├── keyboard │ ├── compile.js │ └── index.js ├── parseCmd.js ├── server │ ├── createBot.js │ ├── createHTTP.js │ ├── createPolling.js │ ├── createVirtual.js │ ├── index.js │ ├── onMsg │ │ ├── index.js │ │ ├── onIncomingCallbackQuery.js │ │ ├── onIncomingChosenInlineResult.js │ │ ├── onIncomingInlineQuery.js │ │ ├── onIncomingMessage.js │ │ ├── onIncomingPreCheckoutQuery.js │ │ ├── onIncomingShippingQuery.js │ │ └── runAction.js │ └── responseBuilder.js └── util.js └── test ├── data ├── MiElPotato.jpg └── msg.json ├── errors.js ├── index.js ├── responseBuilder.js ├── srvHTTP.js ├── srvPolling.js └── srvVirtual.js /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2014 Daeren Torn 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | 'Software'), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 19 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 20 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 21 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 22 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /bin/index.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | require("./../src/cli"); -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telegram-bot-api-c", 3 | "main": "index.js", 4 | "version": "19.5.0", 5 | "description": "Lightweight, Simple, Fastest module for Telegram Bot without Dependencies", 6 | "keywords": [ 7 | "node", 8 | "bot", 9 | "api", 10 | "cli", 11 | "telegram", 12 | "telegram bot", 13 | "telegram bot api", 14 | "telegram-bot-api-c", 15 | "bottality" 16 | ], 17 | "authors": [ 18 | "daeren" 19 | ], 20 | "license": "MIT", 21 | "homepage": "666.io", 22 | "repository": { 23 | "type": "git", 24 | "url": "git://github.com/Daeren/telegram-bot-api-c.git" 25 | }, 26 | "ignore": [ 27 | "**/.*", 28 | "node_modules", 29 | "bower_components" 30 | ] 31 | } 32 | -------------------------------------------------------------------------------- /examples/1sServer.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rChai = require("chai"); 15 | 16 | const expect = rChai.expect; 17 | 18 | const rBot = require("./../index"); 19 | 20 | //----------------------------------------------------- 21 | 22 | const objBot = rBot(process.env.TELEGRAM_BOT_TOKEN); 23 | const objOptions = { 24 | "limit": 100, 25 | "timeout": 0, 26 | "interval": 1 27 | }; 28 | 29 | let objSrv; 30 | 31 | //---------]> 32 | 33 | objSrv = objBot 34 | .polling(objOptions, onNotFound) 35 | .logger(function cbLogger(error, data) { 36 | //console.log(error, data); 37 | 38 | if(error) { 39 | expect(error).to.be.an.instanceof(Error); 40 | expect(data).to.be.null; 41 | } 42 | else { 43 | expect(error).to.be.null; 44 | expect(data).to.be.a("object"); 45 | 46 | expect(data).to.have.property("ok"); 47 | 48 | if(data.ok) { 49 | expect(data).to.have.property("result"); 50 | } 51 | } 52 | }); 53 | 54 | //-----[TEST]-----}> 55 | 56 | expect(objSrv).to.be.a("object"); 57 | expect(objSrv).to.have.property("start").that.is.a("function"); 58 | expect(objSrv).to.have.property("stop").that.is.a("function"); 59 | 60 | //-----[PLUGIN]-----}> 61 | 62 | objSrv 63 | .use(function(bot, data, next) { 64 | expect(next).to.be.a("function"); 65 | 66 | //----------]> 67 | 68 | tCheckBaseBotFields(bot); 69 | 70 | //----------]> 71 | 72 | console.log("Async"); 73 | 74 | if(data === "room") { 75 | next("room.menu"); 76 | } 77 | else { 78 | next(); 79 | } 80 | }) 81 | .use(function(bot) { 82 | tCheckBaseBotFields(bot); 83 | 84 | //----------]> 85 | 86 | console.log("Sync"); 87 | 88 | //return "room.menu"; 89 | }); 90 | 91 | //-----[Filter by `type`]-----}> 92 | 93 | objSrv 94 | .use("text", function(bot, data, next) { 95 | tCheckBaseBotFields(bot); 96 | 97 | //----------]> 98 | 99 | console.log("F:Async | Type: text"); 100 | 101 | next(); 102 | }); 103 | 104 | objSrv 105 | .use(function(bot) { 106 | tCheckBaseBotFields(bot); 107 | 108 | //----------]> 109 | 110 | console.log("Before[use:type] Sync"); 111 | }); 112 | 113 | objSrv 114 | .use("text", function() { 115 | console.log("F:Sync | Type: text"); 116 | }) 117 | .use("photo", function() { 118 | console.log("F:Sync | Type: photo"); 119 | }); 120 | 121 | //-----[EVENTS]-----}> 122 | 123 | [ 124 | "/start", "/", 125 | "enterChat", "leftChat", 126 | 127 | "chatTitle", "chatNewPhoto", "chatDeletePhoto", "chatCreated", 128 | 129 | "text", "photo", "audio", "document", "sticker", "video", "voice", "contact", "location", 130 | 131 | /^empty/i 132 | ] 133 | .forEach(function(type) { 134 | objSrv.on(type, function(bot, cmdParams) { 135 | response(type, bot, cmdParams); 136 | }); 137 | }); 138 | 139 | objSrv.on("/stop", cbCmdStop); 140 | 141 | objSrv.on("text:room.menu", onTextRoom); 142 | 143 | objSrv 144 | .on(/^hello/i, ["type", "id"], onTextRegExp) 145 | .on(/^(id)\s+(\d+)/i, "type id", onTextRegExp) 146 | .on(/^(login)\s+(\w+)/i, ["type", "login"], onTextRegExp); 147 | 148 | 149 | //------]> 150 | 151 | function onNotFound(bot) { 152 | const cmd = bot.command; 153 | 154 | if(cmd) { 155 | expect(cmd).to.be.a("object"); 156 | 157 | expect(cmd).to.have.property("name"); 158 | expect(cmd).to.have.property("text"); 159 | expect(cmd).to.have.property("cmd"); 160 | } 161 | 162 | //----------]> 163 | 164 | response("onNotFound", bot, cmd); 165 | } 166 | 167 | function cbCmdStop(bot, cmdParams) { 168 | response("cbCmdStop", bot, cmdParams); 169 | 170 | objSrv.stop(); 171 | } 172 | 173 | function onTextRoom(bot, params) { 174 | response("onTextRoom:", bot, params); 175 | } 176 | 177 | function onTextRegExp(bot, reParams) { 178 | response("onTextRegExp:", bot, reParams); 179 | } 180 | 181 | //---)> 182 | 183 | function response(who, bot, params) { 184 | tCheckBaseBotFields(bot); 185 | 186 | //----------]> 187 | 188 | console.log("[!]", who, " => "); 189 | console.log("|bot: ", bot); 190 | console.log("|isGroup: %s", bot.isGroup); 191 | console.log("|params: ", params); 192 | console.log("+-----------------------|"); 193 | 194 | bot 195 | .answer() 196 | .text(params && params.id ? "" : bot) 197 | .keyboard(bot.message.text) 198 | .send().then(console.info, console.error); 199 | } 200 | 201 | //-------------]> 202 | 203 | function tCheckBaseBotFields(bot) { 204 | expect(bot).to.be.an.instanceof(objSrv.constructor); 205 | expect(bot).to.be.a("object"); 206 | expect(bot).to.have.property("message").that.is.an("object"); 207 | 208 | //----------]> 209 | 210 | const msg = bot.message; 211 | 212 | expect(msg).to.have.property("message_id"); 213 | expect(msg).to.have.property("from").that.is.an("object"); 214 | expect(msg).to.have.property("chat").that.is.an("object"); 215 | expect(msg).to.have.property("date"); 216 | 217 | expect(bot).to.have.property("isGroup").that.is.a("boolean"); 218 | expect(bot).to.have.property("isReply").that.is.a("boolean"); 219 | 220 | expect(bot).to.have.property("cid").that.equal(msg.chat.id); 221 | expect(bot).to.have.property("mid").that.equal(msg.message_id); 222 | 223 | //----------]> 224 | 225 | expect(bot).to.have.property("answer").that.is.a("function"); 226 | expect(bot).to.have.property("render").that.is.a("function"); 227 | } -------------------------------------------------------------------------------- /examples/MiElPotato.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daeren/telegram-bot-api-c/a32d50f554ccb9185af3d4e48ef770692182ef37/examples/MiElPotato.jpg -------------------------------------------------------------------------------- /examples/channelPost.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rTgBot = require("./../index"); 15 | 16 | const gBot = rTgBot(process.env.TELEGRAM_BOT_TOKEN); 17 | 18 | //----------------]> 19 | 20 | gBot.enable("tgUrlUpload"); 21 | 22 | //----------------]> 23 | 24 | gBot 25 | .polling() 26 | 27 | .use("channelPost", function(bot, data, next) { 28 | bot.answer().text("use:channelPost").send(() => next()); 29 | }) 30 | .use("editedChannelPost", function(bot, data, next) { 31 | bot.answer().text("use:editedChannelPost").send(() => next()); 32 | }) 33 | .use("photo", function(bot, data, next) { 34 | bot.answer().text("use:photo").send(() => next()); 35 | }) 36 | 37 | 38 | .on("text", function(bot, url) { 39 | bot.answer().photo(url).send(); 40 | }) 41 | 42 | 43 | .on("message editedMessage channelPost editedChannelPost photo", function(bot) { 44 | bot 45 | .answer() 46 | .text(bot.updateType + " | " + bot.updateSubType + " | " + bot.eventType + " | " + bot.eventSubType) 47 | .send(); 48 | }) 49 | 50 | 51 | //.on("message", function(bot) { 52 | // bot.answer().text("message").send(); 53 | //}) 54 | //.on("editedMessage", function(bot) { 55 | // bot.answer().text("editedMessage").send(); 56 | //}) 57 | //.on("channelPost", function(bot) { 58 | // bot.answer().text("channelPost").send(); 59 | //}) 60 | //.on("editedChannelPost", function(bot) { 61 | // bot.answer().text("editedChannelPost").send(); 62 | //}) 63 | .on("photo", function(bot) { 64 | bot.answer().text("photo").send(); 65 | }); -------------------------------------------------------------------------------- /examples/financeExchange.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rTgBot = require("./../index"), 15 | rUnirest = require("unirest"); 16 | 17 | const gBot = rTgBot(process.env.TELEGRAM_BOT_TOKEN); 18 | 19 | //----------------]> 20 | 21 | const gStringTable = { 22 | "texts": { 23 | "start": "Hi, {userName}!", 24 | "notFound": "Not found :/" 25 | }, 26 | 27 | "buttons": { 28 | "currency": "USDRUB EURRUB USDRUB,EURRUB" 29 | } 30 | }; 31 | 32 | //----------------]> 33 | 34 | gBot 35 | .polling() 36 | 37 | .on("/start", function(bot) { 38 | const input = { 39 | "userName": bot.message.from.first_name 40 | }; 41 | 42 | bot 43 | .answer() 44 | .text(gStringTable.texts.start) 45 | .render(input) 46 | .keyboard(gStringTable.buttons.currency, "resize") 47 | .send(); 48 | }) 49 | 50 | .on("text", function(bot, currency) { 51 | const url = 52 | "https://query.yahooapis.com/v1/public/yql?q=select+*+from+yahoo.finance.xchange+where+pair+=+'" + 53 | currency + 54 | "'&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys"; 55 | 56 | //----------]> 57 | 58 | rUnirest.get(url).as.json(onEnd); 59 | 60 | //----------]> 61 | 62 | function onEnd(response) { 63 | const answer = bot.answer(), 64 | query = response.body.query; 65 | 66 | let rate = query.results ? query.results.rate : undefined; 67 | let text = ""; 68 | 69 | //------]> 70 | 71 | rate = query.count === 1 ? [rate] : rate; 72 | rate = rate ? rate.filter(r => r.Name !== "N/A") : rate; 73 | 74 | if(!rate || !rate.length) { 75 | answer.text(gStringTable.texts.notFound).send(); 76 | return; 77 | } 78 | 79 | //------]> 80 | 81 | rate.forEach(function(r) { 82 | if(text) { 83 | text += "\n"; 84 | } 85 | 86 | text += `${r.Name}: ${r.Rate} => Ask: ${r.Ask} | Bid: ${r.Bid}`; 87 | }); 88 | 89 | //------]> 90 | 91 | answer.text(text).send(); 92 | } 93 | }); -------------------------------------------------------------------------------- /examples/inlineQuery.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rTgBot = require("./../index"); 15 | 16 | const gBot = rTgBot(process.env.TELEGRAM_BOT_TOKEN); 17 | 18 | //----------------]> 19 | 20 | gBot 21 | .polling() 22 | .catch(function(error) { 23 | console.log(error); 24 | }) 25 | 26 | .on("text", function(bot, data) { 27 | console.log(bot); 28 | 29 | bot.api.getChatMember({"chat_id": bot.cid, "user_id": bot.from.id}, function(error, result) { 30 | bot.answer().text(JSON.stringify(result)).send(); 31 | }); 32 | 33 | bot 34 | .answer() 35 | .text(data) 36 | .inlineKeyboard("1 2 3") 37 | .send() 38 | .then(console.info, console.error); 39 | }) 40 | 41 | .on("chosenInlineResult", function() { 42 | }) 43 | 44 | .on("callbackQuery", function(bot, query) { 45 | bot 46 | .answer() 47 | .callbackQuery(query.data === "2" ? "test" : "") 48 | .text("Hello") 49 | .send() 50 | .then(console.info, console.error); 51 | }) 52 | 53 | .on("inlineQuery", function(bot, data) { 54 | const idx = Date.now().toString(32) + Math.random().toString(24); 55 | 56 | let results = [ 57 | { 58 | "type": "article", 59 | "title": "Title #1", 60 | "message_text": "Text...", 61 | 62 | "thumb_url": "https://pp.vk.me/c627530/v627530230/2fce2/PF9loxF4ick.jpg" 63 | }, 64 | 65 | { 66 | "type": "article", 67 | "title": "Title #2: " + data.query, 68 | "message_text": "Text...yeah" 69 | }, 70 | 71 | { 72 | "type": "photo", 73 | 74 | "photo_width": 128, 75 | "photo_height": 128, 76 | 77 | "photo_url": "https://pp.vk.me/c627530/v627530230/2fce2/PF9loxF4ick.jpg", 78 | "thumb_url": "https://pp.vk.me/c627530/v627530230/2fce2/PF9loxF4ick.jpg" 79 | } 80 | ] 81 | .map((t, i) => { t.id = idx + i; return t; }); 82 | 83 | /* 84 | results = { 85 | "results": results 86 | }; 87 | */ 88 | 89 | bot 90 | .answer() 91 | .inlineQuery(results) 92 | .send() 93 | .then(console.info, console.error); 94 | }); -------------------------------------------------------------------------------- /examples/lightServer.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rChai = require("chai"); 15 | 16 | const expect = rChai.expect; 17 | 18 | const rBot = require("./../index"); 19 | 20 | //----------------------------------------------------- 21 | 22 | const objBot = rBot(process.env.TELEGRAM_BOT_TOKEN); 23 | const objSrvOptions = { 24 | "certDir": "/www/site", 25 | 26 | "key": "/3_site.xx.key", 27 | "cert": "/2_site.xx.crt", 28 | "ca": [ 29 | "/AddTrustExternalCARoot.crt", 30 | "/COMODORSAAddTrustCA.crt", 31 | "/COMODORSADomainValidationSecureServerCA.crt" 32 | ], 33 | 34 | "host": "site.xx" 35 | }; 36 | 37 | //------------------]> 38 | 39 | objBot 40 | .api 41 | .setWebhook({"url": "https://site.xx/botX"}) 42 | 43 | .then(isOk => { 44 | expect(isOk).to.be.a("boolean"); 45 | 46 | if(!isOk) { 47 | throw new Error("Oops...problems with webhook..."); 48 | } 49 | 50 | objBot 51 | .http(objSrvOptions, cbMsg) 52 | .on("/start", cbCmdStart); 53 | }, function(error) { 54 | expect(error).to.be.an.instanceof(Error); 55 | 56 | console.error(error); 57 | }); 58 | 59 | //------------------]> 60 | 61 | function cbMsg(bot) { 62 | const cmd = bot.command; 63 | 64 | if(cmd) { 65 | expect(cmd).to.be.a("object"); 66 | 67 | expect(cmd).to.have.property("name"); 68 | expect(cmd).to.have.property("text"); 69 | expect(cmd).to.have.property("cmd"); 70 | } 71 | 72 | //----------]> 73 | 74 | tCheckBaseBotFields(bot); 75 | 76 | //----------]> 77 | 78 | let commands = { 79 | "help": x => bot.answer().text(x).send() 80 | }; 81 | 82 | let cmdFunc, 83 | cmdParams = bot.parseCmd(bot.message.text); 84 | 85 | if(cmdParams) { 86 | (cmdFunc = commands[cmdParams.name]) ? cmdFunc(cmdParams) : console.log(cmdParams); 87 | } 88 | 89 | //--------------]> 90 | 91 | bot.answer().text("Hell Word!").send(); 92 | } 93 | 94 | function cbCmdStart(bot, params) { 95 | tCheckBaseBotFields(bot); 96 | 97 | expect(params).to.be.an("object"); 98 | 99 | //----------]> 100 | 101 | bot.answer().text("CMD: /start").send(); 102 | } 103 | 104 | //-------------]> 105 | 106 | function tCheckBaseBotFields(bot) { 107 | expect(bot).to.be.a("object"); 108 | expect(bot).to.have.property("message").that.is.an("object"); 109 | 110 | //----------]> 111 | 112 | const msg = bot.message; 113 | 114 | expect(msg).to.have.property("message_id"); 115 | expect(msg).to.have.property("from").that.is.an("object"); 116 | expect(msg).to.have.property("chat").that.is.an("object"); 117 | expect(msg).to.have.property("date"); 118 | 119 | expect(bot).to.have.property("isGroup").that.is.an("boolean"); 120 | expect(bot).to.have.property("isReply").that.is.an("boolean"); 121 | 122 | expect(bot).to.have.property("cid").that.equal(msg.chat.id); 123 | expect(bot).to.have.property("mid").that.equal(msg.message_id); 124 | 125 | //----------]> 126 | 127 | expect(bot).to.have.property("answer").that.is.an("function"); 128 | } -------------------------------------------------------------------------------- /examples/msg.json: -------------------------------------------------------------------------------- 1 | {"chat_id": 59725308, "text": "Hi"} -------------------------------------------------------------------------------- /examples/proxy.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rBot = require("./../index"); 15 | 16 | //----------------------------------------------------- 17 | 18 | const gBot = rBot(process.env.TELEGRAM_BOT_TOKEN); 19 | 20 | const gProxyStr = "207.99.118.74:8080", 21 | gProxyArr = gProxyStr.split(":"), 22 | gProxyObj = { 23 | "host": gProxyStr.split(":")[0], 24 | "port": gProxyStr.split(":")[1] 25 | }; 26 | 27 | //----------------------------------------------------- 28 | 29 | gBot.proxy(gProxyObj); 30 | 31 | getMe(() => { 32 | gBot.proxy(gProxyStr); 33 | 34 | getMe(() => { 35 | gBot.proxy(gProxyArr); 36 | 37 | getMe(() => { 38 | gBot.proxy(); 39 | 40 | getMe(() => { 41 | rBot.callJson({ 42 | "token": process.env.TELEGRAM_BOT_TOKEN, 43 | "method": "getMe", 44 | "proxy": gProxyStr 45 | }, (e, data) => { 46 | console.log(e || data); 47 | 48 | rBot.callJson(process.env.TELEGRAM_BOT_TOKEN, "getMe", (e, data) => console.log(e || data), gProxyObj); 49 | }); 50 | }); 51 | }); 52 | }); 53 | }); 54 | 55 | //----------------------------------------------------- 56 | 57 | function getMe(callback) { 58 | gBot.api.getMe(function(error, data) { 59 | console.log(error, data); 60 | 61 | if(error) { 62 | console.log(error.data && error.data.toString()); 63 | } 64 | 65 | if(callback) { 66 | callback(); 67 | } 68 | }); 69 | } -------------------------------------------------------------------------------- /examples/sendMediaGroup.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rBot = require("./../index"); 15 | 16 | //----------------------------------------------------- 17 | 18 | const token = process.env.TELEGRAM_BOT_TOKEN, 19 | chatId = process.env.TELEGRAM_CHAT_ID; 20 | 21 | const bot = rBot(token); 22 | 23 | //----------------------------------------------------- 24 | 25 | bot.enable("tgUrlUpload"); 26 | 27 | bot.api.sendMediaGroup({ 28 | "chat_id": chatId, 29 | "media": [ 30 | { 31 | "type": "photo", 32 | "media": "https://www.google.ru/images/logos/ps_logo2.png" 33 | }, 34 | { // recommended 35 | "type": "photo", 36 | "media": "AgADAgAD1qcxG2_R8AbjPe6-AjgFdozGWSoABAE2Gi-3QnhSD7wBAAEC" 37 | } 38 | ] 39 | }, function(error, data) { 40 | console.log(error, data); 41 | }); -------------------------------------------------------------------------------- /examples/server.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rChai = require("chai"); 15 | 16 | const expect = rChai.expect; 17 | 18 | const rBot = require("./../index"); 19 | 20 | //----------------------------------------------------- 21 | 22 | const objSrvOptions = { 23 | "certDir": "/www/site", 24 | 25 | "key": "/3_site.xx.key", 26 | "cert": "/2_site.xx.crt", 27 | "ca": [ 28 | "/AddTrustExternalCARoot.crt", 29 | "/COMODORSAAddTrustCA.crt", 30 | "/COMODORSADomainValidationSecureServerCA.crt" 31 | ], 32 | 33 | "host": "site.xx" 34 | }; 35 | 36 | //------------------]> 37 | 38 | const objBot = rBot(); 39 | 40 | const objMyBot = rBot(process.env.TG_BOT_TOKEN_MY), 41 | objOtherBot = rBot(process.env.TG_BOT_TOKEN_OTHER); 42 | 43 | const objSrv = objBot.http(objSrvOptions); 44 | 45 | //-----------]> 46 | 47 | objSrv 48 | .bot(objMyBot, "/myBot") 49 | 50 | .on("/start", cbCmdStart) 51 | .on("/stop", cbCmdStop); 52 | 53 | objSrv 54 | .bot(objOtherBot, "/myOtherBot", cbMsg); 55 | 56 | //-----------]> 57 | 58 | function cbMsg(bot) { 59 | const cmd = bot.command; 60 | 61 | if(cmd) { 62 | expect(cmd).to.be.a("object"); 63 | 64 | expect(cmd).to.have.property("name"); 65 | expect(cmd).to.have.property("text"); 66 | expect(cmd).to.have.property("cmd"); 67 | } 68 | 69 | //----------]> 70 | 71 | tCheckBaseBotFields(bot); 72 | 73 | //----------------]> 74 | 75 | bot.api 76 | .getMe() 77 | .then(() => { 78 | return bot.answer().chatAction("typing").send(); 79 | }) 80 | .then(() => { 81 | return bot.answer().text("Use: /start").send(); 82 | }) 83 | .then(() => { 84 | return bot.answer().photo("https://www.google.ru/images/logos/ps_logo2.png").send(); 85 | }) 86 | .then(() => { 87 | return bot.answer().text("Forward: ok").send(); 88 | }) 89 | .then(console.log, console.error); 90 | } 91 | 92 | //--------)> 93 | 94 | function cbCmdStart(bot, params) { 95 | tCheckBaseBotFields(bot); 96 | 97 | expect(params).to.be.an("object"); 98 | 99 | //----------]> 100 | 101 | bot.answer().text("Hello").send().then(console.log, console.error); 102 | } 103 | 104 | function cbCmdStop(bot, params) { 105 | tCheckBaseBotFields(bot); 106 | 107 | //----------]> 108 | 109 | bot 110 | .answer() 111 | .text(params) 112 | .photo(__dirname + "/MiElPotato.jpg").caption("#2EASY") 113 | .send(); 114 | } 115 | 116 | //-------------]> 117 | 118 | function tCheckBaseBotFields(bot) { 119 | expect(bot).to.be.an("object"); 120 | expect(bot).to.have.property("message").that.is.an("object"); 121 | 122 | //----------]> 123 | 124 | const msg = bot.message; 125 | 126 | expect(msg).to.have.property("message_id"); 127 | expect(msg).to.have.property("from").that.is.an("object"); 128 | expect(msg).to.have.property("chat").that.is.an("object"); 129 | expect(msg).to.have.property("date"); 130 | 131 | expect(bot).to.have.property("isGroup").that.is.an("boolean"); 132 | expect(bot).to.have.property("isReply").that.is.an("boolean"); 133 | 134 | expect(bot).to.have.property("cid").that.equal(msg.chat.id); 135 | expect(bot).to.have.property("mid").that.equal(msg.message_id); 136 | 137 | //----------]> 138 | 139 | expect(bot).to.have.property("answer").that.is.an("function"); 140 | expect(bot).to.have.property("render").that.is.an("function"); 141 | } -------------------------------------------------------------------------------- /examples/template.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rChai = require("chai"); 15 | 16 | const expect = rChai.expect; 17 | 18 | const rBot = require("./../index"); 19 | 20 | //----------------------------------------------------- 21 | 22 | const objBot = rBot(process.env.TELEGRAM_BOT_TOKEN); 23 | 24 | //---------]> 25 | 26 | objBot 27 | .engine(require("ejs")) 28 | .promise(require("bluebird")) 29 | 30 | .polling(onNotFound) 31 | 32 | .use(function(bot, data, next) { 33 | expect(next).to.be.a("function"); 34 | 35 | //----------]> 36 | 37 | tCheckBaseBotFields(bot); 38 | 39 | //-----[DEFAULT]-----}> 40 | 41 | bot.engine(null); 42 | 43 | data = ["H", "i"]; 44 | bot.answer().text(bot.render("Array | Text: {0} + {1}", data)).send(); 45 | 46 | data = {"x": "H", "y": "i"}; 47 | bot.answer().text(bot.render("Hashtable | Text: {x} + {y}", data)).send(); 48 | 49 | //-----[EJS]-----}> 50 | 51 | bot.engine(require("ejs")); 52 | 53 | data = {"x": "H", "y": "i"}; 54 | 55 | bot 56 | .answer() 57 | .text(bot.render("EJS | Text: <%= x %> + <%= y %>", data)) 58 | .keyboard(bot.keyboard.hGb()) 59 | .send(); 60 | }); 61 | 62 | //------]> 63 | 64 | function onNotFound(bot) { 65 | const cmd = bot.command; 66 | 67 | if(cmd) { 68 | expect(cmd).to.be.a("object"); 69 | 70 | expect(cmd).to.have.property("name"); 71 | expect(cmd).to.have.property("text"); 72 | expect(cmd).to.have.property("cmd"); 73 | } 74 | 75 | //----------]> 76 | 77 | response("onNotFound", bot, cmd); 78 | } 79 | 80 | //---)> 81 | 82 | function response(who, bot, params) { 83 | tCheckBaseBotFields(bot); 84 | 85 | //----------]> 86 | 87 | console.log("[!]", who, " => "); 88 | console.log("|bot: ", bot); 89 | console.log("|params: ", params); 90 | console.log("+-----------------------|"); 91 | 92 | bot.answer().text("X").keyboard(bot.message.text).send().then(console.info, console.error); 93 | } 94 | 95 | //-------------]> 96 | 97 | function tCheckBaseBotFields(bot) { 98 | expect(bot).to.be.a("object"); 99 | expect(bot).to.have.property("message").that.is.an("object"); 100 | 101 | //----------]> 102 | 103 | const msg = bot.message; 104 | 105 | expect(msg).to.have.property("message_id"); 106 | expect(msg).to.have.property("from").that.is.an("object"); 107 | expect(msg).to.have.property("chat").that.is.an("object"); 108 | expect(msg).to.have.property("date"); 109 | 110 | expect(bot).to.have.property("isGroup").that.is.an("boolean"); 111 | expect(bot).to.have.property("isReply").that.is.an("boolean"); 112 | 113 | expect(bot).to.have.property("cid").that.equal(msg.chat.id); 114 | expect(bot).to.have.property("mid").that.equal(msg.message_id); 115 | 116 | //----------]> 117 | 118 | expect(bot).to.have.property("answer").that.is.an("function"); 119 | expect(bot).to.have.property("render").that.is.an("function"); 120 | } -------------------------------------------------------------------------------- /examples/virtual.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rBot = require("./../index"); 15 | 16 | //----------------------------------------------------- 17 | 18 | const objBot = rBot(process.env.TELEGRAM_BOT_TOKEN); 19 | const objSrv = objBot 20 | .virtual(bot => { 21 | bot.answer().text("Not found!").send(); 22 | }) 23 | //.on(/./, console.log) 24 | .on("sticker", bot => { 25 | console.log(bot); 26 | console.log(bot.message.sticker.thumb); 27 | console.log("+------"); 28 | }) 29 | .on("photo", bot => { 30 | console.log(bot); 31 | console.log(bot.message.photo); 32 | console.log("+------"); 33 | }); 34 | 35 | //----------------------------------------------------- 36 | 37 | objBot 38 | .api 39 | .setWebhook({"url": "https://site.xx/dev-bot"}) 40 | .then(isOk => { 41 | if(!isOk) { 42 | throw new Error("Oops...problems with webhook..."); 43 | } 44 | 45 | //-------]> 46 | 47 | const rExpress = require("express"), 48 | rBodyParser = require("body-parser"); 49 | 50 | rExpress() 51 | .use(rBodyParser.json()) 52 | .post("/dev-bot", objSrv.middleware) 53 | .listen(1490, "localhost"); 54 | }).catch(console.error); 55 | 56 | //-------[Input]-------}> 57 | 58 | setInterval(function() { 59 | objSrv.input(null, { 60 | "update_id": 0, 61 | "message": { 62 | "message_id": 0, 63 | 64 | "from": { 65 | "id": 59725308, "first_name": "Daeren", "username": "io666" 66 | }, 67 | 68 | "chat": { 69 | "id": 59725308, 70 | "first_name": "Daeren", 71 | "username": "io666", 72 | "type": "private" 73 | }, 74 | 75 | "date": 0, 76 | "text": 0 77 | } 78 | }); 79 | }, 2000); 80 | -------------------------------------------------------------------------------- /examples/yield.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | 10 | "use strict"; 11 | 12 | //----------------------------------------------------- 13 | 14 | const rTgBot = require("./../index"); 15 | 16 | const gBot = rTgBot(process.env.TELEGRAM_BOT_TOKEN); 17 | 18 | //----------------]> 19 | 20 | gBot 21 | .polling(function* (bot) { 22 | const result = yield send(bot); 23 | console.info(result); 24 | 25 | //------]> 26 | 27 | //x / 0; 28 | 29 | yield error(); 30 | }) 31 | .catch(function* (error) { 32 | //x / 0; 33 | console.error(error); 34 | yield Promise.resolve(); 35 | }) 36 | 37 | .use(function* () { 38 | yield auth("D", "13"); 39 | }) 40 | .use("text", function* (bot, text) { 41 | yield save(); 42 | 43 | if(text === "key") { 44 | return "eventYield"; 45 | } 46 | }) 47 | 48 | .on("text:eventYield", function* (bot, data) { 49 | console.log("eventYield:", data); 50 | yield Promise.resolve(); 51 | }); 52 | 53 | //----------------]> 54 | 55 | function auth() { 56 | console.log("auth", arguments); 57 | 58 | return new Promise(x => setTimeout(x, 1000)); 59 | } 60 | 61 | function save() { 62 | console.log("save", arguments); 63 | 64 | return new Promise(x => setTimeout(x, 1000)); 65 | } 66 | 67 | function send(bot) { 68 | console.log("send", arguments); 69 | 70 | return bot.sendMessage("Ok, let's go..."); 71 | } 72 | 73 | function error() { 74 | console.log("error", arguments); 75 | 76 | return new Promise((x, z) => setTimeout(z, 1000, new Error("MyError"))); 77 | } -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rFs = require("fs"), 13 | rPath = require("path"); 14 | 15 | const rTgApi = require("./src/api"); 16 | 17 | const rUtil = require("./src/util"), 18 | rErrors = require("./src/errors"), 19 | 20 | rParseCmd = require("./src/parseCmd"), 21 | rKeyboard = require("./src/keyboard"), 22 | 23 | rServer = require("./src/server"); 24 | 25 | //----------------------------------------------------- 26 | 27 | const gTgHostFile = "api.telegram.org"; 28 | 29 | //-----------------------------]> 30 | 31 | main.keyboard = rKeyboard; 32 | main.parseCmd = rParseCmd; 33 | 34 | main.call = rTgApi.callAPI; 35 | main.callJson = rTgApi.callAPIJson; 36 | 37 | //----------------------------------------------------- 38 | 39 | module.exports = rErrors(main); 40 | 41 | if(!module.parent) { 42 | require("./src/cli"); 43 | } 44 | 45 | //----------------------------------------------------- 46 | 47 | function main(token) { 48 | /*jshint validthis:true */ 49 | 50 | function CMain() { 51 | this.mdPromise = Promise; 52 | this.kvCfgStore = new Map(); 53 | 54 | this.keyboard = rKeyboard; 55 | this.parseCmd = rParseCmd; 56 | 57 | this.api = rTgApi.genMethodsFor(this); 58 | } 59 | 60 | CMain.prototype = { 61 | enable(key) { this.kvCfgStore.set(key, true); return this; }, 62 | disable(key) { this.kvCfgStore.delete(key); return this; }, 63 | enabled(key) { return this.kvCfgStore.has(key); }, 64 | disabled(key) { return !this.enabled(key); }, 65 | 66 | engine(t) { this.mdEngine = t; return this; }, 67 | promise(t) { this.mdPromise = t; return this; }, 68 | 69 | call(method, data, callback) { rTgApi.callAPI(token, method, data, callback, this._optProxy, this.enabled("tgUrlUpload")); }, 70 | callJson(method, data, callback) { rTgApi.callAPIJson(token, method, data, callback, this._optProxy, this.enabled("tgUrlUpload")); }, 71 | 72 | polling(params, callback) { return rServer.polling(this, params, callback); }, 73 | http(params, callback) { return rServer.http(this, params, callback); }, 74 | virtual(callback) { return rServer.virtual(this, callback); }, 75 | 76 | "render": mthCMainRender, 77 | "download": mthCMainDownload, 78 | 79 | proxy(t) { 80 | if(!t) { 81 | this._optProxy = null; 82 | return this; 83 | } 84 | 85 | if(typeof(t) === "string") { 86 | t = t.split(":"); 87 | } 88 | 89 | this._optProxy = this._optProxy || {}; 90 | this._optProxy.host = t.host || t[0]; 91 | this._optProxy.port = t.port || t[1]; 92 | 93 | return this; 94 | }, 95 | 96 | token(t) { 97 | if(!arguments.length) { 98 | return token; 99 | } 100 | 101 | token = t; 102 | 103 | return this; 104 | } 105 | }; 106 | 107 | //-------------------------]> 108 | 109 | return rErrors(new CMain()); 110 | 111 | //-----------[Methods]----------}> 112 | 113 | function mthCMainRender(template, data) { 114 | if(!template) { 115 | return ""; 116 | } 117 | 118 | if(!data) { 119 | return template; 120 | } 121 | 122 | //-------------]> 123 | 124 | const mdEngine = this.mdEngine; 125 | 126 | if(mdEngine) { 127 | return mdEngine.render(template, data); 128 | } 129 | 130 | //-------------]> 131 | 132 | if(Array.isArray(data)) { 133 | data.forEach(defaultRender); 134 | } 135 | else if(typeof(data) === "object") { 136 | for(let name in data) { 137 | if(hasOwnProperty.call(data, name)) { 138 | defaultRender(data[name], name); 139 | } 140 | } 141 | } 142 | 143 | //-------------]> 144 | 145 | return template; 146 | 147 | //-------------]> 148 | 149 | function defaultRender(e, i) { 150 | template = template.replace("{" + i + "}", e); 151 | } 152 | } 153 | 154 | function mthCMainDownload(fid, dir, name, callback) { 155 | const self = this; 156 | 157 | //-----]> 158 | 159 | if(typeof(dir) === "function") { 160 | callback = dir; 161 | dir = undefined; 162 | } 163 | else if(typeof(name) === "function") { 164 | callback = name; 165 | name = undefined; 166 | } 167 | 168 | //-------------------------]> 169 | 170 | if(typeof(callback) === "undefined") { 171 | return new this.mdPromise(cbPromise); 172 | } 173 | 174 | cbPromise(); 175 | 176 | //-------------------------]> 177 | 178 | function cbPromise(resolve, reject) { 179 | callback = callback || ((error, result) => error ? reject(error) : resolve(result)); 180 | 181 | //--------]> 182 | 183 | self.api.getFile({"file_id": fid}, function(error, data) { 184 | if(error) { 185 | callback(error, data); 186 | return; 187 | } 188 | 189 | //--------]> 190 | 191 | const fileId = data.file_id, 192 | fileSize = data.file_size, 193 | filePath = data.file_path; 194 | 195 | const url = "https://" + gTgHostFile + "/file/bot" + token +"/" + filePath; 196 | 197 | let fileName = filePath.split("/").pop(); 198 | 199 | //--------]> 200 | 201 | if(name) { 202 | fileName = fileName.match(/\.(.+)$/); 203 | fileName = fileName && fileName[0] || ""; 204 | } else { 205 | name = Date.now(); 206 | } 207 | 208 | //--------]> 209 | 210 | if(typeof(dir) === "undefined" || typeof(name) === "undefined") { 211 | rUtil.createReadStreamByUrl(url, function(error, response) { 212 | if(error) { 213 | callback(error); 214 | } 215 | else { 216 | callback(null, { 217 | "id": fileId, 218 | "size": fileSize, 219 | "file": fileName, 220 | "stream": response 221 | }); 222 | } 223 | }); 224 | 225 | return; 226 | } 227 | 228 | //--------]> 229 | 230 | dir += "/" + name + fileName; 231 | 232 | //----[Write]----}> 233 | 234 | const file = rFs.createWriteStream(rPath.normalize(dir)); 235 | 236 | //--------]> 237 | 238 | file 239 | .on("error", callback) 240 | .on("open", function() { 241 | rUtil.createReadStreamByUrl(url, function(error, response) { 242 | if(error) { 243 | callback(error); 244 | return; 245 | } 246 | 247 | response.pipe(file); 248 | }); 249 | }) 250 | .on("finish", function() { 251 | callback(null, { 252 | "id": fileId, 253 | "size": fileSize, 254 | "file": dir 255 | }); 256 | }); 257 | }); 258 | } 259 | } 260 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telegram-bot-api-c", 3 | "version": "19.5.0", 4 | "description": "Lightweight, Simple, Fastest module for Telegram Bot without Dependencies", 5 | 6 | "homepage": "https://666.io", 7 | "keywords": [ 8 | "telegram", 9 | "bot", 10 | "api", 11 | "cli", 12 | "telegram-bot-api-c", 13 | "bottality" 14 | ], 15 | 16 | "author": { 17 | "name": "Daeren", 18 | "email": "x@db.gg" 19 | }, 20 | 21 | "bin": { 22 | "tg-bot": "./bin/index.js" 23 | }, 24 | "main": "./index", 25 | "scripts": { 26 | "test": "mocha --reporter spec" 27 | }, 28 | 29 | "repository": { 30 | "type": "git", 31 | "url": "git://github.com/Daeren/telegram-bot-api-c.git" 32 | }, 33 | 34 | "license": "MIT", 35 | "engines": { 36 | "node": "> 0.12.0" 37 | }, 38 | 39 | "readmeFilename": "readme.md", 40 | 41 | "bugs": { 42 | "url": "https://github.com/Daeren/telegram-bot-api-c/issues" 43 | } 44 | } -------------------------------------------------------------------------------- /src/api/index.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rFs = require("fs"), 13 | rPath = require("path"), 14 | rUrl = require("url"); 15 | 16 | const rRequest = require("./request"), 17 | rProto = require("./proto"); 18 | 19 | const rUtil = require("./../util"), 20 | rErrors = require("./../errors"); 21 | 22 | //----------------------------------------------------- 23 | 24 | const gCRLF = "\r\n"; 25 | 26 | const gPipeOptions = {"end": false}; 27 | 28 | const gReIsFilePath = /[\\\/\.]/, 29 | gReIsHttpSUri = /^https?:\/\//; 30 | 31 | const gMaxFileSize = 1024 * 1024 * 50, 32 | gBoundaryUInterval = 1000 * 60 * 5; 33 | 34 | let gBoundaryKey, gBoundaryUDate, gHeaderContentType, 35 | gBoundaryDiv, gBoundaryEnd, 36 | gCRLFBoundaryDiv, gCRLFBoundaryEnd; 37 | 38 | //-----------------------]> 39 | 40 | updateBoundary(); 41 | 42 | //----------------------------------------------------- 43 | 44 | module.exports = { 45 | callAPI, 46 | callAPIJson, 47 | 48 | genMethodsFor 49 | }; 50 | 51 | //----------------------------------------------------- 52 | 53 | function callAPI(token, method, data, callback, proxy, useTgUrlUpload) { 54 | if(token && typeof(token) === "object") { 55 | callback = data; 56 | data = method; 57 | 58 | method = token.method; 59 | proxy = token.proxy; 60 | token = token.token; 61 | 62 | useTgUrlUpload = token.tgUrlUpload; 63 | } 64 | 65 | if(callback && typeof(callback) !== "function") { 66 | proxy = callback; 67 | callback = null; 68 | } 69 | 70 | if(typeof(data) === "function") { 71 | callback = data; 72 | data = null; 73 | } 74 | 75 | if(!token) { 76 | throw new Error("callAPI: `token` was not specified."); 77 | } 78 | 79 | if(!method) { 80 | throw new Error("callAPI: `method` was not specified."); 81 | } 82 | 83 | //-------------------------]> 84 | 85 | rRequest(proxy, token, method, callback, onReqInit); 86 | 87 | //-------------------------]> 88 | 89 | function onReqInit(request) { 90 | const dataIsMap = data instanceof Map, 91 | dataIsArray = dataIsMap ? false : Array.isArray(data); 92 | 93 | const dataLen = (dataIsArray ? data.length : 0); 94 | 95 | let isWritten; 96 | 97 | //-------------]> 98 | 99 | if(data) { 100 | rUtil.forEachAsync(rProto.params[method.toLowerCase()], iterReqMthParams, onReqMthEnd); 101 | } 102 | else { 103 | onReqMthEnd(); 104 | } 105 | 106 | //-------------]> 107 | 108 | function iterReqMthParams(next, param, index) { 109 | if(dataIsArray && index >= dataLen) { 110 | next(null, null, true); 111 | return; 112 | } 113 | 114 | //-------]> 115 | 116 | const type = param[0], 117 | field = param[1]; 118 | 119 | const value = dataIsMap ? data.get(field) : dataIsArray ? data[index] : data[field]; 120 | 121 | //-------]> 122 | 123 | if(typeof(value) === "undefined" || value === null) { 124 | next(); 125 | return; 126 | } 127 | 128 | if(!isWritten) { 129 | isWritten = true; 130 | 131 | if(Date.now() - gBoundaryUDate >= gBoundaryUInterval) { 132 | updateBoundary(); 133 | } 134 | 135 | request.setHeader("Content-Type", gHeaderContentType); 136 | } 137 | 138 | //-------]> 139 | 140 | request.write(gCRLFBoundaryDiv); 141 | request.write("Content-Disposition: form-data; name=\""); 142 | request.write(field); 143 | 144 | if(!writeFieldParam(request, type, value, next) && !writeFieldData(request, data, type, value, useTgUrlUpload, next)) { 145 | throw new Error("Type not found!"); 146 | } 147 | } 148 | 149 | function onReqMthEnd(error) { 150 | if(error) { 151 | request.destroy(error); 152 | } 153 | else { 154 | if(isWritten) { 155 | request.write(gCRLFBoundaryEnd); 156 | } 157 | 158 | request.end(); 159 | } 160 | } 161 | } 162 | } 163 | 164 | function callAPIJson(token, method, data, callback, proxy, useTgUrlUpload) { 165 | if(token && typeof(token) === "object") { 166 | callback = data; 167 | data = method; 168 | 169 | method = token.method; 170 | proxy = token.proxy; 171 | token = token.token; 172 | 173 | useTgUrlUpload = token.tgUrlUpload; 174 | } 175 | 176 | if(callback && typeof(callback) !== "function") { 177 | proxy = callback; 178 | callback = null; 179 | } 180 | 181 | if(typeof(data) === "function") { 182 | callback = data; 183 | data = null; 184 | } 185 | 186 | //-----------]> 187 | 188 | callAPI(token, method, data, callback ? cbCallAPI : null, proxy, useTgUrlUpload); 189 | 190 | //-----------]> 191 | 192 | function cbCallAPI(error, result, response) { 193 | if(!error) { 194 | const isJson = response.headers["content-type"] === "application/json"; 195 | 196 | if(isJson) { 197 | try { 198 | result = JSON.parse(result); 199 | } catch(e) { 200 | error = e; 201 | } 202 | } 203 | 204 | if(!isJson || error) { 205 | error = error || new Error("Expected JSON (" + response.statusCode + ")."); 206 | 207 | error.code = rErrors.ERR_FAILED_PARSE_DATA; 208 | error.data = result; 209 | } 210 | } 211 | 212 | if(error) { 213 | result = null; 214 | } 215 | 216 | callback(error, result, response); 217 | } 218 | } 219 | 220 | //--------[Private]--------}> 221 | 222 | function genMethodsFor(bot) { 223 | const result = {}; 224 | 225 | //--------------]> 226 | 227 | rProto.methods.forEach(setMethod); 228 | 229 | //--------------]> 230 | 231 | return result; 232 | 233 | //--------------]> 234 | 235 | function setMethod(method) { 236 | result[method] = mthAPI; 237 | 238 | function mthAPI(data, callback) { 239 | if(typeof(data) === "function") { 240 | callback = data; 241 | data = null; 242 | } 243 | 244 | //-------------------------]> 245 | 246 | if(!callback) { 247 | return new bot.mdPromise(cbPromise); 248 | } 249 | 250 | cbPromise(); 251 | 252 | //-------------------------]> 253 | 254 | function cbPromise(resolve, reject) { 255 | bot.callJson(method, data, function(error, data) { 256 | error = error || genErrorByTgResponse(data); 257 | data = error ? null : data.result; 258 | callback = callback || ((e, r) => error ? reject(e) : resolve(r)); 259 | 260 | callback(error, data); 261 | }); 262 | } 263 | } 264 | } 265 | 266 | function genErrorByTgResponse(data) { 267 | if(data && !data.ok) { 268 | const error = new Error(data.description); 269 | 270 | error.code = data.error_code; 271 | 272 | if(data.parameters) { 273 | error.retryAfter = data.parameters.retry_after; 274 | } 275 | 276 | return error; 277 | } 278 | 279 | return null; 280 | } 281 | } 282 | 283 | //--------[Sys]--------}> 284 | 285 | function updateBoundary() { 286 | gBoundaryUDate = Date.now(); 287 | 288 | gBoundaryKey = Math.random().toString(16) + Math.random().toString(32).toUpperCase() + gBoundaryUDate.toString(); 289 | gBoundaryDiv = "--" + gBoundaryKey + gCRLF; 290 | gBoundaryEnd = "--" + gBoundaryKey + "--" + gCRLF; 291 | 292 | gCRLFBoundaryDiv = gCRLF + gBoundaryDiv; 293 | gCRLFBoundaryEnd = gCRLF + gBoundaryEnd; 294 | 295 | gHeaderContentType = "multipart/form-data; boundary=\"" + gBoundaryKey + "\""; 296 | } 297 | 298 | //---------]> 299 | 300 | function getReadStreamByUrl(url, callback) { 301 | /*jshint -W069 */ 302 | 303 | let redirectCount = 3; 304 | 305 | //--------------]> 306 | 307 | createStream(); 308 | 309 | //--------------]> 310 | 311 | function createStream() { 312 | rUtil.createReadStreamByUrl(url, onResponse); 313 | } 314 | 315 | function onResponse(error, response) { 316 | const statusCode = response ? response.statusCode : 0; 317 | 318 | //--------------]> 319 | 320 | if(!error && (statusCode < 200 || statusCode > 399)) { 321 | error = new Error("statusCode: " + statusCode); 322 | } 323 | 324 | if(error) { 325 | onEnd(error, response); 326 | return; 327 | } 328 | 329 | //--------------]> 330 | 331 | const headers = response.headers || {}; 332 | 333 | const location = headers["location"], 334 | contentLength = headers["content-length"]; 335 | 336 | //---------]> 337 | 338 | if(location && redirectCount) { 339 | const urlParams = rUrl.parse(url), 340 | locParams = rUrl.parse(location); 341 | 342 | for(let name in urlParams) { 343 | if(hasOwnProperty.call(urlParams, name) && !locParams[name]) { 344 | locParams[name] = urlParams[name]; 345 | } 346 | } 347 | 348 | url = rUrl.format(locParams); 349 | 350 | redirectCount--; 351 | response.destroy(); 352 | 353 | createStream(); 354 | } 355 | else { 356 | if(contentLength && contentLength > gMaxFileSize) { 357 | error = new Error("maxSize: " + gMaxFileSize); 358 | } 359 | 360 | onEnd(error, response); 361 | } 362 | } 363 | 364 | function onEnd(error, response) { 365 | if(error) { 366 | if(response) { 367 | response.destroy(); 368 | } 369 | 370 | response = null; 371 | 372 | error.code = rErrors.ERR_BAD_REQUEST; 373 | } 374 | 375 | callback(error, response); 376 | } 377 | } 378 | 379 | //---------]> 380 | 381 | function writeFieldParam(req, type, value, next) { 382 | switch(type) { 383 | case "boolean": 384 | value = value ? "1" : "0"; 385 | 386 | req.write("\"\r\n\r\n"); 387 | break; 388 | 389 | case "string": 390 | value = typeof(value) === "string" || Buffer.isBuffer(value) ? value : (value + ""); 391 | 392 | req.write("\"\r\n\r\n"); 393 | break; 394 | 395 | case "json": 396 | value = typeof(value) === "string" || Buffer.isBuffer(value) ? value : (JSON.stringify(value) || ""); 397 | 398 | req.write("\"\r\nContent-Type: application/json\r\n\r\n"); 399 | break; 400 | 401 | default: 402 | return false; 403 | } 404 | 405 | //------]> 406 | 407 | req.write(value); 408 | 409 | setImmediate(next); 410 | 411 | //------]> 412 | 413 | return true; 414 | } 415 | 416 | function writeFieldData(req, data, type, value, useTgUrlUpload, next) { 417 | const isBuffer = value && Buffer.isBuffer(value); 418 | 419 | //---------]> 420 | 421 | switch(type) { 422 | case "message": 423 | req.write("\"\r\n\r\n"); 424 | 425 | if(!isBuffer && typeof(value) === "object") { 426 | value 427 | .on("error", next) 428 | .on("end", next) 429 | .pipe(req, gPipeOptions); 430 | } 431 | else { 432 | if(!isBuffer && typeof(value) !== "string") { 433 | value += ""; 434 | } 435 | 436 | req.write(value); 437 | setImmediate(next); 438 | } 439 | 440 | break; 441 | 442 | case "photo": 443 | case "audio": 444 | case "document": 445 | case "sticker": 446 | case "video": 447 | case "voice": 448 | case "video_note": 449 | case "certificate": 450 | if(typeof(value) === "string") { 451 | const isUrl = gReIsHttpSUri.test(value); 452 | 453 | //-------]> 454 | 455 | if(type === "voice" || type === "video_note") { 456 | useTgUrlUpload = false; 457 | } 458 | 459 | if(!useTgUrlUpload && isUrl) { 460 | getReadStreamByUrl(value, function(error, src) { 461 | if(error || !src) { 462 | setImmediate(next, error); 463 | } 464 | else { 465 | writeFieldData(req, data, type, src, useTgUrlUpload, next); 466 | } 467 | }); 468 | } 469 | else if(!isUrl && gReIsFilePath.test(value)) { 470 | writeFieldData(req, data, type, rFs.createReadStream(value), useTgUrlUpload, next); 471 | } 472 | else { 473 | req.write("\"\r\n\r\n"); 474 | req.write(value); 475 | 476 | setImmediate(next); 477 | } 478 | } 479 | else { 480 | let filename = data.filename; 481 | 482 | //-------]> 483 | 484 | if(!filename && !isBuffer && typeof(value) === "object") { 485 | if(value.headers) { 486 | const reqPath = value.req.path, 487 | reqCt = value.headers["content-type"]; 488 | 489 | const ext = reqCt ? rPath.extname(rUtil.getFilenameByMime(reqCt)) : ""; 490 | 491 | filename = ext.length > 1 ? (rPath.parse(reqPath).name + ext) : reqPath; 492 | } 493 | else { 494 | filename = value.path || "file"; 495 | } 496 | } 497 | 498 | //-------]> 499 | 500 | req.write("\"; filename=\""); 501 | req.write(rPath.basename(filename)); 502 | req.write("\"\r\nContent-Type: application/octet-stream\r\n\r\n"); 503 | 504 | if(isBuffer) { 505 | req.write(value); 506 | setImmediate(next); 507 | } 508 | else { 509 | value 510 | .on("error", next) 511 | .on("end", next) 512 | .pipe(req, gPipeOptions); 513 | } 514 | } 515 | 516 | break; 517 | 518 | default: 519 | return false; 520 | } 521 | 522 | //---------]> 523 | 524 | return true; 525 | } 526 | -------------------------------------------------------------------------------- /src/api/proto.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const gMethods = [], 13 | gSendMethods = []; 14 | 15 | const gAliasesSendMethods = {}; 16 | 17 | const gProtoTable = { 18 | "forwardMessage": [ 19 | ["string", "chat_id"], 20 | ["string", "from_chat_id"], 21 | ["boolean", "disable_notification"], 22 | ["string", "message_id"] 23 | ], 24 | 25 | 26 | "sendMessage": [ 27 | ["string", "chat_id"], 28 | ["message", "text"], 29 | ["string", "parse_mode"], 30 | ["boolean", "disable_web_page_preview"], 31 | ["boolean", "disable_notification"], 32 | ["string", "reply_to_message_id"], 33 | ["json", "reply_markup"] 34 | ], 35 | "sendPhoto": [ 36 | ["string", "chat_id"], 37 | ["photo", "photo"], 38 | ["string", "caption"], 39 | ["boolean", "disable_notification"], 40 | ["string", "reply_to_message_id"], 41 | ["json", "reply_markup"] 42 | ], 43 | "sendAudio": [ 44 | ["string", "chat_id"], 45 | ["audio", "audio"], 46 | ["string", "caption"], 47 | ["string", "duration"], 48 | ["string", "performer"], 49 | ["string", "title"], 50 | ["boolean", "disable_notification"], 51 | ["string", "reply_to_message_id"], 52 | ["json", "reply_markup"] 53 | ], 54 | "sendDocument": [ 55 | ["string", "chat_id"], 56 | ["document", "document"], 57 | ["string", "caption"], 58 | ["boolean", "disable_notification"], 59 | ["string", "reply_to_message_id"], 60 | ["json", "reply_markup"] 61 | ], 62 | "sendSticker": [ 63 | ["string", "chat_id"], 64 | ["sticker", "sticker"], 65 | ["boolean", "disable_notification"], 66 | ["string", "reply_to_message_id"], 67 | ["json", "reply_markup"] 68 | ], 69 | "sendVideo": [ 70 | ["string", "chat_id"], 71 | ["video", "video"], 72 | ["string", "duration"], 73 | ["string", "width"], 74 | ["string", "height"], 75 | ["string", "caption"], 76 | ["boolean", "disable_notification"], 77 | ["string", "reply_to_message_id"], 78 | ["json", "reply_markup"] 79 | ], 80 | "sendVoice": [ 81 | ["string", "chat_id"], 82 | ["voice", "voice"], 83 | ["string", "caption"], 84 | ["string", "duration"], 85 | ["boolean", "disable_notification"], 86 | ["string", "reply_to_message_id"], 87 | ["json", "reply_markup"] 88 | ], 89 | "sendVideoNote": [ 90 | ["string", "chat_id"], 91 | ["video_note", "video_note"], 92 | ["string", "duration"], 93 | ["string", "length"], 94 | ["boolean", "disable_notification"], 95 | ["string", "reply_to_message_id"], 96 | ["json", "reply_markup"] 97 | ], 98 | "sendMediaGroup": [ 99 | ["string", "chat_id"], 100 | ["json", "media"], 101 | ["boolean", "disable_notification"], 102 | ["string", "reply_to_message_id"] 103 | ], 104 | "sendLocation": [ 105 | ["string", "chat_id"], 106 | ["string", "latitude"], 107 | ["string", "longitude"], 108 | ["string", "live_period"], 109 | ["boolean", "disable_notification"], 110 | ["string", "reply_to_message_id"], 111 | ["json", "reply_markup"] 112 | ], 113 | "sendVenue": [ 114 | ["string", "chat_id"], 115 | ["string", "latitude"], 116 | ["string", "longitude"], 117 | ["string", "title"], 118 | ["string", "address"], 119 | ["string", "foursquare_id"], 120 | ["boolean", "disable_notification"], 121 | ["string", "reply_to_message_id"], 122 | ["json", "reply_markup"] 123 | ], 124 | "sendContact": [ 125 | ["string", "chat_id"], 126 | ["string", "phone_number"], 127 | ["string", "first_name"], 128 | ["string", "last_name"], 129 | ["boolean", "disable_notification"], 130 | ["string", "reply_to_message_id"], 131 | ["json", "reply_markup"] 132 | ], 133 | "sendChatAction": [ 134 | ["string", "chat_id"], 135 | ["string", "action"] 136 | ], 137 | "sendGame": [ 138 | ["string", "chat_id"], 139 | ["string", "game_short_name"], 140 | ["boolean", "disable_notification"], 141 | ["string", "reply_to_message_id"], 142 | ["json", "reply_markup"] 143 | ], 144 | "sendInvoice": [ 145 | ["string", "chat_id"], 146 | ["string", "title"], 147 | ["string", "description"], 148 | ["string", "payload"], 149 | ["string", "provider_token"], 150 | ["string", "start_parameter"], 151 | ["string", "currency"], 152 | ["json", "prices"], 153 | ["string", "provider_data"], 154 | ["string", "photo_url"], 155 | ["string", "photo_size"], 156 | ["string", "photo_width"], 157 | ["string", "photo_height"], 158 | ["boolean", "need_name"], 159 | ["boolean", "need_phone_number"], 160 | ["boolean", "need_email"], 161 | ["boolean", "need_shipping_address"], 162 | ["boolean", "is_flexible"], 163 | ["boolean", "disable_notification"], 164 | ["string", "reply_to_message_id"], 165 | ["json", "reply_markup"] 166 | ], 167 | 168 | 169 | "getChat": [ 170 | ["string", "chat_id"] 171 | ], 172 | "getChatAdministrators": [ 173 | ["string", "chat_id"] 174 | ], 175 | "getChatMembersCount": [ 176 | ["string", "chat_id"] 177 | ], 178 | "getChatMember": [ 179 | ["string", "chat_id"], 180 | ["string", "user_id"] 181 | ], 182 | 183 | "getUserProfilePhotos": [ 184 | ["string", "user_id"], 185 | ["string", "offset"], 186 | ["string", "limit"] 187 | ], 188 | "getUpdates": [ 189 | ["string", "offset"], 190 | ["string", "limit"], 191 | ["string", "timeout"], 192 | ["json", "allowed_updates"] 193 | ], 194 | "getFile": [ 195 | ["string", "file_id"] 196 | ], 197 | "getMe": null, 198 | "getWebhookInfo": null, 199 | "getGameHighScores": [ 200 | ["string", "user_id"], 201 | ["string", "chat_id"], 202 | ["string", "message_id"], 203 | ["string", "inline_message_id"] 204 | ], 205 | "getStickerSet": [ 206 | ["string", "name"] 207 | ], 208 | 209 | 210 | "setWebhook": [ 211 | ["string", "url"], 212 | ["certificate", "certificate"], 213 | ["string", "max_connections"], 214 | ["json", "allowed_updates"] 215 | ], 216 | "setGameScore": [ 217 | ["string", "user_id"], 218 | ["string", "score"], 219 | ["boolean", "force"], 220 | ["boolean", "disable_edit_message"], 221 | ["string", "chat_id"], 222 | ["string", "message_id"], 223 | ["string", "inline_message_id"] 224 | ], 225 | 226 | 227 | "answerInlineQuery": [ 228 | ["string", "inline_query_id"], 229 | ["json", "results"], 230 | ["string", "cache_time"], 231 | ["boolean", "is_personal"], 232 | ["string", "next_offset"], 233 | ["string", "switch_pm_text"], 234 | ["string", "switch_pm_parameter"] 235 | ], 236 | "answerCallbackQuery": [ 237 | ["string", "callback_query_id"], 238 | ["string", "text"], 239 | ["boolean", "show_alert"], 240 | ["string", "url"], 241 | ["string", "cache_time"] 242 | ], 243 | "answerShippingQuery": [ 244 | ["string", "shipping_query_id"], 245 | ["boolean", "ok"], 246 | ["json", "shipping_options"], 247 | ["string", "error_message"] 248 | ], 249 | "answerPreCheckoutQuery": [ 250 | ["string", "pre_checkout_query_id"], 251 | ["boolean", "ok"], 252 | ["string", "error_message"] 253 | ], 254 | 255 | 256 | "leaveChat": [ 257 | ["string", "chat_id"] 258 | ], 259 | 260 | "kickChatMember": [ 261 | ["string", "chat_id"], 262 | ["string", "user_id"], 263 | ["string", "until_date"] 264 | ], 265 | "unbanChatMember": [ 266 | ["string", "chat_id"], 267 | ["string", "user_id"] 268 | ], 269 | "restrictChatMember": [ 270 | ["string", "chat_id"], 271 | ["string", "user_id"], 272 | ["string", "until_date"], 273 | ["boolean", "can_send_messages"], 274 | ["boolean", "can_send_media_messages"], 275 | ["boolean", "can_send_other_messages"], 276 | ["boolean", "can_add_web_page_previews"] 277 | ], 278 | "promoteChatMember": [ 279 | ["string", "chat_id"], 280 | ["string", "user_id"], 281 | ["boolean", "can_change_info"], 282 | ["boolean", "can_post_messages"], 283 | ["boolean", "can_edit_messages"], 284 | ["boolean", "can_delete_messages"], 285 | ["boolean", "can_invite_users"], 286 | ["boolean", "can_restrict_members"], 287 | ["boolean", "can_pin_messages"], 288 | ["boolean", "can_promote_members"] 289 | ], 290 | 291 | 292 | "exportChatInviteLink": [ 293 | ["string", "chat_id"] 294 | ], 295 | 296 | 297 | "uploadStickerFile": [ 298 | ["string", "user_id"], 299 | ["sticker", "png_sticker"] 300 | ], 301 | "createNewStickerSet": [ 302 | ["string", "user_id"], 303 | ["string", "name"], 304 | ["string", "title"], 305 | ["sticker", "png_sticker"], 306 | ["string", "emojis"], 307 | ["boolean", "contains_masks"], 308 | ["json", "mask_position"] 309 | ], 310 | "addStickerToSet": [ 311 | ["string", "user_id"], 312 | ["string", "name"], 313 | ["sticker", "png_sticker"], 314 | ["string", "emojis"], 315 | ["json", "mask_position"] 316 | ], 317 | "setStickerPositionInSet": [ 318 | ["string", "sticker"], 319 | ["string", "position"] 320 | ], 321 | "deleteStickerFromSet": [ 322 | ["string", "sticker"] 323 | ], 324 | 325 | 326 | "setChatStickerSet": [ 327 | ["string", "chat_id"], 328 | ["string", "sticker_set_name"] 329 | ], 330 | "deleteChatStickerSet": [ 331 | ["string", "chat_id"] 332 | ], 333 | 334 | "setChatPhoto": [ 335 | ["string", "chat_id"], 336 | ["photo", "photo"] 337 | ], 338 | "deleteChatPhoto": [ 339 | ["string", "chat_id"] 340 | ], 341 | 342 | "setChatTitle": [ 343 | ["string", "chat_id"], 344 | ["string", "title"] 345 | ], 346 | "setChatDescription": [ 347 | ["string", "chat_id"], 348 | ["string", "description"] 349 | ], 350 | 351 | "pinChatMessage": [ 352 | ["string", "chat_id"], 353 | ["string", "message_id"], 354 | ["boolean", "disable_notification"] 355 | ], 356 | "unpinChatMessage": [ 357 | ["string", "chat_id"] 358 | ], 359 | 360 | 361 | "editMessageText": [ 362 | ["string", "chat_id"], 363 | ["string", "message_id"], 364 | ["string", "inline_message_id"], 365 | ["string", "text"], 366 | ["string", "parse_mode"], 367 | ["boolean", "disable_web_page_preview"], 368 | ["json", "reply_markup"] 369 | ], 370 | "editMessageCaption": [ 371 | ["string", "chat_id"], 372 | ["string", "message_id"], 373 | ["string", "inline_message_id"], 374 | ["string", "caption"], 375 | ["json", "reply_markup"] 376 | ], 377 | "editMessageReplyMarkup": [ 378 | ["string", "chat_id"], 379 | ["string", "message_id"], 380 | ["string", "inline_message_id"], 381 | ["json", "reply_markup"] 382 | ], 383 | 384 | 385 | "editMessageLiveLocation": [ 386 | ["string", "chat_id"], 387 | ["string", "message_id"], 388 | ["string", "inline_message_id"], 389 | ["string", "latitude"], 390 | ["string", "longitude"], 391 | ["json", "reply_markup"] 392 | ], 393 | "stopMessageLiveLocation": [ 394 | ["string", "chat_id"], 395 | ["string", "message_id"], 396 | ["string", "inline_message_id"], 397 | ["json", "reply_markup"] 398 | ], 399 | 400 | 401 | "deleteMessage": [ 402 | ["string", "chat_id"], 403 | ["string", "message_id"] 404 | ], 405 | "deleteWebhook": null 406 | }; 407 | 408 | const gArgsTable = { 409 | "forwardMessage": [ 410 | "chat_id", 411 | "from_chat_id", 412 | "message_id", 413 | "disable_notification" 414 | ], 415 | 416 | 417 | "sendMessage": [ 418 | "chat_id", 419 | "text", 420 | "parse_mode", 421 | "disable_web_page_preview", 422 | "disable_notification", 423 | "reply_to_message_id", 424 | "reply_markup" 425 | ], 426 | "sendPhoto": [ 427 | "chat_id", 428 | "photo", 429 | "caption", 430 | "disable_notification", 431 | "reply_to_message_id", 432 | "reply_markup" 433 | ], 434 | "sendAudio": [ 435 | "chat_id", 436 | "audio", 437 | "performer", 438 | "title", 439 | "duration", 440 | "caption", 441 | "disable_notification", 442 | "reply_to_message_id", 443 | "reply_markup" 444 | ], 445 | "sendDocument": [ 446 | "chat_id", 447 | "document", 448 | "caption", 449 | "disable_notification", 450 | "reply_to_message_id", 451 | "reply_markup" 452 | ], 453 | "sendSticker": [ 454 | "chat_id", 455 | "sticker", 456 | "disable_notification", 457 | "reply_to_message_id", 458 | "reply_markup" 459 | ], 460 | "sendVideo": [ 461 | "chat_id", 462 | "video", 463 | "width", 464 | "height", 465 | "duration", 466 | "caption", 467 | "disable_notification", 468 | "reply_to_message_id", 469 | "reply_markup" 470 | ], 471 | "sendVoice": [ 472 | "chat_id", 473 | "voice", 474 | "duration", 475 | "caption", 476 | "disable_notification", 477 | "reply_to_message_id", 478 | "reply_markup" 479 | ], 480 | "sendVideoNote": [ 481 | "chat_id", 482 | "video_note", 483 | "duration", 484 | "length", 485 | "disable_notification", 486 | "reply_to_message_id", 487 | "reply_markup" 488 | ], 489 | "sendMediaGroup": [ 490 | "chat_id", 491 | "media", 492 | "disable_notification", 493 | "reply_to_message_id" 494 | ], 495 | "sendLocation": [ 496 | "chat_id", 497 | "latitude", 498 | "longitude", 499 | "live_period", 500 | "disable_notification", 501 | "reply_to_message_id", 502 | "reply_markup" 503 | ], 504 | "sendVenue": [ 505 | "chat_id", 506 | "latitude", 507 | "longitude", 508 | "title", 509 | "address", 510 | "foursquare_id", 511 | "disable_notification", 512 | "reply_to_message_id", 513 | "reply_markup" 514 | ], 515 | "sendContact": [ 516 | "chat_id", 517 | "phone_number", 518 | "first_name", 519 | "last_name", 520 | "disable_notification", 521 | "reply_to_message_id", 522 | "reply_markup" 523 | ], 524 | "sendChatAction": [ 525 | "chat_id", 526 | "action" 527 | ], 528 | "sendGame": [ 529 | "chat_id", 530 | "game_short_name", 531 | "disable_notification", 532 | "reply_to_message_id", 533 | "reply_markup" 534 | ], 535 | "sendInvoice": [ 536 | "chat_id", 537 | "title", 538 | "description", 539 | "payload", 540 | "provider_token", 541 | "start_parameter", 542 | "currency", 543 | "prices", 544 | "provider_data", 545 | "photo_url", 546 | "photo_size", 547 | "photo_width", 548 | "photo_height", 549 | "need_name", 550 | "need_phone_number", 551 | "need_email", 552 | "need_shipping_address", 553 | "is_flexible", 554 | "disable_notification", 555 | "reply_to_message_id", 556 | "reply_markup" 557 | ], 558 | 559 | 560 | "getChat": [ 561 | "chat_id" 562 | ], 563 | "getChatAdministrators": [ 564 | "chat_id" 565 | ], 566 | "getChatMembersCount": [ 567 | "chat_id" 568 | ], 569 | "getChatMember": [ 570 | "chat_id", 571 | "user_id" 572 | ], 573 | 574 | "getUserProfilePhotos": [ 575 | "user_id", 576 | "offset", 577 | "limit" 578 | ], 579 | 580 | "getUpdates": [ 581 | "offset", 582 | "limit", 583 | "timeout", 584 | "allowed_updates" 585 | ], 586 | 587 | "getFile": [ 588 | "file_id" 589 | ], 590 | 591 | "getGameHighScores": [ 592 | "user_id", 593 | "chat_id", 594 | "message_id", 595 | "inline_message_id" 596 | ], 597 | 598 | 599 | "setWebhook": [ 600 | "url", 601 | "certificate", 602 | "max_connections", 603 | "allowed_updates" 604 | ], 605 | "setGameScore": [ 606 | "user_id", 607 | "score", 608 | "force", 609 | "disable_edit_message", 610 | "chat_id", 611 | "message_id", 612 | "inline_message_id" 613 | ], 614 | 615 | 616 | "answerInlineQuery": [ 617 | "inline_query_id", 618 | "results", 619 | "next_offset", 620 | "is_personal", 621 | "cache_time", 622 | "switch_pm_text", 623 | "switch_pm_parameter" 624 | ], 625 | "answerCallbackQuery": [ 626 | "callback_query_id", 627 | "text", 628 | "show_alert", 629 | "url", 630 | "cache_time" 631 | ], 632 | "answerShippingQuery": [ 633 | "shipping_query_id", 634 | "ok", 635 | "shipping_options", 636 | "error_message" 637 | ], 638 | "answerPreCheckoutQuery": [ 639 | "pre_checkout_query_id", 640 | "ok", 641 | "error_message" 642 | ], 643 | 644 | 645 | "leaveChat": [ 646 | "chat_id" 647 | ], 648 | 649 | "kickChatMember": [ 650 | "chat_id", 651 | "user_id" 652 | ], 653 | "unbanChatMember": [ 654 | "chat_id", 655 | "user_id" 656 | ], 657 | "restrictChatMember": [ 658 | ["chat_id"], 659 | ["user_id"], 660 | ["until_date"], 661 | ["can_send_messages"], 662 | ["can_send_media_messages"], 663 | ["can_send_other_messages"], 664 | ["can_add_web_page_previews"] 665 | ], 666 | "promoteChatMember": [ 667 | ["chat_id"], 668 | ["user_id"], 669 | ["can_change_info"], 670 | ["can_post_messages"], 671 | ["can_edit_messages"], 672 | ["can_delete_messages"], 673 | ["can_invite_users"], 674 | ["can_restrict_members"], 675 | ["can_pin_messages"], 676 | ["can_promote_members"] 677 | ], 678 | 679 | 680 | "exportChatInviteLink": [ 681 | ["chat_id"] 682 | ], 683 | 684 | 685 | "uploadStickerFile": [ 686 | ["user_id"], 687 | ["png_sticker"] 688 | ], 689 | "createNewStickerSet": [ 690 | ["user_id"], 691 | ["name"], 692 | ["title"], 693 | ["png_sticker"], 694 | ["emojis"], 695 | ["contains_masks"], 696 | ["mask_position"] 697 | ], 698 | "addStickerToSet": [ 699 | ["user_id"], 700 | ["name"], 701 | ["png_sticker"], 702 | ["emojis"], 703 | ["mask_position"] 704 | ], 705 | "setStickerPositionInSet": [ 706 | ["sticker"], 707 | ["position"] 708 | ], 709 | "deleteStickerFromSet": [ 710 | ["sticker"] 711 | ], 712 | 713 | 714 | "setChatStickerSet": [ 715 | "chat_id", 716 | "sticker_set_name" 717 | ], 718 | "deleteChatStickerSet": [ 719 | "chat_id" 720 | ], 721 | 722 | "setChatPhoto": [ 723 | ["chat_id"], 724 | ["photo"] 725 | ], 726 | "deleteChatPhoto": [ 727 | ["chat_id"] 728 | ], 729 | 730 | "setChatTitle": [ 731 | ["chat_id"], 732 | ["title"] 733 | ], 734 | "setChatDescription": [ 735 | ["chat_id"], 736 | ["description"] 737 | ], 738 | 739 | "pinChatMessage": [ 740 | ["chat_id"], 741 | ["message_id"], 742 | ["disable_notification"] 743 | ], 744 | "unpinChatMessage": [ 745 | ["chat_id"] 746 | ], 747 | 748 | 749 | "editMessageText": [ 750 | "chat_id", 751 | "text", 752 | "message_id", 753 | "inline_message_id", 754 | "parse_mode", 755 | "disable_web_page_preview", 756 | "reply_markup" 757 | ], 758 | "editMessageCaption": [ 759 | "chat_id", 760 | "caption", 761 | "message_id", 762 | "inline_message_id", 763 | "reply_markup" 764 | ], 765 | "editMessageReplyMarkup": [ 766 | "chat_id", 767 | "reply_markup", 768 | "message_id", 769 | "inline_message_id" 770 | ], 771 | 772 | 773 | "editMessageLiveLocation": [ 774 | "chat_id", 775 | "message_id", 776 | "inline_message_id", 777 | "latitude", 778 | "longitude", 779 | "reply_markup" 780 | ], 781 | "stopMessageLiveLocation": [ 782 | "chat_id", 783 | "message_id", 784 | "inline_message_id", 785 | "reply_markup" 786 | ], 787 | 788 | 789 | "deleteMessage": [ 790 | "chat_id", 791 | "message_id" 792 | ] 793 | }; 794 | 795 | //----------------------------------------------------- 796 | 797 | for(let name in gProtoTable) { 798 | if(!gProtoTable.hasOwnProperty(name)) { 799 | continue; 800 | } 801 | 802 | //----------]> 803 | 804 | const sendMethodMatch = name.match(/^send(.+)/); 805 | 806 | if(sendMethodMatch) { 807 | const shortName = sendMethodMatch[1][0].toLowerCase() + sendMethodMatch[1].substr(1), 808 | alias = getAliasByShortMethod(shortName); 809 | 810 | //----]> 811 | 812 | gSendMethods.push(name); 813 | 814 | gAliasesSendMethods[alias] = name; 815 | } 816 | 817 | //----------]> 818 | 819 | gMethods.push(name); 820 | gProtoTable[name.toLowerCase()] = gProtoTable[name]; 821 | 822 | //----------]> 823 | 824 | delete gProtoTable[name]; 825 | } 826 | 827 | //----------------------------------------------------- 828 | 829 | module.exports = { 830 | //------[HELPERS]------}> 831 | 832 | "methods": gMethods, 833 | 834 | "sendMethods": gSendMethods, 835 | "aliasesSendMethods": gAliasesSendMethods, 836 | 837 | //------[PROTO]------}> 838 | 839 | "params": gProtoTable, 840 | "args": gArgsTable, 841 | 842 | //------[METHODS]------}> 843 | 844 | genAliasesSendMethodsFor 845 | }; 846 | 847 | //----------------------------------------------------- 848 | 849 | function genAliasesSendMethodsFor(iter) { 850 | const aliases = gAliasesSendMethods; 851 | 852 | //--------]> 853 | 854 | for(let alias in aliases) { 855 | if(hasOwnProperty.call(aliases, alias)) { 856 | iter(alias, aliases[alias]); 857 | } 858 | } 859 | } 860 | 861 | function getAliasByShortMethod(shortMethod) { 862 | switch(shortMethod) { 863 | case "message": return "text"; 864 | 865 | default: return shortMethod; 866 | } 867 | } -------------------------------------------------------------------------------- /src/api/request.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rHttp = require("http"), 13 | rHttps = require("https"); 14 | 15 | const rErrors = require("./../errors"); 16 | 17 | //----------------------------------------------------- 18 | 19 | const gKeepAliveAgentHTTP = new rHttp.Agent({"keepAlive": true}), 20 | gKeepAliveAgentHTTPS = new rHttps.Agent({"keepAlive": true}); 21 | 22 | const gReqTimeout = 1000 * 60 * 2, 23 | 24 | gReqOptions = { 25 | "path": null, 26 | "method": "POST", 27 | 28 | "host": "api.telegram.org", 29 | "port": 443, 30 | 31 | "agent": gKeepAliveAgentHTTPS 32 | }, 33 | 34 | gReqProxyTunOptions = { 35 | "host": null, 36 | "port": null, 37 | 38 | "method": "CONNECT", 39 | "path": "api.telegram.org:443", 40 | 41 | "agent": gKeepAliveAgentHTTP 42 | }, 43 | gReqProxyOptions = { 44 | "path": null, 45 | "method": "POST", 46 | 47 | "host": "api.telegram.org", 48 | "port": 443, 49 | 50 | "agent": false 51 | }; 52 | 53 | //----------------------------------------------------- 54 | 55 | module.exports = main; 56 | 57 | //----------------------------------------------------- 58 | 59 | function main(proxy, token, method, callback, onInit) { 60 | const path = "/bot" + token + "/" + method; 61 | 62 | //--------------]> 63 | 64 | if(proxy) { 65 | if(typeof(proxy) === "string") { 66 | proxy = proxy.split(":"); 67 | } 68 | 69 | gReqProxyTunOptions.host = proxy.host || proxy[0]; 70 | gReqProxyTunOptions.port = proxy.port || proxy[1]; 71 | 72 | //-------]> 73 | 74 | buildTunRequest(gReqProxyTunOptions, function(response, socket) { 75 | const statusCode = response.statusCode; 76 | 77 | if(statusCode === 200) { 78 | gReqProxyOptions.path = path; 79 | gReqProxyOptions.socket = socket; 80 | 81 | onInit(buildHTTPSRequest(gReqProxyOptions)); 82 | } 83 | else { 84 | const e = new Error("Proxy | connect.statusCode: " + statusCode); 85 | e.code = rErrors.ERR_BAD_PROXY; 86 | 87 | socket 88 | .on("error", onError) 89 | .destroy(e); 90 | } 91 | }); 92 | } 93 | else { 94 | gReqOptions.path = path; 95 | 96 | onInit(buildHTTPSRequest(gReqOptions)); 97 | } 98 | 99 | //--------------]> 100 | 101 | function buildTunRequest(opt, onConnect) { 102 | return (callback ? rHttp.request(opt).on("error", onError) : rHttp.request(opt)).on("connect", onConnect).setTimeout(gReqTimeout, onTimeout).end(); 103 | } 104 | 105 | function buildHTTPSRequest(opt) { 106 | return (callback ? rHttps.request(opt, onResponse).on("error", onError) : rHttps.request(opt)).setTimeout(gReqTimeout, onTimeout); 107 | } 108 | 109 | //-------)> 110 | 111 | function onResponse(response) { 112 | let firstChunk, chunks; 113 | 114 | //--------]> 115 | 116 | response 117 | .on("error", onError) 118 | .on("data", onResponseData) 119 | .on("end", onResponseEnd); 120 | 121 | //--------]> 122 | 123 | function onResponseData(chunk) { 124 | if(!firstChunk) { 125 | firstChunk = chunk; 126 | } 127 | else { 128 | chunks = chunks || [firstChunk]; 129 | chunks.push(chunk); 130 | } 131 | } 132 | 133 | function onResponseEnd() { 134 | callback(null, chunks ? Buffer.concat(chunks) : firstChunk, response); 135 | } 136 | } 137 | 138 | function onError(error) { 139 | if(error.code !== rErrors.ERR_BAD_PROXY) { 140 | error.code = rErrors.ERR_BAD_REQUEST; 141 | } 142 | 143 | callback(error, null, null); 144 | } 145 | 146 | function onTimeout() { 147 | this.destroy(new Error("Timeout.")); 148 | } 149 | } -------------------------------------------------------------------------------- /src/cli.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rBot = require("./../index"); 13 | 14 | //----------------------------------------------------- 15 | 16 | const gCRLF = "\r\n", 17 | 18 | gReCmdNameFull = /^--(\w+)/, 19 | gReCmdNameShort = /^-(\w+)/; 20 | 21 | const gBufLineEnd = new Buffer(gCRLF); 22 | 23 | 24 | const gToken = process.env.TELEGRAM_BOT_TOKEN, 25 | gMethod = process.env.TELEGRAM_BOT_METHOD, 26 | gProxy = process.env.TELEGRAM_BOT_PROXY; 27 | 28 | const gBot = rBot() 29 | 30 | //-----------------]> 31 | 32 | let gOptions, gParams; 33 | 34 | //----------------------------------------------------- 35 | 36 | initBotCmd(); 37 | 38 | //-----------------]> 39 | 40 | if(gOptions.size > 0 || gParams.size > 0) { 41 | callBotMethod(gOptions, gParams); 42 | } 43 | else { 44 | let chunks = []; 45 | 46 | //----)> 47 | 48 | const onEnd = function() { 49 | request(); 50 | }; 51 | 52 | const onReadable = function() { 53 | if(onReadable.lock) { 54 | return; 55 | } 56 | 57 | //-------]> 58 | 59 | let chunk = process.stdin.read(); 60 | 61 | //-------]> 62 | 63 | if(!chunk || gBufLineEnd.equals(chunk)) { 64 | return; 65 | } 66 | 67 | //-------]> 68 | 69 | const idxLineEnd = chunk.indexOf(gBufLineEnd); 70 | 71 | //-------]> 72 | 73 | if(idxLineEnd >= 0) { 74 | onReadable.lock = true; 75 | chunks.push(chunk.slice(0, idxLineEnd)); 76 | 77 | request(function() { 78 | onReadable.lock = false; 79 | onReadable(); 80 | }); 81 | 82 | chunk = chunk.slice(idxLineEnd + gBufLineEnd.length); 83 | chunks = []; 84 | 85 | if(chunk && chunk.length > 0) { 86 | process.stdin.unshift(chunk); 87 | } 88 | } 89 | else { 90 | chunks.push(chunk); 91 | } 92 | } 93 | 94 | //----------]> 95 | 96 | process.stdout 97 | .on("error", process.exit); 98 | 99 | process.stdin 100 | .on("readable", onReadable) 101 | .on("end", onEnd); 102 | 103 | //----------]> 104 | 105 | function request(callback) { 106 | if(!chunks.length) { 107 | return; 108 | } 109 | 110 | //------]> 111 | 112 | initBotCmd(null, objToMap(JSON.parse(Buffer.concat(chunks)))); 113 | callBotMethod(gOptions, gParams, callback); 114 | } 115 | } 116 | 117 | //-----------------]> 118 | 119 | function initBotCmd(data, params) { 120 | gOptions = new Map(); 121 | gParams = params || parseArgv(data); 122 | 123 | moveObjField(gParams, gOptions, ["token", "method", "proxy", "j"]); 124 | } 125 | 126 | //-----)> 127 | 128 | function callBotMethod(options, params, callback) { 129 | gBot 130 | .token(options.get("token") || gToken) 131 | .proxy(options.get("proxy") || gProxy) 132 | .callJson(options.get("method") || gMethod, params, function(error, result) { 133 | if(result && !result.ok && result.parameters && result.parameters.retry_after) { 134 | setTimeout(function() { 135 | callBotMethod(options, params, callback); 136 | }, 1000 * result.parameters.retry_after); 137 | 138 | return; 139 | } 140 | 141 | if(callback) { 142 | setImmediate(callback); 143 | } 144 | 145 | if(error) { 146 | process.stderr.write(error.toString()); 147 | process.stderr.write("\r\n"); 148 | 149 | return; 150 | } 151 | 152 | result = JSON.stringify(result, null, options.get("j") ? " " : null); 153 | 154 | process.stdout.write(result); 155 | process.stdout.write("\r\n"); 156 | }); 157 | } 158 | 159 | //----------------------------------------------------- 160 | 161 | function parseArgv(data) { 162 | const result = new Map(); 163 | 164 | let nextOptIsVal = false, 165 | curOptName = ""; 166 | 167 | //------]> 168 | 169 | (data || process.argv).forEach(explode); 170 | 171 | //------]> 172 | 173 | return result; 174 | 175 | //------]> 176 | 177 | function explode(value) { 178 | if(nextOptIsVal) { 179 | nextOptIsVal = false; 180 | 181 | result.set(curOptName, value); 182 | } 183 | else { 184 | let cmdName = value.match(gReCmdNameFull); 185 | 186 | if(cmdName) { 187 | nextOptIsVal = true; 188 | curOptName = cmdName[1]; 189 | } 190 | else { 191 | cmdName = value.match(gReCmdNameShort); 192 | 193 | if(cmdName) { 194 | result.set(cmdName[1], true); 195 | } 196 | } 197 | } 198 | } 199 | } 200 | 201 | function moveObjField(src, dest, field) { 202 | if(!src.size) { 203 | return; 204 | } 205 | 206 | if(Array.isArray(field)) { 207 | field.forEach(f => moveObjField(src, dest, f)); 208 | return; 209 | } 210 | 211 | if(src.has(field)) { 212 | dest.set(field, src.get(field)); 213 | src.delete(field); 214 | } 215 | } 216 | 217 | function objToMap(obj) { 218 | const result = new Map(); 219 | 220 | for(let k of Object.keys(obj)) { 221 | result.set(k, obj[k]); 222 | } 223 | 224 | return result; 225 | } -------------------------------------------------------------------------------- /src/errors.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const gErrors = { 13 | "ERR_INTERNAL_SERVER": 500, 14 | "ERR_NOT_FOUND": 400, 15 | "ERR_FORBIDDEN": 403, 16 | "ERR_MESSAGE_LIMITS": 429, 17 | "ERR_USED_WEBHOOK": 409, 18 | "ERR_INVALID_TOKEN": 401, 19 | 20 | "ERR_BAD_REQUEST": -10000, 21 | "ERR_BAD_PROXY": -10001, 22 | "ERR_FAILED_PARSE_DATA": -10002 23 | 24 | }; 25 | 26 | //----------------------------------------------------- 27 | 28 | module.exports = bind(function(v) { 29 | return bind(v); 30 | }); 31 | 32 | //----------------------------------------------------- 33 | 34 | function bind(v) { 35 | for(let name in gErrors) { 36 | if(gErrors.hasOwnProperty(name)) { 37 | v[name] = gErrors[name]; 38 | } 39 | } 40 | 41 | return v; 42 | } -------------------------------------------------------------------------------- /src/keyboard/compile.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const gReKbParamsExplode = /\s+/; 13 | 14 | //----------------------------------------------------- 15 | 16 | module.exports = main; 17 | 18 | //----------------------------------------------------- 19 | 20 | function main(input) { 21 | const map = {}; 22 | 23 | //----------]> 24 | 25 | for(let name in input.bin) { 26 | if(input.bin.hasOwnProperty(name)) { 27 | const kb = input.bin[name]; 28 | 29 | name = name[0].toUpperCase() + name.substr(1); 30 | 31 | map["v" + name] = kb.map(x => [x]); 32 | map["h" + name] = [kb]; 33 | } 34 | } 35 | 36 | for(let name in input.norm) { 37 | if(input.norm.hasOwnProperty(name)) { 38 | const kb = input.norm[name]; 39 | 40 | keyboardBuilder[name] = genFKB("keyboard", kb, false); 41 | inlineKeyboardBuilder[name] = genFKB("inline_keyboard", kb, false, true); 42 | } 43 | } 44 | 45 | for(let name in map) { 46 | if(map.hasOwnProperty(name)) { 47 | const kb = map[name]; 48 | 49 | keyboardBuilder[name] = genFKB("keyboard", kb, true); 50 | inlineKeyboardBuilder[name] = genFKB("inline_keyboard", kb, true, true); 51 | } 52 | } 53 | 54 | //-----)> 55 | 56 | keyboardBuilder.inline = inlineKeyboardBuilder; 57 | keyboardBuilder.hide = genFKB("remove_keyboard"); 58 | 59 | //----------]> 60 | 61 | return keyboardBuilder; 62 | 63 | //----------]> 64 | 65 | function inlineKeyboardBuilder(buttons, isV) { 66 | if(typeof(buttons) === "string") { 67 | buttons = buttons.split(/\s+/).map(text => { 68 | text = {text, "callback_data": text}; 69 | return isV ? [text] : text; 70 | }); 71 | 72 | buttons = { 73 | "inline_keyboard": isV ? buttons : [buttons] 74 | }; 75 | } 76 | else if(Array.isArray(buttons)) { 77 | buttons = { 78 | "inline_keyboard": buttons 79 | }; 80 | } 81 | 82 | return buttons; 83 | } 84 | 85 | function keyboardBuilder(buttons, params) { 86 | buttons = typeof(buttons) === "string" ? buttons.split(/\s+/).map(x => [x]) : buttons; 87 | buttons = buttons === false || !arguments.length ? {"remove_keyboard": true} : {"keyboard": buttons}; 88 | 89 | if(!params) { 90 | return buttons; 91 | } 92 | 93 | if(!Array.isArray(params)) { 94 | params = params.split(gReKbParamsExplode); 95 | } 96 | 97 | if(params.indexOf("resize") !== -1) { 98 | buttons.resize_keyboard = true; 99 | } 100 | 101 | if(params.indexOf("once") !== -1) { 102 | buttons.one_time_keyboard = true; 103 | } 104 | 105 | if(params.indexOf("selective") !== -1) { 106 | buttons.selective = true; 107 | } 108 | 109 | return buttons; 110 | } 111 | } 112 | 113 | //----------------------------------]> 114 | 115 | function genFKB(type, kb, resize, inline) { 116 | return inline ? inlineKb : normalKb; 117 | 118 | //----------]> 119 | 120 | function inlineKb() { 121 | return { 122 | [type]: kb.map(x => (Array.isArray(x) ? x.map(y => ({"text": y, "callback_data": y})) : {"text": x, "callback_data": x})) 123 | }; 124 | } 125 | 126 | function normalKb(once, selective) { 127 | const k = { 128 | [type]: kb || true 129 | }; 130 | 131 | if(resize) { 132 | k.resize_keyboard = true; 133 | } 134 | 135 | if(once) { 136 | k.one_time_keyboard = true; 137 | } 138 | 139 | if(selective) { 140 | k.selective = true; 141 | } 142 | 143 | return k; 144 | } 145 | } -------------------------------------------------------------------------------- /src/keyboard/index.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rCompile = require("./compile"); 13 | 14 | //----------------------------------------------------- 15 | 16 | const gKeyboard = rCompile({ 17 | "bin": { 18 | "ox": ["\u2B55\uFE0F", "\u274C"], 19 | "pn": ["\u2795", "\u2796"], 20 | "ud": ["\uD83D\uDD3C", "\uD83D\uDD3D"], 21 | "lr": ["\u25C0\uFE0F", "\u25B6\uFE0F"], 22 | "gb": ["\uD83D\uDC4D\uD83C\uDFFB", "\uD83D\uDC4E\uD83C\uDFFB"] 23 | }, 24 | 25 | "norm": { 26 | "abcd": [ 27 | ["A", "B"], ["C", "D"] 28 | ], 29 | 30 | "numpad": [ 31 | ["7", "8", "9"], 32 | ["4", "5", "6"], 33 | ["1", "2", "3"], 34 | ["0"] 35 | ] 36 | } 37 | }); 38 | 39 | //----------------------------------------------------- 40 | 41 | module.exports = gKeyboard; 42 | -------------------------------------------------------------------------------- /src/parseCmd.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const gReReplaceName = /^@\S+\s+/, 13 | 14 | gReFindCmd = /(^\/\S*?)@\S+\s*(.*)/, 15 | gReSplitCmd = /\s+([\s\S]+)?/, 16 | gReValidCmd = /[\w]+/i; 17 | 18 | //----------------------------------------------------- 19 | 20 | module.exports = main; 21 | 22 | //----------------------------------------------------- 23 | 24 | function main(text, strict) { 25 | if(!text || typeof(text) !== "string") { 26 | return null; 27 | } 28 | 29 | //---------]> 30 | 31 | const firstChar = text[0]; 32 | 33 | let foundCmdParams, 34 | type, name, cmdText, cmd; 35 | 36 | //---------]> 37 | 38 | switch(firstChar) { 39 | case "/": 40 | foundCmdParams = text.match(gReFindCmd); 41 | 42 | if(foundCmdParams) { 43 | cmd = foundCmdParams[1]; 44 | cmdText = foundCmdParams[2]; 45 | 46 | if(cmd) { 47 | type = "private"; 48 | } 49 | } 50 | 51 | break; 52 | 53 | case "@": 54 | text = text.replace(gReReplaceName, ""); 55 | 56 | if(text) { 57 | type = "private"; 58 | } 59 | 60 | break; 61 | 62 | default: 63 | return null; 64 | } 65 | 66 | if(text !== "/") { 67 | if(!foundCmdParams) { 68 | foundCmdParams = text.split(gReSplitCmd, 2); 69 | 70 | cmd = foundCmdParams[0]; 71 | cmdText = foundCmdParams[1]; 72 | 73 | if(!cmd || cmd[0] !== "/") { 74 | return null; 75 | } 76 | } 77 | 78 | name = cmd === "/" ? "" : cmd.substr(1); 79 | } 80 | 81 | //---------]> 82 | 83 | return strict && name && (name.length > 32 || !gReValidCmd.test(name)) ? null : { 84 | "type": type || "common", 85 | "name": name || "", 86 | "text": cmdText || "", 87 | 88 | "cmd": cmd || text 89 | }; 90 | } -------------------------------------------------------------------------------- /src/server/createBot.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rResponseBuilder = require("./responseBuilder"); 13 | 14 | //----------------------------------------------------- 15 | 16 | module.exports = main; 17 | 18 | //----------------------------------------------------- 19 | 20 | function main(bot, onMsg) { 21 | /*jshint validthis:true */ 22 | 23 | const ctx = Object.create(bot); 24 | 25 | const result = { 26 | "instance": bot, 27 | "ctx": ctx, 28 | 29 | "plugins": [], 30 | "events": new Map(), 31 | 32 | "cbCatch": null, 33 | 34 | //-----)> 35 | 36 | "use": srvUse, 37 | 38 | "on": srvEvOn, 39 | "off": srvEvOff, 40 | 41 | "catch": srvCatch, 42 | 43 | //-----)> 44 | 45 | "onMsg": onMsg 46 | }; 47 | 48 | //--------------]> 49 | 50 | ctx.answer = ctxAnswer; 51 | 52 | //--------------]> 53 | 54 | return result; 55 | 56 | //--------------]> 57 | 58 | function ctxAnswer() { 59 | return new rResponseBuilder(this, bot); 60 | } 61 | 62 | //-----)> 63 | 64 | function srvUse(type, params, callback) { 65 | const event = buildEvent(type, params, callback); 66 | 67 | //--------]> 68 | 69 | if(!event) { 70 | throw new Error("Failed to create event!"); 71 | } 72 | 73 | result.plugins.push(event.result); 74 | 75 | //--------]> 76 | 77 | return this; 78 | } 79 | 80 | function srvEvOn(type, params, callback) { 81 | if(typeof(type) === "string") { 82 | const t = type.split(/\s+/); 83 | 84 | if(t.length > 1) { 85 | type = t; 86 | } 87 | } 88 | 89 | //---)> 90 | 91 | if(Array.isArray(type)) { 92 | type.forEach(e => srvEvOn(e, params, callback)); 93 | return this; 94 | } 95 | 96 | //--------]> 97 | 98 | const event = buildEvent(type, params, callback); 99 | 100 | //--------]> 101 | 102 | if(!event) { 103 | throw new Error("Failed to create event!"); 104 | } 105 | 106 | type = event.type; 107 | 108 | if(!result.events.has(type)) { 109 | result.events.set(type, []); 110 | } 111 | 112 | result.events.get(type).push(event.result); 113 | 114 | //--------]> 115 | 116 | return this; 117 | } 118 | 119 | function srvEvOff(type, callback) { 120 | const evProto = buildEvent(type, null, callback), 121 | events = evProto && result.events.get(evProto.type); 122 | 123 | if(events) { 124 | const target = evProto.result; 125 | 126 | let len = events.length; 127 | 128 | while(len--) { 129 | const event = events[len]; 130 | 131 | if(target[3] === event[3] && target[2] === event[2]) { 132 | events.splice(len, 1); 133 | break; 134 | } 135 | } 136 | } 137 | 138 | return this; 139 | } 140 | 141 | function srvCatch(callback) { 142 | result.cbCatch = callback; 143 | return this; 144 | } 145 | 146 | //-----)> 147 | 148 | function buildEvent(type, params, callback) { 149 | let filter, 150 | result = null; 151 | 152 | //--------]> 153 | 154 | if(typeof(type) === "string") { 155 | filter = type.split(":"); 156 | 157 | type = filter[0]; 158 | filter = filter[1]; 159 | } 160 | 161 | //------]> 162 | 163 | if(typeof(type) === "function") { 164 | callback = type; 165 | type = null; 166 | } else if(typeof(params) === "function") { 167 | callback = params; 168 | params = null; 169 | } 170 | 171 | if(!callback) { 172 | return result; 173 | } 174 | 175 | //------]> 176 | 177 | if(type instanceof RegExp) { 178 | if(params) { 179 | if(typeof(params) === "string") { 180 | params = params.split(/\s+/); 181 | } 182 | 183 | if(!Array.isArray(params)) { 184 | throw new Error("buildEvent | RegExp | `params` is not an array"); 185 | } 186 | } 187 | 188 | result = ["text", params, type, callback]; 189 | } 190 | else { 191 | result = [type, params, null, callback]; 192 | } 193 | 194 | type = result[0]; 195 | type = filter ? (type + ":" + filter) : type; 196 | 197 | result = {type, result}; 198 | 199 | //------]> 200 | 201 | return result; 202 | } 203 | } -------------------------------------------------------------------------------- /src/server/createHTTP.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rHttp = require("http"), 13 | rHttps = require("https"), 14 | rCrypto = require("crypto"), 15 | rFs = require("fs"); 16 | 17 | const rCreateBot = require("./createBot"), 18 | rOnMsg = require("./onMsg"); 19 | 20 | const rErrors = require("./../errors"); 21 | 22 | //----------------------------------------------------- 23 | 24 | const gCiphers = [ 25 | "ECDHE-RSA-AES256-SHA384", 26 | "DHE-RSA-AES256-SHA384", 27 | "ECDHE-RSA-AES256-SHA256", 28 | "DHE-RSA-AES256-SHA256", 29 | "ECDHE-RSA-AES128-SHA256", 30 | "DHE-RSA-AES128-SHA256", 31 | "HIGH", 32 | "!aNULL", 33 | "!eNULL", 34 | "!EXPORT", 35 | "!DES", 36 | "!RC4", 37 | "!MD5", 38 | "!PSK", 39 | "!SRP", 40 | "!CAMELLIA" 41 | ].join(":"); 42 | 43 | //----------------------------------------------------- 44 | 45 | module.exports = main; 46 | 47 | //----------------------------------------------------- 48 | 49 | function main(botFather, params, callback) { 50 | if(typeof(params) === "function") { 51 | callback = params; 52 | params = undefined; 53 | } 54 | 55 | if(params) { 56 | params.port = params.port || 88; 57 | } 58 | else { 59 | params = { 60 | "host": "localhost", 61 | "port": 1488, 62 | 63 | "autoWebhook": false, 64 | "ssl": false 65 | }; 66 | } 67 | 68 | //-----------------]> 69 | 70 | const isHTTPS = params.ssl !== false; 71 | 72 | const srvBotDefault = rCreateBot(botFather, callback), 73 | srvBots = Object.create(null); 74 | 75 | let srv; 76 | 77 | //----------)> 78 | 79 | if(isHTTPS) { 80 | const certDir = params.certDir || ""; 81 | 82 | const optKey = rFs.readFileSync(certDir + params.key), 83 | optCert = rFs.readFileSync(certDir + params.cert); 84 | 85 | let optCa = params.ca; 86 | 87 | //------)> 88 | 89 | if(optCa) { 90 | if(Array.isArray(optCa)) { 91 | optCa = optCa.map(e => rFs.readFileSync(certDir + e)); 92 | } 93 | else if(typeof(optCa) === "string") { 94 | optCa = certDir + optCa; 95 | } 96 | } 97 | 98 | //---------]> 99 | 100 | srv = rHttps.createServer({ 101 | "key": optKey, 102 | "cert": optCert, 103 | "ca": optCa, 104 | 105 | "ciphers": gCiphers, 106 | 107 | "honorCipherOrder": true, 108 | 109 | "requestCert": true, 110 | "rejectUnauthorized": false 111 | }, cbServer); 112 | } 113 | else { 114 | srv = rHttp.createServer(cbServer); 115 | } 116 | 117 | srv.listen(params.port, params.host, cbListen); 118 | 119 | //-----------------]> 120 | 121 | return (function() { 122 | const result = Object.create(srvBotDefault); 123 | 124 | result.app = srv; 125 | result.bot = addBot; 126 | 127 | return result; 128 | })(); 129 | 130 | //-----------------]> 131 | 132 | function cbServer(req, res) { 133 | if(req.method !== "POST") { 134 | return response(); 135 | } 136 | 137 | //----------]> 138 | 139 | let firstChunk, chunks; 140 | 141 | //----------]> 142 | 143 | req 144 | .on("data", onRequestData) 145 | .on("end", onRequestEnd); 146 | 147 | //----------]> 148 | 149 | function onRequestData(chunk) { 150 | if(!firstChunk) { 151 | firstChunk = chunk; 152 | } 153 | else { 154 | chunks = chunks || [firstChunk]; 155 | chunks.push(chunk); 156 | } 157 | } 158 | 159 | function onRequestEnd() { 160 | response(); 161 | 162 | //--------]> 163 | 164 | const srvBot = srvBots && srvBots[req.url] || srvBotDefault; 165 | 166 | let data = chunks ? Buffer.concat(chunks) : firstChunk, 167 | error = null; 168 | 169 | //--------]> 170 | 171 | try { 172 | data = JSON.parse(data); 173 | } catch(e) { 174 | error = e; 175 | 176 | error.code = rErrors.ERR_FAILED_PARSE_DATA; 177 | error.data = data; 178 | 179 | data = null; 180 | } 181 | 182 | //--------]> 183 | 184 | rOnMsg(error, srvBot, data); 185 | } 186 | 187 | //-------)> 188 | 189 | function response(code) { 190 | res.writeHead(code || 200); 191 | res.end(); 192 | } 193 | } 194 | 195 | function cbListen() { 196 | const host = srv.address().address, 197 | port = srv.address().port; 198 | 199 | //-------]> 200 | 201 | onAction(null, "START"); 202 | 203 | //-------]> 204 | 205 | process.on("SIGINT", onAction); 206 | process.on("uncaughtException", onAction); 207 | 208 | //-------]> 209 | 210 | function onAction(error, evName) { 211 | evName = evName || "STOP"; 212 | 213 | console.log("\n+---[%s]------------------------------------".slice(0, -1 * evName.length), evName); 214 | console.log("|"); 215 | console.log("| Server: [%s]", getAddress()); 216 | console.log("| Date: %s", getTime()); 217 | console.log("+-----------------------------------------\n"); 218 | 219 | if(error) { 220 | console.error(error.stack); 221 | } 222 | 223 | if(evName === "STOP") { 224 | process.exit(); 225 | } 226 | } 227 | 228 | //---)> 229 | 230 | function getAddress() { 231 | return (params.http ? "http" : "https") + "://" + (host || "*") + ":" + port; 232 | } 233 | 234 | function getTime() { 235 | return new Date().toISOString().replace(/T/, " ").replace(/\..+/, ""); 236 | } 237 | } 238 | 239 | //-----------------]> 240 | 241 | function addBot(bot, path, onMsg) { 242 | if(typeof(path) === "function") { 243 | onMsg = path; 244 | path = null; 245 | } 246 | 247 | if(!path) { 248 | if(!bot.token()) { 249 | throw new Error("`path` and `token` not specified."); 250 | } 251 | 252 | path = "/tg_bot_" + rCrypto.createHash("sha256").update(bot.token()).digest("hex"); 253 | } 254 | 255 | //-------------]> 256 | 257 | if(params.autoWebhook !== false) { 258 | if(params.autoWebhook || params.host && params.port) { 259 | if(!bot.token()) { 260 | throw new Error("`token` not specified."); 261 | } 262 | 263 | //---------]> 264 | 265 | const url = (params.autoWebhook || (params.host + ":" + params.port)) + path; 266 | const data = { 267 | "url": url, 268 | "certificate": params.selfSigned 269 | }; 270 | 271 | //---------]> 272 | 273 | bot.api.setWebhook(data, function(error, isOk) { 274 | if(isOk) { 275 | return; 276 | } 277 | 278 | console.log("[-] Webhook: %s", url); 279 | 280 | if(error) { 281 | if(error.message) { 282 | console.log(error.message); 283 | } 284 | 285 | console.log(error.stack); 286 | } 287 | }); 288 | } 289 | else { 290 | console.log("[!] Warning | `autoWebhook` and `host` not specified, webhook not working."); 291 | } 292 | } 293 | 294 | //-------------]> 295 | 296 | if(srvBots[path]) { 297 | throw new Error("Path '" + path + "' has already been used."); 298 | } 299 | 300 | //-------------]> 301 | 302 | return (srvBots[path] = rCreateBot(bot, onMsg)); 303 | } 304 | } -------------------------------------------------------------------------------- /src/server/createPolling.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rCreateBot = require("./createBot"), 13 | rOnMsg = require("./onMsg"); 14 | 15 | const rErrors = require("./../errors"); 16 | 17 | //----------------------------------------------------- 18 | 19 | module.exports = main; 20 | 21 | //----------------------------------------------------- 22 | 23 | function main(botFather, params, callback) { 24 | if(typeof(params) === "function") { 25 | callback = params; 26 | params = undefined; 27 | } 28 | 29 | if(!params) { 30 | params = {}; 31 | } 32 | 33 | //----------------]> 34 | 35 | const srvBot = rCreateBot(botFather, callback); 36 | 37 | const tmInterval = (parseInt(params.interval, 10) || 2) * 1000; 38 | 39 | let tmPolling, 40 | 41 | isStopped = false, 42 | pollingParams = Object.create(params); 43 | 44 | //------)> 45 | 46 | load(); 47 | 48 | //----------------]> 49 | 50 | return (function() { 51 | const result = Object.create(srvBot); 52 | 53 | result.start = tmStart; 54 | result.stop = tmStop; 55 | 56 | //--------]> 57 | 58 | return result; 59 | 60 | //--------]> 61 | 62 | function tmStart() { 63 | if(isStopped) { 64 | isStopped = false; 65 | runTimer(); 66 | } 67 | 68 | return result; 69 | } 70 | 71 | function tmStop() { 72 | isStopped = true; 73 | clearTimeout(tmPolling); 74 | 75 | return result; 76 | } 77 | })(); 78 | 79 | //----------------]> 80 | 81 | function runTimer() { 82 | if(!isStopped) { 83 | tmPolling = setTimeout(load, tmInterval); 84 | } 85 | } 86 | 87 | function load() { 88 | botFather.api.getUpdates(pollingParams, onUpdates); 89 | } 90 | 91 | function onUpdates(error, data) { 92 | if(error) { 93 | switch(error.code) { 94 | case "ECONNRESET": 95 | runTimer(); 96 | break; 97 | 98 | case rErrors.ERR_USED_WEBHOOK: 99 | botFather.callJson("deleteWebhook", null, load); 100 | break; 101 | 102 | default: 103 | rOnMsg(error, srvBot, null); 104 | runTimer(); 105 | } 106 | } 107 | else { 108 | if(data.length > 0) { 109 | data.forEach(onMsg); 110 | load(); 111 | } else { 112 | runTimer(); 113 | } 114 | } 115 | } 116 | 117 | function onMsg(data) { 118 | pollingParams.offset = data.update_id + 1; 119 | rOnMsg(null, srvBot, data) 120 | } 121 | } -------------------------------------------------------------------------------- /src/server/createVirtual.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rCreateBot = require("./createBot"), 13 | rOnMsg = require("./onMsg"); 14 | 15 | //----------------------------------------------------- 16 | 17 | module.exports = main; 18 | 19 | //----------------------------------------------------- 20 | 21 | function main(botFather, callback) { 22 | const objBot = rCreateBot(botFather, callback); 23 | 24 | //----------------]> 25 | 26 | return (function() { 27 | const result = Object.create(objBot); 28 | 29 | result.input = input; 30 | result.middleware = middleware; 31 | 32 | return result; 33 | })(); 34 | 35 | //----------------]> 36 | 37 | function input(error, data) { 38 | rOnMsg(error, objBot, data); 39 | } 40 | 41 | function middleware(request, response) { 42 | response.send(""); 43 | input(null, request.body); 44 | } 45 | } -------------------------------------------------------------------------------- /src/server/index.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | module.exports = { 13 | "polling": require("./createPolling"), 14 | "http": require("./createHTTP"), 15 | 16 | "virtual": require("./createVirtual") 17 | }; 18 | -------------------------------------------------------------------------------- /src/server/onMsg/index.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rUtil = require("./../../util"), 13 | rErrors = require("./../../errors"); 14 | 15 | const onIncomingMessage = require("./onIncomingMessage"), 16 | 17 | onIncomingShippingQuery = require("./onIncomingShippingQuery"), 18 | onIncomingPreCheckoutQuery = require("./onIncomingPreCheckoutQuery"), 19 | 20 | onIncomingInlineQuery = require("./onIncomingInlineQuery"), 21 | onIncomingCallbackQuery = require("./onIncomingCallbackQuery"), 22 | 23 | onIncomingChosenInlineResult = require("./onIncomingChosenInlineResult"); 24 | 25 | //----------------------------------------------------- 26 | 27 | const gIncomeEv = [ 28 | ["message", "message", onIncomingMessage], 29 | ["edited_message", "editedMessage", onIncomingMessage], 30 | ["channel_post", "channelPost", onIncomingMessage], 31 | ["edited_channel_post", "editedChannelPost", onIncomingMessage], 32 | 33 | ["inline_query", "inlineQuery", onIncomingInlineQuery], 34 | ["callback_query", "callbackQuery", onIncomingCallbackQuery], 35 | 36 | ["shipping_query", "shippingQuery", onIncomingShippingQuery], 37 | ["pre_checkout_query", "preCheckoutQuery", onIncomingPreCheckoutQuery], 38 | 39 | ["chosen_inline_result", "chosenInlineResult", onIncomingChosenInlineResult] 40 | ]; 41 | 42 | //----------------------------------------------------- 43 | 44 | module.exports = main; 45 | 46 | //----------------------------------------------------- 47 | 48 | function main(error, srvBot, data) { 49 | if(!error && (!data || typeof(data) !== "object")) { 50 | error = new Error("Expected [Object]."); 51 | 52 | error.code = rErrors.ERR_FAILED_PARSE_DATA; 53 | error.data = data; 54 | } 55 | 56 | if(error) { 57 | onError(error); 58 | return; 59 | } 60 | 61 | //--------]> 62 | 63 | for(let ctx, ev, updateType, eventType, func, input, i = 0, len = gIncomeEv.length; i < len; i++) { 64 | ev = gIncomeEv[i]; 65 | 66 | updateType = ev[0]; 67 | input = data[updateType]; 68 | 69 | if(input && typeof(input) === "object") { 70 | eventType = ev[1]; 71 | func = ev[2]; 72 | 73 | ctx = Object.create(srvBot.ctx); 74 | 75 | //---------]> 76 | 77 | ctx.updateType = updateType; 78 | ctx.updateSubType = null; 79 | 80 | ctx.eventType = eventType; 81 | ctx.eventSubType = null; 82 | 83 | ctx.from = input.from; 84 | 85 | ctx[eventType] = input; 86 | 87 | //---------]> 88 | 89 | func(ctx, srvBot.plugins, srvBot.events, updateType, eventType, input, onEnd); 90 | 91 | break; 92 | } 93 | } 94 | 95 | //--------]> 96 | 97 | function onEnd(error, reqCtx) { 98 | if(error) { 99 | onError(error); 100 | } 101 | else { 102 | let onMsg = srvBot.onMsg; 103 | 104 | if(onMsg) { 105 | try { 106 | onMsg = onMsg(reqCtx); 107 | } catch(e) { 108 | onError(e); 109 | } 110 | 111 | rUtil.executeGenerator(onMsg, onError); 112 | } 113 | } 114 | } 115 | 116 | function onError(error) { 117 | if(error) { 118 | let cbCatch = srvBot.cbCatch; 119 | 120 | if(cbCatch) { 121 | cbCatch = cbCatch(error); 122 | rUtil.executeGenerator(cbCatch); 123 | } 124 | else { 125 | throw error; 126 | } 127 | } 128 | } 129 | } -------------------------------------------------------------------------------- /src/server/onMsg/onIncomingCallbackQuery.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rRunAction = require("./runAction"); 13 | 14 | //----------------------------------------------------- 15 | 16 | module.exports = main; 17 | 18 | //----------------------------------------------------- 19 | 20 | function main(ctx, plugins, events, updateType, eventType, input, callback) { 21 | const message = input.message; 22 | const msgChat = message && message.chat; 23 | 24 | const isGroup = !!(msgChat && (msgChat.type === "group" || msgChat.type === "supergroup")), 25 | isReply = !!(message && message.reply_to_message); 26 | 27 | //------------]> 28 | 29 | ctx.cqid = input.id; 30 | ctx.qid = input.inline_message_id; 31 | ctx.cid = msgChat && msgChat.id; 32 | ctx.mid = message && message.message_id; 33 | 34 | ctx.isGroup = isGroup; 35 | ctx.isReply = isReply; 36 | 37 | //------------]> 38 | 39 | rRunAction(ctx.updateSubType, eventType, ctx.eventSubType, plugins, events, input, ctx, callback); 40 | } -------------------------------------------------------------------------------- /src/server/onMsg/onIncomingChosenInlineResult.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rRunAction = require("./runAction"); 13 | 14 | //----------------------------------------------------- 15 | 16 | module.exports = main; 17 | 18 | //----------------------------------------------------- 19 | 20 | function main(ctx, plugins, events, updateType, eventType, input, callback) { 21 | ctx.qid = input.inline_message_id; 22 | 23 | //------------]> 24 | 25 | rRunAction(ctx.updateSubType, eventType, ctx.eventSubType, plugins, events, input, ctx, callback); 26 | } -------------------------------------------------------------------------------- /src/server/onMsg/onIncomingInlineQuery.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rRunAction = require("./runAction"); 13 | 14 | //----------------------------------------------------- 15 | 16 | module.exports = main; 17 | 18 | //----------------------------------------------------- 19 | 20 | function main(ctx, plugins, events, updateType, eventType, input, callback) { 21 | ctx.qid = input.id; 22 | 23 | //------------]> 24 | 25 | rRunAction(ctx.updateSubType, eventType, ctx.eventSubType, plugins, events, input, ctx, callback); 26 | } -------------------------------------------------------------------------------- /src/server/onMsg/onIncomingMessage.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rRunAction = require("./runAction"); 13 | 14 | //----------------------------------------------------- 15 | 16 | module.exports = main; 17 | 18 | //----------------------------------------------------- 19 | 20 | function main(ctx, plugins, events, updateType, eventType, input, callback) { 21 | const updateSubType = getMessageDataField(input), 22 | eventSubType = getEventNameByDataField(updateSubType); 23 | 24 | const msgChat = input.chat; 25 | 26 | const isGroup = msgChat.type === "group" || msgChat.type === "supergroup", 27 | isReply = !!(input.reply_to_message); 28 | 29 | //------------]> 30 | 31 | ctx.updateSubType = updateSubType; 32 | ctx.eventSubType = eventSubType; 33 | 34 | //---)> 35 | 36 | ctx.cid = msgChat.id; 37 | ctx.mid = input.message_id; 38 | 39 | ctx.isGroup = isGroup; 40 | ctx.isReply = isReply; 41 | 42 | //------------]> 43 | 44 | rRunAction(updateSubType, eventType, eventSubType, plugins, events, input, ctx, callback); 45 | } 46 | 47 | //---------]> 48 | 49 | function getEventNameByDataField(field) { 50 | /*jshint maxcomplexity:50 */ 51 | 52 | switch(field) { 53 | case "video_note": return "videoNote"; 54 | 55 | case "new_chat_members": return "enterChat"; 56 | case "left_chat_member": return "leftChat"; 57 | 58 | case "new_chat_title": return "chatTitle"; 59 | case "new_chat_photo": return "chatNewPhoto"; 60 | case "delete_chat_photo": return "chatDeletePhoto"; 61 | 62 | case "group_chat_created": return "chatCreated"; 63 | case "supergroup_chat_created": return "superChatCreated"; 64 | case "channel_chat_created": return "channelChatCreated"; 65 | 66 | case "migrate_to_chat_id": return "migrateToChatId"; 67 | case "migrate_from_chat_id": return "migrateFromChatId"; 68 | 69 | case "pinned_message": return "pinnedMessage"; 70 | 71 | case "successful_payment": return "successfulPayment"; 72 | 73 | default: return field; 74 | } 75 | } 76 | 77 | function getMessageDataField(m) { 78 | /*jshint maxcomplexity:50 */ 79 | 80 | let t; 81 | 82 | if( 83 | hasOwnProperty.call(m, t = "text") || 84 | hasOwnProperty.call(m, t = "photo") || 85 | hasOwnProperty.call(m, t = "audio") || 86 | hasOwnProperty.call(m, t = "document") || 87 | hasOwnProperty.call(m, t = "sticker") || 88 | hasOwnProperty.call(m, t = "video") || 89 | hasOwnProperty.call(m, t = "voice") || 90 | hasOwnProperty.call(m, t = "video_note") || 91 | hasOwnProperty.call(m, t = "location") || 92 | hasOwnProperty.call(m, t = "venue") || 93 | hasOwnProperty.call(m, t = "contact") || 94 | hasOwnProperty.call(m, t = "game") || 95 | 96 | hasOwnProperty.call(m, t = "new_chat_members") || 97 | hasOwnProperty.call(m, t = "left_chat_member") || 98 | 99 | hasOwnProperty.call(m, t = "new_chat_title") || 100 | hasOwnProperty.call(m, t = "new_chat_photo") || 101 | hasOwnProperty.call(m, t = "delete_chat_photo") || 102 | 103 | hasOwnProperty.call(m, t = "group_chat_created") || 104 | hasOwnProperty.call(m, t = "supergroup_chat_created") || 105 | hasOwnProperty.call(m, t = "channel_chat_created") || 106 | 107 | hasOwnProperty.call(m, t = "migrate_to_chat_id") || 108 | hasOwnProperty.call(m, t = "migrate_from_chat_id") || 109 | 110 | hasOwnProperty.call(m, t = "pinned_message") || 111 | 112 | hasOwnProperty.call(m, t = "invoice") || 113 | hasOwnProperty.call(m, t = "successful_payment") 114 | ) { 115 | return t; 116 | } 117 | } -------------------------------------------------------------------------------- /src/server/onMsg/onIncomingPreCheckoutQuery.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rRunAction = require("./runAction"); 13 | 14 | //----------------------------------------------------- 15 | 16 | module.exports = main; 17 | 18 | //----------------------------------------------------- 19 | 20 | function main(ctx, plugins, events, updateType, eventType, input, callback) { 21 | ctx.pqid = input.id; 22 | 23 | //------------]> 24 | 25 | rRunAction(ctx.updateSubType, eventType, ctx.eventSubType, plugins, events, input, ctx, callback); 26 | } -------------------------------------------------------------------------------- /src/server/onMsg/onIncomingShippingQuery.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rRunAction = require("./runAction"); 13 | 14 | //----------------------------------------------------- 15 | 16 | module.exports = main; 17 | 18 | //----------------------------------------------------- 19 | 20 | function main(ctx, plugins, events, updateType, eventType, input, callback) { 21 | ctx.sid = input.id; 22 | 23 | //------------]> 24 | 25 | rRunAction(ctx.updateSubType, eventType, ctx.eventSubType, plugins, events, input, ctx, callback); 26 | } -------------------------------------------------------------------------------- /src/server/onMsg/runAction.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rUtil = require("./../../util"), 13 | rParseCmd = require("./../../parseCmd"); 14 | 15 | //----------------------------------------------------- 16 | 17 | module.exports = main; 18 | 19 | //----------------------------------------------------- 20 | 21 | function main(updateSubType, eventType, eventSubType, queue, events, input, reqCtx, callback) { 22 | const data = updateSubType ? input[updateSubType] : null; 23 | 24 | let cmdParams = reqCtx.command; 25 | 26 | //------------]> 27 | 28 | if(cmdParams !== null && eventSubType === "text") { 29 | cmdParams = reqCtx.command = rParseCmd(data); 30 | } 31 | 32 | rUtil.forEachAsync(queue, iterQueue, onEndQueue); 33 | 34 | //------------]> 35 | 36 | function iterQueue(next, plugin) { 37 | const plType = plugin[0], 38 | plParams = plugin[1], 39 | plIsFilter = plugin[2], 40 | plCallback = plugin[3]; 41 | 42 | const isPlGenerator = plCallback.constructor.name === "GeneratorFunction", 43 | isPlWithFilter = !!plType, 44 | isPlSync = isPlGenerator ? false : plCallback.length < 3; 45 | 46 | let plData = data, 47 | isEnd = false; 48 | 49 | let cbPlResult; 50 | 51 | //----------]> 52 | 53 | if(isPlWithFilter) { 54 | if(eventType === plType) { 55 | plData = input; 56 | } 57 | else if(cmdParams && ("/" === plType || cmdParams.cmd === plType)) { 58 | plData = cmdParams; 59 | } 60 | else if(eventSubType !== plType) { 61 | onNext(); 62 | return; 63 | } 64 | else if(plIsFilter && (plIsFilter instanceof RegExp)) { 65 | plData = plData.match(plIsFilter); 66 | 67 | if(!plData) { 68 | onNext(); 69 | return; 70 | } 71 | 72 | if(plParams) { 73 | const result = {}; 74 | 75 | for(let i = 0, len = Math.min(plData.length - 1, plParams.length); i < len; i++) { 76 | result[plParams[i]] = plData[i + 1]; 77 | } 78 | 79 | plData = result; 80 | } 81 | } 82 | } 83 | 84 | //---------]> 85 | 86 | try { 87 | cbPlResult = isPlGenerator || isPlSync ? plCallback(reqCtx, plData) : plCallback(reqCtx, plData, onNext); 88 | } catch(error) { 89 | onNext(error); 90 | return; 91 | } 92 | 93 | //---------]> 94 | 95 | if(isPlGenerator) { 96 | rUtil.executeGenerator(cbPlResult, (error, result) => onNext(error || result)); 97 | } 98 | 99 | if(isPlSync) { 100 | onNext(cbPlResult); 101 | } 102 | 103 | //---------]> 104 | 105 | function onNext(state) { 106 | if(isEnd) { 107 | throw new Error("Plugin: double call `next`"); 108 | } 109 | 110 | isEnd = true; 111 | 112 | setImmediate(next, state); 113 | } 114 | } 115 | 116 | function onEndQueue(state) { 117 | if(state) { 118 | if(state instanceof Error) { 119 | callback(state, null); 120 | return; 121 | } 122 | 123 | reqCtx.gotoState = state; 124 | } 125 | 126 | if(!events) { 127 | return; 128 | } 129 | 130 | if(events.size > 0) { 131 | if(cmdParams) { 132 | callEvent(cmdParams.cmd); 133 | } 134 | else { 135 | if(eventType) { 136 | callEvent(eventType); 137 | } 138 | 139 | if(eventSubType) { 140 | callEvent(eventSubType); 141 | } 142 | } 143 | } 144 | else { 145 | callback(null, reqCtx); 146 | } 147 | 148 | //-----------------]> 149 | 150 | function callEvent(evType) { 151 | queue = (events.get(state ? (evType + ":" + state) : evType) || events.get(evType) || events.get(cmdParams ? "/" : "*")); 152 | 153 | if(queue) { 154 | main(updateSubType, eventType, eventSubType, queue, null, input, reqCtx, callback); 155 | } 156 | } 157 | } 158 | } -------------------------------------------------------------------------------- /src/server/responseBuilder.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rUtil = require("./../util"), 13 | rAPIProto = require("./../api/proto"); 14 | 15 | //----------------------------------------------------- 16 | 17 | const gModifiers = [ 18 | "maxSize", "filename", 19 | 20 | "latitude", "longitude", 21 | "caption", "duration", "performer", "title", "width", "height", 22 | "game_short_name", 23 | 24 | "disable_web_page_preview", "disable_notification", 25 | "show_alert", 26 | "cache_time", "next_offset", "switch_pm_text", "switch_pm_parameter", 27 | 28 | "phone_number", "first_name", "last_name", 29 | "parse_mode", "reply_markup", 30 | 31 | "reply_to_message_id", "message_id", "inline_message_id" 32 | ]; 33 | 34 | //----------------------------------------------------- 35 | 36 | module.exports = CMain; 37 | 38 | //----------------------------------------------------- 39 | 40 | function CMain(botReqCtx, botInstance) { 41 | this.botReqCtx = botReqCtx; 42 | this.botInstance = botInstance; 43 | 44 | this.queue = null; 45 | this.lastElement = null; 46 | 47 | this.enabledReply = false; 48 | } 49 | 50 | CMain.prototype = Object.create(null); 51 | 52 | //-----[Elements]-----}> 53 | 54 | (function createElements() { 55 | rAPIProto.genAliasesSendMethodsFor(addElementMethod); 56 | 57 | addElementMethod("inlineQuery", "answerInlineQuery"); 58 | addElementMethod("callbackQuery", "answerCallbackQuery"); 59 | addElementMethod("shippingQuery", "answerShippingQuery"); 60 | addElementMethod("preCheckoutQuery", "answerPreCheckoutQuery"); 61 | 62 | addElementMethod("markdown", "sendMessage", {"parse_mode": "markdown"}); 63 | addElementMethod("html", "sendMessage", {"parse_mode": "html"}); 64 | 65 | //-------]> 66 | 67 | function addElementMethod(alias, original, defaultParams) { 68 | const argsList = rAPIProto.args[original].slice(1); 69 | 70 | CMain.prototype[alias] = mthElement; 71 | 72 | function mthElement() { 73 | const lastElement = this.lastElement, 74 | elem = defaultParams ? Object.create(defaultParams) : {}; 75 | 76 | const argsLen = arguments.length; 77 | 78 | //--------]> 79 | 80 | for(let i = 0, offset = 0; i < argsLen && offset < argsLen; i++) { 81 | const input = arguments[i], 82 | name = argsList[i + offset]; 83 | 84 | if(defaultParams && hasOwnProperty.call(defaultParams, name)) { 85 | i--; 86 | offset++; 87 | } 88 | else if(input !== null && typeof(input) !== "undefined") { 89 | elem[name] = input; 90 | } 91 | } 92 | 93 | if(lastElement) { 94 | let queue = this.queue; 95 | 96 | if(!queue) { 97 | queue = this.queue = []; 98 | } 99 | 100 | queue.push(lastElement); 101 | } 102 | 103 | if(this.enabledReply) { 104 | elem.reply_to_message_id = this.botReqCtx.mid; 105 | } 106 | 107 | //--------]> 108 | 109 | elem.__method__ = original; 110 | this.lastElement = elem; 111 | 112 | //--------]> 113 | 114 | return this; 115 | } 116 | } 117 | })(); 118 | 119 | //-----[Modifiers]-----}> 120 | 121 | gModifiers 122 | .forEach(function(name) { 123 | let defValue; 124 | 125 | const funcName = name 126 | .split("_") 127 | .map(function(e, i) { 128 | if(!i && (e === "disable" || e === "show")) { 129 | defValue = true; 130 | } 131 | 132 | return i ? (e[0].toUpperCase() + e.substr(1)) : e; 133 | }) 134 | .join(""); 135 | 136 | //--------]> 137 | 138 | CMain.prototype[funcName] = function(v) { 139 | this.lastElement[name] = typeof(v) === "undefined" ? defValue : v; 140 | return this; 141 | }; 142 | }); 143 | 144 | //----)> 145 | 146 | CMain.prototype.isReply = function(t) { 147 | this.enabledReply = typeof(t) === "undefined" ? true : !!t; 148 | return this; 149 | }; 150 | 151 | CMain.prototype.keyboard = function(data, params) { 152 | const lastElement = this.lastElement; 153 | 154 | //--------]> 155 | 156 | if(Array.isArray(data)) { 157 | data = {"keyboard": data}; 158 | } 159 | else if(typeof(data) !== "object") { 160 | data = arguments.length ? data : false; 161 | data = this.botInstance.keyboard(data, params); 162 | } 163 | 164 | lastElement.reply_markup = data; 165 | 166 | if(data.selective) { 167 | lastElement.reply_to_message_id = this.botReqCtx.mid; 168 | } 169 | else { 170 | delete lastElement.reply_to_message_id; 171 | } 172 | 173 | //--------]> 174 | 175 | return this; 176 | }; 177 | 178 | CMain.prototype.inlineKeyboard = function(data, isVertically) { 179 | this.lastElement.reply_markup = this.botInstance.keyboard.inline(data, isVertically); 180 | return this; 181 | }; 182 | 183 | CMain.prototype.render = function(data) { 184 | const bot = this.botInstance, 185 | lastElement = this.lastElement, 186 | 187 | text = lastElement.text, 188 | kb = lastElement.reply_markup; 189 | 190 | //--------]> 191 | 192 | if(text) { 193 | lastElement.text = bot.render(text, data); 194 | } 195 | 196 | if(kb && kb.keyboard) { 197 | kb.keyboard.forEach(x => { 198 | x.forEach((y, i) => { 199 | x[i] = bot.render(y, data); 200 | }); 201 | }); 202 | } 203 | 204 | //--------]> 205 | 206 | return this; 207 | }; 208 | 209 | //-----[Exec]-----}> 210 | 211 | CMain.prototype.send = function(callback) { 212 | const botInstance = this.botInstance, 213 | 214 | chatId = this.botReqCtx.cid, 215 | inlineQueryId = this.botReqCtx.qid, 216 | callbackQueryId = this.botReqCtx.cqid, 217 | shippingQueryId = this.botReqCtx.sid, 218 | preCheckoutQueryId= this.botReqCtx.pqid; 219 | 220 | const queue = this.queue, 221 | lastElement = this.lastElement; 222 | 223 | //-------]> 224 | 225 | if(queue) { 226 | queue.push(lastElement); 227 | this.queue = null; 228 | } 229 | 230 | this.lastElement = null; 231 | 232 | //--------]> 233 | 234 | if(typeof(callback) === "undefined") { 235 | return new botInstance.mdPromise(cbPromise); 236 | } 237 | 238 | cbPromise(); 239 | 240 | //--------]> 241 | 242 | function cbPromise(resolve, reject) { 243 | callback = callback || ((error, result) => error ? reject(error) : resolve(result)); 244 | 245 | //-----]> 246 | 247 | const results = queue ? [] : null; 248 | 249 | if(queue) { 250 | rUtil.forEachAsync(queue, iterElements, callback); 251 | } 252 | else { 253 | iterElements(callback, lastElement, 0); 254 | } 255 | 256 | //-----]> 257 | 258 | function iterElements(next, elem, index) { 259 | const apiMethod = botInstance.api[elem.__method__]; 260 | 261 | if(chatId) { 262 | elem.chat_id = chatId; 263 | } 264 | 265 | if(callbackQueryId) { 266 | elem.callback_query_id = callbackQueryId; 267 | } 268 | 269 | if(inlineQueryId) { 270 | elem.inline_query_id = inlineQueryId; 271 | } 272 | 273 | if(shippingQueryId) { 274 | elem.shipping_query_id = shippingQueryId; 275 | } 276 | 277 | if(preCheckoutQueryId) { 278 | elem.pre_checkout_query_id = preCheckoutQueryId; 279 | } 280 | 281 | apiMethod(elem, function(error, result) { 282 | if(error) { 283 | error.index = index; 284 | error.results = results; 285 | } 286 | else if(results) { 287 | results.push(result); 288 | } 289 | 290 | next(error, results || result); 291 | }); 292 | } 293 | } 294 | }; -------------------------------------------------------------------------------- /src/util.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | "use strict"; 9 | 10 | //----------------------------------------------------- 11 | 12 | const rHttp = require("http"), 13 | rHttps = require("https"), 14 | rUrl = require("url"); 15 | 16 | const rErrors = require("./errors"); 17 | 18 | //----------------------------------------------------- 19 | 20 | const gHttpKeepAliveAgent = new rHttp.Agent({"keepAlive": true}), 21 | gHttpsKeepAliveAgent = new rHttps.Agent({"keepAlive": true}); 22 | 23 | //----------------------------------------------------- 24 | 25 | module.exports = { 26 | createReadStreamByUrl, 27 | getFilenameByMime, 28 | 29 | forEachAsync, 30 | executeGenerator 31 | }; 32 | 33 | //----------------------------------------------------- 34 | 35 | function createReadStreamByUrl(url, callback) { 36 | const urlObj = rUrl.parse(url); 37 | 38 | //-------]> 39 | 40 | if(!urlObj.protocol || !(/^http/).test(urlObj.protocol)) { 41 | onError(new Error("Use the links only with HTTP/HTTPS protocol.")); 42 | return; 43 | } 44 | 45 | //-------]> 46 | 47 | const isHTTPS = urlObj.protocol === "https:"; 48 | const timeout = 1000 * 60 * 2; 49 | 50 | const options = { 51 | "host": urlObj.hostname, 52 | "port": urlObj.port, 53 | "path": urlObj.path, 54 | 55 | "agent": isHTTPS ? gHttpsKeepAliveAgent : gHttpKeepAliveAgent, 56 | 57 | "headers": { 58 | "User-Agent": "TgBApic", 59 | "Referer": url 60 | } 61 | }; 62 | 63 | //-----------]> 64 | 65 | (isHTTPS ? rHttps : rHttp).get(options).on("error", onError).on("response", onResponse).setTimeout(timeout, onTimeout); 66 | 67 | //-----------]> 68 | 69 | function onError(error) { 70 | error.code = rErrors.ERR_BAD_REQUEST; 71 | callback(error, null) 72 | } 73 | 74 | function onTimeout() { 75 | this.destroy(new Error("Timeout.")); 76 | } 77 | 78 | function onResponse(response) { 79 | callback(null, response) 80 | } 81 | } 82 | 83 | function getFilenameByMime(contentType) { 84 | let result; 85 | 86 | if(contentType && typeof(contentType) === "string") { 87 | switch(contentType) { 88 | case "audio/mpeg": 89 | case "audio/MPA": 90 | case "audio/mpa-robust": 91 | result = "audio.mp3"; 92 | 93 | break; 94 | 95 | default: 96 | result = contentType.replace("/", "."); 97 | } 98 | } 99 | 100 | return result || ""; 101 | } 102 | 103 | //-------------[HELPERS]--------------}> 104 | 105 | function forEachAsync(data, iter, cbEnd) { 106 | if(!data) { 107 | if(cbEnd) { 108 | cbEnd(); 109 | } 110 | 111 | return; 112 | } 113 | 114 | //---------]> 115 | 116 | const len = data.length; 117 | 118 | let i = 0; 119 | 120 | //---------]> 121 | 122 | if(len) { 123 | run(); 124 | } 125 | else { 126 | if(cbEnd) { 127 | cbEnd(); 128 | } 129 | } 130 | 131 | //---------]> 132 | 133 | function run() { 134 | iter(cbNext, data[i], i); 135 | } 136 | 137 | function cbNext(error, result, stopped) { 138 | if(error || stopped) { 139 | if(cbEnd) { 140 | cbEnd(error, result, i); 141 | } 142 | 143 | return; 144 | } 145 | 146 | i++; 147 | 148 | if(i >= len) { 149 | if(cbEnd) { 150 | cbEnd(null, result, i); 151 | } 152 | } else { 153 | run(); 154 | } 155 | } 156 | } 157 | 158 | function executeGenerator(generator, callback) { 159 | if(!generator || !generator[Symbol.iterator]) { 160 | return false; 161 | } 162 | 163 | callback = callback || pushException; 164 | 165 | //---------]> 166 | 167 | (function execute(input) { 168 | let next; 169 | 170 | try { 171 | next = generator.next(input); 172 | } catch(e) { 173 | callback(e); 174 | return; 175 | } 176 | 177 | if(!next.done) { 178 | next.value.then(execute, onGenError); 179 | } 180 | else { 181 | callback(null, next.value); 182 | } 183 | })(); 184 | 185 | //---------]> 186 | 187 | return true; 188 | 189 | //---------]> 190 | 191 | function pushException(error) { 192 | if(error) { 193 | setImmediate(function() { throw error; }); 194 | } 195 | } 196 | 197 | function onGenError(error) { 198 | try { 199 | generator.throw(error); 200 | } catch(e) { 201 | callback(e); 202 | } 203 | } 204 | } -------------------------------------------------------------------------------- /test/data/MiElPotato.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Daeren/telegram-bot-api-c/a32d50f554ccb9185af3d4e48ef770692182ef37/test/data/MiElPotato.jpg -------------------------------------------------------------------------------- /test/data/msg.json: -------------------------------------------------------------------------------- 1 | {"chat_id": 59725308, "text": "Hell\r\nWord", "j": true} 2 | {"chat_id": 59725308, "text": "!", "j": true} 3 | -------------------------------------------------------------------------------- /test/errors.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | /*global describe, it*/ 10 | 11 | "use strict"; 12 | 13 | //----------------------------------------------------- 14 | 15 | const rChai = require("chai"); 16 | 17 | const expect = rChai.expect; 18 | 19 | const rBot = require("./../index"); 20 | 21 | const gErrors = require("./../src/errors"); 22 | 23 | //----------------------------------------------------- 24 | 25 | const token = process.env.TELEGRAM_BOT_TOKEN, 26 | chatId = process.env.TELEGRAM_CHAT_ID, 27 | msgId = process.env.TELEGRAM_MSG_ID; 28 | 29 | const objBot = rBot(token); 30 | 31 | //----------------------------------------------------- 32 | 33 | expect(token).to.exist; 34 | expect(chatId).to.exist; 35 | expect(msgId).to.exist; 36 | 37 | //----------------------------------------------------- 38 | 39 | describe("errors", function() { 40 | this.timeout(1000 * 10); 41 | 42 | //-----------------]> 43 | 44 | it("base", function() { 45 | expect(gErrors).to.be.a("function"); 46 | 47 | //-----]> 48 | 49 | const object = gErrors(Object.create(null)); 50 | 51 | for(let name in object) { 52 | if(hasOwnProperty.call(object, name)) { 53 | expect(gErrors).to.have.property(name); 54 | } 55 | } 56 | 57 | for(let name in gErrors) { 58 | if(hasOwnProperty.call(gErrors, name)) { 59 | expect(object).to.have.property(name); 60 | expect(rBot).to.have.property(name); 61 | } 62 | } 63 | }); 64 | 65 | //-----------------]> 66 | 67 | it("check API", function(done) { 68 | objBot.api.setWebhook({"url": "https://db.gg/test"}, onEnd); 69 | 70 | function onEnd(error, isOk) { 71 | expect(error).to.be.null; 72 | expect(isOk).to.be.a("boolean").and.equal(true); 73 | 74 | objBot.callJson("getUpdates", function(error, data) { 75 | const code = data.error_code; 76 | 77 | expect(code).to.equal(rBot.ERR_USED_WEBHOOK); 78 | expect(code).to.equal(objBot.ERR_USED_WEBHOOK); 79 | 80 | done(); 81 | }); 82 | } 83 | }); 84 | 85 | }); -------------------------------------------------------------------------------- /test/index.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | /*global describe, it*/ 10 | 11 | "use strict"; 12 | 13 | //----------------------------------------------------- 14 | 15 | const rChai = require("chai"); 16 | 17 | const expect = rChai.expect; 18 | 19 | const rBot = require("./../index"), 20 | rFs = require("fs"); 21 | 22 | const rAPIProto = require("./../src/api/proto"); 23 | 24 | //----------------------------------------------------- 25 | 26 | const token = process.env.TELEGRAM_BOT_TOKEN, 27 | chatId = process.env.TELEGRAM_CHAT_ID, 28 | msgId = process.env.TELEGRAM_MSG_ID; 29 | 30 | const objBot = rBot(token); 31 | 32 | const api = objBot.api, 33 | keyboard = objBot.keyboard, 34 | 35 | parseCmd = objBot.parseCmd; 36 | 37 | let midEditText, midEditCaption, midDel; 38 | 39 | //----------------------------------------------------- 40 | 41 | expect(token).to.exist; 42 | expect(chatId).to.exist; 43 | expect(msgId).to.exist; 44 | 45 | //----------------------------------]> 46 | 47 | describe("Module: bot", function() { 48 | 49 | it("Require", function() { 50 | expect(rBot).to.be.a("function"); 51 | }); 52 | 53 | describe("Method", function() { 54 | 55 | it("keyboard", function() { 56 | expect(rBot).to.have.property("keyboard").that.is.a("function"); 57 | expect(rBot.keyboard).to.have.property("inline").that.is.a("function"); 58 | }); 59 | 60 | it("parseCmd", function() { 61 | expect(rBot).to.have.property("parseCmd").that.is.a("function"); 62 | }); 63 | 64 | it("call", function() { 65 | expect(rBot).to.have.property("call").that.is.a("function"); 66 | }); 67 | 68 | it("callJson", function() { 69 | expect(rBot).to.have.property("callJson").that.is.a("function"); 70 | }); 71 | 72 | }); 73 | 74 | it("call(getMe) | callback", function(done) { 75 | rBot 76 | .call(token, "getMe", function(error, result, response) { 77 | checkWithoutError(error); 78 | 79 | expect(result).to.be.an.instanceof(Buffer); 80 | expect(response).to.be.an("object").and.not.equal(null); 81 | 82 | done(); 83 | }); 84 | }); 85 | 86 | it("callJson(getMe) | callback", function(done) { 87 | rBot 88 | .callJson(token, "getMe", function(error, result, response) { 89 | checkWithoutError(error); 90 | 91 | expect(result).to.be.an("object").and.not.equal(null); 92 | expect(response).to.be.an("object").and.not.equal(null); 93 | 94 | expect(result).to.have.property("ok").to.equal(true); 95 | expect(result).to.have.property("result").to.be.an("object").and.not.equal(null); 96 | 97 | done(); 98 | }); 99 | }); 100 | 101 | }); 102 | 103 | //-----------------]> 104 | 105 | describe("Instance: bot", function() { 106 | this.timeout(1000 * 60); 107 | 108 | //-----------------]> 109 | 110 | it("Instance", function() { 111 | expect(objBot).to.be.an("object").and.not.equal(null); 112 | }); 113 | 114 | //-----------------]> 115 | 116 | describe("Properties", function() { 117 | 118 | const botMethods = [ 119 | "enable", "disable", "enabled", "disabled", 120 | 121 | "engine", "promise", "token", 122 | 123 | "call", "callJson", 124 | "render", "download", 125 | 126 | "polling", "http", "virtual", 127 | 128 | "keyboard", "parseCmd" 129 | ]; 130 | 131 | //-----------------]> 132 | 133 | it("api", function() { 134 | expect(api).to.be.an("object").and.not.equal(null); 135 | 136 | for(let method of rAPIProto.methods) { 137 | expect(api).to.have.property(method); 138 | } 139 | }); 140 | 141 | for(let method of botMethods) { 142 | it(method, function() { 143 | expect(objBot).to.have.property(method).that.is.a("function"); 144 | }); 145 | } 146 | 147 | }); 148 | 149 | //-----------------]> 150 | 151 | describe("Methods", function() { 152 | 153 | it("enable/enabled/disable/disabled", function() { 154 | const key = "test"; 155 | 156 | //--------]> 157 | 158 | expect(objBot.disabled(key)).to.equal(true); 159 | expect(objBot.enabled(key)).to.equal(false); 160 | 161 | objBot.enable(key); 162 | 163 | expect(objBot.disabled(key)).to.equal(false); 164 | expect(objBot.enabled(key)).to.equal(true); 165 | 166 | objBot.disable(key); 167 | 168 | expect(objBot.disabled(key)).to.equal(true); 169 | expect(objBot.enabled(key)).to.equal(false); 170 | }); 171 | 172 | it("token", function() { 173 | const key = "test", 174 | shToken = token; 175 | 176 | //--------]> 177 | 178 | expect(objBot.token()).to.equal(token); 179 | 180 | objBot.token(key); 181 | 182 | expect(objBot.token()).to.equal(key); 183 | 184 | objBot.token(shToken); 185 | 186 | expect(objBot.token()).to.equal(token); 187 | }); 188 | 189 | it("render", function() { 190 | let r, 191 | 192 | templateObject = "{name} {x}", 193 | templateArray = "{0} {1}", 194 | 195 | equal = "MiElPotato 13"; 196 | 197 | const inputObject = {"name": "MiElPotato", "x": 13}, 198 | inputArray = ["MiElPotato", 13]; 199 | 200 | //--------]> 201 | 202 | r = objBot.render(templateObject, inputObject); 203 | expect(r).to.equal(equal); 204 | 205 | r = objBot.render(templateArray, inputArray); 206 | expect(r).to.equal(equal); 207 | 208 | //--------]> 209 | 210 | r = objBot.render(templateObject); 211 | expect(r).to.equal(templateObject); 212 | 213 | r = objBot.render(templateArray); 214 | expect(r).to.equal(templateArray); 215 | 216 | //--------]> 217 | 218 | r = objBot.render(); 219 | expect(r).to.equal(""); 220 | 221 | r = objBot.render(); 222 | expect(r).to.equal(""); 223 | }); 224 | 225 | //------)> 226 | 227 | it("keyboard", function() { 228 | let buttons; 229 | 230 | //-----]> 231 | 232 | expect(keyboard).to.be.a("function"); 233 | 234 | //-----]> 235 | 236 | buttons = keyboard.hOx; 237 | 238 | expect(buttons).to.be.a("function"); 239 | 240 | expect(buttons()).to.have.property("keyboard").that.is.an("array"); 241 | expect(buttons(true)).to.have.property("one_time_keyboard").that.is.equal(true); 242 | expect(buttons(false, true)).to.have.property("selective").that.is.equal(true); 243 | 244 | //-----]> 245 | 246 | buttons = keyboard("X Y Z"); 247 | 248 | expect(buttons).to.be.an("object").and.not.equal(null); 249 | 250 | expect(buttons).to.have.property("keyboard").that.is.an("array"); 251 | 252 | //-----]> 253 | 254 | buttons = keyboard([["X"]], "resize once selective"); 255 | 256 | expect(buttons).to.be.an("object").and.not.equal(null); 257 | 258 | expect(buttons).to.have.property("keyboard").that.is.an("array"); 259 | expect(buttons).to.have.property("resize_keyboard"); 260 | expect(buttons).to.have.property("one_time_keyboard"); 261 | expect(buttons).to.have.property("selective"); 262 | 263 | //-----]> 264 | 265 | buttons = keyboard.hide(); 266 | buttons.selective = true; 267 | buttons.resize_keyboard = true; 268 | 269 | expect(keyboard.hide()).to.deep.equal(keyboard()); 270 | expect(keyboard()).to.deep.equal(keyboard(false)); 271 | expect(keyboard(false, "selective resize")).to.deep.equal(buttons); 272 | 273 | }); 274 | 275 | //------)> 276 | 277 | 278 | describe("parseCmd | normal-strict", function() { 279 | const cmdSelf = "/12345678901234567890123456789012", 280 | 281 | cmdName = "12345678901234567890123456789012", 282 | cmdText = "[text]"; 283 | 284 | let cmdType = "common"; 285 | let normalCmds = [ 286 | "/12345678901234567890123456789012 [text]", 287 | "/12345678901234567890123456789012@bot [text]", 288 | "@bot /12345678901234567890123456789012 [text]" 289 | ]; 290 | 291 | //-----]> 292 | 293 | expect(parseCmd).to.be.a("function"); 294 | 295 | //-----]> 296 | 297 | for(let cmd of normalCmds) { 298 | it(cmd, function(cmd, cmdType) { 299 | let t = parseCmd(cmd, true); 300 | 301 | expect(t).to.be.an("object").and.not.equal(null); 302 | 303 | expect(t).to.have.property("type").that.is.equal(cmdType); 304 | expect(t).to.have.property("name").that.is.equal(cmdName); 305 | expect(t).to.have.property("text").that.is.equal(cmdText); 306 | expect(t).to.have.property("cmd").that.is.equal(cmdSelf); 307 | }.bind(this, cmd, cmdType)); 308 | 309 | cmdType = "private"; 310 | } 311 | 312 | //--------[@]-------}> 313 | 314 | cmdType = "common"; 315 | normalCmds = [ 316 | "/12345678901234567890123456789012 /[text]", 317 | "/12345678901234567890123456789012@bot /[text]", 318 | "@bot /12345678901234567890123456789012 /[text]" 319 | ]; 320 | 321 | //-----]> 322 | 323 | for(let cmd of normalCmds) { 324 | it(cmd, function(cmd, cmdType) { 325 | let t = parseCmd(cmd, true); 326 | 327 | expect(t).to.be.an("object").and.not.equal(null); 328 | 329 | expect(t).to.have.property("type").that.is.equal(cmdType); 330 | expect(t).to.have.property("name").that.is.equal(cmdName); 331 | expect(t).to.have.property("text").that.is.equal("/" + cmdText); 332 | expect(t).to.have.property("cmd").that.is.equal(cmdSelf); 333 | }.bind(this, cmd, cmdType)); 334 | 335 | cmdType = "private"; 336 | } 337 | 338 | //--------[Empty]-------}> 339 | 340 | cmdType = "common"; 341 | normalCmds = [ 342 | "/ /[text]", 343 | 344 | "/@bot /[text]", 345 | "@bot / /[text]" 346 | ]; 347 | 348 | //-----]> 349 | 350 | for(let cmd of normalCmds) { 351 | it(cmd, function(cmdType) { 352 | let t = parseCmd(cmd, true); 353 | 354 | expect(t).to.be.an("object").and.not.equal(null); 355 | 356 | expect(t).to.have.property("type").that.is.equal(cmdType); 357 | expect(t).to.have.property("name").that.is.equal(""); 358 | expect(t).to.have.property("text").that.is.equal("/" + cmdText); 359 | expect(t).to.have.property("cmd").that.is.equal("/"); 360 | }.bind(this, cmdType)); 361 | 362 | cmdType = "private"; 363 | } 364 | 365 | //----------)> 366 | 367 | cmdType = "common"; 368 | normalCmds = [ 369 | "/ [text]", 370 | 371 | "/@bot [text]", 372 | "@bot / [text]" 373 | ]; 374 | 375 | //-----]> 376 | 377 | for(let cmd of normalCmds) { 378 | it(cmd, function(cmdType) { 379 | let t = parseCmd(cmd, true); 380 | 381 | expect(t).to.be.an("object").and.not.equal(null); 382 | 383 | expect(t).to.have.property("type").that.is.equal(cmdType); 384 | expect(t).to.have.property("name").that.is.equal(""); 385 | expect(t).to.have.property("text").that.is.equal(cmdText); 386 | expect(t).to.have.property("cmd").that.is.equal("/"); 387 | }.bind(this, cmdType)); 388 | 389 | cmdType = "private"; 390 | } 391 | }); 392 | 393 | describe("parseCmd | fake-strict", function() { 394 | const fakeCmds = [ 395 | "/123456789012345678901234567890123 [text]", 396 | "/123456789012345678901234567890123@bot [text]", 397 | "@bot /123456789012345678901234567890123 [text]", 398 | "/123456789012345678901234567890123 /[text]", 399 | "/123456789012345678901234567890123@bot /[text]", 400 | "@bot /123456789012345678901234567890123 /[text]" 401 | ]; 402 | 403 | //-----]> 404 | 405 | expect(parseCmd).to.be.a("function"); 406 | 407 | //-----]> 408 | 409 | for(let cmd of fakeCmds) { 410 | it(cmd, function() { 411 | expect(parseCmd(cmd, true)).to.be.null; 412 | }); 413 | } 414 | }); 415 | 416 | describe("parseCmd | normal", function() { 417 | const cmdSelf = "/start", 418 | cmdName = "start", 419 | cmdText = "[text]"; 420 | 421 | let cmdType = "common"; 422 | let normalCmds = [ 423 | "/start [text]", "/start@bot [text]", "@bot /start [text]" 424 | ]; 425 | 426 | //-----]> 427 | 428 | expect(parseCmd).to.be.a("function"); 429 | 430 | //-----]> 431 | 432 | for(let cmd of normalCmds) { 433 | it(cmd, function(cmd, cmdType) { 434 | let t = parseCmd(cmd); 435 | 436 | expect(t).to.be.an("object").and.not.equal(null); 437 | 438 | expect(t).to.have.property("type").that.is.equal(cmdType); 439 | expect(t).to.have.property("name").that.is.equal(cmdName); 440 | expect(t).to.have.property("text").that.is.equal(cmdText); 441 | expect(t).to.have.property("cmd").that.is.equal(cmdSelf); 442 | }.bind(this, cmd, cmdType)); 443 | 444 | cmdType = "private"; 445 | } 446 | 447 | //--------[@]-------}> 448 | 449 | cmdType = "common"; 450 | normalCmds = [ 451 | "/start /[text]", "/start@bot /[text]", "@bot /start /[text]" 452 | ]; 453 | 454 | //-----]> 455 | 456 | for(let cmd of normalCmds) { 457 | it(cmd, function(cmd, cmdType) { 458 | let t = parseCmd(cmd); 459 | 460 | expect(t).to.be.an("object").and.not.equal(null); 461 | 462 | expect(t).to.have.property("type").that.is.equal(cmdType); 463 | expect(t).to.have.property("name").that.is.equal(cmdName); 464 | expect(t).to.have.property("text").that.is.equal("/" + cmdText); 465 | expect(t).to.have.property("cmd").that.is.equal(cmdSelf); 466 | }.bind(this, cmd, cmdType)); 467 | 468 | cmdType = "private"; 469 | } 470 | }); 471 | 472 | describe("parseCmd | fake", function() { 473 | const fakeCmds = [ 474 | "@bot [text]", 475 | "@ [text]", "@ / [text]", " / [text]" 476 | ]; 477 | 478 | //-----]> 479 | 480 | expect(parseCmd).to.be.a("function"); 481 | 482 | //-----]> 483 | 484 | for(let cmd of fakeCmds) { 485 | it(cmd, function() { 486 | expect(parseCmd(cmd)).to.be.null; 487 | }); 488 | } 489 | }); 490 | 491 | //-----------------]> 492 | 493 | it("call(getMe-2args) | callback", function(done) { 494 | objBot 495 | .call("getMe", function(error, result, response) { 496 | checkWithoutError(error); 497 | 498 | expect(result).to.be.an.instanceof(Buffer); 499 | expect(response).to.be.an("object").and.not.equal(null); 500 | 501 | done(); 502 | }); 503 | }); 504 | 505 | it("call(getMe-data:null) | callback", function(done) { 506 | objBot 507 | .call("getMe", null, function(error, result, response) { 508 | checkWithoutError(error); 509 | 510 | expect(result).to.be.an.instanceof(Buffer); 511 | expect(response).to.be.an("object").and.not.equal(null); 512 | 513 | done(); 514 | }); 515 | }); 516 | 517 | it("call(getFile) | callback", function(done) { 518 | objBot 519 | .call("getFile", { 520 | "file_id": "AgADAgAD1qcxG2_R8AbjPe6-AjgFdozGWSoABAE2Gi-3QnhSD7wBAAEC" 521 | }, function(error, result, response) { 522 | checkWithoutError(error); 523 | 524 | expect(result).to.be.an.instanceof(Buffer); 525 | expect(response).to.be.an("object").and.not.equal(null); 526 | 527 | done(); 528 | }); 529 | }); 530 | 531 | it("call(getFile-file_id:zero) | callback", function(done) { 532 | objBot 533 | .call("getFile", { 534 | "file_id": "0" 535 | }, function(error, result, response) { 536 | checkWithoutError(error); 537 | 538 | expect(result).to.be.an.instanceof(Buffer); 539 | expect(response).to.be.an("object").and.not.equal(null); 540 | 541 | done(); 542 | }); 543 | }); 544 | 545 | it("call(getFile) | callback-undefined", function() { 546 | objBot 547 | .call("getFile", { 548 | "file_id": "AgADAgAD1qcxG2_R8AbjPe6-AjgFdozGWSoABAE2Gi-3QnhSD7wBAAEC" 549 | }); 550 | }); 551 | 552 | it("call(getFile-empty) | callback-undefined", function() { 553 | objBot 554 | .call("getFile", { 555 | "file_id": "0" 556 | }); 557 | }); 558 | 559 | 560 | it("callJson(getMe-2args) | callback", function(done) { 561 | objBot 562 | .callJson("getMe", function(error, result, response) { 563 | checkWithoutError(error); 564 | 565 | expect(result).to.be.an("object").and.not.equal(null); 566 | expect(response).to.be.an("object").and.not.equal(null); 567 | 568 | expect(result).to.have.property("ok").to.equal(true); 569 | expect(result).to.have.property("result").to.be.an("object").and.not.equal(null); 570 | 571 | done(); 572 | }); 573 | }); 574 | 575 | it("callJson(getMe-data:null) | callback", function(done) { 576 | objBot 577 | .callJson("getMe", null, function(error, result, response) { 578 | checkWithoutError(error); 579 | 580 | expect(result).to.be.an("object").and.not.equal(null); 581 | expect(response).to.be.an("object").and.not.equal(null); 582 | 583 | expect(result).to.have.property("ok").to.equal(true); 584 | expect(result).to.have.property("result").to.be.an("object").and.not.equal(null); 585 | 586 | done(); 587 | }); 588 | }); 589 | 590 | it("callJson(getFile) | callback", function(done) { 591 | objBot 592 | .callJson("getFile", { 593 | "file_id": "AgADAgAD1qcxG2_R8AbjPe6-AjgFdozGWSoABAE2Gi-3QnhSD7wBAAEC" 594 | }, function(error, result) { 595 | checkWithoutError(error); 596 | 597 | expect(result).to.be.an("object").and.not.equal(null); 598 | 599 | expect(result).to.have.property("ok").to.equal(true); 600 | expect(result).to.have.property("result").to.be.an("object").and.not.equal(null); 601 | 602 | done(); 603 | }); 604 | }); 605 | 606 | //-----------------]> 607 | 608 | it("download(stream) | callback", function(done) { 609 | objBot 610 | .download("BQADAgADEwADb9HwBoAUlahFM8WGAg", function(error, info) { 611 | checkWithoutError(error); 612 | 613 | expect(info).to.be.an("object").and.not.equal(null); 614 | 615 | expect(info).to.have.property("id"); 616 | expect(info).to.have.property("file"); 617 | expect(info).to.have.property("size"); 618 | expect(info).to.have.property("stream"); 619 | 620 | done(); 621 | }); 622 | }); 623 | 624 | it("download(stream) | promise", function(done) { 625 | objBot 626 | .download("BQADAgADEwADb9HwBoAUlahFM8WGAg") 627 | .then(function(info) { 628 | expect(info).to.be.an("object").and.not.equal(null); 629 | 630 | expect(info).to.have.property("id"); 631 | expect(info).to.have.property("file"); 632 | expect(info).to.have.property("size"); 633 | expect(info).to.have.property("stream"); 634 | 635 | done(); 636 | }, function(error) { 637 | checkWithoutError(error, done); 638 | }); 639 | }); 640 | 641 | }); 642 | 643 | //-----------------]> 644 | 645 | describe("API", function() { 646 | 647 | it("forwardMessage | callback", function(done) { 648 | api.forwardMessage({ 649 | "chat_id": chatId, 650 | "from_chat_id": chatId, 651 | "message_id": msgId 652 | }, function(error, data) { 653 | checkBaseFields(error, data); 654 | 655 | expect(data).to.have.property("forward_from").that.is.an("object"); 656 | 657 | done(); 658 | }); 659 | }); 660 | 661 | //--)> 662 | 663 | it("sendMessage | callback", function(done) { 664 | let data = { 665 | "chat_id": chatId, 666 | "text": "*bold Just* _italic markdown_ [XIII](db.gg)", 667 | "parse_mode": "markdown" 668 | }; 669 | 670 | let dataClone = jsonClone(data); 671 | 672 | api.sendMessage(data, function(error, result) { 673 | checkSendMessage(error, result); 674 | 675 | expect(data).to.deep.equal(dataClone); 676 | 677 | done(); 678 | }); 679 | }); 680 | 681 | it("sendMessage(file-stream) | callback", function(done) { 682 | api.sendMessage({ 683 | "chat_id": chatId, 684 | "text": rFs.createReadStream(__dirname + "/data/msg.json") 685 | }, function(error, result) { 686 | checkBaseFields(error, result); 687 | 688 | expect(result).to.have.property("text").that.is.an("string"); 689 | 690 | done(); 691 | }); 692 | }); 693 | 694 | it("sendMessage(data-map) | callback", function(done) { 695 | const data = new Map([ 696 | ["chat_id", chatId], 697 | ["text", "data-map"] 698 | ]); 699 | 700 | api.sendMessage(data, function(error, result) { 701 | checkBaseFields(error, result); 702 | 703 | expect(result).to.have.property("text").that.is.an("string"); 704 | 705 | midEditText = result.message_id; 706 | 707 | done(); 708 | }); 709 | }); 710 | 711 | //--)> 712 | 713 | it("sendPhoto(file) | array | callback", function(done) { 714 | api.sendPhoto([ 715 | chatId, 716 | __dirname + "/data/MiElPotato.jpg", 717 | "MiElPotato" 718 | ], function(error, result) { 719 | checkBaseFields(error, result); 720 | 721 | expect(result).to.have.property("photo").that.is.an("array"); 722 | 723 | midEditCaption = result.message_id; 724 | 725 | done(); 726 | }); 727 | }); 728 | 729 | it("sendPhoto(file-stream) | callback", function(done) { 730 | api.sendPhoto({ 731 | "chat_id": chatId, 732 | "photo": rFs.createReadStream(__dirname + "/data/MiElPotato.jpg"), 733 | "caption": "MiElPotato" 734 | }, function(error, data) { 735 | checkBaseFields(error, data); 736 | 737 | expect(data).to.have.property("photo").that.is.an("array"); 738 | 739 | midDel = data.message_id; 740 | 741 | done(); 742 | }); 743 | }); 744 | 745 | it("sendPhoto(file-buffer) | callback", function(done) { 746 | api.sendPhoto({ 747 | "chat_id": chatId, 748 | "photo": rFs.readFileSync(__dirname + "/data/MiElPotato.jpg"), 749 | "caption": "MiElPotato", 750 | 751 | "filename": "MiElPotato.jpg" // <-- It is important 752 | }, function(error, data) { 753 | checkBaseFields(error, data); 754 | 755 | expect(data).to.have.property("photo").that.is.an("array"); 756 | 757 | done(); 758 | }); 759 | }); 760 | 761 | it("sendPhoto(file_id) | callback", function(done) { 762 | api.sendPhoto({ 763 | "chat_id": chatId, 764 | "photo": "AgADAgAD1qcxG2_R8AbjPe6-AjgFdozGWSoABAE2Gi-3QnhSD7wBAAEC" 765 | }, function(error, data) { 766 | checkBaseFields(error, data); 767 | 768 | expect(data).to.have.property("photo").that.is.an("array"); 769 | 770 | done(); 771 | }); 772 | }); 773 | 774 | //--)> 775 | 776 | it("sendAudio(url) | callback", function(done) { 777 | api.sendAudio({ 778 | "chat_id": chatId, 779 | "audio": "http://upload.wikimedia.org/wikipedia/en/4/45/ACDC_-_Back_In_Black-sample.ogg", 780 | "performer": "ACDC", 781 | "title": "Back_In_Black", 782 | "duration": 1 // <-- Sec. 783 | }, function(error, data) { 784 | checkBaseFields(error, data); 785 | 786 | // https://core.telegram.org/bots/api#sendaudio 787 | expect(data).to.have.property("voice").that.is.an("object"); 788 | 789 | done(); 790 | }); 791 | }); 792 | 793 | it("sendDocument(url) | callback", function(done) { 794 | api.sendDocument({ 795 | "chat_id": chatId, 796 | "document": "https://www.google.ru/images/logos/ps_logo2.png" 797 | }, function(error, data) { 798 | checkBaseFields(error, data); 799 | 800 | expect(data).to.have.property("document").that.is.an("object"); 801 | 802 | done(); 803 | }); 804 | }); 805 | 806 | it("sendDocument(url-redirect) | callback", function(done) { 807 | api.sendDocument({ 808 | "chat_id": chatId, 809 | "document": "https://jigsaw.w3.org/HTTP/300/301.html" 810 | }, function(error, data) { 811 | checkBaseFields(error, data); 812 | 813 | expect(data).to.have.property("document").that.is.an("object"); 814 | 815 | done(); 816 | }); 817 | }); 818 | 819 | it("sendSticker(url) | callback", function(done) { 820 | api.sendSticker({ 821 | "chat_id": chatId, 822 | "sticker": "https://www.google.ru/images/logos/ps_logo2.png" 823 | }, function(error, data) { 824 | checkBaseFields(error, data); 825 | 826 | expect(data).to.have.property("sticker").that.is.an("object"); 827 | 828 | done(); 829 | }); 830 | }); 831 | 832 | it("sendVideo(url) | callback", function(done) { 833 | api.sendVideo({ 834 | "chat_id": chatId, 835 | "video": "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4", 836 | "duration": 60 837 | }, function(error, data) { 838 | checkBaseFields(error, data); 839 | 840 | expect(data).to.have.property("video").that.is.an("object"); 841 | 842 | done(); 843 | }); 844 | }); 845 | 846 | it("sendVoice(url) | callback", function(done) { 847 | api.sendVoice({ 848 | "chat_id": chatId, 849 | "voice": "http://upload.wikimedia.org/wikipedia/en/4/45/ACDC_-_Back_In_Black-sample.ogg" 850 | }, function(error, result) { 851 | checkBaseFields(error, result); 852 | 853 | expect(result).to.have.property("voice").that.is.an("object"); 854 | 855 | done(); 856 | }); 857 | }); 858 | 859 | it("sendLocation | callback", function(done) { 860 | api.sendLocation({ 861 | "chat_id": chatId, 862 | "latitude": "57.0061726", 863 | "longitude": "40.9821055" 864 | }, function(error, result) { 865 | checkSendLocation(error, result); 866 | done(); 867 | }); 868 | }); 869 | 870 | it("sendVenue | callback", function(done) { 871 | api.sendVenue({ 872 | "chat_id": chatId, 873 | "latitude": "57.0061726", 874 | "longitude": "40.9821055", 875 | "title": "ATK", 876 | "address": "Potatov Str." 877 | }, function(error, result) { 878 | checkBaseFields(error, result); 879 | done(); 880 | }); 881 | }); 882 | 883 | it("sendContact | callback", function(done) { 884 | api.sendContact({ 885 | "chat_id": chatId, 886 | "phone_number": "+8 888 888 88 88", 887 | "first_name": "Potatov Str." 888 | }, function(error, result) { 889 | checkBaseFields(error, result); 890 | done(); 891 | }); 892 | }); 893 | 894 | it("sendChatAction | callback", function(done) { 895 | api.sendChatAction({ 896 | "chat_id": chatId, 897 | "action": "typing" 898 | }, function(error, isOk) { 899 | checkWithoutError(error); 900 | expect(isOk).to.be.a("boolean"); 901 | 902 | done(); 903 | }); 904 | }); 905 | 906 | it("sendVideoNote(url) | callback", function(done) { 907 | api.sendVideoNote({ 908 | "chat_id": chatId, 909 | "video_note": "http://www.quirksmode.org/html5/videos/big_buck_bunny.mp4", 910 | "duration": 10, 911 | "length": 320 912 | }, function(error, data) { 913 | checkBaseFields(error, data); 914 | 915 | expect(data).to.have.property("video").that.is.an("object"); 916 | 917 | done(); 918 | }); 919 | }); 920 | 921 | //-----)> 922 | 923 | it("editMessageText | callback", function(done) { 924 | const text = "EDITED"; 925 | 926 | api.editMessageText({ 927 | "chat_id": chatId, 928 | "message_id": midEditText, 929 | "text": text 930 | }, function(error, result) { 931 | checkWithoutError(error); 932 | 933 | expect(result.text).to.be.a("string").and.equal(text); 934 | 935 | done(); 936 | }); 937 | }); 938 | 939 | it("editMessageCaption | callback", function(done) { 940 | const caption = "EDITED"; 941 | 942 | api.editMessageCaption({ 943 | "chat_id": chatId, 944 | "message_id": midEditCaption, 945 | "caption": caption 946 | }, function(error, result) { 947 | checkWithoutError(error); 948 | 949 | expect(result.caption).to.be.a("string").and.equal(caption); 950 | 951 | done(); 952 | }); 953 | }); 954 | 955 | //-----)> 956 | 957 | it("setWebhook (set) | promise", function(done) { 958 | api 959 | .setWebhook({"url": "https://db.gg/null"}) 960 | .then(function(isOk) { 961 | expect(isOk).to.be.a("boolean").to.equal(true); 962 | 963 | done(); 964 | }, function(error) { 965 | checkWithoutError(error, done); 966 | }); 967 | }); 968 | 969 | //-----)> 970 | 971 | it("deleteMessage | promise", function(done) { 972 | api 973 | .deleteMessage({"chat_id": chatId, "message_id": midDel}) 974 | .then(function(isOk) { 975 | expect(isOk).to.be.a("boolean").to.equal(true); 976 | 977 | done(); 978 | }, function(error) { 979 | checkWithoutError(error, done); 980 | }); 981 | }); 982 | 983 | it("deleteWebhook | promise", function(done) { 984 | api 985 | .deleteWebhook() 986 | .then(function(isOk) { 987 | expect(isOk).to.be.a("boolean").to.equal(true); 988 | 989 | done(); 990 | }, function(error) { 991 | checkWithoutError(error, done); 992 | }); 993 | }); 994 | 995 | //-----)> 996 | 997 | it("getUserProfilePhotos | callback", function(done) { 998 | api.getUserProfilePhotos({ 999 | "user_id": chatId 1000 | }, function(error, data) { 1001 | checkWithoutError(error); 1002 | 1003 | expect(data).to.have.property("photos").that.is.an("array"); 1004 | 1005 | done(); 1006 | }); 1007 | }); 1008 | 1009 | it("getUpdates | callback", function(done) { 1010 | api.getUpdates(function(error, data) { 1011 | checkWithoutError(error); 1012 | expect(data).to.be.a("array"); 1013 | 1014 | done(); 1015 | }); 1016 | }); 1017 | 1018 | //--)> 1019 | 1020 | it("getFile | callback", function(done) { 1021 | api.getFile({ 1022 | "file_id": "AgADAgAD1qcxG2_R8AbjPe6-AjgFdozGWSoABAE2Gi-3QnhSD7wBAAEC" 1023 | }, function(error, data) { 1024 | checkWithoutError(error); 1025 | expect(data).to.be.an("object").and.not.equal(null); 1026 | 1027 | expect(data).to.have.property("file_id"); 1028 | 1029 | done(); 1030 | }); 1031 | }); 1032 | 1033 | //--)> 1034 | 1035 | it("getMe | callback", function(done) { 1036 | api.getMe(function(error, data) { 1037 | checkWithoutError(error); 1038 | expect(data).to.be.an("object").and.not.equal(null); 1039 | 1040 | expect(data).to.have.property("id").that.is.an("number"); 1041 | expect(data).to.have.property("first_name").that.is.an("string"); 1042 | expect(data).to.have.property("username").that.is.an("string"); 1043 | 1044 | done(); 1045 | }); 1046 | }); 1047 | 1048 | it("getWebhookInfo | promise", function(done) { 1049 | api 1050 | .getWebhookInfo() 1051 | .then(function(data) { 1052 | expect(data).to.be.an("object").and.not.equal(null); 1053 | 1054 | expect(data).to.have.property("url").that.is.an("string"); 1055 | 1056 | done(); 1057 | }, function(error) { 1058 | checkWithoutError(error, done); 1059 | }); 1060 | }); 1061 | 1062 | }); 1063 | 1064 | }); 1065 | 1066 | //----------------------------------]> 1067 | 1068 | function checkSendMessage(error, data) { 1069 | checkBaseFields(error, data); 1070 | expect(data).to.have.property("text").that.is.an("string"); 1071 | } 1072 | 1073 | function checkSendLocation(error, data) { 1074 | checkBaseFields(error, data); 1075 | expect(data).to.have.property("location").that.is.an("object"); 1076 | } 1077 | 1078 | //-----------]> 1079 | 1080 | /* 1081 | function checkWithError(error, done) { 1082 | if(done) { 1083 | setTimeout(function() { 1084 | expect(error).to.be.an.instanceof(Error); 1085 | done(); 1086 | }, 0); 1087 | } 1088 | else { 1089 | expect(error).to.be.an.instanceof(Error); 1090 | } 1091 | } 1092 | */ 1093 | 1094 | function checkWithoutError(error, done) { 1095 | if(done) { 1096 | setTimeout(function() { 1097 | expect(error).to.be.null; 1098 | done(); 1099 | }, 0); 1100 | } 1101 | else { 1102 | expect(error).to.be.null; 1103 | } 1104 | } 1105 | 1106 | //-----------]> 1107 | 1108 | function checkBaseFields(error, data) { 1109 | checkWithoutError(error); 1110 | 1111 | expect(data).to.be.an("object").and.not.equal(null); 1112 | 1113 | expect(data).to.have.property("message_id"); 1114 | expect(data).to.have.property("from").that.is.an("object"); 1115 | expect(data).to.have.property("chat").that.is.an("object"); 1116 | expect(data).to.have.property("date"); 1117 | } 1118 | 1119 | //-------[HELPERS]-------}> 1120 | 1121 | function jsonClone(data) { 1122 | return JSON.parse(JSON.stringify(data)); 1123 | } -------------------------------------------------------------------------------- /test/responseBuilder.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | /*global describe, it*/ 10 | 11 | "use strict"; 12 | 13 | //----------------------------------------------------- 14 | 15 | const rChai = require("chai"); 16 | 17 | const expect = rChai.expect; 18 | 19 | const rBot = require("./../index"); 20 | 21 | const CResponseBuilder = require("./../src/server/responseBuilder"); 22 | 23 | //----------------------------------------------------- 24 | 25 | const token = process.env.TELEGRAM_BOT_TOKEN, 26 | chatId = process.env.TELEGRAM_CHAT_ID, 27 | msgId = process.env.TELEGRAM_MSG_ID; 28 | 29 | const objBot = rBot(token); 30 | 31 | const gBotReq = { 32 | "cid": chatId, 33 | "mid": msgId 34 | }; 35 | 36 | const gCreateRB = function() { return new CResponseBuilder(gBotReq, objBot); }; 37 | 38 | const gElements = [ 39 | "text", "photo", "audio", "document", "sticker", "video", "voice", "videoNote", "location", "venue", "contact", "chatAction", "game", "invoice", 40 | 41 | "inlineQuery", "callbackQuery", 42 | "shippingQuery", "preCheckoutQuery", 43 | 44 | "markdown", "html" 45 | ]; 46 | 47 | const gModifiers = [ 48 | "keyboard", 49 | 50 | "maxSize", "filename", 51 | 52 | "latitude", "longitude", 53 | "caption", "duration", "performer", "title", "width", "height", 54 | "gameShortName", 55 | 56 | "disableWebPagePreview", "disableNotification", 57 | "showAlert", 58 | "cacheTime", "nextOffset", "switchPmText", "switchPmParameter", 59 | 60 | "phoneNumber", "firstName", "lastName", 61 | "parseMode", "replyMarkup", 62 | 63 | "replyToMessageId", "messageId", "inlineMessageId" 64 | ]; 65 | 66 | //----------------------------------------------------- 67 | 68 | expect(token).to.exist; 69 | expect(chatId).to.exist; 70 | expect(msgId).to.exist; 71 | 72 | //----------------------------------------------------- 73 | 74 | describe("CResponseBuilder", function() { 75 | this.timeout(1000 * 10); 76 | 77 | //-----------------]> 78 | 79 | describe("Methods", function() { 80 | const rb = gCreateRB(); 81 | 82 | ["send", "isReply"] 83 | .concat(gElements, gModifiers) 84 | .forEach(function(method) { 85 | it(method, function() { 86 | expect(rb).to.have.property(method).that.is.an("function"); 87 | }); 88 | }); 89 | }); 90 | 91 | //-----------------]> 92 | 93 | describe("Build", function() { 94 | 95 | describe("Elements", function() { 96 | const rb = gCreateRB(); 97 | 98 | gElements 99 | .forEach(function(element) { 100 | it(element, function() { 101 | const func = rb[element]; 102 | 103 | expect(func).to.be.a("function"); 104 | 105 | //-------]> 106 | 107 | const r = rb[element](); 108 | 109 | expect(r).to.equal(rb); 110 | }); 111 | }); 112 | }); 113 | 114 | describe("Modifiers", function() { 115 | const rb = gCreateRB().text("test"); 116 | 117 | gModifiers 118 | .forEach(function(modifier) { 119 | it(modifier, function() { 120 | const func = rb[modifier]; 121 | 122 | expect(func).to.be.a("function"); 123 | 124 | //-------]> 125 | 126 | const r = rb[modifier](); 127 | 128 | expect(r).to.equal(rb); 129 | }); 130 | }); 131 | }); 132 | 133 | }); 134 | 135 | //-----------------]> 136 | 137 | describe("Send", function() { 138 | const rb = gCreateRB(); 139 | 140 | it("send(photo-url) | promise", function(done) { 141 | const url = "https://www.google.ru/images/logos/ps_logo2.png"; 142 | 143 | //----------]> 144 | 145 | rb 146 | .chatAction("typing") 147 | .photo(url) 148 | .keyboard("1 2 3"); 149 | 150 | //----------]> 151 | 152 | let lastElement = rb.lastElement; 153 | 154 | expect(lastElement.reply_markup).to.deep.equal(objBot.keyboard("1 2 3")); 155 | 156 | //----------]> 157 | 158 | rb.send() 159 | .then(function(result) { 160 | expect(result).to.be.a("array"); 161 | 162 | done(); 163 | }, function(error) { 164 | checkError(error); 165 | done(); 166 | }); 167 | }); 168 | 169 | it("send(text) | callback", function(done) { 170 | let lastElement = rb.lastElement; 171 | 172 | //----------]> 173 | 174 | expect(rb.queue).to.equal(null); 175 | expect(lastElement).to.equal(null); 176 | 177 | //----------]> 178 | 179 | rb 180 | .text("test") 181 | .keyboard(); 182 | 183 | lastElement = rb.lastElement; 184 | 185 | //----------]> 186 | 187 | expect(lastElement).to.be.a("object"); 188 | expect(lastElement.reply_markup).to.deep.equal(objBot.keyboard()); 189 | 190 | //----------]> 191 | 192 | rb 193 | .send(function(error, result) { 194 | checkSendMessage(error, result); 195 | 196 | done(); 197 | }); 198 | }); 199 | 200 | it("send(text).oneElem | callback", function(done) { 201 | gCreateRB() 202 | .text("test") 203 | .send(function(error, result) { 204 | checkSendMessage(error, result); 205 | 206 | done(); 207 | }); 208 | }); 209 | 210 | it("send(location).oneElem | callback", function(done) { 211 | gCreateRB() 212 | .location(50, 60) 213 | .send(function(error, result) { 214 | checkBaseFields(error, result); 215 | 216 | done(); 217 | }); 218 | }); 219 | 220 | it("send(markdown).oneElem | callback", function(done) { 221 | gCreateRB() 222 | .markdown("*TEST*", false, true) 223 | .send(function(error, result) { 224 | checkBaseFields(error, result); 225 | 226 | done(); 227 | }); 228 | }); 229 | 230 | }); 231 | }); 232 | 233 | //----------------------------------]> 234 | 235 | function checkSendMessage(error, data) { 236 | checkBaseFields(error, data); 237 | expect(data).to.have.property("text").that.is.an("string"); 238 | } 239 | 240 | //-----------]> 241 | 242 | function checkError(error) { 243 | if(error) { 244 | expect(error).to.be.an.instanceof(Error); 245 | } 246 | 247 | setTimeout(function() { 248 | expect(error).to.be.null; 249 | }, 0); 250 | } 251 | 252 | //-----------]> 253 | 254 | function checkBaseFields(error, result) { 255 | checkError(error); 256 | 257 | expect(result).to.be.a("object"); 258 | 259 | expect(result).to.have.property("message_id"); 260 | expect(result).to.have.property("from").that.is.an("object"); 261 | expect(result).to.have.property("chat").that.is.an("object"); 262 | expect(result).to.have.property("date"); 263 | } -------------------------------------------------------------------------------- /test/srvHTTP.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | /*global describe, it*/ 10 | 11 | "use strict"; 12 | 13 | //----------------------------------------------------- 14 | 15 | const rChai = require("chai"); 16 | 17 | const expect = rChai.expect; 18 | 19 | const rBot = require("./../index"); 20 | 21 | //----------------------------------------------------- 22 | 23 | const token = process.env.TELEGRAM_BOT_TOKEN, 24 | chatId = process.env.TELEGRAM_CHAT_ID, 25 | msgId = process.env.TELEGRAM_MSG_ID; 26 | 27 | const objBot = rBot(token); 28 | 29 | //----------------------------------------------------- 30 | 31 | expect(token).to.exist; 32 | expect(chatId).to.exist; 33 | expect(msgId).to.exist; 34 | 35 | //----------------------------------------------------- 36 | 37 | describe("srv.http", function() { 38 | 39 | this.timeout(1000 * 10); 40 | 41 | //-----------------]> 42 | 43 | it("Base", function() { 44 | expect(objBot.http).to.be.a("function"); 45 | }); 46 | 47 | //-----------------]> 48 | 49 | it("Instance", function() { 50 | let servers = [ 51 | objBot.http({"ssl": false, "port": 1379}), 52 | objBot.http(function() { }), 53 | objBot.http({"ssl": false, "port": 1346}, function() { }) 54 | ]; 55 | 56 | servers.forEach(function(srv) { 57 | expect(srv).to.be.an("object").and.not.equal(null); 58 | 59 | expect(srv).to.have.property("app").that.is.an("object").and.not.equal(null); 60 | expect(srv).to.have.property("bot").that.is.a("function"); 61 | 62 | expect(srv).to.have.property("catch").that.is.a("function"); 63 | expect(srv).to.have.property("use").that.is.a("function"); 64 | expect(srv).to.have.property("on").that.is.a("function"); 65 | expect(srv).to.have.property("off").that.is.a("function"); 66 | }); 67 | }); 68 | 69 | }); 70 | -------------------------------------------------------------------------------- /test/srvPolling.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | /*global describe, it*/ 10 | 11 | "use strict"; 12 | 13 | //----------------------------------------------------- 14 | 15 | const rChai = require("chai"); 16 | 17 | const expect = rChai.expect; 18 | 19 | const rBot = require("./../index"); 20 | 21 | //----------------------------------------------------- 22 | 23 | const token = process.env.TELEGRAM_BOT_TOKEN, 24 | chatId = process.env.TELEGRAM_CHAT_ID, 25 | msgId = process.env.TELEGRAM_MSG_ID; 26 | 27 | const objBot = rBot(token); 28 | 29 | //----------------------------------------------------- 30 | 31 | expect(token).to.exist; 32 | expect(chatId).to.exist; 33 | expect(msgId).to.exist; 34 | 35 | //----------------------------------------------------- 36 | 37 | describe("srv.polling", function() { 38 | 39 | this.timeout(1000 * 10); 40 | 41 | //-----------------]> 42 | 43 | it("Base", function() { 44 | expect(objBot.polling).to.be.a("function"); 45 | }); 46 | 47 | //-----------------]> 48 | 49 | it("Instance", function() { 50 | const options = { 51 | "limit": 100, 52 | "timeout": 0, 53 | "interval": 10 54 | }; 55 | 56 | let servers = [ 57 | objBot.polling(), 58 | objBot.polling(function() { }), 59 | objBot.polling(options, function() { }) 60 | ]; 61 | 62 | servers.forEach(function(srv) { 63 | expect(srv).to.be.an("object").and.not.equal(null); 64 | 65 | expect(srv).to.have.property("start").that.is.a("function"); 66 | expect(srv).to.have.property("stop").that.is.a("function"); 67 | 68 | expect(srv).to.have.property("catch").that.is.a("function"); 69 | expect(srv).to.have.property("use").that.is.a("function"); 70 | expect(srv).to.have.property("on").that.is.a("function"); 71 | expect(srv).to.have.property("off").that.is.a("function"); 72 | 73 | srv.stop(); 74 | }); 75 | }); 76 | }); 77 | -------------------------------------------------------------------------------- /test/srvVirtual.js: -------------------------------------------------------------------------------- 1 | //----------------------------------------------------- 2 | // 3 | // Author: Daeren 4 | // Site: 666.io 5 | // 6 | //----------------------------------------------------- 7 | 8 | /*jshint expr: true*/ 9 | /*global describe, it*/ 10 | 11 | "use strict"; 12 | 13 | //----------------------------------------------------- 14 | 15 | const rChai = require("chai"); 16 | 17 | const expect = rChai.expect; 18 | 19 | const rBot = require("./../index"); 20 | 21 | //----------------------------------------------------- 22 | 23 | const token = process.env.TELEGRAM_BOT_TOKEN, 24 | chatId = process.env.TELEGRAM_CHAT_ID, 25 | msgId = process.env.TELEGRAM_MSG_ID; 26 | 27 | const objBot = rBot(token); 28 | 29 | const inputSrvMessage = { 30 | "update_id": 13, 31 | 32 | "message": { 33 | "message_id": msgId, 34 | 35 | "from": { 36 | "id": chatId, 37 | "first_name": "D", 38 | "username": "" 39 | }, 40 | 41 | "chat": { 42 | "id": chatId, 43 | "first_name": "D", 44 | "username": "", 45 | "type": "private" 46 | }, 47 | 48 | "date": Date.now(), 49 | "text": "Hello" 50 | } 51 | }; 52 | 53 | const inputSrvMessageCmd = { 54 | "update_id": 13, 55 | 56 | "message": { 57 | "message_id": msgId, 58 | 59 | "from": { 60 | "id": chatId, 61 | "first_name": "D", 62 | "username": "" 63 | }, 64 | 65 | "chat": { 66 | "id": chatId, 67 | "first_name": "D", 68 | "username": "", 69 | "type": "private" 70 | }, 71 | 72 | "date": Date.now(), 73 | "text": "/test hello" 74 | } 75 | }; 76 | 77 | //----------------------------------------------------- 78 | 79 | expect(token).to.exist; 80 | expect(chatId).to.exist; 81 | expect(msgId).to.exist; 82 | 83 | //----------------------------------------------------- 84 | 85 | describe("srv.virtual", function() { 86 | 87 | this.timeout(1000 * 10); 88 | 89 | //-----------------]> 90 | 91 | it("Base", function() { 92 | expect(objBot.virtual).to.be.a("function"); 93 | }); 94 | 95 | //-----------------]> 96 | 97 | it("Instance", function() { 98 | let servers = [ 99 | objBot.virtual(), 100 | objBot.virtual(function() { }) 101 | ]; 102 | 103 | servers.forEach(function(srv) { 104 | expect(srv).to.be.an("object").and.not.equal(null); 105 | 106 | expect(srv).to.have.property("input").that.is.a("function"); 107 | expect(srv).to.have.property("middleware").that.is.a("function"); 108 | 109 | expect(srv).to.have.property("catch").that.is.a("function"); 110 | expect(srv).to.have.property("use").that.is.a("function"); 111 | expect(srv).to.have.property("on").that.is.a("function"); 112 | expect(srv).to.have.property("off").that.is.a("function"); 113 | }); 114 | }); 115 | 116 | //----------]> 117 | 118 | it("Instance (base [function])", function(done) { 119 | let server = objBot.virtual(function(bot) { 120 | tCheckBaseBotFields(bot); 121 | done(); 122 | }); 123 | 124 | server.input(null, inputSrvMessage); 125 | }); 126 | 127 | it("Instance (base [function] | use)", function(done) { 128 | let server = objBot.virtual(function* (bot) { 129 | const result = yield proc(); 130 | 131 | expect(result).to.be.a("number").and.equal(13); 132 | expect(bot).to.have.property("xMeta").that.is.an("number").and.equal(5); 133 | 134 | tCheckBaseBotFields(bot); 135 | done(); 136 | }); 137 | 138 | //------]> 139 | 140 | server.use(function(bot) { 141 | bot.xMeta = 1; 142 | 143 | tCheckBaseBotFields(bot); 144 | }); 145 | 146 | server.use(function(bot, data, next) { 147 | expect(next).to.be.a("function"); 148 | expect(bot).to.have.property("xMeta").that.is.an("number").and.equal(1); 149 | 150 | bot.xMeta++; 151 | 152 | tCheckBaseBotFields(bot); 153 | next(); 154 | }); 155 | 156 | //------]> 157 | 158 | server.use("text", function(bot) { 159 | tCheckBaseBotFields(bot); 160 | }); 161 | 162 | server.use("text", function(bot, data, next) { 163 | expect(next).to.be.a("function"); 164 | expect(bot).to.have.property("xMeta").that.is.an("number").and.equal(2); 165 | 166 | bot.xMeta++; 167 | 168 | tCheckBaseBotFields(bot); 169 | next(); 170 | }); 171 | 172 | //------]> 173 | 174 | server.use(function* (bot) { 175 | const result = yield proc(); 176 | 177 | expect(result).to.be.a("number").and.equal(13); 178 | expect(bot).to.have.property("xMeta").that.is.an("number").and.equal(3); 179 | 180 | bot.xMeta++; 181 | 182 | tCheckBaseBotFields(bot); 183 | }); 184 | 185 | server.use("text", function* (bot) { 186 | const result = yield proc(); 187 | 188 | expect(result).to.be.a("number").and.equal(13); 189 | expect(bot).to.have.property("xMeta").that.is.an("number").and.equal(4); 190 | 191 | bot.xMeta++; 192 | 193 | tCheckBaseBotFields(bot); 194 | }); 195 | 196 | //------]> 197 | 198 | server.input(null, inputSrvMessage); 199 | 200 | //------]> 201 | 202 | function proc() { 203 | return new Promise(x => x(13)); 204 | } 205 | }); 206 | 207 | //----------]> 208 | 209 | it("Instance (event: regex)", function(done) { 210 | let server = objBot.virtual(function() { 211 | throw new Error("The message passed through the event | #1"); 212 | }); 213 | 214 | server.on(/(\w+)/, function(bot, params, next) { 215 | expect(params).to.be.a("array"); 216 | expect(params[0]).to.be.a("string").and.equal(bot.message.text); 217 | expect(params[1]).to.be.a("string").and.equal(bot.message.text); 218 | 219 | tCheckBaseBotFields(bot, next); // ~next 220 | done(); 221 | }); 222 | 223 | server.on("*", function() { 224 | throw new Error("The message passed through the event | #2"); 225 | }); 226 | 227 | server.on("/", function() { 228 | throw new Error("The message passed through the event | #3"); 229 | }); 230 | 231 | server.on("text", function() { 232 | throw new Error("The message passed through the event | #4"); 233 | }); 234 | 235 | 236 | server.input(null, inputSrvMessage); 237 | }); 238 | 239 | it("Instance (event: regex | map)", function(done) { 240 | let server = objBot.virtual(function() { 241 | throw new Error("The message passed through the event | #1"); 242 | }); 243 | 244 | server.on(/(\w+)/, ["myText"], function(bot, params) { 245 | expect(params).to.be.an("object"); 246 | expect(params.myText).to.be.a("string").and.equal(bot.message.text); 247 | 248 | tCheckBaseBotFields(bot); 249 | done(); 250 | }); 251 | 252 | server.on("*", function() { 253 | throw new Error("The message passed through the event | #2"); 254 | }); 255 | 256 | server.on("/", function() { 257 | throw new Error("The message passed through the event | #3"); 258 | }); 259 | 260 | server.on("text", function() { 261 | throw new Error("The message passed through the event | #4"); 262 | }); 263 | 264 | server.input(null, inputSrvMessage); 265 | }); 266 | 267 | //-----)> 268 | 269 | it("Instance (event: text)", function(done) { 270 | let server = objBot.virtual(function() { 271 | throw new Error("The message passed through the event | #1"); 272 | }); 273 | 274 | server.on("*", function() { 275 | throw new Error("The message passed through the event | #2"); 276 | }); 277 | 278 | server.on("/", function() { 279 | throw new Error("The message passed through the event | #3"); 280 | }); 281 | 282 | server.on("text", function(bot) { 283 | tCheckBaseBotFields(bot); 284 | done(); 285 | }); 286 | 287 | server.input(null, inputSrvMessage); 288 | }); 289 | 290 | it("Instance (event: text | mix)", function(done) { 291 | let server = objBot.virtual(function() { 292 | throw new Error("The message passed through the event | #1"); 293 | }); 294 | 295 | server.on("photo text audio", function(bot) { 296 | tCheckBaseBotFields(bot); 297 | done(); 298 | }); 299 | 300 | server.on("*", function() { 301 | throw new Error("The message passed through the event | #2"); 302 | }); 303 | 304 | server.on("/", function() { 305 | throw new Error("The message passed through the event | #3"); 306 | }); 307 | 308 | server.input(null, inputSrvMessage); 309 | }); 310 | 311 | it("Instance (event: text | off)", function(done) { 312 | let server = objBot.virtual(function() { 313 | throw new Error("The message passed through the event | #1"); 314 | }); 315 | 316 | server.on("text", onText); 317 | server.off("text", onText); 318 | 319 | server.on("text", function(bot) { 320 | tCheckBaseBotFields(bot); 321 | done(); 322 | }); 323 | 324 | server.on("/", function() { 325 | throw new Error("The message passed through the event | #2"); 326 | }); 327 | 328 | server.input(null, inputSrvMessage); 329 | 330 | //------]> 331 | 332 | function onText() { 333 | throw new Error("Call-off events"); 334 | } 335 | }); 336 | 337 | it("Instance (event: text | goto | sync)", function(done) { 338 | let server = objBot.virtual(function() { 339 | throw new Error("The message passed through the event | #1"); 340 | }); 341 | 342 | server.use(function(bot) { 343 | return bot.message.text === "Hello" ? "goto" : ""; 344 | }); 345 | 346 | server.on("*", function() { 347 | throw new Error("The message passed through the event | #2"); 348 | }); 349 | 350 | server.on("/", function() { 351 | throw new Error("The message passed through the event | #3"); 352 | }); 353 | 354 | server.on("text", onText); 355 | server.on("text:goto", onTextGoto); 356 | 357 | //------]> 358 | 359 | server.input(null, inputSrvMessage); 360 | 361 | //------]> 362 | 363 | function onText() { 364 | throw new Error("The message passed through the event | #4"); 365 | } 366 | 367 | function onTextGoto(bot) { 368 | tCheckBaseBotFields(bot); 369 | done(); 370 | } 371 | }); 372 | 373 | it("Instance (event: text | goto | async)", function(done) { 374 | let server = objBot.virtual(function() { 375 | throw new Error("The message passed through the event | #1"); 376 | }); 377 | 378 | server.use(function(bot, data, next) { 379 | next(bot.message.text === "Hello" ? "goto" : ""); 380 | }); 381 | 382 | server.on("*", function() { 383 | throw new Error("The message passed through the event | #2"); 384 | }); 385 | 386 | server.on("/", function() { 387 | throw new Error("The message passed through the event | #3"); 388 | }); 389 | 390 | server.on("text", onText); 391 | server.on("text:goto", onTextGoto); 392 | 393 | //------]> 394 | 395 | server.input(null, inputSrvMessage); 396 | 397 | //------]> 398 | 399 | function onText() { 400 | throw new Error("The message passed through the event | #4"); 401 | } 402 | 403 | function onTextGoto(bot) { 404 | tCheckBaseBotFields(bot); 405 | done(); 406 | } 407 | }); 408 | 409 | //-----)> 410 | 411 | it("Instance (event: default | cmd)", function(done) { 412 | let server = objBot.virtual(function() { 413 | throw new Error("The message passed through the event | #1"); 414 | }); 415 | 416 | server.on("/", function(bot) { 417 | const cmd = bot.command; 418 | 419 | tCheckBaseBotFields(bot); 420 | tCheckBaseCmdFields(cmd); 421 | 422 | done(); 423 | }); 424 | 425 | server.on("*", function() { 426 | throw new Error("The message passed through the event | #2"); 427 | }); 428 | 429 | server.on("text", function() { 430 | throw new Error("The message passed through the event | #3"); 431 | }); 432 | 433 | //------]> 434 | 435 | server.input(null, inputSrvMessageCmd); 436 | }); 437 | 438 | it("Instance (event: cmd | goto | normal)", function(done) { 439 | let server = objBot.virtual(function() { 440 | throw new Error("The message passed through the event | #1"); 441 | }); 442 | 443 | server.use("text", function(bot, data, next) { 444 | next("goto"); 445 | }); 446 | 447 | 448 | server.on("/test:goto", function(bot, params) { 449 | tCheckBaseBotFields(bot); 450 | tCheckBaseCmdFields(params); 451 | 452 | done(); 453 | }); 454 | 455 | server.on("/:goto", function() { 456 | throw new Error("The message passed through the event | #6"); 457 | }); 458 | 459 | server.on("/", function() { 460 | throw new Error("The message passed through the event | #5"); 461 | }); 462 | 463 | 464 | server.on("text:goto", function() { 465 | throw new Error("The message passed through the event | #4"); 466 | }); 467 | 468 | server.on("text", function() { 469 | throw new Error("The message passed through the event | #3"); 470 | }); 471 | 472 | server.on("*", function() { 473 | throw new Error("The message passed through the event | #2"); 474 | }); 475 | 476 | //------]> 477 | 478 | server.input(null, inputSrvMessageCmd); 479 | }); 480 | 481 | it("Instance (event: cmd | goto | base)", function(done) { 482 | let server = objBot.virtual(function() { 483 | throw new Error("The message passed through the event | #5"); 484 | }); 485 | 486 | server.use(function(bot) { 487 | return bot.message.text === "/test hello" ? "goto" : ""; 488 | }); 489 | 490 | server.on("/", function(bot, params) { 491 | expect(bot).to.have.property("command").that.deep.equal(params).that.is.an("object").and.not.equal(null); 492 | 493 | tCheckBaseBotFields(bot); 494 | tCheckBaseCmdFields(bot.command); 495 | 496 | done(); 497 | }); 498 | 499 | server.on("text:goto", function() { 500 | throw new Error("The message passed through the event | #4"); 501 | }); 502 | 503 | server.on("text", function() { 504 | throw new Error("The message passed through the event | #3"); 505 | }); 506 | 507 | server.on("*", function() { 508 | throw new Error("The message passed through the event | #2"); 509 | }); 510 | 511 | //------]> 512 | 513 | server.input(null, inputSrvMessageCmd); 514 | }); 515 | 516 | it("Instance (event: cmd | goto | base-without)", function(done) { 517 | let server = objBot.virtual(function() { 518 | throw new Error("The message passed through the event | #1"); 519 | }); 520 | 521 | 522 | server.use("text", function() { 523 | return "goto"; 524 | }); 525 | 526 | 527 | server.on("/", function(bot) { 528 | const cmd = bot.command; 529 | 530 | tCheckBaseBotFields(bot); 531 | tCheckBaseCmdFields(cmd); 532 | 533 | expect(bot.gotoState).to.be.a("string").and.equal("goto"); 534 | 535 | done(); 536 | }); 537 | 538 | server.on("text:goto", function() { 539 | throw new Error("The message passed through the event | #4"); 540 | }); 541 | 542 | server.on("text", function() { 543 | throw new Error("The message passed through the event | #3"); 544 | }); 545 | 546 | server.on("*", function() { 547 | throw new Error("The message passed through the event | #2"); 548 | }); 549 | 550 | //------]> 551 | 552 | server.input(null, inputSrvMessageCmd); 553 | }); 554 | 555 | it("Instance (event: cmd | goto | default)", function(done) { 556 | let server = objBot.virtual(function() { 557 | throw new Error("The message passed through the event | #6"); 558 | }); 559 | 560 | server.use("text", function() { 561 | return "goto"; 562 | }); 563 | 564 | 565 | server.on("/x:goto", function() { 566 | throw new Error("The message passed through the event | #1"); 567 | }); 568 | 569 | server.on("/x", function() { 570 | throw new Error("The message passed through the event | #5"); 571 | }); 572 | 573 | 574 | server.on("text:goto", function() { 575 | throw new Error("The message passed through the event | #4"); 576 | }); 577 | 578 | server.on("text", function() { 579 | throw new Error("The message passed through the event | #3"); 580 | }); 581 | 582 | server.on("*", function() { 583 | throw new Error("The message passed through the event | #2"); 584 | }); 585 | 586 | server.on("/", function(bot) { 587 | const cmd = bot.command; 588 | 589 | tCheckBaseBotFields(bot); 590 | tCheckBaseCmdFields(cmd); 591 | 592 | expect(bot.gotoState).to.be.a("string").and.equal("goto"); 593 | 594 | done(); 595 | }); 596 | 597 | //------]> 598 | 599 | server.input(null, inputSrvMessageCmd); 600 | }); 601 | 602 | it("Instance (event: cmd | goto | default-generator)", function(done) { 603 | let server = objBot.virtual(function() { 604 | throw new Error("The message passed through the event | #6"); 605 | }); 606 | 607 | server.use("text", function* () { 608 | return yield new Promise(x => setTimeout(x, 0, "goto")); 609 | }); 610 | 611 | 612 | server.on("/x:goto", function() { 613 | throw new Error("The message passed through the event | #1"); 614 | }); 615 | 616 | server.on("/x", function() { 617 | throw new Error("The message passed through the event | #5"); 618 | }); 619 | 620 | 621 | server.on("text:goto", function() { 622 | throw new Error("The message passed through the event | #4"); 623 | }); 624 | 625 | server.on("text", function() { 626 | throw new Error("The message passed through the event | #3"); 627 | }); 628 | 629 | server.on("*", function() { 630 | throw new Error("The message passed through the event | #2"); 631 | }); 632 | 633 | server.on("/", function* (bot) { 634 | const cmd = bot.command; 635 | 636 | tCheckBaseBotFields(bot); 637 | tCheckBaseCmdFields(cmd); 638 | 639 | expect(bot.gotoState).to.be.a("string").and.equal("goto"); 640 | 641 | yield new Promise(x => setTimeout(x)); 642 | 643 | done(); 644 | }); 645 | 646 | //------]> 647 | 648 | server.input(null, inputSrvMessageCmd); 649 | }); 650 | 651 | //-----)> 652 | 653 | it("Instance (catch)", function(done) { 654 | let count = 0; 655 | 656 | let server = objBot 657 | .virtual(function() { 658 | throw new Error("#1"); 659 | }) 660 | .catch(function(error) { 661 | expect(error).to.be.an.instanceof(Error); 662 | 663 | count++; 664 | 665 | if(count === 3) { 666 | done(); 667 | } 668 | }); 669 | 670 | //-------]> 671 | 672 | server.input(null, inputSrvMessage); 673 | 674 | server.on("text", function() { 675 | throw new Error("#2"); 676 | }); 677 | 678 | server.input(null, inputSrvMessage); 679 | 680 | server.on(/(\w+)/, ["myText"], function() { 681 | throw new Error("#3"); 682 | }); 683 | 684 | server.input(null, inputSrvMessage); 685 | }); 686 | 687 | it("Instance (catch | generators)", function(done) { 688 | let count = 0; 689 | 690 | let server = objBot 691 | .virtual(function* () { 692 | yield Promise.resolve(); 693 | throw new Error("#1"); 694 | }) 695 | .catch(function* (error) { 696 | yield Promise.resolve(); 697 | 698 | expect(error).to.be.an.instanceof(Error); 699 | 700 | count++; 701 | 702 | if(count === 3) { 703 | done(); 704 | } 705 | }); 706 | 707 | //-------]> 708 | 709 | server.input(null, inputSrvMessage); 710 | 711 | server.on("text", function* () { 712 | yield Promise.resolve(); 713 | throw new Error("#2"); 714 | }); 715 | 716 | server.input(null, inputSrvMessage); 717 | 718 | server.on(/(\w+)/, ["myText"], function* () { 719 | yield Promise.resolve(); 720 | throw new Error("#3"); 721 | }); 722 | 723 | server.input(null, inputSrvMessage); 724 | }); 725 | 726 | }); 727 | 728 | //-------------]> 729 | 730 | function tCheckBaseBotFields(bot) { 731 | expect(bot).to.be.an("object").and.not.equal(null); 732 | expect(bot).to.have.property("message").that.is.an("object").and.not.equal(null); 733 | 734 | //----------]> 735 | 736 | const msg = bot.message; 737 | 738 | expect(msg).to.have.property("message_id"); 739 | expect(msg).to.have.property("chat").that.is.an("object").and.not.equal(null); 740 | expect(msg).to.have.property("date"); 741 | 742 | expect(bot).to.have.property("isGroup").that.is.an("boolean"); 743 | expect(bot).to.have.property("isReply").that.is.an("boolean"); 744 | 745 | expect(bot).to.have.property("cid").that.equal(msg.chat.id); 746 | expect(bot).to.have.property("mid").that.equal(msg.message_id); 747 | 748 | expect(bot).to.have.property("from").that.deep.equal(msg.from).that.is.an("object").and.not.equal(null); 749 | 750 | //----------]> 751 | 752 | expect(bot.api).to.deep.equal(objBot.api); 753 | 754 | //----------]> 755 | 756 | [ 757 | "render", 758 | "answer" 759 | ] 760 | .forEach(function(e) { 761 | expect(bot[e]).to.be.a("function"); 762 | }); 763 | } 764 | 765 | function tCheckBaseCmdFields(params, isPrivate) { 766 | expect(params).to.be.a("object").and.not.equal(null); 767 | 768 | expect(params).to.have.property("type").that.is.equal(isPrivate ? "private" : "common"); 769 | expect(params).to.have.property("name").that.is.equal("test"); 770 | expect(params).to.have.property("text").that.is.equal("hello"); 771 | expect(params).to.have.property("cmd").that.is.equal("/test"); 772 | } --------------------------------------------------------------------------------