From b87e62a10f22592acdfff6b62e57f4a1b82e2eb1 Mon Sep 17 00:00:00 2001 From: DJ Gillespie Date: Wed, 10 Sep 2025 14:49:55 -0600 Subject: [PATCH] add initial matrix-to-sms gateway support --- Dockerfile | 10 ++++++++ README.md | 6 +++++ app.py | 64 ++++++++++++++++++++++++++++++++++++++++++++++++ requirements.txt | 3 +++ 4 files changed, 83 insertions(+) create mode 100644 Dockerfile create mode 100644 README.md create mode 100644 app.py create mode 100644 requirements.txt diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2d1a5d4 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,10 @@ +FROM python:3.11-slim + +WORKDIR /app + +COPY requirements.txt ./ +RUN pip install --no-cache-dir -r requirements.txt + +COPY app.py ./ + +CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8080"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..bf1a18e --- /dev/null +++ b/README.md @@ -0,0 +1,6 @@ +# Email to Matrix SMS Gateway + +## Setup + +1. Build and run the Docker container: + diff --git a/app.py b/app.py new file mode 100644 index 0000000..153d43f --- /dev/null +++ b/app.py @@ -0,0 +1,64 @@ +import os +import asyncio +from fastapi import FastAPI, Form +from nio import AsyncClient +from fastapi.responses import JSONResponse +from starlette.requests import Request + +app = FastAPI() + +# Matrix credentials and homeserver from environment +HOMESERVER = os.getenv("MATRIX_HOMESERVER") +USER_ID = os.getenv("MATRIX_USER_ID") +PASSWORD = os.getenv("MATRIX_PASSWORD") + +# Initialize nio client globally and login once on startup +matrix_client = AsyncClient(HOMESERVER, USER_ID) + +@app.on_event("startup") +async def startup_event(): + await matrix_client.login(PASSWORD) + +@app.on_event("shutdown") +async def shutdown_event(): + await matrix_client.logout() + await matrix_client.close() + +@app.post("/mailgun-webhook") +async def mailgun_webhook( + request: Request, + recipient: str = Form(...), # 'recipient' field from Mailgun webhook, e.g. 14155551212@yourdomain.com + subject: str = Form(""), + body_plain: str = Form(""), +): + # Extract phone number from recipient email + phone_jid_localpart = recipient.split("@")[0] + # Assuming your bridge uses cheogram.com domain for XMPP JIDs + target_jid = f"_{phone_jid_localpart}=40cheogram.com:aria-net.org" + + # Find or create direct message room with bridged target user + resp = await matrix_client.room_create( + invite=[target_jid], + is_direct=True, + preset="trusted_private_chat", + ) + room_id = resp.room_id + + # Compose SMS body (you could prepend subject if needed) + message = f"{body_plain}" if body_plain else "(empty message)" + + # Send the message to the Matrix bridged user + await matrix_client.room_send( + room_id, + message_type="m.room.message", + content={ + "msgtype": "m.text", + "body": message, + } + ) + + return JSONResponse({"status": "SMS sent", "to": target_jid}) + +if __name__ == "__main__": + import uvicorn + uvicorn.run(app, host="0.0.0.0", port=8080) diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..c530572 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +fastapi +uvicorn[standard] +matrix-nio[e2e]