summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaindropsSys <raindrops@equestria.dev>2023-09-11 12:44:56 +0200
committerRaindropsSys <raindrops@equestria.dev>2023-09-11 12:44:56 +0200
commit011480e177a57e1b329ec417ada3eb58334fcdd5 (patch)
treea89d341afa0cc9b5b9db336a6dab1a6d4ed6a4b5
parentdf5dcc71fe98510034e7882e8df716f340ef7ebd (diff)
downloadchatroom-011480e177a57e1b329ec417ada3eb58334fcdd5.tar.gz
chatroom-011480e177a57e1b329ec417ada3eb58334fcdd5.tar.bz2
chatroom-011480e177a57e1b329ec417ada3eb58334fcdd5.zip
Updated 7 files and added client/icons/settings.svg (automated)
-rw-r--r--client/commands.js42
-rw-r--r--client/icons/settings.svg1
-rwxr-xr-xclient/index.html415
-rwxr-xr-xclient/main.js8
-rw-r--r--launcher/client/main.js2
-rwxr-xr-xserver/server.js18
-rw-r--r--shared/lang/en.json43
-rw-r--r--shared/lang/fr.json43
8 files changed, 489 insertions, 83 deletions
diff --git a/client/commands.js b/client/commands.js
index 5cce914..89a7ee8 100644
--- a/client/commands.js
+++ b/client/commands.js
@@ -10,7 +10,7 @@ commands = {
});
}
},
- "lookup": (argument, _2) => {
+ "lookup": (argument, _) => {
if (argument.trim() === "") {
localSystemMessage(l("commands/lookup/invalid"));
} else {
@@ -21,6 +21,46 @@ commands = {
});
}
},
+ "edit": (argument, _) => {
+ send({
+ type: "edit",
+ author: userName,
+ colors,
+ date: new Date().getTime(),
+ originalDate: argument.split(" ")[0],
+ text: argument.split(" ").slice(1).join(" ")
+ });
+
+ processMessage({
+ type: "edit",
+ author: userName,
+ colors,
+ date: new Date().getTime(),
+ originalDate: argument.split(" ")[0],
+ text: argument.split(" ").slice(1).join(" ")
+ }, {
+ source: localStorage.getItem("userLogin")
+ });
+ },
+ "delete": (argument, _) => {
+ send({
+ type: "delete",
+ author: userName,
+ colors,
+ date: new Date().getTime(),
+ originalDate: argument.split(" ")[0]
+ });
+
+ processMessage({
+ type: "delete",
+ author: userName,
+ colors,
+ date: new Date().getTime(),
+ originalDate: argument.split(" ")[0]
+ }, {
+ source: localStorage.getItem("userLogin")
+ });
+ },
"nick": (argument, _) => {
if (argument.trim() === "") argument = require('os').userInfo().username;
diff --git a/client/icons/settings.svg b/client/icons/settings.svg
new file mode 100644
index 0000000..c870d33
--- /dev/null
+++ b/client/icons/settings.svg
@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" height="48" viewBox="0 -960 960 960" width="48"><path d="m388-80-20-126q-19-7-40-19t-37-25l-118 54-93-164 108-79q-2-9-2.5-20.5T185-480q0-9 .5-20.5T188-521L80-600l93-164 118 54q16-13 37-25t40-18l20-127h184l20 126q19 7 40.5 18.5T669-710l118-54 93 164-108 77q2 10 2.5 21.5t.5 21.5q0 10-.5 21t-2.5 21l108 78-93 164-118-54q-16 13-36.5 25.5T592-206L572-80H388Zm92-270q54 0 92-38t38-92q0-54-38-92t-92-38q-54 0-92 38t-38 92q0 54 38 92t92 38Zm0-60q-29 0-49.5-20.5T410-480q0-29 20.5-49.5T480-550q29 0 49.5 20.5T550-480q0 29-20.5 49.5T480-410Zm0-70Zm-44 340h88l14-112q33-8 62.5-25t53.5-41l106 46 40-72-94-69q4-17 6.5-33.5T715-480q0-17-2-33.5t-7-33.5l94-69-40-72-106 46q-23-26-52-43.5T538-708l-14-112h-88l-14 112q-34 7-63.5 24T306-642l-106-46-40 72 94 69q-4 17-6.5 33.5T245-480q0 17 2.5 33.5T254-413l-94 69 40 72 106-46q24 24 53.5 41t62.5 25l14 112Z" fill="#e0e3e2"/></svg> \ No newline at end of file
diff --git a/client/index.html b/client/index.html
index c699127..a338d05 100755
--- a/client/index.html
+++ b/client/index.html
@@ -3,14 +3,16 @@
<head>
<script>
//window.version = "2.6";
- window.version = "3.0-beta.2023-09-10";
+ window.version = "3.0-beta.2023-09-11";
window.betaVersion = true;
window.changeLog = `
-Thanks for using Localchat. You are currently running Localchat version %1, below are all the changes in that version compared to the previous version.
+Thanks for using Chatroom. You are currently running Chatroom version %1, below are all the changes in that version compared to the previous version.
-Localchat Client 3.0 Beta:
+Chatroom 3.0 Beta:
- Added help information for the commands
- Added a menu when right clicking a user
+ - Clicking on the upload menu now gives you more options
+ - You can now edit and delete messages
Here is what changed in the previous versions:
* Localchat Client 2.6:
@@ -86,7 +88,7 @@ Here is what changed in the previous versions:
window.TurndownService = require('turndown');
</script>
<meta charset="UTF-8">
- <title>Localchat</title>
+ <title>Chatroom</title>
<script src="../shared/crypt.js"></script>
<script src="../shared/verity.js"></script>
<script src="commands.js"></script>
@@ -101,7 +103,7 @@ Here is what changed in the previous versions:
width: 100%;
}
- #title-bar {
+ #title-bar *:not(#title-bar-status):not(.input-icon) {
pointer-events: none;
}
@@ -209,13 +211,22 @@ Here is what changed in the previous versions:
left: 0;
right: 0;
height: 34px;
- -webkit-app-region: drag;
display: grid;
grid-template-columns: 1fr max-content;
color: #e0e3e2;
font-size: 14px;
}
+ #drag-region {
+ -webkit-app-region: drag;
+ position: fixed;
+ top: 0;
+ left: 0;
+ right: 65px;
+ z-index: 99999;
+ height: 35px;
+ }
+
#title-bar.platform-mac {
padding-left: 80px;
}
@@ -596,6 +607,16 @@ Here is what changed in the previous versions:
background-color: #534341 !important;
color: #d8c2be !important;
}
+
+ .message-edit-text {
+ opacity: .5;
+ font-size: 12px;
+ }
+
+ .message-delete-text {
+ opacity: .5;
+ font-style: italic;
+ }
</style>
<script>
if (!localStorage.getItem("userLogin")) {
@@ -821,7 +842,7 @@ Here is what changed in the previous versions:
<div style="margin-top: 50px;">
<div class="oobe-textbox" style="background-color: #3f4949; color: #bec8c8; overflow-y: auto; height: 200px; border-radius: 10px; padding: 20px;">
- <span id="oobe-disclaimer-beta" style="display:none;">PLEASE READ THIS BEFORE CONTINUING<br><br>You are using a beta version of Localchat. Any update can render Localchat unusable and Equestria.dev should not be held responsible for any damage or issue while using a beta version of Localchat.<br>If you wish to switch back to a stable release, click on "Restart" then on "Switch to the stable channel". Continue at your own risk.<br><br>-------------------<br><br></span>Thanks for using Localchat, we hope you are making great use of this software and that you will enjoy your experience.<br><br>You may encounter bugs or other problems preventing you from using Localchat properly. You are encouraged to report these bugs to the developers so they can fix them as quickly as possible.<br><br>Localchat is using end-to-end encryption, meaning your internet service provider, your school or organization, or any VPN or proxy server you are connected to will not be able to see your messages. This, however, does not prevent a malicious software (e.g. "keylogger") or a physical device (e.g. a video capture device) from intercepting your communications.<br><br>Your use of Localchat should remain legal, and should respect the rules of your local server, if applicable. The Localchat developers don't limit the type of content that can be exchanged, but your local server administrator might.<br><br>Enjoy Localchat.
+ <span id="oobe-disclaimer-beta" style="display:none;">PLEASE READ THIS BEFORE CONTINUING<br><br>You are using a beta version of Chatroom. Any update can render Chatroom unusable and Equestria.dev should not be held responsible for any damage or issue while using a beta version of Chatroom.<br>If you wish to switch back to a stable release, click on "Restart" then on "Switch to the stable channel". Continue at your own risk.<br><br>-------------------<br><br></span>Thanks for using Chatroom, we hope you are making great use of this software and that you will enjoy your experience.<br><br>You may encounter bugs or other problems preventing you from using Chatroom properly. You are encouraged to report these bugs to the developers so they can fix them as quickly as possible.<br><br>Chatroom is using end-to-end encryption, meaning your internet service provider, your school or organization, or any VPN or proxy server you are connected to will not be able to see your messages. This, however, does not prevent a malicious software (e.g. "keylogger") or a physical device (e.g. a video capture device) from intercepting your communications.<br><br>Your use of Chatroom should remain legal, and should respect the rules of your local server, if applicable. The Chatroom developers don't limit the type of content that can be exchanged, but your local server administrator might.<br><br>Enjoy Chatroom.
</div>
</div>
</div>
@@ -904,13 +925,16 @@ Here is what changed in the previous versions:
</div>
<div id="app" style="width: 100%; height: 100%; z-index: -2; position: fixed;">
+ <div id="drag-region"></div>
+
<div id="title-bar">
<div id="title-bar-user">
- <b>Localchat</b>
+ <b>Chatroom</b>
</div>
<div id="title-bar-status">
- <span id="beta-badge" style="display: none;margin-right: 10px;background: #324b4c;color: #cce8e8;padding: 5px 10px;border-radius: 999px;" data-i18n-text="beta"></span>
- <img src="icons/signal-none.svg" id="title-bar-signal">
+ <span id="beta-badge" style="display: none;margin-right: 5px;background: #324b4c;color: #cce8e8;padding: 5px 10px;border-radius: 999px;" data-i18n-text="beta"></span>
+ <a data-i18n-title="menu/settings/title" class="input-icon" onclick="settingsMenu(); return false;" style="width: 24px; height: 32px; margin: 2px;margin-right: 10px;"><img src="icons/settings.svg"></a>
+ <img src="icons/signal-none.svg" id="title-bar-signal" style="display: none;">
</div>
</div>
<script>
@@ -1049,13 +1073,13 @@ Here is what changed in the previous versions:
}
for (let message of messages) {
- if (!displayedMessages.includes(message['_id'])) {
- document.getElementById("chat-log").insertAdjacentHTML('beforeend', `
+ if (!displayedMessages.includes(message['_id']) || message['_needsUpdate']) {
+ let html = `
${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 oncontextmenu="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" onclick="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" class="message-author" style="cursor: default; -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>
+ <span oncontextmenu="messageMenu('${message['date']}'); return false;" onclick="messageMenu('${message['date']}'); return false;" class="message-date" style="cursor: default;" title="${new Date(message['date']).toString()}">${new Date(message['date']).toTimeString().split(":").splice(0, 2).join(":")}</span>
+ <span oncontextmenu="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" onclick="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" class="message-author" style="cursor: default; -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 oncontextmenu="messageMenu('${message['date']}'); return false;" class="message-text">${(message['_deleted'] ? `<span class="message-delete-text">${l('edits/deleted')}</span>` : DOMPurify.sanitize(marked.parse(message['text'].replaceAll("\r\n", "\n").replaceAll("\n", "<br>"))).replaceAll("<p>", "").replaceAll("</p>", "").replaceAll("&lt;br&gt;", "<br>")) + ((message['_edited'] && !message['_deleted']) ? `<span class="message-edit-text">${l('edits/edited')}</span>` : "")}</span>
</div>
` : (message['type'] === "system" ? `
<div class="message system-message" id="message-${message['_id']}">
@@ -1074,19 +1098,26 @@ Here is what changed in the previous versions:
</div>
` : (message['type'] === "dm" ? `
<div class="message user-message direct-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 oncontextmenu="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" onclick="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" style="cursor: default;" class="message-author" title="${stripHTML(message['_source'])}">${stripHTML(message['author'])}</span>
- <span class="message-text">${DOMPurify.sanitize(marked.parse(message['text'].replaceAll("\r\n", "\n").replaceAll("\n", "<br>"))).replaceAll("<p>", "").replaceAll("</p>", "")}</span>
+ <span oncontextmenu="messageMenu('${message['date']}'); return false;" onclick="messageMenu('${message['date']}'); return false;" class="message-date" style="cursor: default;" title="${new Date(message['date']).toString()}">${new Date(message['date']).toTimeString().split(":").splice(0, 2).join(":")}</span>
+ <span oncontextmenu="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" onclick="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" style="cursor: default;" class="message-author" title="@${stripHTML(message['_source'])}">${stripHTML(message['author'])}</span>
+ <span oncontextmenu="messageMenu('${message['date']}'); return false;" class="message-text">${DOMPurify.sanitize(marked.parse(message['text'].replaceAll("\r\n", "\n").replaceAll("\n", "<br>"))).replaceAll("<p>", "").replaceAll("</p>", "")}</span>
</div>
` : (message['type'] === "file" ? `
<div class="message user-message message-file" 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 oncontextmenu="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" onclick="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" style="cursor: default;" class="message-author" 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>
- <div class="message-text" style="overflow: hidden;"><div style="display: grid; grid-template-columns: max-content 1fr${(!message['file']['type'].startsWith("video/") && !message['file']['type'].startsWith("audio/")) ? ` max-content` : ""}; grid-gap: 10px;"><div style="display: flex; align-items: center; justify-content: center;"><img src="icons/file-${getIconFromType(message['file']['type'])}.svg" style="width: 32px; height: 32px;"></div><div style="overflow: hidden;"><div style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;" title="${stripHTML(message['file']['name'].replaceAll('"', "''"))}">${stripHTML(message['file']['name'])}</div><span style="opacity: .75;">${formatSize(message['file']['size'])}</span></div><div>${(!message['file']['type'].startsWith("video/") && !message['file']['type'].startsWith("audio/")) ? `<a style="color: white; cursor: pointer;" href="data:${message['file']['type']};base64,${message['file']['content']}" download="${stripHTML(message['file']['name'])}" class="input-icon"><img src="icons/download.svg"></a>` : ``}</div></div>${message['file']['type'].startsWith("image/") ? `<br><img class="message-file-preview" src="data:${message['file']['type']};base64,${message['file']['content']}">` : (message['file']['type'].startsWith("audio/") ? `<br><audio controls class="message-file-preview" src="data:${message['file']['type']};base64,${message['file']['content']}"></audio>` : (message['file']['type'].startsWith("video/") ? `<br><video class="message-file-preview" src="data:${message['file']['type']};base64,${message['file']['content']}" controls></video>` : ``))}</div>
+ <span oncontextmenu="messageMenu('${message['date']}'); return false;" onclick="messageMenu('${message['date']}'); return false;" class="message-date" title="${new Date(message['date']).toString()}">${new Date(message['date']).toTimeString().split(":").splice(0, 2).join(":")}</span>
+ <span oncontextmenu="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" onclick="userMenu('${stripHTML(message['_source']).replaceAll("'", "&apos;").replaceAll('"', "&quot;")}'); return false;" style="cursor: default;" class="message-author" 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>
+ <div oncontextmenu="messageMenu('${message['date']}'); return false;" class="message-text" style="overflow: hidden;"><div style="display: grid; grid-template-columns: max-content 1fr${(!message['file']['type'].startsWith("video/") && !message['file']['type'].startsWith("audio/")) ? ` max-content` : ""}; grid-gap: 10px;"><div style="display: flex; align-items: center; justify-content: center;"><img src="icons/file-${getIconFromType(message['file']['type'])}.svg" style="width: 32px; height: 32px;"></div><div style="overflow: hidden;"><div style="text-overflow: ellipsis;white-space: nowrap;overflow: hidden;" title="${stripHTML(message['file']['name'].replaceAll('"', "''"))}">${stripHTML(message['file']['name'])}</div><span style="opacity: .75;">${formatSize(message['file']['size'])}</span></div><div>${(!message['file']['type'].startsWith("video/") && !message['file']['type'].startsWith("audio/")) ? `<a style="color: white; cursor: pointer;" href="data:${message['file']['type']};base64,${message['file']['content']}" download="${stripHTML(message['file']['name'])}" class="input-icon"><img src="icons/download.svg"></a>` : ``}</div></div>${message['file']['type'].startsWith("image/") ? `<br><img class="message-file-preview" src="data:${message['file']['type']};base64,${message['file']['content']}">` : (message['file']['type'].startsWith("audio/") ? `<br><audio controls class="message-file-preview" src="data:${message['file']['type']};base64,${message['file']['content']}"></audio>` : (message['file']['type'].startsWith("video/") ? `<br><video class="message-file-preview" src="data:${message['file']['type']};base64,${message['file']['content']}" controls></video>` : ``))}</div>
</div>
- ` : ``)))))}`);
+ ` : ``)))))}`;
+
+ if (!displayedMessages.includes(message['_id'])) {
+ document.getElementById("chat-log").insertAdjacentHTML('beforeend', html);
- displayedMessages.push(message['_id']);
+ displayedMessages.push(message['_id']);
+ } else {
+ document.getElementById("message-" + message['_id']).outerHTML = html;
+ message['_needsUpdate'] = false;
+ }
}
}
@@ -1312,52 +1343,7 @@ Here is what changed in the previous versions:
ws.close();
} else if (data.type === "encrypted") {
let decrypted = JSON.parse(decrypt(data.message, keys.privateKey));
-
- if (decrypted.type !== "typing") {
- decrypted._source = data.source;
-
- if (decrypted.type === "file") {
- let components = data[decrypted.file.token].split(':');
- let iv_from_ciphertext = Buffer.from(components.shift(), "base64");
- let decipher = crypto.createDecipheriv("aes-256-ctr", Buffer.from(decrypted.file.key, "base64"), iv_from_ciphertext);
- let deciphered = decipher.update(components.join(':'), "base64", "utf8");
- deciphered += decipher.final("utf8");
- decrypted.file.content = deciphered;
-
- let compare = crypto.createHash("md5").update(Buffer.from(deciphered, "base64")).digest("hex");
-
- if (compare !== decrypted.file.md5) {
- console.warn("Potentially corrupted file. Expected " + decrypted.file.md5 + " but got " + compare + ".");
- }
- }
-
- if (decrypted.type === "motd") {
- window.serverVersion = decrypted.version;
- let components = data["_motd"].split(':');
- let iv_from_ciphertext = Buffer.from(components.shift(), "base64");
- let decipher = crypto.createDecipheriv("aes-256-ctr", Buffer.from(decrypted.key, "base64"), iv_from_ciphertext);
- let deciphered = decipher.update(components.join(':'), "base64", "utf8");
- deciphered += decipher.final("utf8");
- decrypted.text = Buffer.from(deciphered, "base64").toString();
- }
-
- decrypted._id = uuid();
-
- messages.push(decrypted);
- }
-
- if (decrypted.type === "dm" && !window.directOverview) {
- window.directOverview = true;
- localSystemMessage(l("dm_notice"))
- }
-
- if (decrypted.type === "typing") {
- typing[decrypted.author] = new Date().getTime();
- refreshTyping();
- }
-
- console.log(decrypted);
- displayMessages();
+ processMessage(decrypted, data);
} else if (data.type === "pong") {
window.isAdmin = data.admin;
window.ping = new Date().getTime() - data.original.date;
@@ -1377,6 +1363,77 @@ Here is what changed in the previous versions:
}
}
+ function processMessage(decrypted, data) {
+ if (decrypted.type !== "typing") {
+ decrypted._source = data.source;
+
+ if (decrypted.type === "file") {
+ let components = data[decrypted.file.token].split(':');
+ let iv_from_ciphertext = Buffer.from(components.shift(), "base64");
+ let decipher = crypto.createDecipheriv("aes-256-ctr", Buffer.from(decrypted.file.key, "base64"), iv_from_ciphertext);
+ let deciphered = decipher.update(components.join(':'), "base64", "utf8");
+ deciphered += decipher.final("utf8");
+ decrypted.file.content = deciphered;
+
+ let compare = crypto.createHash("md5").update(Buffer.from(deciphered, "base64")).digest("hex");
+
+ if (compare !== decrypted.file.md5) {
+ console.warn("Potentially corrupted file. Expected " + decrypted.file.md5 + " but got " + compare + ".");
+ }
+ }
+
+ if (decrypted.type === "motd") {
+ window.serverVersion = decrypted.version;
+ let components = data["_motd"].split(':');
+ let iv_from_ciphertext = Buffer.from(components.shift(), "base64");
+ let decipher = crypto.createDecipheriv("aes-256-ctr", Buffer.from(decrypted.key, "base64"), iv_from_ciphertext);
+ let deciphered = decipher.update(components.join(':'), "base64", "utf8");
+ deciphered += decipher.final("utf8");
+ decrypted.text = Buffer.from(deciphered, "base64").toString();
+ }
+
+ decrypted._id = uuid();
+ decrypted._edited = false;
+ decrypted._deleted = false;
+ decrypted._needsUpdate = false;
+
+ if (decrypted.type === "edit") {
+ let original = messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0];
+
+ if (original) {
+ messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0].text = decrypted.text;
+ messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0]._edited = true;
+ messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0]._needsUpdate = true;
+ }
+ } else if (decrypted.type === "delete") {
+ let original = messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0];
+
+ if (original) {
+ messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0].text = "";
+ messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0].type = "text";
+ messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0].file = null;
+ messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0]._deleted = true;
+ messages.filter(i => i._source === decrypted._source && i.date === parseInt(decrypted.originalDate))[0]._needsUpdate = true;
+ }
+ } else {
+ messages.push(decrypted);
+ }
+ }
+
+ if (decrypted.type === "dm" && !window.directOverview) {
+ window.directOverview = true;
+ localSystemMessage(l("dm_notice"))
+ }
+
+ if (decrypted.type === "typing") {
+ typing[decrypted.author] = new Date().getTime();
+ refreshTyping();
+ }
+
+ console.log(decrypted);
+ displayMessages();
+ }
+
setInterval(() => {
for (let user of Object.keys(typing)) {
console.log(user, typing[user], new Date() - typing[user]);
@@ -1595,6 +1652,43 @@ Here is what changed in the previous versions:
]);
}
+ function messageMenu(ts) {
+ let source = messages.filter(i => i._source === localStorage.getItem("userLogin") && i.date === parseInt(ts))[0];
+
+ if (!source) return;
+
+ ipcRenderer.send("menu", [
+ {
+ label: l("menu/message/title"),
+ enabled: false
+ },
+ {
+ type: "separator"
+ },
+ {
+ label: l("menu/message/edit"),
+ enabled: !!source.text,
+ script: "(" + ((ts) => {
+ let source = messages.filter(i => i._source === localStorage.getItem("userLogin") && i.date === parseInt(ts))[0];
+ document.getElementById("input-text").value = "/edit " + ts + " " + source.text;
+ document.getElementById("input-text").focus();
+ }).toString() + ")(\"" + ts.replaceAll('"', "\\\"") + "\");"
+ },
+ {
+ label: l("menu/message/copy"),
+ script: "(" + ((ts) => {
+ commands['copy'](ts)
+ }).toString() + ")(\"" + ts.replaceAll('"', "\\\"") + "\");"
+ },
+ {
+ label: l("menu/message/delete"),
+ script: "(" + ((ts) => {
+ commands['delete'](ts)
+ }).toString() + ")(\"" + ts.replaceAll('"', "\\\"") + "\");"
+ }
+ ]);
+ }
+
function uploadMenu() {
ipcRenderer.send("menu", [
{
@@ -1625,6 +1719,164 @@ Here is what changed in the previous versions:
]);
}
+ function settingsMenu() {
+ ipcRenderer.send("menu", [
+ {
+ label: l("menu/settings/title"),
+ enabled: false
+ },
+ {
+ type: "separator"
+ },
+ {
+ label: l("menu/settings/server"),
+ enabled: false
+ },
+ {
+ label: " " + l("menu/settings/switch"),
+ script: "(" + (() => {
+ commands['switch']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/url"),
+ script: "(" + (() => {
+ commands['url']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/verify"),
+ script: "(" + (() => {
+ commands['verify']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/ping"),
+ script: "(" + (() => {
+ commands['ping']("");
+ }).toString() + ")();"
+ },
+ {
+ type: "separator"
+ },
+ {
+ label: l("menu/settings/client"),
+ enabled: false
+ },
+ {
+ label: " " + l("menu/settings/reload"),
+ script: "(" + (() => {
+ commands['reload']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/channel"),
+ script: "(" + (() => {
+ commands['channel']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/language"),
+ script: "(" + (() => {
+ // TODO
+ }).toString() + ")();"
+ },
+ {
+ type: "separator"
+ },
+ {
+ label: l("menu/settings/members"),
+ enabled: false
+ },
+ {
+ label: " " + l("menu/settings/loggedin"),
+ script: "(" + (() => {
+ commands['list']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/banned"),
+ script: "(" + (() => {
+ commands['banlist']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/ops"),
+ script: "(" + (() => {
+ commands['ops']("");
+ }).toString() + ")();"
+ },
+ {
+ type: "separator"
+ },
+ {
+ label: l("menu/settings/appearance"),
+ enabled: false
+ },
+ {
+ label: " " + l("menu/settings/username"),
+ script: "(" + (() => {
+ commands['whoami']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/nick"),
+ script: "(" + (() => {
+ // TODO
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/color/set"),
+ script: "(" + (() => {
+ // TODO
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/color/reset"),
+ script: "(" + (() => {
+ commands['color']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/background/set"),
+ script: "(" + (() => {
+ // TODO
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/background/reset"),
+ script: "(" + (() => {
+ commands['background']("");
+ }).toString() + ")();"
+ },
+ {
+ type: "separator"
+ },
+ {
+ label: l("menu/settings/help"),
+ enabled: false
+ },
+ {
+ label: " " + l("menu/settings/about"),
+ script: "(" + (() => {
+ commands['version']("");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/report"),
+ script: "(" + (() => {
+ require('electron').shell.openExternal("https://bugs.equestria.dev/issues/LCHT");
+ }).toString() + ")();"
+ },
+ {
+ label: " " + l("menu/settings/source"),
+ script: "(" + (() => {
+ require('electron').shell.openExternal("https://source.equestria.dev/equestria.dev/chatroom");
+ }).toString() + ")();"
+ }
+ ]);
+ }
+
document.getElementById("input-text").onkeyup = () => {
if (document.getElementById("input-text").value.length > 501) document.getElementById("input-text").value = document.getElementById("input-text").value.substring(0, 501);
}
@@ -1632,11 +1884,32 @@ Here is what changed in the previous versions:
document.getElementById("input-text").onkeydown = (e) => {
if (document.getElementById("input-text").value.length > 501) document.getElementById("input-text").value = document.getElementById("input-text").value.substring(0, 501);
- if (e.code === "Enter" && !e.shiftKey) {
+ if (e.code === "Escape") {
+ e.preventDefault();
+ document.getElementById("input-text").value = "";
+ } else if (e.code === "ArrowUp") {
+ e.preventDefault();
+
+ let source = messages.reverse().filter(i => i._source === localStorage.getItem("userLogin") && !i._delete && i.type === "text")[0];
+
+ if (source) {
+ document.getElementById("input-text").value = "/edit " + source.date + " " + source.text;
+ document.getElementById("input-text").focus();
+ }
+ } else if (e.code === "Enter" && !e.shiftKey) {
e.preventDefault();
if (document.getElementById("input-text").value.trim() === "" || !window.connected || window.banned) return;
- if (document.getElementById("input-text").value.trim().startsWith("/") && document.getElementById("input-text").value.trim().length > 1) {
+ if (document.getElementById("input-text").value.trim().startsWith("s/") && document.getElementById("input-text").value.trim().length > 3) {
+ let parts = document.getElementById("input-text").value.trim().split("/");
+
+ let source = messages.reverse().filter(i => i._source === localStorage.getItem("userLogin") && !i._delete && i.type === "text")[0];
+
+ if (source) {
+ commands.edit(source.date + " " + source.text.replace(new RegExp(parts[1] ?? "", parts[3] ?? ""), parts[2]));
+ document.getElementById("input-text").value = "";
+ }
+ } else if (document.getElementById("input-text").value.trim().startsWith("/") && document.getElementById("input-text").value.trim().length > 1) {
let text = document.getElementById("input-text").value.trim();
document.getElementById("input-text").value = "";
runCommand(text);
diff --git a/client/main.js b/client/main.js
index 32d3f5a..b6a02b9 100755
--- a/client/main.js
+++ b/client/main.js
@@ -6,7 +6,7 @@ const os = require("os");
const {writeFileSync, existsSync, unlinkSync} = require("fs");
const fs = require("fs");
-let localchatDataRoot = (os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : (os.platform() === "darwin" ? os.homedir() + "/Library/Application Support" : os.homedir())) + "/.localchat";
+let localchatDataRoot = (os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : (os.platform() === "darwin" ? os.homedir() + "/Library/Application Support" : os.homedir())) + (os.platform() === "darwin" ? "/Chatroom" : "/.chatroom");
if (!global._localchatLauncherVersion) {
dialog.showErrorBox("Update required", "Please update to Localchat Client Launcher version 1.4.0 or newer to continue using Localchat, you are currently running version " + app.getVersion() + ". You can get a copy of the updated launcher from your local administrator.");
@@ -26,9 +26,9 @@ app.setAppLogsPath(localchatDataRoot + "/client/logs");
if (require('os').platform() !== "darwin" && require('os').platform() !== "win32" && require('os').platform() !== "linux") return;
const createWindow = () => {
- app.setPath("userData", (os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : os.homedir()) + "/.localchat/client");
- app.setPath("sessionData", (os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : os.homedir()) + "/.localchat/client/_session");
- app.setAppLogsPath((os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : os.homedir()) + "/.localchat/client/_logs");
+ app.setPath("userData", localchatDataRoot + "/client");
+ app.setPath("sessionData", localchatDataRoot + "/client/session");
+ app.setAppLogsPath(localchatDataRoot + "/client/logs");
global.mainWindow = new BrowserWindow({
width: 500,
diff --git a/launcher/client/main.js b/launcher/client/main.js
index 61c6a34..7edee91 100644
--- a/launcher/client/main.js
+++ b/launcher/client/main.js
@@ -3,7 +3,7 @@ const path = require('path');
const fs = require("fs");
const os = require("os");
-let localchatDataRoot = (os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : (os.platform() === "darwin" ? os.homedir() + "/Library/Application Support" : os.homedir())) + "/.localchat";
+let localchatDataRoot = (os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : (os.platform() === "darwin" ? os.homedir() + "/Library/Application Support" : os.homedir())) + (os.platform() === "darwin" ? "/Chatroom" : "/.chatroom");
if (!fs.existsSync(localchatDataRoot)) fs.mkdirSync(localchatDataRoot);
if (!fs.existsSync(localchatDataRoot + "/client")) fs.mkdirSync(localchatDataRoot + "/client");
diff --git a/server/server.js b/server/server.js
index 51f0c3d..c100e13 100755
--- a/server/server.js
+++ b/server/server.js
@@ -8,7 +8,13 @@ const crypto = require('crypto');
const zlib = require("zlib");
const os = require("os");
-let localchatDataRoot = (os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : (os.platform() === "darwin" ? os.homedir() + "/Library/Application Support" : os.homedir())) + "/.localchat";
+let localchatDataRoot = (os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : (os.platform() === "darwin" ? os.homedir() + "/Library/Application Support" : os.homedir())) + (os.platform() === "darwin" ? "/Chatroom" : "/.chatroom");
+
+if (fs.existsSync((os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : (os.platform() === "darwin" ? os.homedir() + "/Library/Application Support" : os.homedir())) + "/.localchat")) {
+ try {
+ fs.renameSync((os.platform() === "win32" ? os.homedir() + "/AppData/Roaming" : (os.platform() === "darwin" ? os.homedir() + "/Library/Application Support" : os.homedir())) + "/.localchat", localchatDataRoot);
+ } catch (e) {}
+}
if (!fs.existsSync(localchatDataRoot)) fs.mkdirSync(localchatDataRoot);
if (!fs.existsSync(localchatDataRoot + "/server")) fs.mkdirSync(localchatDataRoot + "/server");
@@ -18,7 +24,7 @@ if (!fs.existsSync(localchatDataRoot + "/server/logs")) fs.mkdirSync(localchatDa
if (!fs.existsSync(localchatDataRoot + "/server/data")) fs.mkdirSync(localchatDataRoot + "/server/data");
process.chdir(localchatDataRoot + "/server");
-const version = "2.1";
+const version = "2.2";
const port = 27342;
function mergeRecursive(obj1, obj2) {
@@ -78,9 +84,9 @@ function log(message, address) {
}
}
-console.log("Localchat Server v" + version + "; (c) Equestria.dev Developers");
-console.log("powered by love and friendship");
-console.log(" - Want to contribute? https://source.equestria.dev/equestria.dev/localchat");
+console.log("Chatroom Server version " + version + "\n(c) Equestria.dev Developers\n");
+console.log("powered by love and friendship <3\n");
+console.log(" - Want to contribute? https://source.equestria.dev/equestria.dev/chatroom");
console.log(" - Found a bug? https://bugs.equestria.dev/issues/LCHT\n");
const readline = require('readline').createInterface({
@@ -99,7 +105,7 @@ readline.question("Enter the IP address users would use to connect to the server
if (!fs.existsSync("./config/motd.md")) {
log("Generating MOTD template...");
- fs.writeFileSync("./config/motd.md", "Welcome to the Localchat server. This file was created using Localchat Server v" + version + ", edit " + process.cwd() + "/config/motd.md to change this.");
+ fs.writeFileSync("./config/motd.md", "Welcome to the Chatroom server. This file was created using Chatroom Server version " + version + ", edit " + process.cwd() + "/config/motd.md to change this.");
log("Generated MOTD template");
}
diff --git a/shared/lang/en.json b/shared/lang/en.json
index 1856224..59fe654 100644
--- a/shared/lang/en.json
+++ b/shared/lang/en.json
@@ -187,6 +187,43 @@
"file": "Select a file",
"screenshot": "Upload a screenshot",
"clipboard": "Send clipboard content"
+ },
+ "message": {
+ "title": "Message options",
+ "edit": "Edit message",
+ "copy": "Copy text",
+ "delete": "Delete message"
+ },
+ "settings": {
+ "title": "Preferences",
+ "server": "Chatroom server",
+ "client": "Client application",
+ "members": "Server members",
+ "appearance": "Appearance and profile",
+ "help": "Help and support",
+ "switch": "Switch to another server",
+ "url": "Get server URL",
+ "verify": "Verify server identity",
+ "ping": "Display server ping",
+ "reload": "Disconnect and reload",
+ "channel": "Change update channel",
+ "language": "Change language",
+ "loggedin": "Show logged in users",
+ "banned": "Show blocked users",
+ "ops": "Show server operators",
+ "username": "Display user name",
+ "nick": "Change nick name",
+ "color": {
+ "set": "Change name colors",
+ "reset": "Reset name colors"
+ },
+ "background": {
+ "set": "Change background image",
+ "reset": "Remove background image"
+ },
+ "about": "About Chatroom",
+ "report": "Report an issue",
+ "source": "View source code"
}
},
"help": {
@@ -197,7 +234,9 @@
"changelog": "Get update information",
"channel": "Change update channel",
"color": "Change name colors",
+ "delete": "Delete a message",
"deop": "Remove operator",
+ "edit": "Edit a message",
"help": "Get commands help",
"lang": "Change application language",
"list": "List logged-in users",
@@ -215,5 +254,9 @@
"verify": "Verify the server or a user",
"version": "Get packages version",
"whoami": "Get your user name"
+ },
+ "edits": {
+ "edited": "(edited)",
+ "deleted": "This message was deleted"
}
} \ No newline at end of file
diff --git a/shared/lang/fr.json b/shared/lang/fr.json
index c013388..b0fbe7a 100644
--- a/shared/lang/fr.json
+++ b/shared/lang/fr.json
@@ -187,6 +187,43 @@
"file": "Choisir un fichier",
"screenshot": "Téléverser une capture d'écran",
"clipboard": "Envoyer le contenu du presse-papiers"
+ },
+ "message": {
+ "title": "Options du message",
+ "edit": "Modifier le message",
+ "copy": "Copier le texte",
+ "delete": "Supprimer le message"
+ },
+ "settings": {
+ "title": "Préférences",
+ "server": "Serveur Chatroom",
+ "client": "Application client",
+ "members": "Membres du serveur",
+ "appearance": "Apparence et profil",
+ "help": "Aide et support",
+ "switch": "Utiliser un autre serveur",
+ "url": "Obtenir l'adresse du serveur",
+ "verify": "Vérifier l'identité du serveur",
+ "ping": "Afficher la latence du serveur",
+ "reload": "Se déconnecter et recharger",
+ "channel": "Changer de canal de mise à jour",
+ "language": "Changer la langue",
+ "loggedin": "Afficher les utilisateurs connectés",
+ "banned": "Afficher les utilisateurs bloqués",
+ "ops": "Afficher les opérateurs de serveur",
+ "username": "Afficher le nom d'utilisateur",
+ "nick": "Changer de surnom",
+ "color": {
+ "set": "Changer les couleurs de nom",
+ "reset": "Réinitialiser les couleurs de nom"
+ },
+ "background": {
+ "set": "Changer l'image de fond",
+ "reset": "Retirer l'image de fond"
+ },
+ "about": "À propos de Chatroom",
+ "report": "Signaler un problème",
+ "source": "Voir le code source"
}
},
"help": {
@@ -197,7 +234,9 @@
"changelog": "Obtenir des infos sur les MàJ",
"channel": "Changer de canal de MàJ",
"color": "Changer les couleurs du surnom",
+ "delete": "Supprimer un message",
"deop": "Retirer un opérateur",
+ "edit": "Modifier un message",
"help": "Obtenir de l'aide sur les commandes",
"lang": "Changer la langue de l'application",
"list": "Lister les utilisateurs connectés",
@@ -215,5 +254,9 @@
"verify": "Vérifier un serveur ou utilisateur",
"version": "Obtenir les versions de paquets",
"whoami": "Obtenir votre nom d'utlisateur"
+ },
+ "edits": {
+ "edited": "(modifié)",
+ "deleted": "Ce message a été supprimé"
}
} \ No newline at end of file