Tài liệu nhà phát triển
Tiếng Việt

Pabal API

Pabal API (MTProto)

Khi xây dựng client kết nối đến máy chủ theo cùng cách với ứng dụng: thông tin kết nối, khóa công khai của máy chủ, luồng xác thực, update và phạm vi hỗ trợ.

Ứng dụng Pabal giao tiếp với máy chủ bằng MTProto 2.0. Hãy đọc tài liệu này khi bạn xây dựng chương trình kết nối theo cùng cách đó — một ứng dụng khác, công cụ tự động hóa chạy bằng tài khoản người dùng, hay client phục vụ nghiên cứu. Nếu bạn tạo bot, Bot API đơn giản hơn là đủ dùng.

Hãy xem cả tài liệu của Telegram

Pabal tuân theo giao thức công khai của Telegram, nên định nghĩa chi tiết của giao thức và các phương thức lấy tài liệu MTProto và phương thức API làm chuẩn. Tài liệu này ghi lại những điểm khác biệt và phạm vi hỗ trợ khi kết nối đến máy chủ Pabal.

Thông tin kết nối

MụcGiá trị
Địa chỉ122.34.175.215
Cổng8443 (TCP)
DCCả DC 1–5 đều cùng một địa chỉ. Kết nối vào DC nào cũng được, thường dùng DC 2
Giao thứcMTProto 2.0, API layer 216
Kiểu truyền tảiAbridged, Intermediate, Padded Intermediate, Full — mỗi kiểu đều có bản làm rối (obfuscated2)
Khóa công khai máy chủserver-key.pem · fingerprint 8724853375441383205
api_id · api_hashKhông được kiểm tra. Điền giá trị nào cũng được

Hiện chưa có truyền tải qua HTTP, WebSocket, cũng như MTProxy. Đồng hồ trên thiết bị client phải chính xác — số hiệu tin nhắn MTProto được tạo từ thời gian, nên nếu đồng hồ lệch nhiều, máy chủ sẽ bỏ các tin nhắn đó.

Khóa công khai của máy chủ

Khi kết nối lần đầu, client MTProto dùng khóa công khai RSA của máy chủ để mã hóa quá trình trao đổi khóa xác thực. Client Telegram có sẵn khóa công khai của Telegram bên trong, nên muốn kết nối đến Pabal thì phải nhúng khóa này thay vào (hoặc thêm vào). Chính khóa này ngăn máy chủ khác giả làm máy chủ Pabal.

-----BEGIN RSA PUBLIC KEY-----
MIIBCgKCAQEA4hH74xPQsUwr/pyXPdF4tVicYr6QbfeDrKC7mUOVrPLSL4FtmgGn
w+O4u6lVvOf3Udd1KY6+OL4fUZdMBlLzwsoGLoniiVR09dnvyXHE8LhQSS+i1LmI
oJbhQwXplLnUJf272fLXkD23e7ppKLkjYk+jeYObueCy5HYMSThklVeXEzbZVGZv
47o/mjU2vyFoRpa6wCIE4rpj1UIPtOMpekMI/TocIlGVJ+ch6cAVNxIDro53a1eG
/1oZRLQH4oViEGxeMMjBMY5gk5HPkZvjbqy4h8TjXEz7O5o0BRsSqG/OPtP/dRQV
X4ewPhj2WCU4e6l5X59ZKIcv4sQLOwClqQIDAQAB
-----END RSA PUBLIC KEY-----
curl -s -o server-key.pem https://pabal.me/docs/server-key.pem

Khóa này được tạo một lần khi máy chủ chạy lần đầu và không thay đổi. Hãy kiểm tra giá trị Loaded RSA key … (fingerprint …) trong log máy chủ có khớp với fingerprint ở trên không.

Kết nối bằng Telethon

Ví dụ dùng Telethon của Python để đăng nhập bằng tài khoản người dùng và nhắn tin qua lại. Hãy đăng ký tài khoản trong ứng dụng trước (xem bên dưới).

# pabal_client.py — pip install telethon==1.42.0
# Đặt server-key.pem (khóa công khai máy chủ đã tải ở trên) trong cùng thư mục
import asyncio

from telethon import TelegramClient, events
from telethon.crypto import rsa

rsa.add_key(open("server-key.pem").read(), old=False)          # khóa công khai của máy chủ Pabal

client = TelegramClient("pabal", api_id=1, api_hash="0" * 32)  # lưu trạng thái đăng nhập vào pabal.session
client.session.set_dc(2, "122.34.175.215", 8443)


@client.on(events.NewMessage(incoming=True))
async def show(event):
    sender = await event.get_sender()
    print(f"{sender.first_name}: {event.raw_text}")


async def main():
    await client.start(phone=lambda: input("Số điện thoại (+849…): "))   # nhập mã một lần đầu tiên
    me = await client.get_me()
    print(f"Đã đăng nhập: {me.first_name} (id {me.id})")
    await client.send_message("BotFather", "/help")
    await client.run_until_disconnected()


asyncio.run(main())
  • Hãy dùng Telethon 1.42. Đây là bản dùng layer 216. Các bản mới hơn cố đọc phản hồi theo layer cao hơn nên thất bại với TypeNotFoundError.
  • Telethon đã chặn việc đăng ký tài khoản mới (sign_up()). Hãy đăng ký trong ứng dụng, hoặc tự gọi auth.signUp.
  • Trạng thái đăng nhập được lưu vào tệp pabal.session, nên từ lần sau sẽ không hỏi mã nữa. Tệp này là chìa khóa của tài khoản, hãy giữ nó cẩn thận.

Luồng đăng nhập

1 · Khóa xác thực (một lần) req_pq_multi → req_DH_params set_client_DH_params Khóa xác thực 2048 bitBảo vệ bằng khóa công khai máy chủ 2 · Đăng nhập (mỗi khóa xác thực một lần) auth.sendCodeSố điện thoại sentCodeTypeSmsSMS hoặc quản trị viên chuyển SetUpEmailRequiredKiểu email → hỏi địa chỉ account.sendVerifyEmailCodepurpose: loginSetup auth.signInphone_code hoặc mã email auth.authorizationĐăng nhập xong authorizationSignUpRequiredSố mới → auth.signUp(tên) Khi nhận được mã
Tạo khóa xác thực, rồi đăng nhập bằng mã

Giải thích sơ đồ

  • Hai giai đoạn: phía trên là giai đoạn tạo khóa xác thực dùng để mã hóa (giao thức), phía dưới là giai đoạn đăng nhập gắn tài khoản vào khóa đó (API). Thư viện sẽ tự lo giai đoạn phía trên.
  • Trạng thái đăng nhập gắn với khóa xác thực: dù mở nhiều phiên bằng khóa đã đăng nhập, tất cả đều ở trạng thái đăng nhập. Nếu mất khóa (xóa tệp phiên) thì phải đăng nhập lại.
  • Mã được gửi theo đường nào là do người vận hành máy chủ quyết định: nếu là SMS hoặc quản trị viên chuyển thì nhận sentCodeTypeSms; nếu là email thì nhận sentCodeTypeSetUpEmailRequired, khi đó client hỏi địa chỉ email rồi gọi account.sendVerifyEmailCode (hàng giữa).
  • Nét đứt màu đỏ là đường của số mới: nếu mã đúng nhưng chưa có tài khoản, bạn nhận authorizationSignUpRequired; nhập tên và gọi auth.signUp là hoàn tất đăng ký. Chỉ đăng ký được sau khi đã nhập đúng mã.
  • Bot không đi qua giai đoạn phía dưới mà đăng nhập bằng một lần gọi auth.importBotAuthorization (token bot).
LỗiKhi nào
PHONE_NUMBER_INVALIDSố sai, hoặc là số mới trong lúc máy chủ đang đóng đăng ký mới
PHONE_NUMBER_BANNEDSố đã bị người vận hành chặn, hoặc số của tài khoản bị tạm khóa (nếu bị xóa khi đang tạm khóa thì mặc định 1 năm — người vận hành có thể kéo dài hoặc đặt vô thời hạn)
FLOOD_WAIT_n (420)Yêu cầu mã quá thường xuyên. Thử lại sau n giây
PHONE_CODE_INVALIDMã sai (vượt quá số lần cho phép thì bị khóa)
PHONE_CODE_EXPIREDMã đã hết hạn hoặc bị khóa, hoặc phone_code_hash không xác định — bắt đầu lại từ sendCode
EMAIL_INVALID, EMAIL_NOT_ALLOWEDĐịa chỉ email sai / tài khoản đã tồn tại nhưng đây không phải email đăng nhập đã đăng ký
AUTH_KEY_UNREGISTERED (401)Gọi phương thức cần đăng nhập bằng khóa chưa đăng nhập
Số thử nghiệm

Trên máy chủ mà người vận hành bật số thử nghiệm, các số có dạng +99966XYYYY đăng nhập được bằng mã XXXXX (lặp X năm lần) mà không cần gửi mã thật. Tính năng này chỉ dùng cho máy chủ phát triển và bị tắt trên máy chủ vận hành thật.

Nhận update

  • Thời gian thực: khi kết nối đang mở, máy chủ gửi ngay tin nhắn mới, sửa, xóa dưới dạng updateShortMessage, updates. Phiên đã gửi yêu cầu nhận kết quả qua phản hồi RPC, nên cùng nội dung đó sẽ không được đẩy tới phiên ấy thêm lần nữa.
  • pts: mỗi thay đổi của từng người dùng được gắn một số thứ tự (pts). Nếu pts nhận được có khoảng trống thì client đã bỏ lỡ điều gì đó.
  • Bắt kịp: ghi nhớ pts hiện tại bằng updates.getState, và khi kết nối lại thì dùng updates.getDifference(pts, date, qts) để nhận tin nhắn mới, lượt xóa trong khoảng đó cùng người dùng và nhóm liên quan. Nếu pts mà client đang giữ vượt trước máy chủ (ví dụ khi dữ liệu máy chủ đã bị khởi tạo lại), bạn sẽ nhận differenceTooLong, khi đó hãy tải lại danh sách trò chuyện.
  • Thông báo đẩy — Android: ứng dụng Android không có kết nối được đánh thức bằng push của Firebase. Khi ứng dụng đăng ký bằng account.registerDevice (token_type 2 = token FCM, secret = khóa thiết bị 256 byte), máy chủ mã hóa tin nhắn và cuộc gọi mới bằng khóa thiết bị đó rồi gửi qua FCM, còn ứng dụng khi thức dậy sẽ bắt kịp bằng updates.getDifference. Thông báo đẩy chỉ đến được những ứng dụng build bằng dự án Firebase của người vận hành máy chủ.
  • Thông báo đẩy — iPhone: ứng dụng iPhone đăng ký hai thứ bằng cùng yêu cầu đó — token_type 1 (token thông báo APNs) và 9 (token VoIP của PushKit); secret của cả hai đều là khóa thiết bị 256 byte của tài khoản đó. Tin nhắn mới được gửi thành thông báo APNs: phần chữ bên ngoài mà Apple thấy được chỉ là tên ứng dụng và một câu không có nội dung như "Bạn có thông báo mới", còn tin nhắn được mã hóa bằng khóa thiết bị và đặt trong p, rồi phần mở rộng thông báo (Notification Service Extension) của ứng dụng giải mã và hiển thị. Cuộc gọi được gửi bằng push VoIP để hiện màn hình cuộc gọi CallKit (từ bản build mà người vận hành đã bật VoIP; trước đó thì gửi thành thông báo), còn những gì đã đọc hoặc đã xóa trên thiết bị khác được báo bằng push chạy nền. Thông báo đẩy chỉ đến được những ứng dụng được ký bởi cùng nhóm (team) với khóa APNs của người vận hành máy chủ và có cùng bundle ID. Các token_type khác chỉ được trả lời boolTrue và không được gửi gì.
  • Các thư viện như Telethon tự lo toàn bộ quá trình này.

ID và peer

Đối tượngIDGhi chú
NgườiTừ 100001peerUser. @BotFather là 100000
BotCùng hệ thống số với ngườiuser.bot = true. Dãy số ở đầu token là ID của bot
Nhóm cơ bảnTừ 1000001peerChat. Trong Bot API hiển thị là số âm (-chat_id)
Tin nhắnMột dãy số chung cho toàn máy chủTrong trò chuyện 1:1, hai bản sao mang cùng một số, nên các yêu cầu chỉ nhận số tin nhắn mà không có peer (messages.getMessages, messages.deleteMessages) đều chỉ đến cùng một tin nhắn đối với cả hai người. Chỉ kênh là đếm riêng từ 1

Hãy lưu nguyên giá trị access_hash mà máy chủ cấp để dùng về sau (nó đi kèm khi tra cứu tên người dùng, trong danh sách trò chuyện và trong update).

Tệp

  • Tải lên: tải từng phần bằng upload.saveFilePart (mỗi phần tối đa 512KB, tối đa 4.000 phần) và tham chiếu bằng inputFileUploaded…. Tệp lớn hơn 10MB được tải lên bằng upload.saveBigFilePart — vì bạn báo trước số phần nên nếu máy chủ không nhận kích thước đó, nó từ chối ngay bằng FILE_TOO_BIG trước khi bạn gửi hết. Các phần được ghi xuống đĩa ngay khi đến và chỉ được ghép lại thành một tệp khi có tin nhắn trỏ tới tệp đó.
  • Gửi: dùng messages.sendMedia với inputMediaUploadedPhoto (ảnh mới), inputMediaPhoto (ảnh đã có trên máy chủ), inputMediaUploadedDocument (tệp mới), inputMediaDocument (tệp đã có trên máy chủ). Các phương tiện khác trả về MEDIA_INVALID. Bạn cũng có thể tải lên trước mà chưa gửi bằng messages.uploadMedia (media nó trả về sẽ được gửi nguyên như vậy về sau), còn messages.sendMultiMedia thì được chấp nhận nhưng các mục đi từng cái một, không gộp thành nhóm (không có grouped_id).
  • Nhận: dùng upload.getFile với inputPhotoFileLocation (ảnh trong tin nhắn), inputDocumentFileLocation (tệp trong tin nhắn), inputPeerPhotoFileLocation (ảnh đại diện). Tối đa 1MB mỗi lần. Bên nhận cũng chỉ đọc đúng đoạn được yêu cầu chứ không nạp cả tệp vào bộ nhớ.
  • Ảnh đại diện: photos.uploadProfilePhoto, photos.updateProfilePhoto, photos.getUserPhotos, photos.deletePhotos.
  • Ảnh chỉ được lưu ở một kích thước gốc (không tạo ảnh thu nhỏ riêng). Tệp thì được giữ nguyên vẹn, và các documentAttribute* do bên gửi đính kèm được chuyển nguyên byte tới bên nhận — máy chủ không cần hiểu độ dài video hay dạng sóng của âm thanh mà vẫn chuyển đi y nguyên.

Phạm vi hỗ trợ

Máy chủ có bộ xử lý cho 469 phương thức của layer 216 và tất cả đều đọc được yêu cầu, nhưng những phương thức đã được kiểm chứng trọn vẹn bằng client thật là các phương thức dưới đây. Các phương thức còn lại trả lời đúng định dạng nhưng nội dung có thể rỗng hoặc không được ghi lại.

Lĩnh vựcPhương thức đã kiểm chứng
Kết nốiinitConnection, invokeWithLayer, help.getConfig, auth.bindTempAuthKey (khóa tạm thời PFS), auth.exportAuthorization/importAuthorization
Đăng nhậpauth.sendCode, auth.signIn, auth.signUp, auth.logOut, auth.importBotAuthorization, account.sendVerifyEmailCode
Người dùng, danh bạusers.getUsers, users.getFullUser, contacts.resolveUsername, contacts.importContacts, contacts.search
Tin nhắnmessages.sendMessage, messages.sendMedia (ảnh, tệp), messages.getHistory, messages.getDialogs, messages.getMessages, messages.editMessage, messages.deleteMessages
Nhómmessages.createChat, messages.deleteChatUser, messages.editChatTitle (messages.addChatUser chỉ mới kiểm chứng bằng ứng dụng chính thức)
Botmessages.getBotCallbackAnswer, messages.setBotCallbackAnswer, tin nhắn có gắn nút (reply_markup)
Updateupdates.getState, updates.getDifference, đẩy (push) thời gian thực
Tệp, ảnhupload.saveFilePart, upload.saveBigFilePart (trên 10MB), upload.getFile, photos.* (ở trên)

Chúng tôi cũng đã kiểm chứng Telegram Desktop chính thức 6.2.6 hoạt động không cần sửa đổi, từ đăng ký, đăng nhập, trò chuyện, ảnh, nhóm cho đến kết nối lại. Khoảng 60 phương thức mà ứng dụng gọi khi khởi động được kiểm tra định dạng phản hồi riêng.

Những gì thêm sau đó — danh sách thiết bị và đăng xuất thiết bị (account.getAuthorizations, account.resetAuthorization, auth.resetAuthorizations), xuất dữ liệu của ứng dụng máy tính (account.initTakeoutSession, invokeWithTakeout, invokeWithMessagesRange, account.finishTakeoutSession — mỗi tài khoản ba lần bắt đầu mỗi ngày, vượt quá thì 420 TAKEOUT_INIT_DELAY_n), đổi số (account.sendChangePhoneCode, account.changePhone — 400 PHONE_CHANGE_DISABLED khi người vận hành chưa bật, BOT_METHOD_INVALID với bot) — đã được kiểm chứng bằng các bài thử của máy chủ; chưa kiểm tra trọn vẹn bằng ứng dụng thật.

Lỗi

Lỗi được trả về dưới dạng rpc_error chuẩn (error_code + error_message). error_message luôn có dạng chữ hoa, chữ số và dấu gạch dưới (PEER_ID_INVALID), và khi cần sẽ có thêm : mô tả phía sau.

MãÝ nghĩa
400Yêu cầu sai — PEER_ID_INVALID, MESSAGE_ID_INVALID, MEDIA_INVALID, USERNAME_NOT_OCCUPIED …
401Cần đăng nhập — AUTH_KEY_UNREGISTERED
403Không có quyền — ví dụ nhóm mà bạn không phải thành viên
406Chưa được phép lúc này — thiết bị mới đăng nhập chưa đủ 24 giờ cố đăng xuất thiết bị khác: FRESH_RESET_AUTHORISATION_FORBIDDEN
420FLOOD_WAIT_n — chờ n giây
500Lỗi nội bộ máy chủ
Lỗi truyền tải -404Máy chủ không biết khóa xác thực này — tạo khóa mới rồi đăng nhập lại (khóa vĩnh viễn) hoặc gắn lại (khóa tạm thời)

API đồng hồ

/api/watch/v1/ của máy chủ là điểm vào HTTPS JSON chỉ dành cho ứng dụng Apple Watch của Pabal. Nó tồn tại riêng vì đồng hồ không dùng được MTProto; đây không phải đặc tả công khai và đang được chuẩn bị, nên có thể thay đổi mà không báo trước. Nếu muốn viết chương trình kết nối tới Pabal, hãy dùng MTProto ở trang này. Người vận hành máy chủ xem Cài đặt máy chủ — Điểm vào của Apple Watch.

Những gì chưa có

  • Kênh và siêu nhóm (channels.* có trả lời nhưng không hiển thị đúng trong ứng dụng), trò chuyện bí mật, gọi nhóm
  • Tin nhắn video và tin nhắn thoại phát ngay trong ứng dụng, album hiện ra thành một nhóm (grouped_id), sticker, ảnh thu nhỏ (tệp chỉ có bản gốc)
  • Xác minh hai bước (SRP) — không thể thiết lập, và các thao tác cần xác minh hai bước đều bị từ chối
  • Truyền tải qua HTTP và WebSocket, MTProxy, gửi msgs_ack, xoay vòng định kỳ salt của máy chủ (salt sai được trả lời bằng bad_server_salt)
  • Chia ra nhiều máy chủ — một máy chủ đảm nhận cả DC 1–5

Để kết nối ứng dụng Telegram chính thức với Pabal

Ứng dụng Telegram nhúng địa chỉ máy chủ và khóa công khai vào bên trong lúc build. Vì vậy không đổi được trong màn hình cài đặt mà phải sửa mã nguồn rồi build lại. Ứng dụng Pabal (Pabal.app) chính là được làm như vậy. Nếu bạn là người vận hành máy chủ, hãy xem Cài đặt máy chủ — kết nối ứng dụng.