From 7f986bd7c457b3c3598fdbdfef297c22e145c55b Mon Sep 17 00:00:00 2001 From: Ylian Saint-Hilaire Date: Wed, 26 Oct 2022 13:19:40 -0700 Subject: [PATCH] First pass as Discord integration (#4651) --- .npmrc | 2 +- MeshCentralServer.njsproj | 2 + meshcentral.js | 4 +- meshmessaging.js | 86 +++++++++++++++++++++++++++++++++++++-- meshuser.js | 4 +- views/default.handlebars | 6 ++- 6 files changed, 96 insertions(+), 8 deletions(-) diff --git a/.npmrc b/.npmrc index 4fd02195..d1cdf2f0 100644 --- a/.npmrc +++ b/.npmrc @@ -1 +1 @@ -engine-strict=true \ No newline at end of file +engine-strict = true \ No newline at end of file diff --git a/MeshCentralServer.njsproj b/MeshCentralServer.njsproj index cfd70d9f..3974731b 100644 --- a/MeshCentralServer.njsproj +++ b/MeshCentralServer.njsproj @@ -681,6 +681,7 @@ + @@ -723,6 +724,7 @@ + diff --git a/meshcentral.js b/meshcentral.js index 8ed85fc8..3576c614 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -3876,7 +3876,8 @@ var ServerWarnings = { 22: "Failed to sign agent {0}: {1}", 23: "Unable to load agent icon file: {0}.", 24: "Unable to load agent logo file: {0}.", - 25: "This NodeJS version does not support OpenID." + 25: "This NodeJS version does not support OpenID.", + 26: "This NodeJS version does not support Discord.js." }; */ @@ -4028,6 +4029,7 @@ function mainStart() { // Messaging support if (config.messaging != null) { if (config.messaging.telegram != null) { modules.push('telegram'); modules.push('input'); } + if (config.messaging.discord != null) { if (nodeVersion >= 17) { modules.push('discord.js@14.6.0'); } else { delete config.messaging.discord; addServerWarning('This NodeJS version does not support Discord.js.', 25); } } } // Setup web based push notifications diff --git a/meshmessaging.js b/meshmessaging.js index f502239e..e88eac4c 100644 --- a/meshmessaging.js +++ b/meshmessaging.js @@ -32,16 +32,25 @@ "bottoken": "00000000:aaaaaaaaaaaaaaaaaaaaaaaa" } } + +// For Discord login, add this in config.json +"messaging": { + "discord": { + "inviteurl": "https://discord.gg/xxxxxxxxx", + "token": "xxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxx" + } +} */ // Construct a messaging server object module.exports.CreateServer = function (parent) { var obj = {}; obj.parent = parent; - obj.providers = 0; // 1 = Telegram, 2 = Signal + obj.providers = 0; // 1 = Telegram, 2 = Signal, 4 = Discord obj.telegramClient = null; + obj.discordClient = null; - // Messaging client setup + // Telegram client setup if (parent.config.messaging.telegram) { // Validate Telegram configuration values var telegramOK = true; @@ -80,16 +89,85 @@ module.exports.CreateServer = function (parent) { } } + // Discord client setup + if (parent.config.messaging.discord) { + // Validate Discord configuration values + var discordOK = true; + if (typeof parent.config.messaging.discord.inviteurl != 'string') { console.log('Invalid or missing Discord invite URL.'); discordOK = false; } + if (typeof parent.config.messaging.discord.token != 'string') { console.log('Invalid or missing Discord token.'); discordOK = false; } + + if (discordOK) { + // Setup Discord + const { Client, GatewayIntentBits } = require('discord.js'); + var discordClient = new Client({ + intents: [ + GatewayIntentBits.Guilds, + GatewayIntentBits.GuildMessages, + GatewayIntentBits.MessageContent, + GatewayIntentBits.GuildMembers, + GatewayIntentBits.DirectMessages + ] + }); + + // Called when Discord client is connected + discordClient.on('ready', function() { + console.log(`MeshCentral Discord client is connected as ${discordClient.user.tag}!`); + obj.discordClient = discordClient; + obj.providers += 4; // Enable Discord messaging + }); + + // Receives incoming messages, ignore for now + discordClient.on('messageCreate', function(message) { + if (message.author.bot) return false; + console.log(`Discord message from ${message.author.username}: ${message.content}`, message.channel.type); + //message.channel.send("Channel Hello"); + //message.author.send('Private Hello'); + }); + + // Called when Discord client received an interaction + discordClient.on('interactionCreate', async function(interaction) { + console.log('Discord interaction', interaction); + if (!interaction.isChatInputCommand()) return; + if (interaction.commandName === 'ping') { await interaction.reply('Pong!'); } + }); + + // Connect Discord client + discordClient.login(parent.config.messaging.discord.token); + } + } + + // Send a direct message to a specific userid + async function discordSendMsg(userId, message) { + const user = await obj.discordClient.users.fetch(userId).catch(function () { return null; }); + if (!user) return; + await user.send(message).catch(function (ex) { console.log('Discord Error', ex); }); + } + + // Convert a userTag to a userId. We need to query the Discord server to find this information. + // Example: findUserByTab('aaaa#0000', function (userid) { sendMsg(userid, 'message'); }); + async function discordFindUserByTag(userTag, func) { + var username = userTag.split('#')[0]; + const guilds = await obj.discordClient.guilds.fetch(); + guilds.forEach(async function (value, key) { + var guild = await value.fetch(); + const guildMembers = await guild.members.search({ query: username }); + guildMembers.forEach(async function (value, key) { + if ((value.user.username + '#' + value.user.discriminator) == userTag) { func(key); return; } + }); + }); + } + // Send an user message obj.sendMessage = function(to, msg, func) { - // Telegram - if ((to.startsWith('telegram:')) && (obj.telegramClient != null)) { + if ((to.startsWith('telegram:')) && (obj.telegramClient != null)) { // Telegram async function sendTelegramMessage(to, msg, func) { if (obj.telegramClient == null) return; parent.debug('email', 'Sending Telegram message to: ' + to.substring(9) + ': ' + msg); try { await obj.telegramClient.sendMessage(to.substring(9), { message: msg }); if (func != null) { func(true); } } catch (ex) { if (func != null) { func(false, ex); } } } sendTelegramMessage(to, msg, func); + } else if ((to.startsWith('discord:')) && (obj.discordClient != null)) { // Discord + discordFindUserByTag(to.substring(8), function (userid) { discordSendMsg(userid, msg); if (func != null) { func(true); } }); } else { // No providers found func(false, "No messaging providers found for this message."); diff --git a/meshuser.js b/meshuser.js index 086128ec..e5b7c9d9 100644 --- a/meshuser.js +++ b/meshuser.js @@ -6691,6 +6691,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use // Setup the handle for the right messaging service var handle = null; if ((command.service == 1) && ((parent.parent.msgserver.providers & 1) != 0)) { handle = 'telegram:@' + command.handle; } + if ((command.service == 4) && ((parent.parent.msgserver.providers & 4) != 0)) { handle = 'discord:' + command.handle; } if (handle == null) return; // Send a verification message @@ -6831,7 +6832,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if (cmdData.cmdargs['_'].length != 2) { var r = []; if ((parent.parent.msgserver.providers & 1) != 0) { r.push("Usage: MSG \"telegram:@UserHandle\" \"Message\"."); } - if ((parent.parent.msgserver.providers & 2) != 0) { r.push("Usage: MSG \"signal:@UserHandle\" \"Message\"."); } + if ((parent.parent.msgserver.providers & 2) != 0) { r.push("Usage: MSG \"signal:UserHandle\" \"Message\"."); } + if ((parent.parent.msgserver.providers & 4) != 0) { r.push("Usage: MSG \"discord:Username#0000\" \"Message\"."); } cmdData.result = r.join('\r\n'); } else { parent.parent.msgserver.sendMessage(cmdData.cmdargs['_'][0], cmdData.cmdargs['_'][1], function (status, msg) { diff --git a/views/default.handlebars b/views/default.handlebars index 4d71cd1a..4d79383b 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -2354,7 +2354,8 @@ 22: "Failed to sign agent {0}: {1}", 23: "Unable to load agent icon file: {0}.", 24: "Unable to load agent logo file: {0}.", - 25: "This NodeJS version does not support OpenID." + 25: "This NodeJS version does not support OpenID.", + 26: "This NodeJS version does not support Discord.js." }; var x = ''; for (var i in message.warnings) { @@ -11983,6 +11984,7 @@ var y = ''; x += '
' + "Service" + '' + y; x += '
' + "Handle" + ''; @@ -15853,6 +15855,7 @@ var y = ''; x += '
' + "Service" + '' + y; x += '
' + "Handle" + ''; @@ -15870,6 +15873,7 @@ var handle = null; if (Q('d2handleinput').value == '') { handle = ''; } else if (Q('d2serviceselect').value == 1) { handle = 'telegram:@' + Q('d2handleinput').value; } + else if (Q('d2serviceselect').value == 4) { handle = 'discord:' + Q('d2handleinput').value; } if (handle != null) { meshserver.send({ action: 'edituser', id: currentUser._id, msghandle: handle }); } }