Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

TextDM — iMessage, Telegram & WhatsApp API Connector for AI Agents

Give any AI agent (LangChain or anything else) a real messaging channel: send texts, read incoming screenshots, PDFs and voice notes, react with tapbacks, show typing indicators, send tappable buttons, and schedule cron check-ins. One interface, six swappable hosted providers — no Mac required.

Build things like an accountability coach that texts you to hit the gym, checks in on schedule, and judges your workout screenshots.

your phone ⇄ iMessage / Telegram / WhatsApp ── webhook/WS ──▶ FastAPI (Railway) ──▶ your agent
                         ▲                                                          │
                         └──────────── send / media / react / buttons ◀────────────┘

Providers

Set TEXTDM_PROVIDER and the matching credentials in .env (see .env.example). Switching providers changes nothing above the transport.

Provider Channel Free tier Notes
telegram Telegram Free, unlimited Official Bot API. No approval, no per-message cost, no sending window. Easiest way to run this end to end.
whapi WhatsApp Free dev sandbox Connected account — QR-pair your own number. No Meta verification, no templates, no 24h window, native groups. ~$12–35/mo. Drives WhatsApp Web, so outside WhatsApp's terms; keep volume human-shaped.
messagesdev iMessage 50 msgs/day sandbox (your own paired number) Best iMessage API: reply threading, audio transcription, HMAC webhooks. $99/mo per production line.
sendblue iMessage Free API plan / AI-agent plan Proven; tapbacks + typing verified. Inbound tapbacks are heuristic (arrive as Loved "..." text).
loopmessage iMessage Sandbox: 5 contacts, unlimited msgs Contact must text the sandbox number first (24h reply windows). $59.99/mo Light.
clawmessenger iMessage 7-day trial, $5/mo for 250 msgs Text send is REST; media/reactions/typing/receiving are WebSocket (background listener, no webhook needed).

Why not Meta's WhatsApp Cloud API? It's the official path, but since July 2025 it bills per message and only allows free-form replies inside 24 hours of the user's last message. A scheduled 6pm nudge lands outside that window and needs a paid pre-approved template — which breaks the whole point of the scheduler. A connected account has no such window. Add the Cloud API adapter if you need an official business identity.

Quick start

python3 -m venv .venv && .venv/bin/pip install -e ".[langchain,media,clawmessenger]"
cp .env.example .env   # fill in provider credentials
.venv/bin/python test_textdm.py   # sanity check, no network

Fastest path to a working agent — Telegram, about two minutes:

  1. Message @BotFather, /newbot, copy the token into TELEGRAM_BOT_TOKEN.
  2. Set TELEGRAM_WEBHOOK_SECRET to any random string and TELEGRAM_WEBHOOK_URL to https://<your-host>/webhooks/telegram.
  3. Deploy (or ngrok http 8000) and start the app — it registers the webhook on boot.

Registering the webhook is what enables reaction events: Telegram only delivers them when message_reaction is in allowed_updates, which setup_webhook() handles. Skip it and reactions silently never arrive.

Run the example gym-coach agent:

TEXTDM_PROVIDER=sendblue uvicorn examples.gym_coach:app --port 8000

Point the provider's webhook at https://<host>/webhooks/<provider>. On Railway: deploy this repo, add env vars, use the generated domain.

Use it from your own agent

from textdm import create_app, get_transport, to_content_blocks, InboundMessage
from textdm.langchain_tools import build_tools

transport = get_transport("telegram")
tools = build_tools(transport, scheduler)   # send / media / react / buttons / schedule

async def on_message(msg: InboundMessage):
    blocks = await to_content_blocks(msg, transport)   # text + images + PDFs + transcripts
    ...  # hand `blocks` to your agent as the user turn

app = create_app(on_message, [transport])   # FastAPI; run with uvicorn

Every provider normalizes into the same InboundMessage: text, attachments, tapback reactions, button taps (callback_data), group flag, and the raw payload if you need provider-specific fields.

Reading what people send you

An attachment URL is useless to a model — it can't open one, and Telegram and WhatsApp don't even hand out fetchable URLs (you get an opaque media id that needs an authenticated call). to_content_blocks() closes that gap: it pulls the bytes through the transport and returns Anthropic content blocks.

Sent to your agent What it becomes
Screenshot / photo image block the model actually sees (>5 MB degrades to a note)
PDF document block, read natively
Voice note transcribed to text — via the provider when it does that itself (messages.dev, LoopMessage), otherwise Deepgram nova-3
Button tap noted in the lead text block as [tapped button <id>]

Claude has no speech-to-text, so voice needs the transcription hop. Set DEEPGRAM_API_KEY and install the media extra; without it everything else still works and voice notes come through as a short note instead.

Layout

textdm/
  models.py            # InboundMessage / Attachment / Reaction / SendResult
  media.py             # attachments -> content blocks; Deepgram transcription
  server.py            # FastAPI webhook receiver + dedup + WS listener startup
  scheduler.py         # cron pushes that invoke your agent with a prompt
  langchain_tools.py   # ready-made LangChain tools
  transports/          # base.py + one adapter per provider
examples/gym_coach.py  # end-to-end accountability-coach agent

Known limits

  • Adapters are written against each provider's current docs; only webhook parsing is unit-tested. First live send per provider needs real credentials. Deepgram transcription is verified end to end against a real key.
  • Telegram reactions on bot-sent messages in 1:1 chats are unconfirmed. Telegram's docs and grammY say private chats deliver message_reaction; one field report says reactions on bot-authored messages in DMs produce no event. Reactions on the user's own messages are fine. Verify with a live bot before building on the tapback flow.
  • Tapbacks have no cross-platform equivalent. Telegram accepts only a fixed emoji allow-list for bots, with no ‼️ and no , so EMPHASIZE maps to 🔥 and QUESTION to 🤔. WhatsApp takes any emoji and uses the literal ones.
  • Whapi doesn't sign webhooks. Set WHAPI_WEBHOOK_SECRET and configure the same value as an Authorization header on their webhook, or the endpoint is unauthenticated.
  • Telegram caps bot file downloads at 20 MB; larger attachments can't be read.
  • Sendblue inbound-tapback detection is undocumented behavior — verify with a live tapback before relying on it.
  • Claw Messenger webhooks are unadvertised/undocumented; its adapter uses their WebSocket instead.
  • Dedup is in-memory (fine for one replica; move to Postgres to scale out).

About

iMessage connector for AI agents (LangChain-ready) — send texts, read attachments, react with tapbacks, schedule cron check-ins via 4 swappable hosted providers (Sendblue, LoopMessage, messages.dev, Claw Messenger). No Mac required.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages