├── .gitignore ├── package.json ├── LICENSE ├── say.js ├── README.md └── index.js /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | .vagrant 4 | Vagrantfile 5 | cert 6 | *.iml 7 | node_modules -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telegram-cli", 3 | "version": "2.0.0", 4 | "description": "Control your server using a Telegram bot", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node index.js" 9 | }, 10 | "author": "http://github.com/matheuss", 11 | "license": "ISC", 12 | "dependencies": { 13 | "body-parser": "^1.14.1", 14 | "express": "^4.13.3", 15 | "node-emoji": "^1.0.4", 16 | "randomstring": "~1.1.0", 17 | "request": "^2.65.0" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Matheus Fernandes – http://github.com/matheuss 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /say.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Created by matheus on 10/25/15. 3 | */ 4 | 5 | /** 6 | * TODO: figure an alternative to Google TTS private API 7 | */ 8 | if(message.text.indexOf("say") == 0) { 9 | json.action = "record_audio"; 10 | res.json(json); 11 | 12 | var lang = "en"; 13 | var text = "What do you want me to say?"; 14 | var fileName = randomstring.generate(); 15 | 16 | if(message.text.length > 3) { // has something after 'say' 17 | if (message.text.indexOf("-l") == 4) { // has -l 18 | lang = message.text.substring(7, 9); 19 | 20 | if (message.text.length > 9) { // has text to speak 21 | text = message.text.substring(10, message.text.length); 22 | } 23 | console.log(lang); 24 | console.log(text); 25 | } else { // doesn't have -l 26 | text = message.text.substring(4, message.text.length); 27 | } 28 | } 29 | exec("curl 'http://translate.google.com/translate_tts?ie=UTF-8&q=" + encodeURIComponent(text)+ "&client=t&tl=" + lang + 30 | "' -H 'Referer: http://translate.google.com/' -H 'User-Agent: stagefright/1.2 (Linux;Android 5.0)'" + 31 | " > " + fileName + ".mp3; mv " + fileName + ".mp3 " + fileName + ".ogg", 32 | function(err, stdout, stderr) { 33 | if(err) { 34 | sendTroubleMessage(); 35 | } else { 36 | data.voice = fs.createReadStream(fileName + '.ogg'); 37 | request.post({ 38 | url: "https://api.telegram.org/bot" + vars.BOT_TOKEN + "/sendMessage", 39 | formData: data 40 | }, function (err, res, body) { 41 | console.log(err); 42 | console.log(body); 43 | } 44 | ); 45 | } 46 | }); 47 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # telegram-cli 2 | Control your server using a Telegram bot 3 | 4 | ## Installation 5 | ```sh 6 | git clone https://github.com/matheuss/telegram-cli.git 7 | cd telegram-cli 8 | npm install 9 | ``` 10 | 11 | ## Configuration 12 | ### build.js 13 | You need to set some "build variables": 14 | 15 | ```js 16 | exports.PORT = 0; 17 | exports.WEBHOOK_ENDPOINT = ""; 18 | exports.BOT_TOKEN = ""; 19 | exports.OWNER_ID = ""; 20 | exports.OWNER_USERNAME = ""; 21 | ``` 22 | The **PORT** should be one of the following: **443, 80, 88 or 8443.** 23 | 24 | The **WEBHOOK_ENDPOINT** should be a "secret" path, to ensure that the requests are coming from Telegram. Example: 25 | ```js 26 | exports.WEBHOOK_ENDPOINT = "/BIcif92WztBwirBmgVnsmiZqR3WMZJveaRF5cznO" // don't use that one! 27 | ``` 28 | The **BOT_TOKEN** you can obtain with [BotFather](https://telegram.me/botfather). 29 | 30 | The **OWNER_ID** should be your Telegram user id. To find out yours, talk to [this bot](https://telegram.me/user_id_bot). 31 | 32 | The **OWNER_USERNAME** should be your Telegram username. 33 | 34 | ### HTTPS 35 | HTTPS is mandatory when using a webhook. You can generate a self-signed certificate with the following: 36 | ```sh 37 | openssl req -newkey rsa:2048 -sha256 -nodes -keyout key.key -x509 -days 365 -out cert.pem -subj "/C=US/ST=New York/L=Brooklyn/O=Example Brooklyn Company/CN=CHANGE_THIS" 38 | ``` 39 | The **CN** field should contain the domain/IP where your bot will be running. Examples: 40 | ``` 41 | CN=my-bot.herokuapp.com 42 | CN=205.139.40.96 43 | ``` 44 | Note: don't include anything else. Just the domain or the IP. No protocols (http:// or https://) are required. 45 | 46 | Now you should issue a POST request to register your webhook. I recommend [Postman](https://www.getpostman.com) for that. [Here's an example of the request](http://cl.ly/dgdE). 47 | 48 | ## Running 49 | All set, ready to go: 50 | ```sh 51 | node index.js 52 | ``` 53 | Now you should be able to talk with your bot. If he doesn't responds, you probably have done something wrong on the HTTPS step. Feel free to reach me [@matheus.frndes](https://telegram.me/matheusfrndes) (It took me a long time to get this step done correctly, so I think I can help :blush:) -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | fs = require('fs'), 3 | bodyParser = require('body-parser'), 4 | request = require('request'), 5 | exec = require('child_process').exec, 6 | randomstring = require("randomstring"), 7 | vars = require("./build-vars"), 8 | emoji = require("node-emoji"); 9 | 10 | var options = { 11 | cert: fs.readFileSync('cert/cert.pem'), 12 | key: fs.readFileSync('cert/key.key'), 13 | requestCert: false, 14 | rejectUnauthorized: false 15 | }; 16 | 17 | var app = express(), 18 | https = require('https').createServer(options, app); 19 | 20 | https.listen(vars.PORT); 21 | 22 | app.use(bodyParser.json()); 23 | app.use(bodyParser.urlencoded({ extended: false })); 24 | 25 | var help = "/exec _command_ – execute something\n" + 26 | "/help – display this message\n" + 27 | "\n" + 28 | "You can also use the commands without the slash (e.g: exec ls -lat)"; 29 | var friends = []; 30 | var strangers = []; 31 | var protectedFiles = []; 32 | var queue = []; 33 | 34 | app.post(vars.WEBHOOK_URL, function(req, res) { 35 | console.log(req.body); 36 | 37 | var json = {}; 38 | var message = req.body.message; 39 | 40 | var data = {}; 41 | data.chat_id = message.chat.id; 42 | json.method = "sendChatAction"; 43 | json.action = "typing"; 44 | res.json(json); 45 | 46 | if(message.text == undefined) { // message != text (sticker, file etc) 47 | offerHelp(data); 48 | return 49 | } 50 | 51 | console.log(strangers); 52 | if(strangers.indexOf(message.from.id) != -1) { 53 | var choosen = randint(1, 2); 54 | if (choosen == 1) { 55 | sendSticker(data, "BQADAgADvgADEwvrBCIBxkL5iigcAg"); 56 | } else if (choosen == 2) { 57 | queue.push([data, 'message', "I'm sorry but I need to sleep right now – it's kinda late here in the cloud " 58 | + emoji.get("sweat_smile"), false]); 59 | queue.push([data, 'sticker', 'BQADBQADIQADw20RBFERg4uIH5-_Ag']); 60 | } 61 | sendQueue(); 62 | return 63 | 64 | } else if(friends.indexOf(message.from.id) == -1 && message.from.id != vars.OWNER_ID) { 65 | sendMessage(data, "Hey, I don't know you " + emoji.get("cold_sweat") + "\nI was told not to talk to strangers. " + 66 | "If you wanna talk to me, first you need to ask to @" + vars.OWNER_USERNAME + 67 | ". He takes care of me " + emoji.get("blush")); 68 | strangers.push(message.from.id); 69 | return 70 | } 71 | 72 | if(message.text.indexOf("/") == 0) { 73 | message.text = message.text.substring(1, message.text.length); 74 | } 75 | 76 | if(message.text.indexOf("exec") == 0) { 77 | if(message.text.length < 6) { 78 | sendMessage(data, "What do you want me to execute? If you're lost, try this: *exec date*", true) 79 | return; 80 | } else { 81 | var files = message.text.substring(message.text.indexOf(" ", message.text.lastIndexOf("-")) + 1, message.text.length); 82 | files = files.split(" "); 83 | for(f in files) { 84 | if (protectedFiles.indexOf(files[f]) != -1) { 85 | queue.push([data, 'sticker', "BQADAgADsgADEwvrBJysIqEeQXeSAg"]); 86 | queue.push([data, 'message', "What are you trying to do with me???", false]); 87 | return; 88 | } 89 | } 90 | } 91 | 92 | exec(message.text.substring(5, message.text.length), function(err, stdout, stderr) { 93 | if(err) { 94 | if(stderr.indexOf("Permission denied") != -1) { 95 | sendMessage(data, "Ops. Looks like I don't have the necessary rights to do that.\n" + 96 | "For now, there's nothing I can do for you – " + 97 | "I have to learn how to __sudo__ things first " + emoji.get("sweat"), true); 98 | } else { 99 | sendMessage(data, stderr); 100 | } 101 | } else { 102 | if(stdout) { 103 | sendMessage(data, stdout); 104 | } else { 105 | sendMessage(data, emoji.get("white_check_mark")); 106 | } 107 | } 108 | }); 109 | 110 | } else if(message.text.indexOf("start") == 0) { 111 | sendMessage(data, "Hi there! :)\nSince I'm newborn, I can't do many things yet.\n" + 112 | "Here's a list of what I've learned so far:\n\n" + help, true); 113 | } else if(message.text.indexOf("help") == 0) { 114 | sendMessage(data, help, true); 115 | } else { 116 | offerHelp(data); 117 | } 118 | }); 119 | 120 | function sendMessage(_data, text, markdown) { 121 | var data = JSON.parse(JSON.stringify(_data)); 122 | data.text = text; 123 | 124 | if(markdown){ 125 | data.parse_mode = "Markdown"; 126 | } 127 | request.post({ 128 | url: "https://api.telegram.org/bot" + vars.BOT_TOKEN + "/sendMessage", 129 | formData: data 130 | }, function (err, res, body) { 131 | sendQueue(); 132 | }); 133 | } 134 | 135 | function sendTroubleMessage(data) { 136 | data.text = "I'm sorry, but I'm having trouble doing this right now :("; 137 | sendMessage(data); 138 | } 139 | 140 | function randint (low, high) { 141 | return Math.floor(Math.random() * (high - low + 1) + low); 142 | } 143 | 144 | function sendSticker(_data, sticker) { 145 | var data = JSON.parse(JSON.stringify(_data)); 146 | data.sticker = sticker; 147 | 148 | request.post({ 149 | url: "https://api.telegram.org/bot" + vars.BOT_TOKEN + "/sendSticker", 150 | formData: data 151 | }, function (err, res, body) { 152 | sendQueue(); 153 | }); 154 | } 155 | 156 | function offerHelp(data) { 157 | queue.push([data, 'sticker', "BQADBQADIwADw20RBDWi8t98s12bAg"]); 158 | queue.push([data, 'message', "Do you need some /help?", false]); 159 | sendQueue(); 160 | } 161 | 162 | exec("ls -a", function(err, stdout, stderr) { 163 | if(err) { 164 | console.log("Can't ls current directory"); 165 | console.log(stderr); 166 | process.exit(); 167 | } 168 | protectedFiles = stdout.split("\n"); 169 | }); 170 | 171 | function sendQueue() { 172 | if(queue.length > 0) { 173 | var el = queue.shift(); 174 | if(el[1] == 'sticker') { 175 | sendSticker(el[0], el[2]); 176 | } else if(el[1] == 'message') { 177 | sendMessage(el[0], el[2], el[3]); 178 | } 179 | } 180 | } 181 | --------------------------------------------------------------------------------