2018-09-15 17:27:45 +00:00
|
|
|
const express = require("express");
|
|
|
|
const low = require("lowdb");
|
|
|
|
const FileAsync = require('lowdb/adapters/FileAsync');
|
|
|
|
const websocket = require('ws');
|
|
|
|
const humanID = require("human-id");
|
2018-10-05 19:44:11 +00:00
|
|
|
const cors = require("cors");
|
2018-09-15 17:27:45 +00:00
|
|
|
|
|
|
|
const makeID = () => humanID({
|
|
|
|
separator: "-",
|
|
|
|
capitalize: false
|
|
|
|
});
|
|
|
|
|
|
|
|
const send = (sock, msg) => sock.send(JSON.stringify(msg));
|
|
|
|
const validateChannel = channel => { if (typeof channel !== "string" && typeof channel !== "number") throw new Error("Invalid type for channel!"); }
|
|
|
|
const wildcardChannel = "*";
|
|
|
|
|
2018-10-03 17:18:11 +00:00
|
|
|
let messageLogs = [];
|
|
|
|
|
2018-09-15 17:27:45 +00:00
|
|
|
const commands = {
|
|
|
|
open(ws, message) {
|
|
|
|
const channel = message.channel;
|
|
|
|
validateChannel(channel);
|
|
|
|
|
|
|
|
console.log("Opening", channel, "on", ws.ID);
|
|
|
|
// Avoid duplicate channels
|
|
|
|
if (!ws.channels.includes(channel)) ws.channels.push(channel);
|
|
|
|
|
|
|
|
return { channels: ws.channels };
|
|
|
|
},
|
|
|
|
close(ws, message) {
|
|
|
|
const channel = message.channel;
|
|
|
|
validateChannel(channel);
|
|
|
|
|
|
|
|
console.log("Closing", channel, "on", ws.ID);
|
|
|
|
|
|
|
|
// Remove channel from list if exists
|
|
|
|
const index = ws.channels.indexOf(channel);
|
|
|
|
if (index > -1) {
|
|
|
|
ws.channels.splice(index, 1);
|
2018-09-16 14:05:58 +00:00
|
|
|
return { closed: channel }
|
2018-09-15 17:27:45 +00:00
|
|
|
} else {
|
|
|
|
throw new Error("Channel " + channel + " not open.");
|
|
|
|
}
|
|
|
|
},
|
|
|
|
message(ws, message, db, wss) {
|
|
|
|
const toSend = {
|
|
|
|
...message,
|
|
|
|
ID: makeID(),
|
2018-10-03 17:34:03 +00:00
|
|
|
senderID: ws.ID,
|
|
|
|
time: new Date().getTime()
|
2018-09-15 17:27:45 +00:00
|
|
|
};
|
|
|
|
|
2018-09-16 15:19:21 +00:00
|
|
|
console.log("Sending", message.message, "on", message.channel, "from", ws.ID);
|
2018-09-15 21:08:32 +00:00
|
|
|
|
2018-09-15 17:27:45 +00:00
|
|
|
// Send message to all clients listening on this channel or the wildcard channel.
|
|
|
|
const sentTo = [];
|
|
|
|
wss.clients.forEach(client => {
|
2018-09-16 15:15:33 +00:00
|
|
|
if ((client.channels.includes(message.channel) || client.channels.includes(wildcardChannel)) && client.readyState === websocket.OPEN && client.ID !== ws.ID) {
|
2018-09-15 17:27:45 +00:00
|
|
|
client.send(JSON.stringify(toSend));
|
|
|
|
sentTo.push(client.ID);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2018-10-03 17:18:11 +00:00
|
|
|
toSend.sentTo = sentTo;
|
|
|
|
|
|
|
|
messageLogs.unshift(toSend);
|
|
|
|
|
2018-09-16 15:15:33 +00:00
|
|
|
return { sentTo, ID: toSend.ID };
|
2018-09-15 17:27:45 +00:00
|
|
|
},
|
2018-10-03 17:18:11 +00:00
|
|
|
ID(ws) {
|
2018-09-16 14:05:58 +00:00
|
|
|
return { ID: ws.ID };
|
2018-10-03 17:18:11 +00:00
|
|
|
},
|
|
|
|
log(ws, data) {
|
|
|
|
const start = data.start || 0;
|
|
|
|
const end = data.end || 100;
|
|
|
|
return { log: messageLogs.slice(start, end) };
|
2018-09-15 17:27:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
low(new FileAsync(process.env.DB || "./db.json")).then(db => {
|
|
|
|
db.defaults({ channels: [] }).write();
|
|
|
|
|
|
|
|
const app = express();
|
|
|
|
const expressWSS = require("express-ws")(app);
|
|
|
|
|
2018-10-05 19:44:11 +00:00
|
|
|
app.use(cors());
|
|
|
|
app.use(express.static(__dirname));
|
|
|
|
|
2018-09-15 17:27:45 +00:00
|
|
|
app.ws("/connect/", function(ws, req) {
|
|
|
|
ws.channels = [];
|
|
|
|
ws.ID = makeID();
|
|
|
|
ws.on("message", msg => {
|
|
|
|
let data;
|
|
|
|
try {
|
|
|
|
data = JSON.parse(msg);
|
|
|
|
} catch(e) {
|
|
|
|
console.log("Invalid data:", msg);
|
|
|
|
send(ws, {
|
|
|
|
type: "error",
|
|
|
|
error: "JSON expected."
|
|
|
|
})
|
|
|
|
}
|
|
|
|
|
|
|
|
// If sent data has required keys, execute corresponding command
|
|
|
|
if (typeof data === "object" && data !== null && data.type) {
|
|
|
|
const cmd = data.type;
|
|
|
|
try {
|
|
|
|
const result = commands[cmd](ws, data, db, expressWSS.getWss());
|
|
|
|
if (result !== null && result !== undefined) {
|
|
|
|
if ("then" in result) { // Allow returning promises
|
|
|
|
result.then(res => send(ws, {
|
|
|
|
type: "result",
|
|
|
|
for: cmd,
|
|
|
|
...res
|
|
|
|
}));
|
|
|
|
} else {
|
|
|
|
send(ws, {
|
|
|
|
type: "result",
|
|
|
|
for: cmd,
|
|
|
|
...result
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} catch(e) {
|
|
|
|
// Send & log error
|
|
|
|
console.log(e);
|
|
|
|
send(ws, {
|
|
|
|
type: "error",
|
|
|
|
for: cmd,
|
|
|
|
error: e.message
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
app.listen(parseInt(process.env.PORT) || 4567);
|
|
|
|
});
|