{"id":19615008,"url":"https://github.com/ghurtchu/blabbermouth","last_synced_at":"2026-05-16T17:42:51.595Z","repository":{"id":201201474,"uuid":"707197139","full_name":"Ghurtchu/blabbermouth","owner":"Ghurtchu","description":"live support chat server (currently in dev)","archived":false,"fork":false,"pushed_at":"2025-04-06T20:34:13.000Z","size":36892,"stargazers_count":2,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-04-06T21:29:54.074Z","etag":null,"topics":["chat","concurrency","fs2","functional-programming","http","http4s","play-json","queue","redis","redis-pubsub","scala","streams","websockets"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/Ghurtchu.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2023-10-19T12:15:02.000Z","updated_at":"2025-04-06T20:34:16.000Z","dependencies_parsed_at":"2024-03-02T12:28:16.021Z","dependency_job_id":"9ed58b0c-109d-4cf3-b0ef-f40e1bf6f514","html_url":"https://github.com/Ghurtchu/blabbermouth","commit_stats":{"total_commits":70,"total_committers":2,"mean_commits":35.0,"dds":"0.12857142857142856","last_synced_commit":"4a16984788709bbe6d4754dc2632b17ce0fd148f"},"previous_names":["ghurtchu/live-chat-support","ghurtchu/blabbermouth","scalevolvable/blabbermouth"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/Ghurtchu/blabbermouth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ghurtchu%2Fblabbermouth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ghurtchu%2Fblabbermouth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ghurtchu%2Fblabbermouth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ghurtchu%2Fblabbermouth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/Ghurtchu","download_url":"https://codeload.github.com/Ghurtchu/blabbermouth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/Ghurtchu%2Fblabbermouth/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278722757,"owners_count":26034463,"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","status":"online","status_checked_at":"2025-10-07T02:00:06.786Z","response_time":59,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"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","concurrency","fs2","functional-programming","http","http4s","play-json","queue","redis","redis-pubsub","scala","streams","websockets"],"created_at":"2024-11-11T10:54:54.038Z","updated_at":"2025-10-07T05:22:40.252Z","avatar_url":"https://github.com/Ghurtchu.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Live Support Chat Server\n\nCurrently creating independent SPA-s for user and support.\n\nTypical flow:\n\n1) User attempts to register themselves to the live support chat system\n\n![My Image](assets/registration.png)\n\n2) They get notified to wait (stay reactive!)\n\n![My Image](assets/loading.png)\n\n3) Support specialists receive a notification on UI about pending user\n\n![My Image](assets/pending_users.png)\n\n4) One of the support specialists clicks on the button and chat session starts\n\n![My Image](assets/chat.png)\n\nService oriented architecture with two backends:\n- `ChatServer`:\n  - publishes user join requests in `Redis pub/sub`, `users` channel\n  - enables `User` and `Support` to exchange messages via chat using `WebSockets`\n- `Subscriber`:\n  - subscribes to `users` channel in `Redis pub/sub` and forwards user join requests to UI using `WebSockets`\n\nRequirements for running the whole project:\n- Docker engine\n\nInstructions:\n- clone the repo\n- cd into repo\n- start docker engine\n- run: `docker-compose up` (it starts `ChatServer`, `Subscriber` and `Redis` in three different docker containers which are connected via docker network)\n\n**Protocol and flow description for building UI**:\n\n1) Register `User` in `ChatServer` by sending HTTP request to: `GET localhost:9000/user/join/{userName}`\n   - Expect `JSON` response:\n    ```json\n    {\n        \"userId\": \"9d6d2db0cdb84d8ba67105670d94d641\",\n        \"username\": \"Nika\",\n        \"chatId\": \"d2464b5386044daf9e36ffd414260f67\"\n    }\n    ```\n2) Initiate WebSocket connection for `User` in `ChatServer` by sending request to: `GET localhost:9000/chat/{chatId}`\n   - server immediately starts sending `ping` message for each second, UI must respond with: `pong:user` (covers heartbeat functionality)\n   - for requesting to join, send message:\n    ```json\n    {\n        \"type\": \"Join\",\n        \"args\": {\n            \"userId\": \"9d6d2db0cdb84d8ba67105670d94d641\",\n            \"username\": \"Nika\",\n            \"from\": \"User\"\n        }\n    }\n    ```\n   - Expect `JSON` response:\n    ```json\n    {\n        \"type\": \"UserJoined\",\n        \"args\": {\n            \"username\": \"Nika\",\n            \"userId\": \"9d6d2db0cdb84d8ba67105670d94d641\",\n            \"chatId\": \"d2464b5386044daf9e36ffd414260f67\"\n        }\n    }\n    ```\n3) Initiate WebSocket connection for `Support` in `Subscriber` by sending request to: `GET localhost:9001/users`\n   - server immediately starts sending `ping` message for each second, UI must respond with: `pong:support` (covers heartbeat functionality)\n   - for loading pending users send this WS message, it also subscribes to `Redis PubSub` and reads newly joined users\n     ```json\n     {\n         \"type\": \"Load\"\n     }\n     ```\n   - Expect one re more `JSON` WebSocket messages (TODO - consider sending one message with list of users instead of single message for each user):\n     for pending users \n     ```json\n     {\n         \"username\": \"Nika\",\n         \"userId\": \"9d6d2db0cdb84d8ba67105670d94d641\",\n         \"chatId\": \"d2464b5386044daf9e36ffd414260f67\"\n     }\n     ```\n     for newly joined users\n     ```json\n     {\n         \"type\": \"UserJoined\",\n         \"args\": {\n             \"username\": \"Dodo\",\n             \"userId\": \"b646273c1f8840b1ad296b40ef62f2c5\",\n             \"chatId\": \"2500f760be124d299f214c5f3aa0ecf2\"\n          }\n     }\n     ```\n   - once `Support` clicks to the special user request, UI must send the `JoinUser` as websocket message so that it gets filtered out for other `Support` agents, at the same time UI must send another request to `ChatServer` - `step 4` (details in `step 4`)\n   `JoinUser` looks like\n   ```json \n    {\n        \"type\": \"JoinUser\",\n        \"args\": {\n            \"userId\": \"8e0a7c3e1b2f489f8ce129a5167f71b5\",\n            \"chatId\": \"0765d196ec4e4d108b9e4b6fca7c8254\",\n            \"username\": \"Zaza\"\n        }\n    }\n   ```\n   as a response the backend sends following WebSocket message:\n   ```json\n   {\n       \"type\": \"RemoveUser\",\n       \"args\": {\n           \"userId\": \"8e0a7c3e1b2f489f8ce129a5167f71b5\"\n       }\n   }\n   ```\n   This message will be broadcasted to all support UI-s and Frontend will be able to drop this user from the list\n4) Initiate WebSocket connection for `Support` in `ChatServer` by sending request to: `GET localhost:9000/chat/{chatId}`\n   - attach request body:\n    ```json\n    {\n        \"type\": \"Join\",\n        \"args\": {\n            \"userId\" : \"9d6d2db0cdb84d8ba67105670d94d641\",\n            \"supportUserName\": \"Vika\",\n            \"username\": \"Nika\",\n            \"from\": \"Support\"    \n        }\n    }\n   ```\n  - Expect `JSON` response:\n    ```json\n    {\n        \"type\": \"SupportJoinedUser\",\n        \"args\": {\n            \"supportId\": \"1939d1c378ea45809521c16668c21c10\",\n            \"supportUserName\": \"Kiku\",\n            \"username\": \"f3eb5276f29c4971827d31acd12327a7\",\n            \"userId\": \"Gela\",\n            \"chatId\": \"91a68f25d5e5444aaaa5dc47ebf7d893\"\n        }\n    }\n    ```\n5) Send chat message to `Support` from `User` by sending the following WebSocket text message on the opened WS connection:\n    ```json\n    {\n        \"type\": \"ChatMessage\",\n        \"args\": {\n            \"userId\": \"9d6d2db0cdb84d8ba67105670d94d641\",\n            \"supportId\": \"7c270542e64a4dfbb2bc1c7793746674\",\n            \"content\": \"Hey, I want to transfer money offshore, how can I do it via internet bank?\",\n            \"from\": \"User\"    \n        }\n    }\n    ```\n   - Expect `JSON` response for both `User` and `Support` WebSocket connections:\n   ```json\n    {\n        \"type\": \"ChatMessage\",\n        \"args\": {\n            \"userId\": \"9d6d2db0cdb84d8ba67105670d94d641\",\n            \"supportId\": \"7c270542e64a4dfbb2bc1c7793746674\",\n            \"content\": \"hey\",\n            \"timestamp\": \"2023-10-22T18:18:59.360855Z\",\n            \"from\": \"User\"\n        }\n    }\n   ```\n6) Send chat message to `User` from `Support` by sending the following WebSocket text message on the opened WS connection:  \n    ```json\n    {\n        \"type\": \"ChatMessage\",\n        \"args\": {\n            \"userId\": \"9d6d2db0cdb84d8ba67105670d94d641\",\n            \"supportId\": \"7c270542e64a4dfbb2bc1c7793746674\",\n            \"supportUserName\": \"Vika\",\n            \"content\": \"Hello, please navigate to Transfer and then select Offshore :)\",\n            \"from\": \"Support\"    \n        }\n    }\n    ```\n    - Expect `JSON` response for both `User` and `Support` WebSocket connections:\n   ```json\n    {\n        \"type\": \"ChatMessage\",\n        \"args\": {\n            \"userId\": \"f49f3cc665af4cb38092af714a4c87fa\",\n            \"supportId\": \"7c270542e64a4dfbb2bc1c7793746674\",\n            \"content\": \"Hello, please navigate to Transfer and then select Offshore :)\",\n            \"timestamp\": \"2023-10-22T18:19:19.119515Z\",\n            \"from\": \"Support\"\n        }\n   }\n   ```\n   \nEach chat expires after two minutes of inactivity. Also, WebSocket connection automatically closes after 120 seconds of inactivity.\n\nIn case `User` or `Support` refreshes browser backend either loads:\n- conversation history (if the chat is still active)\n- message about chat expiration (if the chat was expired)\n\n7) In case `User` refreshes browser the UI must send new `Join` (re-join) WebSocket text message:\n    ```json\n    {   \n        \"type\": \"Join\",\n        \"args\": {\n            \"userId\": \"8b5ee49aa3bb45e1a8719179e5e25c12\",\n            \"supportId\": \"7c270542e64a4dfbb2bc1c7793746674\",\n            \"username\": \"Nika\",\n            \"from\": \"User\"\n        }\n   }\n   ```\n   - Expect `JSON` response:\n    ```json\n    {\n        \"type\": \"ChatHistory\",\n        \"args\": {\n            \"messages\": [\n                {\n                    \"userId\": \"718b568c22f6460ca46a07cfc6aae3ba\",\n                    \"supportId\": \"35eda9304e554514915fdbb8f26b710d\",\n                    \"content\": \"Hey, I want to tranasfer money offshore, how can I do it via internet bank?\",\n                    \"timestamp\": \"2023-10-22T18:36:54.834408Z\",\n                    \"from\": \"User\"\n                },\n                {\n                    \"userId\": \"718b568c22f6460ca46a07cfc6aae3ba\",\n                    \"supportId\": \"35eda9304e554514915fdbb8f26b710d\",\n                    \"content\": \"Hello, please navigate to Transfer and then select Offshore :)\",\n                    \"timestamp\": \"2023-10-22T18:37:10.219775Z\",\n                    \"from\": \"Support\"\n                }\n            ]\n        }\n    }\n    ```\n   \n8) Same message is loaded for `Support`, however a bit different `Join` (re-join) WebSocket text message must be sent:\n    ```json\n    {   \n        \"type\": \"Join\",\n        \"args\": {\n            \"userId\": \"8b5ee49aa3bb45e1a8719179e5e25c12\",\n            \"supportId\": \"7c270542e64a4dfbb2bc1c7793746674\",\n            \"username\": \"Nika\",\n            \"supportUserName\": \"Vika\",\n            \"from\": \"Support\"\n        }\n   }\n   ```\n\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghurtchu%2Fblabbermouth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fghurtchu%2Fblabbermouth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fghurtchu%2Fblabbermouth/lists"}