Chat


General


Actionhero ships with a chat framework which may be used by all persistent connections (socket and websocket). There are methods to create and manage chat rooms and control the users in those rooms. Chat does not have to be for peer-to-peer communication, and is a metaphor used for many things, including the sharing of all realtime data between client and server, and client to client. This can be used for games, syndication, etc.

Clients themselves interact with rooms via verbs. Verbs are short-form commands that will attempt to modify the connection's state, either joining or leaving a room. Clients can be in many rooms at once.

Relevant chat verbs are:

  • roomAdd
  • roomLeave
  • roomView
  • say

The special verb for persistent connections say makes use of chatRoom.broadcast to tell a message to all other users in the room, IE: say myRoom Hello World from a socket client or client.say("myRoom", 'Hello World") for a websocket.

Chat on multiple Actionhero nodes relies on redis for both chat (pub/sub) and a key store defined by config.redis. The redis pub/sub server and the key store don't need to be the same instance of redis, but they do need to be the same for all Actionhero servers you are running in the cluster. This is how Actionhero scales the chat features.

There is no limit to the number of rooms which can be created, but keep in mind that each room stores information in redis, and there load created for each connection.


Examples


The full documentation for the chatRoom module can be found here: https://docs.actionherojs.com/modules/chatroom.html

In an initializer, ensure a room exists and log the room membership every minute

// in an initializer, /src/initializers/lobby.ts
import { Initializer, chatRoom, log } from "actionhero";

let timer: NodeJS.Timeout;

export class MyInitializer extends Initializer {
  constructor() {
    super();
    this.name = "rooms";
  }

  async initialize() {
    // ensure a room exists
    const lobbyExists = await chatRoom.exists("lobby");
    if (!lobbyExists) {
      await chatRoom.add("lobby");
    }
  }

  async start() {
    timer = setInterval(() => {
      this.logLobbyStatus();
    }, 60 * 1000);
  }

  async stop() {
    if (timer) {
      clearInterval(timer);
    }
  }

  async logLobbyStatus() {
    const { membersCount } = await chatRoom.roomStatus("lobby");
    log(`lobby has ${membersCount} members`);
  }
}

And if you want to broadcast a message to all clients in a room, you can use:

// client should either be a real client you are emulating (found in api.connections) or just `{}`
// room is the string name of a room
// message is a string or JSON
chatRoom.broadcast(connection, room, message);

Middleware


There are 4 types of middleware you can install for the chat system: say, onSayReceive, join, and leave. You can learn more about chat middleware in the middleware section of this site. Using middleware when messages are sent or when connections join rooms is how you build up authentication and more complex workflows.


Specific Client Communication


Every connection object also has a connection.sendMessage(message) method which you can call directly from the server.


Client Use


The details of communicating within a chat room are up to each individual server (see websocket).

  • Client will join a room (client.roomAdd(room)).
  • Once in the room, clients can send messages (which are strings) to everyone else in the room via say, ie: {client.say('room', Hello World')}
  • Once a client is in a room, they will revive messages from other members of the room as events. For example, catching say events from the websocket client looks like {client.on('say', function(message){ console.log(message); })}. You can inspect message.room if you are in more than one room.
    • The payload of a message will contain the room, sender, and the message body: {{message: "Hello World", room: "SecretRoom", from: "7d419af9-accf-40ac-8d78-9281591dd59e", context: "user", sentAt: 1399437579346}}

If you want to create an authenticated room, there are 2 steps:

  • First, create an action which modifies some property either on the connection object it self, or stores permissions to a database.
  • Then, create a join-style middleware which checks these values. In this middleware, you can determine if the connection should be added to the room or not.

    Solutions

    Actionhero was built from the ground up to include all the features you expect from a modern API framework.

    Open Source


    The Actionhero server is open source, under the Apache-2 license


    Actionhero runs on Linux, OS X, and Windows


    You always have access to the Actionhero team via Slack and Github



    Premium Training & Review


    We provide support for corporate & nonprofit customers starting at a flat rate of $150/hr. Our services include:


    • Remote training for your team
    • Code Reviews
    • Best Practices Audits
    • Custom plugin & Feature Development

    We have packages appropriate for all company sizes. Contact us to learn more.


    Premium Training & Review


    We provide support for corporate & nonprofit customers starting at a flat rate of $150/hr. Our services include:


    • Remote training for your team
    • Code Reviews
    • Best Practices Workshops
    • Custom plugin & Feature Development

    We have packages appropriate for all company sizes. Contact us to learn more.


    Enterprise


    For larger customers in need of a support contract, we offer an enterprise plan including everything in the Premium plan plus:


    • 24/7 access to core members of the Actionhero Team
    • Emergency response packages
    • Deployment support
    • ...and custom development against Actionhero’s core as needed.