UNPKG

node-red-node-telegrambot

Version:
612 lines (534 loc) 21.8 kB
// Created by Karl-Heinz Wind module.exports = function (RED) { 'use strict'; const { Api, TelegramClient } = require('telegram'); const { StringSession } = require('telegram/sessions'); const { NewMessage } = require('telegram/events'); const { DeletedMessage } = require('telegram/events/DeletedMessage'); const { EditedMessage } = require('telegram/events/EditedMessage'); const { Album } = require('telegram/events/Album'); const { CallbackQuery } = require('telegram/events/CallbackQuery'); // -------------------------------------------------------------------------------------------- let getPhoneCodeResolve; let getPhoneCodeReject; let getPasswordResolve; let getPasswordReject; function createPhoneCodePromise() { let getPhoneCode = new Promise((resolve, reject) => { getPhoneCodeResolve = resolve; getPhoneCodeReject = reject; }); return getPhoneCode; } function createPasswordPromise() { let getPassword = new Promise((resolve, reject) => { getPasswordResolve = resolve; getPasswordReject = reject; }); return getPassword; } async function login(parameters, getPhoneCode, getPassword, sessionCreated, error) { try { let apiId = Number(parameters.apiId); let apiHash = parameters.apiHash; let phoneNumber = parameters.phoneNumber; let botToken = parameters.botToken; let password = parameters.password; let loginMode = parameters.loginMode; let proxy = parameters.proxy; let useWSS = parameters.useWSS; let deviceModel = parameters.devicemodel || ''; let systemVersion = parameters.systemversion || ''; let appVersion = parameters.appversion || ''; if (password === undefined || password === '') { password = async () => await getPassword; } if (apiId !== undefined && apiHash !== undefined && phoneNumber !== undefined) { const stringSession = new StringSession(''); let clientParams = { connectionRetries: 5, proxy: proxy, useWSS: useWSS, }; // optional login parameters. if (deviceModel !== '') { clientParams.deviceModel = deviceModel; } if (systemVersion !== '') { clientParams.systemVersion = systemVersion; } if (appVersion !== '') { clientParams.appVersion = appVersion; } const client = new TelegramClient(stringSession, apiId, apiHash, clientParams); client.setLogLevel('warn'); let authParams; if (loginMode === 'user') { authParams = { phoneNumber: phoneNumber, phoneCode: async () => await getPhoneCode, password: password, onError: (err) => { console.log(err); // if (err.errorMessage === 'PHONE_CODE_INVALID') { // } return true; // abort }, }; } else if (loginMode === 'bot') { authParams = { botAuthToken: botToken, }; } else { throw 'LoginMode ' + loginMode + ' is not supported'; } await client.start(authParams); let session = client.session.save(); sessionCreated(session); } else { error('Parameters are missing: apiId, apiHash, phoneNumber'); } } catch (err) { error(err); } } RED.httpAdmin.get('/node-red-node-telegrambot-setphonecode', function (req, res) { let parameters = req.query; if (getPhoneCodeResolve !== undefined && getPhoneCodeResolve !== null) { let phoneCode = parameters.phoneCode; if (phoneCode !== '') { getPhoneCodeResolve(phoneCode); } else { // if (err.errorMessage === "RESTART_AUTH") { getPhoneCodeReject(phoneCode); } } res.json('ok'); }); RED.httpAdmin.get('/node-red-node-telegrambot-setpassword', function (req, res) { let parameters = req.query; if (getPasswordResolve !== undefined && getPasswordResolve !== null) { let password = parameters.password; if (password !== '') { getPasswordResolve(password); } else { getPasswordReject(password); } } res.json('ok'); }); RED.httpAdmin.get('/node-red-node-telegrambot-login', function (req, res) { let parameters = req.query; let getPhoneCode = createPhoneCodePromise(); let getPassword = createPasswordPromise(); try { login( parameters, getPhoneCode, getPassword, (session) => { let data = { session: session }; res.json(data); }, (error) => { let message; if (error.code !== undefined) { message = 'Error ' + error.code + ' (' + error.errorMessage + '): ' + error.message; } else if (error.message !== undefined) { message = error.message; } else { message = error; } let data = { type: 'error', error: message, }; res.json(data); } ); } catch (allErrors) { // TODO: } }); // -------------------------------------------------------------------------------------------- // The configuration node function TelegramConfigNode(n) { RED.nodes.createNode(this, n); this.config = n; this.client = null; this.logLevel = 'warn'; // 'none', 'error', 'warn','info', 'debug' this.verbose = n.verboselogging; this.useProxy = n.useproxy || false; this.useWSS = n.usewss || false; this.proxy; let deviceModel = n.devicemodel || ''; let systemVersion = n.systemversion || ''; let appVersion = n.appversion || ''; if (this.useProxy) { this.proxy = { ip: n.host, socksType: Number(n.sockstype), port: Number(n.port), username: n.username, password: n.password, secret: n.secret, MTProxy: n.mtproxy, timeout: Number(n.timeout), }; } this.loginMode = n.loginmode; if (!this.loginMode) { this.loginMode = 'user'; } if (this.verbose) { this.logLevel = 'debug'; } // let self = this; if (this.credentials !== undefined) { this.apiId = this.credentials.apiid || ''; this.apiHash = this.credentials.apihash || ''; this.session = this.credentials.session || ''; this.phoneNumber = this.credentials.phonenumber || ''; } this.createTelegramClient = async function createTelegramClient(node, apiId, apiHash, session, phoneNumber, botToken, logLevel, proxy, useWSS) { let client; try { if (apiId !== undefined && apiId !== '' && session !== undefined && session !== '') { const stringSession = new StringSession(session); const ID = Number(apiId); let clientParams = { connectionRetries: 5, proxy: proxy, useWSS: useWSS, }; // optional login parameters. if (deviceModel !== '') { clientParams.deviceModel = deviceModel; } if (systemVersion !== '') { clientParams.systemVersion = systemVersion; } if (appVersion !== '') { clientParams.appVersion = appVersion; } client = new TelegramClient(stringSession, ID, apiHash, clientParams); client.setLogLevel(logLevel); let authParams; if (botToken === undefined) { authParams = { phoneNumber: phoneNumber, onError: (err) => { console.log(err); return true; // abort }, }; } else { authParams = { botAuthToken: botToken, }; } await client.start(authParams); await client.connect(); } else { node.warn('No session: login first.'); } } catch (error) { node.warn(error); } return client; }; // Activates the client or returns the already activated bot. this.getTelegramClient = async function (node) { if (!this.client) { this.client = await this.createTelegramClient( node, this.apiId, this.apiHash, this.session, this.phoneNumber, this.botToken, this.logLevel, this.proxy, this.useWSS ); } return this.client; }; this.onStarted = function () {}; RED.events.on('flows:started', this.onStarted); this.on('close', function (removed, done) { RED.events.removeListener('flows:started', this.onStarted); done(); }); } RED.nodes.registerType('telegram client config', TelegramConfigNode, { credentials: { apiid: { type: 'text' }, apihash: { type: 'text' }, session: { type: 'text' }, phonenumber: { type: 'text' }, }, }); // -------------------------------------------------------------------------------------------- // The receiver node receives messages. function TelegramReceiverNode(config) { RED.nodes.createNode(this, config); let node = this; this.bot = config.bot; this.config = RED.nodes.getNode(this.bot); this.sendRawEvents = config.sendrawevents || false; this.sendNewMessage = config.sendnewmessage || false; this.sendDeletedMessage = config.senddeletedmessage || false; this.sendEditedMessage = config.sendeditedmessage || false; this.sendAlbum = config.sendalbum || false; this.sendCallbackQuery = config.sendcallbackquery || false; this.rawEventHandlerAdded = false; this.newMessageEventHandlerAdded = false; this.deletedMessageEventHandlerAdded = false; this.editedMessageEventHandlerAdded = false; this.albumEventHandlerAdded = false; this.callbackQueryEventHandlerAdded = false; this.rawEventHandler = async (event) => { let msg = { type: 'Raw', payload: event, }; node.send(msg); }; this.newMessageEventHandler = async (event) => { let message = event.message; let msg = { payload: { type: 'NewMessage', message: message, originalUpdate: message.originalUpdate, sender: await message.getSender(), chat: await message.getChat(), event: event, }, }; node.send(msg); }; this.deletedMessageEventHandler = async (event) => { let msg = { payload: { type: 'DeletedMessage', // peer, chatPeer ? deletedIds: event.deletedIds, event: event, }, }; node.send(msg); }; this.editedMessageEventHandler = async (event) => { let message = event.message; let msg = { payload: { type: 'EditedMessage', message: message, sender: await message.getSender(), chat: await message.getChat(), event: event, }, }; node.send(msg); }; this.albumEventHandler = async (event) => { let msg = { payload: { type: 'Album', messages: event.messages, originalUpdates: event.originalUpdates, event: event, }, }; node.send(msg); }; this.callbackQueryEventHandler = async (event) => { let msg = { payload: { type: 'CallbackQuery', query: event.query, event: event, }, }; node.send(msg); }; this.stop = async () => { if (node.config) { let client = await node.config.getTelegramClient(node); if (client) { if (node.rawEventHandlerAdded) { client.removeEventHandler(node.rawEventHandler); node.rawEventHandlerAdded = false; } if (node.newMessageEventHandlerAdded) { client.removeEventHandler(node.newMessageEventHandler, new NewMessage({})); node.newMessageEventHandlerAdded = false; } if (node.deletedMessageEventHandlerAdded) { client.removeEventHandler(node.deletedMessageEventHandler, new DeletedMessage({})); node.deletedMessageEventHandlerAdded = false; } if (node.editedMessageEventHandlerAdded) { client.removeEventHandler(node.editedMessageEventHandler, new EditedMessage({})); node.editedMessageEventHandlerAdded = false; } if (node.albumEventHandlerAdded) { client.removeEventHandler(node.albumEventHandler, new Album({})); node.albumEventHandlerAdded = false; } if (node.callbackQueryEventHandlerAdded) { client.removeEventHandler(node.callbackQueryEventHandler, new CallbackQuery({})); node.callbackQueryEventHandlerAdded = false; } } } node.status({ fill: 'red', shape: 'ring', text: 'disconnected', }); }; this.start = async () => { if (node.config) { let client = await node.config.getTelegramClient(node); if (client) { if (node.sendRawEvents) { client.addEventHandler(node.rawEventHandler); node.rawEventHandlerAdded = true; } if (node.sendNewMessage) { client.addEventHandler(node.newMessageEventHandler, new NewMessage({})); node.newMessageEventHandlerAdded = true; } if (node.sendDeletedMessage) { client.addEventHandler(node.deletedMessageEventHandler, new DeletedMessage({})); node.deletedMessageEventHandlerAdded = true; } if (node.sendEditedMessage) { client.addEventHandler(node.editedMessageEventHandler, new EditedMessage({})); node.editedMessageEventHandlerAdded = true; } if (node.sendAlbum) { client.addEventHandler(node.albumEventHandler, new Album({})); node.albumEventHandlerAdded = true; } if (node.sendCallbackQuery) { client.addEventHandler(node.callbackQueryEventHandler, new CallbackQuery({})); node.callbackQueryEventHandlerAdded = true; } node.status({ fill: 'green', shape: 'ring', text: 'connected', }); } else { node.status({ fill: 'red', shape: 'ring', text: 'disconnected', }); } } else { // no config node? } }; this.start(); this.on('close', function (removed, done) { node.stop(); // node.status({}); done(); }); } RED.nodes.registerType('telegram client receiver', TelegramReceiverNode); // -------------------------------------------------------------------------------------------- // The sender node sends messages. function TelegramSenderNode(config) { RED.nodes.createNode(this, config); let node = this; this.bot = config.bot; this.config = RED.nodes.getNode(this.bot); this.start = async () => { if (node.config) { let client = await node.config.getTelegramClient(node); if (client) { node.status({ fill: 'green', shape: 'ring', text: 'connected', }); } else { node.status({ fill: 'red', shape: 'ring', text: 'disconnected', }); } } else { // no config node? } }; this.start(); this.stop = async () => { node.status({ fill: 'red', shape: 'ring', text: 'disconnected', }); }; this.processMessage = function (client, msg, nodeSend, nodeDone) { if (msg.payload !== undefined) { let api = msg.payload.api; let func = msg.payload.func; let args = msg.payload.args || {}; if (func !== undefined) { (async () => { try { let result; if (api === undefined || api === '') { // sendMessage, forwardMessages, editMessage, deleteMessages, pinMessage, unpinMessage, markAsRead, sendFile // args must be an array result = client[func](...args); } else { // args must be an object let request = new Api[api][func](args); result = await client.invoke(request); } msg.payload = result; nodeSend(msg); } catch (error) { nodeDone(error); } })(); } else { nodeDone('msg.payload: api or func is missing.'); } } // TODO: // await client.sendMessage(sender, { // message: `hi your id is ${message.senderId}`, //}); // const entity = await client.getEntity('Windhose'); // await client.sendMessage(entity, { message: 'Hello!' }); }; this.on('input', async function (msg, nodeSend, nodeDone) { if (msg.payload) { if (node.config) { let client = await node.config.getTelegramClient(node); if (client) { this.processMessage(client, msg, nodeSend, nodeDone); } } else { // no config node? } } }); this.on('close', function (removed, done) { node.stop(); // node.status({}); done(); }); } RED.nodes.registerType('telegram client sender', TelegramSenderNode); };