sapbot

Modern Messenger

Feb 8th, 2026
60
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. const express = require('express');
  2. const http = require('http');
  3. const { Server } = require('socket.io');
  4. const path = require('path');
  5.  
  6. const app = express();
  7. const server = http.createServer(app);
  8. const io = new Server(server);
  9.  
  10. app.use(express.json());
  11. app.use(express.static('public'));
  12.  
  13. // In-memory storage
  14. const chats = new Map();
  15. const users = new Map();
  16.  
  17. // Translations
  18. const translations = {
  19.   en: {
  20.     title: 'Modern Messenger',
  21.     setUsername: 'Set Username',
  22.     usernamePlaceholder: 'Your username',
  23.     setBtn: 'Set',
  24.     noChats: 'No chats yet. Create one!',
  25.     enterUsernameToSee: 'Enter username to see chats',
  26.     newChatName: 'New chat name',
  27.     create: 'Create',
  28.     privateChat: 'Private chat (requires password)',
  29.     passwordPlaceholder: 'Set password',
  30.     welcome: 'Welcome to Modern Messenger',
  31.     welcomeDesc: 'Set your username and create or join a chat to start messaging',
  32.     online: 'online',
  33.     joined: 'Joined',
  34.     join: 'Join',
  35.     joinLock: 'Join 🔒',
  36.     typeMessage: 'Type a message...',
  37.     send: 'Send',
  38.     privateChatPrompt: 'This is a private chat. Enter password:',
  39.     incorrectPassword: 'Incorrect password',
  40.     chatNotFound: 'Chat not found',
  41.     setUsernameFirst: 'Please set username first',
  42.     setPasswordPrivate: 'Please set a password for private chat',
  43.     enterUsername: 'Please enter a username',
  44.     systemJoined: 'joined the chat',
  45.     language: 'Language:',
  46.     privateChatTitle: '🔒 Private Chat',
  47.     privateChatDesc: 'This chat requires a password to join',
  48.     enterPassword: 'Enter password',
  49.     cancel: 'Cancel',
  50.     joinChat: 'Join Chat',
  51.     passwordRequired: 'Password is required'
  52.   },
  53.   ru: {
  54.     title: 'Современный Мессенджер',
  55.     setUsername: 'Установить имя',
  56.     usernamePlaceholder: 'Ваше имя',
  57.     setBtn: 'Установить',
  58.     noChats: 'Нет чатов. Создайте первый!',
  59.     enterUsernameToSee: 'Введите имя для просмотра чатов',
  60.     newChatName: 'Название чата',
  61.     create: 'Создать',
  62.     privateChat: 'Приватный чат (нужен пароль)',
  63.     passwordPlaceholder: 'Установить пароль',
  64.     welcome: 'Добро пожаловать в Мессенджер',
  65.     welcomeDesc: 'Установите имя и создайте или присоединитесь к чату',
  66.     online: 'онлайн',
  67.     joined: 'Присоединились',
  68.     join: 'Войти',
  69.     joinLock: 'Войти 🔒',
  70.     typeMessage: 'Введите сообщение...',
  71.     send: 'Отправить',
  72.     privateChatPrompt: 'Это приватный чат. Введите пароль:',
  73.     incorrectPassword: 'Неверный пароль',
  74.     chatNotFound: 'Чат не найден',
  75.     setUsernameFirst: 'Сначала установите имя',
  76.     setPasswordPrivate: 'Установите пароль для приватного чата',
  77.     enterUsername: 'Введите имя',
  78.     systemJoined: 'присоединился к чату',
  79.     language: 'Язык:',
  80.     privateChatTitle: '🔒 Приватный чат',
  81.     privateChatDesc: 'Для входа в этот чат требуется пароль',
  82.     enterPassword: 'Введите пароль',
  83.     cancel: 'Отмена',
  84.     joinChat: 'Войти в чат',
  85.     passwordRequired: 'Требуется пароль'
  86.   }
  87. };
  88.  
  89. // HTML/JS/CSS all in one
  90. app.get('/', (req, res) => {
  91.   res.send(`
  92. <!DOCTYPE html>
  93. <html lang="en">
  94. <head>
  95.   <meta charset="UTF-8">
  96.   <meta name="viewport" content="width=device-width, initial-scale=1.0">
  97.   <title>Modern Messenger</title>
  98.   <style>
  99.     * { margin: 0; padding: 0; box-sizing: border-box; }
  100.     body {
  101.       font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  102.       background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  103.       height: 100vh;
  104.       display: flex;
  105.       align-items: center;
  106.       justify-content: center;
  107.     }
  108.     .container {
  109.       width: 100%;
  110.       max-width: 1200px;
  111.       height: 90vh;
  112.       background: white;
  113.       border-radius: 20px;
  114.       box-shadow: 0 20px 60px rgba(0,0,0,0.3);
  115.       display: flex;
  116.       overflow: hidden;
  117.     }
  118.     .sidebar {
  119.       width: 300px;
  120.       background: #f8f9fa;
  121.       border-right: 1px solid #e9ecef;
  122.       display: flex;
  123.       flex-direction: column;
  124.     }
  125.     .sidebar-header {
  126.       padding: 20px;
  127.       background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  128.       color: white;
  129.     }
  130.     .sidebar-header h1 { font-size: 1.5rem; margin-bottom: 10px; }
  131.     .user-input {
  132.       display: flex;
  133.       gap: 10px;
  134.     }
  135.     .user-input input {
  136.       flex: 1;
  137.       padding: 8px 12px;
  138.       border: none;
  139.       border-radius: 20px;
  140.       font-size: 14px;
  141.     }
  142.     .btn {
  143.       padding: 8px 16px;
  144.       border: none;
  145.       border-radius: 20px;
  146.       cursor: pointer;
  147.       font-size: 14px;
  148.       transition: all 0.3s;
  149.     }
  150.     .btn-primary {
  151.       background: #667eea;
  152.       color: white;
  153.     }
  154.     .btn-primary:hover { background: #5568d3; }
  155.     .btn-success {
  156.       background: #28a745;
  157.       color: white;
  158.     }
  159.     .btn-success:hover { background: #218838; }
  160.     .btn-secondary {
  161.       background: #6c757d;
  162.       color: white;
  163.     }
  164.     .btn-secondary:hover { background: #5a6268; }
  165.     .chat-list {
  166.       flex: 1;
  167.       overflow-y: auto;
  168.       padding: 10px;
  169.     }
  170.     .chat-item {
  171.       padding: 15px;
  172.       border-radius: 12px;
  173.       cursor: pointer;
  174.       transition: all 0.2s;
  175.       margin-bottom: 8px;
  176.       display: flex;
  177.       justify-content: space-between;
  178.       align-items: center;
  179.     }
  180.     .chat-item:hover { background: #e9ecef; }
  181.     .chat-item.active { background: #667eea; color: white; }
  182.     .chat-item span { font-weight: 500; }
  183.     .chat-item small { opacity: 0.7; font-size: 12px; }
  184.     .private-badge {
  185.       font-size: 10px;
  186.       background: #dc3545;
  187.       color: white;
  188.       padding: 2px 6px;
  189.       border-radius: 8px;
  190.       margin-left: 5px;
  191.     }
  192.     .new-chat {
  193.       padding: 15px;
  194.       border-top: 1px solid #e9ecef;
  195.     }
  196.     .new-chat-input {
  197.       display: flex;
  198.       gap: 10px;
  199.       margin-bottom: 10px;
  200.     }
  201.     .new-chat-input input {
  202.       flex: 1;
  203.       padding: 10px;
  204.       border: 1px solid #ddd;
  205.       border-radius: 20px;
  206.       font-size: 14px;
  207.     }
  208.     .private-option {
  209.       display: flex;
  210.       align-items: center;
  211.       gap: 8px;
  212.       font-size: 12px;
  213.       color: #666;
  214.     }
  215.     .private-option input[type="checkbox"] {
  216.       width: 16px;
  217.       height: 16px;
  218.     }
  219.     .main {
  220.       flex: 1;
  221.       display: flex;
  222.       flex-direction: column;
  223.     }
  224.     .chat-header {
  225.       padding: 20px;
  226.       border-bottom: 1px solid #e9ecef;
  227.       display: flex;
  228.       justify-content: space-between;
  229.       align-items: center;
  230.     }
  231.     .chat-header h2 { color: #333; }
  232.     .online-count { color: #28a745; font-size: 14px; }
  233.     .messages {
  234.       flex: 1;
  235.       overflow-y: auto;
  236.       padding: 20px;
  237.       background: #fafafa;
  238.     }
  239.     .message {
  240.       max-width: 70%;
  241.       margin-bottom: 15px;
  242.       animation: fadeIn 0.3s ease;
  243.     }
  244.     @keyframes fadeIn {
  245.       from { opacity: 0; transform: translateY(10px); }
  246.       to { opacity: 1; transform: translateY(0); }
  247.     }
  248.     .message.own { margin-left: auto; }
  249.     .message-bubble {
  250.       padding: 12px 16px;
  251.       border-radius: 18px;
  252.       word-wrap: break-word;
  253.     }
  254.     .message:not(.own) .message-bubble {
  255.       background: white;
  256.       border: 1px solid #e9ecef;
  257.       border-bottom-left-radius: 4px;
  258.     }
  259.     .message.own .message-bubble {
  260.       background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  261.       color: white;
  262.       border-bottom-right-radius: 4px;
  263.     }
  264.     .message-info {
  265.       font-size: 12px;
  266.       color: #888;
  267.       margin-top: 4px;
  268.       display: flex;
  269.       gap: 8px;
  270.     }
  271.     .message.own .message-info { justify-content: flex-end; }
  272.     .input-area {
  273.       padding: 20px;
  274.       border-top: 1px solid #e9ecef;
  275.       display: flex;
  276.       gap: 10px;
  277.     }
  278.     .input-area input {
  279.       flex: 1;
  280.       padding: 12px 20px;
  281.       border: 1px solid #ddd;
  282.       border-radius: 25px;
  283.       font-size: 16px;
  284.       outline: none;
  285.     }
  286.     .input-area input:focus { border-color: #667eea; }
  287.     .empty-state {
  288.       display: flex;
  289.       flex-direction: column;
  290.       align-items: center;
  291.       justify-content: center;
  292.       height: 100%;
  293.       color: #888;
  294.     }
  295.     .empty-state h3 { margin-bottom: 10px; color: #555; }
  296.     .hidden { display: none !important; }
  297.     .join-btn {
  298.       padding: 4px 12px;
  299.       background: #28a745;
  300.       color: white;
  301.       border: none;
  302.       border-radius: 12px;
  303.       cursor: pointer;
  304.       font-size: 12px;
  305.     }
  306.     .joined-badge {
  307.       padding: 4px 12px;
  308.       background: #6c757d;
  309.       color: white;
  310.       border-radius: 12px;
  311.       font-size: 12px;
  312.     }
  313.     .lang-selector {
  314.       display: flex;
  315.       align-items: center;
  316.       gap: 8px;
  317.       margin-bottom: 10px;
  318.       font-size: 12px;
  319.     }
  320.     .lang-selector select {
  321.       padding: 4px 8px;
  322.       border-radius: 8px;
  323.       border: 1px solid rgba(255,255,255,0.3);
  324.       background: rgba(255,255,255,0.2);
  325.       color: white;
  326.       cursor: pointer;
  327.     }
  328.     .lang-selector select option {
  329.       color: #333;
  330.     }
  331.   </style>
  332. </head>
  333. <body>
  334.   <div class="container">
  335.     <div class="sidebar">
  336.       <div class="sidebar-header">
  337.         <div class="lang-selector">
  338.           <span id="langLabel">Language:</span>
  339.           <select id="langSelect">
  340.             <option value="en">English</option>
  341.             <option value="ru">Русский</option>
  342.           </select>
  343.         </div>
  344.         <h1>💬 Messenger</h1>
  345.         <div class="user-input">
  346.           <input type="text" id="usernameInput" placeholder="Your username" maxlength="20">
  347.           <button class="btn btn-primary" id="setUsernameBtn">Set</button>
  348.         </div>
  349.       </div>
  350.       <div class="chat-list" id="chatList">
  351.         <div style="text-align: center; padding: 20px; color: #888;" id="noChatsMsg">
  352.           Enter username to see chats
  353.         </div>
  354.       </div>
  355.       <div class="new-chat">
  356.         <div class="new-chat-input">
  357.           <input type="text" id="newChatName" placeholder="New chat name" maxlength="30">
  358.           <button class="btn btn-success" id="createChatBtn">Create</button>
  359.         </div>
  360.         <label class="private-option">
  361.           <input type="checkbox" id="isPrivate">
  362.           <span id="privateChatLabel">Private chat (requires password)</span>
  363.         </label>
  364.         <div class="new-chat-input" id="passwordField" style="display: none; margin-top: 10px;">
  365.           <input type="password" id="chatPassword" placeholder="Set password" maxlength="30">
  366.         </div>
  367.       </div>
  368.     </div>
  369.     <div class="main" id="mainArea">
  370.       <div class="empty-state">
  371.         <h3 id="welcomeTitle">Welcome to Modern Messenger</h3>
  372.         <p id="welcomeDesc">Set your username and create or join a chat to start messaging</p>
  373.       </div>
  374.     </div>
  375.   </div>
  376.  
  377.   <script src="/socket.io/socket.io.js"></script>
  378.   <script>
  379.     (function() {
  380.       const socket = io();
  381.       let currentUser = '';
  382.       let currentChat = '';
  383.       let joinedChats = new Set();
  384.       let chatCache = {};
  385.       let currentLang = 'en';
  386.  
  387.       const translations = ${JSON.stringify(translations)};
  388.  
  389.       function t(key) {
  390.         return translations[currentLang][key] || translations['en'][key] || key;
  391.       }
  392.  
  393.       function updateLanguage() {
  394.         // Update static elements
  395.         document.getElementById('langLabel').textContent = t('language');
  396.         document.getElementById('usernameInput').placeholder = t('usernamePlaceholder');
  397.         document.getElementById('setUsernameBtn').textContent = t('setBtn');
  398.         document.getElementById('noChatsMsg').textContent = t('enterUsernameToSee');
  399.         document.getElementById('newChatName').placeholder = t('newChatName');
  400.         document.getElementById('createChatBtn').textContent = t('create');
  401.         document.getElementById('privateChatLabel').textContent = t('privateChat');
  402.         document.getElementById('chatPassword').placeholder = t('passwordPlaceholder');
  403.         document.getElementById('welcomeTitle').textContent = t('welcome');
  404.         document.getElementById('welcomeDesc').textContent = t('welcomeDesc');
  405.        
  406.         // Refresh chat list if visible
  407.         if (currentUser) {
  408.           loadChats();
  409.         }
  410.        
  411.         // Refresh current chat if open
  412.         if (currentChat && joinedChats.has(currentChat)) {
  413.           selectChat(currentChat);
  414.         }
  415.       }
  416.  
  417.       function setUsername() {
  418.         const usernameInput = document.getElementById('usernameInput');
  419.         const username = usernameInput.value.trim();
  420.         if (!username) return alert(t('enterUsername'));
  421.         currentUser = username;
  422.         socket.emit('set-username', username);
  423.         usernameInput.disabled = true;
  424.         document.getElementById('setUsernameBtn').disabled = true;
  425.         loadChats();
  426.       }
  427.  
  428.       function loadChats() {
  429.         fetch('/chats')
  430.           .then(function(r) { return r.json(); })
  431.           .then(function(data) {
  432.             chatCache = {};
  433.             data.chats.forEach(function(c) { chatCache[c.id] = c; });
  434.             renderChatList(data.chats);
  435.           });
  436.       }
  437.  
  438.       function renderChatList(chats) {
  439.         const list = document.getElementById('chatList');
  440.         if (chats.length === 0) {
  441.           list.innerHTML = '<div style="text-align: center; padding: 20px; color: #888;">' + t('noChats') + '</div>';
  442.           return;
  443.         }
  444.         list.innerHTML = '';
  445.         for (let i = 0; i < chats.length; i++) {
  446.           const chat = chats[i];
  447.           const div = document.createElement('div');
  448.           div.className = 'chat-item' + (currentChat === chat.id ? ' active' : '');
  449.          
  450.           const privateIcon = chat.isPrivate ? '<span class="private-badge">🔒</span>' : '';
  451.           let joinBtnText = '';
  452.           if (joinedChats.has(chat.id)) {
  453.             joinBtnText = t('joined');
  454.           } else if (chat.isPrivate) {
  455.             joinBtnText = t('joinLock');
  456.           } else {
  457.             joinBtnText = t('join');
  458.           }
  459.          
  460.           const joinBtnClass = joinedChats.has(chat.id) ? 'joined-badge' : 'join-btn';
  461.           const joinBtnStyle = chat.isPrivate && !joinedChats.has(chat.id) ? ' style="background: #dc3545;"' : '';
  462.          
  463.           div.innerHTML = '<div><span>#' + chat.name + privateIcon + '</span><br><small>' + chat.userCount + ' ' + t('online') + '</small></div>' +
  464.             '<button class="' + joinBtnClass + '"' + joinBtnStyle + '>' + joinBtnText + '</button>';
  465.          
  466.           div.onclick = function() {
  467.             selectChat(chat.id);
  468.           };
  469.          
  470.           const btn = div.querySelector('button');
  471.           if (btn && !joinedChats.has(chat.id)) {
  472.             btn.onclick = function(e) {
  473.               e.stopPropagation();
  474.               if (chat.isPrivate) {
  475.                 const password = prompt(t('privateChatPrompt'));
  476.                 if (password !== null) {
  477.                   joinChat(chat.id, password);
  478.                 }
  479.               } else {
  480.                 joinChat(chat.id, '');
  481.               }
  482.             };
  483.           }
  484.          
  485.           list.appendChild(div);
  486.         }
  487.       }
  488.  
  489.       function togglePasswordField() {
  490.         const isChecked = document.getElementById('isPrivate').checked;
  491.         document.getElementById('passwordField').style.display = isChecked ? 'flex' : 'none';
  492.       }
  493.  
  494.       function createChat() {
  495.         if (!currentUser) return alert(t('setUsernameFirst'));
  496.         const nameInput = document.getElementById('newChatName');
  497.         const name = nameInput.value.trim();
  498.         if (!name) return;
  499.         const isPrivate = document.getElementById('isPrivate').checked;
  500.         const password = isPrivate ? document.getElementById('chatPassword').value : '';
  501.         if (isPrivate && !password) return alert(t('setPasswordPrivate'));
  502.         socket.emit('create-chat', { name: name, isPrivate: isPrivate, password: password });
  503.         nameInput.value = '';
  504.         document.getElementById('isPrivate').checked = false;
  505.         document.getElementById('chatPassword').value = '';
  506.         document.getElementById('passwordField').style.display = 'none';
  507.       }
  508.  
  509.       function joinChat(chatId, password) {
  510.         if (!currentUser) return alert(t('setUsernameFirst'));
  511.         socket.emit('join-chat', { chatId: chatId, password: password }, function(response) {
  512.           if (response && response.success) {
  513.             joinedChats.add(chatId);
  514.             selectChat(chatId);
  515.           } else {
  516.             alert(response && response.error ? response.error : t('incorrectPassword'));
  517.           }
  518.         });
  519.       }
  520.  
  521.       function selectChat(chatId) {
  522.         if (!joinedChats.has(chatId)) return;
  523.         currentChat = chatId;
  524.         loadChats();
  525.         fetch('/messages/' + chatId)
  526.           .then(function(r) { return r.json(); })
  527.           .then(function(data) { renderChat(data.messages); });
  528.       }
  529.  
  530.       function renderChat(messages) {
  531.         const chat = chatCache[currentChat];
  532.         const chatName = chat ? chat.name : 'Chat';
  533.         const privateLabel = chat && chat.isPrivate ? ' <span style="color: #dc3545;">🔒</span>' : '';
  534.        
  535.         let messagesHtml = '';
  536.         for (let i = 0; i < messages.length; i++) {
  537.           messagesHtml += renderMessage(messages[i]);
  538.         }
  539.        
  540.         const mainArea = document.getElementById('mainArea');
  541.         mainArea.innerHTML =
  542.           '<div class="chat-header">' +
  543.             '<h2>#' + chatName + privateLabel + '</h2>' +
  544.             '<span class="online-count">● ' + t('online') + '</span>' +
  545.           '</div>' +
  546.           '<div class="messages" id="messages">' + messagesHtml + '</div>' +
  547.           '<div class="input-area">' +
  548.             '<input type="text" id="messageInput" placeholder="' + t('typeMessage') + '">' +
  549.             '<button class="btn btn-primary" id="sendBtn">' + t('send') + '</button>' +
  550.           '</div>';
  551.        
  552.         const msgInput = document.getElementById('messageInput');
  553.         const sendBtn = document.getElementById('sendBtn');
  554.        
  555.         sendBtn.onclick = function() { sendMessage(); };
  556.         msgInput.onkeypress = function(e) {
  557.           if (e.key === 'Enter') sendMessage();
  558.         };
  559.         msgInput.onkeydown = function(e) {
  560.           if (e.key === 'Enter') {
  561.             e.preventDefault();
  562.             sendMessage();
  563.           }
  564.         };
  565.        
  566.         scrollToBottom();
  567.         msgInput.focus();
  568.       }
  569.  
  570.       function renderMessage(msg) {
  571.         const isOwn = msg.username === currentUser;
  572.         let username = msg.username;
  573.         if (username === 'System') {
  574.           username = currentLang === 'ru' ? 'Система' : 'System';
  575.         }
  576.         return '<div class="message ' + (isOwn ? 'own' : '') + '">' +
  577.           '<div class="message-bubble">' + escapeHtml(msg.text) + '</div>' +
  578.           '<div class="message-info">' +
  579.             '<span>' + escapeHtml(username) + '</span>' +
  580.             '<span>' + new Date(msg.timestamp).toLocaleTimeString() + '</span>' +
  581.           '</div>' +
  582.         '</div>';
  583.       }
  584.  
  585.       function sendMessage() {
  586.         const input = document.getElementById('messageInput');
  587.         if (!input) return;
  588.         const text = input.value.trim();
  589.         if (!text || !currentChat) return;
  590.         socket.emit('send-message', { chatId: currentChat, text: text });
  591.         input.value = '';
  592.       }
  593.  
  594.       function scrollToBottom() {
  595.         const messages = document.getElementById('messages');
  596.         if (messages) messages.scrollTop = messages.scrollHeight;
  597.       }
  598.  
  599.       function escapeHtml(text) {
  600.         const div = document.createElement('div');
  601.         div.textContent = text;
  602.         return div.innerHTML;
  603.       }
  604.  
  605.       // Setup event listeners
  606.       document.getElementById('langSelect').onchange = function() {
  607.         currentLang = this.value;
  608.         updateLanguage();
  609.       };
  610.      
  611.       document.getElementById('setUsernameBtn').onclick = setUsername;
  612.       document.getElementById('createChatBtn').onclick = createChat;
  613.       document.getElementById('isPrivate').onchange = togglePasswordField;
  614.       document.getElementById('usernameInput').onkeypress = function(e) {
  615.         if (e.key === 'Enter') setUsername();
  616.       };
  617.       document.getElementById('newChatName').onkeypress = function(e) {
  618.         if (e.key === 'Enter') createChat();
  619.       };
  620.  
  621.       // Socket events
  622.       socket.on('chat-created', loadChats);
  623.       socket.on('user-joined', loadChats);
  624.       socket.on('user-left', loadChats);
  625.      
  626.       socket.on('new-message', function(data) {
  627.         if (data.chatId === currentChat) {
  628.           const messages = document.getElementById('messages');
  629.           if (messages) {
  630.             messages.innerHTML += renderMessage(data.message);
  631.             scrollToBottom();
  632.           }
  633.         }
  634.       });
  635.  
  636.       // Refresh chat list periodically
  637.       setInterval(function() {
  638.         if (currentUser) loadChats();
  639.       }, 3000);
  640.     })();
  641.   </script>
  642. </body>
  643. </html>
  644.   `);
  645. });
  646.  
  647. // API Routes
  648. app.get('/chats', (req, res) => {
  649.   const chatList = Array.from(chats.values()).map(chat => ({
  650.     id: chat.id,
  651.     name: chat.name,
  652.     userCount: chat.users.size,
  653.     isPrivate: chat.isPrivate
  654.   }));
  655.   res.json({ chats: chatList });
  656. });
  657.  
  658. app.get('/messages/:chatId', (req, res) => {
  659.   const chat = chats.get(req.params.chatId);
  660.   if (!chat) return res.json({ messages: [] });
  661.   res.json({ messages: chat.messages });
  662. });
  663.  
  664. // Socket.io handling
  665. io.on('connection', (socket) => {
  666.   let username = '';
  667.   let joinedChats = new Set();
  668.  
  669.   socket.on('set-username', (name) => {
  670.     username = name;
  671.     users.set(socket.id, { username, socket });
  672.   });
  673.  
  674.   socket.on('create-chat', ({ name, isPrivate, password }) => {
  675.     const chatId = 'chat_' + Date.now();
  676.     const chat = {
  677.       id: chatId,
  678.       name: name,
  679.       isPrivate: isPrivate || false,
  680.       password: password || null,
  681.       users: new Set(),
  682.       messages: []
  683.     };
  684.     chats.set(chatId, chat);
  685.     io.emit('chat-created');
  686.   });
  687.  
  688.   socket.on('join-chat', ({ chatId, password }, callback) => {
  689.     const chat = chats.get(chatId);
  690.     if (!chat) {
  691.       return callback({ success: false, error: 'Chat not found' });
  692.     }
  693.    
  694.     // Check password for private chats
  695.     if (chat.isPrivate && chat.password !== password) {
  696.       return callback({ success: false, error: 'Incorrect password' });
  697.     }
  698.    
  699.     socket.join(chatId);
  700.     chat.users.add(socket.id);
  701.     joinedChats.add(chatId);
  702.    
  703.     socket.to(chatId).emit('user-joined');
  704.    
  705.     // Send join notification
  706.     const joinMsg = {
  707.       username: 'System',
  708.       text: username + ' joined the chat',
  709.       timestamp: Date.now()
  710.     };
  711.     chat.messages.push(joinMsg);
  712.     io.to(chatId).emit('new-message', { chatId, message: joinMsg });
  713.    
  714.     callback({ success: true });
  715.   });
  716.  
  717.   socket.on('send-message', ({ chatId, text }) => {
  718.     const chat = chats.get(chatId);
  719.     if (!chat || !joinedChats.has(chatId)) return;
  720.    
  721.     const message = {
  722.       username,
  723.       text,
  724.       timestamp: Date.now()
  725.     };
  726.     chat.messages.push(message);
  727.     io.to(chatId).emit('new-message', { chatId, message });
  728.   });
  729.  
  730.   socket.on('disconnect', () => {
  731.     users.delete(socket.id);
  732.     joinedChats.forEach(chatId => {
  733.       const chat = chats.get(chatId);
  734.       if (chat) {
  735.         chat.users.delete(socket.id);
  736.         socket.to(chatId).emit('user-left');
  737.       }
  738.     });
  739.   });
  740. });
  741.  
  742. const PORT = process.env.PORT || 3000;
  743. server.listen(PORT, () => {
  744.   console.log('🚀 Messenger running on http://localhost:' + PORT);
  745. });
Advertisement
Add Comment
Please, Sign In to add comment