# tg: a terminal Telegram client with vi style keys and a Python config

> tg is a terminal client for Telegram written in Python, with a curses style interface, vi like keybindings, media viewing and sending, voice recording and secret chats, configured through a plain Python file at ~/.config/tg/conf.py. It suits a keyboard only workflow on a server, and it is released under the Unlicense with two features still unchecked, search and bot keyboards.

**paul-nameless/tg** — terminal telegram client

- Repository: https://github.com/paul-nameless/tg
- Stars: 1,200 · Forks: 94
- Language: Python
- License: Unlicense
- Published: 2026-10-19 · Updated: 2026-10-19 · Language: en
- Canonical page: https://hysenlabs.com/projects/paul-nameless-tg

## A Telegram client that never opens a window

tg is a terminal Telegram client, and the design is obvious from the keybinding list: j and k move up and down, capital J and K move ten chats at a time, g goes to the top chat, l opens the messages of the current chat, and there is a mute toggle on m, a pin toggle on p, and a mark read toggle on u. The project describes the bindings as vi like, and you can use commands like 4j to move four lines down, with arrow keys also working for navigation. There is a Telegram chat linked from the readme badge at the time of writing.

The feature list covers most of what an everyday Telegram session needs. You can view media files, meaning photos, video, voice and video notes and documents, and you can send pictures, documents, audio and video in the other direction. Messages can be replied to, edited, forwarded and deleted. There are stickers, notifications, and the ability to record and send voice messages. Files can be downloaded automatically. Chats can be pinned or unpinned, marked read or unread, and muted or unmuted. There is message history, a contact list, user status display, and secret chats.

Two items are still unchecked in the list, and they are not trivial. Search is not implemented, and bot keyboards are not implemented. For a client whose whole point is keyboard driven work, a missing search is a real daily cost, and the absence of bot keyboards means the bot interaction model, which Telegram clients normally render as a custom keyboard, is not available. Those two boxes are the most honest signal in the readme about what state the project is in.

## Four install routes, and what Docker costs you

The installation section offers a route for each situation. For a normal user, PyPI, and the readme says this option is recommended for production.

```sh
pip3 install tg
tg
```

For development, the sources route, where the readme asks you to install the python-telegram package first and then the project itself.

```sh
git clone https://github.com/paul-nameless/tg.git
cd tg
pip install python-telegram
pip install .
tg
```

Docker runs the published image interactively.

```sh
docker run -it --rm ghcr.io/paul-nameless/tg
```

And there is an AUR package for Arch users, installed with the yay helper, plus a separate AUR package that tracks the development version.

```bash
yay -S telegram-tg
```

The Docker note is the one to read carefully, because it states that voice recordings and notifications will not work when using Docker. That is not a small limitation. A terminal client on a remote machine is exactly the case where Docker is most attractive, and the two features that fail in a container are the two that need access to the host's audio device and the host's notification daemon. Voice recording needs ffmpeg and a real capture device, and desktop notifications need terminal-notifier on a Mac or dunst on Linux, neither of which is a container concern. The container is therefore for reading and sending messages, not for a full session.

The build itself is a small image based on python:3.13.3-slim-bullseye, with poetry installed to handle the dependencies, and the image skips the dev dependency group since the comment in the Dockerfile notes that the no-dev option is deprecated.

## The config file is Python, so it can call your password manager

The configuration file lives at ~/.config/tg/conf.py, and it is not a settings format, it is a Python module that gets executed at start time. The readme says you can write anything you want in it, and the advanced example shows why that is the interesting part. A get_pass function retrieves a key from a password store, and the phone number and the database encryption key are both fetched that way.

```python
def get_pass(key):
    # retrieves key from password store
    return os.popen("pass show {} | head -n 1".format(key)).read().strip()


PHONE = get_pass("i/telegram-phone")
# encrypt you local tdlib database with the key
ENC_KEY = get_pass("i/telegram-enc-key")
```

The comment in the example says you can keep your sensitive information in password managers or gpg encrypted files. This is the single best design decision in the project. A client that stores your phone number and a database encryption key usually wants a stored credential, and a stored credential is a file on disk with a secret in it. Here the secret is never written to the config, the config just knows how to ask for it, and the file that ends up on disk is code that looks the same as any other.

The minimum configuration is a single line, the phone number in international format, and the readme notes it should start with a plus sign and contain the country code. The rest of the advanced file shows the surface area: LOG_LEVEL and LOG_PATH for debugging, TDLIB_PATH when the bundled TDLIB is a problem, NOTIFY_CMD as a format string that can take title, msg, subtitle and icon_path, VOICE_RECORD_CMD with a note that the voice note must be Opus encoded inside an OGG container and can have only a single audio channel, CHAT_FLAGS and MSG_FLAGS for the status markers, USERS_COLORS for the colour range, KEEP_MEDIA as a day count for cache cleanup, FILE_PICKER_CMD, MAILCAP_FILE, and DOWNLOAD_DIR.

## Mailcap decides how a file opens, not the client

The mailcap file is how tg opens the files it receives. The readme is explicit that it is used for deciding how to open Telegram files, documents, pictures and voice notes, and that the path can be overridden with MAILCAP_FILE in the config file, with the example at ~/.mailcap.

```ini
# media
video/*; mpv "%s"
audio/ogg; mpv --speed=1.33 "%s"
audio/mpeg; mpv --no-video "%s"
image/*; qview "%s"

# text
text/html; w3m "%s"
text/plain; less "%s"

# fallback to vim
text/*; vim "%s"
```

Delegating to mailcap is the right call, and it is the same mechanism the rest of the Unix world uses. It means the client's behaviour is not hard coded in the client, so a user who wants a different terminal image viewer or a different audio player changes one line in a file they already know how to edit. The readme also covers the special case of stickers, where you need a mailcap handler for webp files and an application that can open them, with the example image/webp; mpv %s.

The same delegation idea shows up elsewhere. The URL_VIEW setting picks the program used when a message contains more than one link, and the readme notes it should accept urls on stdin. The FILE_PICKER_CMD setting names the program used to choose a file when sending, with ranger and nnn given as the examples, and the format string carries the path. And fzf is listed as a dependency for creating groups and secret chats, used for single and multiple user selection. All of these are conventional Unix patterns, and a user who has configured their system for a different terminal program already knows how to point tg at it.

## Where tg is the wrong tool

Three cases argue against it. The first is the missing search. A Telegram client without search is a client you scroll through, and for a heavy user with hundreds of chats and deep history, that is a serious functional gap rather than a missing convenience. The second is the mobile and desktop experience, which is not a gap but a different product. tg has no client for a phone, and the media experience, the stickers and the voice messages are all constrained by what a terminal can render, so someone who reads rich media in a chat is choosing a worse experience by design.

The third is a workflow that wants a hosted or official client. Telegram's own clients are maintained alongside the API, they support the full range of message types, and they handle the notification and media integration properly. tg exists because the terminal is the right surface for some people, and for a server or a remote shell that is a genuine advantage, but it is a specialisation rather than a general answer.

There is also a dependency question worth flagging. The project pins python-telegram to an exact version, 0.19.0 in pyproject.toml, and the bundled TDLIB may not work everywhere, which is why the TDLIB_PATH override exists and why the readme suggests installing TDLIB yourself with brew on macOS. The TDLIB database also holds the local message cache, and the ENC_KEY setting encrypts it, which is a sensible design given the data involved.

On the state of the repository, the last push was on 2026-05-04, and the newest tagged release is v0.22.0 from 2025-04-12, with v0.21.0 published the same day. The project is released under the Unlicense, which is a public domain dedication rather than a conventional permissive licence, so there is no warranty clause and no requirement to carry the licence forward. The repository carries a Dockerfile, a Taskfile.yml, a check.sh, a poetry.lock, a pyproject.toml and a setup.py side by side, which reflects a project that has moved between packaging tools and kept the old path working.

## Conclusion

tg suits someone who lives in a terminal, works on a remote machine, and wants Telegram without a GUI, especially if they already keep their secrets in a password manager and are comfortable with a plain Python config file. It does not suit a phone-first workflow, and the two unchecked features, search and bot keyboards, rule it out if either matters to you. Before you install it, decide where the local database lives, since the TDLIB path and the encryption key are separate settings, and note that the last push was on 2026-05-04 with the newest tagged release at v0.22.0 from 2025-04-12.

## FAQ

### How do I install the tg terminal Telegram client?

The readme recommends pip3 install tg followed by tg for normal use, and the source route for development is git clone, pip install python-telegram, pip install . and then tg. There is also a Docker image, ghcr.io/paul-nameless/tg, and an AUR package for Arch.

### What does the tg configuration file look like?

It is a plain Python file at ~/.config/tg/conf.py that is executed at start time, so you can fetch values from a password manager. The minimum is a PHONE variable with the number in international format, and the advanced example also sets ENC_KEY, LOG_LEVEL, TDLIB_PATH, NOTIFY_CMD, VOICE_RECORD_CMD, the flag dictionaries, KEEP_MEDIA, FILE_PICKER_CMD, MAILCAP_FILE and DOWNLOAD_DIR.

### Which features are not implemented in tg?

Two items are unchecked in the readme feature list: search, and bots with a bot keyboard. Everything else in the list, including media viewing, sending, voice recording, stickers, secret chats and message history, is marked as done.

### What does not work when tg runs in Docker?

The readme states that voice recordings and notifications will not work when using Docker, since those need the host's audio capture and notification tooling rather than a container.

## Sources

- [Issues](https://github.com/paul-nameless/tg/issues)
- [License: Unlicense](https://github.com/paul-nameless/tg/blob/master/LICENSE)
- [paul-nameless/tg on GitHub](https://github.com/paul-nameless/tg)
- [README](https://github.com/paul-nameless/tg/blob/master/README.md)
- [Releases](https://github.com/paul-nameless/tg/releases)

---

Hysen Labs editorial analysis, written from the project's own repository and release notes. Cite the canonical page: https://hysenlabs.com/projects/paul-nameless-tg
