Spaces:
Sleeping
Sleeping
Download bot/bootstrap_runtime.py from JuanCS-Dev/twitch-byte-bot: direct link, hf CLI and curl.
- Browser
- Download file 5.93 kB
-
https://huggingface.co/spaces/JuanCS-Dev/twitch-byte-bot/resolve/main/bot/bootstrap_runtime.py
- Command line
-
hf download hf://spaces/JuanCS-Dev/twitch-byte-bot/bot/bootstrap_runtime.py
-
curl -L -o bootstrap_runtime.py https://huggingface.co/spaces/JuanCS-Dev/twitch-byte-bot/resolve/main/bot/bootstrap_runtime.py
5.93 kB
| import asyncio | |
| import os | |
| from bot.autonomy_runtime import autonomy_runtime | |
| from bot.clip_jobs_runtime import clip_jobs | |
| from bot.eventsub_runtime import ByteBot | |
| from bot.irc_runtime import IrcByteBot | |
| from bot.observability import observability | |
| from bot.persistence_layer import persistence | |
| from bot.runtime_config import ( | |
| TWITCH_BOT_LOGIN, | |
| TWITCH_CHANNEL_LOGIN, | |
| TWITCH_CHANNEL_LOGINS_RAW, | |
| TWITCH_CLIENT_ID, | |
| TWITCH_CLIENT_SECRET_INLINE, | |
| TWITCH_CLIENT_SECRET_NAME, | |
| TWITCH_IRC_HOST, | |
| TWITCH_IRC_PORT, | |
| TWITCH_IRC_TLS, | |
| TWITCH_TOKEN_REFRESH_MARGIN_SECONDS, | |
| logger, | |
| ) | |
| from bot.sentiment_engine import sentiment_engine | |
| from bot.status_runtime import parse_channel_logins | |
| from bot.twitch_tokens import TwitchTokenManager, TwitchTokenManagerSettings | |
| def get_secret(secret_name: str = "twitch-client-secret") -> str: | |
| value = os.environ.get("TWITCH_CLIENT_SECRET") or "" | |
| if not value: | |
| raise RuntimeError(f"Secret não configurado: {secret_name}") | |
| return value | |
| def require_env(name: str) -> str: | |
| value = (os.environ.get(name) or "").strip() | |
| if not value: | |
| raise RuntimeError(f"Variavel obrigatoria ausente: {name}") | |
| return value | |
| async def resolve_irc_channel_logins() -> list[str]: | |
| """Resolve a lista de canais para entrar (Supabase com Fallback para ENV).""" | |
| # Tentativa 1: Supabase (PersistenceLayer) | |
| db_channels = await persistence.get_active_channels() | |
| if db_channels: | |
| return db_channels | |
| # Tentativa 2: Variáveis de Ambiente (Legado/Fallback) | |
| explicit_channels = parse_channel_logins(TWITCH_CHANNEL_LOGINS_RAW) | |
| if explicit_channels: | |
| return explicit_channels | |
| fallback_channels = parse_channel_logins(TWITCH_CHANNEL_LOGIN) | |
| if fallback_channels: | |
| return fallback_channels | |
| required_single_channel = require_env("TWITCH_CHANNEL_LOGIN") | |
| required_channels = parse_channel_logins(required_single_channel) | |
| if not required_channels: | |
| raise RuntimeError( | |
| "TWITCH_CHANNEL_LOGIN invalido. Use login Twitch sem # e com 3-25 caracteres." | |
| ) | |
| return required_channels | |
| def resolve_client_secret_for_irc_refresh() -> str: | |
| if TWITCH_CLIENT_SECRET_INLINE: | |
| return TWITCH_CLIENT_SECRET_INLINE | |
| secret_name = TWITCH_CLIENT_SECRET_NAME or "twitch-client-secret" | |
| try: | |
| return get_secret(secret_name=secret_name) | |
| except RuntimeError as error: | |
| logger.warning("Falha ao carregar secret '%s': %s", secret_name, error) | |
| return "" | |
| except Exception as error: | |
| logger.warning("Falha ao carregar secret '%s': %s", secret_name, error) | |
| return "" | |
| def build_irc_token_manager() -> TwitchTokenManager: | |
| user_token = require_env("TWITCH_USER_TOKEN") | |
| refresh_token = os.environ.get("TWITCH_REFRESH_TOKEN") | |
| client_id = TWITCH_CLIENT_ID or require_env("TWITCH_CLIENT_ID") | |
| client_secret = resolve_client_secret_for_irc_refresh() | |
| return TwitchTokenManager( | |
| access_token=user_token, | |
| refresh_token=refresh_token, | |
| client_id=client_id, | |
| client_secret=client_secret or "", | |
| refresh_margin_seconds=TWITCH_TOKEN_REFRESH_MARGIN_SECONDS, | |
| settings=TwitchTokenManagerSettings(), | |
| observability=observability, | |
| logger=logger, | |
| ) | |
| def run_irc_mode() -> None: | |
| try: | |
| from bot.logic import context_manager | |
| from bot.runtime_config import irc_channel_control | |
| token_manager = build_irc_token_manager() | |
| async def run_with_channel_control() -> None: | |
| # Injeta o loop para persistência cross-thread (Dashboard) | |
| running_loop = asyncio.get_running_loop() | |
| context_manager.set_main_loop(running_loop) | |
| # Sincroniza canais permitidos (Supabase ou ENV) | |
| channel_logins = await resolve_irc_channel_logins() | |
| bot = IrcByteBot( | |
| host=TWITCH_IRC_HOST, | |
| port=TWITCH_IRC_PORT, | |
| use_tls=TWITCH_IRC_TLS, | |
| bot_login=TWITCH_BOT_LOGIN or require_env("TWITCH_BOT_LOGIN"), | |
| channel_logins=channel_logins, | |
| token_manager=token_manager, | |
| ) | |
| irc_channel_control.bind(loop=running_loop, bot=bot) | |
| async def verify_clips_auth_loop() -> None: | |
| while True: | |
| try: | |
| await token_manager.validate_clips_auth() | |
| except Exception as e: | |
| logger.error("Falha ao validar auth de clips: %s", e) | |
| await asyncio.sleep(3600) | |
| # Start background tasks | |
| asyncio.create_task(verify_clips_auth_loop()) | |
| # Fase 4: Loop de limpeza de memoria (Context + Sentiment) | |
| asyncio.create_task(context_manager.start_cleanup_loop()) | |
| try: | |
| await bot.run_forever() | |
| finally: | |
| clip_jobs.stop() | |
| autonomy_runtime.unbind() | |
| irc_channel_control.unbind() | |
| asyncio.run(run_with_channel_control()) | |
| except Exception as error: | |
| logger.critical("Fatal Byte Error (IRC): %s", error) | |
| observability.record_error(category="fatal_irc", details=str(error)) | |
| def run_eventsub_mode() -> None: | |
| async def run_async() -> None: | |
| from bot.logic import context_manager | |
| context_manager.set_main_loop(asyncio.get_running_loop()) | |
| require_env("TWITCH_CLIENT_ID") | |
| require_env("TWITCH_BOT_ID") | |
| require_env("TWITCH_CHANNEL_ID") | |
| bot = ByteBot(client_secret=get_secret()) | |
| try: | |
| await bot.run() | |
| finally: | |
| autonomy_runtime.unbind() | |
| try: | |
| asyncio.run(run_async()) | |
| except Exception as error: | |
| logger.critical("Fatal Byte Error (EventSub): %s", error) | |
| observability.record_error(category="fatal_eventsub", details=str(error)) | |