diff options
author | RaindropsSys <contact@minteck.org> | 2023-04-19 18:14:37 +0200 |
---|---|---|
committer | RaindropsSys <contact@minteck.org> | 2023-04-19 18:14:37 +0200 |
commit | 17694a26612d251edcf1094a0dff4f4e6892acf5 (patch) | |
tree | 3cb2f6be37bb8ca070c67ed78599bf6e79c8e02a | |
parent | d08872c5cae533d1002692fb2ea5a00808a9ff29 (diff) | |
download | chatroom-17694a26612d251edcf1094a0dff4f4e6892acf5.tar.gz chatroom-17694a26612d251edcf1094a0dff4f4e6892acf5.tar.bz2 chatroom-17694a26612d251edcf1094a0dff4f4e6892acf5.zip |
Updated 6 files and added 9 files (automated)
-rw-r--r-- | .fleet/run.json | 12 | ||||
-rw-r--r-- | client/commands.js | 35 | ||||
-rw-r--r-- | client/icons/signal-0.svg | 1 | ||||
-rw-r--r-- | client/icons/signal-100.svg | 1 | ||||
-rw-r--r-- | client/icons/signal-200.svg | 1 | ||||
-rw-r--r-- | client/icons/signal-300.svg | 1 | ||||
-rw-r--r-- | client/icons/signal-400.svg | 1 | ||||
-rw-r--r-- | client/icons/signal-none.svg | 1 | ||||
-rwxr-xr-x | client/index.html | 75 | ||||
-rwxr-xr-x | client/main.js | 4 | ||||
-rw-r--r-- | launcher/client/main.js | 15 | ||||
-rw-r--r-- | launcher/client/package.json | 2 | ||||
-rw-r--r-- | launcher/server/operators.json | 1 | ||||
-rw-r--r-- | launcher/server/ops.json | 1 | ||||
-rwxr-xr-x | server/server.js | 183 |
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> 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"> </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("<br>", "<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
}));
|