var Client = require('./lib/Client.js'); global.createMPPbridge = async function createMPPbridge({room, channel, uri}) { channel = dClient.channels.resolve(channel); var webhooks = await channel.fetchWebhooks(); var webhook = webhooks.filter(w => w.token).first() || await channel.createWebhook("sdfadffg"); { // discord message sending let msgBuffer = []; function _dSend(options) { if (options.content.length > 2000) { options.attachments ||= []; options.attachments.push(new Discord.MessageAttachment(Buffer.from(options.content), "message.txt")); delete options.content; } let username = gClient.channel && gClient.channel._id || room; if (username.length > 80) username = username.substr(0,79) + '…'; webhook.send(Object.assign(options, {username})).catch(error => { handleError(error, `webhook fail in ${channel.id}`); channel.send(options).catch(error => handleError(error, `send fail in ${channel.id} after webhook send fail`)); }); } function dSend(msg, options) { if (arguments.length == 1) options = msg; else { options ||= {}; options.content = msg; } msgBuffer.push(options); } setInterval(()=>{ if (msgBuffer.length == 0) return; _dSend(msgBuffer.join('\n')); msgBuffer = []; }, 3000); } const gClient = new Client(uri); if (uri == "wss://mppclone.com:8443") gClient.token = config.mpc_token; //todo hmm gClient.setChannel(room, {visible:false}); gClient.start(); // maintain the client's presence in the channel gClient.channelCorrectorInterval = setInterval(()=>{ // if client is connected and not in a channel (meaning setChannel failed due to ratelimit because another client joined a channel with the same user within the last second) OR client is in a channel but it is not the right channel… if ((gClient.isConnected() && !gClient.channel) || (gClient.channel && gClient.channel._id != room)) // …set the channel! gClient.setChannel(room, {visible:false}); }, 1000); let lastError; gClient.on("error", error => { handleError(error, `[${uri}][${room}]`); error = error.toString(); if (lastError != error) { dSend(`**${error.toString()}**`); lastError = error; } }); var isConnected = false; // TODO use gClient.isConnected() ? gClient.on('connect', () => { console.log(`[${uri}][${room}] Connected to server`); dSend(`**Connected to server; joining channel…**`); isConnected = true; lastError = undefined; }); gClient.on('hi', ()=>{ console.log(`[${uri}][${room}] Received greeting`); if (!testmode) { gClient.sendArray([{m: "userset", set: {name: config.mppname }}]); } gClient.sendArray([{m:'m',x:Math.floor(Math.random()*100),y:Math.floor(Math.random()*100)}]) }); gClient.on('disconnect', () => { if (isConnected) { console.log(`[${uri}][${room}] Disconnected from server`); dSend(`**Disconnected from server**`); isConnected = false; } }); /*gClient.on('status', status => { console.log(`[${uri}] [${room}] ${status}`); });*/ // on channel change { let lastCh; gClient.on('ch', async msg => { // announce channel join if (!lastCh) { dSend(`**Joined channel \`${msg.ch._id}\`**`); console.log(`[${uri}][${room}] Joined channel ${msg.ch._id}`); } // announce channel change else if (msg.ch._id !== lastCh) { dSend(`**Channel changed from \`${lastCh}\` to \`${msg.ch._id}\`**`); console.log(`[${uri}][${room}] Channel changed from ${lastCh} to ${msg.ch._id}`); } lastCh = msg.ch._id; }); gClient.on("disconnect", () => lastCh = undefined); } // MPP to Discord gClient.on('a', async msg => { if (msg.p._id == gClient.getOwnParticipant()._id) return; var id = msg.p._id.substr(0,6); var name = sanitizeName(msg.p.name); var content = escapeDiscordMentions(msg.a); var str = `\`${id}\` **${name}:** ${content}`; dSend(str); }); // Discord to MPP { let msgQueue = []; dClient.on('message', async message => { if (message.channel.id !== channel.id || message.author.id == dClient.user.id || !message.member /*|| message.content.startsWith('!')*/) return; var str = message.cleanContent; var aname = `${message.member.displayName}#${message.member.user.discriminator}`; if (str.startsWith('/') || str.startsWith('\\')) msgQueue.push(`⤹ ${aname}`); else str = `${aname}: ${str}`; if (str.startsWith('\\')) str = str.slice(1); if (message.attachments.first()) str += ' '+message.attachments.first().url; if (str.length > 512) str = str.substr(0,511) + '…'; msgQueue.push(str); }); setInterval(()=>{ let message = msgQueue.shift(); if (message) gClient.sendArray([{m:'a', message}]); }, 1600); // just about fastest without exceeding quota; I figured quota is 4 messages per 6 seconds in lobbies } // announce join/leave/rename gClient.prependListener("p", async participant => { if (gClient.ppl[participant.id]) { // is update let oldName = gClient.ppl[participant.id].name, newName = participant.name; if (newName != oldName) dSend(`\`${participant._id.substr(0,6)}\` ___**${sanitizeName(oldName)}** changed their name to **${sanitizeName(newName)}**___`); } else { // is join dSend(`\`${participant._id.substr(0,6)}\` ___**${sanitizeName(participant.name)}** entered the room.___`); } }); gClient.prependListener("bye", async msg => { var participant = gClient.ppl[msg.p]; dSend(`\`${participant._id.substr(0,6)}\` ___**${sanitizeName(participant.name)}** left the room.___`); }); // on notifications gClient.on('notification', async msg => { // show notification dSend({embeds:[{ title: msg.title, description: msg.text || msg.html }]}); // handle bans if (msg.text && (msg.text.startsWith('Banned from "'+room+'"') || msg.text.startsWith('Currently banned from "'+room+'"'))) { // Banned from "{room}" for {n} minutes. // Currently banned from "{room}" for {n} minutes. let arr = msg.text.split(' '); arr.pop(); let minutes = arr.pop(); gClient.stop(); setTimeout(()=>{ gClient.setChannel(room); gClient.start(); }, minutes*60*1000+3000); dSend(`**Attempting to rejoin in ${minutes} minutes.**`); } }); // make room invisible when nobody else is in it gClient.on("ch", async function(msg){ if (gClient.isOwner()) { if (gClient.countParticipants() <= 1) { gClient.sendArray([{m:'chset', set: { visible: false }}]) } else { gClient.sendArray([{m:'chset', set: { visible: true }}]) } } }); // addons { // record raw data let createWSMessageCollector = require("./datacollector") gClient.on("message", createWSMessageCollector(async function(data, startDate, endDate){ var attachmentName = `${uri} ${room} raw data recording from ${startDate.toISOString()} to ${endDate.toISOString()} .txt.gz`; await channel.send(new Discord.MessageAttachment(data, attachmentName)); })); } return gClient; }; // start (async function () { global.bridges = require("./bridges"); for (let bridge of bridges) { try { bridge.client = await createMPPbridge(bridge); } catch(e) { handleError(error, JSON.stringify(bridge)); } } })();