First pass as Discord integration (#4651)

Ylian Saint-Hilaire committed Oct 26, 2022 at 13:19 UTC 7f986bd7c457b3c3598fdbdfef297c22e145c55b
6 files changed +96 -8
.npmrc
+1 -1
@@ -1 +1 @@
1 -engine-strict=true
\ No newline at end of file
1 +engine-strict = true
\ No newline at end of file
MeshCentralServer.njsproj
+2
@@ -681,6 +681,7 @@
681 <Folder Include="typings\globals\ajv\" />
682 <Folder Include="typings\globals\async\" />
683 <Folder Include="typings\globals\axios\" />
684 + <Folder Include="typings\globals\busboy\" />
685 <Folder Include="typings\globals\connect-redis\" />
686 <Folder Include="typings\globals\cookie-session\" />
687 <Folder Include="typings\globals\core-js\" />
@@ -723,6 +724,7 @@
724 <TypeScriptCompile Include="typings\globals\ajv\index.d.ts" />
725 <TypeScriptCompile Include="typings\globals\async\index.d.ts" />
726 <TypeScriptCompile Include="typings\globals\axios\index.d.ts" />
727 + <TypeScriptCompile Include="typings\globals\busboy\index.d.ts" />
728 <TypeScriptCompile Include="typings\globals\connect-redis\index.d.ts" />
729 <TypeScriptCompile Include="typings\globals\cookie-session\index.d.ts" />
730 <TypeScriptCompile Include="typings\globals\core-js\index.d.ts" />
meshcentral.js
+3 -1
@@ -3876,7 +3876,8 @@ var ServerWarnings = {
3876 22: "Failed to sign agent {0}: {1}",
3877 23: "Unable to load agent icon file: {0}.",
3878 24: "Unable to load agent logo file: {0}.",
3879 - 25: "This NodeJS version does not support OpenID."
3879 + 25: "This NodeJS version does not support OpenID.",
3880 + 26: "This NodeJS version does not support Discord.js."
3881 };
3882 */
3883
@@ -4028,6 +4029,7 @@ function mainStart() {
4029 // Messaging support
4030 if (config.messaging != null) {
4031 if (config.messaging.telegram != null) { modules.push('telegram'); modules.push('input'); }
4032 + 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); } }
4033 }
4034
4035 // Setup web based push notifications
meshmessaging.js
+82 -4
@@ -32,16 +32,25 @@
32 "bottoken": "00000000:aaaaaaaaaaaaaaaaaaaaaaaa"
33 }
34 }
35 +
36 +// For Discord login, add this in config.json
37 +"messaging": {
38 + "discord": {
39 + "inviteurl": "https://discord.gg/xxxxxxxxx",
40 + "token": "xxxxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxxxx"
41 + }
42 +}
43 */
44
45 // Construct a messaging server object
46 module.exports.CreateServer = function (parent) {
47 var obj = {};
48 obj.parent = parent;
41 - obj.providers = 0; // 1 = Telegram, 2 = Signal
49 + obj.providers = 0; // 1 = Telegram, 2 = Signal, 4 = Discord
50 obj.telegramClient = null;
51 + obj.discordClient = null;
52
44 - // Messaging client setup
53 + // Telegram client setup
54 if (parent.config.messaging.telegram) {
55 // Validate Telegram configuration values
56 var telegramOK = true;
@@ -80,16 +89,85 @@ module.exports.CreateServer = function (parent) {
89 }
90 }
91
92 + // Discord client setup
93 + if (parent.config.messaging.discord) {
94 + // Validate Discord configuration values
95 + var discordOK = true;
96 + if (typeof parent.config.messaging.discord.inviteurl != 'string') { console.log('Invalid or missing Discord invite URL.'); discordOK = false; }
97 + if (typeof parent.config.messaging.discord.token != 'string') { console.log('Invalid or missing Discord token.'); discordOK = false; }
98 +
99 + if (discordOK) {
100 + // Setup Discord
101 + const { Client, GatewayIntentBits } = require('discord.js');
102 + var discordClient = new Client({
103 + intents: [
104 + GatewayIntentBits.Guilds,
105 + GatewayIntentBits.GuildMessages,
106 + GatewayIntentBits.MessageContent,
107 + GatewayIntentBits.GuildMembers,
108 + GatewayIntentBits.DirectMessages
109 + ]
110 + });
111 +
112 + // Called when Discord client is connected
113 + discordClient.on('ready', function() {
114 + console.log(`MeshCentral Discord client is connected as ${discordClient.user.tag}!`);
115 + obj.discordClient = discordClient;
116 + obj.providers += 4; // Enable Discord messaging
117 + });
118 +
119 + // Receives incoming messages, ignore for now
120 + discordClient.on('messageCreate', function(message) {
121 + if (message.author.bot) return false;
122 + console.log(`Discord message from ${message.author.username}: ${message.content}`, message.channel.type);
123 + //message.channel.send("Channel Hello");
124 + //message.author.send('Private Hello');
125 + });
126 +
127 + // Called when Discord client received an interaction
128 + discordClient.on('interactionCreate', async function(interaction) {
129 + console.log('Discord interaction', interaction);
130 + if (!interaction.isChatInputCommand()) return;
131 + if (interaction.commandName === 'ping') { await interaction.reply('Pong!'); }
132 + });
133 +
134 + // Connect Discord client
135 + discordClient.login(parent.config.messaging.discord.token);
136 + }
137 + }
138 +
139 + // Send a direct message to a specific userid
140 + async function discordSendMsg(userId, message) {
141 + const user = await obj.discordClient.users.fetch(userId).catch(function () { return null; });
142 + if (!user) return;
143 + await user.send(message).catch(function (ex) { console.log('Discord Error', ex); });
144 + }
145 +
146 + // Convert a userTag to a userId. We need to query the Discord server to find this information.
147 + // Example: findUserByTab('aaaa#0000', function (userid) { sendMsg(userid, 'message'); });
148 + async function discordFindUserByTag(userTag, func) {
149 + var username = userTag.split('#')[0];
150 + const guilds = await obj.discordClient.guilds.fetch();
151 + guilds.forEach(async function (value, key) {
152 + var guild = await value.fetch();
153 + const guildMembers = await guild.members.search({ query: username });
154 + guildMembers.forEach(async function (value, key) {
155 + if ((value.user.username + '#' + value.user.discriminator) == userTag) { func(key); return; }
156 + });
157 + });
158 + }
159 +
160 // Send an user message
161 obj.sendMessage = function(to, msg, func) {
85 - // Telegram
86 - if ((to.startsWith('telegram:')) && (obj.telegramClient != null)) {
162 + if ((to.startsWith('telegram:')) && (obj.telegramClient != null)) { // Telegram
163 async function sendTelegramMessage(to, msg, func) {
164 if (obj.telegramClient == null) return;
165 parent.debug('email', 'Sending Telegram message to: ' + to.substring(9) + ': ' + msg);
166 try { await obj.telegramClient.sendMessage(to.substring(9), { message: msg }); if (func != null) { func(true); } } catch (ex) { if (func != null) { func(false, ex); } }
167 }
168 sendTelegramMessage(to, msg, func);
169 + } else if ((to.startsWith('discord:')) && (obj.discordClient != null)) { // Discord
170 + discordFindUserByTag(to.substring(8), function (userid) { discordSendMsg(userid, msg); if (func != null) { func(true); } });
171 } else {
172 // No providers found
173 func(false, "No messaging providers found for this message.");
meshuser.js
+3 -1
@@ -6691,6 +6691,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
6691 // Setup the handle for the right messaging service
6692 var handle = null;
6693 if ((command.service == 1) && ((parent.parent.msgserver.providers & 1) != 0)) { handle = 'telegram:@' + command.handle; }
6694 + if ((command.service == 4) && ((parent.parent.msgserver.providers & 4) != 0)) { handle = 'discord:' + command.handle; }
6695 if (handle == null) return;
6696
6697 // Send a verification message
@@ -6831,7 +6832,8 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use
6832 if (cmdData.cmdargs['_'].length != 2) {
6833 var r = [];
6834 if ((parent.parent.msgserver.providers & 1) != 0) { r.push("Usage: MSG \"telegram:@UserHandle\" \"Message\"."); }
6834 - if ((parent.parent.msgserver.providers & 2) != 0) { r.push("Usage: MSG \"signal:@UserHandle\" \"Message\"."); }
6835 + if ((parent.parent.msgserver.providers & 2) != 0) { r.push("Usage: MSG \"signal:UserHandle\" \"Message\"."); }
6836 + if ((parent.parent.msgserver.providers & 4) != 0) { r.push("Usage: MSG \"discord:Username#0000\" \"Message\"."); }
6837 cmdData.result = r.join('\r\n');
6838 } else {
6839 parent.parent.msgserver.sendMessage(cmdData.cmdargs['_'][0], cmdData.cmdargs['_'][1], function (status, msg) {
views/default.handlebars
+5 -1
@@ -2354,7 +2354,8 @@
2354 22: "Failed to sign agent {0}: {1}",
2355 23: "Unable to load agent icon file: {0}.",
2356 24: "Unable to load agent logo file: {0}.",
2357 - 25: "This NodeJS version does not support OpenID."
2357 + 25: "This NodeJS version does not support OpenID.",
2358 + 26: "This NodeJS version does not support Discord.js."
2359 };
2360 var x = '';
2361 for (var i in message.warnings) {
@@ -11983,6 +11984,7 @@
11984 var y = '<select id=d2serviceselect style=width:160px;margin-left:8px>';
11985 if ((serverinfo.userMsgProviders & 1) != 0) { y += '<option value=1>' + "Telegram" + '</option>'; }
11986 if ((serverinfo.userMsgProviders & 2) != 0) { y += '<option value=2>' + "Signal Messenger" + '</option>'; }
11987 + if ((serverinfo.userMsgProviders & 4) != 0) { y += '<option value=4>' + "Discord" + '</option>'; }
11988 y += '</select>';
11989 x += '<table><tr><td>' + "Service" + '<td>' + y;
11990 x += '<tr><td>' + "Handle" + '<td><input maxlength=64 style=width:160px;margin-left:8px id=d2handleinput onKeyUp=account_manageMessagingValidate() onkeypress="if (event.key==\'Enter\') account_manageMessagingValidate(1)">';
@@ -15853,6 +15855,7 @@
15855 var y = '<select id=d2serviceselect style=width:160px;margin-left:8px>';
15856 if ((serverinfo.userMsgProviders & 1) != 0) { y += '<option value=1>' + "Telegram" + '</option>'; }
15857 if ((serverinfo.userMsgProviders & 2) != 0) { y += '<option value=2>' + "Signal Messenger" + '</option>'; }
15858 + if ((serverinfo.userMsgProviders & 4) != 0) { y += '<option value=4>' + "Discord" + '</option>'; }
15859 y += '</select>';
15860 x += '<table style=margin-top:12px><tr><td>' + "Service" + '<td>' + y;
15861 x += '<tr><td>' + "Handle" + '<td><input maxlength=64 style=width:160px;margin-left:8px id=d2handleinput onKeyUp=p30editMessagingValidate() onkeypress="if (event.key==\'Enter\') p30editMessagingValidate(1)">';
@@ -15870,6 +15873,7 @@
15873 var handle = null;
15874 if (Q('d2handleinput').value == '') { handle = ''; }
15875 else if (Q('d2serviceselect').value == 1) { handle = 'telegram:@' + Q('d2handleinput').value; }
15876 + else if (Q('d2serviceselect').value == 4) { handle = 'discord:' + Q('d2handleinput').value; }
15877 if (handle != null) { meshserver.send({ action: 'edituser', id: currentUser._id, msghandle: handle }); }
15878 }
15879