telegram-asm
Библиотека для написания Telegram-ботов на чистом x86-64 ассемблере (NASM).
Подключается одной строкой %include "telegram.inc" и даёт весь транспорт:
собственный TLS 1.3, HTTP/1.1 с keep-alive, маршрутизацию (напрямую или
через прокси) и обёртки над Bot API. Без libc и внешних библиотек — только
системные вызовы Linux.
Возможности
- Свой TLS 1.3 — X25519, AES-128-GCM, SHA-256, HKDF, полностью на ассемблере.
- HTTP/1.1 keep-alive с авто-переподключением при обрыве соединения.
- Обёртки Bot API —
sendMessage, отправка фото/документов, разборgetUpdates. - Загрузка файлов — потоковая передача
multipart/form-dataпрямо с диска. - URL-кодирование — корректная передача UTF-8 (пробелы, кириллица, эмодзи).
Установка
Через nasmpkg (рекомендуется):
nasmpkg install telegram-asm
После этого библиотека доступна в проекте:
%include "telegram-asm/telegram.inc"
Сборка своего бота (nasmpkg прописывает пути через NASMENV):
nasm -f elf64 -g mybot.asm -o mybot.o
ld mybot.o -o mybot
./mybot # запускать там, где лежит папка config/
Токен кладётся в config/token.txt (одна строка).
Соглашение о вызовах
Аргументы — в rdi, rsi, rdx, rcx (порядок System V). Результат — в rax
(для tg_api_call ещё длина в rdx). Функции сохраняют rbx, rbp, r12–r15;
регистры rax, rcx, rdx, rsi, rdi, r8–r11 могут быть затёрты.
Быстрый старт
Минимальный бот, отвечающий hello world на /start:
%include "telegram-asm/telegram.inc"
section .rodata
tok_path: db "config/token.txt", 0
m_getupd: db "getUpdates"
s_start: db "/start"
hello: db "hello world"
hello_len equ $ - hello
section .bss
offset: resq 1
section .text
global _start
_start:
lea rdi, [tok_path]
call tg_load_token
call tg_connect
test rax, rax
js .exit
.poll:
lea rdi, [m_getupd]
mov rsi, 10
xor rdx, rdx ; без query
xor rcx, rcx
call tg_api_call ; rax=тело, rdx=длина
test rax, rax
jz .poll
mov rdi, rax
mov rsi, rdx
call tg_iter_init
.each:
call tg_iter_next ; rax=update_id или 0
test rax, rax
jz .poll
inc rax
mov [offset], rax ; offset = update_id + 1
call tg_update_text ; rax=ptr, rdx=длина
test rax, rax
jz .each
mov rdi, rax
lea rsi, [s_start]
mov rcx, 6
call startswith
test rax, rax
jz .each
call tg_update_chatid ; rax=длина -> tg_chatid_buf
lea rdi, [tg_chatid_buf]
mov rsi, rax
lea rdx, [hello]
mov rcx, hello_len
call tg_send_message
jmp .each
.exit:
mov eax, 60
xor edi, edi
syscall
Здесь опущена передача offset в getUpdates. В рабочем боте
формируй query offset=<update_id+1>&timeout=25, чтобы не получать одни и те же
апдейты повторно.
Справочник API
Соединение и токен
| Функция | Аргументы | Возвращает |
|---|---|---|
tg_load_token | rdi=путь (NUL-строка) | rax=0/-1; читает файл, срезает перевод строки |
tg_set_token | rdi=ptr, rsi=длина | — задаёт токен из памяти |
tg_connect | — | rax=0/-1; маршрутизация + TLS-рукопожатие |
Запросы
| Функция | Аргументы | Возвращает |
|---|---|---|
tg_api_call | rdi=метод, rsi=длина, rdx=query, rcx=длина | rax=ptr на тело, rdx=длина (rax=0 → ошибка) |
tg_api_call_post | rdi=метод, rsi=длина, rdx=тело, rcx=длина | то же, но POST |
tg_send_message | rdi=chat_id (строка), rsi=длина, rdx=текст, rcx=длина | rax=0/-1 |
tg_url_encode | rdi=источник, rsi=длина, rdx=назначение | rax=длина результата |
tg_api_call строит GET /bot<token>/<метод>?<query>, шлёт по keep-alive
соединению и один раз авто-переподключается при обрыве. query может быть пустым
(rcx=0). tg_api_call_post — то же через POST (тело application/x-www-form-urlencoded),
без лимита длины URL. tg_send_message использует POST и URL-кодирует текст.
Отправка файлов
| Функция | Аргументы | Возвращает |
|---|---|---|
tg_send_photo | rdi=chat_id, rsi=длина, rdx=путь (NUL), rcx=подпись, r8=длина подписи | rax=0/-1 |
tg_send_document | то же | rax=0/-1 |
Файл стримится как multipart/form-data прямо с диска через несколько TLS-записей
(без лимита на размер). Имя файла берётся из пути, подпись опциональна (r8=0).
Итерация апдейтов (getUpdates)
| Функция | Аргументы | Возвращает |
|---|---|---|
tg_iter_init | rdi=тело, rsi=длина | — копирует ответ, запускает итератор |
tg_iter_next | — | rax=update_id (0 = конец); задаёт tg_cur_ptr/tg_cur_len |
tg_update_text | — | rax=ptr, rdx=длина текста (rax=0 → нет) |
tg_update_chatid | — | rax=длина; цифры в tg_chatid_buf (rax=0 → нет) |
tg_iter_init копирует ответ в свой буфер, чтобы tg_send_message внутри цикла
не затёр тело, по которому идёт итерация.
JSON-хелперы
| Функция | Назначение |
|---|---|
json_after(hay, haylen, needle, nlen) | rax=ptr сразу после подстроки, или 0 |
parse_uint(ptr) | rax=число, rdx=ptr после него |
extract_number(src, dst) | копирует число (с ведущим -) в dst, rax=длина |
startswith(str, prefix, len) | rax=1/0 |
u64_to_dec(val, dst) | rax=длина; десятичная запись |
Требования и ограничения
- Платформа: только Linux x86-64; CPU с AES-NI, PCLMULQDQ, RDSEED.
- Сертификат сервера не проверяется — TLS шифрует канал, но цепочка не валидируется.
- Одно соединение, общее состояние — один вызов API за раз, не потокобезопасно.
- Разбор JSON по подстрокам — надёжен для плоских ответов Bot API, но не для произвольной вложенности.