ChatRooms/index.js

367 lines
9.7 KiB
JavaScript
Raw Normal View History

// Global logging toggles
// const fileLogging = false;
2023-01-21 21:13:29 +00:00
const consoleLogging = true;
2023-01-17 18:15:50 +00:00
// Imports
// const fs = require('fs');
2023-01-16 13:13:04 +00:00
const http = require('http');
const express = require('express');
// Global server setup
2023-01-16 13:13:04 +00:00
const app = express();
2023-01-17 18:30:00 +00:00
const clientPath = __dirname+'/../html';
2023-01-16 13:40:33 +00:00
console.log('Serving static from ' + clientPath);
2023-01-16 14:21:30 +00:00
app.use(express.json());
2023-01-16 13:13:04 +00:00
app.use(express.static(clientPath));
const server = http.createServer(app);
// Check if string is already in array
function isStringUnique(arr, str) {
let match = true;
for (const s of arr)
if (s === str) {
match = false;
break;
}
return match;
}
// Generate the partial ID after username
function generatePartialID() {
return Math.floor(Math.random()*10000).toString().padStart(4, '0');
}
// Check if input is a user
function isUser(user, other) {
// Check by full name
if ('full' in other)
return `${user.name}#${user.id}` === other.full;
// Check by unique id
else if ('id' in other && 'name' in other)
return other.id === user.id && other.name === user.name;
// Not applicable
else return false;
}
2023-02-02 08:16:41 +00:00
// Check for user inactivity and delete accordingly
function userInactivity(name, id) {
for (let i = 0; i < users.length; i++)
if (isUser(users[i], {name, id}) && new Date() - users[i].seen > 86400000) {
2023-02-06 08:25:14 +00:00
// User with ID 'name#id' was inactive for longer than a day.
2023-02-02 08:16:41 +00:00
if (consoleLogging)
console.log(`Deleting user with ID '${name}#${id}' because of inactivity.`);
users.splice(i, 1);
break;
}
}
2023-02-03 12:57:30 +00:00
// User and chat data
const users = [], chats = [];
// Incoming user connection
app.post('/connect', (req, res) => {
2023-02-06 08:25:14 +00:00
// Retrieve username
const { name } = req.body;
2023-02-06 08:25:14 +00:00
// Generate id in unique pattern 'name#id'
let id = generatePartialID();
const name_id = users.map(u => `${u.name}#${u.id}`);
while (!isStringUnique(name_id, `${name}#${id}`))
id = generatePartialID();
2023-02-06 08:25:14 +00:00
// Save user data
2023-01-17 18:15:50 +00:00
users.push({
2023-02-02 08:16:41 +00:00
deletion: setInterval(
()=>userInactivity(name, id),
3600000),
seen: new Date(),
event: null,
2023-01-18 13:35:18 +00:00
chats: {},
name,
2023-01-17 18:15:50 +00:00
id
});
2023-02-06 08:25:14 +00:00
// Log connection
2023-01-18 14:24:44 +00:00
if (consoleLogging)
console.log(`User with ID '${name}#${id}' connected.`);
2023-02-06 08:25:14 +00:00
// Respond with id
2023-01-17 18:15:50 +00:00
res.status(200).json({ id });
});
// User has disconnected
2023-01-17 18:15:50 +00:00
app.post('/disconnect', (req, res) => {
2023-02-06 08:25:14 +00:00
// Retrieve user data
const { name, id } = req.body;
2023-02-06 08:25:14 +00:00
// Search for user in data
2023-01-17 18:15:50 +00:00
for (let i = 0; i < users.length; i++)
2023-02-06 08:25:14 +00:00
// If user was found, delete all of their data
if (isUser(users[i], {name, id})) {
2023-02-06 08:25:14 +00:00
// Log disconnect
2023-01-18 14:24:44 +00:00
if (consoleLogging)
console.log(`User with ID '${name}#${id}' disconnected.`);
2023-02-06 08:25:14 +00:00
// Delete user
2023-01-17 18:15:50 +00:00
users.splice(i, 1);
break;
}
2023-02-06 08:25:14 +00:00
// Respond with id as confirmation
2023-01-17 18:15:50 +00:00
res.status(200).send();
});
2023-01-30 07:17:33 +00:00
// User wants to measure ping
app.get('/ping', (req, res) => {
res.status(200).send();
});
// User wants to change name
2023-01-17 18:15:50 +00:00
app.post('/nickname', (req, res) => {
2023-02-06 08:25:14 +00:00
// Retrieve user data and old name
const { oldName, name, id } = req.body;
2023-01-18 07:02:42 +00:00
2023-02-06 08:25:14 +00:00
// Check if unique pattern 'name#id' is valid, generate new one
const name_id = users.map(u => `${u.name}#${u.id}`);
let currentID = id;
while (!isStringUnique(name_id, `${name}#${currentID}`))
currentID = generatePartialID();
2023-02-06 08:25:14 +00:00
// Find specified user
2023-01-17 18:15:50 +00:00
for (const u of users)
if (isUser(u, {name: oldName, id})) {
2023-02-06 08:25:14 +00:00
// Log renaming
if (consoleLogging)
2023-01-30 07:17:33 +00:00
console.log(`(Re-)named user with ID '${oldName}#${id}'.`);
2023-02-06 08:25:14 +00:00
// Save new name
u.name = name;
u.id = currentID;
2023-02-02 08:16:41 +00:00
u.seen = new Date();
2023-01-17 18:15:50 +00:00
break;
}
2023-01-18 07:02:42 +00:00
2023-02-06 08:25:14 +00:00
// Respond with confirmation
res.status(200).json({ id: currentID });
2023-01-18 07:02:42 +00:00
});
// User is pulling messages
app.get('/getChat', (req, res) => {
res.setHeader('Content-Type', 'text/event-stream');
res.setHeader('Cache-Control', 'no-cache');
res.setHeader('Connection', 'keep-alive');
2023-01-18 13:35:18 +00:00
2023-02-06 08:25:14 +00:00
// Save user activity
let full = req.query.full, found = false;
2023-02-02 08:16:41 +00:00
for (const u of users)
if (isUser(u, {full})) {
found = true;
u.event = res;
2023-02-02 08:16:41 +00:00
u.seen = new Date();
break;
}
if (!found) {
res.write('data: {"message":"Invalid user provided."}');
res.end();
2023-01-18 13:35:18 +00:00
return;
}
// Log if messages were delivered
2023-01-18 14:24:44 +00:00
if (consoleLogging)
console.log(`New message event listener opened by user with ID '${full}'.`);
2023-01-18 13:35:18 +00:00
// Handle close-event for when client is no longer listening
res.on('close', () => {
for (const u of users)
if (isUser(u, {full}))
u.event = null;
});
2023-01-17 18:15:50 +00:00
});
// User sent new message
2023-01-16 13:30:44 +00:00
app.post('/message', (req, res) => {
2023-02-06 08:25:14 +00:00
// Retrieve user data, their message and where to deliver it to
2023-02-02 09:45:07 +00:00
const { name, id, to, message } = req.body;
2023-02-06 08:25:14 +00:00
// Search for sender
2023-01-17 18:15:50 +00:00
for (const u of users)
if (isUser(u, {name, id})) {
2023-02-06 08:25:14 +00:00
// Go through every recipient
2023-02-02 09:45:07 +00:00
for (const recipient of to) {
2023-02-06 08:25:14 +00:00
// Save message for recipient
for (const o of users)
if (isUser(o, {full: recipient}) && o.event !== null)
o.event.write(`data: ${JSON.stringify({from: `${name}#${id}`, message})}\n\n`);
2023-02-02 09:45:07 +00:00
}
2023-02-06 08:25:14 +00:00
// Save user activity
2023-02-02 08:16:41 +00:00
u.seen = new Date();
2023-01-17 18:15:50 +00:00
break;
}
2023-01-18 07:02:42 +00:00
2023-02-06 08:25:14 +00:00
// Log users' message
if (consoleLogging)
2023-02-02 09:45:07 +00:00
console.log(`User with ID '${name}#${id}' sent message '${message}' to '${to}'.`);
2023-01-18 07:02:42 +00:00
// Confirm process
2023-01-18 07:02:42 +00:00
res.status(200).send();
2023-01-16 13:30:44 +00:00
});
2023-01-16 13:13:04 +00:00
// User requesting all names
app.get('/getNames', (req, res) => {
2023-02-06 08:25:14 +00:00
// Log request
if (consoleLogging)
2023-02-02 08:16:41 +00:00
console.log('Some user requested all usernames + IDs.');
2023-02-06 08:25:14 +00:00
// Respond with unique patterns 'name#id'
res.status(200).json(users.map(u => {
return {
name: u.name,
id: u.id
};
}));
});
2023-02-02 08:16:41 +00:00
// User sends activity notification
app.post('/activity', (req, res) => {
2023-02-06 08:25:14 +00:00
// Retrieve user data
2023-02-02 08:16:41 +00:00
const { name, id } = req.body;
2023-02-06 08:25:14 +00:00
// Save activity
2023-02-02 08:16:41 +00:00
for (const u of users)
if (isUser(u, {name, id})) {
2023-02-06 08:29:11 +00:00
// Log activity
if (consoleLogging)
console.log(`User with ID '${name}#${id}' is online.`);
2023-02-02 08:16:41 +00:00
u.seen = new Date();
break;
}
2023-02-06 08:25:14 +00:00
// Confirm process
2023-02-02 08:16:41 +00:00
res.status(200).send();
});
2023-02-06 08:25:14 +00:00
// User asks if their data exists
2023-02-02 08:16:41 +00:00
app.post('/login', (req, res) => {
2023-02-06 08:25:14 +00:00
// Retrieve user data
2023-02-02 08:16:41 +00:00
const { name, id } = req.body;
2023-02-06 08:25:14 +00:00
// Log login
2023-02-02 08:16:41 +00:00
if (consoleLogging)
console.log(`Some user requested existence of '${name}#${id}'.`);
2023-02-06 08:25:14 +00:00
// Go through users, success if user exists
2023-02-02 08:16:41 +00:00
let success = false;
for (const u of users)
if (isUser(u, {name, id})) {
2023-02-02 08:16:41 +00:00
success = true;
2023-02-06 08:25:14 +00:00
// Save activity
2023-02-02 08:16:41 +00:00
u.seen = new Date();
break;
}
2023-02-06 08:25:14 +00:00
// Respond with status
2023-02-02 08:16:41 +00:00
res.status(200).json({success});
2023-02-03 12:57:30 +00:00
});
2023-02-06 08:25:14 +00:00
// User wants to join chat
2023-02-03 12:57:30 +00:00
app.post('/chatInit', (req, res) => {
2023-02-06 08:25:14 +00:00
// Retrieve user data and chat name
2023-02-03 12:57:30 +00:00
const { name, id, chat } = req.body;
2023-02-06 08:25:14 +00:00
// Save user activity
for (const u of users)
if (isUser(u, {name, id})) {
2023-02-06 08:25:14 +00:00
u.seen = new Date();
2023-02-03 12:57:30 +00:00
break;
}
2023-02-06 08:25:14 +00:00
// Check if chats already exist
const fullName = `${name}#${id}`;
let existance = chat.map(e => [e, false]);
// Go through chats
for (const c of chats)
// Go through provided names
for (const n of existance) {
// If name is this chat
if (n[0] === c.name) {
// Chat exists, add user to it
n[1] = true;
if (!c.users.includes(fullName))
c.users.push(fullName);
2023-02-06 08:25:14 +00:00
break;
}
}
2023-02-03 12:57:30 +00:00
2023-02-06 08:25:14 +00:00
// Go through provided names
for (const n of existance) {
// Chat didn't exist
if (!n[1])
// Create it and save user in it
chats.push({
users: [fullName],
2023-02-06 08:25:14 +00:00
name: n[0]
});
}
// Log chat initialization
2023-02-03 12:57:30 +00:00
if (consoleLogging)
console.log(`User with ID '${name}#${id}' joined chat '${chat}'.`);
2023-02-06 08:25:14 +00:00
// Confirm process
2023-02-03 12:57:30 +00:00
res.status(200).send();
});
2023-02-06 08:25:14 +00:00
// User sends a message in chat
2023-02-03 12:57:30 +00:00
app.post('/sendGroup', (req, res) => {
2023-02-06 08:25:14 +00:00
// Retrieve user data and chat name and message
2023-02-03 12:57:30 +00:00
const { name, id, message, chat } = req.body;
2023-02-06 08:25:14 +00:00
// Save user activity
2023-02-03 12:57:30 +00:00
for (const u of users)
if (isUser(u, {name, id})) {
2023-02-03 12:57:30 +00:00
u.seen = new Date();
break;
}
2023-02-06 08:25:14 +00:00
// Find provided chat
const fullName = `${name}#${id}`;
for (const c of chats)
if (chat.includes(c.name)) {
// Go through users in chat
for (const u of c.users)
2023-02-06 08:25:14 +00:00
if (u !== fullName) {
// Send message to recipient
for (const o of users)
if (isUser(o, {full: u}) && o.event !== null)
o.event.write(`data: ${JSON.stringify({from: fullName, message})}\n\n`);
2023-02-06 08:25:14 +00:00
}
}
// Log users' message
2023-02-03 12:57:30 +00:00
if (consoleLogging)
console.log(`User with ID '${name}#${id}' sent message '${message}' in '${chat}'.`);
2023-02-06 08:25:14 +00:00
// Confirm process
2023-02-03 12:57:30 +00:00
res.status(200).send();
});
2023-02-02 08:16:41 +00:00
// Internal server error
2023-01-16 13:13:04 +00:00
server.on('error', err => {
2023-01-16 14:21:30 +00:00
console.error('Internal server error', err);
2023-01-16 13:13:04 +00:00
});
// Server has closed
2023-01-16 13:13:04 +00:00
server.on('close', () => {
console.log('Shutting down server ...');
2023-01-16 13:13:04 +00:00
});
// Listen on port 3000 for webserver connection
2023-01-16 13:13:04 +00:00
server.listen(3000, () => {
2023-01-16 13:40:33 +00:00
console.log('Server running on port 3000');
2023-01-16 13:13:04 +00:00
});