The Problem
Developers who rely on remote agents need a quick way to type into a macOS terminal without setting up SSH tunnels, VPNs, or multiplexers. Existing solutions require extra networking layers and cannot capture on‑screen graphics, which hampers workflows that need visual feedback (e.g., debugging GUI output).
What This Does
tgterm builds a native macOS binary that bridges a Telegram bot to local terminal windows.
- The source files
bot.c(containsmain) andbotlib.c/himplement the Telegram API calls, message parsing, and keystroke injection. cJSON.c/handjson_wrap.chandle JSON payloads from Telegram.qrcodegen.c/hrenders the QR code shown on first run for TOTP provisioning.sqlite_wrap.c/hstores the Telegram user ID and OTP secret in a local SQLite DB.- Utility libraries
sds*provide dynamic strings, whilesha1.c/hsupplies the HMAC‑SHA1 needed for TOTP generation.
The program runs as a single user‑space process, reads the API key from the command line (./tgterm --apikey <key>), and then waits for Telegram messages. Commands such as .list and .2 let the owner select a terminal window; subsequent messages are translated into keystrokes via macOS Accessibility services, and screenshots are sent back using the Screen Recording permission.
How It Is Wired
Entry point – bot.c:main parses --apikey, initializes the bot (bot_init() in botlib.c), loads the SQLite DB (sqlite_open() in sqlite_wrap.c), and registers the Telegram update callback bot_handle_update().
Message flow
- Telegram delivers an HTTP update →
botlib.cuses libcurl to POST to the Telegram API. bot_handle_update()extracts the text, checks the sender against the stored owner ID (SQLite).- If the user is not yet authenticated,
totp_verify()(usessha1.c+sdshelpers) validates the 6‑digit code generated from the secret shown in the QR code (qrcodegen.c). - Authenticated commands are dispatched:
.list→list_terminals()(macOS window enumeration, defined inbot.c)..N→select_terminal(N)(stores the target window handle). * any other text →inject_keystrokes()(Accessibility API calls, also inbot.c). - After each command,
capture_screenshot()(macOS screen‑recording API) writes a PNG buffer thatbot_send_photo()(inbotlib.c) uploads back to Telegram.
Data stores – sqlite_wrap.c is the sole persistence layer; it holds the owner’s Telegram ID and the encrypted TOTP secret. No other external files are touched.
Dependency graph – bot.c depends on botlib.c/h, sqlite_wrap.c/h, json_wrap.c, cJSON.c, qrcodegen.c/h, sds*.c/h, and sha1.c. The call graph is a star centered on bot.c; no circular imports were detected.
How To Use It
# Clone the repo
git clone https://github.com/moses-y/tgterm
cd tgterm
# Build the binary (requires libcurl and libsqlite3 development headers)
make
# Run, supplying the Telegram Bot API key
./tgterm --apikey <YOUR_API_KEY>
- First execution prints a QR code and a base‑32 secret. Scan it with any TOTP app.
- Open the bot in Telegram, send any message to become the owner, then reply with the 6‑digit code.
- Use
.listto see open terminal windows,.2(or similar) to select one, and type any further text to have it injected into that window.
The README lists the same commands; no additional configuration files are required.
Real‑World Use
A developer working on a macOS‑only GUI tool can start tgterm on their laptop, then, while away, open Telegram on their phone, authenticate with the TOTP, and issue .list → .3 → make test. The bot will type make test into the chosen terminal, capture the resulting output screenshot, and send it back—all without an SSH tunnel or a VPN.
Code Health & Issues
- High – Add CI workflow – 8 source files, no CI configuration. Without automated builds, any change can break the compile or runtime behavior.
- High – Deep nesting –
botlib.h,sds.c,sqlite_wrap.ccontain blocks nested up to 10 levels, making reasoning difficult. - High – Duplicated code – Repeated 6‑line blocks across
botlib.c,sds.c/h,sdsalloc.h. Consolidate into shared helpers. - High – Oversized files –
cJSON.c,bot.c,botlib.ceach exceed 2 500 lines; splitting by logical responsibility would reduce blast radius. - Medium – High branching density –
bot.c,json_wrap.c,qrcodegen.chave >200 branch points in ~750 lines; refactor into smaller decision tables or strategy objects. - Low – TODO/FIXME markers – 3 markers remain in
cJSON.c; resolve or file as issues.
Additional observations: the repository includes a LICENSE but no test suite, Dockerfile, or lockfile. No secrets were found in the commit history.
The Bottom Line
tgterm delivers a functional, macOS‑only bridge between Telegram and local terminals with minimal setup, but the codebase suffers from large, deeply nested files and a lack of automated testing or CI. It is suitable for personal or experimental use; teams planning production deployment should first refactor the core modules and introduce a CI pipeline.