{"id":21708357,"url":"https://github.com/an-sh/chat-service","last_synced_at":"2025-04-12T16:27:18.193Z","repository":{"id":35206581,"uuid":"39465430","full_name":"an-sh/chat-service","owner":"an-sh","description":"An extensible and scalable chat-like messaging server.","archived":false,"fork":false,"pushed_at":"2022-12-30T19:20:12.000Z","size":1672,"stargazers_count":74,"open_issues_count":17,"forks_count":17,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-26T10:52:18.829Z","etag":null,"topics":["chat","nodejs","presence","room-messaging","websocket"],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"seek-oss/css-modules-typescript-loader","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/an-sh.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-07-21T19:26:48.000Z","updated_at":"2025-03-04T14:28:18.000Z","dependencies_parsed_at":"2023-01-15T16:10:11.570Z","dependency_job_id":null,"html_url":"https://github.com/an-sh/chat-service","commit_stats":null,"previous_names":[],"tags_count":22,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/an-sh%2Fchat-service","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/an-sh%2Fchat-service/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/an-sh%2Fchat-service/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/an-sh%2Fchat-service/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/an-sh","download_url":"https://codeload.github.com/an-sh/chat-service/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248362533,"owners_count":21091148,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chat","nodejs","presence","room-messaging","websocket"],"created_at":"2024-11-25T22:23:21.504Z","updated_at":"2025-04-12T16:27:18.151Z","avatar_url":"https://github.com/an-sh.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\n# Chat Service\n\n[![NPM Version](https://badge.fury.io/js/chat-service.svg)](https://badge.fury.io/js/chat-service)\n[![Build Status](https://travis-ci.org/an-sh/chat-service.svg?branch=master)](https://travis-ci.org/an-sh/chat-service)\n[![Appveyor status](https://ci.appveyor.com/api/projects/status/qy7v2maica2urkss/branch/master?svg=true)](https://ci.appveyor.com/project/an-sh/chat-service)\n[![Coverage Status](https://codecov.io/gh/an-sh/chat-service/branch/master/graph/badge.svg)](https://codecov.io/gh/an-sh/chat-service)\n[![Dependency Status](https://david-dm.org/an-sh/chat-service.svg)](https://david-dm.org/an-sh/chat-service)\n[![JavaScript Style Guide](https://img.shields.io/badge/code%20style-standard-brightgreen.svg)](http://standardjs.com/)\n\nRoom messaging server implementation that is using a bidirectional RPC\nprotocol to implement chat-like communication. Designed to handle\ncommon public network messaging problems like reliable delivery,\nmultiple connections from a single user, real-time permissions and\npresence. RPC requests processing and a room messages format are\ncustomisable via hooks, allowing to implement anything from a\nchat-rooms server to a collaborative application with a complex\nconflict resolution. Room messages also can be used to create public\nAPIs or to tunnel M2M communications for IoT devices.\n\n\n### Features\n\n\n- Reliable room messaging using a server side history storage and a\n  synchronisation API.\n\n- Arbitrary messages format via just a validation function (hook),\n  allowing custom/heterogeneous messages formats (including a binary\n  data inside messages).\n\n- Per-room user presence API with notifications.\n\n- Realtime room creation and per-room users permissions management\n  APIs. Supports for blacklist or whitelist based access modes and an\n  optional administrators group.\n\n- Seamless support of multiple users' connections from various devises\n  to any service instance.\n\n- Written as a stateless microservice, uses Redis (also supports\n  cluster configurations) as a state store, can be horizontally scaled\n  on demand.\n\n- Extensive customisation support. Custom functionality can be added\n  via hooks before/after for any client request processing. And\n  requests (commands) handlers can be invoked server side via an API.\n\n- Pluginable networking transport. Client-server communication is done\n  via a bidirectional RPC protocol. Socket.io transport implementation\n  is included.\n\n- Pluginable state store. Memory and Redis stores are included.\n\n- Supports lightweight online user to online user messaging.\n\n\n## Table of Contents\n\n- [Background](#background)\n- [Installation](#installation)\n- [Usage](#usage)\n- [API](#api)\n- [Concepts overview](#concepts-overview)\n- [Customisation examples](#customisation-examples)\n- [Contribute](#contribute)\n- [License](#license)\n\n\n## Background\n\nRead this\n[article](https://medium.com/@an_sh_1/chat-service-project-announcement-and-overview-92283fe80d93)\nfor more background information.\n\n\n## Installation\n\nThis project is a [node](http://nodejs.org) module available via\n[npm](https://npmjs.com). Go check them out if you don't have them\nlocally installed.\n\n```sh\n$ npm i chat-service\n```\n\n\n## Usage\n\n### Quickstart with socket.io\n\nFirst define a server configuration. On a server-side define a socket\nconnection hook, as the service is relying on an extern auth\nimplementation. An user just needs to pass an auth check, no explicit\nuser adding step is required.\n\n```javascript\nconst ChatService = require('chat-service')\n\nconst port = 8000\n\nfunction onConnect (service, id) {\n  // Assuming that auth data is passed in a query string.\n  let { query } = service.transport.getHandshakeData(id)\n  let { userName } = query\n  // Actually check auth data.\n  // ...\n  // Return a promise that resolves with a login string.\n  return Promise.resolve(userName)\n}\n```\n\nCreating a server is a simple object instantiation. Note: `close`\nmethod _must_ be called to correctly shutdown a service instance (see\n[Failures recovery](#failures-recovery)).\n\n```javascript\nconst chatService = new ChatService({port}, {onConnect})\n\nprocess.on('SIGINT', () =\u003e chatService.close().finally(() =\u003e process.exit()))\n```\n\nServer is now running on port `8000`, using `memory` state. By default\n`'/chat-service'` socket.io namespace is used. Add a room with `admin`\nuser as the room owner. All rooms must be explicitly created (option\nto allow rooms creation from a client side is also provided).\n\n```javascript\n// The room configuration and messages will persist if redis state is\n// used. addRoom will reject a promise if the room is already created.\nchatService.hasRoom('default').then(hasRoom =\u003e {\n  if (!hasRoom) {\n    return chatService.addRoom('default', { owner: 'admin' })\n  }\n})\n```\n\nOn a client just a `socket.io-client` implementation is required. To\nsend a request (command) use `emit` method, the result (or an error)\nwill be returned in socket.io ack callback. To listen to server\nmessages use `on` method.\n\n```javascript\nconst io = require('socket.io-client')\n\n// Use https or wss in production.\nlet url = 'ws://localhost:8000/chat-service'\nlet userName = 'user' // for example and debug\nlet token = 'token' // auth token\nlet query = `userName=${userName}\u0026token=${token}`\nlet opts = { query }\n\n// Connect to a server.\nlet socket = io.connect(url, opts)\n\n// Rooms messages handler (own messages are here too).\nsocket.on('roomMessage', (room, msg) =\u003e {\n  console.log(`${msg.author}: ${msg.textMessage}`)\n})\n\n// Auth success handler.\nsocket.on('loginConfirmed', userName =\u003e {\n  // Join room named 'default'.\n  socket.emit('roomJoin', 'default', (error, data) =\u003e {\n    // Check for a command error.\n    if (error) { return }\n    // Now we will receive 'default' room messages in 'roomMessage' handler.\n    // Now we can also send a message to 'default' room:\n    socket.emit('roomMessage', 'default', { textMessage: 'Hello!' })\n  })\n})\n\n// Auth error handler.\nsocket.on('loginRejected', error =\u003e {\n  console.error(error)\n})\n```\n\nIt is a runnable code, files are in `example` directory.\n\n### Integrating with other messaging systems\n\nIt is possible to use other transports other than socket.io. There is\na proof of concept\n[transport](https://github.com/an-sh/chat-service-ws-messaging), that\nis using a WebSocket connection with some minimal API abstraction\nlayer [ws-messaging](https://github.com/an-sh/ws-messaging) and a\nsimple\n[emitter-pubsub-broker](https://github.com/an-sh/emitter-pubsub-broker)\nas backend messaging fanout abstraction.\n\nHere are the main things that a transport must allow to do:\n\n- Send messages from a server to groups of clients (based on a single\n  string full match criteria, a.k.a. room messaging).\n\n- Implement request-reply communication from a client to a server.\n\n- Implement some kind of persistent connection (or semantically\n  equivalent), it is required for a presence tracking.\n\n### Integrating with other databases\n\nChat Service is using Redis as a shared store with persistence. In a\nreal application some of this information may be needed by other\nservices, but it is not practical to fully reimplement the state\nstore. A better alternative approach is to use hooks. For example, to\nsave all room messages inside an another database just a\n`roomMessageAfter` hook can be used. Also `ServiceAPI` can be exposed\nvia backend messaging buses to other internal servers.\n\n\n### Debugging\n\nUnder normal circumstances all errors that are returned to a service\nuser (via request replies, `loginConfirmed` or `loginRejected`\nmessages) are instances of `ChatServiceError`. All other errors\nindicate a program bug or a failure in a service infrastructure. To\nenable debug logging of such errors use `export\nNODE_DEBUG=ChatService`. The library is using bluebird `^3.0.0`\npromises implementation, so to enable long stack traces use `export\nBLUEBIRD_DEBUG=1`. It is highly recommended to use promise versions of\nAPIs for hooks and `ChatServiceError` subclasses for returning hooks\ncustom errors.\n\n\n## API\n\nServer side\n[API](https://an-sh.github.io/chat-service/1.0/chat-service.html) and\n[RPC](https://an-sh.github.io/chat-service/1.0/rpc.html) documentation\nis available online.\n\n\n## Concepts overview\n\n### User multiple connections\n\nService completely abstracts a connection concept from a user concept,\nso a single user can have more than one connection (including\nconnections across different nodes). For user presence the number of\njoined sockets must be just greater than zero. All APIs designed to\nwork on the user level, handling seamlessly user's multiple\nconnections.\n\nConnections are completely independent, no additional client side\nsupport is required. But there are info messages and commands that can\nbe used to get information about other user's connections. It makes\npossible to realise client-side sync patterns, like keeping all\nconnections to be joined to the same rooms.\n\n### Room permissions\n\nEach room has a permissions system. There is a single owner user, that\nhas all administrator privileges and can assign users to the\nadministrators group. Administrators can manage other users' access\npermissions. Two modes are supported: blacklist and whitelist. After\naccess lists/mode modifications, service automatically removes users\nthat have lost an access permission.\n\nIf `enableRoomsManagement` options is enabled users can create rooms\nvia `roomCreate` command. The creator of a room will be it's owner and\ncan also delete it via `roomDelete` command.\n\nBefore hooks can be used to implement additional permissions systems.\n\n### Reliable messaging and history synchronisation\n\nWhen a user sends a room message, in RPC reply the message `id` is\nreturned. It means that the message has been saved in a store (in an\nappend only circular buffer like structure). Room message ids are a\nsequence starting from `1`, that increases by one for each\nsuccessfully sent message in the room. A client can always check the\nlast room message id via `roomHistoryInfo` command, and use\n`roomHistoryGet` command to get missing messages. Such approach\nensures that a message can be received, unless it is deleted due to\nrotation.\n\n### Custom messages format\n\nBy default a client can send messages that are limited to just a\n`{textMessage: 'Some string'}`. To enable custom messages format\nprovide `directMessagesChecker` or `roomMessagesChecker` hooks. When a\nhook resolves, a message format is accepted. Messages can be arbitrary\ndata with a few restrictions. The top level must be an `Object`,\nwithout `timestamp`, `author` or `id` fields (service will fill this\nfields before sending messages). The nested levels can include\narbitrary data types (even binary), but no nested objects with a field\n`type` set to `'Buffer'` (used for binary data manipulations).\n\n### Integration and customisations\n\nEach user command supports before and after hook adding, and a client\nconnection/disconnection hooks are supported too. Command and hooks\nare executed sequentially: before hook - command - after hook (it will\nbe called on command errors too). Sequence termination in before hooks\nis possible. Clients can send additional command arguments, hooks can\nread them, and reply with additional arguments.\n\nTo execute an user command server side `execUserCommand` is\nprovided. Also there are some more server side only methods provided\nby `ServiceAPI` and `TransportInterface`. Look for some customisation\ncases in [Customisation examples](#customisation-examples).\n\n### Failures recovery\n\nService keeps user presence and connection data in a store, that may\nbe persistent or shared. So if an instance is shutdown incorrectly\n(without calling or waiting for `close` method to finish) or lost\ncompletely network connection to a store, presence data will become\nincorrect. To fix this case `instanceRecovery` method is provided.\n\nAlso there are more subtle cases regarding connection-dependant data\nconsistency. Transport communication instances and store instances can\nexperience various kind of network, software or hardware failures. In\nsome edge cases (like operation on multiple users) such failures can\ncause inconsistencies (for the most part errors will be returned to\nthe command's issuers). These events are reported via an instance\nemitter (like `storeConsistencyFailure` event), and data can be sync\nvia `RecoveryAPI` methods.\n\n\n## Customisation examples\n\n### Anonymous listeners\n\nBy default every user is assumed to have an unique login\n(userName). Instead of managing names generation, an integration with\na separate transport can be used (or a multiplexed connection, for\nexample an another socket.io namespace). Room messages can be\nforwarded from `roomMessage` after hook to a transport, that is\naccessible without a login. And vice versa some service commands can\nbe executed by anonymous users via `execUserCommand` with bypassing\npermissions option turned on.\n\n### Messages aggregation and filtering\n\nA `roomMessage` after hook can be also used to forward messages from\none room to another. So rooms can be used for messages aggregation\nfrom another rooms. Since hooks are just functions and have a full\naccess to messages content, it allows to implement arbitrary\ncontent-based forwarding rules. Including implementing systems with\nhighly personalised user (client) specific feeds.\n\n### Explicit multi-device announcements\n\nBy default there is no way for other users to know the number and\ntypes of user connections joined to a room. Such information can be\npassed, for example in a query string and then saved via a connection\nhook. The announcement can be made in `onJoin` and `onLeave` hooks,\nusing directly transport `sendToChannel` method. Also additional\ninformation regarding joined devices types should be sent from\n`roomGetAccessList` after hook (when list name is equal to\n`'userlist'`).\n\n### Messages editing and deletion\n\nThere is no delete or edit operation, as they will make\ninconsistencies inside a room history. A common alternative for\ndeleting and editing is to use room messages with a special meaning\nthat clients will use to hide or alter messages.\n\n\n## Contribute\n\nIf you encounter a bug in this package, please submit a bug report to\ngithub repo [issues](https://github.com/an-sh/chat-service/issues).\n\nPRs are also accepted.\n\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fan-sh%2Fchat-service","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fan-sh%2Fchat-service","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fan-sh%2Fchat-service/lists"}