import asyncio import logging from collections.abc import Awaitable, Callable logger = logging.getLogger(__name__) MC_CHAT_MAX_CHARS = 256 CHUNK_PREFIX_OVERHEAD = 8 DELAY_BETWEEN_CHUNKS = 0.5 def split_for_minecraft( content: str, max_chars: int = MC_CHAT_MAX_CHARS, page_indicator: bool = True, ) -> list[str]: result: list[str] = [] remaining = content while remaining: available = max_chars if len(remaining) <= available: result.append(remaining) break split_at = -1 for sep in ("\n", "。", "!", "?", ". ", "! ", "? ", ",", ", ", " "): pos = remaining.rfind(sep, 0, available) if pos > available * 0.5: split_at = pos + len(sep) break if split_at == -1: split_at = available result.append(remaining[:split_at].rstrip()) remaining = remaining[split_at:].lstrip() if page_indicator and len(result) > 1: total = len(result) result = [f"[{i + 1}/{total}] {chunk}" for i, chunk in enumerate(result)] return result async def stream_block_minecraft( send_fn: Callable[[str], Awaitable[None]], content: str, chunk_delay: float = DELAY_BETWEEN_CHUNKS, ) -> None: chunks = split_for_minecraft(content) total = len(chunks) logger.info("Minecraft streaming: %d chars -> %d chunks", len(content), total) for i, chunk in enumerate(chunks): await send_fn(chunk) if i < total - 1: logger.debug("Chunk %d/%d sent, waiting %.1fs", i + 1, total, chunk_delay) await asyncio.sleep(chunk_delay)