1389 lines
68 KiB
JavaScript
1389 lines
68 KiB
JavaScript
|
/**
|
||
|
* Created by Karl-Heinz Wind
|
||
|
**/
|
||
|
|
||
|
// Temporary fix for 0.30.0 api
|
||
|
process.env["NTBA_FIX_319"] = 1;
|
||
|
|
||
|
module.exports = function (RED) {
|
||
|
"use strict";
|
||
|
var telegramBot = require('node-telegram-bot-api');
|
||
|
const Agent = require('socks5-https-client/lib/Agent')
|
||
|
|
||
|
// --------------------------------------------------------------------------------------------
|
||
|
// The configuration node
|
||
|
// holds the token
|
||
|
// and establishes the connection to the telegram bot
|
||
|
// you can either select between polling mode and webhook mode.
|
||
|
function TelegramBotNode(n) {
|
||
|
RED.nodes.createNode(this, n);
|
||
|
|
||
|
var self = this;
|
||
|
|
||
|
// contains all the nodes that make use of the bot connection maintained by this configuration node.
|
||
|
this.users = [];
|
||
|
|
||
|
this.status = "disconnected";
|
||
|
|
||
|
// Reading configuration properties...
|
||
|
this.botname = n.botname;
|
||
|
this.verbose = n.verboselogging;
|
||
|
|
||
|
this.usernames = [];
|
||
|
if (n.usernames) {
|
||
|
this.usernames = n.usernames.split(',');
|
||
|
}
|
||
|
|
||
|
this.chatids = [];
|
||
|
if (n.chatids) {
|
||
|
this.chatids = n.chatids.split(',').map(function (item) {
|
||
|
return parseInt(item, 10);
|
||
|
});
|
||
|
}
|
||
|
|
||
|
this.baseApiUrl = n.baseapiurl;
|
||
|
|
||
|
this.updateMode = n.updatemode;
|
||
|
if(!this.updateMode){
|
||
|
this.updateMode = "polling";
|
||
|
}
|
||
|
|
||
|
// 1. optional when polling mode is used
|
||
|
this.pollInterval = parseInt(n.pollinterval);
|
||
|
if (isNaN(this.pollInterval)) {
|
||
|
this.pollInterval = 300;
|
||
|
}
|
||
|
|
||
|
// 2. optional when webhook is used.
|
||
|
this.botHost = n.bothost;
|
||
|
|
||
|
this.publicBotPort = parseInt(n.publicbotport);
|
||
|
if (isNaN(this.publicBotPort)) {
|
||
|
this.publicBotPort = 8443;
|
||
|
}
|
||
|
|
||
|
this.localBotPort = parseInt(n.localbotport);
|
||
|
if (isNaN(this.localBotPort)) {
|
||
|
this.localBotPort = this.publicBotPort;
|
||
|
}
|
||
|
|
||
|
// 3. optional when webhook and self signed certificate is used
|
||
|
this.privateKey = n.privatekey;
|
||
|
this.certificate = n.certificate;
|
||
|
this.useSelfSignedCertificate = n.useselfsignedcertificate;
|
||
|
this.sslTerminated = n.sslterminated;
|
||
|
|
||
|
// 4. optional when request via SOCKS5 is used.
|
||
|
this.useSocks = n.usesocks;
|
||
|
if(this.useSocks){
|
||
|
this.socksRequest = {
|
||
|
agentClass: Agent,
|
||
|
agentOptions: {
|
||
|
socksHost: n.sockshost,
|
||
|
socksPort: n.socksport,
|
||
|
socksUsername: n.socksusername,
|
||
|
socksPassword: n.sockspassword,
|
||
|
},
|
||
|
};
|
||
|
}
|
||
|
|
||
|
this.useWebhook = false;
|
||
|
if (this.updateMode == "webhook") {
|
||
|
if (this.botHost && (this.sslTerminated || (this.privateKey && this.certificate))) {
|
||
|
this.useWebhook = true;
|
||
|
} else{
|
||
|
self.error("Configuration data for webhook is not complete. Defaulting to polling mode.");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// Activates the bot or returns the already activated bot.
|
||
|
this.getTelegramBot = function () {
|
||
|
if (!this.telegramBot) {
|
||
|
if (this.credentials) {
|
||
|
this.token = this.credentials.token;
|
||
|
if (this.token) {
|
||
|
this.token = this.token.trim();
|
||
|
if (!this.telegramBot) {
|
||
|
|
||
|
if (this.useWebhook){
|
||
|
var webHook =
|
||
|
{
|
||
|
autoOpen: true,
|
||
|
port : this.localBotPort,
|
||
|
};
|
||
|
if (!this.sslTerminated) {
|
||
|
webHook.key = this.privateKey;
|
||
|
webHook.cert = this.certificate;
|
||
|
}
|
||
|
var options =
|
||
|
{
|
||
|
webHook: webHook,
|
||
|
baseApiUrl: this.baseApiUrl,
|
||
|
request: this.socksRequest,
|
||
|
};
|
||
|
this.telegramBot = new telegramBot(this.token, options);
|
||
|
|
||
|
this.telegramBot.on('webhook_error', function (error) {
|
||
|
|
||
|
self.setUsersStatus({ fill: "red", shape: "ring", text: "webhook error" })
|
||
|
|
||
|
if(self.verbose) {
|
||
|
self.warn("Webhook error: " + error.message);
|
||
|
}
|
||
|
|
||
|
// TODO: check if we should abort in future when this happens
|
||
|
// self.abortBot(error.message, function () {
|
||
|
// self.warn("Bot stopped: Webhook error.");
|
||
|
// });
|
||
|
});
|
||
|
|
||
|
var botUrl = "https://" + this.botHost + ":" + this.publicBotPort + "/" + this.token;
|
||
|
var setWebHookOptions;
|
||
|
if (!this.sslTerminated && this.useSelfSignedCertificate){
|
||
|
setWebHookOptions = {
|
||
|
certificate: options.webHook.cert,
|
||
|
};
|
||
|
}
|
||
|
this.telegramBot.setWebHook(botUrl, setWebHookOptions).then(function (success) {
|
||
|
|
||
|
if(self.verbose) {
|
||
|
self.telegramBot.getWebHookInfo().then(function (result) {
|
||
|
self.log("Webhook enabled: " + JSON.stringify(result));
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if(success) {
|
||
|
self.status = "connected";
|
||
|
}
|
||
|
else {
|
||
|
self.abortBot("Failed to set webhook " + botUrl, function () {
|
||
|
self.warn("Bot stopped: Webhook not set.");
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
var polling = {
|
||
|
autoStart: true,
|
||
|
interval: this.pollInterval
|
||
|
}
|
||
|
|
||
|
var options = {
|
||
|
polling: polling,
|
||
|
baseApiUrl: this.baseApiUrl,
|
||
|
request: this.socksRequest,
|
||
|
};
|
||
|
this.telegramBot = new telegramBot(this.token, options);
|
||
|
self.status = "connected";
|
||
|
|
||
|
this.telegramBot.on('polling_error', function (error) {
|
||
|
|
||
|
self.setUsersStatus({ fill: "red", shape: "ring", text: "polling error" })
|
||
|
|
||
|
if(self.verbose) {
|
||
|
self.warn(error.message);
|
||
|
|
||
|
var stopPolling = false;
|
||
|
var hint;
|
||
|
if (error.message === "ETELEGRAM: 401 Unauthorized") {
|
||
|
hint = "Please check if the bot token is valid: " + self.credentials.token;
|
||
|
stopPolling = true;
|
||
|
}
|
||
|
else if (error.message.startsWith("EFATAL: Error: connect ETIMEDOUT")) {
|
||
|
hint = "Timeout connecting to server. Trying again.";
|
||
|
}
|
||
|
else if (error.message.startsWith("EFATAL: Error: read ECONNRESET")) {
|
||
|
hint = "Network connection may be down. Trying again.";
|
||
|
}
|
||
|
else if (error.message.startsWith("EFATAL: Error: getaddrinfo EAI_AGAIN")) {
|
||
|
hint = "Network connection may be down. Trying again.";
|
||
|
}
|
||
|
else if (error.message.startsWith("EFATAL: Error: getaddrinfo ENOTFOUND")) {
|
||
|
hint = "Network connection may be down. Trying again.";
|
||
|
}
|
||
|
else if (error.message.startsWith("EFATAL: Error: SOCKS connection failed. Connection refused.")) {
|
||
|
hint = "Username or password may be be wrong. Aborting.";
|
||
|
stopPolling = true;
|
||
|
}
|
||
|
else if (error.message.startsWith("EFATAL: Error: connect ETIMEDOUT")) {
|
||
|
hint = "Server did not respond. Maybe proxy blocked polling. Trying again.";
|
||
|
}
|
||
|
else {
|
||
|
// unknown error occured... we simply ignore it.
|
||
|
hint = "Unknown error. Trying again.";
|
||
|
}
|
||
|
|
||
|
if (stopPolling) {
|
||
|
self.abortBot(error.message, function () {
|
||
|
self.warn("Bot stopped: " + hint);
|
||
|
});
|
||
|
}
|
||
|
else {
|
||
|
// here we simply ignore the bug and continue polling.
|
||
|
// The following line is removed as this would create endless log files
|
||
|
if(self.verbose){
|
||
|
self.warn(hint);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
this.telegramBot.on('error', function (error) {
|
||
|
self.warn("Bot error: " + error.message);
|
||
|
|
||
|
self.abortBot(error.message, function () {
|
||
|
self.warn("Bot stopped: Fatal Error.");
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return this.telegramBot;
|
||
|
}
|
||
|
|
||
|
this.on('close', function (done) {
|
||
|
self.abortBot("closing", done);
|
||
|
});
|
||
|
|
||
|
this.abortBot = function (hint, done) {
|
||
|
|
||
|
if (self.telegramBot !== null) {
|
||
|
if (self.telegramBot._polling){
|
||
|
self.telegramBot.stopPolling()
|
||
|
.then(function () {
|
||
|
self.telegramBot = null;
|
||
|
self.status = "disconnected";
|
||
|
self.setUsersStatus({ fill: "red", shape: "ring", text: "bot stopped. " + hint })
|
||
|
done();
|
||
|
});
|
||
|
}
|
||
|
|
||
|
if (self.telegramBot._webHook){
|
||
|
self.telegramBot.deleteWebHook();
|
||
|
self.telegramBot.closeWebHook()
|
||
|
.then(function () {
|
||
|
self.telegramBot = null;
|
||
|
self.status = "disconnected";
|
||
|
self.setUsersStatus({ fill: "red", shape: "ring", text: "bot stopped. " + hint })
|
||
|
done();
|
||
|
});
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
self.status = "disconnected";
|
||
|
self.setUsersStatus({ fill: "red", shape: "ring", text: "bot stopped. " + hint })
|
||
|
done();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.isAuthorizedUser = function (user) {
|
||
|
var isAuthorized = false;
|
||
|
if (self.usernames.length > 0) {
|
||
|
if (self.usernames.indexOf(user) >= 0) {
|
||
|
isAuthorized = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return isAuthorized;
|
||
|
}
|
||
|
|
||
|
this.isAuthorizedChat = function (chatid) {
|
||
|
var isAuthorized = false;
|
||
|
var length = self.chatids.length;
|
||
|
if (length > 0) {
|
||
|
for (var i = 0; i < length; i++) {
|
||
|
var id = self.chatids[i];
|
||
|
if (id === chatid) {
|
||
|
isAuthorized = true;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return isAuthorized;
|
||
|
}
|
||
|
|
||
|
this.isAuthorized = function (chatid, user) {
|
||
|
var isAuthorizedUser = self.isAuthorizedUser(user);
|
||
|
var isAuthorizedChatId = self.isAuthorizedChat(chatid);
|
||
|
|
||
|
var isAuthorized = false;
|
||
|
|
||
|
if (isAuthorizedUser || isAuthorizedChatId) {
|
||
|
isAuthorized = true;
|
||
|
} else {
|
||
|
if (self.chatids.length === 0 && self.usernames.length === 0) {
|
||
|
isAuthorized = true;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
return isAuthorized;
|
||
|
}
|
||
|
|
||
|
this.register = function (node) {
|
||
|
var id = node.id;
|
||
|
if (self.users.indexOf(id) === -1) {
|
||
|
self.users.push(id);
|
||
|
}
|
||
|
else {
|
||
|
if(self.verbose) {
|
||
|
// This warnin was removed as it caused confusion: see https://github.com/windkh/node-red-contrib-telegrambot/issues/87
|
||
|
// self.warn("Node " + id + " registered twice at the configuration node: ignoring.");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
this.setUsersStatus = function (status) {
|
||
|
self.users.forEach(function (nodeId) {
|
||
|
var userNode = RED.nodes.getNode(nodeId);
|
||
|
if(userNode) {
|
||
|
userNode.status(status);
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
|
||
|
}
|
||
|
RED.nodes.registerType("telegram bot", TelegramBotNode, {
|
||
|
credentials: {
|
||
|
token: { type: "text" }
|
||
|
}
|
||
|
});
|
||
|
|
||
|
// adds the caption of the message into the options.
|
||
|
function addCaptionToMessageOptions(msg) {
|
||
|
var options = msg.payload.options;
|
||
|
if (options === undefined) {
|
||
|
options = {};
|
||
|
}
|
||
|
|
||
|
if (msg.payload.caption !== undefined) {
|
||
|
options.caption = msg.payload.caption;
|
||
|
}
|
||
|
|
||
|
msg.payload.options = options;
|
||
|
|
||
|
return msg;
|
||
|
}
|
||
|
|
||
|
function getPhotoIndexWithHighestResolution(photoArray) {
|
||
|
var photoIndex = 0;
|
||
|
var highestResolution = 0;
|
||
|
|
||
|
photoArray.forEach(function (photo, index, array) {
|
||
|
var resolution = photo.width * photo.height;
|
||
|
if (resolution > highestResolution) {
|
||
|
highestResolution = resolution;
|
||
|
photoIndex = index;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
return photoIndex;
|
||
|
}
|
||
|
|
||
|
// creates the message details object from the original message
|
||
|
function getMessageDetails(botMsg) {
|
||
|
|
||
|
// Note that photos and videos can be sent as media group. The bot will receive the contents as single messages.
|
||
|
// Therefore the photo and video messages can contain a mediaGroupId if so....
|
||
|
var messageDetails;
|
||
|
if (botMsg.text) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'message', content: botMsg.text };
|
||
|
} else if (botMsg.photo) {
|
||
|
// photos are sent using several resolutions. Therefore photo is an array. We choose the one with the highest resolution in the array.
|
||
|
var index = getPhotoIndexWithHighestResolution(botMsg.photo);
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'photo', content: botMsg.photo[index].file_id, caption: botMsg.caption, date: botMsg.date, blob: true, photos: botMsg.photo, mediaGroupId: botMsg.media_group_id };
|
||
|
} else if (botMsg.audio) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'audio', content: botMsg.audio.file_id, caption: botMsg.caption, date: botMsg.date, blob: true };
|
||
|
} else if (botMsg.document) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'document', content: botMsg.document.file_id, caption: botMsg.caption, date: botMsg.date, blob: true };
|
||
|
} else if (botMsg.sticker) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'sticker', content: botMsg.sticker.file_id, date: botMsg.date, blob: true };
|
||
|
} else if (botMsg.video) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'video', content: botMsg.video.file_id, caption: botMsg.caption, date: botMsg.date, blob: true, mediaGroupId: botMsg.media_group_id };
|
||
|
} else if (botMsg.video_note) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'video_note', content: botMsg.video_note.file_id, caption: botMsg.caption, date: botMsg.date, blob: true };
|
||
|
} else if (botMsg.voice) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'voice', content: botMsg.voice.file_id, caption: botMsg.caption, date: botMsg.date, blob: true };
|
||
|
} else if (botMsg.location) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'location', content: botMsg.location, date: botMsg.date };
|
||
|
} else if (botMsg.venue) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'venue', content: botMsg.venue, date: botMsg.date };
|
||
|
} else if (botMsg.contact) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'contact', content: botMsg.contact, date: botMsg.date };
|
||
|
} else if (botMsg.new_chat_title) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'new_chat_title', content: botMsg.new_chat_title, date: botMsg.date };
|
||
|
} else if (botMsg.new_chat_photo) {
|
||
|
// photos are sent using several resolutions. Therefore photo is an array. We choose the one with the highest resolution in the array.
|
||
|
var index = getPhotoIndexWithHighestResolution(botMsg.new_chat_photo);
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'new_chat_photo', content: botMsg.new_chat_photo[index].file_id, date: botMsg.date, blob: true, photos: botMsg.new_chat_photo };
|
||
|
} else if (botMsg.new_chat_members) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'new_chat_members', content: botMsg.new_chat_members, date: botMsg.date, };
|
||
|
} else if (botMsg.left_chat_member) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'left_chat_member', content: botMsg.left_chat_member, date: botMsg.date, };
|
||
|
} else if (botMsg.delete_chat_photo) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'delete_chat_photo', content: botMsg.delete_chat_photo, date: botMsg.date };
|
||
|
} else if (botMsg.pinned_message) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'pinned_message', content: botMsg.pinned_message, date: botMsg.date };
|
||
|
} else if (botMsg.channel_chat_created) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'channel_chat_created', content: botMsg.channel_chat_created, date: botMsg.date };
|
||
|
} else if (botMsg.group_chat_created) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'group_chat_created', content: botMsg.group_chat_created, chat: botMsg.chat, date: botMsg.date };
|
||
|
} else if (botMsg.supergroup_chat_created) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'supergroup_chat_created', content: botMsg.supergroup_chat_created, chat: botMsg.chat, date: botMsg.date };
|
||
|
} else if (botMsg.migrate_from_chat_id) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'migrate_from_chat_id', content: botMsg.migrate_from_chat_id, chat: botMsg.chat, date: botMsg.date };
|
||
|
} else if (botMsg.migrate_to_chat_id) {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'migrate_to_chat_id', content: botMsg.migrate_to_chat_id, chat: botMsg.chat, date: botMsg.date };
|
||
|
} else {
|
||
|
// unknown type --> no output
|
||
|
|
||
|
// TODO:
|
||
|
// 'successful_payment',
|
||
|
// 'invoice',
|
||
|
// 'game',
|
||
|
}
|
||
|
|
||
|
return messageDetails;
|
||
|
}
|
||
|
|
||
|
// --------------------------------------------------------------------------------------------
|
||
|
// The input node receives messages from the chat.
|
||
|
// the message details are stored in the payload
|
||
|
// chatId
|
||
|
// type
|
||
|
// content
|
||
|
// depending on type caption and date is part of the output, too.
|
||
|
// The original message is stored next to payload.
|
||
|
//
|
||
|
// The message ist send to output 1 if the message is from an authorized user
|
||
|
// and to output2 if the message is not from an authorized user.
|
||
|
//
|
||
|
// message : content string
|
||
|
// photo : content file_id of first image in array
|
||
|
// audio : content file_id
|
||
|
// document: content file_id of document
|
||
|
// sticker : content file_id
|
||
|
// video : content file_id
|
||
|
// voice : content file_id
|
||
|
// location: content is object with latitude and longitude
|
||
|
// contact : content is full contact object
|
||
|
function TelegramInNode(config) {
|
||
|
RED.nodes.createNode(this, config);
|
||
|
var node = this;
|
||
|
this.bot = config.bot;
|
||
|
|
||
|
this.config = RED.nodes.getNode(this.bot);
|
||
|
if (this.config) {
|
||
|
this.config.register(node);
|
||
|
|
||
|
node.status({ fill: "red", shape: "ring", text: "not connected" });
|
||
|
|
||
|
node.telegramBot = this.config.getTelegramBot();
|
||
|
if (node.telegramBot) {
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
|
||
|
node.telegramBot.on('message', function (botMsg) {
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
|
||
|
var username = botMsg.from.username;
|
||
|
var chatid = botMsg.chat.id;
|
||
|
var messageDetails = getMessageDetails(botMsg);
|
||
|
if (messageDetails) {
|
||
|
var msg = { payload: messageDetails, originalMessage: botMsg };
|
||
|
|
||
|
if (node.config.isAuthorized(chatid, username)) {
|
||
|
// downloadable "blob" message?
|
||
|
if (messageDetails.blob) {
|
||
|
var fileId = msg.payload.content;
|
||
|
node.telegramBot.getFileLink(fileId).then(function (weblink) {
|
||
|
msg.payload.weblink = weblink;
|
||
|
|
||
|
// download and provide with path
|
||
|
if (config.saveDataDir) {
|
||
|
node.telegramBot.downloadFile(fileId, config.saveDataDir).then(function (path) {
|
||
|
msg.payload.path = path;
|
||
|
node.send([msg, null]);
|
||
|
});
|
||
|
} else {
|
||
|
node.send([msg, null]);
|
||
|
}
|
||
|
});
|
||
|
// vanilla message
|
||
|
} else {
|
||
|
node.send([msg, null]);
|
||
|
}
|
||
|
} else {
|
||
|
if(node.config.verbose){
|
||
|
node.warn("Unauthorized incoming call from " + username);
|
||
|
}
|
||
|
node.send([null, msg]);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
} else {
|
||
|
node.warn("bot not initialized");
|
||
|
node.status({ fill: "red", shape: "ring", text: "bot not initialized" });
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("config node failed to initialize.");
|
||
|
node.status({ fill: "red", shape: "ring", text: "config node failed to initialize" });
|
||
|
}
|
||
|
|
||
|
this.on("close", function () {
|
||
|
node.telegramBot.off('message');
|
||
|
node.status({});
|
||
|
});
|
||
|
}
|
||
|
RED.nodes.registerType("telegram receiver", TelegramInNode);
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------------------------
|
||
|
// The input node receives a command from the chat.
|
||
|
// The message details are stored in the payload
|
||
|
// chatId
|
||
|
// type
|
||
|
// content
|
||
|
// depending on type caption and date is part of the output, too.
|
||
|
// The original message is stored next to payload.
|
||
|
//
|
||
|
// message : content string
|
||
|
function TelegramCommandNode(config) {
|
||
|
RED.nodes.createNode(this, config);
|
||
|
var node = this;
|
||
|
var command = config.command;
|
||
|
var strict = config.strict;
|
||
|
this.bot = config.bot;
|
||
|
|
||
|
this.config = RED.nodes.getNode(this.bot);
|
||
|
if (this.config) {
|
||
|
this.config.register(node);
|
||
|
|
||
|
node.status({ fill: "red", shape: "ring", text: "not connected" });
|
||
|
|
||
|
node.telegramBot = this.config.getTelegramBot();
|
||
|
node.botname = this.config.botname;
|
||
|
if (node.telegramBot) {
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
|
||
|
node.telegramBot.on('message', function (botMsg) {
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
|
||
|
var username = botMsg.from.username;
|
||
|
var chatid = botMsg.chat.id;
|
||
|
if (node.config.isAuthorized(chatid, username)) {
|
||
|
var msg;
|
||
|
var messageDetails;
|
||
|
if (botMsg.text) {
|
||
|
var message = botMsg.text;
|
||
|
var tokens = message.split(" ");
|
||
|
|
||
|
var isChatCommand = tokens[0] === command;
|
||
|
var command2 = command + "@" + node.botname;
|
||
|
var isDirectCommand = tokens[0] === command2;
|
||
|
var isGroupChat = chatid < 0;
|
||
|
|
||
|
if (isDirectCommand ||
|
||
|
(isChatCommand && !isGroupChat) ||
|
||
|
(isChatCommand && isGroupChat && !strict)) {
|
||
|
|
||
|
var remainingText;
|
||
|
if(isDirectCommand)
|
||
|
{
|
||
|
remainingText = message.replace(command2, "");
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
remainingText = message.replace(command, "");
|
||
|
}
|
||
|
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'message', content: remainingText };
|
||
|
msg = { payload: messageDetails, originalMessage: botMsg };
|
||
|
node.send([msg, null]);
|
||
|
} else {
|
||
|
messageDetails = { chatId: botMsg.chat.id, messageId: botMsg.message_id, type: 'message', content: botMsg.text };
|
||
|
msg = { payload: messageDetails, originalMessage: botMsg };
|
||
|
node.send([null, msg]);
|
||
|
}
|
||
|
} else {
|
||
|
// unknown type --> no output
|
||
|
}
|
||
|
} else {
|
||
|
// ignoring unauthorized calls
|
||
|
if(node.config.verbose){
|
||
|
node.warn("Unauthorized incoming call from " + username);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
} else {
|
||
|
node.warn("bot not initialized.");
|
||
|
node.status({ fill: "red", shape: "ring", text: "no bot token found in config" });
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("config node failed to initialize.");
|
||
|
node.status({ fill: "red", shape: "ring", text: "config node failed to initialize" });
|
||
|
}
|
||
|
|
||
|
this.on("close", function () {
|
||
|
node.telegramBot.off('message');
|
||
|
node.status({});
|
||
|
});
|
||
|
}
|
||
|
RED.nodes.registerType("telegram command", TelegramCommandNode);
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------------------------
|
||
|
// The input node receives an event from the chat.
|
||
|
// The type of event can be configured:
|
||
|
// - callback_query
|
||
|
// - inline_query
|
||
|
// - edited_message
|
||
|
// - channel_post
|
||
|
// - edited_channel_post
|
||
|
// - edited_message_text
|
||
|
// The message details are stored in the payload
|
||
|
// chatId
|
||
|
// messageId
|
||
|
// type
|
||
|
// content
|
||
|
// depending on type from and date is part of the output, too.
|
||
|
// The original message is stored next to payload.
|
||
|
// callback_query : content string
|
||
|
function TelegramEventNode(config) {
|
||
|
RED.nodes.createNode(this, config);
|
||
|
var node = this;
|
||
|
this.bot = config.bot;
|
||
|
this.event = config.event;
|
||
|
this.autoAnswerCallback = config.autoanswer;
|
||
|
|
||
|
this.config = RED.nodes.getNode(this.bot);
|
||
|
if (this.config) {
|
||
|
this.config.register(node);
|
||
|
|
||
|
node.status({ fill: "red", shape: "ring", text: "not connected" });
|
||
|
|
||
|
node.telegramBot = this.config.getTelegramBot();
|
||
|
node.botname = this.config.botname;
|
||
|
if (node.telegramBot) {
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
|
||
|
node.telegramBot.on(this.event, (botMsg) => {
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
|
||
|
var username;
|
||
|
var chatid;
|
||
|
if (botMsg.chat) { //channel
|
||
|
username = botMsg.chat.username;
|
||
|
chatid = botMsg.chat.id;
|
||
|
} else if (botMsg.from) { //private, group, supergroup
|
||
|
username = botMsg.from.username;
|
||
|
chatid = botMsg.from.id;
|
||
|
} else {
|
||
|
node.error("username or chatid undefined");
|
||
|
}
|
||
|
if (node.config.isAuthorized(chatid, username)) {
|
||
|
var msg;
|
||
|
var messageDetails;
|
||
|
|
||
|
switch (this.event) {
|
||
|
case 'callback_query':
|
||
|
var callbackQueryId = botMsg.id;
|
||
|
messageDetails = {
|
||
|
chatId: chatid,
|
||
|
messageId: botMsg.message.message_id,
|
||
|
type: 'callback_query',
|
||
|
content: botMsg.data,
|
||
|
callbackQueryId: callbackQueryId,
|
||
|
from: botMsg.from
|
||
|
};
|
||
|
if (node.autoAnswer) {
|
||
|
node.telegramBot.answerCallbackQuery(callbackQueryId).then(function (result) {
|
||
|
// Nothing to do here
|
||
|
;
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// /setinline must be set before in botfather see https://core.telegram.org/bots/inline
|
||
|
case 'inline_query':
|
||
|
var inlineQueryId = botMsg.id;
|
||
|
messageDetails = {
|
||
|
chatId: chatid,
|
||
|
type: 'inline_query',
|
||
|
content: botMsg.query,
|
||
|
inlineQueryId: inlineQueryId,
|
||
|
offset: botMsg.offset,
|
||
|
from: botMsg.from,
|
||
|
location: botMsg.location // location is only available when /setinlinegeo is set in botfather
|
||
|
};
|
||
|
// Right now this is not supported as a result is required!
|
||
|
//if (node.autoAnswer) {
|
||
|
// // result = https://core.telegram.org/bots/api#inlinequeryresult
|
||
|
// node.telegramBot.answerInlineQuery(inlineQueryId, results).then(function (result) {
|
||
|
// // Nothing to do here
|
||
|
// ;
|
||
|
// });
|
||
|
//}
|
||
|
break;
|
||
|
|
||
|
case 'edited_message':
|
||
|
messageDetails = {
|
||
|
chatId: chatid,
|
||
|
messageId: botMsg.message_id,
|
||
|
type: "edited_message",
|
||
|
content: botMsg.text,
|
||
|
editDate: botMsg.edit_date,
|
||
|
date: botMsg.date,
|
||
|
from: botMsg.from,
|
||
|
chat: botMsg.chat,
|
||
|
location: botMsg.location // for live location updates
|
||
|
};
|
||
|
break;
|
||
|
|
||
|
// the text of an already sent message.
|
||
|
case 'edited_message_text':
|
||
|
messageDetails = {
|
||
|
chatId: chatid,
|
||
|
type: "edited_message_text",
|
||
|
messageId: botMsg.message_id,
|
||
|
content: botMsg.text,
|
||
|
editDate: botMsg.edit_date,
|
||
|
date: botMsg.date,
|
||
|
chat: botMsg.chat
|
||
|
};
|
||
|
break;
|
||
|
|
||
|
// the caption of a document or an image ...
|
||
|
case 'edited_message_caption':
|
||
|
messageDetails = {
|
||
|
chatId: chatid,
|
||
|
type: "edited_message_caption",
|
||
|
messageId: botMsg.message_id,
|
||
|
content: botMsg.caption,
|
||
|
editDate: botMsg.edit_date,
|
||
|
date: botMsg.date,
|
||
|
chat: botMsg.chat
|
||
|
};
|
||
|
break;
|
||
|
|
||
|
case 'channel_post':
|
||
|
messageDetails = {
|
||
|
chatId: chatid,
|
||
|
messageId: botMsg.message_id,
|
||
|
type: "channel_post",
|
||
|
content: botMsg.text,
|
||
|
date: botMsg.date,
|
||
|
chat: botMsg.chat
|
||
|
};
|
||
|
break;
|
||
|
|
||
|
case 'edited_channel_post':
|
||
|
messageDetails = {
|
||
|
chatId: chatid,
|
||
|
type: "edited_channel_post",
|
||
|
messageId: botMsg.message_id,
|
||
|
content: botMsg.text,
|
||
|
editDate: botMsg.edit_date,
|
||
|
date: botMsg.date,
|
||
|
chat: botMsg.chat
|
||
|
};
|
||
|
break;
|
||
|
|
||
|
case 'edited_channel_post_text':
|
||
|
messageDetails = {
|
||
|
chatId: chatid,
|
||
|
type: "edited_channel_post_text",
|
||
|
messageId: botMsg.message_id,
|
||
|
content: botMsg.text,
|
||
|
editDate: botMsg.edit_date,
|
||
|
date: botMsg.date,
|
||
|
chat: botMsg.chat
|
||
|
};
|
||
|
break;
|
||
|
|
||
|
case 'edited_channel_post_caption':
|
||
|
messageDetails = {
|
||
|
chatId: chatid,
|
||
|
type: "edited_channel_post_caption",
|
||
|
messageId: botMsg.message_id,
|
||
|
content: botMsg.caption,
|
||
|
editDate: botMsg.edit_date,
|
||
|
date: botMsg.date,
|
||
|
chat: botMsg.chat
|
||
|
};
|
||
|
break;
|
||
|
|
||
|
|
||
|
// TODO: implement those
|
||
|
// chosen_inline_result,
|
||
|
// shippingQuery, preCheckoutQuery
|
||
|
|
||
|
default:
|
||
|
}
|
||
|
|
||
|
if (messageDetails != null) {
|
||
|
msg = {
|
||
|
payload: messageDetails,
|
||
|
originalMessage: botMsg
|
||
|
};
|
||
|
node.send(msg);
|
||
|
}
|
||
|
} else {
|
||
|
// ignoring unauthorized calls
|
||
|
if(node.config.verbose){
|
||
|
node.warn("Unauthorized incoming call from " + username);
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
} else {
|
||
|
node.warn("bot not initialized.");
|
||
|
node.status({ fill: "red", shape: "ring", text: "no bot token found in config" });
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("config node failed to initialize.");
|
||
|
node.status({ fill: "red", shape: "ring", text: "config node failed to initialize" });
|
||
|
}
|
||
|
|
||
|
this.on("close", function () {
|
||
|
node.telegramBot.off(this.event);
|
||
|
node.status({});
|
||
|
});
|
||
|
}
|
||
|
RED.nodes.registerType("telegram event", TelegramEventNode);
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------------------------
|
||
|
// The output node sends to the chat and passes the msg through.
|
||
|
// The payload needs three fields
|
||
|
// chatId : string destination chat
|
||
|
// type : string type of message to send
|
||
|
// content : message content
|
||
|
// The type is a string can be any of the following:
|
||
|
// message content is String
|
||
|
// photo content is String|stream.Stream|Buffer
|
||
|
// audio content is String|stream.Stream|Buffer
|
||
|
// document content is String|stream.Stream|Buffer
|
||
|
// sticker content is String|stream.Stream|Buffer
|
||
|
// video content is String|stream.Stream|Buffer
|
||
|
// voice content is String|stream.Stream|Buffer
|
||
|
// location content is an object that contains latitude and logitude
|
||
|
// contact content is full contact object
|
||
|
// mediaGroup content is array of mediaObject
|
||
|
// action content is one of the following:
|
||
|
// typing, upload_photo, record_video, upload_video, record_audio, upload_audio,
|
||
|
// upload_document, find_location, record_video_note, upload_video_note
|
||
|
function TelegramOutNode(config) {
|
||
|
RED.nodes.createNode(this, config);
|
||
|
var node = this;
|
||
|
this.bot = config.bot;
|
||
|
|
||
|
this.config = RED.nodes.getNode(this.bot);
|
||
|
if (this.config) {
|
||
|
this.config.register(node);
|
||
|
|
||
|
node.status({ fill: "red", shape: "ring", text: "not connected" });
|
||
|
|
||
|
node.telegramBot = this.config.getTelegramBot();
|
||
|
if (node.telegramBot) {
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
} else {
|
||
|
node.warn("bot not initialized.");
|
||
|
node.status({ fill: "red", shape: "ring", text: "bot not initialized" });
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("config node failed to initialize.");
|
||
|
node.status({ fill: "red", shape: "ring", text: "config node failed to initialize" });
|
||
|
}
|
||
|
|
||
|
this.hasContent = function (msg) {
|
||
|
var hasContent;
|
||
|
if (msg.payload.content) {
|
||
|
hasContent = true;
|
||
|
}
|
||
|
else {
|
||
|
node.warn("msg.payload.content is empty");
|
||
|
hasContent = false;
|
||
|
}
|
||
|
|
||
|
return hasContent;
|
||
|
}
|
||
|
|
||
|
this.on('input', function (msg, nodeSend, nodeDone) {
|
||
|
nodeSend = nodeSend || function() { node.send.apply(node,arguments) };
|
||
|
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
|
||
|
if (msg.payload) {
|
||
|
if (msg.payload.chatId) {
|
||
|
if (msg.payload.type) {
|
||
|
|
||
|
var chatId = msg.payload.chatId;
|
||
|
var type = msg.payload.type;
|
||
|
addCaptionToMessageOptions(msg);
|
||
|
|
||
|
switch (type) {
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
case 'message':
|
||
|
if (this.hasContent(msg)) {
|
||
|
// the maximum message size is 4096 so we must split the message into smaller chunks.
|
||
|
var chunkSize = 4000;
|
||
|
var message = msg.payload.content;
|
||
|
|
||
|
var done = false;
|
||
|
do {
|
||
|
var messageToSend;
|
||
|
if (message.length > chunkSize) {
|
||
|
messageToSend = message.substr(0, chunkSize);
|
||
|
message = message.substr(chunkSize);
|
||
|
} else {
|
||
|
messageToSend = message;
|
||
|
done = true;
|
||
|
}
|
||
|
|
||
|
node.telegramBot.sendMessage(chatId, messageToSend, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
}).catch(function (err) {
|
||
|
// markdown error? try plain mode
|
||
|
if (
|
||
|
String(err).includes("can't parse entities in message text:") &&
|
||
|
msg.payload.options && msg.payload.options.parse_mode === 'Markdown'
|
||
|
) {
|
||
|
delete msg.payload.options.parse_mode;
|
||
|
node.telegramBot.sendMessage(chatId, messageToSend, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
return;
|
||
|
}
|
||
|
if (nodeDone) {
|
||
|
nodeDone(err);
|
||
|
} else {
|
||
|
throw err;
|
||
|
}
|
||
|
});
|
||
|
|
||
|
} while (!done)
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'photo':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendPhoto(chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
case 'mediaGroup':
|
||
|
if(this.hasContent(msg)) {
|
||
|
if(Array.isArray(msg.payload.content)) {
|
||
|
for (var i = 0; i < msg.payload.content.length; i++) {
|
||
|
var mediaItem = msg.payload.content[i];
|
||
|
if(typeof mediaItem.type !== "string") {
|
||
|
node.warn("msg.payload.content[" + i + "].type is not a string it is " + typeof mediaItem.type);
|
||
|
break;
|
||
|
}
|
||
|
if(mediaItem.media === undefined) {
|
||
|
node.warn("msg.payload.content[" + i + "].media is not defined");
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
node.telegramBot.sendMediaGroup(chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
} else {
|
||
|
node.warn("msg.payload.content for mediaGroup is not an array of mediaItem");
|
||
|
}
|
||
|
}
|
||
|
break;
|
||
|
case 'audio':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendAudio(chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'document':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendDocument(chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'sticker':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendSticker(chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'video':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendVideo(chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'video_note':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendVideoNote(chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'voice':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendVoice(chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'location':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendLocation(chatId, msg.payload.content.latitude, msg.payload.content.longitude, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'venue':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendVenue(chatId, msg.payload.content.latitude, msg.payload.content.longitude, msg.payload.content.title, msg.payload.content.address, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'contact':
|
||
|
if (this.hasContent(msg)) {
|
||
|
if (msg.payload.content.last_name) {
|
||
|
if (!msg.payload.options) {
|
||
|
msg.payload.options = {};
|
||
|
}
|
||
|
msg.payload.options.last_name = msg.payload.content.last_name;
|
||
|
}
|
||
|
node.telegramBot.sendContact(chatId, msg.payload.content.phone_number, msg.payload.content.first_name, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
// --------------------------------------------------------------------
|
||
|
|
||
|
case 'editMessageLiveLocation':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.editMessageLiveLocation(msg.payload.content.latitude, msg.payload.content.longitude, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'stopMessageLiveLocation':
|
||
|
// This message requires the options to be set!
|
||
|
//if (this.hasContent(msg)) {
|
||
|
node.telegramBot.stopMessageLiveLocation(msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
//}
|
||
|
break;
|
||
|
|
||
|
case 'editMessageCaption':
|
||
|
case 'editMessageText':
|
||
|
case 'editMessageReplyMarkup':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot[type](msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
msg.payload.sentMessageId = result.message_id;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'callback_query':
|
||
|
if (this.hasContent(msg)) {
|
||
|
// The new signature expects one object instead of three arguments.
|
||
|
var callbackQueryId = msg.payload.callbackQueryId;
|
||
|
var options = {
|
||
|
callback_query_id: callbackQueryId,
|
||
|
text: msg.payload.content,
|
||
|
show_alert: msg.payload.options
|
||
|
};
|
||
|
node.telegramBot.answerCallbackQuery(callbackQueryId, options).then(function (result) {
|
||
|
msg.payload.content = result; // true if succeeded
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'inline_query':
|
||
|
if (this.hasContent(msg)) {
|
||
|
var inlineQueryId = msg.payload.inlineQueryId;
|
||
|
var results = msg.payload.results; // this type requires results to be set: see https://core.telegram.org/bots/api#inlinequeryresult
|
||
|
node.telegramBot.answerInlineQuery(inlineQueryId, results).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'action':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot.sendChatAction(chatId, msg.payload.content).then(function (result) {
|
||
|
msg.payload.content = result; // true if succeeded
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
// Some of the following functions require the bot to be administrator of the chat/channel
|
||
|
|
||
|
case 'getChatAdministrators':
|
||
|
case 'getChatMembersCount':
|
||
|
case 'getChat':
|
||
|
case 'leaveChat':
|
||
|
case 'exportChatInviteLink':
|
||
|
case 'unpinChatMessage':
|
||
|
case 'deleteChatPhoto':
|
||
|
node.telegramBot[type](chatId).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
break;
|
||
|
|
||
|
case 'kickChatMember':
|
||
|
case 'unbanChatMember':
|
||
|
case 'restrictChatMember':
|
||
|
case 'promoteChatMember':
|
||
|
case 'getChatMember':
|
||
|
// The userId must be passed in msg.payload.content: note that this is is a number not the username.
|
||
|
// Right now there is no way for resolving the user_id by username in the official API.
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot[type](chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// --------------------------------------------------------------------
|
||
|
case 'setChatTitle':
|
||
|
case 'setChatDescription':
|
||
|
case 'pinChatMessage':
|
||
|
case 'deleteMessage':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot[type](chatId, msg.payload.content).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
case 'setChatPhoto':
|
||
|
if (this.hasContent(msg)) {
|
||
|
node.telegramBot[type](chatId, msg.payload.content, msg.payload.options).then(function (result) {
|
||
|
msg.payload.content = result;
|
||
|
nodeSend(msg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
break;
|
||
|
|
||
|
// TODO:
|
||
|
// getUserProfilePhotos, getFile,
|
||
|
// setChatStickerSet, deleteChatStickerSet
|
||
|
// sendGame, setGameScore, getGameHighScores
|
||
|
// sendInvoice, answerShippingQuery, answerPreCheckoutQuery
|
||
|
// getStickerSet, uploadStickerFile, createNewStickerSet, addStickerToSet, setStickerPositionInSet, deleteStickerFromSet
|
||
|
|
||
|
default:
|
||
|
// unknown type nothing to send.
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("msg.payload.type is empty");
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("msg.payload.chatId is empty");
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("msg.payload is empty");
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
RED.nodes.registerType("telegram sender", TelegramOutNode);
|
||
|
|
||
|
|
||
|
|
||
|
// --------------------------------------------------------------------------------------------
|
||
|
// The output node receices the reply for a specified message and passes the msg through.
|
||
|
// The payload needs three fields
|
||
|
// chatId : string destination chat
|
||
|
// sentMessageId : string the id of the message the reply coresponds to.
|
||
|
// content : message content
|
||
|
function TelegramReplyNode(config) {
|
||
|
RED.nodes.createNode(this, config);
|
||
|
var node = this;
|
||
|
this.bot = config.bot;
|
||
|
|
||
|
this.config = RED.nodes.getNode(this.bot);
|
||
|
if (this.config) {
|
||
|
this.config.register(node);
|
||
|
|
||
|
node.status({ fill: "red", shape: "ring", text: "not connected" });
|
||
|
|
||
|
node.telegramBot = this.config.getTelegramBot();
|
||
|
if (node.telegramBot) {
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
} else {
|
||
|
node.warn("bot not initialized.");
|
||
|
node.status({ fill: "red", shape: "ring", text: "bot not initialized" });
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("config node failed to initialize.");
|
||
|
node.status({ fill: "red", shape: "ring", text: "config node failed to initialize" });
|
||
|
}
|
||
|
|
||
|
this.on('input', function (msg, nodeSend, nodeDone) {
|
||
|
|
||
|
node.status({ fill: "green", shape: "ring", text: "connected" });
|
||
|
|
||
|
if (msg.payload) {
|
||
|
if (msg.payload.chatId) {
|
||
|
if (msg.payload.sentMessageId) {
|
||
|
|
||
|
var chatId = msg.payload.chatId;
|
||
|
var messageId = msg.payload.sentMessageId;
|
||
|
|
||
|
node.telegramBot.onReplyToMessage(chatId, messageId, function (botMsg) {
|
||
|
|
||
|
var messageDetails = getMessageDetails(botMsg);
|
||
|
if (messageDetails) {
|
||
|
var newMsg = { payload: messageDetails, originalMessage: botMsg };
|
||
|
nodeSend(newMsg);
|
||
|
if (nodeDone) {
|
||
|
nodeDone();
|
||
|
}
|
||
|
}
|
||
|
});
|
||
|
|
||
|
} else {
|
||
|
node.warn("msg.payload.sentMessageId is empty");
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("msg.payload.chatId is empty");
|
||
|
}
|
||
|
} else {
|
||
|
node.warn("msg.payload is empty");
|
||
|
}
|
||
|
});
|
||
|
}
|
||
|
RED.nodes.registerType("telegram reply", TelegramReplyNode);
|
||
|
}
|