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 (contains main) and botlib.c/h implement the Telegram API calls, message parsing, and keystroke injection.
  • cJSON.c/h and json_wrap.c handle JSON payloads from Telegram.
  • qrcodegen.c/h renders the QR code shown on first run for TOTP provisioning.
  • sqlite_wrap.c/h stores the Telegram user ID and OTP secret in a local SQLite DB.
  • Utility libraries sds* provide dynamic strings, while sha1.c/h supplies 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

  1. Telegram delivers an HTTP update → botlib.c uses libcurl to POST to the Telegram API.
  2. bot_handle_update() extracts the text, checks the sender against the stored owner ID (SQLite).
  3. If the user is not yet authenticated, totp_verify() (uses sha1.c + sds helpers) validates the 6‑digit code generated from the secret shown in the QR code (qrcodegen.c).
  4. Authenticated commands are dispatched: .list → list_terminals() (macOS window enumeration, defined in bot.c). .N → select_terminal(N) (stores the target window handle). * any other text → inject_keystrokes() (Accessibility API calls, also in bot.c).
  5. After each command, capture_screenshot() (macOS screen‑recording API) writes a PNG buffer that bot_send_photo() (in botlib.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 .list to 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.c contain 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.c each exceed 2 500 lines; splitting by logical responsibility would reduce blast radius.
  • Medium – High branching density – bot.c, json_wrap.c, qrcodegen.c have >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.