Skip to content

Cards

Chattice provides typed facade builders over the official google-apps-card SDK — no manual Cards JSON.

Facades

card = Card(
    header=CardHeader(title="Deploy production?"),
    sections=[
        Section(
            widgets=[
                TextParagraph("Deploy v2.1?"),
                ButtonList(
                    buttons=[
                        Button(
                            "Deploy",
                            action="deploy.confirm",
                            parameters={"env": "prod"},
                        ),
                        Button("Cancel", action="deploy.cancel"),
                    ]
                ),
            ]
        )
    ],
)

Each facade builds the SDK proto via to_proto(), serializes via to_dict() (documented camelCase Cards v2 JSON), and rebuilds via from_dict()/from_proto(). Card.from_dict() takes a lossless raw JSON snapshot: unknown top-level/known-widget fields survive to_dict(), and unsupported widgets become RawWidget facade entries instead of raising or being dropped. The raw .proto object remains the escape hatch for future Google SDK fields; schema-unknown JSON cannot be retained by protobuf itself, so use the JSON path when exact unknown-field round-trip matters.

Image(image_url=..., alt_text=..., on_click=...) and Image.from_url() are the typed HTTPS picture widget. Image.from_path() and Image.from_bytes() store a private immutable source description; the Bot resolves that source through an application-provided AssetPublisher, builds a new resolved Card, and only then serializes it. The user's Card remains unchanged. Card asset publication is deliberately separate from message attachments and their USER authentication semantics (see files-media). Published Card image URLs are not a confidential-media channel: Google's image fetcher must be able to retrieve them. Confidential generated files belong in native USER-authenticated attachments or private Drive links.

Buttons and actions

Button(action=..., parameters=...) produces onClick.action with string parameters — the same shape normalized into ActionEvent, so clicks route through @router.action("deploy.confirm").

A button can also open a dialog: Button(..., interaction=ButtonInteraction.OPEN_DIALOG) serializes onClick.action.interaction: "OPEN_DIALOG" — see Dialogs & App Home.

Forms and validation

Form widgets carry the full documented field set:

from chattice.cards import (
    DateTimePicker,
    SelectionInput,
    TextInput,
    TextInputType,
    Validation,
)

text = TextInput(
    name="email",
    label="Email",
    hint_text="email address",
    value="",
    validation=Validation(character_limit=254, input_type=TextInputType.EMAIL),
)
select = SelectionInput(
    name="tier",
    label="Tier",
    items=[{"value": "free", "text": "Free"}, {"value": "pro", "text": "Pro"}],
)
picker = DateTimePicker(name="deadline", label="Deadline")
  • TextInputname, label, optional hint_text, value, and validation.
  • SelectionInputname, label, and items (value/text pairs).
  • DateTimePickername, label, optional value_ms_epoch and timezone_offset_date.

Validation enforces rules client-side per the documented JSON: characterLimit (max input length) and inputType (TEXT, INTEGER, FLOAT, EMAIL, EMOJI_PICKER).

Submitted widget values arrive back in event.form_inputs as typed FormInputs values (StringInput, DateInput, DateTimeInput, TimeInput) via the interaction parser, and Section.from_proto() rebuilds form widgets from a raw SDK proto.

Sync card updates

A handler returning a Card: - for MESSAGE events → {"cardsV2": [...]}; - for CARD_CLICKED → {"actionResponse": {"type": "UPDATE_MESSAGE"}, "cardsV2": [...]}.

UPDATE_MESSAGE is documented as «only permitted on a CARD_CLICKED event where the message sender type is BOT». Updating cards on human messages uses UPDATE_USER_MESSAGE_CARDS (implemented, sender-derived: BOT → UPDATE_MESSAGE, HUMAN → UPDATE_USER_MESSAGE_CARDS; a MESSAGE with a matched URL also updates via UPDATE_USER_MESSAGE_CARDS). Async card updates go through bot.app.messages.update.