UNPKG

telegram-node-singlebot

Version:
985 lines (892 loc) 31.2 kB
'use strict' const InlineKeyboardButton = require('../models/InlineKeyboardButton') const InlineKeyboardMarkup = require('../models/InlineKeyboardMarkup') const ReplyKeyboardMarkup = require('../models/ReplyKeyboardMarkup') const KeyboardButton = require('../models/KeyboardButton') class Scope { /** * * @param {Update} update * @param {TelegramApi} api * @param {BaseScopeExtension[]} extensions * @param {Function[]} waitingRequests * @param {Object} waitingCallbackQueries * @param {BaseLogger} logger * @param {Function} processUpdate * @param {TelegramSessionStorage} sessionStorage * @param {Function} waitForUpdate */ constructor( update, api, extensions, waitingRequests, waitingCallbackQueries, logger, sessionStorage, waitForUpdate ) { this._api = api this._update = update /** * * @type {BaseScopeExtension[]} * @private */ this._extensions = extensions this._waitingRequests = waitingRequests this._waitingCallbackQueries = waitingCallbackQueries this._isEditedMessage = update.editedMessage ? true : false this._callbackQuery = update.callbackQuery this._message = update.message || update.editedMessage || update.callbackQuery.message this._chatId = this._message.chat.id this._userId = this._message.from.id this._messageId = this._message.messageId this._fromGroupChat = !(this._userId === this._chatId) this._logger = logger this._sessionStorage = sessionStorage this._waitForUpdate = waitForUpdate this._extensions.forEach(extension => { const extensionInstance = new extension(this) this[extensionInstance.name] = extensionInstance.process }) if(update.callbackQuery) this.answerCallbackQuery() } /** * @returns {TelegramSessionStorage} */ get sessionStorage() { return this._sessionStorage } /** * @returns {BaseStorage} */ get storage() { return this._sessionStorage } /** * * @returns {Update} */ get update() { return this._update } /** * * @returns {Message} */ get message() { return this._message } /** * * @returns {Message} */ get defaultmessage() { return {reply_markup: JSON.stringify({hide_keyboard: true}), parse_mode: 'HTML', disable_web_page_preview: true} } /** * * @returns {CallBackQuery} */ get callbackQuery() { return this._callbackQuery } /** * * @returns {number} */ get chatId() { return this._chatId } /** * * @returns {string} */ get firstName() { return this._message.from.firstName } /** * * @returns {number} */ get userId() { return this._userId } /** * * @returns {number} */ get messageId() { return this._messageId } /** * * @returns {boolean} */ get idFromGroupChat() { return this._fromGroupChat } /** * * @returns {TelegramApi} */ get api() { return this._api } /** * @param {string} key * @returns {Promise.<*>} */ getUserSession(key) { return this._sessionStorage.getUserSession(this.userId, key) } /** * @param {string} key * @param {*} value * @returns {Promise} */ setUserSession(key, value) { return this._sessionStorage.setUserSession(this.userId, key, value) } /** * @param {string} key * @returns {Promise.<*>} */ getChatSession(key) { return this._sessionStorage.getChatSession(this.chatId, key) } /** * @param {string} key * @param {*} value * @returns {Promise} */ setChatSession(key, value) { return this._sessionStorage.setChatSession(this.chatId, key, value) } /** * * @returns {BaseLogger} */ get logger() { return this._logger } /** * * @returns {boolean} */ get isEditedMessage() { return this._isEditedMessage } /** * After calling this the next update * from current user will be passed to promise * * @returns {Promise<Scope>} */ get waitForRequest() { return new Promise(resolve => { this._waitingRequests[this.chatId] = resolve this._waitForUpdate(this.chatId) }) } /** * * @param text * @param keyboard * @param callback * @param close * @param updatekeyboard * @constructor */ SessionSetMessage(text, keyboard, callback, close, updatekeyboard){ this._sessionStorage.getUserSession(this.userId, 'session').then(messageId => { if((isNaN(messageId) || (messageId != this.messageId && !this._callbackQuery) || close)){ if(this._callbackQuery) this.SessionClose(this._messageId, null, ()=>{}) this._api.sendMessage(this.userId, text, { parse_mode: 'HTML', reply_markup: keyboard }).then(message => { this._sessionStorage.setUserSession(this.userId, 'session', message.messageId).then(() => { callback() }) }) }else if ((messageId == this.messageId) && !updatekeyboard) { this._api.editMessageText(text, { chat_id: this._chatId, message_id: messageId, parse_mode: 'HTML', reply_markup: keyboard }).then(() => { callback() }) }else if((messageId != this.messageId) && updatekeyboard){ this.api.editMessageReplyMarkup({ chat_id: this._chatId, message_id: messageId, reply_markup: keyboard }).then(() => { callback() }) }else{ callback() } }) } /** * * @param onthisquery * @param onquery * @param onmessage * @constructor */ SessionGetMessage(onthisquery, onquery, onmessage){ this._sessionStorage.getUserSession(this.userId, 'session').then(messageId => { if(this._callbackQuery){ if(messageId == this._messageId){ onthisquery(messageId) }else { onquery(messageId) } }else { onmessage(messageId) } }) } /** * * @param messageId * @param text * @param callback * @constructor */ SessionClose(messageId, text, callback){ this._api.editMessageText(text || lg.closesession, { chat_id: this._chatId, message_id: messageId, parse_mode: 'HTML' }).then(() => { callback() }) } /** * * @param menuData * @param stop * @param updatekeyboard */ runMenu(menuData, stop, updatekeyboard){ if(typeof menuData.title == 'undefined') throw Error('titleis not set') if(typeof menuData.menu == 'undefined') throw Error('menu is not set') const message = '<b>'+menuData.title+'</b>'+((typeof menuData.message == 'undefined') ? '' : '\n'+menuData.message) var keyboard = [], action = {} for (var i = 0; i < menuData.menu.length; i++){ var array = [] for (var q = 0; q < menuData.menu[i].length; q++){ let random = Math.random().toString(36).substring(7) array.push({ text: menuData.menu[i][q].text, callback_data: random }) action[random] = menuData.menu[i][q].action } keyboard.push(array) if(menuData.menu.length-1 == i){ if(typeof menuData.back != 'undefined'){ let random = Math.random().toString(36).substring(7) keyboard.push([{ text: lg.back, callback_data: random }]) action[random] = menuData.back } keyboard = JSON.stringify({inline_keyboard: keyboard}) } } this.SessionSetMessage(message, keyboard, () => { this.waitForRequest.then($ => { $.SessionGetMessage((messageId) => { if(typeof action[$.message.text] != 'undefined') action[$.message.text]($) }, (messageId) => { $.SessionClose($.messageId, null, () => { $.runMenu(menuData, null, true) }) }, (messageId) => { $.SessionClose(messageId, null, () => { $.runMenu(menuData) }) }) }) }, stop, updatekeyboard) } /** * * @param menuData * @param offset * @param stop * @param updatekeyboard */ runManyMenu(menuData, offset, stop, updatekeyboard){ if(menuData == null || typeof menuData == 'undefined') throw Error('menuData for not set runManyMenu') if(offset == null || typeof offset =='undefined') offset = 0 if(!menuData.menu.length) throw Error('menu not set') if(typeof menuData.menu[offset].message == 'undefined') throw Error('Not set message for menu') let message = '<i>'+menuData.title+'</i>'+'\n'+menuData.menu[offset].message, keyboard = [], action = {} if(menuData.menu.length > 1 && menuData.menu.length < 6){ let i = 0 keyboard.push([]) for(i; menuData.menu.length>i; i++){ let random = Math.random().toString(36).substring(7) keyboard[0].push({ text: (i == offset) ? '·'+(i+1).toString()+'·' : (i+1).toString(), callback_data: random }) action[random] = { page: true, number: i } } }else if (menuData.menu.length > 5) { keyboard.push([]) for (let i = 0; 5 > i; i++) { let text = ''; let noffset = 0; if (offset < 3) { switch (i) { case 3: text = (i + 1).toString() + '›' noffset = i break case 4: text = menuData.menu.length.toString() + '»' noffset = menuData.menu.length - 1 break default: text = (i == offset) ? '·' + (i + 1).toString() + '·' : (i + 1).toString() noffset = i break } } else if (offset > menuData.menu.length - 4) { let menu_offset; switch (i) { case 0: text = '«1' break case 1: text = '‹' + (menuData.menu.length - 3).toString() noffset = menuData.menu.length - 4 break case 2: menu_offset = 2; text = ((menuData.menu.length - 1 - menu_offset) == offset) ? '·' + ((menuData.menu.length - menu_offset)).toString() + '·' : ((menuData.menu.length - menu_offset)).toString() noffset = (menuData.menu.length - 1 - menu_offset); break; case 3: menu_offset = 1; text = ((menuData.menu.length - 1 - menu_offset) == offset) ? '·' + ((menuData.menu.length - menu_offset)).toString() + '·' : ((menuData.menu.length - menu_offset)).toString() noffset = (menuData.menu.length - 1 - menu_offset); break case 4: menu_offset = 0; text = ((menuData.menu.length - 1 - menu_offset) == offset) ? '·' + ((menuData.menu.length - menu_offset)).toString() + '·' : ((menuData.menu.length - menu_offset)).toString() noffset = (menuData.menu.length - 1 - menu_offset); break } } else { switch (i) { case 0: text = '«1' break case 1: text = '‹' + (offset).toString() noffset = offset - 1 break case 2: text = '·' + (offset + 1).toString() + '·' noffset = offset break case 3: text = (offset + 2).toString() + '›' noffset = offset + 1 break case 4: text = menuData.menu.length.toString() + '»' noffset = menuData.menu.length - 1 break default: break } } let random = Math.random().toString(36).substring(7) keyboard[0].push({ text: text, callback_data: random }) action[random] = { page: true, number: noffset } } } for (var i = 0; i < menuData.menu[offset].keyboard.length; i++){ var array = [] for (var q = 0; q < menuData.menu[offset].keyboard[i].length; q++){ let random = Math.random().toString(36).substring(7) if(typeof menuData.menu[offset].keyboard[i][q].text == "undefined") throw Error('not set text button for '+menuData.menu[offset].message) if(typeof menuData.menu[offset].keyboard[i][q].action == "undefined") throw Error('not set action for '+menuData.menu[offset].message) array.push({ text: menuData.menu[offset].keyboard[i][q].text, callback_data: random }) action[random] = menuData.menu[offset].keyboard[i][q].action } keyboard.push(array) } if(typeof menuData.back != 'undefined'){ let random = Math.random().toString(36).substring(7) keyboard.push([{ text: lg.back, callback_data: random }]) action[random] = menuData.back } keyboard = JSON.stringify({inline_keyboard: keyboard}) this.SessionSetMessage(message, keyboard, () => { this.waitForRequest.then($ => { $.SessionGetMessage((messageId) => { if(typeof action[$.message.text] == 'object'){ if(action[$.message.text].page){ $.runManyMenu(menuData, action[$.message.text].number) } }else if (typeof action[$.message.text] == 'function'){ action[$.message.text]($) } }, (messageId) => { $.SessionClose($.messageId, null, () => { $.runManyMenu(menuData, offset, null, true) }) }, (messageId) => { $.SessionClose(messageId, null, () => { $.runManyMenu(menuData, offset) }) }) }) }, stop, updatekeyboard) } /** * * @param formData * @param callback * @param resultData * @param index * @param stop * @param updatekeyboard */ runform(formData, callback, resultData, index, stop, updatekeyboard){ var i = index ? index : 0 const run = (stop, updatekeyboard) => { var keyboardword = {}, keyboard = [], syskeyboard = [], message if (i === Object.keys(formData.form).length) { try { message = formData.confirm(result) keyboard.push( [{ text: lg.confirm, callback_data: '/confirm' }], [{ text: lg.cancel, callback_data: '/cancel' },{ text: lg.back, callback_data: '/back' }] ) keyboard = JSON.stringify({inline_keyboard: keyboard}) this.SessionSetMessage(message, keyboard, () => { this.waitForRequest.then($ => { $.SessionGetMessage( messageId => { switch ($.message.text){ case '/confirm': $.SessionClose(messageId, formData.finish(result), () => { callback(result) }) break case '/cancel': formData.cancel($) break case '/back': delete result[keys[i-1]] var index = i-1 $.runform(formData, callback, result, index) break default: $.runform(formData, callback, result, i) } }, messageId => { $.SessionClose($.messageId, null, () => { $.runform(formData, callback, result, i, null, true) }) }, messageId => { $.SessionClose(messageId, null, () => { $.runform(formData, callback, result, i, null, null) }) } ) }) }) } catch (e) { this.logger.error({ 'error in user callback:': e }) } return } const key = formData.form[keys[i]] var message = '<b>'+formData.title+'</b>' for(var y = 0; i+1 > y; y++){ if(y == i){ message += '\n<i>'+formData.form[keys[y]].text message += (typeof formData.form[keys[y]].question != 'undefined' && formData.form[keys[y]].question) ? '?' : '' message += '</i>' } else { if(typeof result[keys[y]] === 'string') message += '\n<i>'+formData.form[keys[y]].text+':</i> <b>'+result[keys[y]]+'</b>' else message += '\n<i>'+formData.form[keys[y]].text+'</i> \u2714' } } if(typeof key.keyboard != 'undefined'){ for (var k = 0; k < key.keyboard.length; k++){ var array = [] for (var q = 0; q < key.keyboard[k].length; q++){ let random = Math.random().toString(36).substring(7) array.push({ text: key.keyboard[k][q], callback_data: random }) keyboardword[random] = key.keyboard[k][q] } keyboard.push(array) } } syskeyboard.push({ text: lg.cancel, callback_data: '/cancel' }) if(i){ syskeyboard.push({ text: lg.back, callback_data: '/back' }) } keyboard.push(syskeyboard) keyboard = JSON.stringify({inline_keyboard: keyboard}) this.SessionSetMessage(message, keyboard, () => { this.waitForRequest.then($ => { $.SessionGetMessage( messageId => { if($.message && $.message.text == '/cancel') return formData.cancel($) if($.message && $.message.text == '/back'){ delete result[keys[i-1]] var index = i-1 return $.runform(formData, callback, result, index) } if(typeof keyboardword[$.message.text] != 'undefined') result[keys[i]] = keyboardword[$.message.text] var index = i+1 $.runform(formData, callback, result, index) }, messageId => { $.SessionClose($.messageId, null, () => { $.runform(formData, callback, result, i, null, true) }) }, messageId => { var returnmessage = '<i>'+key.text+'</i>' returnmessage += (typeof key.question != 'undefined' && key.question) ? '?' : '' if(typeof key.keyboard != 'undefined'){ if(key.keyboardonly || $.message.text == null) return $.SessionClose(messageId, key.error, () => { $.runform(formData, callback, result, i) }) $.SessionClose(messageId, returnmessage, () => { result[keys[i]] = $.message.text var index = i+1 $.runform(formData, callback, result, index) }) }else { key.validator($.message, (valid, value) => { if(valid) return $.SessionClose(messageId, returnmessage, () => { result[keys[i]] = value var index = i+1 $.runform(formData, callback, result, index) }) else return $.SessionClose(messageId, key.error, () => { $.runform(formData, callback, result, i) }) }) } }) }) }, stop, updatekeyboard) } var result = (resultData) ? resultData : {} const keys = Object.keys(formData.form) run(stop, updatekeyboard) } //api methods starts here /** * * @returns {Promise<answerCallbackQuery>} */ answerCallbackQuery() { return this._api.answerCallbackQuery(this._callbackQuery.id) } /** * * @param {string} text * @param {Object} [options] * @returns {Promise<Message>} */ sendMessage(text, options) { return this._api.sendMessage(this.chatId, text, Object.assign({parse_mode: 'HTML'}, options)) } /** * * @param {string} text * @returns {Promise<Message>} */ senddefaultMessage(text) { return this._api.sendMessage(this.chatId, text, this.defaultmessage) } /** * * @param keybord */ inlinekeybord(keybord){ var options = {inline_keyboard: []} for (var i = 0; i < keybord.length; i++){ var array = [] for (var q = 0; q < keybord[i].length; q = q + 2){ array.push({ text: keybord[i][q], callback_data: keybord[i][q+1] }) } options.inline_keyboard.push(array) } return JSON.stringify(options) } /** * * @param {string} text * @param {array}, keybord * @returns {Promise<Message>} */ sendinlinekeyMessage(text, keybord) { return this._api.sendMessage(this.chatId, text, {reply_markup: this.inlinekeybord(keybord), parse_mode: 'HTML', disable_web_page_preview: true}) } /** * * @param {string} text * @param {Object} [options] * @returns {Promise<Message>} */ editMessageText(text, options) { options = options ? options : {} return this._api.editMessageText(text, Object.assign({ chat_id: this._chatId, message_id: this._messageId, parse_mode: 'HTML' }, options)) } /** * * @param {number} fromChatId * @param {number} messageId * @param {Object} [options] * @returns {Promise<Message>} */ forwardMessage(fromChatId, messageId, options) { return this._api.forwardMessage(this.chatId, fromChatId, messageId, options) } /** * * @param {InputFile|Object} photo * @param {Object} [options] * @returns {Promise<Message>} */ sendPhoto(photo, options) { return this._api.sendPhoto(this.chatId, photo, options) } /** * * @param {InputFile|Object} audio * @param {Object} [options] * @returns {Promise<Message>} */ sendAudio(audio, options) { return this._api.sendAudio(this.chatId, audio, options) } /** * * @param {InputFile|Object} document * @param {Object} [options] * @returns {Promise<Message>} */ sendDocument(document, options) { return this._api.sendDocument(this.chatId, document, options) } /** * * @param {InputFile|Object} sticker * @param {Object} [options] * @returns {Promise<Message>} */ sendSticker(sticker, options) { return this._api.sendSticker(this.chatId, sticker, options) } /** * * @param {InputFile|Object} video * @param {Object} [options] * @returns {Promise<Message>} */ sendVideo(video, options) { return this._api.sendVideo(this.chatId, video, options) } /** * * @param {InputFile|Object} voice * @param {Object} [options] * @returns {Promise<Message>} */ sendVoice(voice, options) { return this._api.sendVoice(this.chatId, voice, options) } /** * * @param {number} latitude * @param {number} longitude * @param {Object} [options] * @returns {Promise<Message>} */ sendLocation(latitude, longitude, options) { return this._api.sendLocation(this.chatId, latitude, longitude, options) } /** * * @param {number} latitude * @param {number} longitude * @param {string} title * @param {string}address * @param {Object} [options] * @returns {Promise<Message>} */ sendVenue(latitude, longitude, title, address, options) { return this._api.sendVenue(this.chatId, latitude, longitude, title, address, options) } /** * * @param {string} phoneNumber * @param {string} firstName * @param {Object} [options] * @returns {Promise<Message>} */ sendContact(phoneNumber, firstName, options) { return this._api.sendContact(this.chatId, phoneNumber, firstName, options) } /** * * @param {string} action * @returns {Promise<Object>} */ sendChatAction(action) { return this._api.sendChatAction(this.chatId, action) } /** * * @param {number} offset * @param {number} limit * @returns {Promise<UserProfilePhotos>} */ getUserProfilePhotos(offset, limit) { return this._api.getUserProfilePhotos(userId, offset, limit) } /** * * @param {number} userId * @returns {Promise.<boolean>} */ kickChatMember(userId) { return this._api.kickChatMember(this.chatId, userId) } /** * * @returns {Promise.<boolean>} */ leaveChat() { return this._api.leaveChat(this.chatId) } /** * * @param {number} userId * @returns {Promise.<boolean>} */ unbanChatMember(userId) { return this._api.unbanChatMember(this.chatId, userId) } /** * * @returns {Promise<Chat>} */ getChat() { return this._api.getChat(this.chatId) } /** * * @returns {Promise<ChatMember[]>} */ getChatAdministrators() { return this._api.getChatAdministrators(this.chatId) } /** * * @returns {Promise<number>} */ getChatMembersCount() { return this._api.getChatMembersCount(this.chatId) } /** * * @param {number} userId * @returns {Promise.<ChatMember>} */ getChatMember(userId) { return this._api.getChatMember(this.chatId, userId) } } module.exports = Scope