diff --git a/meshcentral-config-schema.json b/meshcentral-config-schema.json index ccdd562a..a0abb392 100644 --- a/meshcentral-config-schema.json +++ b/meshcentral-config-schema.json @@ -1417,6 +1417,14 @@ "userurl": { "type": "string", "description": "A URL given to users to help them setup this service." } }, "description": "Enabled ntfy.sh integration support." + }, + "zulip": { + "type": "object", + "properties": { + "email": { "type": "string", "description": "Bot email address to login as." }, + "api_key": { "type": "string", "description": "Bot api key." } + }, + "description": "Enabled Zulip integration support." } } } diff --git a/meshcentral.js b/meshcentral.js index c3d4836e..b554e3d3 100644 --- a/meshcentral.js +++ b/meshcentral.js @@ -4032,6 +4032,7 @@ function mainStart() { 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.', 26); } } if (config.messaging.xmpp != null) { modules.push('@xmpp/client'); } if (config.messaging.pushover != null) { modules.push('node-pushover'); } + if (config.messaging.zulip != null) { modules.push('zulip'); } } // Setup web based push notifications diff --git a/meshmessaging.js b/meshmessaging.js index c52adf3a..b63f4bbb 100644 --- a/meshmessaging.js +++ b/meshmessaging.js @@ -78,13 +78,21 @@ } } +// For zulip +{ + "messaging": { + email: "your-bot@zulip.com", + api_key: "your_32_character_api_key" + } +} + */ // Construct a messaging server object module.exports.CreateServer = function (parent) { var obj = {}; obj.parent = parent; - obj.providers = 0; // 1 = Telegram, 2 = Signal, 4 = Discord, 8 = XMPP, 16 = CallMeBot, 32 = Pushover + obj.providers = 0; // 1 = Telegram, 2 = Signal, 4 = Discord, 8 = XMPP, 16 = CallMeBot, 32 = Pushover, 64 = ntfy, 128 = Zulip obj.telegramClient = null; obj.discordClient = null; obj.discordUrl = null; @@ -92,6 +100,7 @@ module.exports.CreateServer = function (parent) { var xmppXml = null; obj.callMeBotClient = null; obj.pushoverClient = null; + obj.zulipClient = null; // Telegram client setup if (parent.config.messaging.telegram) { @@ -228,6 +237,13 @@ module.exports.CreateServer = function (parent) { obj.providers += 64; // Enable ntfy messaging } + // Zulip client setup (https://zulip.com/) + if (typeof parent.config.messaging.zulip == 'object') { + var zulip = require('zulip'); + obj.zulipClient = new zulip.Client(parent.config.messaging.zulip); + obj.providers += 128; // Enable zulip messaging + } + // 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; }); @@ -296,6 +312,14 @@ module.exports.CreateServer = function (parent) { const req = require('https').request(new URL(url), { method: 'POST' }, function (res) { if (func != null) { func(true); } }); req.on('error', function (err) { if (func != null) { func(false); } }); req.end(msg); + } else if ((to.startsWith('zulip:')) && (obj.zulipClient != null)) { // zulip + obj.zulipClient.sendMessage({ + type: 'private', + content: msg, + to: [ to.substring(6) ], + subject: domain.title ? domain.title : 'MeshCentral' + }); + 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 c06121ec..f3c36a07 100644 --- a/meshuser.js +++ b/meshuser.js @@ -6706,6 +6706,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((command.service == 16) && ((parent.parent.msgserver.providers & 16) != 0)) { handle = parent.parent.msgserver.callmebotUrlToHandle(command.handle); } if ((command.service == 32) && ((parent.parent.msgserver.providers & 32) != 0)) { handle = 'pushover:' + command.handle; } if ((command.service == 64) && ((parent.parent.msgserver.providers & 64) != 0)) { handle = 'ntfy:' + command.handle; } + if ((command.service == 128) && ((parent.parent.msgserver.providers & 128) != 0)) { handle = 'zulip:' + command.handle; } if (handle == null) return; // Send a verification message @@ -6851,6 +6852,7 @@ module.exports.CreateMeshUser = function (parent, db, ws, req, args, domain, use if ((parent.parent.msgserver.providers & 8) != 0) { r.push("Usage: MSG \"xmpp:[username@server.com]\" \"Message\"."); } if ((parent.parent.msgserver.providers & 32) != 0) { r.push("Usage: MSG \"pushover:[userkey]\" \"Message\"."); } if ((parent.parent.msgserver.providers & 64) != 0) { r.push("Usage: MSG \"ntfy:[topic]\" \"Message\"."); } + if ((parent.parent.msgserver.providers & 128) != 0) { r.push("Usage: MSG \"zulip:[topic]\" \"Message\"."); } cmdData.result = r.join('\r\n'); } else { parent.parent.msgserver.sendMessage(cmdData.cmdargs['_'][0], cmdData.cmdargs['_'][1], domain, function (status, msg) { diff --git a/package.json b/package.json index 605d456f..8d0a7ffb 100644 --- a/package.json +++ b/package.json @@ -37,6 +37,7 @@ "sample-config-advanced.json" ], "dependencies": { + "@yetzt/nedb": "^1.8.0", "archiver": "^5.3.1", "body-parser": "^1.19.0", "cbor": "~5.2.0", @@ -48,10 +49,10 @@ "ipcheck": "^0.1.0", "minimist": "^1.2.5", "multiparty": "^4.2.1", - "@yetzt/nedb": "^1.8.0", "node-forge": "^1.0.0", "ws": "^5.2.3", - "yauzl": "^2.10.0" + "yauzl": "^2.10.0", + "zulip": "^0.1.0" }, "engines": { "node": ">=10.0.0" diff --git a/views/default.handlebars b/views/default.handlebars index 9130f56a..79dbcd16 100644 --- a/views/default.handlebars +++ b/views/default.handlebars @@ -11988,6 +11988,7 @@ if ((serverinfo.userMsgProviders & 16) != 0) { y += ''; } if ((serverinfo.userMsgProviders & 32) != 0) { y += ''; } if ((serverinfo.userMsgProviders & 64) != 0) { y += ''; } + if ((serverinfo.userMsgProviders & 128) != 0) { y += ''; } y += ''; x += '
' + "Service" + '' + y; x += '
' + "Handle" + ''; @@ -12013,6 +12014,7 @@ else if (Q('d2serviceselect').value == 16) { Q('d2handleinput')['placeholder'] = "https://api.callmebot.com/..."; } else if (Q('d2serviceselect').value == 32) { Q('d2handleinput')['placeholder'] = "User key"; } else if (Q('d2serviceselect').value == 64) { Q('d2handleinput')['placeholder'] = "Topic"; } + else if (Q('d2serviceselect').value == 128) { Q('d2handleinput')['placeholder'] = "username@sample.com"; } else { Q('d2handleinput')['placeholder'] = "Username"; } var ok = (Q('d2handleinput').value.length > 0); QE('idx_dlgOkButton', ok); if ((x == 1) && ok) { dialogclose(1); } } @@ -15878,6 +15880,7 @@ if ((serverinfo.userMsgProviders & 16) != 0) { y += ''; } if ((serverinfo.userMsgProviders & 32) != 0) { y += ''; } if ((serverinfo.userMsgProviders & 64) != 0) { y += ''; } + if ((serverinfo.userMsgProviders & 128) != 0) { y += ''; } y += ''; x += '
' + "Service" + '' + y; x += '
' + "Handle" + ''; @@ -15901,6 +15904,7 @@ } if (userinfo.msghandle.startsWith('pushover:') && ((serverinfo.userMsgProviders & 32) != 0)) { Q('d2serviceselect').value = 32; Q('d2handleinput').value = userinfo.msghandle.substring(9); } if (userinfo.msghandle.startsWith('ntfy:') && ((serverinfo.userMsgProviders & 64) != 0)) { Q('d2serviceselect').value = 64; Q('d2handleinput').value = userinfo.msghandle.substring(5); } + if (userinfo.msghandle.startsWith('zulip:') && ((serverinfo.userMsgProviders & 128) != 0)) { Q('d2serviceselect').value = 128; Q('d2handleinput').value = userinfo.msghandle.substring(6); } } p30editMessagingValidate(); } @@ -15917,6 +15921,7 @@ else if (Q('d2serviceselect').value == 16) { Q('d2handleinput')['placeholder'] = "https://api.callmebot.com/..."; } else if (Q('d2serviceselect').value == 32) { Q('d2handleinput')['placeholder'] = "User key"; } else if (Q('d2serviceselect').value == 64) { Q('d2handleinput')['placeholder'] = "Topic"; } + else if (Q('d2serviceselect').value == 128) { Q('d2handleinput')['placeholder'] = "username@sample.com"; } else { Q('d2handleinput')['placeholder'] = "Username"; } if (x == 1) { dialogclose(1); } } @@ -15931,6 +15936,7 @@ else if (Q('d2serviceselect').value == 16) { handle = 'callmebot:' + Q('d2handleinput').value; } else if (Q('d2serviceselect').value == 32) { handle = 'pushover:' + Q('d2handleinput').value; } else if (Q('d2serviceselect').value == 64) { handle = 'ntfy:' + Q('d2handleinput').value; } + else if (Q('d2serviceselect').value == 128) { handle = 'zulip:' + Q('d2handleinput').value; } if (handle != null) { meshserver.send({ action: 'edituser', id: currentUser._id, msghandle: handle }); } }