From 8c5ace0a20e7763b970da39ccbcf15ee7f55c5ab Mon Sep 17 00:00:00 2001 From: Miroslav Pejic Date: Sat, 26 Oct 2024 01:32:30 +0200 Subject: [PATCH] [mirotalksfu] - #170 add Mattermost integration, update dep --- README.md | 1 + app/src/Mattermost.js | 99 ++++++++++++++++++++++++++++++++++++++ app/src/Server.js | 17 ++++--- app/src/config.template.js | 34 +++++++++++++ package.json | 6 +-- public/js/Room.js | 4 +- public/js/RoomClient.js | 2 +- 7 files changed, 150 insertions(+), 13 deletions(-) create mode 100644 app/src/Mattermost.js diff --git a/README.md b/README.md index faada0a3..d325b767 100644 --- a/README.md +++ b/README.md @@ -80,6 +80,7 @@ - Supports [REST API](app/api/README.md) (Application Programming Interface). - Integration with [Slack](https://api.slack.com/apps/) for enhanced communication. - Integration with [Discord](https://discord.com) for enhanced communication. +- Integration with [Mattermost](https://mattermost.com/) for enhanced communication. - Utilizes [Sentry](https://sentry.io/) for error reporting. - And much more... diff --git a/app/src/Mattermost.js b/app/src/Mattermost.js new file mode 100644 index 00000000..e9afba56 --- /dev/null +++ b/app/src/Mattermost.js @@ -0,0 +1,99 @@ +'use strict'; + +const { Client4 } = require('@mattermost/client'); + +const { v4: uuidV4 } = require('uuid'); + +const config = require('./config'); + +const Logger = require('./Logger'); + +const log = new Logger('Mattermost'); + +class Mattermost { + constructor(app) { + const { + enabled, + token, + serverUrl, + username, + password, + commands = '/sfu', + texts = '/sfu', + } = config.mattermost || {}; + + if (!enabled) return; // Check if Mattermost integration is enabled + + this.app = app; + this.allowed = config.api.allowed && config.api.allowed.mattermost; + this.token = token; + this.serverUrl = serverUrl; + this.username = username; + this.password = password; + this.commands = commands; + this.texts = texts; + + this.client = new Client4(); + this.client.setUrl(this.serverUrl); + this.authenticate(); + this.setupEventHandlers(); + } + + async authenticate() { + try { + const user = await this.client.login(this.username, this.password); + log.debug('--------> Logged into Mattermost as', user.username); + } catch (error) { + log.error('Failed to log into Mattermost:', error); + } + } + + setupEventHandlers() { + this.app.post('/mattermost', (req, res) => { + // + if (!this.allowed) { + return res + .status(403) + .send('This endpoint has been disabled. Please contact the administrator for further information.'); + } + + log.debug('Mattermost request received:', { header: req.header, body: req.body }); + + const { token, text, command, channel_id } = req.body; + if (token !== this.token) { + return res.status(403).send('Invalid token'); + } + + const payload = { text: '', channel_id }; + if (this.processInput(command, payload, req) || this.processInput(text, payload, req)) { + return res.json(payload); + } + + return res.status(200).send('Command not recognized'); + }); + } + + processInput(input, payload, req) { + for (const cmd of [...this.commands, ...this.texts]) { + if (input.trim() === cmd.name) { + switch (cmd.name) { + case '/sfu': + payload.text = `${cmd.message} ${this.getMeetingURL(req)}`; + break; + default: + break; + } + return true; + } + } + return false; + } + + getMeetingURL(req) { + const host = req.headers.host; + const protocol = host.includes('localhost') ? 'http' : 'https'; + return `${protocol}://${host}/join/${uuidV4()}`; + } +} + +module.exports = Mattermost; diff --git a/app/src/Server.js b/app/src/Server.js index b0d9a4a1..84c81c9d 100644 --- a/app/src/Server.js +++ b/app/src/Server.js @@ -8,7 +8,7 @@ ███████ ███████ ██  ██   ████   ███████ ██  ██                                        prod dependencies: { - @ffmpeg-installer/ffmpeg: https://www.npmjs.com/package/@ffmpeg-installer/ffmpeg + @mattermost/client : https://www.npmjs.com/package/@mattermost/client @sentry/node : https://www.npmjs.com/package/@sentry/node axios : https://www.npmjs.com/package/axios body-parser : https://www.npmjs.com/package/body-parser @@ -56,7 +56,7 @@ dev dependencies: { * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.6.00 + * @version 1.6.10 * */ @@ -88,6 +88,7 @@ const swaggerUi = require('swagger-ui-express'); const swaggerDocument = yaml.load(fs.readFileSync(path.join(__dirname, '/../api/swagger.yaml'), 'utf8')); const Sentry = require('@sentry/node'); const Discord = require('./Discord.js'); +const Mattermost = require('./Mattermost.js'); const restrictAccessByIP = require('./middleware/IpWhitelist.js'); const packageJson = require('../../package.json'); @@ -110,7 +111,6 @@ const CryptoJS = require('crypto-js'); const qS = require('qs'); const slackEnabled = config.slack.enabled; const slackSigningSecret = config.slack.signingSecret; -const bodyParser = require('body-parser'); const app = express(); @@ -328,12 +328,12 @@ function OIDCAuth(req, res, next) { function startServer() { // Start the app + app.use(express.static(dir.public)); app.use(cors(corsOptions)); app.use(compression()); - app.use(express.json({ limit: '50mb' })); // Ensure the body parser can handle large files - app.use(express.static(dir.public)); - app.use(bodyParser.urlencoded({ extended: true })); - app.use(bodyParser.raw({ type: 'video/webm', limit: '50mb' })); // handle raw binary data + app.use(express.json({ limit: '50mb' })); // Handles JSON payloads + app.use(express.urlencoded({ extended: true, limit: '50mb' })); // Handles URL-encoded payloads + app.use(express.raw({ type: 'video/webm', limit: '50mb' })); // Handles raw binary data app.use(restApi.basePath + '/docs', swaggerUi.serve, swaggerUi.setup(swaggerDocument)); // api docs // IP Whitelist check ... @@ -352,6 +352,9 @@ function startServer() { }); */ + // Mattermost + const mattermost = new Mattermost(app); + // POST start from here... app.post('*', function (next) { next(); diff --git a/app/src/config.template.js b/app/src/config.template.js index 92980937..51aec0cc 100644 --- a/app/src/config.template.js +++ b/app/src/config.template.js @@ -121,6 +121,7 @@ module.exports = { join: true, token: false, slack: true, + mattermost: true, //... }, }, @@ -273,6 +274,39 @@ module.exports = { DSN: '', tracesSampleRate: 0.5, }, + mattermost: { + /* + Mattermost: https://mattermost.com + 1. Navigate to Main Menu > Integrations > Slash Commands in Mattermost. + 2. Click on Add Slash Command and configure the following settings: + - Title: Enter a descriptive title (e.g., `P2P Command`). + - Command Trigger Word: Set the trigger word to `p2p`. + - Callback URLs: Enter the URL for your Express server (e.g., `https://yourserver.com/mattermost`). + - Request Method: Select POST. + - Enable Autocomplete: Check the box for **Autocomplete**. + - Autocomplete Description: Provide a brief description (e.g., `Get MiroTalk P2P meeting room`). + 3. Save the slash command and copy the generated token here as MATTERMOST_TOKEN. + */ + enabled: false, + serverUrl: 'YourMattermostServerUrl', + username: 'YourMattermostUsername', + password: 'YourMattermostPassword', + token: 'YourMattermostToken', + commands: [ + { + name: '/sfu', + message: 'Here is your meeting room:', + }, + //.... + ], + texts: [ + { + name: '/sfu', + message: 'Here is your meeting room:', + }, + //.... + ], + }, slack: { /* Slack diff --git a/package.json b/package.json index 4f25bac0..38aa1dab 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "mirotalksfu", - "version": "1.6.00", + "version": "1.6.10", "description": "WebRTC SFU browser-based video calls", "main": "Server.js", "scripts": { @@ -57,9 +57,9 @@ "node": ">=18" }, "dependencies": { + "@mattermost/client": "^10.0.0", "@sentry/node": "^8.35.0", "axios": "^1.7.7", - "body-parser": "1.20.3", "colors": "1.4.0", "compression": "1.7.4", "cors": "2.8.5", @@ -80,7 +80,7 @@ "nodemailer": "^6.9.15", "openai": "^4.68.4", "qs": "6.13.0", - "socket.io": "4.8.0", + "socket.io": "4.8.1", "swagger-ui-express": "5.0.1", "uuid": "10.0.0" }, diff --git a/public/js/Room.js b/public/js/Room.js index efe7c088..459a1c11 100644 --- a/public/js/Room.js +++ b/public/js/Room.js @@ -11,7 +11,7 @@ if (location.href.substr(0, 5) !== 'https') location.href = 'https' + location.h * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.6.00 + * @version 1.6.10 * */ @@ -4500,7 +4500,7 @@ function showAbout() { imageUrl: image.about, customClass: { image: 'img-about' }, position: 'center', - title: 'WebRTC SFU v1.6.00', + title: 'WebRTC SFU v1.6.10', html: `
diff --git a/public/js/RoomClient.js b/public/js/RoomClient.js index ac66fda3..a5934d26 100644 --- a/public/js/RoomClient.js +++ b/public/js/RoomClient.js @@ -9,7 +9,7 @@ * @license For commercial or closed source, contact us at license.mirotalk@gmail.com or purchase directly via CodeCanyon * @license CodeCanyon: https://codecanyon.net/item/mirotalk-sfu-webrtc-realtime-video-conferences/40769970 * @author Miroslav Pejic - miroslav.pejic.85@gmail.com - * @version 1.6.00 + * @version 1.6.10 * */