soup-sdk 0.3.1

채팅 이벤트 수신 SDK
Documentation
# Live Chat Reference

This document describes the public live chat interface.

Main types:

- `SoopChatOptions`
- `SoopChatConnection`
- `Command`
- `Event`

## Basic Flow

```rust
use std::sync::Arc;

use soup_sdk::{
    Result, SoopHttpClient,
    chat::{Event, SoopChatConnection, SoopChatOptions},
};

#[tokio::main]
async fn main() -> Result<()> {
    let http = Arc::new(SoopHttpClient::new());
    let options = SoopChatOptions {
        streamer_id: "streamer_id".to_string(),
        password: "".to_string(),
        diagnostics: false,
    };

    let connection = SoopChatConnection::new(Arc::clone(&http), options)?;
    let mut events = connection.subscribe();

    connection.start().await?;

    while let Ok(event) = events.recv().await {
        if let Event::Chat(chat) = event {
            println!("{}: {}", chat.user.label, chat.comment);
        }
    }

    Ok(())
}
```

## `SoopChatOptions`

Configuration for a live chat connection.

Fields:

- `streamer_id: String`: SOOP streamer ID.
- `password: String`: broadcast password. Use an empty string for non-password streams.
- `diagnostics: bool`: when `true`, emits raw frame, decoded frame, unknown-code, and parse-failure diagnostic events.

Current limitation:

- Login/session options and custom base URLs are not currently exposed.

## `SoopChatConnection::new`

Creates a chat connection handle.

```rust
let connection = SoopChatConnection::new(http_client, options)?;
```

Inputs:

- `http_client: Arc<SoopHttpClient>`
- `options: SoopChatOptions`

Returns:

```rust
Result<SoopChatConnection>
```

Behavior:

- Initializes TLS crypto provider if needed.
- Creates an internal command channel.
- Creates a broadcast event channel with capacity `1024`.
- Does not open the network connection yet.

Failure modes:

- TLS crypto provider initialization can return `Error::Protocol` if installation fails.

## `subscribe`

Creates an event receiver.

```rust
let mut events = connection.subscribe();
```

Returns:

```rust
tokio::sync::broadcast::Receiver<Event>
```

Behavior:

- Can be called before `start`.
- Multiple subscribers can receive the same event stream.
- Broadcast receivers can lag. If a receiver falls behind the channel capacity, Tokio broadcast semantics apply and events may be missed.

## `start`

Starts the background live chat connection task.

```rust
connection.start().await?;
```

Returns:

```rust
Result<()>
```

Behavior:

- Calls `SoopHttpClient::get_live_status`.
- Returns `Error::StreamOffline` if the streamer is not live.
- Builds the WebSocket URL from live detail.
- Opens the WebSocket with a 15-second connection timeout.
- Takes ownership of the internal command receiver.
- Spawns the connection loop in a background Tokio task.

Current contract:

- A `SoopChatConnection` is one-shot. Calling `start` more than once returns `Error::AlreadyStarted`.

Failure modes:

- live detail HTTP/API failure.
- stream offline.
- invalid live detail state.
- already-started connection.

## `command`

Sends a command to the background connection loop.

```rust
use soup_sdk::chat::commands::Command;

connection.command(Command::Shutdown)?;
connection.command(Command::RequestParticipantList)?;
```

Returns:

```rust
Result<()>
```

Implemented commands:

- `Command::Shutdown`: stops the active communication loop and leads to `Event::Disconnected`.
- `Command::RequestParticipantList`: sends a participant-list request packet. The SDK aggregates the server response internally and emits one final `Event::ParticipantList`.

Current note:

- Chat sending is not exposed in v0.3.

Failure modes:

- internal command channel is closed or full.

## Connection Lifecycle

Current session lifecycle:

```text
start
  -> fetch live detail
  -> connect to WebSocket
  -> emit Connected
  -> send Connect packet
  -> receive server CONNECT
  -> send JOIN packet
  -> process incoming chat frames
  -> send participant-list request when commanded
  -> send Ping every 60 seconds
  -> Shutdown or socket failure
  -> emit Disconnected with a reason
```

Reconnect note:

- v0.3 does not automatically reconnect.
- Downstream users should treat disconnect as terminal and create a new session when they want to retry.

TLS note:

- Live chat currently disables TLS certificate verification for compatibility with the upstream chat endpoint. This is a deliberate security tradeoff in v0.3 and should not be treated as a general-purpose secure WebSocket default.

## Incoming Message Handling

Incoming WebSocket binary frames are handled as:

```text
binary frame
  -> Event::Diagnostic(RawFrame) when diagnostics is enabled
  -> parse raw protocol header/body
  -> Event::Diagnostic(DecodedFrame) when diagnostics is enabled
  -> match message code
  -> parse typed event
  -> broadcast typed Event or Event::Diagnostic(ParseFailed) when diagnostics is enabled
```

Raw protocol facts:

- header length: 14 bytes.
- body separator: form feed byte `0x0c`.
- message code constants live in `chat::constants::message_codes`.

## Debugging Workflow

For protocol analysis, set `SoopChatOptions::diagnostics` to `true`, subscribe to events, and inspect:

- `Event::Diagnostic(RawFrame)`: original binary frame.
- `Event::Diagnostic(DecodedFrame)`: decoded message code, ret code, body fields, and raw bytes.
- `Event::Unknown(code)` and `Event::Diagnostic(UnknownCode)`: decoded message code without a typed handler.
- `Event::Diagnostic(ParseFailed)`: message code, body field count, field index, expected type, reason, and raw bytes.
- typed events with surprising field values.

Recommended workflow for adding support for a new message:

1. Capture `Event::Diagnostic(RawFrame)`.
2. Decode the message code and body fields.
3. Record body field indexes and sample values.
4. Add or update a parser.
5. Add replay tests for the raw frame.
6. Update `EVENTS.md`.

## Threading and Runtime Notes

- The SDK expects a Tokio runtime.
- `start` spawns a background task with `tokio::spawn`.
- Events are delivered through `tokio::sync::broadcast`.
- Commands are delivered through `tokio::sync::mpsc`.