Chattice
Async, typed event framework for Google Chat apps — aiogram-quality DX, Google-native semantics.
from chattice import Dispatcher, F, Router
from chattice.events import MessageEvent
router = Router()
@router.message(F.text == "ping")
async def ping(message: MessageEvent) -> str:
return "pong" # synchronous interaction response (no auth, 30 s deadline)
dispatcher = Dispatcher()
dispatcher.include_router(router)
What this is
- Google Chat-native. Space, Thread, Message, Command, Action, Card, Dialog, App Home, Workspace Events are first-class typed objects. No Telegram concepts leak into the domain model.
- aiogram ergonomics. Routers, magic filters (
F), middleware, dependency injection, FSM — the DX you know, without Telegram semantics. - Explicit response model. A handler RETURN is the synchronous
interaction response (30 s deadline); outbound
Botcalls are separate authenticated operations. Contextualmessage.reply()/thread.send()/space.send()methods are zero-fetch adapters over that same Bot, never a hidden second channel. - Secure by default. Incoming verification is fail-closed, push endpoints require a verifier, outbound privacy gates (private messages, notifications, preview features) fail closed before any network call.
- Diagnosable in production. DEBUG routing logs identify the observer, handler, and result; Pub/Sub failures include safe delivery context, exception messages, and tracebacks without dumping raw event payloads.
- Core is small. Core abstracts Google Chat platform primitives and technical boilerplate ONLY. Business scenarios (polls, approvals, tickets) are assembled from the primitives in your application code — never added to core.
- Curated outbound facade. An operation registry is the single
source of outbound auth truth; identity-bound resource clients
(
bot.app.*/bot.user.*for spaces, messages, memberships, reactions, and per-user state) and one executor path make every Google call explicit about who is calling. - Explicit identity. APP and USER are always chosen by the caller
(
bot.raw.app()/bot.raw.user()); the framework never switches silently. Direct messages are first-class:bot.user.spaces.get_or_setup_direct_message(...)returns the officialSpacewith its clickablespace_uri. - Diagnosable in production. Structured lifecycle stages
(
handler_startedbefore invocation), stage-tagged error logs, Pub/Sub delivery identity/age, per-card update serialization, configurableRuntimeDiagnosticsthresholds, and CI-verified operation registry against the Google discovery document.
What this is not
- Not a Telegram framework. No
chat_id, noCallbackQuery, noUpdatehierarchy, no Telegram-style long polling (see the aiogram comparison). - Not a Google product. Independent open-source project, not endorsed by Google.
Two ways to run
Chattice has two honest startup models (see the aiogram side-by-side):
# HTTP — Google calls your HTTPS endpoint; the handler RETURN is the
# synchronous response (Dialogs and App Home work here).
app.include_router(create_chat_router(dispatcher, GoogleTokenVerifier(audience=...)))
# python -m uvicorn app:app
# Pub/Sub streaming pull — a persistent subscriber process, visually
# like aiogram's start_polling, but it is NOT Telegram polling.
# No domain/TLS needed; no Dialogs, no App Home — answers go outbound
# through the authenticated Bot.
await dispatcher.run_pubsub(subscription, bot=bot)
# python app.py
Capabilities
| Area | Support |
|---|---|
| Events | MESSAGE, CARD_CLICKED, APP_COMMAND (slash/quick/message actions), ADDED/REMOVED_FROM_SPACE, APP_HOME, SUBMIT_FORM, WIDGET_UPDATED |
| Routing | Routers, observer-wide filters, magic filters, middleware, DI |
| Cards | Typed facades, public/non-sensitive PNG/JPEG publication through an optional AssetPublisher, sync updates |
| Memberships | APP-auth add/get/list/remove for human users; caller-member Space listing |
| Dialogs | OPEN_DIALOG, submit/cancel observers, action status; capability-gated per transport |
| FSM | State/StatesGroup, Memory + Redis record storage (compare-and-set) |
| Ingress | HTTP (verified), Pub/Sub push (verified), Pub/Sub streaming pull, Workspace Events CloudEvents |
| Media | Local-file attachments sent with USER auth end to end (media.upload → messages.create, sender = HUMAN); download USER/APP; metadata APP; 200 MB local preflight |
| Auth | App (service account), user (OAuth refresh), incoming verification |
| Diagnostics | DEBUG routing diagnostics, structured Pub/Sub failure logs, typed Chat API errors |
| Testing | MockBot, EventFactory, assertions, fake transport |
Install
Python 3.11+ (3.11–3.13 tested). MIT-licensed.
pip install "chattice[fastapi,pubsub,redis]" # core + all integrations
# or pick only what you need:
# pip install chattice core
# pip install "chattice[fastapi]" FastAPI integration
# pip install "chattice[redis]" Redis FSM/idempotency storage
# pip install "chattice[pubsub]" Pub/Sub push + streaming pull
# pip install "chattice[media]" Media/attachment uploads (Google API client)
# pip install "chattice[gcs]" Local Card image publication through GCS
# pip install "chattice[gemini]" Experimental Gemini agent bridge
# using uv
uv add "chattice[fastapi,pubsub,redis]"
Quickstart
- Parse a documented interaction payload:
from chattice.adapters.google_chat import parse_interaction
event = parse_interaction(payload)
result = await dispatcher.feed_update(event)
- Serve HTTP interactions (FastAPI):
from fastapi import FastAPI
from chattice.integrations.fastapi import create_chat_router
from chattice.transports.http import GoogleTokenVerifier
app = FastAPI()
app.include_router(create_chat_router(dispatcher, GoogleTokenVerifier(audience="...")))
- Send messages:
from chattice.client import Bot
bot = Bot(credentials=...)
await bot.app.messages.create("spaces/AAA", text="hello")
Inside an interaction handler, configure the same Bot once and use the known Google context without extra fetches:
dispatcher = Dispatcher(bot=bot)
@router.message()
async def reply(message: MessageEvent) -> None:
await message.reply("hello in this thread")
Docs
Start with Installation, the
5-minute Quickstart, and the
Space → Thread → Message mental model. The
public API reference, Google mapping,
the debugging guide, and
aiogram comparison are linked from the docs site.
Runnable examples live in examples/; machine-readable documentation is available in
llms.txt.
Status
Pre-1.0 public release (current version 0.3.3) — see
CHANGELOG. The documented public
surface is the compatibility baseline: pre-1.0 work may add APIs but does not incompatibly
rename, remove, or change existing contracts. chattice.experimental
has no compatibility promise. bot.raw.app() / bot.raw.user() and raw event payloads
follow Google's SDK and wire schema rather than the stable facade
promise. Not an official Google product — independent open-source
software, not endorsed by Google. MIT License, see LICENSE.
Metadata
Release files for chattice 0.3.3
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| chattice-0.3.3.tar.gz | 2.8 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| chattice-0.3.3-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.0 MB
Release files / chattice-0.3.3.tar.gz
| Download URL | chattice-0.3.3.tar.gz |
|---|---|
| Size | 2.8 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
cd5dcd45fd2697a2e790cd50195c4945f17df4581e83bad186760762aeb22168
|
|
BLAKE2b-256 checksum How to use checksums |
aaa652583bd8d9c4748af99c594adc756447887e2df40d3190944cf838ab0a91
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|
Release files / chattice-0.3.3-py3-none-any.whl
| Download URL | chattice-0.3.3-py3-none-any.whl |
|---|---|
| Size | 170.3 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
2925a5722ef8815b8ad7033c91cb46dfa6ddc070d1e13bbf862f1859afc1d751
|
|
BLAKE2b-256 checksum How to use checksums |
959f6c7a40423ab8e25131bcc12ae1b7aef792b3c3cf0b6d900e780c84791d6d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
uv/0.12.9 {"installer":{"name":"uv","version":"0.12.9","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}
|