oglimmer/podcast-human-syncer

View on GitHub
server/websockets.js

Summary

Maintainability
A
1 hr
Test Coverage

const ioFactory = require('socket.io')
const capitalize = require('capitalize')

const { SISMEMBER, SADD, SREM, GET, MGET, SET, MSET, HGET, HSET, DEL } = require('./Redis')

const log = require('./log')
const getUpdateObject = require('./getUpdateObject')
const { RECIPIENT_CURRENT, RECIPIENT_OTHERS, RECIPIENT_ALL,
  FIELDS_SIMPLE,
  CURRENT, NEXT, URGENT, WANT_OF_FINISH, LAST_TAKE_OVER, CONNECTED, TOTAL_TIME, CHAT_MESSAGES } = require('./constants')

function updateStatus (socket, roomname, recipients, emitData) {
  if (recipients & RECIPIENT_CURRENT) {
    socket.emit('update status', emitData)
  }
  if (recipients & RECIPIENT_OTHERS) {
    socket.to(roomname).emit('update status', emitData)
  }
}

async function transferName (socket, { username, roomname }) {
  const nameCaped = capitalize(username)
  log('transferName', `${nameCaped}:${roomname}`)
  if (!await SISMEMBER(`${roomname}:${CONNECTED}`, nameCaped)) {
    socket.join(roomname)
    socket.username = nameCaped
    socket.roomname = roomname
    await SADD(`${roomname}:${CONNECTED}`, nameCaped)
    socket.emit('user accepted', Object.assign({},
      { username: nameCaped },
      await getUpdateObject(roomname, [CURRENT, NEXT, URGENT, WANT_OF_FINISH, LAST_TAKE_OVER, CONNECTED, TOTAL_TIME])
    ))
    updateStatus(socket, roomname, RECIPIENT_OTHERS, await getUpdateObject(roomname, [CONNECTED]))
  } else {
    socket.emit('user join failed', 'Name already exists')
  }
}

async function rejoinUser (socket, { username, roomname }) {
  const nameCaped = capitalize(username)
  log('rejoin user', `${nameCaped}:${roomname}`)
  socket.join(roomname)
  socket.username = nameCaped
  socket.roomname = roomname
  await SADD(`${roomname}:${CONNECTED}`, nameCaped)
  socket.emit('user accepted', Object.assign({},
    { username: nameCaped },
    await getUpdateObject(roomname, [CURRENT, NEXT, URGENT, WANT_OF_FINISH, LAST_TAKE_OVER, CONNECTED, TOTAL_TIME])
  ))
  updateStatus(socket, roomname, RECIPIENT_OTHERS, await getUpdateObject(roomname, [CONNECTED]))
}

async function disconnect (socket) {
  log('disconnect', `${socket.username}:${socket.roomname}`)
  if (socket.username) {
    await SREM(`${socket.roomname}:${CONNECTED}`, socket.username)
  }
  updateStatus(socket, socket.roomname, RECIPIENT_OTHERS, await getUpdateObject(socket.roomname, [CONNECTED]))
}

async function tookOver (socket, { username, roomname }) {
  log('took over', `${username}:${roomname}`)
  const [ currentUser, lastTakeOver ] = await MGET(`${roomname}:${CURRENT}`, `${roomname}:${LAST_TAKE_OVER}`)
  if (currentUser !== username) {
    await MSET(
      `${roomname}:${CURRENT}`, username,
      `${roomname}:${NEXT}`, '',
      `${roomname}:${URGENT}`, '',
      `${roomname}:${WANT_OF_FINISH}`, '',
      `${roomname}:${LAST_TAKE_OVER}`, new Date().toUTCString())
    if (currentUser) {
      const lastTotalTimeForUser = await HGET(`${roomname}:${TOTAL_TIME}`, `${currentUser}`)
      const timeToAdd = Math.floor((Date.now() - new Date(lastTakeOver).getTime()) / 1000)
      const newTotalTime = lastTotalTimeForUser ? parseInt(lastTotalTimeForUser) + timeToAdd : timeToAdd
      await HSET(`${roomname}:${TOTAL_TIME}`, `${currentUser}`, newTotalTime)
    }
    updateStatus(socket, roomname, RECIPIENT_ALL, await getUpdateObject(roomname, [TOTAL_TIME, ...FIELDS_SIMPLE]))
  }
}

async function wantToFinish (socket, { username, roomname }) {
  log('ask for finish', `${username}:${roomname}`)
  if (await GET(`${roomname}:${CURRENT}`) === username) {
    await SET(`${roomname}:${WANT_OF_FINISH}`, username)
    updateStatus(socket, roomname, RECIPIENT_ALL, await getUpdateObject(roomname, [WANT_OF_FINISH]))
  }
}

async function askForNext (socket, { username, roomname }) {
  log('ask for next', `${username}:${roomname}`)
  const [next, urgent, current] = await MGET(`${roomname}:${NEXT}`, `${roomname}:${URGENT}`, `${roomname}:${CURRENT}`)
  if (next === '' && urgent === '' && current !== username) {
    await SET(`${roomname}:${NEXT}`, username)
    updateStatus(socket, roomname, RECIPIENT_ALL, await getUpdateObject(roomname, [NEXT]))
  }
}

async function askForUrgent (socket, { username, roomname }) {
  log('ask for urgent', `${username}:${roomname}`)
  if (await GET(`${roomname}:${CURRENT}`) !== username) {
    await MSET(`${roomname}:${URGENT}`, username, `${roomname}:${NEXT}`, '')
    updateStatus(socket, roomname, RECIPIENT_ALL, await getUpdateObject(roomname, [NEXT, URGENT]))
  }
}

async function changeChat (socket, { inputChat, username, roomname }) {
  log('change chat', `${inputChat}:${roomname}`)
  await HSET(`${roomname}:${CHAT_MESSAGES}`, username, inputChat)
  updateStatus(socket, roomname, RECIPIENT_OTHERS, await getUpdateObject(roomname, [CHAT_MESSAGES]))
}

async function resetAllRimers (socket, { roomname }) {
  await DEL(`${roomname}:${TOTAL_TIME}`)
  await SET(`${roomname}:${LAST_TAKE_OVER}`, new Date().toUTCString())
  updateStatus(socket, roomname, RECIPIENT_ALL, await getUpdateObject(roomname, [TOTAL_TIME, LAST_TAKE_OVER]))
}

module.exports = server => {
  ioFactory(server, { serveClient: false }).on('connection', socket => {
    socket.on('transfer name', data => transferName(socket, data))
    socket.on('rejoin user', data => rejoinUser(socket, data))
    socket.on('disconnect', () => disconnect(socket))
    socket.on('took over', data => tookOver(socket, data))
    socket.on('want to finish', data => wantToFinish(socket, data))
    socket.on('ask for next', data => askForNext(socket, data))
    socket.on('ask for urgent', data => askForUrgent(socket, data))
    socket.on('change chat', data => changeChat(socket, data))
    socket.on('reset all timers', data => resetAllRimers(socket, data))
  })
}