chat_archive
This package contains an Owlbot module that writes Owncast chat activity and stream context updates to JSON files for later processing.
Behavior
An archive is the JSON file this module writes for one live stream. The module starts an archive when Owlbot sees a live stream, including streams that are already live when the module loads.
While an archive is active, the module records chat messages, user joins, user parts, name changes, stream title updates, and message visibility updates in the order Owlbot dispatches them. Events outside an active stream are ignored.
Archive files are stream-scoped. When a stream ends, the module keeps that stream's archive open for a five-minute grace period so it can capture chat that Owncast still accepts after the stream stops. If no new stream starts during that window, the archive is finalized when the grace period expires. If a new stream starts first, the stopped stream's archive is finalized immediately and a new archive is opened for the new stream. Module unload also finalizes the active archive.
Configuration
modules:
chat_archive:
enabled: true
archive_dir: "data/chat_archives"
pretty_json: true
archive_dir: directory where archive files are written. The module creates it if needed.pretty_json: when true, write indented JSON. When false, write compact JSON.
Archive Format
Archive filenames are based on the detected stream start time when available:
chat-archive-20260526T120000Z.json
If a file already exists for that timestamp, the module adds a suffix such as
chat-archive-20260526T120000Z-2.json.
Each archive is a single JSON document with these top-level fields:
archive_start_reason: one ofmodule_startuporstream_started_event.archive_started_at: ISO 8601 timestamp for when the archive file was started.archive_updated_at: ISO 8601 timestamp for the most recent archive write.archive_stopped_at: ISO 8601 timestamp for when the archive was finalized, ornullwhile the archive is active.archive_stop_reason: reason the archive was finalized, ornullwhile the archive is active or waiting through the post-stream grace period. Current values arestream_stopped_event,module_teardown, andreplaced_by_stream_started_event.stream_title: stream title when the archive started, ornullif unavailable.stream_stopped_at: stream stop timestamp recorded when Owlbot receives a stream stop event, ornullotherwise.event_count: number of objects inevents.events: ordered list of captured activity events.
Each object in events contains:
event_type: one ofCHAT,USER_JOINED,USER_PARTED,NAME_CHANGE,STREAM_TITLE_UPDATED, orVISIBILITY-UPDATE.archived_at: ISO 8601 timestamp for when the event was written.event_data: parsed Owlbot event data converted to JSON-compatible values. The exact fields depend onevent_type.
Abridged example archive:
{
"archive_start_reason": "stream_started_event",
"archive_started_at": "2026-05-26T12:00:01.000000+00:00",
"archive_updated_at": "2026-05-26T12:01:00.000000+00:00",
"archive_stopped_at": null,
"archive_stop_reason": null,
"stream_title": "Game Night",
"stream_stopped_at": null,
"event_count": 1,
"events": [
{
"event_type": "CHAT",
"archived_at": "2026-05-26T12:01:00.000000+00:00",
"event_data": {
"message_id": "msg-1",
"raw_body": "hello chat",
"body": "hello chat",
"is_visible": true
}
}
]
}
Owlbot modules receive parsed event dataclasses rather than the untouched
Owncast webhook payload. event_data stores that parsed event data after
converting values such as datetimes and sets into JSON-compatible values.
The archive is rewritten after each captured event through a temporary sibling file and atomic replace. For an existing archive file, readers should see either the previous complete JSON document or the next complete JSON document.