# Messaging ## Sending a Message (SignalR) A message typed in the TUI travels through encryption, the SignalR hub, `ChatService` validation, database storage, and fan-out to both SignalR and IRC clients. ```mermaid sequenceDiagram participant UI as MainWindow (TUI) participant AO as AppOrchestrator participant EHC as EchoHubConnection participant Hub as ChatHub participant CS as ChatService participant LE as LinkEmbedService participant DB as SQLite participant SRB as SignalRBroadcaster participant IRCB as IrcBroadcaster participant Clients as Other Clients UI->>AO: OnMessageSubmitted(channel, text) AO->>AO: IsCommand(text)? → No AO->>EHC: SendMessageAsync(channel, text) EHC->>EHC: Encrypt(text) → ciphertext EHC->>Hub: InvokeAsync("SendMessage", channel, ciphertext) Hub->>CS: SendMessageAsync(userId, username, channel, ciphertext) CS->>CS: Decrypt(ciphertext) → plaintext CS->>CS: Validate (length, newlines, channel exists) CS->>DB: Check mute status CS->>LE: TryGetEmbedsAsync(plaintext) LE->>LE: Extract URLs, fetch OG tags LE-->>CS: List (or null) CS->>DB: INSERT Message (encrypted at rest) CS->>CS: Re-encrypt plaintext for transport CS->>CS: Build MessageDto with embeds par Fan-out to all broadcasters CS->>SRB: SendMessageToChannelAsync(channel, dto) SRB->>Clients: HubContext.Group(channel).ReceiveMessage(dto) and CS->>IRCB: SendMessageToChannelAsync(channel, dto) IRCB->>IRCB: Decrypt → format as PRIVMSG lines IRCB->>Clients: Send to each IRC conn (skip sender) end ``` --- ## Sending a Message (IRC) Messages from IRC clients follow the same `ChatService` path but enter as plaintext (no app-layer encryption). ```mermaid sequenceDiagram participant IRC as IRC Client participant CH as IrcCommandHandler participant CS as ChatService participant DB as SQLite participant SRB as SignalRBroadcaster participant IRCB as IrcBroadcaster IRC->>CH: PRIVMSG #channel :Hello world CH->>CH: Parse target + content CH->>CH: IrcToEchoHubChannel("#channel") → "channel" CH->>CS: SendMessageAsync(userId, username, "channel", "Hello world") CS->>CS: Decrypt("Hello world") → passthrough (no $ENC$ prefix) CS->>CS: Validate, check mute, fetch embeds CS->>DB: INSERT Message CS->>CS: Encrypt plaintext for SignalR transport par Fan-out CS->>SRB: SendMessageToChannelAsync (encrypted for SignalR) and CS->>IRCB: SendMessageToChannelAsync (decrypt → PRIVMSG) IRCB->>IRCB: Skip sender (IRC echo suppression) end ``` --- ## Receiving a Message (TUI Client) When a message arrives via SignalR, the client decrypts it, adds it to the chat view, and optionally plays a notification sound for @mentions. ```mermaid sequenceDiagram participant SRB as SignalRBroadcaster participant EHC as EchoHubConnection participant AO as AppOrchestrator participant MM as ChatMessageManager participant UI as MainWindow SRB->>EHC: ReceiveMessage(MessageDto) EHC->>EHC: Decrypt(message.Content) EHC-->>AO: OnMessageReceived(decrypted dto) AO->>AO: InvokeUI (thread-safe) AO->>MM: AddMessage(message) MM->>UI: Render in chat ListView alt Message contains @username AO->>AO: PlayAsync() notification sound end ``` --- ## Command Execution Slash commands (`/status`, `/nick`, `/kick`, etc.) are parsed client-side and dispatched to appropriate handlers, which call REST APIs or SignalR methods. ```mermaid sequenceDiagram participant UI as MainWindow participant AO as AppOrchestrator participant CMD as CommandHandler participant API as ApiClient participant EHC as EchoHubConnection participant Server as Server (API/Hub) UI->>AO: OnMessageSubmitted(channel, "/kick baduser") AO->>CMD: IsCommand("/kick baduser")? → true AO->>CMD: HandleAsync("/kick baduser") CMD->>CMD: Parse → command="kick", args="baduser" alt API command (kick, ban, mute, nick, etc.) CMD-->>AO: Fire OnKickRequested("baduser") AO->>API: KickUserAsync("baduser") API->>Server: POST /api/moderation/kick/baduser else Hub command (status, join, leave, etc.) CMD-->>AO: Fire OnSetStatus / OnJoinChannel / etc. AO->>EHC: UpdateStatusAsync() / JoinChannelAsync() / etc. EHC->>Server: SignalR Invoke else Local command (theme, help, quit) CMD-->>AO: Fire OnThemeChanged / etc. AO->>UI: Apply locally (no server call) end AO->>UI: AddSystemMessage(result) ``` **Available commands:** | Command | Type | Handler | |---------|------|---------| | `/status [message]` | Hub | `UpdateStatusAsync` | | `/nick ` | API | `UpdateProfileAsync` | | `/color ` | API | `UpdateProfileAsync` | | `/join ` | Hub | `JoinChannelAsync` | | `/leave` | Hub | `LeaveChannelAsync` | | `/topic ` | API | `UpdateChannelTopicAsync` | | `/kick ` | API | `POST /api/moderation/kick/{user}` | | `/ban ` | API | `POST /api/moderation/ban/{user}` | | `/unban ` | API | `POST /api/moderation/unban/{user}` | | `/mute [mins]` | API | `POST /api/moderation/mute/{user}` | | `/unmute ` | API | `POST /api/moderation/unmute/{user}` | | `/role ` | API | `PUT /api/moderation/role/{user}` | | `/nuke` | API | `DELETE /api/channels/{channel}/messages` | | `/send ` | API | `POST /api/channels/{channel}/upload` | | `/profile [user]` | Local | Show profile dialog | | `/avatar` | API | `POST /api/users/avatar` | | `/theme ` | Local | `ThemeManager.SetTheme()` | | `/servers` | API | `GET /api/serverdir/servers` | | `/users` | Local | Show userlist | | `/help` | Local | Show help text | | `/quit` | Local | Exit application |