| <!DOCTYPE html> |
| <html lang="en"> |
| <head> |
| <meta charset="UTF-8"> |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> |
| <title>Tech Null Messenger</title> |
| <script src="https://cdn.tailwindcss.com"></script> |
| <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> |
| <style> |
| .gradient-bg { |
| background: linear-gradient(135deg, #1e3a8a 0%, #0ea5e9 100%); |
| } |
| .chat-container { |
| height: calc(100vh - 200px); |
| } |
| .message-animation { |
| animation: fadeIn 0.3s ease-in-out; |
| } |
| @keyframes fadeIn { |
| from { opacity: 0; transform: translateY(10px); } |
| to { opacity: 1; transform: translateY(0); } |
| } |
| .blink { |
| animation: blink 1s infinite; |
| } |
| @keyframes blink { |
| 0% { opacity: 1; } |
| 50% { opacity: 0.5; } |
| 100% { opacity: 1; } |
| } |
| </style> |
| </head> |
| <body class="bg-gray-100 font-sans"> |
| <div id="app" class="min-h-screen flex flex-col"> |
| |
| <header class="gradient-bg text-white p-4 shadow-lg"> |
| <div class="container mx-auto flex justify-between items-center"> |
| <h1 class="text-2xl font-bold flex items-center"> |
| <i class="fas fa-robot mr-2"></i> Tech Null |
| </h1> |
| <div id="auth-status" class="flex items-center space-x-4"> |
| <span id="user-info" class="hidden"> |
| <span id="username-display" class="font-semibold"></span> |
| <button id="logout-btn" class="ml-2 bg-red-500 hover:bg-red-600 text-white px-3 py-1 rounded text-sm"> |
| <i class="fas fa-sign-out-alt mr-1"></i> Logout |
| </button> |
| </span> |
| </div> |
| </div> |
| </header> |
|
|
| |
| <main class="flex-grow container mx-auto p-4"> |
| |
| <div id="auth-screens" class="max-w-md mx-auto"> |
| |
| <div id="login-screen" class="bg-white rounded-lg shadow-md p-6 mb-4"> |
| <h2 class="text-xl font-bold mb-4 text-gray-800">Login</h2> |
| <div class="mb-4"> |
| <label for="login-id" class="block text-gray-700 mb-2">ID</label> |
| <input type="number" id="login-id" class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| </div> |
| <div class="mb-4"> |
| <label for="login-password" class="block text-gray-700 mb-2">Password</label> |
| <input type="password" id="login-password" class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| </div> |
| <button id="login-btn" class="w-full bg-blue-500 hover:bg-blue-600 text-white py-2 px-4 rounded font-medium"> |
| <i class="fas fa-sign-in-alt mr-2"></i> Login |
| </button> |
| <p class="text-center mt-4 text-gray-600">Don't have an account? <button id="show-register-btn" class="text-blue-500 hover:text-blue-700 font-medium">Register</button></p> |
| </div> |
|
|
| |
| <div id="register-screen" class="bg-white rounded-lg shadow-md p-6 hidden"> |
| <h2 class="text-xl font-bold mb-4 text-gray-800">Register</h2> |
| <div class="mb-4"> |
| <label for="register-username" class="block text-gray-700 mb-2">Username (max 20 chars)</label> |
| <input type="text" id="register-username" maxlength="20" class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| </div> |
| <div class="mb-4"> |
| <label for="register-password" class="block text-gray-700 mb-2">Password (max 20 chars)</label> |
| <input type="password" id="register-password" maxlength="20" class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| </div> |
| <button id="register-btn" class="w-full bg-green-500 hover:bg-green-600 text-white py-2 px-4 rounded font-medium"> |
| <i class="fas fa-user-plus mr-2"></i> Register |
| </button> |
| <p class="text-center mt-4 text-gray-600">Already have an account? <button id="show-login-btn" class="text-blue-500 hover:text-blue-700 font-medium">Login</button></p> |
| </div> |
| </div> |
|
|
| |
| <div id="chat-screen" class="hidden"> |
| <div class="flex flex-col lg:flex-row gap-4"> |
| |
| <div class="flex-grow"> |
| <div class="bg-white rounded-lg shadow-md overflow-hidden"> |
| <div class="p-4 border-b border-gray-200 bg-gray-50"> |
| <h2 class="text-xl font-bold text-gray-800">Global Chat</h2> |
| <div id="message-stats" class="text-sm text-gray-600 mt-1"> |
| Messages: <span id="message-count">0</span> • Users: <span id="user-count">1</span> |
| </div> |
| </div> |
| <div id="chat-messages" class="chat-container overflow-y-auto p-4 space-y-4"> |
| |
| </div> |
| <div class="p-4 border-t border-gray-200 bg-gray-50"> |
| <div class="flex items-center"> |
| <input type="text" id="message-input" maxlength="1000" placeholder="Type your message..." class="flex-grow px-3 py-2 border border-gray-300 rounded-l focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| <button id="send-btn" class="bg-blue-500 hover:bg-blue-600 text-white px-4 py-2 rounded-r"> |
| <i class="fas fa-paper-plane"></i> |
| </button> |
| </div> |
| <div id="message-counter" class="text-sm text-gray-600 mt-2"> |
| Messages this hour: <span id="messages-sent">0</span>/<span id="message-limit">10</span> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div class="w-full lg:w-64"> |
| <div class="bg-white rounded-lg shadow-md overflow-hidden"> |
| <div class="p-4 border-b border-gray-200 bg-gray-50"> |
| <h2 class="text-xl font-bold text-gray-800">User Panel</h2> |
| </div> |
| <div class="p-4 space-y-4"> |
| <div> |
| <h3 class="font-semibold text-gray-700 mb-2">Account Info</h3> |
| <div class="text-sm text-gray-600 space-y-1"> |
| <p>ID: <span id="user-id-display" class="font-medium"></span></p> |
| <p>Username: <span id="user-name-display" class="font-medium"></span></p> |
| <p>Registered: <span id="user-registered" class="font-medium"></span></p> |
| </div> |
| </div> |
| <div> |
| <h3 class="font-semibold text-gray-700 mb-2">Settings</h3> |
| <div class="space-y-2"> |
| <button id="change-username-btn" class="w-full text-left bg-gray-100 hover:bg-gray-200 text-gray-800 px-3 py-2 rounded text-sm"> |
| <i class="fas fa-user-edit mr-1"></i> Change Username |
| </button> |
| <button id="change-password-btn" class="w-full text-left bg-gray-100 hover:bg-gray-200 text-gray-800 px-3 py-2 rounded text-sm"> |
| <i class="fas fa-key mr-1"></i> Change Password |
| </button> |
| </div> |
| </div> |
| <div id="admin-panel" class="hidden"> |
| <h3 class="font-semibold text-gray-700 mb-2">Admin Panel</h3> |
| <div class="space-y-2"> |
| <div class="flex items-center"> |
| <input type="text" id="admin-command" placeholder="Enter command..." class="flex-grow px-2 py-1 text-sm border border-gray-300 rounded-l focus:outline-none focus:ring-1 focus:ring-blue-500"> |
| <button id="execute-command-btn" class="bg-blue-500 hover:bg-blue-600 text-white px-2 py-1 rounded-r text-sm"> |
| <i class="fas fa-terminal"></i> |
| </button> |
| </div> |
| <button id="help-btn" class="w-full text-left bg-gray-100 hover:bg-gray-200 text-gray-800 px-3 py-2 rounded text-sm"> |
| <i class="fas fa-question-circle mr-1"></i> Show Help |
| </button> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </div> |
| </main> |
|
|
| |
| |
| <div id="username-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-md"> |
| <h3 class="text-lg font-bold mb-4">Change Username</h3> |
| <div class="mb-4"> |
| <label for="new-username" class="block text-gray-700 mb-2">New Username (max 20 chars)</label> |
| <input type="text" id="new-username" maxlength="20" class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| </div> |
| <div class="flex justify-end space-x-3"> |
| <button id="cancel-username-btn" class="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-100">Cancel</button> |
| <button id="save-username-btn" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">Save</button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="password-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-md"> |
| <h3 class="text-lg font-bold mb-4">Change Password</h3> |
| <div class="mb-4"> |
| <label for="current-password" class="block text-gray-700 mb-2">Current Password</label> |
| <input type="password" id="current-password" class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| </div> |
| <div class="mb-4"> |
| <label for="new-password" class="block text-gray-700 mb-2">New Password (max 20 chars)</label> |
| <input type="password" id="new-password" maxlength="20" class="w-full px-3 py-2 border border-gray-300 rounded focus:outline-none focus:ring-2 focus:ring-blue-500"> |
| </div> |
| <div class="flex justify-end space-x-3"> |
| <button id="cancel-password-btn" class="px-4 py-2 border border-gray-300 rounded text-gray-700 hover:bg-gray-100">Cancel</button> |
| <button id="save-password-btn" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">Save</button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="help-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-2xl max-h-[80vh] overflow-y-auto"> |
| <h3 class="text-lg font-bold mb-4">Admin Commands Help</h3> |
| <div class="space-y-3 text-sm"> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/mute id x</p> |
| <p class="text-gray-600">Mute account by ID for x hours</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/unmute id</p> |
| <p class="text-gray-600">Unmute account by ID</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/del_m mid</p> |
| <p class="text-gray-600">Delete message by message ID</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/del_um id</p> |
| <p class="text-gray-600">Delete all messages by user ID</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/limit_m x</p> |
| <p class="text-gray-600">Set message limit per hour to x</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/limit_reset</p> |
| <p class="text-gray-600">Reset message counters for all users</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/vipe_chat</p> |
| <p class="text-gray-600">Delete all messages from chat</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/vipe_all</p> |
| <p class="text-gray-600">Delete all messages and accounts (except ID 0)</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/change_n id x</p> |
| <p class="text-gray-600">Change username of user ID to x</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/change_p id x</p> |
| <p class="text-gray-600">Change password of user ID to x</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/check_ip mid</p> |
| <p class="text-gray-600">Check IP of message by message ID</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/mute_ip ip x</p> |
| <p class="text-gray-600">Mute IP address for x hours</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/unmute_ip ip</p> |
| <p class="text-gray-600">Unmute IP address</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/list</p> |
| <p class="text-gray-600">List all muted IPs/accounts</p> |
| </div> |
| <div class="p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">/help</p> |
| <p class="text-gray-600">Show this help</p> |
| </div> |
| </div> |
| <div class="mt-4 flex justify-end"> |
| <button id="close-help-btn" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">Close</button> |
| </div> |
| </div> |
| </div> |
|
|
| |
| <div id="register-success-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50"> |
| <div class="bg-white rounded-lg shadow-xl p-6 w-full max-w-md"> |
| <h3 class="text-lg font-bold mb-4 text-green-600">Registration Successful!</h3> |
| <div class="mb-4"> |
| <p class="text-gray-700">Your account has been created.</p> |
| <div class="mt-3 p-3 bg-gray-50 rounded"> |
| <p class="font-semibold">ID: <span id="registered-id" class="font-normal"></span></p> |
| <p class="font-semibold">Username: <span id="registered-username" class="font-normal"></span></p> |
| </div> |
| <p class="mt-3 text-sm text-gray-600">Please remember your ID as you'll need it to login.</p> |
| </div> |
| <div class="flex justify-end"> |
| <button id="ok-register-btn" class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600">OK</button> |
| </div> |
| </div> |
| </div> |
| </div> |
|
|
| <script> |
| |
| const data = { |
| users: [ |
| { |
| id: 0, |
| username: "Null", |
| password: "QWERTYnull123!", |
| registered: new Date().toISOString(), |
| ip: "127.0.0.1", |
| deviceId: "admin-device" |
| } |
| ], |
| messages: [], |
| mutedUsers: {}, |
| mutedIPs: {}, |
| messageCounters: {}, |
| deviceRegistrations: {}, |
| messageLimit: 10, |
| nextMessageId: 1 |
| }; |
| |
| |
| function saveData() { |
| localStorage.setItem('techNullData', JSON.stringify(data)); |
| } |
| |
| function loadData() { |
| const savedData = localStorage.getItem('techNullData'); |
| if (savedData) { |
| Object.assign(data, JSON.parse(savedData)); |
| } |
| } |
| |
| |
| loadData(); |
| |
| |
| let currentUser = null; |
| let currentIP = "127.0.0.1"; |
| let currentDeviceId = "demo-device"; |
| |
| |
| const loginScreen = document.getElementById('login-screen'); |
| const registerScreen = document.getElementById('register-screen'); |
| const chatScreen = document.getElementById('chat-screen'); |
| const loginIdInput = document.getElementById('login-id'); |
| const loginPasswordInput = document.getElementById('login-password'); |
| const registerUsernameInput = document.getElementById('register-username'); |
| const registerPasswordInput = document.getElementById('register-password'); |
| const messageInput = document.getElementById('message-input'); |
| const chatMessages = document.getElementById('chat-messages'); |
| const userInfo = document.getElementById('user-info'); |
| const usernameDisplay = document.getElementById('username-display'); |
| const logoutBtn = document.getElementById('logout-btn'); |
| const showRegisterBtn = document.getElementById('show-register-btn'); |
| const showLoginBtn = document.getElementById('show-login-btn'); |
| const loginBtn = document.getElementById('login-btn'); |
| const registerBtn = document.getElementById('register-btn'); |
| const sendBtn = document.getElementById('send-btn'); |
| const userIdDisplay = document.getElementById('user-id-display'); |
| const userNameDisplay = document.getElementById('user-name-display'); |
| const userRegistered = document.getElementById('user-registered'); |
| const adminPanel = document.getElementById('admin-panel'); |
| const adminCommand = document.getElementById('admin-command'); |
| const executeCommandBtn = document.getElementById('execute-command-btn'); |
| const changeUsernameBtn = document.getElementById('change-username-btn'); |
| const changePasswordBtn = document.getElementById('change-password-btn'); |
| const usernameModal = document.getElementById('username-modal'); |
| const newUsernameInput = document.getElementById('new-username'); |
| const saveUsernameBtn = document.getElementById('save-username-btn'); |
| const cancelUsernameBtn = document.getElementById('cancel-username-btn'); |
| const passwordModal = document.getElementById('password-modal'); |
| const currentPasswordInput = document.getElementById('current-password'); |
| const newPasswordInput = document.getElementById('new-password'); |
| const savePasswordBtn = document.getElementById('save-password-btn'); |
| const cancelPasswordBtn = document.getElementById('cancel-password-btn'); |
| const helpModal = document.getElementById('help-modal'); |
| const helpBtn = document.getElementById('help-btn'); |
| const closeHelpBtn = document.getElementById('close-help-btn'); |
| const registerSuccessModal = document.getElementById('register-success-modal'); |
| const registeredId = document.getElementById('registered-id'); |
| const registeredUsername = document.getElementById('registered-username'); |
| const okRegisterBtn = document.getElementById('ok-register-btn'); |
| const messageCount = document.getElementById('message-count'); |
| const userCount = document.getElementById('user-count'); |
| const messagesSent = document.getElementById('messages-sent'); |
| const messageLimit = document.getElementById('message-limit'); |
| |
| |
| showRegisterBtn.addEventListener('click', () => { |
| loginScreen.classList.add('hidden'); |
| registerScreen.classList.remove('hidden'); |
| }); |
| |
| showLoginBtn.addEventListener('click', () => { |
| registerScreen.classList.add('hidden'); |
| loginScreen.classList.remove('hidden'); |
| }); |
| |
| loginBtn.addEventListener('click', handleLogin); |
| registerBtn.addEventListener('click', handleRegister); |
| logoutBtn.addEventListener('click', handleLogout); |
| sendBtn.addEventListener('click', sendMessage); |
| messageInput.addEventListener('keypress', (e) => { |
| if (e.key === 'Enter') { |
| sendMessage(); |
| } |
| }); |
| changeUsernameBtn.addEventListener('click', () => { |
| newUsernameInput.value = currentUser.username; |
| usernameModal.classList.remove('hidden'); |
| }); |
| changePasswordBtn.addEventListener('click', () => { |
| currentPasswordInput.value = ''; |
| newPasswordInput.value = ''; |
| passwordModal.classList.remove('hidden'); |
| }); |
| saveUsernameBtn.addEventListener('click', saveUsername); |
| cancelUsernameBtn.addEventListener('click', () => usernameModal.classList.add('hidden')); |
| savePasswordBtn.addEventListener('click', savePassword); |
| cancelPasswordBtn.addEventListener('click', () => passwordModal.classList.add('hidden')); |
| executeCommandBtn.addEventListener('click', executeAdminCommand); |
| adminCommand.addEventListener('keypress', (e) => { |
| if (e.key === 'Enter') { |
| executeAdminCommand(); |
| } |
| }); |
| helpBtn.addEventListener('click', () => helpModal.classList.remove('hidden')); |
| closeHelpBtn.addEventListener('click', () => helpModal.classList.add('hidden')); |
| okRegisterBtn.addEventListener('click', () => { |
| registerSuccessModal.classList.add('hidden'); |
| registerScreen.classList.add('hidden'); |
| loginScreen.classList.remove('hidden'); |
| }); |
| |
| |
| function handleLogin() { |
| const id = parseInt(loginIdInput.value); |
| const password = loginPasswordInput.value; |
| |
| if (isNaN(id)) { |
| showAlert('Please enter a valid ID'); |
| return; |
| } |
| |
| if (!password) { |
| showAlert('Please enter your password'); |
| return; |
| } |
| |
| const user = data.users.find(u => u.id === id); |
| |
| if (!user) { |
| showAlert('User not found'); |
| return; |
| } |
| |
| |
| if (data.mutedUsers[user.id] && new Date(data.mutedUsers[user.id]) > new Date()) { |
| const muteTime = new Date(data.mutedUsers[user.id]); |
| showAlert(`You are muted until ${muteTime.toLocaleString()}`); |
| return; |
| } |
| |
| |
| if (data.mutedIPs[currentIP] && new Date(data.mutedIPs[currentIP]) > new Date()) { |
| const muteTime = new Date(data.mutedIPs[currentIP]); |
| showAlert(`Your IP is muted until ${muteTime.toLocaleString()}`); |
| return; |
| } |
| |
| if (user.password !== password) { |
| showAlert('Incorrect password'); |
| return; |
| } |
| |
| currentUser = user; |
| loginSuccess(); |
| } |
| |
| function handleRegister() { |
| const username = registerUsernameInput.value.trim(); |
| const password = registerPasswordInput.value; |
| |
| if (!username) { |
| showAlert('Please enter a username'); |
| return; |
| } |
| |
| if (username.length > 20) { |
| showAlert('Username must be 20 characters or less'); |
| return; |
| } |
| |
| if (!password) { |
| showAlert('Please enter a password'); |
| return; |
| } |
| |
| if (password.length > 20) { |
| showAlert('Password must be 20 characters or less'); |
| return; |
| } |
| |
| |
| const now = new Date(); |
| const month = now.getFullYear() + '-' + now.getMonth(); |
| |
| if (!data.deviceRegistrations[currentDeviceId]) { |
| data.deviceRegistrations[currentDeviceId] = {}; |
| } |
| |
| if (!data.deviceRegistrations[currentDeviceId][month]) { |
| data.deviceRegistrations[currentDeviceId][month] = 0; |
| } |
| |
| if (data.deviceRegistrations[currentDeviceId][month] >= 2) { |
| showAlert('You can only create 2 accounts per month from this device'); |
| return; |
| } |
| |
| |
| const newId = data.users.length > 0 ? Math.max(...data.users.map(u => u.id)) + 1 : 1; |
| const newUser = { |
| id: newId, |
| username, |
| password, |
| registered: new Date().toISOString(), |
| ip: currentIP, |
| deviceId: currentDeviceId |
| }; |
| |
| data.users.push(newUser); |
| data.deviceRegistrations[currentDeviceId][month]++; |
| saveData(); |
| |
| |
| registeredId.textContent = newId; |
| registeredUsername.textContent = username; |
| registerSuccessModal.classList.remove('hidden'); |
| } |
| |
| function loginSuccess() { |
| loginScreen.classList.add('hidden'); |
| registerScreen.classList.add('hidden'); |
| chatScreen.classList.remove('hidden'); |
| |
| usernameDisplay.textContent = currentUser.username; |
| userInfo.classList.remove('hidden'); |
| |
| userIdDisplay.textContent = currentUser.id; |
| userNameDisplay.textContent = currentUser.username; |
| userRegistered.textContent = new Date(currentUser.registered).toLocaleString(); |
| |
| |
| if (currentUser.id === 0) { |
| adminPanel.classList.remove('hidden'); |
| } else { |
| adminPanel.classList.add('hidden'); |
| } |
| |
| |
| if (!data.messageCounters[currentUser.id]) { |
| data.messageCounters[currentUser.id] = { |
| count: 0, |
| lastReset: new Date().toISOString() |
| }; |
| } |
| |
| |
| updateMessageCounter(); |
| updateStats(); |
| loadMessages(); |
| } |
| |
| function handleLogout() { |
| currentUser = null; |
| chatScreen.classList.add('hidden'); |
| loginScreen.classList.remove('hidden'); |
| userInfo.classList.add('hidden'); |
| loginIdInput.value = ''; |
| loginPasswordInput.value = ''; |
| messageInput.value = ''; |
| } |
| |
| function sendMessage() { |
| if (!currentUser) return; |
| |
| const text = messageInput.value.trim(); |
| if (!text) return; |
| |
| |
| if (data.mutedUsers[currentUser.id] && new Date(data.mutedUsers[currentUser.id]) > new Date()) { |
| const muteTime = new Date(data.mutedUsers[currentUser.id]); |
| showAlert(`You are muted until ${muteTime.toLocaleString()}`); |
| return; |
| } |
| |
| |
| if (data.mutedIPs[currentIP] && new Date(data.mutedIPs[currentIP]) > new Date()) { |
| const muteTime = new Date(data.mutedIPs[currentIP]); |
| showAlert(`Your IP is muted until ${muteTime.toLocaleString()}`); |
| return; |
| } |
| |
| |
| const now = new Date(); |
| const lastReset = new Date(data.messageCounters[currentUser.id].lastReset); |
| const hoursDiff = Math.abs(now - lastReset) / 36e5; |
| |
| if (hoursDiff >= 1) { |
| |
| data.messageCounters[currentUser.id] = { |
| count: 0, |
| lastReset: now.toISOString() |
| }; |
| } |
| |
| if (data.messageCounters[currentUser.id].count >= data.messageLimit) { |
| showAlert(`You can only send ${data.messageLimit} messages per hour`); |
| return; |
| } |
| |
| |
| const message = { |
| id: data.nextMessageId++, |
| userId: currentUser.id, |
| username: currentUser.username, |
| text, |
| timestamp: now.toISOString(), |
| ip: currentIP |
| }; |
| |
| data.messages.push(message); |
| data.messageCounters[currentUser.id].count++; |
| saveData(); |
| |
| |
| messageInput.value = ''; |
| appendMessage(message); |
| updateMessageCounter(); |
| updateStats(); |
| } |
| |
| function loadMessages() { |
| chatMessages.innerHTML = ''; |
| data.messages.forEach(message => { |
| appendMessage(message); |
| }); |
| } |
| |
| function appendMessage(message) { |
| const user = data.users.find(u => u.id === message.userId); |
| if (!user) return; |
| |
| const messageDiv = document.createElement('div'); |
| messageDiv.className = 'message-animation bg-gray-50 p-3 rounded-lg border border-gray-200'; |
| |
| const header = document.createElement('div'); |
| header.className = 'flex justify-between items-center text-sm mb-1'; |
| |
| const userInfo = document.createElement('span'); |
| userInfo.className = 'font-semibold'; |
| userInfo.textContent = `${user.username} / ${user.id} / m${message.id}`; |
| |
| const time = document.createElement('span'); |
| time.className = 'text-gray-500'; |
| time.textContent = new Date(message.timestamp).toLocaleTimeString(); |
| |
| header.appendChild(userInfo); |
| header.appendChild(time); |
| |
| const content = document.createElement('div'); |
| content.className = 'text-gray-800'; |
| content.textContent = message.text; |
| |
| messageDiv.appendChild(header); |
| messageDiv.appendChild(content); |
| |
| chatMessages.appendChild(messageDiv); |
| chatMessages.scrollTop = chatMessages.scrollHeight; |
| } |
| |
| function updateMessageCounter() { |
| if (!currentUser) return; |
| |
| const now = new Date(); |
| const lastReset = new Date(data.messageCounters[currentUser.id].lastReset); |
| const hoursDiff = Math.abs(now - lastReset) / 36e5; |
| |
| if (hoursDiff >= 1) { |
| |
| data.messageCounters[currentUser.id] = { |
| count: 0, |
| lastReset: now.toISOString() |
| }; |
| saveData(); |
| } |
| |
| messagesSent.textContent = data.messageCounters[currentUser.id].count; |
| messageLimit.textContent = data.messageLimit; |
| } |
| |
| function updateStats() { |
| messageCount.textContent = data.messages.length; |
| userCount.textContent = data.users.length; |
| } |
| |
| function saveUsername() { |
| const newUsername = newUsernameInput.value.trim(); |
| |
| if (!newUsername) { |
| showAlert('Please enter a username'); |
| return; |
| } |
| |
| if (newUsername.length > 20) { |
| showAlert('Username must be 20 characters or less'); |
| return; |
| } |
| |
| currentUser.username = newUsername; |
| usernameDisplay.textContent = newUsername; |
| userNameDisplay.textContent = newUsername; |
| |
| |
| data.messages.forEach(message => { |
| if (message.userId === currentUser.id) { |
| message.username = newUsername; |
| } |
| }); |
| |
| saveData(); |
| usernameModal.classList.add('hidden'); |
| loadMessages(); |
| showAlert('Username updated successfully'); |
| } |
| |
| function savePassword() { |
| const currentPassword = currentPasswordInput.value; |
| const newPassword = newPasswordInput.value; |
| |
| if (!currentPassword) { |
| showAlert('Please enter your current password'); |
| return; |
| } |
| |
| if (currentUser.password !== currentPassword) { |
| showAlert('Current password is incorrect'); |
| return; |
| } |
| |
| if (!newPassword) { |
| showAlert('Please enter a new password'); |
| return; |
| } |
| |
| if (newPassword.length > 20) { |
| showAlert('Password must be 20 characters or less'); |
| return; |
| } |
| |
| currentUser.password = newPassword; |
| saveData(); |
| passwordModal.classList.add('hidden'); |
| showAlert('Password updated successfully'); |
| } |
| |
| function executeAdminCommand() { |
| if (!currentUser || currentUser.id !== 0) return; |
| |
| const command = adminCommand.value.trim(); |
| if (!command) return; |
| |
| adminCommand.value = ''; |
| |
| const parts = command.split(' '); |
| const cmd = parts[0].toLowerCase(); |
| const args = parts.slice(1); |
| |
| let response = ''; |
| |
| switch (cmd) { |
| case '/mute': |
| if (args.length !== 2) { |
| response = 'Usage: /mute id hours'; |
| break; |
| } |
| const muteId = parseInt(args[0]); |
| const muteHours = parseInt(args[1]); |
| |
| if (isNaN(muteId) || isNaN(muteHours) || muteHours <= 0) { |
| response = 'Invalid arguments'; |
| break; |
| } |
| |
| if (muteId === 0) { |
| response = 'Cannot mute admin'; |
| break; |
| } |
| |
| const muteUser = data.users.find(u => u.id === muteId); |
| if (!muteUser) { |
| response = 'User not found'; |
| break; |
| } |
| |
| const muteTime = new Date(); |
| muteTime.setHours(muteTime.getHours() + muteHours); |
| data.mutedUsers[muteId] = muteTime.toISOString(); |
| saveData(); |
| response = `User ${muteUser.username} (ID: ${muteId}) muted for ${muteHours} hours`; |
| break; |
| |
| case '/unmute': |
| if (args.length !== 1) { |
| response = 'Usage: /unmute id'; |
| break; |
| } |
| const unmuteId = parseInt(args[0]); |
| |
| if (isNaN(unmuteId)) { |
| response = 'Invalid ID'; |
| break; |
| } |
| |
| if (!data.mutedUsers[unmuteId]) { |
| response = 'User is not muted'; |
| break; |
| } |
| |
| delete data.mutedUsers[unmuteId]; |
| saveData(); |
| response = `User ID ${unmuteId} unmuted`; |
| break; |
| |
| case '/del_m': |
| if (args.length !== 1) { |
| response = 'Usage: /del_m mid'; |
| break; |
| } |
| const messageId = parseInt(args[0]); |
| |
| if (isNaN(messageId)) { |
| response = 'Invalid message ID'; |
| break; |
| } |
| |
| const messageIndex = data.messages.findIndex(m => m.id === messageId); |
| if (messageIndex === -1) { |
| response = 'Message not found'; |
| break; |
| } |
| |
| data.messages.splice(messageIndex, 1); |
| saveData(); |
| loadMessages(); |
| response = `Message m${messageId} deleted`; |
| break; |
| |
| case '/del_um': |
| if (args.length !== 1) { |
| response = 'Usage: /del_um id'; |
| break; |
| } |
| const userId = parseInt(args[0]); |
| |
| if (isNaN(userId)) { |
| response = 'Invalid user ID'; |
| break; |
| } |
| |
| const userMessages = data.messages.filter(m => m.userId === userId); |
| if (userMessages.length === 0) { |
| response = 'No messages found for this user'; |
| break; |
| } |
| |
| data.messages = data.messages.filter(m => m.userId !== userId); |
| saveData(); |
| loadMessages(); |
| response = `Deleted ${userMessages.length} messages from user ID ${userId}`; |
| break; |
| |
| case '/limit_m': |
| if (args.length !== 1) { |
| response = 'Usage: /limit_m x'; |
| break; |
| } |
| const newLimit = parseInt(args[0]); |
| |
| if (isNaN(newLimit) || newLimit <= 0) { |
| response = 'Invalid limit'; |
| break; |
| } |
| |
| data.messageLimit = newLimit; |
| saveData(); |
| updateMessageCounter(); |
| response = `Message limit set to ${newLimit} per hour`; |
| break; |
| |
| case '/limit_reset': |
| for (const userId in data.messageCounters) { |
| data.messageCounters[userId] = { |
| count: 0, |
| lastReset: new Date().toISOString() |
| }; |
| } |
| saveData(); |
| updateMessageCounter(); |
| response = 'All message counters reset'; |
| break; |
| |
| case '/vipe_chat': |
| data.messages = []; |
| saveData(); |
| loadMessages(); |
| response = 'Chat cleared'; |
| break; |
| |
| case '/vipe_all': |
| data.messages = []; |
| data.users = data.users.filter(u => u.id === 0); |
| data.mutedUsers = {}; |
| data.mutedIPs = {}; |
| data.messageCounters = {}; |
| data.deviceRegistrations = {}; |
| data.messageLimit = 10; |
| data.nextMessageId = 1; |
| saveData(); |
| loadMessages(); |
| response = 'All data cleared except admin account'; |
| break; |
| |
| case '/change_n': |
| if (args.length < 2) { |
| response = 'Usage: /change_n id new_username'; |
| break; |
| } |
| const changeId = parseInt(args[0]); |
| const newName = args.slice(1).join(' '); |
| |
| if (isNaN(changeId)) { |
| response = 'Invalid ID'; |
| break; |
| } |
| |
| if (newName.length > 20) { |
| response = 'Username must be 20 characters or less'; |
| break; |
| } |
| |
| const changeUser = data.users.find(u => u.id === changeId); |
| if (!changeUser) { |
| response = 'User not found'; |
| break; |
| } |
| |
| changeUser.username = newName; |
| |
| |
| data.messages.forEach(message => { |
| if (message.userId === changeId) { |
| message.username = newName; |
| } |
| }); |
| |
| saveData(); |
| loadMessages(); |
| |
| if (currentUser.id === changeId) { |
| usernameDisplay.textContent = newName; |
| userNameDisplay.textContent = newName; |
| } |
| |
| response = `Username for ID ${changeId} changed to "${newName}"`; |
| break; |
| |
| case '/change_p': |
| if (args.length !== 2) { |
| response = 'Usage: /change_p id new_password'; |
| break; |
| } |
| const passId = parseInt(args[0]); |
| const newPass = args[1]; |
| |
| if (isNaN(passId)) { |
| response = 'Invalid ID'; |
| break; |
| } |
| |
| if (newPass.length > 20) { |
| response = 'Password must be 20 characters or less'; |
| break; |
| } |
| |
| const passUser = data.users.find(u => u.id === passId); |
| if (!passUser) { |
| response = 'User not found'; |
| break; |
| } |
| |
| passUser.password = newPass; |
| saveData(); |
| response = `Password for ID ${passId} changed`; |
| break; |
| |
| case '/check_ip': |
| if (args.length !== 1) { |
| response = 'Usage: /check_ip mid'; |
| break; |
| } |
| const checkId = parseInt(args[0]); |
| |
| if (isNaN(checkId)) { |
| response = 'Invalid message ID'; |
| break; |
| } |
| |
| const checkMessage = data.messages.find(m => m.id === checkId); |
| if (!checkMessage) { |
| response = 'Message not found'; |
| break; |
| } |
| |
| response = `Message m${checkId} was sent from IP: ${checkMessage.ip}`; |
| break; |
| |
| case '/mute_ip': |
| if (args.length !== 2) { |
| response = 'Usage: /mute_ip ip hours'; |
| break; |
| } |
| const muteIp = args[0]; |
| const muteIpHours = parseInt(args[1]); |
| |
| if (isNaN(muteIpHours) || muteIpHours <= 0) { |
| response = 'Invalid hours'; |
| break; |
| } |
| |
| const muteIpTime = new Date(); |
| muteIpTime.setHours(muteIpTime.getHours() + muteIpHours); |
| data.mutedIPs[muteIp] = muteIpTime.toISOString(); |
| saveData(); |
| response = `IP ${muteIp} muted for ${muteIpHours} hours`; |
| break; |
| |
| case '/unmute_ip': |
| if (args.length !== 1) { |
| response = 'Usage: /unmute_ip ip'; |
| break; |
| } |
| const unmuteIp = args[0]; |
| |
| if (!data.mutedIPs[unmuteIp]) { |
| response = 'IP is not muted'; |
| break; |
| } |
| |
| delete data.mutedIPs[unmuteIp]; |
| saveData(); |
| response = `IP ${unmuteIp} unmuted`; |
| break; |
| |
| case '/list': |
| const mutedUsersList = Object.entries(data.mutedUsers) |
| .map(([id, time]) => { |
| const user = data.users.find(u => u.id === parseInt(id)); |
| const username = user ? user.username : 'Unknown'; |
| return `User: ${username} (ID: ${id}) until ${new Date(time).toLocaleString()}`; |
| }) |
| .join('\n'); |
| |
| const mutedIPsList = Object.entries(data.mutedIPs) |
| .map(([ip, time]) => `IP: ${ip} until ${new Date(time).toLocaleString()}`) |
| .join('\n'); |
| |
| response = 'Muted Users:\n' + (mutedUsersList || 'None') + '\n\nMuted IPs:\n' + (mutedIPsList || 'None'); |
| break; |
| |
| case '/help': |
| helpModal.classList.remove('hidden'); |
| break; |
| |
| default: |
| response = 'Unknown command. Type /help for available commands'; |
| } |
| |
| if (response) { |
| showAlert(response); |
| } |
| } |
| |
| function showAlert(message) { |
| const alertDiv = document.createElement('div'); |
| alertDiv.className = 'fixed bottom-4 right-4 bg-gray-800 text-white px-4 py-3 rounded shadow-lg message-animation'; |
| alertDiv.textContent = message; |
| |
| document.body.appendChild(alertDiv); |
| |
| setTimeout(() => { |
| alertDiv.classList.add('opacity-0', 'transition-opacity', 'duration-300'); |
| setTimeout(() => { |
| document.body.removeChild(alertDiv); |
| }, 300); |
| }, 3000); |
| } |
| |
| |
| updateStats(); |
| </script> |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Dapshen/null-v-0" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> |
| </html> |