1. Why SMS2Tg – Turning a Phone into a Tiny Gateway
SMS is still everywhere: banks, VPNs, monitoring systems, old IoT gear, random services that never heard of webhooks. But reading all those messages from one single device is painful, especially if that device lives at home plugged into a charger.
I wanted something that:
- Runs on a cheap Android phone that can stay at home.
- Forwards every SMS to a Telegram chat I actually watch.
- Uses a real Telegram bot instead of weird bridges.
- Is written in modern Kotlin with proper permission handling and coroutines. :contentReference[oaicite:1]{index=1}
SMS2Tg is that app: a tiny, focused forwarder that listens for incoming SMS via
a BroadcastReceiver and pushes them to Telegram over HTTPS using your bot token and
chat ID. No extra backend; the Android device is the backend. :contentReference[oaicite:2]{index=2}
2. Architecture, Features & Project Structure
The project is intentionally small and Android-native: everything lives inside one app module written in Kotlin, based on modern Android APIs. :contentReference[oaicite:3]{index=3}
2.1 High-Level Flow
The end-to-end flow is:
- An SMS arrives on the device.
- Android fires a broadcast with
Telephony.Sms.Intents.SMS_RECEIVED_ACTION. SmsReceiver.ktreceives it, extracts sender + body, and formats a message.TelegramForwarder.ktsends an HTTPS request to the Telegram Bot API. :contentReference[oaicite:4]{index=4}- You see the SMS instantly in your Telegram chat.
Nothing is stored long-term; the app just reads, forwards and logs what happened.
2.2 Core Features
- Automatic SMS forwarding – no manual forwarding or copy-paste. :contentReference[oaicite:5]{index=5}
- Telegram integration – messages land in a bot chat you control.
- Modern permission handling – uses
ActivityResultContractsto ask for SMS permissions at runtime. :contentReference[oaicite:6]{index=6} - Coroutines for networking – HTTP calls run on
Dispatchers.IOso the main thread stays responsive. :contentReference[oaicite:7]{index=7} - goAsync() in the receiver to keep work running safely even as the broadcast finishes. :contentReference[oaicite:8]{index=8}
- Lightweight & efficient – no heavy services constantly running; only reacts on SMS broadcasts. :contentReference[oaicite:9]{index=9}
The repo itself looks like a standard Gradle Android project:
sms2Tg/
├─ app/
│ ├─ src/main/java/.../MainActivity.kt
│ ├─ src/main/java/.../SmsReceiver.kt
│ ├─ src/main/java/.../TelegramForwarder.kt
│ ├─ src/main/res/layout/activity_main.xml
│ └─ src/main/AndroidManifest.xml
├─ build.gradle.kts
├─ settings.gradle.kts
├─ gradle.properties
├─ README.md
└─ LICENSE
3. Implementation – From Clone to First Forwarded SMS
SMS2Tg is meant to be easy to build from source in Android Studio and run on any device with API 23+ and Internet connectivity. :contentReference[oaicite:10]{index=10}
3.1 Prerequisites
Before you start, you need: :contentReference[oaicite:11]{index=11}
- An Android device on API 23+ (Android 6 or newer).
- A Telegram account.
- A Telegram bot token from
@BotFather. - Your personal chat ID (using
getUpdatesor a helper bot). :contentReference[oaicite:12]{index=12} - Android Studio installed.
3.2 Clone & Open in Android Studio
Standard workflow:
git clone https://github.com/ciscoAnass/sms2Tg.git
cd sms2Tg
Then open the folder in Android Studio. Gradle will sync and pull dependencies:
dependencies {
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.activity:activity-ktx:1.9.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
}
These give you Material UI components, modern activity helpers, and Kotlin coroutines for non-blocking network calls. :contentReference[oaicite:13]{index=13}
3.3 Configure Your Bot Token & Chat ID
In SmsReceiver.kt, there’s a small companion object that you customize with your
own bot token and chat ID: :contentReference[oaicite:14]{index=14}
companion object {
const val BOT_TOKEN = "YOUR_BOT_TOKEN_HERE"
const val CHAT_ID = "YOUR_CHAT_ID_HERE"
}
For a quick personal setup, hard-coding these values is fine. If you want to make SMS2Tg more “productized”, you can later move them into a settings screen or encrypted local storage.
3.4 Permissions & Manifest
The app needs three core permissions: :contentReference[oaicite:15]{index=15}
INTERNET– to call the Telegram Bot API.RECEIVE_SMS– to receive incoming SMS broadcasts.READ_SMS– to read the message content. :contentReference[oaicite:16]{index=16}
They live in AndroidManifest.xml, and at runtime MainActivity uses
ActivityResultContracts.RequestMultiplePermissions to request them in a modern,
user-friendly way. :contentReference[oaicite:17]{index=17}
3.5 Listening & Forwarding – Under the Hood
The real magic is in SmsReceiver.kt and TelegramForwarder.kt: :contentReference[oaicite:18]{index=18}
- SmsReceiver.kt
- Registered in the manifest for
SMS_RECEIVEDbroadcasts. - Extracts sender number and SMS body from the intent.
- Calls
goAsync()to safely do async work afteronReceive. - Hands the formatted message to
TelegramForwarder.
- Registered in the manifest for
- TelegramForwarder.kt
- Builds the Telegram
sendMessageURL with bot token + chat ID. - URL-encodes content so special characters survive HTTP. :contentReference[oaicite:19]{index=19}
- Uses a coroutine on
Dispatchers.IOto make the network request. - Catches exceptions to avoid crashes when the network is flaky.
- Builds the Telegram
- MainActivity.kt
- Shows a simple status about permissions.
- Triggers permission requests when needed.
- Acts as a small “control panel” for the app. :contentReference[oaicite:20]{index=20}
Once all that is in place, you just build & run the app on your device, grant permissions, send a test SMS to the SIM, and watch it appear in Telegram.
4. Design Choices & Lessons Learned
Even a “simple” SMS forwarder forces a few design decisions, especially around permissions, reliability and privacy.
- Keep everything on-device. There is no extra server; the phone does everything. That’s simpler to operate and better for privacy.
- Use modern permission APIs. Relying on
ActivityResultContractsmeans the flow survives rotations and future Android changes better than older patterns. - Asynchronous by default. Network calls never block the main thread and are wrapped in try/catch, so a temporary network issue doesn’t kill the app.
- Crash-safe BroadcastReceiver. Using
goAsync()plus coroutines is safer than doing all work directly inonReceive. - Minimal storage footprint. No local DB, no complex caching; messages are forwarded and then only live in your Telegram history.
From a DevOps/SRE mindset, it also opens a fun path: you can turn an old Android phone into a tiny gateway that “bridges” legacy SMS-based alerts into your modern chat workflows.
5. Next Steps – Ideas to Evolve SMS2Tg
SMS2Tg deliberately stays small, but there are obvious directions to extend it in future versions:
- In-app configuration: UI to edit bot token, chat ID, and filters instead of
editing
SmsReceiver.kt. - Filtering rules: forward only from certain senders, or tag messages with emojis based on patterns (bank, VPN, monitoring, etc.).
- Multi-destination support: choose between one-on-one chat, group, or channels dynamically.
- Delivery status & retries: store a tiny queue of messages that failed to send and retry later if the network was down.
- Simple analytics: count messages per sender and expose a tiny dashboard for nerdy stats.
For now, it does one job well: take every SMS that lands on a device and push it into a place where you actually see it – your Telegram.
💬 Want to extend SMS2Tg?
Reach out on LinkedIn or by email if you want to add filters, dashboards, or hook SMS2Tg into a bigger monitoring setup. I’m always happy to talk about Android, Telegram bots and automation.