MTProto library for the V programming language
VTOL is a V library for building Telegram clients on top of MTProto. It provides a high-level vtol.Client for login, session reuse, messaging, media transfer, peer resolution, and update handling, while still exposing the generated raw TL layer when you need to work closer to Telegram's API surface.
The library is aimed at application code, bots, automation scripts, and experiments that want a practical default path first: restore a saved session, send messages, handle updates, and drop down to raw calls only when the typed helpers are not enough.
VTOL works with Telegram credentials and session material. Treat all of the following as secrets:
- Telegram API ID and API hash
- bot tokens
- string sessions
- SQLite session files
Do not commit session files, log encoded string sessions, or reuse the same session across unrelated environments. If a session leaks, rotate it and create a new one.
VTOL exists to make Telegram's MTProto protocol usable from V without forcing every consumer to build their own client lifecycle, session storage, auth flow, update recovery, and TL wiring.
The project combines two layers:
- a high-level client API for the common path
- a generated TL layer for direct access to Telegram methods and result types
That split lets application code stay simple for the everyday cases:
- create a client
- restore or authorize a session
- resolve peers
- send text, files, or photos
- receive updates through typed handlers
When the high-level surface is not enough, VTOL still exposes the raw tl.* constructors and client.invoke(...) so you can call Telegram methods directly.
Install from VPM if you want to consume VTOL as a dependency:
v install watzon.vtolClone the repository and symlink it into V's module path if you are developing VTOL itself locally:
git clone https://github.com/watzon/vtol.git
cd vtol
mkdir -p ~/.vmodules
ln -sfn "$PWD" ~/.vmodules/vtolTo verify the checkout compiles and the local environment is usable:
v test .VTOL expects:
- a recent V compiler
- OpenSSL development headers and libraries
Typical OpenSSL installs:
# macOS (Homebrew)
brew install openssl@3
# Debian / Ubuntu
sudo apt-get install libssl-devSQLite-backed sessions are built into VTOL through vtol.session.SQLiteSession. If you do not want disk persistence, use vtol.new_client(...) for an in-memory session instead.
Set your Telegram application credentials before running code that authenticates:
export VTOL_API_ID=12345
export VTOL_API_HASH=your-app-hashThe common path is a file-backed client plus start(...), which restores an existing session or performs login on the first run:
module main
import os
import vtol
fn main() {
run() or {
eprintln(err)
exit(1)
}
}
fn run() ! {
mut client := vtol.new_client_with_session_file(vtol.ClientConfig{
app_id: os.getenv('VTOL_API_ID').int()
app_hash: os.getenv('VTOL_API_HASH')
}, '.vtol.session.sqlite')!
defer {
client.disconnect() or {}
}
client.start(vtol.StartOptions{
phone_number: fn () !string {
return os.input('Phone number: ').trim_space()
}
code: fn (request vtol.LoginCodeRequest) !string {
return os.input('Login code: ').trim_space()
}
password: fn () !string {
return os.input('2FA password: ').trim_space()
}
})!
message := vtol.parse_markdown('*hello* from `vtol`') or {
vtol.plain_text('hello from vtol')
}
sent := client.send_text('me', message)!
println('sent message ${sent.id}: ${sent.text}')
}For long-lived applications, register a handler and let the client own the update loop:
handler_id := client.on_new_message(fn (event vtol.NewMessageEvent) ! {
if event.text == '!ping' {
event.reply('pong')!
}
})!
defer {
client.remove_event_handler(handler_id)
}
client.run_until_disconnected()!Runnable examples live under examples/:
examples/auth_basicexamples/send_messageexamples/watch_updatesexamples/download_fileexamples/userbot
Project guides live in docs/:
docs/quick-start.mdfor the end-to-end login and first message flowdocs/auth-and-sessions.mdfor memory, string, and SQLite session backendsdocs/peers.mdfor usernames, cached peers, and peer-like inputsdocs/messages.mdfor text formatting, media sends, and history helpersdocs/updates.mdfor update handlers, pump loops, and recovery behaviordocs/client-reference.mdfor the highest-valuevtol.Cliententry pointsdocs/raw-api.mdfor direct TL-level usage
VTOL's main exported surface is the vtol module.
Core types and entry points:
vtol.Clientmanages connection state, auth, session restore, peer caching, RPC calls, and update handling.vtol.ClientConfigconfigures MTProto transport, datacenter endpoints, retry behavior, and default RPC options.vtol.StartOptionsdefines the callbacks used byclient.start(...)for interactive user or bot login.vtol.Session,vtol.ResolvedPeer,vtol.SentMessage, andvtol.NewMessageEventprovide the high-level results returned by common workflows.
For normal production mode, ClientConfig only needs app_id and app_hash. Add dc_options only when you need a custom bootstrap endpoint or when targeting Telegram test mode.
Common methods:
- construction:
new_client,new_client_with_session_file,new_client_with_string_session,new_client_with_store - auth and lifecycle:
start,connect,disconnect,did_restore_session,get_me,login_bot - messaging and media:
send_text,send_text_with,send_file,send_photo,download_file - dialogs and history:
get_dialog_page,get_history_page,each_dialog,each_history_message - updates:
on_new_message,on_raw_update,pump_updates_once,idle,run_until_disconnected - raw Telegram access:
invoke
Supporting modules are also public where needed:
vtol.sessionforMemorySession,StringSession, andSQLiteSessionvtol.updatesfor lower-level subscription and state managementvtol.tlfor generated Telegram TL constructors, functions, and result types
For fuller guidance, use the docs in docs/ rather than treating this README as the complete API reference.
Questions and bug reports should go through GitHub issues.
Pull requests are accepted. Before opening one, read CONTRIBUTING.md and run:
v run scripts/check_tl_schema.vsh
v fmt -w .
v test .Keep changes scoped, update docs when public APIs change, and avoid changing generated TL files by hand unless the generator flow requires it.
MIT © Chris Watson