# Copyright (c) 2026 Krzysztof Rudnicki. MIT licence. """Answering a specific message rather than the room. The container advertises this: /v1/about reports ``capabilities: {"v2/send": ["quotes", "mentions"]}`` and /v2/send accepts ``quote_timestamp``, ``quote_author`` and ``quote_message``. A quote needs the *sent timestamp of the message being answered*, which is why ``IncomingMessage`` had to start carrying one -- parse_envelope threw it away, so until now nothing downstream could have built a quote even in principle. """ from __future__ import annotations from dataclasses import dataclass from typing import TYPE_CHECKING if TYPE_CHECKING: from signal_bot.models import IncomingMessage @dataclass(frozen=True, slots=True) class Quote: """The message a reply points at.""" timestamp: int author: str text: str def payload(self) -> dict[str, str | int]: """The three keys /v2/send wants for a quoted reply.""" return { "quote_timestamp": self.timestamp, "quote_author": self.author, "quote_message": self.text, } def quote_of(message: IncomingMessage) -> Quote | None: """Return the quote that answers this message, or None if it has none. Both fields are required by the API, and an envelope missing either is a shape this bot has not seen -- worth replying without a quote rather than sending something the container will reject. """ if not message.timestamp or not message.source_uuid: return None return Quote(message.timestamp, message.source_uuid, message.text or "")