Приватный чат на node.js — что может быть проще?

в 10:39, , рубрики: javascript, node.js, socket.io, общение, социальные сети, метки: , , ,

Привет, друзья!

Дело было вечером, делать было нечего… Разбирая пример чата в Socket.IO, заинтересовала возможности без особых ухищрений сделать чат приватным. Идея не новая, и достаточна простая — выбираешь свой пол и возраст, указываешь желаемый пол и возраст собеседника, и, поехали! Своего рода рулетка.

Итак, начнем с требований:

  1. Форма входа — указание пола и возраста (как для себя, так и для собеседника);
  2. Возможность создания приватных комнат (при отсутствии подходящего собеседника);
  3. Подключение к приватной комнате (при наличии собеседника);
  4. Анонимность (не будем сохранять никакие данные пользователя).

Что нам потребуется?

Node.JS (на момент написания — 0.6.14) c модулями express, socket.io. Дополнительно используются nib, stylus, i18n и util.

Итак, поехали.

Подключение нового пользователя

Тут все достаточно просто. Создаем объект пользователя, инкриментим счетчики, обновляем цифры у клиентов.

    var user = {};
    var uid = socket.store.id;
    var room_id = uid + "_room";
    
    online++;
    total++;
    
    socket.broadcast.emit('online', total, free);
    socket.emit('online', total, free);

    user.id = socket.store.id;
    users[socket.store.id] = user;

Вход пользователя

Под входом имеется ввиду уже вход в сам чат — после выбора требуемых параметров на начальной форме.
Сам механизм выбора собеседника реализован в виде простого цикла (не знаю, насколько это будет быстро работать при большой нагрузке). Проверяем введенные параметры, сравниваем их и при совпадении инициируем подключение к другому пользователю, либо оставляем ждать.

  socket.on('nickname', function (nick, m_sex, m_age, c_sex, c_age, fn) {
    
    if (nicknames[socket.user_id]) {
      fn(true);
    } else {
      fn(false);
     
      nicknames[socket.store.id] = socket.nickname = socket.store.id;
      socket.user_id = socket.store.id;
      socket.gender = m_sex;

      var user = users[socket.store.id];
      
      user.m_sex = m_sex;
      user.m_age = m_age;
      user.c_sex = c_sex;
      user.c_age = c_age;
      
      user.active = 0;
      
      free++;
      
      // Check existing rooms
      for(var __room in rooms){
          
          if(user.active == 0){
          
              _room = rooms[__room];

              if(_room.c_sex == m_sex && _room.c_age == m_age && _room.m_sex == c_sex && _room.m_age == c_age && _room.active == 0){
                  room_id = _room.id;
                  _room.active = 1;
                  _room.nicknames[socket.user_id] = socket.user_id;
                  user.active = 1
                  free--;
                  console.log('         room finded! this - ' + room_id);
              }
          
          }
      }
      
      // Create own room
      if(user.active == 0){
          
          console.log('         no room finded :( create own - ' + room_id);
          
          var room = {};
          
          room.id = room_id;
          
          room.m_sex = m_sex;
          room.m_age = m_age;
          room.c_sex = c_sex;
          room.c_age = c_age;
          
          room.active = 0
          room.nicknames = {};
          room.nicknames[socket.user_id] = socket.user_id;
          
          rooms[room_id] = room;

          socket.emit("user message", "System", i18n.__("waiting"));
          
      }
      
      users[socket.store.id] = user;
      
      socket.join(room_id);
      
      socket.broadcast.to(room_id).emit('announcement', socket.user_id + ' ' + i18n.__('connected'));
      socket.broadcast.to(room_id).emit('nicknames', rooms[room_id].nicknames);
      
      socket.emit('nicknames', rooms[room_id].nicknames);
    }
  });

Осталось дело за малым — собственно позволить пользователям общаться между собой.

Отправка сообщений

socket.on('user message', function (msg) {
        msg = sanitize(msg).entityEncode();
        console.log('           message from ' + socket.user_id + ' to room ' + room_id + ": " + msg);
        socket.broadcast.to(room_id).emit('user message', socket.gender == 1 ? i18n.__("he") : i18n.__("she") , msg);
  });

Ну и собственно…

Выход

socket.on('disconnect', function () {
    
    online--;
    total--;
    
    socket.broadcast.emit('online', total, free);

    if (!socket.user_id) return;
    
    if(free > 0) free--;
    
    socket.broadcast.emit('online', total, free);
    
    console.log(socket.user_id);
    
    socket.leave(room_id);
    socket.broadcast.to(room_id).emit('announcement', socket.user_id + ' ' +  i18n.__('disconnected'));
    
    if(rooms[room_id]) delete rooms[room_id].nicknames[socket.user_id];
    if(rooms[room_id]) socket.broadcast.to(room_id).emit('nicknames', rooms[room_id].nicknames);
    
    delete users[socket.user_id];
    delete nicknames[socket.user_id];
    delete rooms[room_id];
    
  });

На этом всё. Тестировалось под нагрузкой < 10 человек, проблем не возникало :) Если сообщество заинтересует проект такого чата — сегодня же выложу на github, делалось just for fun.

P.S. Топик начинал писаться давно, и перенесен из черновиков — поэтому версии указанных продуктов могут быть не актуальны.

P.S.S. Посмотреть в живую можно тут — virt.in

Автор: eudj1n

* - обязательные к заполнению поля


https://ajax.googleapis.com/ajax/libs/jquery/3.4.1/jquery.min.js