From 5c920fa3091e656f6022a186cf9330c270b43519 Mon Sep 17 00:00:00 2001 From: Ken Schaefer Date: Tue, 12 May 2026 18:57:49 -0500 Subject: [PATCH] Add offline Twitch chat test mode --- app/config.py | 1 + app/main.py | 26 +++++++++++++++++++++++++- docker-compose.yml | 1 + 3 files changed, 27 insertions(+), 1 deletion(-) diff --git a/app/config.py b/app/config.py index 6fd92fd..81e8e73 100644 --- a/app/config.py +++ b/app/config.py @@ -50,6 +50,7 @@ class Settings(BaseSettings): TWITCH_CHAT_ENABLED: bool = True TWITCH_REQUIRE_LIVE_STREAM: bool = True TWITCH_LIVE_STATUS_CACHE_SECONDS: int = 60 + TWITCH_OFFLINE_CHAT_TEST_MODE: bool = False # LLM LLM_PROVIDER: Optional[str] = None # "openai", "ollama", "lm_studio", or None diff --git a/app/main.py b/app/main.py index 9c58de1..2749665 100644 --- a/app/main.py +++ b/app/main.py @@ -47,6 +47,7 @@ twitch_chat_client: TwitchIRCClient | None = None twitch_chat_task: asyncio.Task | None = None twitch_session_id: str | None = None twitch_live_status: TwitchLiveStatusService | None = None +offline_chat_test_mode: bool = settings.TWITCH_OFFLINE_CHAT_TEST_MODE async def require_admin( @@ -92,6 +93,8 @@ def twitch_configured() -> bool: async def channel_is_live_for_chat(channel_name: str) -> bool: """Return whether the agent may interact with chat for a channel.""" + if offline_chat_test_mode: + return True if not settings.TWITCH_REQUIRE_LIVE_STREAM: return True if not twitch_live_status: @@ -496,6 +499,7 @@ async def get_twitch_status() -> dict: "configured": configured, "running": bool(twitch_chat_task and not twitch_chat_task.done()), "require_live_stream": settings.TWITCH_REQUIRE_LIVE_STREAM, + "offline_chat_test_mode": offline_chat_test_mode, "live_status": live_status, "session_id": twitch_session_id, **client_status, @@ -503,6 +507,24 @@ async def get_twitch_status() -> dict: } +@app.post("/admin/twitch/offline-test-mode", dependencies=[Depends(require_admin)]) +async def set_offline_chat_test_mode(enabled: bool = Form(...)) -> dict: + """Allow or block Twitch chat posting while the stream is offline.""" + global offline_chat_test_mode + + offline_chat_test_mode = enabled + return { + "status": "offline_chat_test_mode_updated", + "offline_chat_test_mode": offline_chat_test_mode, + "warning": ( + "Agent may post to Twitch chat while the stream is offline" + if offline_chat_test_mode + else None + ), + "timestamp": datetime.utcnow().isoformat(), + } + + @app.get("/admin/stream/status", dependencies=[Depends(require_admin)]) async def get_current_stream_status() -> dict: """Get one operator view of the current Twitch stream runtime state.""" @@ -538,7 +560,8 @@ async def get_current_stream_status() -> dict: await twitch_live_status.get_status(session_info["channel_name"]) ).to_dict() chat_interaction_allowed = ( - not settings.TWITCH_REQUIRE_LIVE_STREAM + offline_chat_test_mode + or not settings.TWITCH_REQUIRE_LIVE_STREAM or bool(live_status and live_status["is_live"]) ) @@ -557,6 +580,7 @@ async def get_current_stream_status() -> dict: "configured": twitch_configured(), "running": bool(twitch_chat_task and not twitch_chat_task.done()), "require_live_stream": settings.TWITCH_REQUIRE_LIVE_STREAM, + "offline_chat_test_mode": offline_chat_test_mode, "live_status": live_status, "session_id": twitch_session_id, **twitch_status, diff --git a/docker-compose.yml b/docker-compose.yml index ce5f55f..3635851 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -42,6 +42,7 @@ services: TWITCH_CHAT_ENABLED: ${TWITCH_CHAT_ENABLED:-true} TWITCH_REQUIRE_LIVE_STREAM: ${TWITCH_REQUIRE_LIVE_STREAM:-true} TWITCH_LIVE_STATUS_CACHE_SECONDS: ${TWITCH_LIVE_STATUS_CACHE_SECONDS:-60} + TWITCH_OFFLINE_CHAT_TEST_MODE: ${TWITCH_OFFLINE_CHAT_TEST_MODE:-false} LLM_PROVIDER: ${LLM_PROVIDER:-} LLM_BASE_URL: ${LLM_BASE_URL:-} LLM_API_KEY: ${LLM_API_KEY:-}