first commit

main
root 2024-11-10 07:13:49 +00:00
commit a16dda44db
33 changed files with 2048 additions and 0 deletions

View File

@ -0,0 +1,42 @@
import mysql.connector
def change_user_role():
# Database connection
conn = mysql.connector.connect(
host="localhost",
user="root",
password="",
database="messaging_app"
)
cursor = conn.cursor()
# Show all users
cursor.execute("SELECT username FROM users")
users = cursor.fetchall()
print("Current users:")
for user in users:
print(user[0])
print("Roles available: mod, admin")
# Get input from user
username = input("Enter the username: ")
role = input("Enter the role: ")
# Check if the user exists in the users table
cursor.execute("SELECT * FROM users WHERE username = %s", (username,))
user = cursor.fetchone()
if user:
# Insert into admins table
cursor.execute("INSERT INTO admins (username, role) VALUES (%s, %s)", (username, role))
conn.commit()
print("User role updated successfully.")
else:
print("User does not exist in the users table.")
# Close the connection
cursor.close()
conn.close()
if __name__ == "__main__":
change_user_role()

View File

@ -0,0 +1,16 @@
import mysql.connector
def connect_to_db():
conn = mysql.connector.connect(
host="localhost", user="root", password="", database="messaging_app"
)
return conn
conn = connect_to_db()
cursor = conn.cursor()
cursor.execute("DELETE FROM group_messages")
conn.commit()
print("Deleted Messages")

View File

@ -0,0 +1,16 @@
import mysql.connector
def connect_to_db():
conn = mysql.connector.connect(
host="localhost", user="root", password="", database="messaging_app"
)
return conn
conn = connect_to_db()
cursor = conn.cursor()
cursor.execute("DELETE FROM private_messages")
conn.commit()
print("Deleted Messages")

View File

@ -0,0 +1,34 @@
import mysql.connector
def connect_to_db():
conn = mysql.connector.connect(
host="localhost", user="root", password="", database="messaging_app"
)
return conn
def flush_database():
conn = connect_to_db()
cursor = conn.cursor()
tables = [
"group_messages",
"private_messages",
]
for table in tables:
try:
cursor.execute(f"TRUNCATE TABLE {table};")
print(f"Flushed table: {table}")
except mysql.connector.Error as err:
print(f"Error flushing table {table}: {err}")
conn.commit()
cursor.close()
conn.close()
print("All tables flushed successfully!")
if __name__ == "__main__":
flush_database()

View File

@ -0,0 +1,42 @@
import mysql.connector
def connect_to_db():
conn = mysql.connector.connect(
host="localhost", user="root", password="", database="messaging_app"
)
return conn
def flush_database():
conn = connect_to_db()
cursor = conn.cursor()
try:
cursor.execute("SET FOREIGN_KEY_CHECKS = 0;")
tables = [
"group_messages",
"private_messages",
"users",
"admins",
]
for table in tables:
cursor.execute(f"TRUNCATE TABLE {table};")
print(f"Flushed table: {table}")
except mysql.connector.Error as err:
print(f"Error flushing tables: {err}")
finally:
cursor.execute("SET FOREIGN_KEY_CHECKS = 1;")
conn.commit()
cursor.close()
conn.close()
print("All tables flushed successfully!")
if __name__ == "__main__":
flush_database()

View File

@ -0,0 +1,63 @@
import mysql.connector
from mysql.connector import errorcode
import json
import os
# Correct the path to the JSON file
json_path = os.path.join(os.path.dirname(__file__), '..', 'dbinfo.json')
try:
with open(json_path, "r") as file:
db_info = json.load(file)
except FileNotFoundError:
print(f"File not found: {json_path}")
exit(1)
except json.JSONDecodeError:
print(f"Error decoding JSON from file: {json_path}")
exit(1)
# Ensure the required keys are in the JSON
required_keys = ["host", "user", "password"]
for key in required_keys:
if key not in db_info:
print(f"Missing required key in JSON: {key}")
exit(1)
config = {
"host": db_info["host"],
"user": db_info["user"],
"password": db_info["password"],
"database": "messaging_app"
}
conn = None
try:
# Connect to the database
conn = mysql.connector.connect(**config)
cursor = conn.cursor()
# Create the admins table
cursor.execute('''
CREATE TABLE IF NOT EXISTS admins (
username VARCHAR(255) PRIMARY KEY,
role ENUM('mod', 'admin') NOT NULL
)
''')
# Commit the changes
conn.commit()
print("Admins table created successfully.")
except mysql.connector.Error as err:
if err.errno == errorcode.ER_ACCESS_DENIED_ERROR:
print("Something is wrong with your user name or password")
elif err.errno == errorcode.ER_BAD_DB_ERROR:
print("Database does not exist")
else:
print(err)
finally:
# Close the connection
if conn is not None and conn.is_connected():
cursor.close()
conn.close()

View File

@ -0,0 +1,127 @@
import mysql.connector
import random
import string
def connect_to_db():
conn = mysql.connector.connect(
host="localhost", user="root", password="", database="messaging_app"
)
return conn
def random_string(length=20):
letters = string.ascii_letters + string.digits
return "".join(random.choice(letters) for i in range(length))
def create_tables(cursor):
cursor.execute("""
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL,
password VARCHAR(255) NOT NULL
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS groups (
id INT AUTO_INCREMENT PRIMARY KEY,
group_name VARCHAR(255) NOT NULL,
created_by INT NOT NULL,
FOREIGN KEY (created_by) REFERENCES users(id)
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS group_messages (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
group_id INT NOT NULL,
message TEXT NOT NULL,
FOREIGN KEY (user_id) REFERENCES users(id),
FOREIGN KEY (group_id) REFERENCES groups(id)
)
""")
cursor.execute("""
CREATE TABLE IF NOT EXISTS private_messages (
id INT AUTO_INCREMENT PRIMARY KEY,
sender_id INT NOT NULL,
receiver_id INT NOT NULL,
message TEXT NOT NULL,
FOREIGN KEY (sender_id) REFERENCES users(id),
FOREIGN KEY (receiver_id) REFERENCES users(id)
)
""")
def create_sample_users(cursor, num_users=10):
for i in range(num_users):
username = f"user{i + 1}"
password = random_string(10)
cursor.execute(
"INSERT INTO users (username, password) VALUES (%s, %s)",
(username, password),
)
print(f"Created {num_users} sample users.")
def populate_database():
conn = connect_to_db()
cursor = conn.cursor()
create_tables(cursor)
create_sample_users(cursor)
cursor.execute("SELECT COUNT(*) FROM users")
user_count = cursor.fetchone()[0]
print(f"Total users in database: {user_count}")
group_ids = []
for i in range(1, 11):
group_name = f"Group {i}"
cursor.execute(
"INSERT INTO groups (group_name, created_by) VALUES (%s, %s)",
(group_name, 1),
)
group_ids.append(cursor.lastrowid)
print(f"Created groups with IDs: {group_ids}")
for _ in range(30):
user_id = random.randint(1, user_count)
group_id = random.choice(group_ids)
message = f"Message from {user_id} in group {group_id}: {random_string()}"
try:
cursor.execute(
"INSERT INTO group_messages (user_id, group_id, message) VALUES (%s, %s, %s)",
(user_id, group_id, message),
)
except mysql.connector.Error as err:
print(f"Error inserting group message: {err}")
for _ in range(30):
sender_id = random.randint(1, user_count)
receiver_id = random.randint(1, user_count)
while sender_id == receiver_id:
receiver_id = random.randint(1, user_count)
message = (
f"{random_string()}"
)
try:
cursor.execute(
"INSERT INTO private_messages (sender_id, receiver_id, message) VALUES (%s, %s, %s)",
(sender_id, receiver_id, message),
)
except mysql.connector.Error as err:
print(f"Error inserting private message: {err}")
conn.commit()
cursor.close()
conn.close()
print("Database populated with sample data!")
if __name__ == "__main__":
populate_database()

View File

@ -0,0 +1,99 @@
import json
import mysql.connector
import os
def get_db_info():
# Correct the path to the JSON file
json_path = os.path.join(os.path.dirname(__file__), '..', 'dbinfo.json')
try:
with open(json_path, "r") as file:
db_info = json.load(file)
return db_info
except FileNotFoundError:
print(f"File not found: {json_path}")
exit(1)
except json.JSONDecodeError:
print(f"Error decoding JSON from file: {json_path}")
exit(1)
def connect_to_db():
db_info = get_db_info()
conn = mysql.connector.connect(
host=db_info["host"], user=db_info["user"], password=db_info["password"], database="messaging_app"
)
return conn
def create_database():
db_info = get_db_info()
conn = mysql.connector.connect(
host=db_info["host"], user=db_info["user"], password=db_info["password"]
)
cursor = conn.cursor()
cursor.execute("CREATE DATABASE IF NOT EXISTS messaging_app")
conn.commit()
cursor.close()
conn.close()
def create_tables():
conn = connect_to_db()
cursor = conn.cursor()
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS users (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(255) NOT NULL UNIQUE,
password VARCHAR(255) NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
"""
)
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS group_messages (
id INT AUTO_INCREMENT PRIMARY KEY,
user_id INT NOT NULL,
message TEXT NOT NULL,
sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
file_path VARCHAR(255),
FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE
);
"""
)
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS private_messages (
id INT AUTO_INCREMENT PRIMARY KEY,
sender_id INT NOT NULL,
receiver_id INT NOT NULL,
message TEXT NOT NULL,
sent_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
file_path VARCHAR(255),
FOREIGN KEY (sender_id) REFERENCES users(id) ON DELETE CASCADE,
FOREIGN KEY (receiver_id) REFERENCES users(id) ON DELETE CASCADE
);
"""
)
cursor.execute(
"""
CREATE TABLE IF NOT EXISTS admins (
username VARCHAR(255) PRIMARY KEY,
role ENUM('mod', 'admin') NOT NULL
);
"""
)
conn.commit()
cursor.close()
conn.close()
if __name__ == "__main__":
try:
create_database()
create_tables()
print("Database and tables created successfully!")
except FileNotFoundError as e:
print(e)

View File

@ -0,0 +1,25 @@
import subprocess
import os
def run_script(script_path):
if os.path.exists(script_path):
try:
subprocess.run(['python3', script_path], check=True)
print(f"Successfully ran {script_path}")
except subprocess.CalledProcessError as e:
print(f"Error running {script_path}: {e}")
else:
print(f"Script {script_path} does not exist.")
def main():
base_dir = os.path.dirname(os.path.abspath(__file__))
scripts = [
os.path.join(base_dir, 'database-dependencies', 'updatedb.py'),
os.path.join(base_dir, 'database-dependencies', 'makeadmintable.py')
]
for script in scripts:
run_script(script)
if __name__ == "__main__":
main()

View File

@ -0,0 +1,5 @@
{
"host": "localhost",
"user": "root",
"password": ""
}

308
global.php 100644
View File

@ -0,0 +1,308 @@
<?php
session_start();
include './php/db.php';
if (!isset($_SESSION['user_id'])) {
header("Location: ./login.php");
exit;
}
$user_id = $_SESSION['user_id'];
$username = $_SESSION['username'];
$is_admin = false;
$is_mod = false;
$sql_check_roles = "SELECT role FROM admins WHERE username = ?";
$stmt_check_roles = $conn->prepare($sql_check_roles);
$stmt_check_roles->bind_param("s", $username);
$stmt_check_roles->execute();
$result_check_roles = $stmt_check_roles->get_result();
while ($row = $result_check_roles->fetch_assoc()) {
if ($row['role'] == 'admin') {
$is_admin = true;
} elseif ($row['role'] == 'mod') {
$is_mod = true;
}
}
$stmt_check_roles->close();
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (isset($_POST['group_message'])) {
$group_message = $_POST['group_message'];
$stmt = $conn->prepare("INSERT INTO group_messages (user_id, message) VALUES (?, ?)");
$stmt->bind_param("is", $user_id, $group_message);
if ($stmt->execute()) {
echo json_encode(['status' => 'success']);
} else {
echo json_encode(['status' => 'error', 'message' => $conn->error]);
}
$stmt->close();
exit;
}
}
$sql_group_messages = "SELECT gm.message, u.username, gm.sent_at
FROM group_messages gm
JOIN users u ON gm.user_id = u.id
ORDER BY gm.sent_at DESC";
$result_group_messages = $conn->query($sql_group_messages);
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Group Messages</title>
<script src="./js/globalFetch.js"></script>
<link rel="stylesheet" href="style.css">
</head>
<body>
<div class="container">
<h2>Welcome, <?php echo htmlspecialchars($username); ?>!</h2>
<h3>Public Chat</h3>
<ul id="messageList">
<?php
if (!$result_group_messages->num_rows > 0) {
echo "<li>No messages in the public group yet.</li>";
}
?>
</ul>
<form id="messageForm" action="home.php" method="post" enctype="multipart/form-data">
<textarea id="messageInput" name="group_message" rows="4" cols="50" placeholder="Type your message here..."></textarea>
<input type="file" id="fileInput" name="file" accept="image/*,audio/*,video/*">
<input type="button" id="sendButton" value="Send Message">
</form>
</div>
<div class="sidebar">
<p><a href="private.php">Private Messages</a></p>
<p><a href="./php/logout.php">Logout</a></p>
<!-- <button class="settings">ads</button> -->
</div>
<!-- <script src="./js/themeModal.js"></script> -->
<?php if ($is_admin): ?>
<!-- Modal for Admins -->
<div id="adminModal" class="modal">
<div class="modal-content">
<h2>Admin Panel</h2>
<p>Welcome, Admin <?php echo htmlspecialchars($username); ?>!</p>
<hr>
<h3>Admin Actions</h3>
<p>Remove a user</p>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['user_to_kick'])) {
$user_to_kick = $_POST['user_to_kick'];
$stmt_kick_user = $conn->prepare("DELETE FROM users WHERE id = ?");
$stmt_kick_user->bind_param("i", $user_to_kick);
$conn->query("SET FOREIGN_KEY_CHECKS=0");
if ($stmt_kick_user->execute()) {
$message = "User successfully removed.";
} else {
$message = "Error removing user: " . $conn->error;
}
$conn->query("SET FOREIGN_KEY_CHECKS=1");
$stmt_kick_user->close();
} else if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['user_roles']) && isset($_POST['roles'])) {
$user_roles = $_POST['user_roles'];
$roles = $_POST['roles'];
if ($roles == 'no-role') {
$stmt_remove_role = $conn->prepare("DELETE FROM admins WHERE username = (SELECT username FROM users WHERE id = ?)");
$stmt_remove_role->bind_param("i", $user_roles);
if ($stmt_remove_role->execute()) {
$message = "Role successfully removed.";
} else {
$message = "Error removing role: " . $conn->error;
}
$stmt_remove_role->close();
} else {
$stmt_update_role = $conn->prepare("REPLACE INTO admins (username, role) VALUES ((SELECT username FROM users WHERE id = ?), ?)");
$stmt_update_role->bind_param("is", $user_roles, $roles);
if ($stmt_update_role->execute()) {
$message = "Role successfully updated.";
} else {
$message = "Error updating role: " . $conn->error;
}
$stmt_update_role->close();
}
}
?>
<form id="kickForm" action="" method="post">
<select name="user_to_kick" id="user_to_kick">
<?php
$sql_users = "SELECT id, username FROM users";
$result_users = $conn->query($sql_users);
if ($result_users->num_rows > 0) {
while ($user = $result_users->fetch_assoc()) {
echo "<option value='" . htmlspecialchars($user['id']) . "'>" . htmlspecialchars($user['username']) . "</option>";
}
} else {
echo "<option value=''>No users available</option>";
}
?>
</select>
<input type="submit" id="kickButton" value="Kick User">
</form>
<br>
<hr>
<p>Change user roles</p>
<form id="addModForm" action="" method="post">
<select name="user_roles" id="user_roles">
<?php
$sql_users = "SELECT id, username FROM users";
$result_users = $conn->query($sql_users);
if ($result_users->num_rows > 0) {
while ($user = $result_users->fetch_assoc()) {
echo "<option value='" . htmlspecialchars($user['id']) . "'>" . htmlspecialchars($user['username']) . "</option>";
}
} else {
echo "<option value=''>No users available</option>";
}
?>
</select>
<select name="roles" id="roles">
<option value="admin">Admin</option>
<option value="mod">Mod</option>
<option value="no-role">No Role</option>
</select>
<input type="submit" id="updateUser" value="Update User">
</form>
<?php
if (isset($message)) {
echo "<p>$message</p>";
}
?>
</div>
</div>
<script>
var modal = document.getElementById("adminModal");
window.onload = function() {
modal.style.display = "block";
}
</script>
<script src="./js/pushChatScroll.js"></script>
<?php endif; ?>
<?php if ($is_mod && !$is_admin): ?>
<!-- Modal for Mods -->
<div id="modModal" class="modal">
<div class="modal-content">
<h2>Mod Panel</h2>
<p>Welcome, Mod <?php echo htmlspecialchars($username); ?>!</p>
<hr>
<h3>Mod Actions</h3>
<p>Kick a user</p>
<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_POST['user_to_kick'])) {
$user_to_kick = $_POST['user_to_kick'];
// Check if the user to kick is an admin
$stmt_check_admin = $conn->prepare("SELECT role FROM admins WHERE username = (SELECT username FROM users WHERE id = ?)");
$stmt_check_admin->bind_param("i", $user_to_kick);
$stmt_check_admin->execute();
$result_check_admin = $stmt_check_admin->get_result();
if ($result_check_admin->num_rows > 0) {
$row = $result_check_admin->fetch_assoc();
if ($row['role'] == 'admin') {
$message = "You cannot kick an admin.";
} else {
$stmt_kick_user = $conn->prepare("DELETE FROM users WHERE id = ?");
$stmt_kick_user->bind_param("i", $user_to_kick);
$conn->query("SET FOREIGN_KEY_CHECKS=0");
if ($stmt_kick_user->execute()) {
$message = "User successfully removed.";
} else {
$message = "Error removing user: " . $conn->error;
}
$conn->query("SET FOREIGN_KEY_CHECKS=1");
$stmt_kick_user->close();
}
} else {
// User is not an admin, proceed to kick
$stmt_kick_user = $conn->prepare("DELETE FROM users WHERE id = ?");
$stmt_kick_user->bind_param("i", $user_to_kick);
$conn->query("SET FOREIGN_KEY_CHECKS=0");
if ($stmt_kick_user->execute()) {
$message = "User successfully removed.";
} else {
$message = "Error removing user: " . $conn->error;
}
$conn->query("SET FOREIGN_KEY_CHECKS=1");
$stmt_kick_user->close();
}
$stmt_check_admin->close();
}
?>
<form id="kickForm" action="" method="post">
<select name="user_to_kick" id="user_to_kick">
<?php
$sql_users = "SELECT id, username FROM users";
$result_users = $conn->query($sql_users);
if ($result_users->num_rows > 0) {
while ($user = $result_users->fetch_assoc()) {
echo "<option value='" . htmlspecialchars($user['id']) . "'>" . htmlspecialchars($user['username']) . "</option>";
}
} else {
echo "<option value=''>No users available</option>";
}
?>
</select>
<input type="submit" id="kickButton" value="Kick User">
</form>
<?php
if (isset($message)) {
echo "<p>$message</p>";
}
?>
</div>
</div>
<script>
var modal = document.getElementById("modModal");
window.onload = function() {
modal.style.display = "block";
}
</script>
<?php endif; ?>
<script src="./js/sendGlobal.js"></script>
</body>
</html>

3
index.php 100644
View File

@ -0,0 +1,3 @@
<?php
header("Location: login.php");
exit();

24
js/globalFetch.js 100644
View File

@ -0,0 +1,24 @@
let lastTimestamp = '1970-01-01 00:00:00';
function fetchMessages() {
var xhr = new XMLHttpRequest();
xhr.open("GET", `./php/fetchMessages.php?last_timestamp=${encodeURIComponent(lastTimestamp)}`, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
const messages = response.messages;
lastTimestamp = response.latest_timestamp;
const messageList = document.getElementById("messageList");
messages.forEach(msg => {
const newMessage = document.createElement("li");
newMessage.innerHTML = msg;
messageList.insertBefore(newMessage, messageList.firstChild);
});
}
};
xhr.send();
}
setInterval(fetchMessages, 1000);

30
js/login.js 100644
View File

@ -0,0 +1,30 @@
document.addEventListener('DOMContentLoaded', function() {
const submitButton = document.querySelector('#submit-login');
submitButton.addEventListener('click', (event) => {
event.preventDefault(); // Prevent form submission
let username = document.querySelector('#username').value;
let password = document.querySelector('#password').value;
if (!username.includes(' ')) {
fetch('./php/login.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `username=${encodeURIComponent(username)}&password=${encodeURIComponent(password)}`
})
.then(response => response.text())
.then(data => {
if (data.includes('Invalid credentials!') || data.includes('User not found!')) {
alert(data);
} else {
window.location.href = './global.php';
}
})
.catch(error => console.error('Error:', error));
};
});
console.log('DOM fully loaded and parsed');
});

83
js/private.js 100644
View File

@ -0,0 +1,83 @@
document.addEventListener('DOMContentLoaded', function() {
const messageForm = document.getElementById('privateMessageForm');
const messageInput = messageForm.querySelector('textarea[name="private_message"]');
const receiverIdInput = messageForm.querySelector('input[name="receiver_id"]');
const fileInput = messageForm.querySelector('input[name="file"]');
const userButtons = document.querySelectorAll('button[name="select_user"]');
let lastTimestamp = '1970-01-01 00:00:00';
userButtons.forEach(button => {
button.addEventListener('click', function(event) {
event.preventDefault();
const selectedUserId = this.value;
receiverIdInput.value = selectedUserId;
lastTimestamp = '1970-01-01 00:00:00'; // Reset timestamp when a new user is selected
fetchPrivateMessages(selectedUserId);
startMessagePolling(selectedUserId);
});
});
messageForm.addEventListener('submit', function(event) {
event.preventDefault();
const formData = new FormData(this);
console.log('Sending data:', Object.fromEntries(formData));
fetch('./php/send_private_message.php', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
console.log('Response:', data);
if (data.status === 'success') {
fetchPrivateMessages(receiverIdInput.value);
messageInput.value = '';
fileInput.value = '';
} else {
alert(data.message);
}
})
.catch(error => {
console.error('Error:', error);
});
});
messageInput.addEventListener('keypress', function(event) {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
messageForm.dispatchEvent(new Event('submit'));
}
});
function fetchPrivateMessages(selectedUserId) {
var xhr = new XMLHttpRequest();
xhr.open("GET", `./php/fetch_private_messages.php?user_id=${selectedUserId}&last_timestamp=${encodeURIComponent(lastTimestamp)}`, true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200) {
const response = JSON.parse(xhr.responseText);
const messages = response.messages;
lastTimestamp = response.latest_timestamp;
const messageList = document.getElementById("messageList");
messages.forEach(msg => {
const newMessage = document.createElement("li");
newMessage.innerHTML = msg;
messageList.appendChild(newMessage);
});
messageList.scrollTop = messageList.scrollHeight;
}
};
xhr.send();
}
function startMessagePolling(selectedUserId) {
if (window.messagePollingInterval) {
clearInterval(window.messagePollingInterval);
}
window.messagePollingInterval = setInterval(() => {
fetchPrivateMessages(selectedUserId);
}, 1000); // Poll every 1 second
}
});

View File

@ -0,0 +1,13 @@
document.addEventListener('DOMContentLoaded', function() {
const chat = document.querySelector('#messageList');
if (chat) {
const observer = new MutationObserver(function() {
chat.scrollTop = chat.scrollHeight;
});
observer.observe(chat, { childList: true });
// Initial scroll to bottom in case the list is already populated
chat.scrollTop = chat.scrollHeight;
}
});

34
js/register.js 100644
View File

@ -0,0 +1,34 @@
document.addEventListener('DOMContentLoaded', function() {
const submitButton = document.querySelector('#submit-register');
submitButton.addEventListener('click', (event) => {
event.preventDefault(); // Prevent form submission
let username = document.querySelector('#username').value;
let email = document.querySelector('#email').value; // Capture email
let password = document.querySelector('#password').value;
let confirm_password = document.querySelector('#confirm-password').value;
if (password === confirm_password) {
fetch('./php/register.php', {
method: 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: `username=${encodeURIComponent(username)}&email=${encodeURIComponent(email)}&password=${encodeURIComponent(password)}`
})
.then(response => response.text())
.then(data => {
if (data.includes('Invalid credentials!') || data.includes('User not found!')) {
alert(data);
} else {
window.location.href = './global.php';
}
})
.catch(error => console.error('Error:', error));
} else {
alert('Incorrect Credentials', 'Please enter the right details.');
}
});
console.log('DOM fully loaded and parsed');
});

59
js/sendGlobal.js 100644
View File

@ -0,0 +1,59 @@
document.getElementById("sendButton").addEventListener("click", sendMessage);
document
.getElementById("messageInput")
.addEventListener("keydown", function (event) {
if (event.key === "Enter") {
sendMessage();
}
});
function sendMessage() {
const chat = document.querySelector('#messageList');
if (chat) {
const observer = new MutationObserver(function() {
chat.scrollTop = chat.scrollHeight;
});
observer.observe(chat, { childList: true });
// Initial scroll to bottom in case the list is already populated
chat.scrollTop = chat.scrollHeight;
}
var message = document.getElementById("messageInput").value.trim();
var fileInput = document.getElementById("fileInput").files[0];
// Basic validation
if (!message && !fileInput) {
alert("Please enter a message or select a file.");
return;
}
var formData = new FormData();
formData.append("group_message", message);
if (fileInput) {
formData.append("file", fileInput);
}
var xhr = new XMLHttpRequest();
xhr.open("POST", "./php/sendGlobal.php", true);
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
var response = JSON.parse(xhr.responseText);
if (response.status === "success") {
document.getElementById("messageInput").value = "";
document.getElementById("fileInput").value = "";
} else {
alert("Error: " + response.message);
}
} else {
alert("Error: Unable to send message.");
}
}
};
xhr.send(formData);
}

43
js/themeModal.js 100644
View File

@ -0,0 +1,43 @@
const button = document.querySelector('.settings');
button.addEventListener('click', () => {
const modal = document.createElement('div');
modal.classList.add('settingsModal');
modal.innerHTML = `
<div class="theme-options">
<label for="background">Background Color:</label>
<input type="color" id="background" name="background">
<label for="sidebar">Sidebar Color:</label>
<input type="color" id="sidebar" name="sidebar">
<label for="buttons">Buttons Color:</label>
<input type="color" id="buttons" name="buttons">
<label for="mention">Mention Color:</label>
<input type="color" id="mention" name="mention">
<label for="mentionborder">Mention Border Color:</label>
<input type="color" id="mentionborder" name="mentionborder">
<button id="applyTheme">Apply Theme</button>
</div>
`;
document.body.appendChild(modal);
document.getElementById('applyTheme').addEventListener('click', () => {
const background = document.getElementById('background').value;
const sidebar = document.getElementById('sidebar').value;
const buttons = document.getElementById('buttons').value;
const mention = document.getElementById('mention').value;
const mentionborder = document.getElementById('mentionborder').value;
document.body.style.backgroundColor = background;
document.querySelector('.sidebar').style.backgroundColor = sidebar;
document.querySelectorAll('input[type=button]').forEach(button => button.style.backgroundColor = buttons);
document.querySelectorAll('.mention').forEach(mention => mention.style.backgroundColor = mention);
document.querySelectorAll('.highlight').forEach(mention => mention.style.borderColor = mentionborder);
});
});

30
login.php 100644
View File

@ -0,0 +1,30 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Strife | Login</title>
<link rel="stylesheet" href="style.css">
</head>
<body>
<section id="login">
<h2>Welcome to Strife!</h2>
<p>Please enter your details to log in.</p>
<hr>
<form>
<label for="username">Username: </label>
<input id="username" type="text" name="username" placeholder="Username" required>
<label for="password">Password: </label>
<input id="password" type="password" name="password" placeholder="Password" required>
<button id="submit-login" type="submit">Submit</button>
</form>
<hr>
<h3>Don't have an account?</h3>
<p>Create one here!</p>
<a href="./register.php">Register</a>
</section>
<script src="./js/login.js"></script>
</body>
</html>

12
php/db.php 100644
View File

@ -0,0 +1,12 @@
<?php
$host = 'localhost';
$user = 'root';
$password = '';
$dbname = 'messaging_app';
$conn = new mysqli($host, $user, $password, $dbname);
if ($conn->connect_error) {
die("Connection failed: " . $conn->connect_error);
}

View File

@ -0,0 +1,98 @@
<?php
session_start();
include './db.php';
$current_user = $_SESSION['username'];
$last_timestamp = $_GET['last_timestamp'] ?? '1970-01-01 00:00:00';
$timeout = 1;
$start_time = time();
while (true) {
$sql_group_messages = "SELECT gm.message, u.username, gm.sent_at, gm.file_path
FROM group_messages gm
JOIN users u ON gm.user_id = u.id
WHERE gm.sent_at > ?
ORDER BY gm.sent_at DESC
LIMIT 25";
$stmt = $conn->prepare($sql_group_messages);
$stmt->bind_param("s", $last_timestamp);
$stmt->execute();
$result_group_messages = $stmt->get_result();
if ($result_group_messages->num_rows > 0) {
$messages = [];
$latest_timestamp = '';
$sql_users = "SELECT username FROM users";
$user_result = $conn->query($sql_users);
$users = [];
while ($user = $user_result->fetch_assoc()) {
$users[$user['username']] = true;
}
while ($message = $result_group_messages->fetch_assoc()) {
$message_text = htmlspecialchars($message['message']);
$highlight_class = '';
preg_match_all('/@(\w+)/', $message['message'], $mentions);
$unique_mentions = array_unique($mentions[1]);
foreach ($unique_mentions as $mention) {
if (isset($users[$mention])) {
$message_text = preg_replace('/@' . preg_quote($mention, '/') . '/', "<span class=\"mention\">@$mention</span>", $message_text, 1);
if ($mention === $current_user) {
$highlight_class = 'highlight';
}
}
}
$message_text = preg_replace('/(https?:\/\/[^\s]+)/', '<a href="$1" class="link" target="_blank">$1</a>', $message_text);
$message_text = preg_replace('/\b(www\.[^\s]+)/', '<a href="http://$1" class="link" target="_blank">$1</a>', $message_text);
$output = "<span class=\"$highlight_class\"><b>" . htmlspecialchars($message['username']) . ":</b> " . $message_text;
if (!empty($message['file_path'])) {
$path = '/projects/strife/uploads/';
$full_file_path = $_SERVER['DOCUMENT_ROOT'] . $path . $message['file_path'];
if (file_exists($full_file_path)) {
$file_type = mime_content_type($full_file_path);
if (strpos($file_type, 'image') !== false) {
$output .= "<br><img src='$path" . htmlspecialchars($message['file_path']) . "' alt='image' style='max-width: 200px;' />";
} elseif (strpos($file_type, 'audio') !== false) {
$output .= "<br><audio controls><source src='$path" . htmlspecialchars($message['file_path']) . "' type='$file_type'></audio>";
} elseif (strpos($file_type, 'video') !== false) {
$output .= "<br><video controls style='max-width: 200px;'><source src='$path" . htmlspecialchars($message['file_path']) . "' type='$file_type'></video>";
} else {
$output .= "<br>Unsupported file type: " . htmlspecialchars($file_type);
}
} else {
$output .= "<br>File does not exist.";
}
}
$output .= " <i>(" . htmlspecialchars($message['sent_at']) . ")</i></span>";
$messages[] = $output;
if ($latest_timestamp === '' || $message['sent_at'] > $latest_timestamp) {
$latest_timestamp = $message['sent_at'];
}
}
header('Content-Type: application/json');
echo json_encode(['messages' => array_reverse($messages), 'latest_timestamp' => $latest_timestamp]);
exit;
}
if (time() - $start_time >= $timeout) {
break;
}
usleep(5000);
}
header('Content-Type: application/json');
echo json_encode(['messages' => [], 'latest_timestamp' => $last_timestamp]);

View File

@ -0,0 +1,97 @@
<?php
session_start();
include 'db.php';
define('DECRYPTION_KEY', 'fdadsihuiads');
function decryptMessage($encryptedMessage, $key) {
$data = base64_decode($encryptedMessage);
$iv = substr($data, 0, openssl_cipher_iv_length('aes-256-cbc'));
$encryptedMessage = substr($data, openssl_cipher_iv_length('aes-256-cbc'));
return openssl_decrypt($encryptedMessage, 'aes-256-cbc', $key, 0, $iv);
}
if (!isset($_SESSION['user_id'])) {
echo json_encode(["status" => "error", "message" => "You must be logged in."]);
exit;
}
$user_id = $_SESSION['user_id'];
$selected_user_id = isset($_GET['user_id']) ? intval($_GET['user_id']) : 0;
$last_timestamp = isset($_GET['last_timestamp']) ? $_GET['last_timestamp'] : '1970-01-01 00:00:00';
$timeout = 10; // Timeout in seconds
$start_time = time();
while (true) {
$sql_private_messages = "
SELECT pm.message, u.username AS sender, UNIX_TIMESTAMP(pm.sent_at) AS sent_at, pm.file_path
FROM private_messages pm
JOIN users u ON pm.sender_id = u.id
WHERE ((pm.sender_id = ? AND pm.receiver_id = ?)
OR (pm.sender_id = ? AND pm.receiver_id = ?))
AND pm.sent_at > ?
ORDER BY pm.sent_at ASC";
$stmt_private_messages = $conn->prepare($sql_private_messages);
$stmt_private_messages->bind_param("iiiis", $user_id, $selected_user_id, $selected_user_id, $user_id, $last_timestamp);
$stmt_private_messages->execute();
$result_private_messages = $stmt_private_messages->get_result();
if ($result_private_messages->num_rows > 0) {
$private_messages = [];
$latest_timestamp = $last_timestamp;
while ($row = $result_private_messages->fetch_assoc()) {
$row['message'] = decryptMessage($row['message'], DECRYPTION_KEY);
$message_text = htmlspecialchars($row['message']);
$output = "<li><b>" . htmlspecialchars($row['sender']) . ":</b> " . $message_text;
if (!empty($row['file_path'])) {
$path = '/projects/strife/uploads/';
$full_file_path = $_SERVER['DOCUMENT_ROOT'] . $path . $row['file_path'];
if (file_exists($full_file_path)) {
$file_type = mime_content_type($full_file_path);
if (strpos($file_type, 'image') !== false) {
$output .= "<br><img src='$path" . htmlspecialchars($row['file_path']) . "' alt='image' style='max-width: 200px;' />";
} elseif (strpos($file_type, 'audio') !== false) {
$output .= "<br><audio controls><source src='$path" . htmlspecialchars($row['file_path']) . "' type='$file_type'></audio>";
} elseif (strpos($file_type, 'video') !== false) {
$output .= "<br><video controls style='max-width: 200px;'><source src='$path" . htmlspecialchars($row['file_path']) . "' type='$file_type'></video>";
} else {
$output .= "<br>Unsupported file type: " . htmlspecialchars($file_type);
}
} else {
$output .= "<br>File does not exist.";
}
}
$output .= " <i>(" . date('Y-m-d H:i:s', $row['sent_at']) . ")</i></li>";
$private_messages[] = $output;
if ($row['sent_at'] > $latest_timestamp) {
$latest_timestamp = date('Y-m-d H:i:s', $row['sent_at']);
}
}
header('Content-Type: application/json');
echo json_encode(['messages' => $private_messages, 'latest_timestamp' => $latest_timestamp]);
$stmt_private_messages->close();
$conn->close();
exit;
}
if (time() - $start_time >= $timeout) {
break;
}
usleep(500000); // Sleep for 0.5 seconds
}
header('Content-Type: application/json');
echo json_encode(['messages' => [], 'latest_timestamp' => $last_timestamp]);
$stmt_private_messages->close();
$conn->close();

33
php/login.php 100644
View File

@ -0,0 +1,33 @@
<?php
session_start();
include 'db.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$username = $_POST['username'];
$password = $_POST['password'];
if (isset($conn)) {
$sql = "SELECT * FROM users WHERE username=?";
$stmt = $conn->prepare($sql);
$stmt->bind_param("s", $username);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
$user = $result->fetch_assoc();
if (password_verify($password, $user['password'])) {
$_SESSION['user_id'] = $user['id'];
$_SESSION['username'] = $user['username'];
header("Location: home.php");
exit;
} else {
echo "Invalid credentials!";
}
} else {
echo "User not found!";
}
} else {
echo "Database connection error!";
}
}

5
php/logout.php 100644
View File

@ -0,0 +1,5 @@
<?php
session_start();
session_destroy();
header("Location: ../login.php");
exit;

32
php/register.php 100644
View File

@ -0,0 +1,32 @@
<?php
include 'db.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$username = $_POST['username'];
$email = $_POST['email'];
$password = password_hash($_POST['password'], PASSWORD_BCRYPT);
$check_sql = "SELECT * FROM users WHERE username = ? OR email = ?";
$stmt = $conn->prepare($check_sql);
$stmt->bind_param("ss", $username, $email);
$stmt->execute();
$result = $stmt->get_result();
if ($result->num_rows > 0) {
echo "Username or email already exists. Please choose a different one.";
} else {
$sql = "INSERT INTO users (username, email, password) VALUES (?, ?, ?)";
$stmt = $conn->prepare($sql);
$stmt->bind_param("sss", $username, $email, $password);
if ($stmt->execute() === TRUE) {
echo "Registration successful! <a href='login.html'>Login here</a>";
} else {
echo "Error: " . $conn->error;
}
}
$stmt->close();
}
$conn->close();
?>

59
php/sendGlobal.php 100644
View File

@ -0,0 +1,59 @@
<?php
session_start();
include 'db.php';
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (!isset($_SESSION['user_id'])) {
echo json_encode(["status" => "error", "message" => "You must be logged in to send a message."]);
exit;
}
$user_id = $_SESSION['user_id'];
$message = isset($_POST['group_message']) ? trim($_POST['group_message']) : '';
$file_path = null;
if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
$allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'audio/mpeg', 'video/mp4', 'audio/mp4'];
$file_type = $_FILES['file']['type'];
if (!in_array($file_type, $allowed_types)) {
echo json_encode(["status" => "error", "message" => "File type not allowed."]);
exit;
}
$upload_dir = '../uploads/';
if (!is_dir($upload_dir)) {
if (!mkdir($upload_dir, 0777, true) && !is_dir($upload_dir)) {
echo json_encode(["status" => "error", "message" => "Failed to create upload directory."]);
exit;
}
}
$file_name = basename($_FILES['file']['name']);
$file_path = $upload_dir . uniqid() . "_" . $file_name;
if (!move_uploaded_file($_FILES['file']['tmp_name'], $file_path)) {
echo json_encode(["status" => "error", "message" => "File upload failed."]);
exit;
}
}
$stmt = $conn->prepare("INSERT INTO group_messages (user_id, message, file_path) VALUES (?, ?, ?)");
if ($stmt === false) {
echo json_encode(["status" => "error", "message" => "Error preparing the statement: {$conn->error}"]);
exit;
}
$stmt->bind_param("iss", $user_id, $message, $file_path);
if ($stmt->execute()) {
echo json_encode(["status" => "success", "message" => "Message sent successfully!"]);
} else {
echo json_encode(["status" => "error", "message" => "Error executing query: {$stmt->error}"]);
error_log("Database Insert Error: {$stmt->error}");
}
$stmt->close();
$conn->close();
}

View File

@ -0,0 +1,86 @@
<?php
session_start();
include 'db.php';
define('ENCRYPTION_KEY', 'fdadsihuiads');
function encryptMessage($message, $key) {
$iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length('aes-256-cbc'));
$encryptedMessage = openssl_encrypt($message, 'aes-256-cbc', $key, 0, $iv);
return base64_encode($iv . $encryptedMessage);
}
function decryptMessage($encryptedMessage, $key) {
$data = base64_decode($encryptedMessage);
$iv = substr($data, 0, openssl_cipher_iv_length('aes-256-cbc'));
$encryptedMessage = substr($data, openssl_cipher_iv_length('aes-256-cbc'));
return openssl_decrypt($encryptedMessage, 'aes-256-cbc', $key, 0, $iv);
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (!isset($_SESSION['user_id'])) {
echo json_encode(["status" => "error", "message" => "You must be logged in to send a message."]);
exit;
}
$user_id = $_SESSION['user_id'];
$receiver_id = isset($_POST['receiver_id']) ? intval($_POST['receiver_id']) : 0;
$message = isset($_POST['private_message']) ? trim($_POST['private_message']) : '';
if (empty($message) || empty($receiver_id)) {
echo json_encode(["status" => "error", "message" => "Message or receiver ID is missing."]);
exit;
}
$file_path = null;
if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
$allowed_types = ['image/jpeg', 'image/png', 'image/gif', 'audio/mpeg', 'video/mp4', 'audio/mp4'];
$file_type = $_FILES['file']['type'];
if (!in_array($file_type, $allowed_types)) {
echo json_encode(["status" => "error", "message" => "File type not allowed."]);
exit;
}
$upload_dir = '../uploads/';
if (!is_dir($upload_dir)) {
if (!mkdir($upload_dir, 0777, true) && !is_dir($upload_dir)) {
echo json_encode(["status" => "error", "message" => "Failed to create upload directory."]);
exit;
}
}
$file_name = basename($_FILES['file']['name']);
$file_path = $upload_dir . uniqid() . "_" . $file_name;
if (!move_uploaded_file($_FILES['file']['tmp_name'], $file_path)) {
echo json_encode(["status" => "error", "message" => "File upload failed."]);
exit;
}
}
// Encrypt the message
$encrypted_message = encryptMessage($message, ENCRYPTION_KEY);
$stmt = $conn->prepare("INSERT INTO private_messages (sender_id, receiver_id, message, file_path) VALUES (?, ?, ?, ?)");
if ($stmt === false) {
echo json_encode(["status" => "error", "message" => "Error preparing the statement: {$conn->error}"]);
exit;
}
$stmt->bind_param("iiss", $user_id, $receiver_id, $encrypted_message, $file_path);
if ($stmt->execute()) {
echo json_encode(["status" => "success", "message" => "Message sent successfully!"]);
} else {
echo json_encode(["status" => "error", "message" => "Error executing query: {$stmt->error}"]);
error_log("Database Insert Error: {$stmt->error}");
}
$stmt->close();
$conn->close();
} else {
echo json_encode(["status" => "error", "message" => "Invalid request method."]);
}

65
private.php 100644
View File

@ -0,0 +1,65 @@
<?php
session_start();
include './php/db.php';
if (!isset($_SESSION['user_id'])) {
header("Location: ./login.php");
exit;
}
$user_id = $_SESSION['user_id'];
$username = $_SESSION['username'];
// Fetch all users for messaging
$sql_users = "SELECT id, username FROM users WHERE id != ?";
$stmt_users = $conn->prepare($sql_users);
$stmt_users->bind_param("i", $user_id);
$stmt_users->execute();
$result_users = $stmt_users->get_result();
$stmt_users->close();
?>
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Private Messages</title>
<link rel="stylesheet" href="style.css">
<script src="./js/themeModal.js"></script>
<script src="./js/private.js"></script>
</head>
<body>
<div class="container">
<h2>Welcome, <?php echo htmlspecialchars($username); ?>!</h2>
<div class="messages">
<h3>Your Private Messages</h3>
<ul id="messageList" style="flex-direction: column;">
<!-- Messages will be dynamically loaded here -->
</ul>
<form id="privateMessageForm" enctype="multipart/form-data">
<input type="hidden" name="receiver_id" value=""> <!-- This will be set dynamically -->
<textarea name="private_message" required placeholder="Type your message..."></textarea>
<input type="file" name="file"> <!-- Optional file upload -->
<button type="submit">Send Message</button>
</form>
</div>
<div class="sidebar">
<h3>Select User:</h3>
<?php
if ($result_users->num_rows > 0) {
while ($user = $result_users->fetch_assoc()) {
echo '<button type="button" name="select_user" value="' . htmlspecialchars($user['id']) . '">' . htmlspecialchars($user['username']) . '</button>';
}
} else {
echo "<p>No users available.</p>";
}
?>
<p><a href="global.php">Home</a></p>
<p><a href="./php/logout.php">Logout</a></p>
</div>
</div>
</body>
</html>

20
readme.md 100644
View File

@ -0,0 +1,20 @@
# Strife - PHP Messaging App
Welcome to Strife, a PHP-based messaging application.
## Installation
To get started with Strife, follow these steps:
1. Install the required Python packages:
```bash
pip3 install -r requirements.txt
```
2. Set up the database:
```bash
python3 ./database/dbMasterFile.py
```
You're all set! Enjoy using Strife.

36
register.php 100644
View File

@ -0,0 +1,36 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Strife | Register</title>
<link rel="stylesheet" href="style.css"> <!-- Link to the updated CSS -->
</head>
<body>
<section id="register">
<h2>Welcome to Strife!</h2>
<p>Please enter your details to register.</p>
<hr>
<form>
<label for="username">Username: </label>
<input id="username" type="text" name="username" placeholder="Username" required>
<label for="email">Email: </label> <!-- Added email input -->
<input id="email" type="email" name="email" placeholder="Email" required>
<label for="password">Password: </label>
<input id="password" type="password" name="password" placeholder="Password" required>
<label for="confirm-password">Confirm Password: </label>
<input id="confirm-password" type="password" name="confirm-password" placeholder="Confirm Password" required>
<button id="submit-register" type="submit">Submit</button>
</form>
<hr>
<h3>Already have an account?</h3>
<p>Log in below!</p>
<a href="login.php">Login</a>
</section>
<script src="./js/register.js"></script>
</body>
</html>

1
requirements.txt 100644
View File

@ -0,0 +1 @@
mysql-connector-python

408
style.css 100644
View File

@ -0,0 +1,408 @@
/* General Reset */
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: 'Comic Sans MS', cursive;
}
::-webkit-scrollbar {
width: 0;
}
html {
color-scheme: dark;
}
/* Body Styling */
body {
display: flex;
flex-direction: row;
height: 100vh;
background-color: #2f3136; /* Discord dark background */
color: #ffffff; /* White text */
padding: 0;
font-family: 'Comic Sans MS', cursive;
}
/* Shared Section Styling for Login and Register */
#login,
#register {
background-color: #202225; /* Slightly darker background */
padding: 40px 40px;
border-radius: 10px;
max-width: 400px;
width: 100%;
text-align: center;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.8);
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
}
h2 {
color: #ffffff;
margin-bottom: 24px;
font-size: 24px;
}
p {
color: #b9bbbe; /* Light gray for instructions */
margin-bottom: 10px;
font-size: 16px;
}
hr {
border: 0;
border-top: 1px solid #4f545c;
margin: 20px 0;
}
h3 {
color: #b9bbbe;
margin: 16px 0 10px;
font-size: 16px;
}
a {
color: #5865f2; /* Discord blue */
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
/* Form Styling */
form {
display: flex;
flex-direction: column;
gap: 15px;
}
input[type="text"],
input[type="password"] {
padding: 12px 15px;
background-color: #40444b;
color: #dcddde;
border: 1px solid #4f545c;
border-radius: 5px;
outline: none;
font-size: 14px;
transition: border-color 0.2s ease;
width: 100%;
}
input[type="text"]:focus,
input[type="password"]:focus {
border-color: #5865f2;
}
button {
padding: 12px 20px;
background-color: #5865f2; /* Discord blue */
color: #ffffff;
border: none;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s ease;
width: 100%;
}
button:hover {
background-color: #4752c4;
}
button:active {
background-color: #3b44a1;
}
/* Add Responsiveness for smaller screens */
@media screen and (max-width: 1024px) {
#login,
#register {
width: 90%;
padding: 20px;
}
}
h2 {
margin-bottom: 16px;
}
/* Add Responsiveness for smaller screens */
@media screen and (max-width: 1024px) {
.sidebar {
width: 60px; /* Smaller sidebar on mobile */
padding: 10px 0;
}
}
/* User Sidebar */
.user-sidebar {
position: fixed;
right: 0;
width: 250px;
background-color: #202225;
padding: 20px;
box-shadow: -2px 0 10px rgba(0, 0, 0, 0.6);
height: 100vh;
display: flex;
flex-direction: column;
gap: 10px;
}
.user-sidebar h2 {
color: white;
}
.user-sidebar p {
color: #b9bbbe;
font-size: 14px;
}
.user-sidebar img {
width: 60px;
height: 60px;
border-radius: 50%;
}
#adminModal,
#modModal {
padding: 16px;
border-radius: 0.5rem;
background-color: #202225;
position: absolute;
right: 16px;
top: 16px;
z-index: +1;
}
#adminModal input,
#modModal input {
padding: 10px 20px;
background-color: #5865f2; /* Discord blue */
border: none;
color: #ffffff;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s ease;
}
.settings {
display: flex;
justify-content: center;
align-items: center;
position: absolute;
top: 8px;
left: 8px;
background-color: unset;
padding: unset;
border-radius: unset;
width: unset;
}
.settings svg {
height: 32px;
width: 32px;
}
.settings:hover {
background-color: unset;
}
/* Additional Styling for Logout and other elements */
p {
margin-top: 10px;
text-align: center;
}
p a {
color: #5865f2;
text-decoration: none;
}
p a:hover {
text-decoration: underline;
}
.settingsModal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
z-index: 1;
background-color: #202225;
padding: 16px;
border-radius: 0.5rem;
}
/* Left Sidebar */
.sidebar {
width: 240px;
background-color: #202225; /* Slightly darker gray */
padding: 20px 10px;
display: flex;
flex-direction: column;
justify-content: space-between;
gap: 10px;
box-shadow: 2px 0 10px rgba(0, 0, 0, 0.6);
position: fixed;
height: 100%;
left: 0;
}
.sidebar a {
color: #b9bbbe; /* Light gray links */
text-decoration: none;
display: block;
padding: 10px 15px;
border-radius: 4px;
transition: background-color 0.2s ease;
}
.sidebar a:hover {
background-color: #3a3d42;
}
/* Main Chat Area */
.container {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: flex-end;
padding: 20px;
padding-left: 270px; /* Space for the sidebar */
height: 100vh;
overflow: hidden;
position: relative;
}
.highlight {
border: 2px solid orange;
}
.mention {
color: #5865f2;
font-weight: bold;
padding: unset;
}
/* Chat Messages */
#messageList {
display: flex;
flex-direction: column-reverse;
gap: 10px;
overflow-y: auto;
max-height: 70vh;
}
#messageList li span:not(.mention) {
padding: 12px;
border-radius: 10px;
background-color: #40444b; /* Dark gray background for messages */
margin-bottom: 5px;
word-wrap: break-word;
max-width: 60%;
display: inline-block;
transition: background-color 0.3s ease;
font-family: 'Comic Sans MS', cursive;
}
#messageList li.user {
background-color: #5865f2; /* Blue color for user messages */
color: white;
align-self: flex-end;
}
#messageList li span:hover {
background-color: #3a3d42;
}
#messageList li b {
font-weight: bold;
}
/* Message Timestamps */
#messageList i {
font-size: 12px;
color: #6e737a; /* Lighter gray for timestamps */
}
/* Textarea for message input */
#messageForm {
margin-top: 16px;
display: flex;
gap: 10px;
align-items: flex-end;
flex-direction: unset;
}
textarea {
height: 36px;
padding: 10px;
background-color: #40444b;
color: #dcddde;
border: 1px solid #4f545c;
border-radius: 5px;
resize: none;
width: 80%;
font-size: 14px;
outline: none;
transition: border-color 0.2s ease;
}
textarea:focus {
border-color: #5865f2; /* Blue outline on focus */
}
input[type="button"] {
padding: 10px 20px;
background-color: #5865f2; /* Discord blue */
border: none;
color: #ffffff;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s ease;
width: 15%;
}
input[type="button"]:hover {
background-color: #4752c4;
}
input[type="button"]:active {
background-color: #3b44a1;
}
input[type="button"]:disabled {
background-color: #202225;
cursor: not-allowed;
}
/* file upload button */
input[type="file"]::file-selector-button {
padding: 10px 20px;
background-color: #5865f2; /* Discord blue */
border: none;
color: #ffffff;
border-radius: 5px;
cursor: pointer;
font-size: 14px;
transition: background-color 0.2s ease;
margin-right: 32px;
}
/* file upload button hover state */
input[type="file"]::file-selector-button:hover {
background-color: #4752c4;
}
/* file upload button active state */
input[type="file"]::file-selector-button:active {
background-color: #3b44a1;
}