From f59dbeec3b3759cd9878195554293ef2a6641ff0 Mon Sep 17 00:00:00 2001 From: Brett Taylor Date: Tue, 27 Apr 2021 09:39:10 -0400 Subject: [PATCH 1/2] initial discord webook bot --- .../Trading-Bot/Announcements/DiscordBot.js | 66 +++++++++++++++++++ .../Trading-Bot/Announcements/SocialBots.js | 12 +++- 2 files changed, 75 insertions(+), 3 deletions(-) create mode 100644 Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/DiscordBot.js diff --git a/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/DiscordBot.js b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/DiscordBot.js new file mode 100644 index 000000000..73840e132 --- /dev/null +++ b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/DiscordBot.js @@ -0,0 +1,66 @@ +exports.newSuperalgosBotModulesDiscordBot = function (processIndex) { + + const MODULE_NAME = 'Discord Bot' + + let thisObject = { + DiscordBot: undefined, + webhookURL: undefined, + sendMessage: sendMessage, + initialize: initialize, + finalize: finalize + } + + return thisObject + + function initialize(webhookURL) { + /* Discord Bot Initialization */ + + try { + thisObject.webhookURL = webhookURL + + const message = "Discord bot has started." + thisObject.sendMessage(message).catch(err => parentLogger.write(MODULE_NAME, "[WARN] initializeDiscordBot -> Discord error -> err = " + err)) + + } catch (err) { + parentLogger.write(MODULE_NAME, "[WARN] initialize -> err = " + err.stack); + } + + } + + function finalize() { + const message = "Discord bot has stopped." + thisObject.sendMessage(message).catch(err => parentLogger.write(MODULE_NAME, "[WARN] finalize -> Discord error -> err = " + err)) + } + + function sendMessage(message) { + const https = require('https'); + + try { + message = JSON.stringify(message); + } catch (err) { + parentLogger.write(MODULE_NAME, "[WARN] announce -> Discord JSON message error -> err = " + err) + } + + return new Promise((resolve, reject) => { + const requestOptions = { + method: 'POST', + header: { 'Content-Type': 'application/json' } + } + + const req = https.request(thisObject.webhookURL, requestOptions, (res) => { + let response = '' + res.on('data', (data) => { response += data }) + res.on('end', () => { resolve(response) }) + }) + + req.on('error', (err) => { + reject(err) + parentLogger.write(MODULE_NAME, "[WARN] announce -> Discord request error -> err = " + err) + }) + + req.write(message) + req.end() + }) + } + +} diff --git a/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/SocialBots.js b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/SocialBots.js index 6e72dbc49..614c136cb 100644 --- a/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/SocialBots.js +++ b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/SocialBots.js @@ -19,6 +19,10 @@ exports.newSuperalgosBotModulesSocialBots = function (processIndex) { let config = socialBot.config socialBot.botInstance = TS.projects.superalgos.botModules.telegramBot.newSuperalgosBotModulesTelegramBot(processIndex) socialBot.botInstance.initialize(config.botToken, config.chatId) + } else if (socialBot.type === "Discord Bot"){ + let config = socialBot.config + socialBot.botInstance = TS.projects.superalgos.botModules.discordBot.newSuperalgosBotModulesTelegramBot(processIndex) + socialBot.botInstance.initialize(config.webhookURL) } } } @@ -30,6 +34,8 @@ exports.newSuperalgosBotModulesSocialBots = function (processIndex) { try { if (socialBot.type === "Telegram Bot") { socialBot.botInstance.telegramAPI.sendMessage(socialBot.botInstance.chatId, text).catch(err => TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, "[WARN] initialize -> initializeSocialBots -> announce -> Telegram API error -> err = " + err)) + } else if (socialBot.type === "Discord Bot") { + socialBot.botInstance.sendMessage(text).catch(err => TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, "[WARN] initialize -> initializeSocialBots -> announce -> Discord Bot error -> err = " + err)) } } catch (err) { TS.projects.superalgos.globals.loggerVariables.VARIABLES_BY_PROCESS_INDEX_MAP.get(processIndex).BOT_MAIN_LOOP_LOGGER_MODULE_OBJECT.write(MODULE_NAME, "[WARN] initialize -> announce -> err = " + err.stack); @@ -47,7 +53,7 @@ exports.newSuperalgosBotModulesSocialBots = function (processIndex) { if (TS.projects.superalgos.globals.processConstants.CONSTANTS_BY_PROCESS_INDEX_MAP.get(processIndex).SESSION_NODE.socialBots.bots !== undefined) { for (let i = 0; i < TS.projects.superalgos.globals.processConstants.CONSTANTS_BY_PROCESS_INDEX_MAP.get(processIndex).SESSION_NODE.socialBots.bots.length; i++) { let socialBot = TS.projects.superalgos.globals.processConstants.CONSTANTS_BY_PROCESS_INDEX_MAP.get(processIndex).SESSION_NODE.socialBots.bots[i] - if (socialBot.type === "Telegram Bot") { + if (socialBot.type === "Telegram Bot" || socialBot.type === "Discord Bot") { socialBot.botInstance.finalize() } } @@ -59,11 +65,11 @@ exports.newSuperalgosBotModulesSocialBots = function (processIndex) { if (TS.projects.superalgos.globals.processConstants.CONSTANTS_BY_PROCESS_INDEX_MAP.get(processIndex).SESSION_NODE.socialBots.bots !== undefined) { for (let i = 0; i < TS.projects.superalgos.globals.processConstants.CONSTANTS_BY_PROCESS_INDEX_MAP.get(processIndex).SESSION_NODE.socialBots.bots.length; i++) { let socialBot = TS.projects.superalgos.globals.processConstants.CONSTANTS_BY_PROCESS_INDEX_MAP.get(processIndex).SESSION_NODE.socialBots.bots[i] - if (socialBot.type === "Telegram Bot") { + if (socialBot.type === "Telegram Bot" || socialBot.type === "Discord Bot") { socialBot.botInstance.sendMessage(message) } } } } } -} \ No newline at end of file +} From 3831809f6c5f69d8b8df243118775596d9d685e5 Mon Sep 17 00:00:00 2001 From: Brett Taylor Date: Tue, 27 Apr 2021 14:41:06 -0400 Subject: [PATCH 2/2] add more discord bot config and docs --- Projects/ProjectsSchema.json | 9 ++- .../Schemas/App-Schema/discord-bot.json | 41 +++++++++++++ .../Schemas/App-Schema/social-bots.json | 10 ++- .../Schemas/Docs-Nodes/discord-bot.json | 14 +++++ .../Schemas/Docs-Nodes/social-bots.json | 4 +- .../Trading-Bot/Announcements/DiscordBot.js | 1 - .../UI/Function-Libraries/ActionSwitch.js | 5 ++ .../Function-Libraries/SocialBotsFunctions.js | 61 ++++++++++++++++++- 8 files changed, 138 insertions(+), 7 deletions(-) create mode 100644 Projects/Superalgos/Schemas/App-Schema/discord-bot.json create mode 100644 Projects/Superalgos/Schemas/Docs-Nodes/discord-bot.json diff --git a/Projects/ProjectsSchema.json b/Projects/ProjectsSchema.json index e6c08f917..2fbb81987 100644 --- a/Projects/ProjectsSchema.json +++ b/Projects/ProjectsSchema.json @@ -675,6 +675,13 @@ "fileName": "TelegramBot.js", "folderName": "Trading-Bot/Announcements" }, + { + "name": "Discord Bot", + "propertyName": "discordBot", + "functionName": "newSuperalgosBotModulesDiscordBot", + "fileName": "DiscordBot.js", + "folderName": "Trading-Bot/Announcements" + }, { "name": "Trading Process", "propertyName": "tradingProcess", @@ -943,4 +950,4 @@ ] } } -] \ No newline at end of file +] diff --git a/Projects/Superalgos/Schemas/App-Schema/discord-bot.json b/Projects/Superalgos/Schemas/App-Schema/discord-bot.json new file mode 100644 index 000000000..615f623b3 --- /dev/null +++ b/Projects/Superalgos/Schemas/App-Schema/discord-bot.json @@ -0,0 +1,41 @@ +{ + "type": "Discord Bot", + "editors": { + "config": true + }, + "menuItems": [ + { + "action": "Configure", + "actionFunction": "uiObject.configEditor.activate", + "label": "Configure", + "iconPathOn": "configuration", + "iconPathOff": "configuration" + }, + { + "action": "Send Discord Test Message", + "label": "Send Discord Test Message", + "iconPathOn": "test-entity", + "iconPathOff": "test-entity", + "actionFunction": "payload.executeAction" + }, + { + "action": "Delete UI Object", + "askConfirmation": true, + "confirmationLabel": "Confirm to Delete", + "label": "Delete", + "iconPathOn": "delete-entity", + "iconPathOff": "delete-entity", + "actionFunction": "payload.executeAction" + } + ], + "initialValues": { + "config": "{\n\"webhookURL\": \"Paste the webhook url obtained from Discord > Channel Settings > Integrations > Webhooks > New Webhook\"\n}" + }, + "addLeftIcons": true, + "level": 4, + "attachingRules": { + "compatibleTypes": "->Social Bots->" + }, + "childrenNodesProperties": [], + "propertyNameAtParent": "bots" +} diff --git a/Projects/Superalgos/Schemas/App-Schema/social-bots.json b/Projects/Superalgos/Schemas/App-Schema/social-bots.json index 0cefd91a6..e0963016c 100644 --- a/Projects/Superalgos/Schemas/App-Schema/social-bots.json +++ b/Projects/Superalgos/Schemas/App-Schema/social-bots.json @@ -7,6 +7,12 @@ "relatedUiObject": "Telegram Bot", "actionFunction": "payload.executeAction" }, + { + "action": "Add Discord Bot", + "label": "Add Discord Bot", + "relatedUiObject": "Discord Bot", + "actionFunction": "payload.executeAction" + }, { "action": "Delete UI Object", "askConfirmation": true, @@ -26,9 +32,9 @@ { "name": "bots", "type": "array", - "childType": "Telegram Bot", + "childType": "Telegram Bot, Discord Bot", "autoAdd": true } ], "propertyNameAtParent": "socialBots" -} \ No newline at end of file +} diff --git a/Projects/Superalgos/Schemas/Docs-Nodes/discord-bot.json b/Projects/Superalgos/Schemas/Docs-Nodes/discord-bot.json new file mode 100644 index 000000000..826b51e65 --- /dev/null +++ b/Projects/Superalgos/Schemas/Docs-Nodes/discord-bot.json @@ -0,0 +1,14 @@ +{ + "type": "Discord Bot", + "definition": { + "text": "The Discord Bot node is used for defining the configuration for a Discord Bot. This bot allows Superalgos to send announcements to a Discord Channel.", + "updated": 1619546769461 + }, + "paragraphs": [ + { + "style": "Link", + "text": "In order to use the Discord Bot, you will need to create a new Webhook Integration in Discord. The only configuration that is required within Superalgos is the webhookURL.", + "updated": 1619546875758 + } + ] +} \ No newline at end of file diff --git a/Projects/Superalgos/Schemas/Docs-Nodes/social-bots.json b/Projects/Superalgos/Schemas/Docs-Nodes/social-bots.json index cf564cab2..36f35b99d 100644 --- a/Projects/Superalgos/Schemas/Docs-Nodes/social-bots.json +++ b/Projects/Superalgos/Schemas/Docs-Nodes/social-bots.json @@ -8,8 +8,8 @@ }, { "style": "Note", - "text": "At present Superalgos only supports the Telegram messaging application.", - "updated": 1618221056200 + "text": "At present Superalgos only supports Telegram and Discord messaging applications.", + "updated": 1619547181067 } ], "definition": { diff --git a/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/DiscordBot.js b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/DiscordBot.js index 73840e132..27c02da09 100644 --- a/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/DiscordBot.js +++ b/Projects/Superalgos/TS/Bot-Modules/Trading-Bot/Announcements/DiscordBot.js @@ -3,7 +3,6 @@ exports.newSuperalgosBotModulesDiscordBot = function (processIndex) { const MODULE_NAME = 'Discord Bot' let thisObject = { - DiscordBot: undefined, webhookURL: undefined, sendMessage: sendMessage, initialize: initialize, diff --git a/Projects/Superalgos/UI/Function-Libraries/ActionSwitch.js b/Projects/Superalgos/UI/Function-Libraries/ActionSwitch.js index 5e2324136..3b1016f58 100644 --- a/Projects/Superalgos/UI/Function-Libraries/ActionSwitch.js +++ b/Projects/Superalgos/UI/Function-Libraries/ActionSwitch.js @@ -717,6 +717,11 @@ function newSuperalgosActionSwitch() { UI.projects.superalgos.functionLibraries.socialBotsFunctions.sendTelegramTestMessage(action.node, action.callBackFunction) } break + case 'Send Discord Test Message': + { + UI.projects.superalgos.functionLibraries.socialBotsFunctions.sendDiscordTestMessage(action.node, action.callBackFunction) + } + break } } } diff --git a/Projects/Superalgos/UI/Function-Libraries/SocialBotsFunctions.js b/Projects/Superalgos/UI/Function-Libraries/SocialBotsFunctions.js index ad4be83fb..c0be2c963 100644 --- a/Projects/Superalgos/UI/Function-Libraries/SocialBotsFunctions.js +++ b/Projects/Superalgos/UI/Function-Libraries/SocialBotsFunctions.js @@ -1,6 +1,7 @@ function newSuperalgosFunctionLibrarySocialBotsFunctions() { thisObject = { - sendTelegramTestMessage: sendTelegramTestMessage + sendTelegramTestMessage: sendTelegramTestMessage, + sendDiscordTestMessage: sendDiscordTestMessage } return thisObject @@ -70,4 +71,62 @@ function newSuperalgosFunctionLibrarySocialBotsFunctions() { } } } + + function sendDiscordTestMessage(node, callBackFunction) { + + let webhookURL = UI.projects.superalgos.utilities.nodeConfig.loadPropertyFromNodeConfig(node.payload, 'webhookURL') + + if (webhookURL === undefined) { + node.payload.uiObject.setErrorMessage('webhookURL parameter not defined.') + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + return + } + + let params = { + text: "Test message from Superalgos!" + } + + httpRequestJSON(JSON.stringify(params), webhookURL, onResponse) + + function onResponse(err) { + if (err.result === GLOBAL.DEFAULT_OK_RESPONSE.result) { + node.payload.uiObject.setInfoMessage('Discord message sent.') + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE) + } else { + node.payload.uiObject.setErrorMessage('Could not send Discord message. Error Response: ' + err.message) + callBackFunction(GLOBAL.DEFAULT_FAIL_RESPONSE) + } + } + + function httpRequestJSON(pContentToSend, pPath, callBackFunction) { + let xmlHttpRequest = new XMLHttpRequest(); + xmlHttpRequest.onreadystatechange = function () { + if (this.readyState === 4 && this.status === 200) { + callBackFunction(GLOBAL.DEFAULT_OK_RESPONSE, xmlHttpRequest.responseText); + return; + } else if (this.readyState === 4 && (this.status === 404 || this.status === 401 || this.status === 400)) { + callBackFunction({ + result: "Fail", + message: JSON.parse(xmlHttpRequest.responseText).description, + code: JSON.parse(xmlHttpRequest.responseText).error_code + }); + return; + } + }; + + if (pContentToSend === undefined) { + xmlHttpRequest.open("GET", pPath, true); + xmlHttpRequest.send(); + } else { + try { + let blob = new Blob([pContentToSend], { type: 'application/json' }); + xmlHttpRequest.open("POST", pPath, true); + xmlHttpRequest.send(blob); + } catch (err) { + if (ERROR_LOG === true) { console.log(spacePad(MODULE_NAME, 50) + " : " + "[ERROR] callServer -> err.message = " & err.message); } + callBackFunction({ result: "Fail", message: err.message }) + } + } + } + } }