Files
chanora/docs/designs/badge-fetching-design.md
T
Edison Jwa 020218a7a1 docs: add badge fetching design, update verified items (TODO-042,058,062,065)
Badge fetching design with Protobuf parsing, 24h cache refresh, cacache
integration. Mark TODO-042 verified, TODO-058 monitoring, TODO-062 complete.
2026-06-11 21:30:27 +09:00

4.1 KiB

Badge Fetching Design (TODO-065)

Purpose

Fetch, cache, and display TeamSpeak client badges. Badges are visual indicators (icons) shown next to client names in the UI.

Protocol Details

Source: badges-content.teamspeak.com/list

  • Format: Protobuf (binary)
  • Refresh: Every 24 hours
  • Cache location: cache/badges (via cacache)

Protobuf structure (from YaTQA §8.9):

  1. BigNum: revision number
  2. BigNum: Unix timestamp
  3. Repeated badge entries:
    • GUID (string)
    • Name (string)
    • URL base (string, used to construct icon URL)
    • Description (string)
    • Timestamp
    • Unknown field (1-3 variants)

Badge references in protocol:

  • Server sends client_badges field in client info events
  • Format: overwolf=0:badges=GUID1=GUID2=...
  • Client looks up badge definitions by GUID, fetches icon by URL base

Architecture

New module: crates/chanora_cache/src/badges.rs

badges.rs
├── BadgeMeta { guid, name, url_base, description }
├── BadgeCache
│   ├── fetch_badge_list() -> Vec<BadgeMeta>
│   ├── get_icon(guid) -> Option<bytes>
│   ├── refresh_if_stale()
│   └── metadata: RwLock<HashMap<Guid, BadgeMeta>>

Extend chanora_cache/src/lib.rs

  • Add PREFIX_BADGE: &str = "bg_" for badge icon blobs
  • Update validate_key() to accept badge prefix (GUID format: 8-4-4-4-12 hex)

Data Flow

1. On connect → server sends client_badges (GUIDs)
2. BadgeCache.refresh_if_stale():
   - Check last_refresh timestamp
   - If >24h: fetch badges-content.teamspeak.com/list
   - Parse Protobuf → Vec<BadgeMeta>
   - Update metadata map
3. For each GUID in client_badges:
   - Lookup BadgeMeta by GUID
   - Fetch icon from url_base (HTTP GET)
   - Cache icon blob in BlobCache (PREFIX_BADGE + guid)
   - Return icon bytes to UI
4. Flutter UI displays badge icons in:
   - Client info panel (profile)
   - Chat message sender badges

Dependencies

Crate Status Purpose
reqwest Already in chanora_protocol HTTP fetch
prost Add Protobuf parsing
cacache Already in chanora_cache Blob storage
tokio Already Async runtime

Cargo.toml additions for chanora_cache:

reqwest = { version = "0.13", default-features = false, features = ["rustls-tls"] }
prost = "0.13"

Integration Points

Component Integration
chanora_protocol/src/adapter.rs Parse client_badges from notifyclientupdated events
chanora_bridge Expose get_badge_icon(guid) and get_client_badges(client_id) to Flutter
Flutter UI Display badge icons in ClientInfoPanel and ChatMessage widgets

Files to Create/Modify

  • Create: crates/chanora_cache/src/badges.rs
  • Modify: crates/chanora_cache/src/lib.rs (add PREFIX_BADGE, export module)
  • Modify: crates/chanora_cache/Cargo.toml (add reqwest, prost)
  • Modify: crates/chanora_protocol/src/adapter.rs (parse client_badges)
  • Modify: crates/chanora_bridge/src/ (expose badge API to Flutter)

Error Handling

  • Network failure: Log warning, serve stale cache if available, retry on next refresh
  • Protobuf parse error: Log error, keep previous badge data, alert user "badges unavailable"
  • Icon fetch failure: Cache miss, show placeholder or skip badge display

Effort Estimate

L (3-5 days)

  • Day 1: Protobuf schema research, add prost dependency, parse badge list
  • Day 2: BadgeCache implementation, icon fetch and caching
  • Day 3: Protocol adapter integration (parse client_badges)
  • Day 4: Flutter bridge API, UI display
  • Day 5: Testing, error handling, edge cases

Open Questions

  1. Protobuf schema: Need to define .proto file or use dynamic parsing. Prost requires compile-time schema — may need to reverse-engineer from reference or use protobuf crate for runtime parsing.
  2. Icon URL construction: Exact URL pattern for badge icons needs verification (likely https://badges-content.teamspeak.com/{url_base}.png).
  3. Offline mode: Should we ship a static badge list as fallback? (YAGNI for now)