from __future__ import annotations import logging from yuxi.channel.extensions.googlechat.api import ( delete_message, send_message, update_message, ) from yuxi.channel.extensions.googlechat.formatting import ( build_typing_indicator_text, chunk_markdown_text, resolve_bot_display_name, ) from yuxi.channel.extensions.googlechat.types import ResolvedGoogleChatAccount logger = logging.getLogger(__name__) class TypingPlaceholder: def __init__(self, message_name: str, space_name: str, consumed: bool = False): self.message_name = message_name self.space_name = space_name self.consumed = consumed async def send_typing_placeholder( account: ResolvedGoogleChatAccount, space_name: str, bot_name: str | None = None, ) -> TypingPlaceholder: name = bot_name or resolve_bot_display_name(account.config if hasattr(account, "config") else {}) text = build_typing_indicator_text(name) result = await send_message(account, space_name, text) message_name = result.get("name", "") return TypingPlaceholder(message_name=message_name, space_name=space_name) async def deliver_text_chunk( account: ResolvedGoogleChatAccount, space_name: str, text: str, placeholder: TypingPlaceholder | None = None, thread_name: str | None = None, ) -> str: if placeholder and not placeholder.consumed: await update_message(account, placeholder.message_name, text) placeholder.consumed = True return placeholder.message_name if placeholder and placeholder.consumed: result = await send_message(account, space_name, text, thread_name=thread_name) return result.get("name", "") result = await send_message(account, space_name, text, thread_name=thread_name) return result.get("name", "") async def deliver_text_with_streaming( account: ResolvedGoogleChatAccount, space_name: str, full_text: str, bot_name: str | None = None, thread_name: str | None = None, text_chunk_limit: int = 4000, ) -> list[str]: chunks = chunk_markdown_text(full_text, text_chunk_limit) if not chunks: return [] placeholder = await send_typing_placeholder(account, space_name, bot_name) message_names = [] for i, chunk in enumerate(chunks): try: name = await deliver_text_chunk( account, space_name, chunk, placeholder if i == 0 else None, thread_name ) if name: message_names.append(name) except Exception as e: logger.warning("Failed to deliver chunk %d: %s", i, e) return message_names async def clear_typing_placeholder( account: ResolvedGoogleChatAccount, placeholder: TypingPlaceholder, ) -> None: try: await delete_message(account, placeholder.message_name) except Exception: try: await update_message(account, placeholder.message_name, "") except Exception: pass