{ "name": "global-chat", "version": "1.0.0", "description": "نظام شات عالمي بغرف ورتب مع دعم صوتي", "main": "server.js", "scripts": { "start": "node server.js" }, "dependencies": { "express": "^4.18.2", "socket.io": "^4.7.2", "peer": "^1.0.0" } }const express = require('express'); const http = require('http'); const socketIo = require('socket.io'); const { ExpressPeerServer } = require('peer'); const app = express(); const server = http.createServer(app); const io = socketIo(server); // إعداد PeerJS للصوت const peerServer = ExpressPeerServer(server, { debug: true, path: '/' }); app.use('/peerjs', peerServer); // خدمة الملفات الثابتة app.use(express.static('public')); // نظام الرتب const ROLES = { OWNER: 'مالك', ADMIN: 'مشرف', MOD: 'مراقب', MEMBER: 'عضو', GUEST: 'زائر' }; // بيانات الغرف والمستخدمين const rooms = new Map(); // roomId -> { name, users: Map(socketId -> userData) } // إنشاء غرفة افتراضية rooms.set('general', { name: 'الغرفة العامة', users: new Map() }); // مساعدة لإنشاء معرف فريد function generateId() { return Math.random().toString(36).substring(2, 10); } // إرسال قائمة الغرف function emitRoomsList() { const roomsList = Array.from(rooms.entries()).map(([id, room]) => ({ id, name: room.name, usersCount: room.users.size })); io.emit('rooms:list', roomsList); } // إرسال بيانات المستخدمين في غرفة function emitRoomUsers(roomId) { const room = rooms.get(roomId); if (!room) return; const users = Array.from(room.users.values()).map(u => ({ id: u.id, username: u.username, role: u.role, peerId: u.peerId })); io.to(roomId).emit('room:users', users); } // تعيين رتبة افتراضية حسب الحالة function getDefaultRole(isFirstUser) { return isFirstUser ? ROLES.OWNER : ROLES.MEMBER; } io.on('connection', (socket) => { console.log('اتصال جديد:', socket.id); let currentRoom = null; let userData = null; // إرسال قائمة الغرف عند الاتصال socket.emit('rooms:list', Array.from(rooms.entries()).map(([id, room]) => ({ id, name: room.name, usersCount: room.users.size }))); // الانضمام إلى غرفة socket.on('room:join', ({ roomId, username, peerId }) => { // مغادرة الغرفة الحالية if (currentRoom && rooms.has(currentRoom)) { const oldRoom = rooms.get(currentRoom); oldRoom.users.delete(socket.id); socket.leave(currentRoom); emitRoomUsers(currentRoom); emitRoomsList(); } // إنشاء الغرفة إذا لم تكن موجودة if (!rooms.has(roomId)) { rooms.set(roomId, { name: roomId, users: new Map() }); } const room = rooms.get(roomId); currentRoom = roomId; // تحديد الرتبة const isFirstUser = room.users.size === 0; const role = getDefaultRole(isFirstUser); userData = { id: socket.id, username: username || 'زائر_' + socket.id.substring(0, 4), role: role, peerId: peerId || null }; room.users.set(socket.id, userData); socket.join(roomId); // إرسال تأكيد الانضمام socket.emit('room:joined', { roomId, roomName: room.name, user: userData }); // إشعار باقي المستخدمين socket.to(roomId).emit('chat:message', { type: 'system', text: `${userData.username} انضم إلى الغرفة`, timestamp: Date.now() }); emitRoomUsers(roomId); emitRoomsList(); }); // استقبال رسالة نصية socket.on('chat:message', (text) => { if (!currentRoom || !userData) return; const message = { type: 'text', userId: socket.id, username: userData.username, role: userData.role, text: text.substring(0, 500), // حد أقصى 500 حرف timestamp: Date.now() }; io.to(currentRoom).emit('chat:message', message); }); // مشاركة إشارات الصوت (WebRTC signaling) socket.on('voice:signal', ({ targetPeerId, signal }) => { if (!currentRoom) return; const room = rooms.get(currentRoom); if (!room) return; const target = room.users.get(targetPeerId); if (target && target.peerId) { socket.to(currentRoom).emit('voice:signal', { fromPeerId: socket.id, signal: signal }); } }); // مغادرة الغرفة socket.on('room:leave', () => { if (currentRoom && rooms.has(currentRoom)) { const room = rooms.get(currentRoom); room.users.delete(socket.id); socket.to(currentRoom).emit('chat:message', { type: 'system', text: `${userData?.username || 'مستخدم'} غادر الغرفة`, timestamp: Date.now() }); socket.leave(currentRoom); emitRoomUsers(currentRoom); emitRoomsList(); currentRoom = null; userData = null; } }); // فصل الاتصال socket.on('disconnect', () => { if (currentRoom && rooms.has(currentRoom)) { const room = rooms.get(currentRoom); room.users.delete(socket.id); socket.to(currentRoom).emit('chat:message', { type: 'system', text: `${userData?.username || 'مستخدم'} قطع الاتصال`, timestamp: Date.now() }); emitRoomUsers(currentRoom); emitRoomsList(); } }); // إنشاء غرفة جديدة socket.on('room:create', (roomName) => { const roomId = generateId(); rooms.set(roomId, { name: roomName || 'غرفة_' + roomId, users: new Map() }); emitRoomsList(); socket.emit('room:created', { roomId, name: roomName }); }); }); const PORT = process.env.PORT || 3000; server.listen(PORT, () => { console.log(`الخادم يعمل على المنفذ ${PORT}`); });