Перейти к основному содержимому

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 APIsendMessage, отправка фото/документов, разбор 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_tokenrdi=путь (NUL-строка)rax=0/-1; читает файл, срезает перевод строки
tg_set_tokenrdi=ptr, rsi=длина— задаёт токен из памяти
tg_connectrax=0/-1; маршрутизация + TLS-рукопожатие

Запросы

ФункцияАргументыВозвращает
tg_api_callrdi=метод, rsi=длина, rdx=query, rcx=длинаrax=ptr на тело, rdx=длина (rax=0 → ошибка)
tg_api_call_postrdi=метод, rsi=длина, rdx=тело, rcx=длинато же, но POST
tg_send_messagerdi=chat_id (строка), rsi=длина, rdx=текст, rcx=длинаrax=0/-1
tg_url_encoderdi=источник, 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_photordi=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_initrdi=тело, rsi=длина— копирует ответ, запускает итератор
tg_iter_nextrax=update_id (0 = конец); задаёт tg_cur_ptr/tg_cur_len
tg_update_textrax=ptr, rdx=длина текста (rax=0 → нет)
tg_update_chatidrax=длина; цифры в 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, но не для произвольной вложенности.