summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaindropsSys <contact@minteck.org>2023-04-19 18:14:37 +0200
committerRaindropsSys <contact@minteck.org>2023-04-19 18:14:37 +0200
commit17694a26612d251edcf1094a0dff4f4e6892acf5 (patch)
tree3cb2f6be37bb8ca070c67ed78599bf6e79c8e02a
parentd08872c5cae533d1002692fb2ea5a00808a9ff29 (diff)
downloadchatroom-17694a26612d251edcf1094a0dff4f4e6892acf5.tar.gz
chatroom-17694a26612d251edcf1094a0dff4f4e6892acf5.tar.bz2
chatroom-17694a26612d251edcf1094a0dff4f4e6892acf5.zip
Updated 6 files and added 9 files (automated)
-rw-r--r--.fleet/run.json12
-rw-r--r--client/commands.js35
-rw-r--r--client/icons/signal-0.svg1
-rw-r--r--client/icons/signal-100.svg1
-rw-r--r--client/icons/signal-200.svg1
-rw-r--r--client/icons/signal-300.svg1
-rw-r--r--client/icons/signal-400.svg1
-rw-r--r--client/icons/signal-none.svg1
-rwxr-xr-xclient/index.html75
-rwxr-xr-xclient/main.js4
-rw-r--r--launcher/client/main.js15
-rw-r--r--launcher/client/package.json2
-rw-r--r--launcher/server/operators.json1
-rw-r--r--launcher/server/ops.json1
-rwxr-xr-xserver/server.js183
15 files changed, 299 insertions, 35 deletions
diff --git a/.fleet/run.json b/.fleet/run.json
new file mode 100644
index 0000000..605942a
--- /dev/null
+++ b/.fleet/run.json
@@ -0,0 +1,12 @@
+{
+ "configurations": [
+ {
+ "type": "command",
+ "name": "Client",
+ "program": "$PROJECT_DIR$/launcher/client/node_modules/electron/dist/Electron.app/Contents/MacOS/Electron",
+ "args": ["main.js", "$PROJECT_DIR$/client"],
+ "workingDir": "$PROJECT_DIR$/launcher/client",
+ },
+
+ ]
+} \ No newline at end of file
diff --git a/client/commands.js b/client/commands.js
index 46c3d34..0bd3c65 100644
--- a/client/commands.js
+++ b/client/commands.js
@@ -91,6 +91,36 @@ commands = {
user: argument
});
},
+ "ops": (argument, _) => {
+ send({
+ server: true,
+ type: "ops"
+ });
+ },
+ "op": (argument, _) => {
+ if (argument.trim() === "") {
+ localSystemMessage("Please specify the IP address of a client you want to make an operator");
+ return;
+ }
+
+ send({
+ server: true,
+ type: "op",
+ user: argument
+ });
+ },
+ "deop": (argument, _) => {
+ if (argument.trim() === "") {
+ localSystemMessage("Please specify the IP address of a client you want to not make an operator");
+ return;
+ }
+
+ send({
+ server: true,
+ type: "deop",
+ user: argument
+ });
+ },
"msg": (argument, _) => {
let parts = argument.split(" ");
@@ -146,11 +176,14 @@ commands = {
},
"help": (_1, _2) => {
localSystemMessage("Available commands: " + Object.keys(commands).filter(i => i !== "_").sort((a, b) => a.localeCompare(b)).join(", "));
+ },
+ "reload": (_1, _2) => {
+ location.reload();
}
}
function runCommand(message) {
- let words = message.substring(1).split(" ").filter(i => i.trim() !== "");
+ let words = message.replaceAll(" ", " ").trim().substring(1).split(" ").filter(i => i.trim() !== "");
let command = stripHTML(words[0]);
words.shift();
let argument = words.join(" ");
diff --git a/client/icons/signal-0.svg b/client/icons/signal-0.svg
new file mode 100644
index 0000000..e7502d6
--- /dev/null
+++ b/client/icons/signal-0.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48" fill="#e0e3e2"><path d="m81 976 800-800v800H81Z"/></svg> \ No newline at end of file
diff --git a/client/icons/signal-100.svg b/client/icons/signal-100.svg
new file mode 100644
index 0000000..97d7a72
--- /dev/null
+++ b/client/icons/signal-100.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48" fill="#e0e3e2"><path d="m81 976 800-800v800H81Zm525-60h215V320L606 535v381Z"/></svg> \ No newline at end of file
diff --git a/client/icons/signal-200.svg b/client/icons/signal-200.svg
new file mode 100644
index 0000000..aee5197
--- /dev/null
+++ b/client/icons/signal-200.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48" fill="#e0e3e2"><path d="m81 976 800-800v800H81Zm445-60h295V320L526 615v301Z"/></svg> \ No newline at end of file
diff --git a/client/icons/signal-300.svg b/client/icons/signal-300.svg
new file mode 100644
index 0000000..a546f7e
--- /dev/null
+++ b/client/icons/signal-300.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48" fill="#e0e3e2"><path d="m81 976 800-800v800H81Zm325-60h415V320L406 735v181Z"/></svg> \ No newline at end of file
diff --git a/client/icons/signal-400.svg b/client/icons/signal-400.svg
new file mode 100644
index 0000000..59e79da
--- /dev/null
+++ b/client/icons/signal-400.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48" fill="#e0e3e2"><path d="m79 976 800-800v800H79Zm144-60h596V320L223 916Z"/></svg> \ No newline at end of file
diff --git a/client/icons/signal-none.svg b/client/icons/signal-none.svg
new file mode 100644
index 0000000..99b103e
--- /dev/null
+++ b/client/icons/signal-none.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48" fill="#e0e3e2"><path d="m676 942-42-42 84-84-84-84 42-42 84 84 84-84 42 42-83 84 83 84-42 42-84-83-84 83ZM80 976l800-800v466q-14-9-28.5-16.5T820 613V320L224 916h350q9 17 21.5 32t26.5 28H80Zm144-60 596-596-183 183-140.5 140.5-128 128L224 916Z"/></svg> \ No newline at end of file
diff --git a/client/index.html b/client/index.html
index 70a1193..9d1211f 100755
--- a/client/index.html
+++ b/client/index.html
@@ -1,11 +1,11 @@
<!DOCTYPE html>
-<html>
+<html> <!-- 006a6c -->
<head>
<script>
- window.version = "2.0-dev.m2.2023-04-16";
+ window.version = "2.0-dev.m2.2023-04-17";
</script>
<meta charset="UTF-8">
- <title>LocalChat</title>
+ <title>Localchat</title>
<script src="../shared/crypt.js"></script>
<script src="commands.js"></script>
<script type="text/javascript" src="lib/purify.min.js"></script>
@@ -24,6 +24,13 @@
outline: none;
}
+ .message-badge {
+ vertical-align: middle;
+ height: 16px;
+ width: 16px;
+ margin-left: 3px;
+ }
+
#chat-log {
position: fixed;
top: 34px;
@@ -32,6 +39,7 @@
bottom: 48px;
padding: 20px 20px 28px;
overflow-y: auto;
+ z-index: -1;
}
#input {
@@ -70,7 +78,7 @@
height: 34px;
-webkit-app-region: drag;
display: grid;
- grid-template-columns: repeat(3, 1fr);
+ grid-template-columns: 1fr max-content;
color: #e0e3e2;
font-size: 14px;
}
@@ -83,11 +91,29 @@
padding-right: 138px;
}
+ #title-bar-user {
+ padding: 0 5px;
+ }
+
+ #title-bar-status {
+ padding: 0 5px;
+ }
+
+ #title-bar-signal {
+ width: 24px;
+ }
+
+ #title-bar.platform-mac #title-bar-status {
+ padding-right: 10px !important;
+ }
+
+ #title-bar.platform-windows #title-bar-user {
+ padding-left: 10px !important;
+ }
+
#title-bar > div {
display: flex;
align-items: center;
- text-align: center;
- justify-content: center;
}
.message {
@@ -552,11 +578,12 @@
</div>
<div id="title-bar">
- <div>
+ <div id="title-bar-user">
<b>Localchat</b>
</div>
- <div><span id="users">-</span>&nbsp;online</div>
- <div id="ping">Offline</div>
+ <div id="title-bar-status">
+ <img src="icons/signal-none.svg" id="title-bar-signal">
+ </div>
</div>
<script>
if (require('os').platform() === "darwin") {
@@ -567,7 +594,7 @@
</script>
<div id="chat-log"></div>
<div id="input">
- <a class="input-icon" onclick="if (window.connected) uploadFile();"><img src="icons/upload.svg"></a>
+ <a title="Upload a file" class="input-icon" onclick="if (window.connected) uploadFile();"><img src="icons/upload.svg"></a>
<div contenteditable="true" style="font-family: inherit; font-size: 14px;" id="input-text">&nbsp;</div>
<div style="font-family: inherit; font-size: 14px; opacity: .5;" id="input-text-placeholder">Send a message</div>
</div>
@@ -624,6 +651,7 @@
window.files = {};
window.ping = -1;
window.displayedMessages = [];
+ window.isAdmin = false;
setInterval(() => {
if (JSON.stringify(messages) !== lastMessages) {
@@ -645,9 +673,7 @@
let willScroll = false;
let el = document.getElementById("chat-log");
- if (el.scrollTop === el.scrollHeight) {
- willScroll = true;
- } else if (Math.abs(el.scrollTop - el.scrollHeight) < 10) {
+ if (el.scrollTop !== el.scrollHeight) {
willScroll = true;
} else if (!document.hasFocus()) {
willScroll = true;
@@ -659,7 +685,7 @@
${message['type'] === "text" ? `
<div class="message user-message" id="message-${message['_id']}">
<span class="message-date" title="${new Date(message['date']).toString()}">${new Date(message['date']).toTimeString().split(":").splice(0, 2).join(":")}</span>
- <span class="message-author" style="-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-image: linear-gradient(90deg, #${stripHTML(message['colors'][0])} 0%, #${stripHTML(message['colors'][1])} 100%)" title="${stripHTML(message['_source'])}">${stripHTML(message['author'])}</span>
+ <span class="message-author" style="-webkit-background-clip: text; -webkit-text-fill-color: transparent; background-image: linear-gradient(90deg, #${stripHTML(message['colors'][0])} 0%, #${stripHTML(message['colors'][1])} 100%)" title="${stripHTML(message['_source'])}">${stripHTML(message['author'])}${message['admin'] ? `<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 96 960 960" width="48" class="message-badge" fill="#${stripHTML(message['colors'][1])}"><path d="M480 975q-140-35-230-162.5T160 533V295l320-120 320 120v238q0 152-90 279.5T480 975Zm0-62q115-38 187.5-143.5T740 533V337l-260-98-260 98v196q0 131 72.5 236.5T480 913Zm0-337Z"/></svg>` : ``}</span>
<span class="message-text">${DOMPurify.sanitize(marked.parse(message['text'].replaceAll("\r\n", "\n").replaceAll("\n", "<br>"))).replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("&lt;br&gt;", "<br>")}</span>
</div>
` : (message['type'] === "system" ? `
@@ -810,13 +836,12 @@
}
if (!window.banned && !window.connected && window.reconnect && !showedServerErrorMessage) {
- localSystemMessage("Unable to connect with the Localchat server at " + window.serverURL + ", make sure the server is turned on");
+ localSystemMessage("Unable to connect to the Localchat server at " + window.serverURL + ", make sure the server is turned on");
}
window.connected = false;
console.log(e);
- document.getElementById("ping").innerText = "Offline";
- document.getElementById("users").innerText = "-";
+ document.getElementById("title-bar-signal").src = "icons/signal-none.svg";
clearInterval(wsPingInterval);
if (!showedServerErrorMessage) {
@@ -912,9 +937,20 @@
console.log(decrypted);
displayMessages();
} else if (data.type === "pong") {
- document.getElementById("users").innerText = data.users;
- document.getElementById("ping").innerText = (new Date().getTime() - data.original.date).toString() + " ms";
+ window.isAdmin = data.admin;
window.ping = new Date().getTime() - data.original.date;
+
+ if (window.ping < 100) {
+ document.getElementById("title-bar-signal").src = "icons/signal-0.svg";
+ } else if (window.ping < 200) {
+ document.getElementById("title-bar-signal").src = "icons/signal-100.svg";
+ } else if (window.ping < 300) {
+ document.getElementById("title-bar-signal").src = "icons/signal-200.svg";
+ } else if (window.ping < 400) {
+ document.getElementById("title-bar-signal").src = "icons/signal-300.svg";
+ } else {
+ document.getElementById("title-bar-signal").src = "icons/signal-400.svg";
+ }
}
}
}
@@ -1074,6 +1110,7 @@
send(obj);
obj['_source'] = "localhost";
obj['_id'] = uuid();
+ obj['admin'] = window.isAdmin;
messages.push(obj);
diff --git a/client/main.js b/client/main.js
index 72fdec5..47aadba 100755
--- a/client/main.js
+++ b/client/main.js
@@ -20,8 +20,8 @@ const createWindow = () => {
vibrancy: "menu",
frame: false,
titleBarOverlay: {
- color: "#151515",
- symbolColor: "#ffffff",
+ color: "#191c1c",
+ symbolColor: "#e0e3e2",
height: 34
},
trafficLightPosition: {
diff --git a/launcher/client/main.js b/launcher/client/main.js
index a52e870..feeb0c9 100644
--- a/launcher/client/main.js
+++ b/launcher/client/main.js
@@ -12,6 +12,7 @@ const createWindow = () => {
icon: require('os').platform() ? "./icon.icns" : "./icon.ico",
resizable: false,
maximizable: false,
+ show: false,
minimizable: false,
disableAutoHideCursor: true,
backgroundColor: "#111111",
@@ -35,11 +36,15 @@ const createWindow = () => {
require(path + "/main.js");
});
- if (process.argv[1]) {
- loaderWindow.send("local", process.argv[1]);
- } else {
- loaderWindow.send("download");
- }
+ loaderWindow.once("ready-to-show", () => {
+ loaderWindow.show();
+
+ if (process.argv[1]) {
+ loaderWindow.send("local", process.argv[1]);
+ } else {
+ loaderWindow.send("download");
+ }
+ });
}
app.whenReady().then(() => {
diff --git a/launcher/client/package.json b/launcher/client/package.json
index cdfcaef..6bb8a9e 100644
--- a/launcher/client/package.json
+++ b/launcher/client/package.json
@@ -1,5 +1,5 @@
{
- "version": "1.0.0",
+ "version": "1.2.0",
"dependencies": {
"@electron/remote": "^2.0.9",
"axios": "^1.3.5",
diff --git a/launcher/server/operators.json b/launcher/server/operators.json
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/launcher/server/operators.json
@@ -0,0 +1 @@
+[] \ No newline at end of file
diff --git a/launcher/server/ops.json b/launcher/server/ops.json
new file mode 100644
index 0000000..0637a08
--- /dev/null
+++ b/launcher/server/ops.json
@@ -0,0 +1 @@
+[] \ No newline at end of file
diff --git a/server/server.js b/server/server.js
index f9e81e0..529f42a 100755
--- a/server/server.js
+++ b/server/server.js
@@ -6,7 +6,7 @@ const fs = require('fs');
const crypto = require('crypto');
const zlib = require("zlib");
-const version = "1.6-dev";
+const version = "1.8-dev";
const port = 27342;
function log(message, address) {
@@ -67,6 +67,11 @@ readline.question("Enter the IP address users would use to connect to the server
let clients = [];
log("Loaded ban list");
+ log("Loading operators list...");
+ if (!fs.existsSync("./ops.json")) fs.writeFileSync("./ops.json", "[]");
+ let operators = JSON.parse(fs.readFileSync("./ops.json").toString());
+ log("Loaded operators list");
+
function parseAddress(address) {
if (address.startsWith("::ffff:")) {
return address.substring(7);
@@ -314,15 +319,27 @@ readline.question("Enter the IP address users would use to connect to the server
}));
log("Requested ban list", ws.address);
+ } else if (message.type === "ops") {
+ ws.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: operators.length > 0 ? "Server operators: " + operators.join(", ") : "There are no additional operators"
+ }), ws.publicKey)
+ }));
+
+ log("Requested operators list", ws.address);
} else if (message.type === "ban") {
- if (ws.address !== "::1" && !ws.address.startsWith("127.")) {
+ if (ws.address !== "::1" && !ws.address.startsWith("127.") && !operators.includes(ws.address)) {
ws.send(JSON.stringify({
type: "encrypted",
source: ws.address,
message: encrypt(JSON.stringify({
type: "system",
date: new Date().getTime(),
- text: "Only the server owner can use the /ban command"
+ text: "Only server operators can use the /ban command"
}), ws.publicKey)
}));
@@ -373,7 +390,7 @@ readline.question("Enter the IP address users would use to connect to the server
type: "system",
date: new Date().getTime(),
text: ws.address + " blocked " + message.user
- }), ws.publicKey)
+ }), socket.publicKey)
}));
} catch (e) {}
}
@@ -383,14 +400,14 @@ readline.question("Enter the IP address users would use to connect to the server
log("Banned user " + message.user, ws.address);
}
} else if (message.type === "unban") {
- if (ws.address !== "::1" && !ws.address.startsWith("127.")) {
+ if (ws.address !== "::1" && !ws.address.startsWith("127.") && !operators.includes(ws.address)) {
ws.send(JSON.stringify({
type: "encrypted",
source: ws.address,
message: encrypt(JSON.stringify({
type: "system",
date: new Date().getTime(),
- text: "Only the server owner can use the /unban command"
+ text: "Only server operators can use the /unban command"
}), ws.publicKey)
}));
@@ -430,7 +447,7 @@ readline.question("Enter the IP address users would use to connect to the server
type: "system",
date: new Date().getTime(),
text: ws.address + " unblocked " + message.user
- }), ws.publicKey)
+ }), socket.publicKey)
}));
} catch (e) {}
}
@@ -439,6 +456,152 @@ readline.question("Enter the IP address users would use to connect to the server
log("Unbanned user " + message.user, ws.address);
fs.writeFileSync("./bans.json", JSON.stringify(bans));
}
+ } else if (message.type === "op") {
+ if (ws.address !== "::1" && !ws.address.startsWith("127.")) {
+ ws.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: "Only the server owner can use the /op command"
+ }), ws.publicKey)
+ }));
+
+ log("Permission denied while running op", ws.address);
+ } else if (operators.includes(message.user)) {
+ ws.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: message.user + " is already operator on this server"
+ }), ws.publicKey)
+ }));
+
+ log("Failed to op " + message.user + ": user is already operator", ws.address);
+ } else {
+ operators.push(message.user);
+
+ for (let socket of clients) {
+ if (socket.address === message.user) {
+ try {
+ socket.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: "You are now a server operator"
+ }), socket.publicKey)
+ }));
+ } catch (e) {}
+ }
+ }
+
+ ws.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: "Successfully made " + message.user + " a server operator"
+ }), ws.publicKey)
+ }));
+
+ for (let socket of clients) {
+ if (socket.address !== ws.address) {
+ try {
+ socket.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: ws.address + " made " + message.user + " a server operator"
+ }), socket.publicKey)
+ }));
+ } catch (e) {}
+ }
+ }
+
+ fs.writeFileSync("./operators.json", JSON.stringify(operators));
+ log("Op-ed user " + message.user, ws.address);
+ }
+ } else if (message.type === "deop") {
+ if (ws.address !== "::1" && !ws.address.startsWith("127.")) {
+ ws.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: "Only the server owner can use the /deop command"
+ }), ws.publicKey)
+ }));
+
+ log("Permission denied while running deop", ws.address);
+ } else if (!operators.includes(message.user)) {
+ ws.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: message.user + " is not an operator on this server"
+ }), ws.publicKey)
+ }));
+
+ log("Failed to deop " + message.user + ": user is not operator", ws.address);
+ } else {
+ operators = operators.filter(i => i !== message.user);
+
+ ws.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: "Successfully made " + message.user + " not a server operator"
+ }), ws.publicKey)
+ }));
+
+ for (let socket of clients) {
+ if (socket.address === message.user) {
+ try {
+ socket.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: "You are not a server operator anymore"
+ }), socket.publicKey)
+ }));
+ } catch (e) {}
+ }
+ }
+
+ for (let socket of clients) {
+ if (socket.address !== ws.address) {
+ try {
+ socket.send(JSON.stringify({
+ type: "encrypted",
+ source: ws.address,
+ message: encrypt(JSON.stringify({
+ type: "system",
+ date: new Date().getTime(),
+ text: ws.address + " made " + message.user + " not a server operator"
+ }), socket.publicKey)
+ }));
+ } catch (e) {}
+ }
+ }
+
+ log("Deop-ed user " + message.user, ws.address);
+ fs.writeFileSync("./operators.json", JSON.stringify(operators));
+ }
}
} else {
log("Encrypted message, MD5: " + require('crypto').createHash("md5").update(data.message).digest("hex"), ws.address);
@@ -448,6 +611,11 @@ readline.question("Enter the IP address users would use to connect to the server
if (socket.id === ws.id) continue;
data.source = ws.address;
+
+ let _decrypted = JSON.parse(decrypted);
+ _decrypted.admin = !(ws.address !== "::1" && !ws.address.startsWith("127.") && !operators.includes(ws.address));
+ decrypted = JSON.stringify(_decrypted);
+
data.message = encrypt(decrypted, socket.publicKey);
socket.send(JSON.stringify(data));
@@ -457,6 +625,7 @@ readline.question("Enter the IP address users would use to connect to the server
} else if (data.type === "ping") {
ws.send(JSON.stringify({
type: "pong",
+ admin: !(ws.address !== "::1" && !ws.address.startsWith("127.") && !operators.includes(ws.address)),
users: clients.length,
original: data
}));