POC project for integrating TDLib with a Spring Boot Java application.
- Group:
io.ndmik - App name:
tdlib-poc - Stack:
Java + Spring Boot + TDLib
- Implement Telegram authorization flow via TDLib.
- Fetch current user profile (
getMe). - Send a plain text message to a target chat/user.
- This repository is focused on a minimal, testable integration POC.
- Detailed execution checklist is tracked in
POC_PLAN.md. - Architecture notes and flow diagram are in
ARCHITECTURE.md.
- Build TDLib on your machine and keep
libtdjson+td_json_client.havailable. - Place the native TDLib library in the project
native/directory. - Configure TDLib environment variables:
TD_ENABLED=true- Optional:
TD_USE_TEST_DC=true(for Telegram test DC) TD_API_IDTD_API_HASHTD_PHONE- Optional:
TD_DB_DIR,TD_FILES_DIR,TD_NATIVE_LIB_NAME,TD_NATIVE_LIB_PATH
- Run the application from project root:
./gradlew bootRun
- When native loading is wired, run with Java library path pointing to
native/:./gradlew bootRun -Djava.library.path=./native
- Follow the POC checklist in
POC_PLAN.mdto complete auth,getMe, and message send flow.
GET /api/auth/status- current TDLib authorization state.POST /api/auth/phone- submit phone number:- body:
{ "phoneNumber": "+1234567890" }
- body:
POST /api/auth/code- submit login code:- body:
{ "code": "12345" }
- body:
GET /api/td/me- fetch current authorized user (getMe).POST /api/td/messages/send- send plain text message:- body:
{ "chatId": 123456789, "text": "hello from tdlib-poc" }
- body:
GET /api/td/chats?limit=50- list known TDLib chat IDs.GET /api/td/chats/{chatId}- get chat details by TDLib chat ID.POST /api/td/chats/search-public- resolve public chat/group by username:- body:
{ "username": "group_or_channel_username" }
- body:
- Complete auth until status is
READY:curl -s http://localhost:8080/api/auth/status
- Resolve a valid TDLib chat ID:
curl -s -X POST http://localhost:8080/api/td/chats/search-public -H "Content-Type: application/json" -d '{"username":"your_group_username"}'- or
curl -s "http://localhost:8080/api/td/chats?limit=50"and inspectchat_ids
- Fetch current user:
curl -s http://localhost:8080/api/td/me
- Send message using TDLib
chat.id:curl -s -X POST http://localhost:8080/api/td/messages/send -H "Content-Type: application/json" -d '{"chatId":123456789,"text":"hello from tdlib-poc"}'
- Script:
httpie-requests.sh - Supports:
- auth status check
- phone/code flow (with hidden code input prompt)
getMe- chat id resolution by
PUBLIC_CHAT_USERNAME - interactive chat selection from
/api/td/chats - send message
Examples:
- interactive:
bash ./httpie-requests.sh
- resolve by public username:
PUBLIC_CHAT_USERNAME=group_username bash ./httpie-requests.sh
- direct chat id:
CHAT_ID=123456789 bash ./httpie-requests.sh
- Local run:
./run-local.sh
- Run with TDLib enabled and native path:
TD_API_ID=... TD_API_HASH=... TD_PHONE=... ./run-with-native-lib.sh
- Smoke test (
getMe+ send message):CHAT_ID=123456789 ./smoke-test.sh
- Final sign-off automation (smoke + optional restart/session reuse):
CHAT_ID=123456789 ./final-signoff.sh- optional:
MANAGE_APP=falseto run against already running app
- Environment template:
.env.example
- Release checklist:
RELEASE_CHECKLIST.md
TD_API_ID and TD_API_HASH must be set:- ensure vars are present in the same shell that starts the app.
Chat not found:- use TDLib chat id from
/api/td/chatsor/api/td/chats/search-public.
- use TDLib chat id from
- No chats returned:
- open/join target chat in Telegram with same account, then retry.
Can't lock file ... td.binlog:- another process uses
.tdlib/db; stop the other app instance.
- another process uses
- Username resolution fails:
searchPublicChatworks only for public usernames, not private group titles.
- Auth reaches
READY. /api/td/mereturns current user./api/td/messages/sendsucceeds with TDLib chat id.- App restart keeps TDLib session (no re-login required).
./gradlew testpasses.
Automated variant:
CHAT_ID=123456789 ./final-signoff.sh
- Phase 1: completed
- Phase 2: completed
- Phase 3: completed
- Phase 4: completed
- Phase 5: completed
- Phase 6: completed
- Build validation:
./gradlew testpassed
- Structured TDLib auth/client lifecycle logs are enabled (
event=...format). - Sensitive values in surfaced auth errors are redacted.
- Update loop uses retry with bounded exponential backoff on failures.
- Shutdown now drains/stops auth loop and pending TDLib response futures gracefully.
- Added unit tests:
src/test/java/io/ndmik/tdlibpoc/tdlib/TdJsonCodecTests.javasrc/test/java/io/ndmik/tdlibpoc/tdlib/auth/TdAuthorizationStateTests.javasrc/test/java/io/ndmik/tdlibpoc/tdlib/auth/TdlibAuthServiceTests.java
- Added gated native smoke test:
src/test/java/io/ndmik/tdlibpoc/tdlib/TdClientNativeLoadSmokeTests.java- runs only when
TD_NATIVE_SMOKE=true
Run all tests:
./gradlew test
Run native smoke test explicitly:
TD_NATIVE_SMOKE=true TD_NATIVE_LIB_PATH=./native ./gradlew test --tests "*TdClientNativeLoadSmokeTests"