Connect orchestrator to Twitch chat
This commit is contained in:
101
app/main.py
101
app/main.py
@@ -14,6 +14,7 @@ from app.memory.database import init_db
|
||||
from app.memory.database import get_session as get_db_session
|
||||
from app.memory.repository import Repository
|
||||
from app.exports.markdown import MarkdownExporter
|
||||
from app.twitch.chat import TwitchChatMessage, TwitchIRCClient, set_active_client
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -40,6 +41,9 @@ class DashboardRequest(BaseModel):
|
||||
# Global orchestrator instance
|
||||
orchestrator: AgentOrchestrator | None = None
|
||||
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
|
||||
|
||||
|
||||
async def require_admin(
|
||||
@@ -73,6 +77,78 @@ async def agent_loop() -> None:
|
||||
await asyncio.sleep(orchestrator.loop_interval_seconds)
|
||||
|
||||
|
||||
def twitch_configured() -> bool:
|
||||
"""Return whether Twitch chat has enough runtime configuration to start."""
|
||||
return bool(
|
||||
settings.TWITCH_CHAT_ENABLED
|
||||
and settings.TWITCH_CHANNEL_NAME
|
||||
and settings.TWITCH_BOT_USERNAME
|
||||
and settings.TWITCH_ACCESS_TOKEN
|
||||
)
|
||||
|
||||
|
||||
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:
|
||||
raise RuntimeError("Orchestrator not initialized")
|
||||
|
||||
normalized_channel = channel_name.lower()
|
||||
matching_sessions = [
|
||||
(session_id, session)
|
||||
for session_id, session in orchestrator.active_sessions.items()
|
||||
if session.get("channel_name", "").lower() == normalized_channel
|
||||
]
|
||||
if matching_sessions:
|
||||
session_id, _ = max(
|
||||
matching_sessions,
|
||||
key=lambda item: item[1].get("started_at", datetime.min),
|
||||
)
|
||||
return session_id
|
||||
|
||||
return await orchestrator.start_session(channel_name)
|
||||
|
||||
|
||||
async def handle_twitch_chat_message(message: TwitchChatMessage) -> None:
|
||||
"""Route a Twitch chat message into the orchestrator."""
|
||||
if not orchestrator or not twitch_session_id:
|
||||
return
|
||||
|
||||
if settings.TWITCH_BOT_USERNAME and (
|
||||
message.username.lower() == settings.TWITCH_BOT_USERNAME.lower()
|
||||
):
|
||||
return
|
||||
|
||||
await orchestrator.handle_chat_message(
|
||||
session_id=twitch_session_id,
|
||||
username=message.display_name or message.username,
|
||||
message=message.content,
|
||||
)
|
||||
|
||||
|
||||
async def start_twitch_chat() -> None:
|
||||
"""Start Twitch chat monitoring when configured."""
|
||||
global twitch_chat_client, twitch_chat_task, twitch_session_id
|
||||
|
||||
if not orchestrator or not twitch_configured():
|
||||
logger.info("Twitch chat listener not started; configuration is incomplete")
|
||||
return
|
||||
|
||||
twitch_session_id = await get_or_create_twitch_session(settings.TWITCH_CHANNEL_NAME)
|
||||
twitch_chat_client = TwitchIRCClient(
|
||||
channel_name=settings.TWITCH_CHANNEL_NAME,
|
||||
bot_username=settings.TWITCH_BOT_USERNAME,
|
||||
access_token=settings.TWITCH_ACCESS_TOKEN,
|
||||
on_message=handle_twitch_chat_message,
|
||||
)
|
||||
set_active_client(twitch_chat_client)
|
||||
twitch_chat_task = asyncio.create_task(twitch_chat_client.run())
|
||||
logger.info(
|
||||
"Twitch chat listener starting for %s on session %s",
|
||||
settings.TWITCH_CHANNEL_NAME,
|
||||
twitch_session_id,
|
||||
)
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup_event():
|
||||
"""Initialize database and services on startup."""
|
||||
@@ -84,6 +160,7 @@ async def startup_event():
|
||||
)
|
||||
await orchestrator.restore_active_sessions()
|
||||
agent_loop_task = asyncio.create_task(agent_loop())
|
||||
await start_twitch_chat()
|
||||
logger.info("Application started successfully")
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to start application: {e}")
|
||||
@@ -93,6 +170,16 @@ async def startup_event():
|
||||
@app.on_event("shutdown")
|
||||
async def shutdown_event():
|
||||
"""Clean up resources on shutdown."""
|
||||
global twitch_chat_client
|
||||
|
||||
if twitch_chat_task:
|
||||
twitch_chat_task.cancel()
|
||||
with suppress(asyncio.CancelledError):
|
||||
await twitch_chat_task
|
||||
if twitch_chat_client:
|
||||
await twitch_chat_client.disconnect()
|
||||
set_active_client(None)
|
||||
twitch_chat_client = None
|
||||
if agent_loop_task:
|
||||
agent_loop_task.cancel()
|
||||
with suppress(asyncio.CancelledError):
|
||||
@@ -308,6 +395,20 @@ async def get_loop_status() -> dict:
|
||||
}
|
||||
|
||||
|
||||
@app.get("/admin/twitch/status", dependencies=[Depends(require_admin)])
|
||||
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 {}
|
||||
return {
|
||||
"configured": configured,
|
||||
"running": bool(twitch_chat_task and not twitch_chat_task.done()),
|
||||
"session_id": twitch_session_id,
|
||||
**client_status,
|
||||
"timestamp": datetime.utcnow().isoformat(),
|
||||
}
|
||||
|
||||
|
||||
@app.post("/admin/loop/frequency", dependencies=[Depends(require_admin)])
|
||||
async def set_loop_frequency(interval_seconds: float = Form(...)) -> dict:
|
||||
"""Set how frequently the background agent loop runs."""
|
||||
|
||||
Reference in New Issue
Block a user