开发者文档
简体中文

Pabal API

Pabal API(MTProto)

构建像应用一样连接服务器的客户端时需要的内容:连接信息、服务器公钥、登录流程、更新和支持范围。

Pabal 应用通过 MTProto 2.0 与服务器通信。如果你要开发以同样方式连接的程序——其他应用、以真人账号运行的自动化程序、研究用客户端——请阅读本文档。如果只是开发机器人,更简单的 Bot API 就足够了。

请同时参阅 Telegram 的文档

Pabal 遵循 Telegram 的公开协议,因此协议和方法的详细定义以 MTProtoAPI 方法文档为准。本文档记录的是连接 Pabal 服务器时的不同之处以及支持范围

连接信息

项目
地址122.34.175.215
端口8443(TCP)
DC1~5 都是同一个地址。连接哪个 DC 都可以,通常使用 2
协议MTProto 2.0,API Layer 216
传输方式Abridged、Intermediate、Padded Intermediate、Full——各自都包括混淆(obfuscated2)版本
服务器公钥server-key.pem · fingerprint 8724853375441383205
api_id · api_hash不做校验。填任何值都可以

目前还没有 HTTP 传输、WebSocket 传输和 MTProxy。客户端设备的时钟必须准确——MTProto 的消息编号由时间生成,时钟偏差过大时,服务器会丢弃消息。

服务器公钥

MTProto 客户端首次连接时,会用服务器的 RSA 公钥加密授权密钥交换过程。Telegram 客户端内置的是 Telegram 的公钥,因此要连接 Pabal,就必须用这个密钥替换(或同时加入)。这个密钥可以防止其他服务器冒充 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

这个密钥在服务器首次启动时生成一次,之后不会改变。请确认服务器日志中 Loaded RSA key … (fingerprint …) 的值与上面的 fingerprint 一致。

用 Telethon 连接

下面是用 Python 的 Telethon 登录真人账号并收发消息的示例。请先在应用中注册账号(见下文)。

# pabal_client.py — pip install telethon==1.42.0
# 在同一文件夹中放入 server-key.pem(上面下载的服务器公钥)
import asyncio

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

rsa.add_key(open("server-key.pem").read(), old=False)          # Pabal 服务器的公钥

client = TelegramClient("pabal", api_id=1, api_hash="0" * 32)  # 登录状态保存在 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("手机号码 (+86…): "))   # 第一次需要输入一次验证码
    me = await client.get_me()
    print(f"已登录:{me.first_name} (id {me.id})")
    await client.send_message("BotFather", "/help")
    await client.run_until_disconnected()


asyncio.run(main())
  • 请使用 Telethon 1.42。这是使用 Layer 216 的版本。更新的版本会尝试用更高的 Layer 解析响应,从而以 TypeNotFoundError 失败。
  • Telethon 禁止了注册新账号sign_up())。请在应用中注册,或者直接调用 auth.signUp
  • 登录状态保存在 pabal.session 文件中,之后不会再询问验证码。这个文件就是账号的钥匙,请妥善保管。

登录流程

1 · 授权密钥 (一次) req_pq_multi → req_DH_params set_client_DH_params 授权密钥 2048 位由服务器公钥保护 2 · 登录 (每个授权密钥一次) auth.sendCode手机号码 sentCodeTypeSmsSMS 或管理员转交 SetUpEmailRequired邮件方式 → 询问地址 account.sendVerifyEmailCodepurpose: loginSetup auth.signInphone_code 或 email 验证码 auth.authorization登录完成 authorizationSignUpRequired新号码 → auth.signUp(名字) 收到验证码后
先生成授权密钥,再用验证码登录

图示说明

  • 两个阶段:上方是生成加密所用授权密钥的阶段(协议),下方是把账号绑定到该密钥上的登录阶段(API)。上方的阶段由库自动完成。
  • 登录绑定在授权密钥上:用一个已经登录的密钥打开多个会话,这些会话全都处于登录状态。丢失密钥(删除会话文件)后需要重新登录。
  • 验证码的发送途径由服务器运营方决定:短信或管理员转交时返回 sentCodeTypeSms;电子邮件时返回 sentCodeTypeSetUpEmailRequired,客户端会询问电子邮件地址并调用 account.sendVerifyEmailCode(中间一行)。
  • 红色虚线是新号码的路径:验证码正确但账号不存在时,会返回 authorizationSignUpRequired,填入名字并调用 auth.signUp 即可完成注册。只有在验证码输入正确之后才能注册。
  • 机器人不走下方的阶段,而是用一次 auth.importBotAuthorization(机器人令牌)登录。
错误何时出现
PHONE_NUMBER_INVALID号码有误,或者在服务器关闭新注册的状态下使用了新号码
PHONE_NUMBER_BANNED被运营方封禁的号码
FLOOD_WAIT_n (420)请求验证码过于频繁。请在 n 秒后重试
PHONE_CODE_INVALID验证码错误(超过规定次数后会被锁定)
PHONE_CODE_EXPIRED验证码已过期或被锁定,或者 phone_code_hash 未知——请从 sendCode 重新开始
EMAIL_INVALID, EMAIL_NOT_ALLOWED电子邮件地址有误 / 已有账号,但该地址不是已登记的登录邮箱
AUTH_KEY_UNREGISTERED (401)用未登录的密钥调用了需要登录的方法
测试号码

在运营方开启了测试号码的服务器上,+99966XYYYY 形式的号码无需实际发送验证码,用验证码 XXXXX(X 重复五次)即可登录。这是只用于开发服务器的功能,在正式运营的服务器上是关闭的。

接收更新

  • 实时:连接保持打开时,服务器会立即以 updateShortMessageupdates 发送新消息、修改和删除。发出请求的会话会通过 RPC 响应收到结果,所以同样的内容不会再以推送形式重复送达。
  • pts:每个用户的变化都带有序号(pts)。如果客户端收到的 pts 出现空缺,就说明错过了什么。
  • 补齐:先用 updates.getState 记下当前的 pts,重新连接后用 updates.getDifference(pts, date, qts) 获取这段时间内的新消息、删除以及相关的用户和群组。如果客户端持有的 pts 比服务器还超前(例如服务器数据被重置),会返回 differenceTooLong,这时请重新加载对话列表。
  • Telethon 之类的库会自动完成上述全部过程。

ID 与对端

对象ID备注
用户从 100001 开始peerUser。@BotFather 为 100000
机器人与用户使用同一套编号user.bot = true。令牌前面的数字就是机器人 ID
普通群组从 1000001 开始peerChat。在 Bot API 中显示为负数(-chat_id
消息每个消息箱从 1 开始一对一对话中,每个参与者都有自己的副本和自己的编号。即使是同一条消息,两个人看到的编号也可能不同

请把服务器给出的 access_hash 原样保存下来再使用(它会随用户名查询、对话列表和更新一起送达)。

文件

  • 上传:用 upload.saveFilePart 分块上传,再用 inputFileUploaded… 引用。用于大文件的 upload.saveBigFilePart 暂不支持,因此实际上限为 10MB。
  • 发送:在 messages.sendMedia 中使用 inputMediaUploadedPhoto(新照片)或 inputMediaPhoto(服务器上已有的照片)。其他媒体返回 MEDIA_INVALID
  • 下载:在 upload.getFile 中使用 inputPhotoFileLocation(消息中的照片)、inputPeerPhotoFileLocation(头像)。每次最多 1MB。
  • 头像photos.uploadProfilePhotophotos.updateProfilePhotophotos.getUserPhotosphotos.deletePhotos
  • 照片只以原图一种尺寸保存(不单独生成缩略图)。

支持范围

服务器为 Layer 216 的 408 个方法都提供了处理程序,全部能够读取请求,但用真实客户端端到端验证过的是下面这些方法。其余方法的响应格式正确,但内容可能为空,或者不会被记录。

领域已验证的方法
连接initConnection, invokeWithLayer, help.getConfig, auth.bindTempAuthKey(PFS 临时密钥), auth.exportAuthorization/importAuthorization
登录auth.sendCode, auth.signIn, auth.signUp, auth.logOut, auth.importBotAuthorization, account.sendVerifyEmailCode
用户、联系人users.getUsers, users.getFullUser, contacts.resolveUsername, contacts.importContacts, contacts.search
消息messages.sendMessage, messages.sendMedia(照片), messages.getHistory, messages.getDialogs, messages.getMessages, messages.editMessage, messages.deleteMessages
群组messages.createChat, messages.deleteChatUser, messages.editChatTitlemessages.addChatUser 只用官方应用验证过)
机器人messages.getBotCallbackAnswer, messages.setBotCallbackAnswer, 带按钮(reply_markup)的消息
更新updates.getState, updates.getDifference, 实时推送
文件、照片upload.saveFilePart, upload.getFile, photos.*(见上文)

我们还验证了官方 Telegram Desktop 6.2.6 无需修改即可完成注册、登录、聊天、照片、群组和重新连接。应用启动时调用的约 60 个方法,其响应格式会另外进行检查。

错误

错误以标准的 rpc_errorerror_code + error_message)返回。error_message 始终是大写字母、数字和下划线的形式(PEER_ID_INVALID),必要时会附上 : 说明

代码含义
400请求有误——PEER_ID_INVALIDMESSAGE_ID_INVALIDMEDIA_INVALIDUSERNAME_NOT_OCCUPIED
401需要登录——AUTH_KEY_UNREGISTERED
403没有权限——例如不是成员的群组
420FLOOD_WAIT_n——等待 n 秒
500服务器内部错误
传输错误 -404服务器不认识这个授权密钥——生成新密钥后重新登录(永久密钥)或重新绑定(临时密钥)

尚不支持的功能

  • 频道和超级群组(channels.* 会响应,但在应用中无法正常显示)、私密聊天、通话
  • 照片以外的媒体、upload.saveBigFilePart、缩略图
  • 两步验证(SRP)——无法设置,需要两步验证的操作会被拒绝
  • HTTP 和 WebSocket 传输、MTProxy、发送 msgs_ack、更换 bad_server_salt
  • 拆分到多台服务器——由一台服务器同时承担 DC 1~5

让官方 Telegram 应用连接 Pabal

Telegram 应用在构建时就把服务器地址和公钥内置进去。因此不能在设置界面中修改,而是需要修改源代码后重新构建。Pabal 应用(Pabal.app)就是这样做出来的。如果你是服务器运营方,请参阅服务器安装——连接应用