Gate chat interaction on Twitch live status

This commit is contained in:
2026-05-12 18:49:28 -05:00
parent e3b0fc5be8
commit b249f82631
5 changed files with 255 additions and 2 deletions

View File

@@ -16,6 +16,7 @@ from app.memory.models import AgentActionType
from app.memory.repository import Repository
from app.exports.markdown import MarkdownExporter
from app.twitch.chat import TwitchChatMessage, TwitchIRCClient, set_active_client
from app.twitch.live import TwitchLiveStatusService
logger = logging.getLogger(__name__)
@@ -45,6 +46,7 @@ agent_loop_task: asyncio.Task | None = None
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
async def require_admin(
@@ -88,6 +90,16 @@ 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 not settings.TWITCH_REQUIRE_LIVE_STREAM:
return True
if not twitch_live_status:
return False
status = await twitch_live_status.get_status(channel_name)
return status.is_live
async def get_or_create_twitch_session(channel_name: str) -> str:
"""Use an active session for the Twitch channel, or create one."""
if not orchestrator:
@@ -140,12 +152,16 @@ async def start_twitch_chat() -> None:
@app.on_event("startup")
async def startup_event():
"""Initialize database and services on startup."""
global orchestrator, agent_loop_task
global orchestrator, agent_loop_task, twitch_live_status
try:
await init_db()
twitch_live_status = TwitchLiveStatusService(
cache_seconds=settings.TWITCH_LIVE_STATUS_CACHE_SECONDS
)
orchestrator = AgentOrchestrator(
loop_interval_seconds=settings.AGENT_LOOP_INTERVAL_SECONDS
)
orchestrator.set_chat_interaction_gate(channel_is_live_for_chat)
await orchestrator.restore_active_sessions()
await start_twitch_chat()
agent_loop_task = asyncio.create_task(agent_loop())
@@ -158,7 +174,7 @@ async def startup_event():
@app.on_event("shutdown")
async def shutdown_event():
"""Clean up resources on shutdown."""
global twitch_chat_client
global twitch_chat_client, twitch_live_status
if twitch_chat_task:
twitch_chat_task.cancel()
@@ -168,6 +184,7 @@ async def shutdown_event():
await twitch_chat_client.disconnect()
set_active_client(None)
twitch_chat_client = None
twitch_live_status = None
if agent_loop_task:
agent_loop_task.cancel()
with suppress(asyncio.CancelledError):
@@ -470,9 +487,16 @@ async def get_twitch_status() -> dict:
"""Get Twitch chat connection status."""
configured = twitch_configured()
client_status = twitch_chat_client.status() if twitch_chat_client else {}
live_status = None
if twitch_live_status and settings.TWITCH_CHANNEL_NAME:
live_status = (
await twitch_live_status.get_status(settings.TWITCH_CHANNEL_NAME)
).to_dict()
return {
"configured": configured,
"running": bool(twitch_chat_task and not twitch_chat_task.done()),
"require_live_stream": settings.TWITCH_REQUIRE_LIVE_STREAM,
"live_status": live_status,
"session_id": twitch_session_id,
**client_status,
"timestamp": datetime.utcnow().isoformat(),
@@ -508,6 +532,15 @@ async def get_current_stream_status() -> dict:
dashboard_data = serialize_dashboard(dashboard)
runtime = orchestrator.get_hearthkeeper_runtime_status(session_id)
twitch_status = twitch_chat_client.status() if twitch_chat_client else {}
live_status = None
if twitch_live_status:
live_status = (
await twitch_live_status.get_status(session_info["channel_name"])
).to_dict()
chat_interaction_allowed = (
not settings.TWITCH_REQUIRE_LIVE_STREAM
or bool(live_status and live_status["is_live"])
)
return {
"session": {
@@ -523,6 +556,8 @@ async def get_current_stream_status() -> dict:
"twitch": {
"configured": twitch_configured(),
"running": bool(twitch_chat_task and not twitch_chat_task.done()),
"require_live_stream": settings.TWITCH_REQUIRE_LIVE_STREAM,
"live_status": live_status,
"session_id": twitch_session_id,
**twitch_status,
},
@@ -534,6 +569,7 @@ async def get_current_stream_status() -> dict:
},
"hearthkeeper": {
**runtime,
"chat_interaction_allowed": chat_interaction_allowed,
"last_human_chat_at": (
latest_human_message.timestamp.isoformat()
if latest_human_message