UNPKG

actionhero

Version:

actionhero.js is a multi-transport API Server with integrated cluster capabilities and delayed tasks

233 lines (232 loc) 9.71 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.chatRoom = void 0; const index_1 = require("./../index"); var chatRoom; (function (chatRoom) { /** * Middleware definition for processing chat events. Can be of the * * ```js * var chatMiddleware = { * name: 'chat middleware', * priority: 1000, * join: (connection, room) => { * // announce all connections entering a room * api.chatRoom.broadcast({}, room, 'I have joined the room: ' + connection.id, callback) * }, * leave:(connection, room, callback) => { * // announce all connections leaving a room * api.chatRoom.broadcast({}, room, 'I have left the room: ' + connection.id, callback) * }, * // Will be executed once per client connection before delivering the message. * say: (connection, room, messagePayload) => { * // do stuff * log(messagePayload) * }, * // Will be executed only once, when the message is sent to the server. * onSayReceive: (connection, room, messagePayload) => { * // do stuff * log(messagePayload) * } * } * api.chatRoom.addMiddleware(chatMiddleware) * ``` */ /** * Add a middleware component to connection handling. */ async function addMiddleware(data) { if (!data.name) { throw new Error("middleware.name is required"); } if (!data.priority) { data.priority = index_1.config.general.defaultMiddlewarePriority; } data.priority = Number(data.priority); index_1.api.chatRoom.middleware[data.name] = data; index_1.api.chatRoom.globalMiddleware.push(data.name); index_1.api.chatRoom.globalMiddleware.sort((a, b) => { if (index_1.api.chatRoom.middleware[a].priority > index_1.api.chatRoom.middleware[b].priority) { return 1; } else { return -1; } }); } chatRoom.addMiddleware = addMiddleware; /** * List all chat rooms created */ async function list() { return index_1.api.redis.clients.client.smembers(index_1.api.chatRoom.keys.rooms); } chatRoom.list = list; /** * Add a new chat room. Throws an error if the room already exists. */ async function add(room) { const found = await chatRoom.exists(room); if (found === false) { return index_1.api.redis.clients.client.sadd(index_1.api.chatRoom.keys.rooms, room); } else { throw new Error(await index_1.config.errors.connectionRoomExists(room)); } } chatRoom.add = add; /** * Remove an existing chat room. All connections in the room will be removed. Throws an error if the room does not exist. */ async function destroy(room) { const found = await chatRoom.exists(room); if (found === true) { await index_1.api.chatRoom.broadcast({}, room, await index_1.config.errors.connectionRoomHasBeenDeleted(room)); const membersHash = await index_1.api.redis.clients.client.hgetall(index_1.api.chatRoom.keys.members + room); for (const id in membersHash) { await chatRoom.removeMember(id, room, false); } await index_1.api.redis.clients.client.srem(index_1.api.chatRoom.keys.rooms, room); await index_1.api.redis.clients.client.del(index_1.api.chatRoom.keys.members + room); } else { throw new Error(await index_1.config.errors.connectionRoomNotExist(room)); } } chatRoom.destroy = destroy; /** * Check if a room exists. */ async function exists(room) { const bool = await index_1.api.redis.clients.client.sismember(index_1.api.chatRoom.keys.rooms, room); let found = false; // @ts-ignore if (bool === 1 || bool === true) { found = true; } return found; } chatRoom.exists = exists; /** * Configures what properties of connections in a room to return via `api.chatRoom.roomStatus` */ async function sanitizeMemberDetails(memberData) { return { id: memberData.id, joinedAt: memberData.joinedAt, }; } chatRoom.sanitizeMemberDetails = sanitizeMemberDetails; /** * Learn about the connections in the room. * Returns a hash of the form { room: room, members: cleanedMembers, membersCount: count }. Members is an array of connections in the room sanitized via `api.chatRoom.sanitizeMemberDetails` */ async function roomStatus(room) { if (room) { const found = await chatRoom.exists(room); if (found === true) { const key = index_1.api.chatRoom.keys.members + room; const members = (await index_1.api.redis.clients.client.hgetall(key)); const cleanedMembers = {}; let count = 0; for (const id in members) { const data = JSON.parse(members[id]); cleanedMembers[id] = chatRoom.sanitizeMemberDetails(data); count++; } return { room: room, members: cleanedMembers, membersCount: count, }; } else { throw new Error(await index_1.config.errors.connectionRoomNotExist(room)); } } else { throw new Error(await index_1.config.errors.connectionRoomRequired()); } } chatRoom.roomStatus = roomStatus; /** * An overwrite-able method which configures what properties of connections in a room are initially stored about a connection when added via `api.chatRoom.addMember` */ async function generateMemberDetails(connection) { return { id: connection.id, joinedAt: new Date().getTime(), host: index_1.id, }; } chatRoom.generateMemberDetails = generateMemberDetails; /** * Add a connection (via id) to a room. Throws errors if the room does not exist, or the connection is already in the room. Middleware errors also throw. */ async function addMember(connectionId, room) { const connection = index_1.api.connections.connections[connectionId]; if (!connection) { return index_1.redis.doCluster("api.chatRoom.addMember", [connectionId, room], connectionId, true); } if (connection.rooms.indexOf(room) >= 0) { throw new Error(await index_1.config.errors.connectionAlreadyInRoom(connection, room)); } if (connection.rooms.indexOf(room) < 0) { const found = await chatRoom.exists(room); if (!found) { throw new Error(await index_1.config.errors.connectionRoomNotExist(room)); } } if (connection.rooms.indexOf(room) < 0) { await index_1.api.chatRoom.runMiddleware(connection, room, "join"); } if (connection.rooms.indexOf(room) < 0) { const memberDetails = chatRoom.generateMemberDetails(connection); connection.rooms.push(room); await index_1.api.redis.clients.client.hset(index_1.api.chatRoom.keys.members + room, connection.id, JSON.stringify(memberDetails)); } return true; } chatRoom.addMember = addMember; /** * Remote a connection (via id) from a room. Throws errors if the room does not exist, or the connection is not in the room. Middleware errors also throw. * toWaitRemote: Should this method wait until the remote Actionhero server (the one the connection is connected too) responds? */ async function removeMember(connectionId, room, toWaitRemote = true) { const connection = index_1.api.connections.connections[connectionId]; if (!connection) { return index_1.redis.doCluster("api.chatRoom.removeMember", [connectionId, room], connectionId, toWaitRemote); } if (connection.rooms.indexOf(room) < 0) { throw new Error(await index_1.config.errors.connectionNotInRoom(connection, room)); } if (connection.rooms.indexOf(room) >= 0) { const found = await chatRoom.exists(room); if (!found) { throw new Error(await index_1.config.errors.connectionRoomNotExist(room)); } } if (connection.rooms.indexOf(room) >= 0) { await index_1.api.chatRoom.runMiddleware(connection, room, "leave"); } if (connection.rooms.indexOf(room) >= 0) { const index = connection.rooms.indexOf(room); connection.rooms.splice(index, 1); await index_1.api.redis.clients.client.hdel(index_1.api.chatRoom.keys.members + room, connection.id); } return true; } chatRoom.removeMember = removeMember; /** * Send a message to all clients connected to this room * - connection should either be a real client you are emulating (found in api.connections) or just `{}` for a mock * - room is the string name of an already-existing room * - message can be anything: string, json, object, etc */ async function broadcast(connection, room, message) { return index_1.api.chatRoom.broadcast(connection, room, message); } chatRoom.broadcast = broadcast; })(chatRoom = exports.chatRoom || (exports.chatRoom = {}));