├── data ├── chatStorage.json └── userStorage.json ├── .gitignore ├── controllers ├── otherwise.js └── todo.js ├── package.json ├── README.md ├── index.js ├── LICENSE └── adapters └── PersistentMemoryStorage.js /data/chatStorage.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /data/userStorage.json: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode 2 | node_modules -------------------------------------------------------------------------------- /controllers/otherwise.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Telegram = require('telegram-node-bot'); 4 | 5 | class OtherwiseController extends Telegram.TelegramBaseController { 6 | handle($) { 7 | $.sendMessage('Sorry, I don\'t understand.'); 8 | } 9 | } 10 | 11 | module.exports = OtherwiseController; -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "telegram-demo-todo-bot", 3 | "version": "1.0.0", 4 | "description": "Tutorial onhHow to create a basic Telegram bot", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "start": "node index.js" 9 | }, 10 | "keywords": ["chatbot", "nodejs", "telegram", "todo"], 11 | "author": { 12 | "name": "Ferdinand Mütsch", 13 | "url": "https://ferdinand-muetsch.de" 14 | }, 15 | "license": "MIT", 16 | "dependencies": { 17 | "telegram-node-bot": "^4.0.3" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # telegram-bot-tutorial 2 | 3 | [![Say thanks](https://img.shields.io/badge/SayThanks.io-%E2%98%BC-1EAEDB.svg)](https://saythanks.io/to/n1try) 4 | 5 | Code of my video tutorial on how to create a basic **ToDo** application as a **Telegram bot** using Node.js and the [telegram-node-bot](https://github.com/Naltox/telegram-node-bot) framework. 6 | 7 | [![youtube_thumbnail](http://img.youtube.com/vi/Te7HcRhwOI4/0.jpg)](http://www.youtube.com/watch?v=Te7HcRhwOI4 "Tutorial: How to create a Telegram Bot with Node.js") 8 | 9 | [![Buy me a coffee](https://www.buymeacoffee.com/assets/img/custom_images/orange_img.png)](https://buymeacoff.ee/n1try) 10 | 11 | ## License 12 | MIT @ [Ferdinand Mütsch](https://ferdinand-muetsch.de) 13 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Telegram = require('telegram-node-bot'), 4 | PersistentMemoryStorage = require('./adapters/PersistentMemoryStorage'), 5 | storage = new PersistentMemoryStorage( 6 | `${__dirname}/data/userStorage.json`, 7 | `${__dirname}/data/chatStorage.json` 8 | ), 9 | tg = new Telegram.Telegram('', { 10 | workers: 1, 11 | storage: storage 12 | }); 13 | 14 | const TodoController = require('./controllers/todo') 15 | , OtherwiseController = require('./controllers/otherwise'); 16 | 17 | const todoCtrl = new TodoController(); 18 | 19 | tg.router.when(new Telegram.TextCommand('/add', 'addCommand'), todoCtrl) 20 | .when(new Telegram.TextCommand('/get', 'getCommand'), todoCtrl) 21 | .when(new Telegram.TextCommand('/check', 'checkCommand'), todoCtrl) 22 | .otherwise(new OtherwiseController()); 23 | 24 | function exitHandler(exitCode) { 25 | storage.flush(); 26 | process.exit(exitCode); 27 | } 28 | 29 | process.on('SIGINT', exitHandler.bind(null, 0)); 30 | process.on('uncaughtException', exitHandler.bind(null, 1)); -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2016 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. -------------------------------------------------------------------------------- /adapters/PersistentMemoryStorage.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Telegram = require('telegram-node-bot'), 4 | fs = require('fs'); 5 | 6 | class PersistentMemoryStorage extends Telegram.BaseStorage { 7 | constructor(userStoragePath, chatStoragePath) { 8 | super(); 9 | 10 | this.userStoragePath = userStoragePath; 11 | this.chatStoragePath = chatStoragePath; 12 | this.isClean = false; 13 | 14 | this._storage = { 15 | userStorage: require(userStoragePath), 16 | chatStorage: require(chatStoragePath) 17 | }; 18 | } 19 | 20 | get(storage, key) { 21 | return new Promise((resolve) => { 22 | resolve(this._storage[storage][key] || {}); 23 | }); 24 | } 25 | 26 | set(storage, key, data) { 27 | return new Promise((resolve) => { 28 | this._storage[storage][key] = data; 29 | if (this.clean) this.clean = false; 30 | resolve(); 31 | }); 32 | } 33 | 34 | remove(storage, key) { 35 | return new Promise((resolve) => { 36 | if (this.clean) this.clean = false; 37 | delete this._storage[storage][key]; 38 | resolve(); 39 | }); 40 | } 41 | 42 | flush() { 43 | if (this.clean) return; 44 | fs.writeFileSync(this.userStoragePath, JSON.stringify(this._storage.userStorage)); 45 | fs.writeFileSync(this.chatStoragePath, JSON.stringify(this._storage.chatStorage)); 46 | this.clean = true; 47 | } 48 | } 49 | 50 | module.exports = PersistentMemoryStorage; -------------------------------------------------------------------------------- /controllers/todo.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | const Telegram = require('telegram-node-bot'); 4 | 5 | class TodoController extends Telegram.TelegramBaseController { 6 | addHandler($) { 7 | let todo = $.message.text.split(' ').slice(1).join(' '); 8 | 9 | if (!todo) return $.sendMessage('Sorry, please pass a todo item.'); 10 | 11 | $.getUserSession('todos') 12 | .then(todos => { 13 | if (!Array.isArray(todos)) $.setUserSession('todos', [todo]); 14 | else $.setUserSession('todos', todos.concat([todo])); 15 | $.sendMessage('Added new todo!'); 16 | }); 17 | } 18 | 19 | getHandler($) { 20 | $.getUserSession('todos').then(todos => { 21 | $.sendMessage(this._serializeList(todos), { parse_mode: 'Markdown' }); 22 | }); 23 | } 24 | 25 | checkHandler($) { 26 | let index = parseInt($.message.text.split(' ').slice(1)[0]); 27 | if (isNaN(index)) return $.sendMessage('Sorry, you didn\'t pass a valid index.'); 28 | 29 | $.getUserSession('todos') 30 | .then(todos => { 31 | if (index >= todos.length) return $.sendMessage('Sorry, you didn\'t pass a valid index.'); 32 | todos.splice(index, 1); 33 | $.setUserSession('todos', todos); 34 | $.sendMessage('Checked todo!'); 35 | }); 36 | } 37 | 38 | get routes() { 39 | return { 40 | 'addCommand': 'addHandler', 41 | 'getCommand': 'getHandler', 42 | 'checkCommand': 'checkHandler' 43 | }; 44 | } 45 | 46 | _serializeList(todoList) { 47 | let serialized = '*Your Todos:*\n\n'; 48 | todoList.forEach((t, i) => { 49 | serialized += `*${i}* - ${t}\n`; 50 | }); 51 | return serialized; 52 | } 53 | } 54 | 55 | module.exports = TodoController; --------------------------------------------------------------------------------