A fork of arigram -- a hackable telegram TUI client
- Python 98.4%
- Shell 0.7%
- Nix 0.7%
- Dockerfile 0.2%
Tuigram
Is a fork of arigram, which is a fork of tg -- a hackable telegram TUI client
Features
- view media: photo, video, voice/video notes, documents
- ability to send pictures, documents, audio, video
- reply, edit, forward, delete, send messages
- stickers
- notifications
- record and send voice messages
- auto download files
- toggle chats: pin/unpin, mark as read/unread, mute/unmute
- message history
- list contacts
- show user status
- secret chats
- automation
- better default file picker
- custom keybindings
- consistent styling
- drafts
- scheduled messages
- polls
- local passwords (maybe GPG?)
- debug mode
- modules/addons
- stickers (sticker keyboard)
- less crowded UI
- search (for users)
- bots (bot keyboard)
- profile pictures
- message/chat archiving
- better error reporting
- joining of groups and channels based on t.me links and @s
Requirements
To use tg, you'll need to have the following installed:
Optional dependencies
- terminal-notifier - for Mac (used by default). You can change it to dunst for Linux or any other notifications program (see
NOTIFY_CMDin configuration) - ffmpeg - to record voice msgs and upload videos.
- tdlib - in case of incompatibility with built in package.
For example, macOS:
and then set in configbrew install tdlibTDLIB_PATH. - macOS arm64: you can also manually put the tdlib prebuilt (tdlib-arm64-prebuilts/libtdjson.dylib) in
/usr/local/lib. urlviewto choose urls when there is multiple in message, useURL_VIEWin config file to use another app (it should accept urls in stdin)- to open
stickersandanimatedones (thumbnail preview) you need to set in mailcap appropriate handler and have app which will openwebpfile:image/webp; mpv %s - ranger, nnn, fzf, yazi - can be used to choose file when sending, customizable with
FILE_PICKER_CMD - fzf - to create groups and secret chats (used for single and multiple user selection)
Installation
From sources
This option is recommended:
mkdir -p ~/.local/src
cd ~/.local/src
git clone https://codeberg.org/YeRudIT/tuigram.git
cd tuigram
./do local
Optionally install the desktop entry
$ sudo ./do entry
And add this to ~/.bashrc or whatever POSIX complient shell you use:
export PATH="${PATH}:${HOME}/.local/bin"
To Launch it
tuigram
Upgrading
From sources
This option is recommended:
cd ~/.local/src/tuigram
git reset --hard # This discards every change you made locally
git pull
./do upgrade
or if you want to keep local changes
cd ~/.local/src/tuigram
./do local
Configuration
Config file should be stored at ~/.config/tuigram/config.py. This is simple python file.
Simple config:
# should start with + (plus) and contain country code
PHONE = "[phone number in international format]"
# For enable `PHONE = "+1234567890"`
Advanced configuration:
All configurable variables can be found here
import os
from tuigram.controllers import msg_handler
from plyer import notification
from tuigram import config as tg_config
from simpleaudio import WaveObject
from threading import Thread
def notify(self, *args, **kwargs) -> None:
del args
def notifier() -> None:
notification.notify(
app_name=f"tuigram {tuigram.__version__}", title=str(kwargs.get("title")), message=str(kwargs.get("msg")),
)
notif = Thread(target=notifier)
notif.setDaemon(True)
notif.start()
def player() -> None:
wave_obj = WaveObject.from_wave_file(f"{tg_config.CONFIG_DIR}resources/notification.wav")
play_obj = wave_obj.play()
play_obj.wait_done()
sound = Thread(target=player)
sound.setDaemon(True)
sound.start()
# You can write anything you want here, file will be executed at start time
# You can keep you sensitive information in password managers or gpg
# encrypted files for example
def get_pass(key):
# retrieves key from password store
return os.popen("pass show {} | head -n 1".format(key)).read().strip()
# Custom methods (doesn't need to be named "custom")
PHONE = get_pass("i/telegram-phone")
# encrypt you local tdlib database with the key
ENC_KEY = get_pass("i/telegram-enc-key")
# log level for debugging, info by default
LOG_LEVEL = "DEBUG"
# path where logs will be stored (all.log and error.log)
LOG_PATH = os.path.expanduser("~/.local/share/tg/")
# If you have problems with tdlib shipped with the client, you can install and
# use your own, for example:
TDLIB_PATH = "/usr/local/Cellar/tdlib/1.8.0/lib/libtdjson.dylib"
# A callback to notify a user,
# Arguments get passed in kwargs
NOTIFY_FUNCTION = notify
# You can use your own voice recording cmd but it's better to use default one.
# The voice note must be encoded with the Opus codec, and stored inside an OGG
# container. Voice notes can have only a single audio channel.
VOICE_RECORD_CMD = "ffmpeg -f avfoundation -i ':0' -c:a libopus -b:a 32k {file_path}"
# You can customize chat and msg flags however you want.
# By default words will be used for readability, but you can make
# it as simple as one letter flags like in mutt or add emojies
CHAT_FLAGS = {
"online": "●くろまる",
"pinned": "P",
"muted": "M",
# chat is marked as unread
"unread": "U",
# last msg haven't been seen by recipient
"unseen": "✓",
"secret": "🔒",
"seen": "✓✓", # leave empty if you don't want to see it
}
MSG_FLAGS = {
"selected": "*",
"forwarded": "From {sender}", # sender is optional
"new": "N",
"unseen": "U",
"edited": "Edited at {edit_date}", # edit_date is optional
"pending": "...",
"failed": "💩",
"seen": "✓✓", # leave empty if you don't want to see it
"reactions": " [{reactions}]"
}
# use this app to open url when there are multiple
URL_VIEW = 'urlview'
# Specifies range of colours to use for drawing users with
# different colours
# this one uses base 16 colors which should look good by default
USERS_COLOURS = tuple(range(2, 16))
# to use 256 colours, set range appropriately
# though 233 looks better, because last colours are black and gray
# USERS_COLOURS = tuple(range(233))
# to make one colour for all users
# USERS_COLOURS = (4,)
# cleanup cache
# Values: N days, None (never)
KEEP_MEDIA = 7
FILE_PICKER_CMD = "ranger --choosefile={file_path}"
# FILE_PICKER_CMD = "nnn -p {file_path}"
MAILCAP_FILE = os.path.expanduser("~/.config/mailcap") # Deprecated (Does nothing)
# Mailcap is used for deciding how to open telegram files (docs, pics, voice notes, etc.)
MAILCAP = {
# media
"video/*": 'mpv {file_path}',
"audio/ogg": 'mpv --speed=1.33 {file_path}',
"audio/mpeg": 'mpv --no-video {file_path}',
"image/*": 'qview {file_path}',
# text
"text/html": 'w3m {file_path}',
"text/html": 'open -a Firefox {file_path}',
"text/plain": 'less "{file_path}"',
# fallback to vim
"text/*": 'vim {file_path}',
}
DOWNLOAD_DIR = os.path.expanduser("~/Downloads/") # copy file to this dir
def send_hello(ctrl, *args) -> None:
# ctrl = the current Controller class instance
ctrl.model.send_message(text=f"Hello people!") # Sends a message
# CUSTOM_KEYBINDS = {"KEY": {"func": SOME_FUNCTION, "handler": CONTEXT_HANDLER}}
CUSTOM_KEYBINDS = {"z": {"func": send_hello, "handler": msg_handler, "repeat": False, "is_remap": False}}
# What to add before file picker (while using fzf (default))
EXTRA_FILE_CHOOSER_PATHS = ["..", "/", "~"]
# This is the max truncation limit when truncating paths, messages, etc.
TRUNCATE_LIMIT = 10
# If you set this to True this will automatically disable link previews
# WARNING: only do this if you know what you are doing, this is a dangerous option
EXTRA_TDLIB_HEADERS = {"disable_web_page_preview": True}
# Where to store your drafts
DRAFTS_FILE = "/tmp/.drafts.json"
# This defines if stuff like \n (Unicode escapes)
# get interpreted literally (False) or get decoded
# into an escape and you get a newline (True)
DECODE_INPUT_ESCAPES = False
Another configuration exemple
You can also get some inspiration from my personal config.
Keybindings
ViM like keybindings are used in the project. Can be used commands like 4j - 4 lines down.
Chats:
j,k: move up/downJ,K: move 10 chats up/downg: go to top chatl: open msgs of the chatm: mute/unmute current chatp: pin/unpin current chatu: mark read/unreadr: read current chatc: show list of contactsdd: delete chat or remove historyng: create new group chatns: create new secret chat/: search in chats?: show help
Msgs:
j,k: move up/downJ,K: move 10 msgs up/downG: move to the last msg (at the bottom)D: download filel, <enter>: if video, pics or audio then open app specified in mailcap, for example:
# Images
image/png; qView "%s"
audio/*; mpv "%s"
If text, open in less (to view multiline msgs)
e: edit current msg<space>: select msg and jump one msg down (use for deletion or forwarding)<ctrl+space>: same as space but jumps one msg upy: yank (copy) selected msgs with to internal buffer (for forwarding) and copy current msg text or path to file to clipboardp: forward (paste) yanked (copied) msgs to current chat^V: paste image from clipboarddd: delete msg for everybody (multiple messages will be deleted if selected)i or a: insert mode, type new messageI or A: open vim to write long msg and sendv: record and send voice messager,RR: reply to a current msg with a messageRS: reply to a current msg with a chosen fileRa: reply to a current msg with an audioRd: reply to a current msg with a documentRp: reply to a current msg with a pictureRv: reply to a current msg with a videoRn: reply to a current msg with a animationsv: send videosa: send audiosp: send picturesd: send documento: open url present in message (if multiple urls,urlviewwill be opened)]: next chat[: prev chatu: show user info (username, bio, phone, etc.)V: show people who viewed your message in groupsP: open user's profile picturec: show chat info (e.g. secret chat encryption key, chat id, state, etc.)?: show help!: open message with custom command|: open message with custom command inEDITOR
Input (insert mode)
^E: enterEDITORmode^H: move cursor left^L: move cursor right
When opening message with custom command
^P: Previous command^N: Next command
Publish
Run script to automatically increase version and release
./do release