twitch-byte-bot / bot /channel_points_runtime.py
JuanCS-Dev's picture
Deploy: streamer Stream Deck dashboard at /studio
8e12fe5 verified
Raw History Blame Contribute Delete
2.17 kB
"""Channel Points runtime — viewers redeem points to make Byte speak.
The 2026 flagship engagement: a viewer redeems a custom reward and Byte voices
their message in persona. Redemptions to the dedicated "voice" reward are
spoken (sanitized + length-capped); anything else is filed as a streamer
suggestion in the risk queue. Pure/​testable core; the EventSub subscription
that feeds it activates in eventsub mode with the channel:read:redemptions scope.
"""
from __future__ import annotations
from typing import Any
from bot.control_plane import control_plane
from bot.control_plane_constants import RISK_SUGGEST_STREAMER
from bot.voice_runtime import voice_runtime
MAX_REDEMPTION_CHARS = 200
_VOICE_REWARD_MARKERS = ("byte fala", "byte fale", "fala byte", "byte tts", "byte speak")
def is_voice_reward(reward_title: str) -> bool:
lowered = (reward_title or "").strip().lower()
return any(marker in lowered for marker in _VOICE_REWARD_MARKERS)
def _sanitize(text: str) -> str:
return " ".join((text or "").split())[:MAX_REDEMPTION_CHARS]
def process_redemption(user: str, reward_title: str, user_input: str = "") -> dict[str, Any]:
"""Turn a Channel Points redemption into voice (voice reward) or a suggestion."""
safe_user = (user or "viewer").strip() or "viewer"
message = _sanitize(user_input)
if is_voice_reward(reward_title):
if not message:
return {"ok": False, "reason": "empty_message"}
spoken = voice_runtime.say(f"{safe_user} diz: {message}", source="channel_points")
return {"ok": True, "action": "voice", "spoke": spoken.get("spoke", False)}
# Non-voice rewards go to the risk queue for the streamer to handle.
item = control_plane.enqueue_action(
kind="channel_points",
risk=RISK_SUGGEST_STREAMER,
title=f"Resgate de {safe_user}: {(_sanitize(reward_title) or 'recompensa')}",
body=message or "(sem mensagem)",
payload={"user": safe_user, "reward": _sanitize(reward_title)},
created_by="channel_points",
)
return {"ok": True, "action": "queued", "item": item}
__all__ = ["is_voice_reward", "process_redemption"]