├── bin └── run.js ├── termgram.png ├── .npmignore ├── README.md ├── .gitignore ├── i18n ├── mark-down.js └── en-US.js ├── package.json ├── LICENSE ├── lib ├── user-data.js ├── updates.js ├── use-case │ ├── chat.js │ ├── select-chat.js │ ├── sign-in.js │ └── sign-up.js ├── user-interface.js └── client-proxy.js └── termgram.js /bin/run.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | 3 | require('../termgram'); -------------------------------------------------------------------------------- /termgram.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/enricostara/termgram/HEAD/termgram.png -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # Compiled binary addons (http://nodejs.org/api/addons.html) 20 | build/Release 21 | 22 | # Logs 23 | log/* 24 | 25 | # Dev-Config 26 | *.yml 27 | .gitignore 28 | .npmignore 29 | gulpfile.js 30 | 31 | # Users Environment Variables 32 | .lock-wscript 33 | 34 | # IDE Project files 35 | *.iws 36 | *.iml 37 | *.ipr 38 | .idea 39 | .classpath 40 | .project -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # 2 | 3 | ##TERMGRAM 4 | 5 | ###A terminal client to connect with [Telegram](http://www.telegram.org). 6 | 7 | The main purpose of the project is to provide a working **example** of 8 | how to use the [**telegram.link**](http://telegram.link) library in order to build a Telegram client application 9 | 10 | ## Install & Run 11 | 12 | ```bash 13 | $ npm install -g termgram 14 | $ termgram 15 | ``` 16 | 17 | ## Node.js 0.12.x 18 | In order to take advantage of some new Javascript features, the **Node.js version required is the 0.12.x** 19 | 20 | **Note:** [**telegram.link**](http://telegram.link) just requires the version **0.10.X** 21 | 22 | ### License 23 | 24 | The project is released under the [MIT license](./LICENSE) 25 | 26 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Logs 26 | log/* 27 | 28 | # Dependency directory 29 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 30 | node_modules 31 | 32 | # IDE Project files 33 | *.iws 34 | *.iml 35 | *.ipr 36 | .idea 37 | .classpath 38 | .project -------------------------------------------------------------------------------- /i18n/mark-down.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // import the dependencies 7 | require('colors'); 8 | var boldEx = /\*\*(.+?)\*\*/g; 9 | var emEx = /_(.+?)_/g; 10 | 11 | module.exports = function (i18n) { 12 | return convert(i18n); 13 | }; 14 | 15 | function convert(obj) { 16 | for (var propertyName in obj) { 17 | var value = obj[propertyName]; 18 | //console.log(propertyName + ': ' + value); 19 | switch (typeof value) { 20 | case 'string': 21 | obj[propertyName] = value.replace(boldEx, '$1'.bold).replace(emEx, '$1'.underline); 22 | break; 23 | default: 24 | obj[propertyName] = convert(value); 25 | } 26 | } 27 | return obj; 28 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "termgram", 3 | "version": "0.2.1", 4 | "description": "Termgram App", 5 | "keywords": [ 6 | "telegram", 7 | "terminal", 8 | "client", 9 | "telegram.link" 10 | ], 11 | "author": "Enrico Stara ", 12 | "homepage": "http://termgram.me", 13 | "repository": { 14 | "type": "git", 15 | "url": "git://github.com/enricostara/termgram.git" 16 | }, 17 | "preferGlobal": true, 18 | "bugs": { 19 | "url": "https://github.com/enricostara/termgram/issues" 20 | }, 21 | "dependencies": { 22 | "telegram.link": "latest", 23 | "colors": "latest", 24 | "mute-stream": "latest", 25 | "requirish": "latest", 26 | "get-log": "latest" 27 | }, 28 | "license": "MIT", 29 | "main": "./termgram", 30 | "engines": { 31 | "node": "0.12.x" 32 | }, 33 | "bin": { 34 | "termgram": "./bin/run.js" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Enrico Stara 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /lib/user-data.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // import the dependencies 7 | var fs = require('fs'); 8 | var util = require('util'); 9 | var fileResolver = /^\.(\w+)\.user$/; 10 | var baseFolder = '.'; 11 | 12 | 13 | function UserData(data) { 14 | util._extend(this, data); 15 | } 16 | 17 | UserData.prototype.setDataCenter = function (dataCenter) { 18 | this.dataCenter = dataCenter; 19 | }; 20 | 21 | UserData.prototype.getDataCenter = function () { 22 | return this.dataCenter; 23 | }; 24 | 25 | UserData.prototype.setAuthKey = function (authKeyBuffer) { 26 | this.authKey = authKeyBuffer.toString('base64'); 27 | }; 28 | 29 | UserData.prototype.getAuthKey = function () { 30 | return this.authKey ? new Buffer(this.authKey, 'base64') : null; 31 | }; 32 | 33 | UserData.prototype.save = function () { 34 | var filePath = baseFolder + '/.' + this.name + '.user'; 35 | var ws = fs.createWriteStream(filePath); 36 | ws.write(JSON.stringify(this)); 37 | ws.end(); 38 | }; 39 | 40 | function retrieveUsernameList() { 41 | var list = fs.readdirSync(baseFolder); 42 | list = list.map(function (value) { 43 | var match = value.match(fileResolver); 44 | return (match ? match[1] : null); 45 | }).filter(function (value) { 46 | return value 47 | }); 48 | return list; 49 | } 50 | 51 | function loadUser(username) { 52 | var filePath = baseFolder + '/.' + username + '.user'; 53 | return new UserData(JSON.parse(fs.readFileSync(filePath))); 54 | } 55 | 56 | function setBaseFolder(folder) { 57 | baseFolder = folder; 58 | } 59 | 60 | module.exports = exports = UserData; 61 | exports.retrieveUsernameList = retrieveUsernameList; 62 | exports.loadUser = loadUser; 63 | exports.setBaseFolder = setBaseFolder; 64 | 65 | -------------------------------------------------------------------------------- /lib/updates.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // import the dependencies 7 | require('requirish')._(module); 8 | require('colors'); 9 | 10 | var clientProxy = require('lib/client-proxy'); 11 | var getLogger = require('get-log'); 12 | var logger = getLogger('update-emitter'); 13 | 14 | var UPDATE_INTERVAL = 1000; 15 | 16 | function Updates() { 17 | } 18 | 19 | // Start the emitter. 20 | Updates.prototype.start = function () { 21 | var self = this; 22 | logger.info('start updates'); 23 | console.log('start updates'); 24 | return new Promise(function (fulfill, reject) { 25 | try { 26 | clientProxy.getClient().account.updateStatus(false).then(function () { 27 | clientProxy.getClient().registerOnUpdates(function (update) { 28 | console.log('update', update.toPrintable()); 29 | }); 30 | try { 31 | clientProxy.getClient().updates.getState().then(function (state) { 32 | try { 33 | setState.call(self, state); 34 | clientProxy.getClient().on('error', function (error) { 35 | console.log('client error', error.stack); 36 | }); 37 | clientProxy.getClient().httpPoll(); 38 | setTimeout(fulfill, 100); 39 | //fulfill(); 40 | } catch (e) { 41 | reject(e) 42 | } 43 | }, reject); 44 | } catch (e) { 45 | reject(e) 46 | } 47 | }); 48 | } catch (e) { 49 | reject(e) 50 | } 51 | }); 52 | }; 53 | 54 | function setState(state) { 55 | this.pts = state.pts; 56 | this.date = state.date; 57 | this.qts = state.qts; 58 | this.unreadCount = state.unread_count; 59 | logger.info('set state', state.toPrintable()); 60 | console.log('set state', state.toPrintable()); 61 | } 62 | 63 | 64 | // Stop the emitter. 65 | Updates.prototype.stop = function () { 66 | logger.info('stop updates'); 67 | console.log('stop updates'); 68 | clientProxy.getClient().stopHttpPollLoop(); 69 | clientProxy.getClient().account.updateStatus(true); 70 | }; 71 | 72 | var instance; 73 | Updates.getInstance = function () { 74 | return instance = instance || new Updates(); 75 | }; 76 | 77 | // export the services 78 | module.exports = exports = Updates; -------------------------------------------------------------------------------- /lib/use-case/chat.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // import the dependencies 7 | require('requirish')._(module); 8 | require('colors'); 9 | 10 | var clientProxy = require('lib/client-proxy'); 11 | var ui = require('lib/user-interface'); 12 | var i18n = require('i18n/en-US'); 13 | var getLogger = require('get-log'); 14 | var logger = getLogger('use-case.select-chat'); 15 | 16 | var OPEN_TOTAL_MESSAGES = 10; 17 | var UPDATE_INTERVAL = 5000; 18 | 19 | function chat(peer) { 20 | return new Promise(function (fulfill, reject) { 21 | open(peer).then(function () { 22 | //fulfill(); 23 | }, reject); 24 | }); 25 | } 26 | 27 | function open(peer) { 28 | return new Promise(function (fulfill, reject) { 29 | ui.spinner(); 30 | try { 31 | clientProxy.getClient().messages.getHistory(peer, 0, 0, OPEN_TOTAL_MESSAGES).then(function (container) { 32 | try { 33 | ui.spinner(); 34 | // title 35 | var title; 36 | if (peer.instanceOf('api.type.InputPeerChat')) { 37 | title = container.chats.getById(peer.chat_id).title; 38 | } else { 39 | var user = container.users.getById(peer.user_id); 40 | title = user.first_name + ' ' + user.last_name; 41 | } 42 | ui.hRule(); 43 | console.log('\t' + title.bold); 44 | // messages 45 | var total = container && container.messages && container.messages.list ? container.messages.list.length : 0; 46 | logger.info('Message list retrieved, total messages ', total); 47 | if (total > 0) { 48 | var lastUsername = ''; 49 | for (var index = total - 1; index >= 0; index--) { 50 | lastUsername = renderMessage(container, index, lastUsername); 51 | } 52 | } 53 | // set history as read 54 | clientProxy.getClient().messages.readHistory(peer, 0, 0, true). 55 | then(fulfill, reject); 56 | } catch (e) { 57 | reject(e) 58 | } 59 | }, reject); 60 | } catch (e) { 61 | reject(e) 62 | } 63 | }); 64 | } 65 | 66 | function renderMessage(container, index, lastUserName) { 67 | // message 68 | var message = container.messages.list[index]; 69 | // check if a pure message 70 | if (!message.instanceOf('api.type.Message')) { 71 | ui.spacer(); 72 | console.log(message.toPrintable()); 73 | // todo: manage the MessageService type 74 | return lastUserName; 75 | } 76 | var msgText = message.media.instanceOf('api.type.MessageMediaEmpty') ? message.message : '[image]'; 77 | // user 78 | var userName = container.users.getById(message.from_id).first_name; 79 | // render 80 | if (userName !== lastUserName) { 81 | ui.spacer(); 82 | console.log(' ' + userName.bold.cyan); 83 | } 84 | console.log(' ' + msgText); 85 | return userName; 86 | } 87 | 88 | // export the services 89 | module.exports = exports = chat; -------------------------------------------------------------------------------- /i18n/en-US.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | module.exports = require('./mark-down') 7 | ({ 8 | "welcome": "Welcome to **TERMGRAM**, a terminal client to connect with _Telegram_.", 9 | "signUp": { 10 | "info": "To protect your Telegram identity you must create a local user/password,\n" + 11 | "**Termgram** will use your password to encrypt your authorization data saved locally.\n" + 12 | "Choose your **username**, it will be _local_ and it will _not be used_ by Telegram!", 13 | "choose_username": "Username", 14 | "choose_username_error": "The username must be between 3 and 12 characters long!", 15 | "choose_username_alreadyIn": "The username '**%s**' is already registered!", 16 | "choose_username_hello": "Hi **%s** !", 17 | "choose_passwordType_answer": "Which type of password fits your needs?", 18 | "choose_passwordType_option_simple": "**Numeric**, only 4 digits for fast typing", 19 | "choose_passwordType_option_simple_case": "Case: you are on your PC already protected by a secure password..", 20 | "choose_passwordType_option_strong": "**PassPhrase**, at least 10 characters long and " + 21 | "should contain lowercase/uppercase letters, numbers and symbols", 22 | "choose_passwordType_option_strong_case": "Case: you are connected remotely to a departmental server or to a nuclear plant or whatever..", 23 | "choose_passwordType": "Select the password type number", 24 | "choose_passwordType_chose": "You chose:", 25 | "choose_password_simple": "Password (4 digits)", 26 | "choose_password_strong": "Password (10+ chars)", 27 | "choose_password_retype": "Retype the password for confirmation", 28 | "choose_password_retype_error": "Hey, the _two passwords didn't match_!", 29 | "auth_inProgress": "Authorization on going, wait few seconds..", 30 | "ask_phoneNumber_info": "Provide your phone number in order to receive the verification code from Telegram", 31 | "ask_phoneNumber": "Phone number, must have the international code", 32 | "ask_authorizationCode": "Authorization code received by SMS", 33 | "phone_unregistered": "Your phone number is not registered, give some info to complete a new registration:", 34 | "choose_firstName": "First name", 35 | "choose_lastName": "Last name", 36 | "error_PHONE_NUMBER_INVALID": "Phone number **%s** is invalid! try again..", 37 | "error_PHONE_CODE_INVALID": "Authorization code **%s** is invalid! try again..", 38 | "error_PHONE_CODE_EXPIRED": "Authorization code is expired!", 39 | "error_FIRSTNAME_INVALID": "First name is invalid! try again..", 40 | "error_LASTNAME_INVALID": "Last name is invalid! try again.." 41 | }, 42 | "signIn": { 43 | "info": "Termgram Login", 44 | "welcome": "Welcome back **%s**!", 45 | "ask_username": "Username", 46 | "ask_password": "Password", 47 | "ask_signUp": "The username provided is new, do you want to create a new local account?" 48 | }, 49 | "selectChat": { 50 | "list": "Chats:", 51 | "noChatAvailable": "No chat available!", 52 | "userSelfName": "You", 53 | "choose_chat": "Choose a chat **number**" 54 | }, 55 | "ui": { 56 | "askConfirmation": "y/n", 57 | "askConfirmation_defaultYes": "_Y_/n", 58 | "askConfirmation_defaultNo": "y/_N_", 59 | "error": { 60 | "askWord": "Hey, you must enter only alphanumeric chars!", 61 | "askNumeric": "Hey, you must enter only numeric chars!", 62 | "askPhone": "Hey, this is not an international phone number!", 63 | "askPassword": "Hey, the password didn't match the requirements!" 64 | } 65 | }, 66 | "exit": "Are you sure you want to exit?" 67 | }); -------------------------------------------------------------------------------- /termgram.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // clear the term 7 | clearTerminal(); 8 | 9 | // setup the fs 10 | var fs = require('fs'); 11 | var userHome = (process.env.HOME || process.env.USERPROFILE) + '/.termgram'; 12 | var logFolder = userHome + '/log'; 13 | try { 14 | fs.mkdirSync(userHome, '0770'); 15 | fs.mkdirSync(logFolder, '0770'); 16 | } catch (e) { 17 | } 18 | 19 | // setup the logger 20 | process.env.LOGGER_FILE = logFolder + '/termgram'; 21 | var getLogger = require('get-log'); 22 | getLogger.PROJECT_NAME = 'termgram'; 23 | var logger = getLogger('main'); 24 | 25 | // import other dependencies 26 | require('colors'); 27 | require('telegram.link')(getSignature()); 28 | var ui = require('./lib/user-interface'); 29 | var i18n = require('./i18n/en-US'); 30 | var signUp = require('./lib/use-case/sign-up'); 31 | var signIn = require('./lib/use-case/sign-in'); 32 | var Updates = require('./lib/updates'); 33 | var selectChat = require('./lib/use-case/select-chat'); 34 | var chat = require('./lib/use-case/chat'); 35 | var userData = require('./lib/user-data'); 36 | userData.setBaseFolder(userHome); 37 | 38 | 39 | // begin 40 | function main() { 41 | var users = userData.retrieveUsernameList(); 42 | console.log(i18n.welcome); 43 | ui.spacer(); 44 | 45 | function doSignUp() { 46 | signUp(users).then(function (res) { 47 | logger.info('signUp res: %s', res); 48 | home(); 49 | }, function (error) { 50 | console.log('signUp error: ', error.stack); 51 | shutdown(); 52 | }); 53 | } 54 | 55 | // if no users 56 | if (users.length == 0) { 57 | logger.info('User list is empty, sign up a new user.'); 58 | doSignUp(); 59 | } else { 60 | signIn(users).then(function (res) { 61 | logger.info('signIn res:', res); 62 | home(); 63 | }, function (error) { 64 | if (error) { 65 | console.log('signIn error: ', error.stack); 66 | shutdown(); 67 | } else { 68 | doSignUp(); 69 | } 70 | }); 71 | } 72 | ui.events.on(ui.EVENT.EXIT, function () { 73 | ui.askConfirmationInput(i18n.exit, true).then(shutdown, function () { 74 | console.log('nothing to do, again...') 75 | }); 76 | }); 77 | } 78 | 79 | // userHome page 80 | function home() { 81 | var updates = Updates.getInstance(); 82 | updates.start().then(function() { 83 | ui.spacer(); 84 | selectChat().then(function (peer) { 85 | if (peer) { 86 | ui.spacer(); 87 | chat(peer).then(function () { 88 | console.log('nothing to do, now...'); 89 | shutdown(); 90 | }, function (error) { 91 | console.log('chat error: ', error.stack); 92 | shutdown(); 93 | }); 94 | } else { 95 | console.log('No chat selected'); 96 | console.log('nothing to do, now...'); 97 | shutdown(); 98 | } 99 | }, function (error) { 100 | console.log('selectChat error: ', error.stack); 101 | shutdown(); 102 | }); 103 | 104 | }, function (error) { 105 | console.log('update-emitter error: ', error.stack); 106 | shutdown(); 107 | }); 108 | } 109 | 110 | // end 111 | function shutdown() { 112 | ui.close(); 113 | Updates.getInstance().stop(); 114 | } 115 | 116 | // clear the term 117 | function clearTerminal() { 118 | process.stdout.write('\033c'); 119 | } 120 | 121 | // get the application signature 122 | function getSignature() { 123 | return (' T E R M G R A M '.bold + ' ' + require('./package.json').version ).cyan; 124 | } 125 | 126 | // run 127 | main(); -------------------------------------------------------------------------------- /lib/use-case/select-chat.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // import the dependencies 7 | require('requirish')._(module); 8 | require('colors'); 9 | 10 | var api = require('telegram.link')(); 11 | var clientProxy = require('lib/client-proxy'); 12 | var ui = require('lib/user-interface'); 13 | var i18n = require('i18n/en-US'); 14 | var getLogger = require('get-log'); 15 | var logger = getLogger('use-case.select-chat'); 16 | 17 | function selectChat() { 18 | return new Promise(function (fulfill, reject) { 19 | ui.spinner(); 20 | try { 21 | clientProxy.getClient().messages.getDialogs(0, 0, 0).then(function (container) { 22 | try { 23 | ui.spinner(); 24 | var total = container && container.dialogs && container.dialogs.list ? container.dialogs.list.length : 0; 25 | logger.info('Chat list retrieved, total chats ', total); 26 | if (total > 0) { 27 | ui.hRule(); 28 | console.log('\t' + i18n.selectChat.list); 29 | ui.spacer(); 30 | var peers = {}; 31 | for (var index = total - 1; index >= 0; index--) { 32 | peers[index] = renderChat(container, index); 33 | ui.spacer(); 34 | } 35 | ui.askNumericInput(i18n.selectChat.choose_chat, '1').then(function (index) { 36 | logger.info('Selected chat:', index); 37 | fulfill(peers[index - 1]); 38 | }); 39 | } else { 40 | console.log(i18n.selectChat.noChatAvailable); 41 | fulfill(null); 42 | } 43 | } catch (e) { 44 | reject(e) 45 | } 46 | }, reject); 47 | } catch (e) { 48 | reject(e) 49 | } 50 | }); 51 | } 52 | 53 | function renderChat(container, index) { 54 | var dialog = container.dialogs.list[index]; 55 | var unreadCount = dialog.unread_count > 0 ? ('(' + dialog.unread_count + ')').bold : ''; 56 | var message = container.messages.getById(dialog.top_message); 57 | var msgText = message.media.instanceOf('api.type.MessageMediaEmpty') ? message.message : '[image]'; 58 | var maxLength = 50; 59 | msgText = msgText.length > maxLength ? msgText.slice(0, maxLength - 3) + '...' : msgText; 60 | 61 | var user; 62 | var title; 63 | var peer = dialog.peer; 64 | var inputPeer; 65 | if (peer.instanceOf('api.type.PeerChat')) { 66 | var chat = container.chats.getById(peer.chat_id); 67 | title = chat.title; 68 | user = container.users.getById(message.from_id); 69 | inputPeer = new api.type.InputPeerChat(); 70 | inputPeer.chat_id = peer.chat_id; 71 | } else if (peer.instanceOf('api.type.PeerUser')) { 72 | user = container.users.getById(peer.user_id); 73 | inputPeer = retrieveInputPeer(user); 74 | } else { 75 | throw new Error('Unknown peer type ' + peer.getTypeName()); 76 | } 77 | 78 | var userName = user.instanceOf('api.type.UserSelf') ? 79 | i18n.selectChat.userSelfName : 80 | (user.first_name + ' ' + user.last_name); 81 | title = title ? title.bold + ' - ' + userName : userName.bold; 82 | 83 | ui.option(index + 1, title + ' ' + unreadCount); 84 | console.log('\t\t' + msgText); 85 | return inputPeer; 86 | } 87 | 88 | function retrieveInputPeer(user) { 89 | var inputPeer; 90 | if (user.instanceOf('api.type.UserContact')) { 91 | inputPeer = new api.type.InputPeerContact(); 92 | inputPeer.user_id = user.id; 93 | } else if (user.instanceOf('api.type.UserRequest') || user.instanceOf('api.type.UserForeign')) { 94 | inputPeer = new api.type.InputPeerForeign(); 95 | inputPeer.user_id = user.id; 96 | inputPeer.access_hash = user.access_hash; 97 | } else { 98 | throw new Error('Unknown user type ' + user.getTypeName()); 99 | } 100 | return inputPeer; 101 | } 102 | 103 | // export the services 104 | module.exports = exports = selectChat; -------------------------------------------------------------------------------- /lib/use-case/sign-in.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // import the dependencies 7 | require('requirish')._(module); 8 | require('colors'); 9 | 10 | var clientProxy = require('lib/client-proxy'); 11 | var ui = require('lib/user-interface'); 12 | var UserData = require('lib/user-data'); 13 | var i18n = require('i18n/en-US'); 14 | var getLogger = require('get-log'); 15 | var logger = getLogger('use-case.sign-in'); 16 | var userData; 17 | 18 | function signIn(users) { 19 | return new Promise(function (fulfill, reject) { 20 | console.log(i18n.signIn.info); 21 | askUsername(users).then(function (username) { 22 | userData = UserData.loadUser(username); 23 | logger.info('User %s data loaded', username, userData); 24 | askPassword(username).then(function () { 25 | ui.spacer(); 26 | console.log(i18n.signIn.welcome, username); 27 | clientProxy.createClientForUser(userData.getDataCenter()).then(fulfill, reject); 28 | /* 29 | var clientPromise = clientProxy.createClientForUser(userData.getDataCenter()); 30 | clientPromise.then(function () { 31 | try{ 32 | //var peer = new api.type.InputPeerContact({props: {user_id: ....}}); 33 | //var msg = 'Test'; 34 | // 35 | //clientProxy.getClient().messages.sendMessage(peer, msg, 9876543211).then(function(sentMsg) { 36 | // console.log('sentMsg:', sentMsg.toPrintable()); 37 | // fulfill(); 38 | //}, reject); 39 | //clientProxy.getClient().messages.getHistory(peer, 0, 0, 100).then(function(messages) { 40 | // console.log('messages:', messages.toPrintable()); 41 | // fulfill(); 42 | //}, reject); 43 | 44 | //clientProxy.getClient().messages.getDialogs(0, 0, 1).then(function(dialogs) { 45 | // console.log('dialogs:', dialogs.toPrintable()); 46 | // fulfill(); 47 | //}, reject); 48 | 49 | //clientProxy.getClient().contacts.getContacts('').then(function(contacts) { 50 | // console.log('contacts:', contacts.toPrintable()); 51 | // fulfill(); 52 | //}, reject); 53 | 54 | //clientProxy.getClient().updates.getState().then(function(state) { 55 | // console.log('state:', state.toPrintable()); 56 | // fulfill(); 57 | //}, reject); 58 | 59 | console.log('client created'); 60 | } catch (e) { 61 | reject(e) 62 | } 63 | }); 64 | */ 65 | }, reject) 66 | }, function () { 67 | ui.askConfirmationInput(i18n.signIn.ask_signUp, true).then( 68 | function () { 69 | ui.spacer(); 70 | reject(); 71 | }, 72 | function () { 73 | ui.spacer(); 74 | signIn(users); 75 | }); 76 | }); 77 | }); 78 | } 79 | 80 | 81 | function askUsername(usernameList) { 82 | return new Promise(function (fulfill, reject) { 83 | ui.askWordInput(i18n.signIn.ask_username, process.env.USER).then(function (username) { 84 | if (usernameList.indexOf(username) < 0) { 85 | reject(); 86 | } else { 87 | fulfill(username); 88 | } 89 | }); 90 | }) 91 | } 92 | 93 | var attempts = 3; 94 | function askPassword() { 95 | return new Promise(function (fulfill, reject) { 96 | var authKeyBuffer = userData.getAuthKey(); 97 | (function ask() { 98 | ui.askPasswordInput(i18n.signIn.ask_password).then(function (password) { 99 | var authKey = clientProxy.setAuthKey(authKeyBuffer, password); 100 | if (!authKey) { 101 | if (--attempts > 0) { 102 | ask(); 103 | } else { 104 | reject(new Error('User failed to authenticate after 3 attempts.')); 105 | } 106 | } else { 107 | fulfill() 108 | } 109 | }); 110 | })(); 111 | }) 112 | } 113 | 114 | 115 | // export the services 116 | module.exports = exports = signIn; -------------------------------------------------------------------------------- /lib/user-interface.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // import the dependencies 7 | require('requirish')._(module); 8 | var outStream = new (require('mute-stream'))({replace: '*'}); 9 | outStream.pipe(process.stdout); 10 | require('colors'); 11 | var i18n = require('i18n/en-US'); 12 | var rl = require('readline').createInterface(process.stdin, outStream); 13 | var events = new (require('events').EventEmitter); 14 | 15 | 16 | function askWordInput(question, defaultAnswer) { 17 | return new Promise(function (fulfill) { 18 | ask(question, defaultAnswer, /^[\w\+]+$/, i18n.ui.error.askWord, function (answer) { 19 | fulfill(answer); 20 | }) 21 | }); 22 | } 23 | 24 | function askPasswordInput(question, regEx) { 25 | return new Promise(function (fulfill, reject) { 26 | rl.setPrompt(question + ': '); 27 | rl.prompt(); 28 | outStream.mute(); 29 | rl.once('line', function (password) { 30 | outStream.unmute(); 31 | if (!regEx) { 32 | fulfill(password); 33 | } else if (password.match(regEx)) { 34 | fulfill(password); 35 | } else { 36 | console.log(i18n.ui.error.askPassword); 37 | reject(); 38 | } 39 | }); 40 | }); 41 | } 42 | 43 | function askNumericInput(question, defaultAnswer) { 44 | return new Promise(function (fulfill) { 45 | ask(question, defaultAnswer, /^\d+$/, i18n.ui.error.askNumeric, function (answer) { 46 | fulfill(answer); 47 | }) 48 | }); 49 | } 50 | 51 | function askPhoneInput(question, defaultAnswer) { 52 | return new Promise(function (fulfill) { 53 | ask(question, defaultAnswer, /^(\+|00)\d{5,}$/, i18n.ui.error.askPhone, function (answer) { 54 | fulfill(answer); 55 | }) 56 | }); 57 | } 58 | 59 | function ask(question, defaultAnswer, regex, errorMsg, callback) { 60 | var postfix = defaultAnswer ? ' (' + defaultAnswer.underline + ')' : ''; 61 | rl.question(question + postfix + ': ', function (answer) { 62 | if (defaultAnswer && answer.length === 0) { 63 | callback(defaultAnswer); 64 | } else if (answer.match(regex)) { 65 | callback(answer); 66 | } else { 67 | console.log(errorMsg); 68 | ask(question, defaultAnswer, regex, errorMsg, callback); 69 | } 70 | }); 71 | } 72 | 73 | function askConfirmationInput(question, defaultYes) { 74 | return new Promise(function (fulfill, reject) { 75 | var postfix = defaultYes === undefined ? i18n.ui.askConfirmation : 76 | (defaultYes === false ? i18n.ui.askConfirmation_defaultNo : i18n.ui.askConfirmation_defaultYes); 77 | rl.question(question + ' (' + postfix + '): ', function (answer) { 78 | if (defaultYes !== undefined && answer.length === 0) { 79 | if (defaultYes) { 80 | fulfill(); 81 | } else { 82 | reject(); 83 | } 84 | } else if (answer.match(/^y(es)?$/i)) { 85 | fulfill(); 86 | } else { 87 | reject(); 88 | } 89 | }); 90 | }); 91 | } 92 | 93 | function spacer(rows) { 94 | var spacer = ''; 95 | if (rows) { 96 | for (var i = 1; i < rows; i++) { 97 | spacer += '\n'; 98 | } 99 | } 100 | console.log(spacer); 101 | } 102 | 103 | function hRule() { 104 | console.log('________________________________________\n'); 105 | } 106 | 107 | var interval; 108 | function spinner() { 109 | if(interval) { 110 | clearInterval(interval); 111 | outStream.write(' \b'); 112 | rl.resume(); 113 | return; 114 | } 115 | var chars = ['\\', '|', '/', '-']; 116 | var i = 0; 117 | interval = setInterval(function () { 118 | outStream.write(chars[i++ % 4]); 119 | outStream.write('\b'); 120 | }, 200); 121 | rl.pause(); 122 | } 123 | 124 | function option(key, msg) { 125 | console.log('\t[ ' + ('' + key).bold.cyan + ' ] ' + msg); 126 | } 127 | 128 | 129 | rl.on('SIGINT', function () { 130 | events.emit(exports.EVENT.EXIT); 131 | }); 132 | 133 | function close() { 134 | rl.close(); 135 | } 136 | 137 | exports.askWordInput = askWordInput; 138 | exports.askPasswordInput = askPasswordInput; 139 | exports.askNumericInput = askNumericInput; 140 | exports.askPhoneInput = askPhoneInput; 141 | exports.askConfirmationInput = askConfirmationInput; 142 | exports.spacer = spacer; 143 | exports.hRule = hRule; 144 | exports.spinner = spinner; 145 | exports.option = option; 146 | exports.close = close; 147 | exports.events = events; 148 | exports.EVENT = { 149 | EXIT: 'exit' 150 | }; 151 | -------------------------------------------------------------------------------- /lib/client-proxy.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // import the dependencies 7 | var telegramLink = require('telegram.link')(); 8 | var getLogger = require('get-log'); 9 | var logger = getLogger('client-factory'); 10 | var os = require('os'); 11 | 12 | // set the environment 13 | var app = { 14 | // NOTE: if you FORK the project you MUST use your APP ID. 15 | // Otherwise YOUR APPLICATION WILL BE BLOCKED BY TELEGRAM 16 | // You can obtain your own APP ID for your application here: https://my.telegram.org 17 | id: 19896, 18 | hash: 'b43316c048960d6a599d4fe3497c3610', 19 | version: require('../package.json').version, 20 | lang: 'en', 21 | deviceModel: os.type().replace('Darwin', 'OS_X'), 22 | systemVersion: os.platform() + '/' + os.release() 23 | //,connectionType: 'TCP' 24 | }; 25 | 26 | //var primaryDC = telegramLink.TEST_PRIMARY_DC; 27 | var primaryDC = telegramLink.PROD_PRIMARY_DC; 28 | 29 | var client; 30 | var dataCenters; 31 | var authKey; 32 | 33 | function createClientForUser(dataCenter) { 34 | if (client) { 35 | throw new Error('Authorization key already created.') 36 | } 37 | return new Promise(function (fulfill, reject) { 38 | client = createClient(dataCenter || primaryDC, function () { 39 | client.once('error', reject); 40 | if(!dataCenter) { 41 | logger.info('Look for the nearest data centers..'); 42 | client.getDataCenters(function (dcs) { 43 | client.removeListener('error', reject); 44 | dataCenters = dcs; 45 | logger.info('Data centers: ', dataCenters.toPrintable()); 46 | //check if the current data-center is also the nearest one 47 | if (dataCenters.current != dataCenters.nearest) { 48 | var newDc = dataCenters[dataCenters.nearest]; 49 | logger.info('Change the current data-center with the nearest one %s = %s:%s', dataCenters.nearest, newDc.host, newDc.port); 50 | authKey = null; 51 | client = createClient(newDc, fulfill, reject); 52 | } else { 53 | fulfill(dataCenters[dataCenters.current]); 54 | } 55 | }); 56 | } else { 57 | fulfill(dataCenter); 58 | } 59 | }, reject); 60 | }); 61 | } 62 | 63 | function sendCodeToPhone(phoneNumber) { 64 | return new Promise(function (fulfill, reject) { 65 | client.once('error', reject); 66 | client.auth.sendCode(phoneNumber, 5, 'en', function (result) { 67 | if (result.instanceOf('mtproto.type.Rpc_error')) { 68 | switch (result.error_code) { 69 | // check if the phone number requires a different data-center (PHONE_MIGRATE_X) 70 | case 303: 71 | var requiredDCName = 'DC_' + result.error_message.slice(-1); 72 | logger.info('The phone number %s requires the data-center %s', phoneNumber, requiredDCName); 73 | var requiredDC = dataCenters[requiredDCName]; 74 | client = createClient(requiredDC, function () { 75 | client.sendCodeToPhone(phoneNumber, fulfill); 76 | }, reject); 77 | break; 78 | default : 79 | reject(new Error(result.error_message)); 80 | } 81 | } else { 82 | fulfill(result); 83 | } 84 | }); 85 | }); 86 | } 87 | 88 | function signIn(phoneNumber, codeHash, code) { 89 | return new Promise(function (fulfill, reject) { 90 | client.once('error', reject); 91 | client.auth.signIn(phoneNumber, codeHash, code, function (result) { 92 | if (result.instanceOf('mtproto.type.Rpc_error')) { 93 | if (result.error_message == 'PHONE_NUMBER_UNOCCUPIED') { 94 | fulfill(result); 95 | } else { 96 | reject(new Error(result.error_message)); 97 | } 98 | } else { 99 | fulfill(result); 100 | } 101 | }); 102 | }); 103 | } 104 | 105 | 106 | function getClient() { 107 | return client; 108 | } 109 | 110 | function createClient(dataCenter, fulfill, reject) { 111 | if (client) { 112 | client.end(); 113 | client = null; 114 | } 115 | logger.info('Start to create the client connecting to DC %s:%s..', dataCenter.host, dataCenter.port); 116 | var newClient = telegramLink.createClient(app, dataCenter, function () { 117 | if (!authKey) { 118 | logger.info('Start to create the auth-key..'); 119 | newClient.createAuthKey(function (auth) { 120 | logger.info('..the auth-key [%s] was created, the client is now ready.', auth.key.id.toString('hex')); 121 | authKey = auth.key; 122 | fulfill(dataCenter); 123 | }); 124 | } else { 125 | logger.info('auth-key already set'); 126 | fulfill(dataCenter); 127 | } 128 | 129 | }); 130 | newClient.once('error', reject); 131 | logger.info('..client created.'); 132 | return newClient; 133 | } 134 | 135 | function getAuthKey() { 136 | return authKey; 137 | } 138 | 139 | function setAuthKey(authKeyBuffer, password) { 140 | authKey = telegramLink.retrieveAuthKey(authKeyBuffer, password); 141 | app.authKey = authKey; 142 | return authKey; 143 | } 144 | 145 | // export the services 146 | exports.createClientForUser = createClientForUser; 147 | exports.sendCodeToPhone = sendCodeToPhone; 148 | exports.signIn = signIn; 149 | exports.getAuthKey = getAuthKey; 150 | exports.setAuthKey = setAuthKey; 151 | exports.getClient = getClient; 152 | -------------------------------------------------------------------------------- /lib/use-case/sign-up.js: -------------------------------------------------------------------------------- 1 | // Termgram 2 | // Copyright 2015 Enrico Stara 'enrico.stara@gmail.com' 3 | // Released under the MIT License 4 | // http://termgram.me 5 | 6 | // import the dependencies 7 | require('requirish')._(module); 8 | require('colors'); 9 | var clientProxy = require('lib/client-proxy'); 10 | var ui = require('lib/user-interface'); 11 | var UserData = require('lib/user-data'); 12 | var i18n = require('i18n/en-US'); 13 | var getLogger = require('get-log'); 14 | var logger = getLogger('use-case.sign-up'); 15 | 16 | // constants 17 | var PASSWORD_TYPE = { 18 | SIMPLE: '1', 19 | STRONG: '2' 20 | }; 21 | 22 | function signUp(users) { 23 | return new Promise(function (fulfill, reject) { 24 | console.log(i18n.signUp.info); 25 | ui.spacer(); 26 | askUsername(users, function (username) { 27 | logger.info('New user name: %s', username); 28 | var userData = new UserData({name: username}); 29 | ui.spacer(); 30 | console.log(i18n.signUp.choose_username_hello, username); 31 | var clientPromise = clientProxy.createClientForUser(); 32 | askPassword(function (password) { 33 | console.log(i18n.signUp.auth_inProgress); 34 | ui.spinner(); 35 | clientPromise.then(function (dataCenter) { 36 | userData.setDataCenter(dataCenter); 37 | ui.spinner(); 38 | ui.spacer(); 39 | askPhoneNumberToSendCode().then(function (res) { 40 | logger.info('Send code to phone number response: %s', res.sendCodeRes.toPrintable()); 41 | // ask the received code 42 | askAuthCode(res.phoneNumber, res.sendCodeRes.phone_code_hash).then(function (authCodeRes) { 43 | function saveUser(signUpRes) { 44 | try { 45 | var authKeyBuffer = clientProxy.getAuthKey().encrypt(password); 46 | userData.setAuthKey(authKeyBuffer); 47 | userData.save(); 48 | logger.info('User data saved'); 49 | fulfill(signUpRes); 50 | } catch (e) { 51 | reject(e); 52 | } 53 | } 54 | logger.info('Auth code response: %s', authCodeRes.res.toPrintable()); 55 | if (res.sendCodeRes.phone_registered) { 56 | saveUser(authCodeRes); 57 | } else { 58 | askFirstLastName(res.phoneNumber, res.sendCodeRes.phone_code_hash, authCodeRes.code).then(function(firstLastNamesRes) { 59 | saveUser(firstLastNamesRes); 60 | }, reject) 61 | } 62 | }, reject); 63 | }); 64 | }, reject); 65 | }); 66 | }, reject); 67 | }); 68 | } 69 | 70 | 71 | function askUsername(users, callback) { 72 | ui.askWordInput(i18n.signUp.choose_username, process.env.USER).then(function (user) { 73 | if (user.length < 3 || user.length > 12) { 74 | console.log(i18n.signUp.choose_username_error); 75 | ui.spacer(); 76 | askUsername(users, callback); 77 | } else if (users.indexOf(user) > -1) { 78 | console.log(i18n.signUp.choose_username_alreadyIn, user); 79 | ui.spacer(); 80 | askUsername(users, callback); 81 | } else { 82 | callback(user); 83 | } 84 | }); 85 | } 86 | 87 | function askPassword(callback) { 88 | console.log(i18n.signUp.choose_passwordType_answer); 89 | ui.spacer(); 90 | ui.option(PASSWORD_TYPE.SIMPLE, 91 | i18n.signUp.choose_passwordType_option_simple + '\n (' + i18n.signUp.choose_passwordType_option_simple_case + ')'); 92 | ui.spacer(); 93 | ui.option(PASSWORD_TYPE.STRONG, 94 | i18n.signUp.choose_passwordType_option_strong + '\n (' + i18n.signUp.choose_passwordType_option_strong_case + ')'); 95 | ui.spacer(); 96 | (function askType() { 97 | ui.askNumericInput(i18n.signUp.choose_passwordType, '1').then(function (passwordType) { 98 | switch (passwordType) { 99 | case PASSWORD_TYPE.SIMPLE: 100 | ui.spacer(); 101 | console.log('You chose: ' + i18n.signUp.choose_passwordType_option_simple); 102 | (function askSimple() { 103 | askTwice(i18n.signUp.choose_password_simple, 104 | i18n.signUp.choose_password_retype, /^\d{4}$/).then(callback, askSimple); 105 | })(); 106 | break; 107 | case PASSWORD_TYPE.STRONG: 108 | ui.spacer(); 109 | console.log(i18n.signUp.choose_passwordType_chose + ' ' + i18n.signUp.choose_passwordType_option_strong); 110 | (function askStrong() { 111 | askTwice(i18n.signUp.choose_password_strong, 112 | i18n.signUp.choose_password_retype, /^.{10,}$/).then(callback, askStrong); 113 | })(); 114 | break; 115 | default: 116 | askType(); 117 | } 118 | }); 119 | })(); 120 | function askTwice(question, retypeQuestion, regEx) { 121 | return new Promise(function (fulfill, reject) { 122 | ui.askPasswordInput(question, regEx).then(function (password) { 123 | ui.askPasswordInput(retypeQuestion, regEx).then(function (password2) { 124 | if (password == password2) { 125 | fulfill(password); 126 | } else { 127 | console.log(i18n.signUp.choose_password_retype_error); 128 | ui.spacer(); 129 | reject(); 130 | } 131 | }, function () { 132 | ui.spacer(); 133 | reject(); 134 | }); 135 | }, function () { 136 | ui.spacer(); 137 | reject(); 138 | }); 139 | }); 140 | } 141 | } 142 | 143 | function askPhoneNumberToSendCode() { 144 | return new Promise(function (fulfill, reject) { 145 | console.log(i18n.signUp.ask_phoneNumber_info); 146 | (function askPhone() { 147 | ui.askPhoneInput(i18n.signUp.ask_phoneNumber).then(function (phoneNumber) { 148 | logger.info('Phone number: %s', phoneNumber); 149 | ui.spinner(); 150 | clientProxy.sendCodeToPhone(phoneNumber).then(function (res) { 151 | ui.spinner(); 152 | fulfill({ 153 | phoneNumber: phoneNumber, 154 | sendCodeRes: res 155 | }); 156 | }, function (error) { 157 | console.log(i18n.signUp['error_' + error.message], phoneNumber); 158 | if (error.message == 'PHONE_NUMBER_INVALID') { 159 | askPhone(); 160 | } else { 161 | reject(error); 162 | } 163 | }) 164 | }); 165 | })(); 166 | }); 167 | } 168 | 169 | function askAuthCode(phoneNumber, code_hash) { 170 | return new Promise(function (fulfill, reject) { 171 | (function askCode() { 172 | ui.askNumericInput(i18n.signUp.ask_authorizationCode).then(function (code) { 173 | ui.spinner(); 174 | // check if the authorization is valid 175 | clientProxy.signIn( 176 | phoneNumber, 177 | code_hash, 178 | code).then(function (res) { 179 | ui.spinner(); 180 | fulfill({ 181 | code: code, 182 | res: res 183 | }); 184 | }, function (error) { 185 | console.log(i18n.signUp['error_' + error.message], code); 186 | if (error.message == 'PHONE_CODE_INVALID') { 187 | askCode(); 188 | } else { 189 | reject(error); 190 | } 191 | }); 192 | }); 193 | })(); 194 | }); 195 | } 196 | 197 | function askFirstLastName(phoneNumber, code_hash, code) { 198 | return new Promise(function (fulfill, reject) { 199 | console.log(i18n.signUp.phone_unregistered); 200 | (function askNames() { 201 | ui.askWordInput(i18n.signUp.choose_firstName).then(function (firstName) { 202 | ui.askWordInput(i18n.signUp.choose_lastName).then(function (lastName) { 203 | ui.spinner(); 204 | clientProxy.getClient().auth.signUp( 205 | phoneNumber, 206 | code_hash, 207 | code, 208 | firstName, 209 | lastName 210 | ).then(function (res) { 211 | ui.spinner(); 212 | fulfill(res); 213 | }, function (error) { 214 | console.log(i18n.signUp['error_' + error.message], code); 215 | if (error.message == 'FIRSTNAME_INVALID' || error.message == 'LASTNAME_INVALID') { 216 | askNames(); 217 | } else { 218 | reject(error); 219 | } 220 | }); 221 | }); 222 | }); 223 | })(); 224 | }); 225 | } 226 | 227 | // export the services 228 | module.exports = exports = signUp; --------------------------------------------------------------------------------