API de Pabal
API de Pabal (MTProto)
Para crear un cliente que se conecte al servidor igual que la aplicación: datos de conexión, clave pública del servidor, flujo de inicio de sesión, actualizaciones y alcance del soporte.
La aplicación Pabal se comunica con el servidor mediante MTProto 2.0. Consulta este documento si vas a crear un programa que se conecte de la misma manera: otra aplicación, una automatización que funcione con una cuenta de persona o un cliente para investigación. Si lo que vas a crear es un bot, te basta con la Bot API, que es más sencilla.
Pabal sigue el protocolo público de Telegram, así que la referencia para las definiciones detalladas del protocolo y de los métodos son los documentos de MTProto y de los métodos de la API. Este documento recoge lo que cambia al conectarse a un servidor Pabal y el alcance del soporte.
Datos de conexión
| Concepto | Valor |
|---|---|
| Dirección | 122.34.175.215 |
| Puerto | 8443 (TCP) |
| DC | Del 1 al 5, todos con la misma dirección. Puedes conectarte a cualquier DC; lo habitual es usar el 2 |
| Protocolo | MTProto 2.0, capa (layer) de la API 216 |
| Transportes | Abridged, Intermediate, Padded Intermediate y Full, cada uno también con ofuscación (obfuscated2) |
| Clave pública del servidor | server-key.pem · fingerprint 8724853375441383205 |
| api_id · api_hash | No se comprueban. Pon cualquier valor |
Todavía no hay transporte HTTP, transporte WebSocket ni MTProxy. El reloj del dispositivo cliente debe estar en hora: los números de los mensajes MTProto se derivan de la hora, así que si el reloj va muy desviado, el servidor descarta los mensajes.
Clave pública del servidor
La primera vez que se conecta, un cliente MTProto cifra el intercambio de la clave de autorización con la clave pública RSA del servidor. Los clientes de Telegram llevan incorporada la clave pública de Telegram, así que para conectarse a Pabal hay que incorporar esta clave en su lugar (o además). Esta clave impide que otro servidor se haga pasar por el servidor 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
Esta clave se genera una sola vez, la primera vez que arranca el servidor, y no cambia. Comprueba que el valor de Loaded RSA key … (fingerprint …) en el log del servidor coincide con el fingerprint de arriba.
Conectarse con Telethon
Este es un ejemplo en el que, con Telethon para Python, se inicia sesión con una cuenta de persona y se intercambian mensajes. Regístrate antes en la aplicación (ver más abajo).
# pabal_client.py — pip install telethon==1.42.0
# En la misma carpeta, server-key.pem (la clave pública del servidor descargada arriba)
import asyncio
from telethon import TelegramClient, events
from telethon.crypto import rsa
rsa.add_key(open("server-key.pem").read(), old=False) # clave pública del servidor Pabal
client = TelegramClient("pabal", api_id=1, api_hash="0" * 32) # la sesión se guarda en 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("Número de teléfono (+8210…): ")) # la primera vez, introduce el código
me = await client.get_me()
print(f"Sesión iniciada: {me.first_name} (id {me.id})")
await client.send_message("BotFather", "/help")
await client.run_until_disconnected()
asyncio.run(main())
- Usa Telethon 1.42, que es la versión que habla la capa 216. Las versiones más recientes intentan leer las respuestas con una capa superior y fallan con
TypeNotFoundError. - Telethon tiene bloqueado el registro de cuentas nuevas (
sign_up()). Regístrate en la aplicación o llama directamente aauth.signUp. - La sesión se guarda en el archivo
pabal.session, así que las siguientes veces no se pide el código. Ese archivo es la llave de la cuenta: protégelo.
Flujo de inicio de sesión
Explicación del diagrama
- Dos fases: arriba, la fase en la que se crea la clave de autorización que se usará para cifrar (protocolo); abajo, la fase de inicio de sesión, que asocia una cuenta a esa clave (API). Las bibliotecas se encargan solas de la fase de arriba.
- El inicio de sesión queda asociado a la clave de autorización: aunque abras varias sesiones con una clave con la que ya iniciaste sesión, todas quedan con la sesión iniciada. Si pierdes la clave (borrando el archivo de sesión), tendrás que volver a iniciar sesión.
- El camino que sigue el código lo decide el operador del servidor: si es SMS o entrega por el administrador, llega
sentCodeTypeSms; si es correo electrónico, llegasentCodeTypeSetUpEmailRequired, y el cliente pide la dirección de correo y llama aaccount.sendVerifyEmailCode(fila del medio). - La línea discontinua roja es el camino de un número nuevo: si el código es correcto pero la cuenta no existe, llega
authorizationSignUpRequired, y al llamar aauth.signUpcon un nombre termina el registro. El registro solo es posible después de acertar el código. - Los bots, en lugar de la fase de abajo, inician sesión con una sola llamada a
auth.importBotAuthorization(con el token del bot).
| Error | Cuándo |
|---|---|
PHONE_NUMBER_INVALID | El número es incorrecto, o es un número nuevo mientras el servidor tiene cerrados los registros |
PHONE_NUMBER_BANNED | Número bloqueado por el operador |
FLOOD_WAIT_n (420) | Se ha pedido el código con demasiada frecuencia. Vuelve a intentarlo dentro de n segundos |
PHONE_CODE_INVALID | El código es incorrecto (se bloquea al superar el número de intentos permitido) |
PHONE_CODE_EXPIRED | El código ha caducado o está bloqueado, o el phone_code_hash es desconocido: vuelve a empezar desde sendCode |
EMAIL_INVALID, EMAIL_NOT_ALLOWED | La dirección de correo es incorrecta / la cuenta ya existe y ese no es su correo de inicio de sesión registrado |
AUTH_KEY_UNREGISTERED (401) | Se ha llamado a un método que requiere sesión iniciada con una clave sin sesión iniciada |
En un servidor en el que el operador ha activado los números de prueba, los números con la forma +99966XYYYY inician sesión sin que se entregue ningún código real, con el código XXXXX (la X repetida cinco veces). Es una función solo para servidores de desarrollo y está desactivada en los servidores de producción.
Recibir actualizaciones
- En tiempo real: mientras la conexión está abierta, el servidor envía al instante los mensajes nuevos, las ediciones y los borrados como
updateShortMessageyupdates. La sesión que envió la petición recibe el resultado como respuesta RPC, así que no le llega lo mismo otra vez como push. - pts: los cambios de cada usuario llevan un número de orden (pts). Si el cliente ve un hueco en los pts recibidos, es que se ha perdido algo.
- Ponerse al día: guarda el pts actual con
updates.getStatey, al volver a conectarte, obtén conupdates.getDifference(pts, date, qts)los mensajes nuevos y los borrados de ese intervalo, junto con los usuarios y grupos relacionados. Si el pts que tiene el cliente va por delante del servidor (por ejemplo, si se reiniciaron los datos del servidor), llegadifferenceTooLong: vuelve a cargar la lista de chats. - Bibliotecas como Telethon se encargan de todo este proceso por sí solas.
IDs y peers
| Entidad | ID | Notas |
|---|---|---|
| Personas | Desde 100001 | peerUser. @BotFather es el 100000 |
| Bots | La misma numeración que las personas | user.bot = true. El número al principio del token es el ID del bot |
| Grupos básicos | Desde 1000001 | peerChat. En la Bot API se ven como número negativo (-chat_id) |
| Mensajes | Desde 1 en cada buzón | En un chat uno a uno, cada participante tiene su propia copia con su propio número. El mismo mensaje puede tener números distintos para cada una de las dos personas |
Guarda el access_hash tal como te lo da el servidor y úsalo después (llega junto con la resolución de nombres de usuario, la lista de chats y las actualizaciones).
Archivos
- Subir: sube los fragmentos con
upload.saveFileParty haz referencia a ellos coninputFileUploaded…. Todavía no existeupload.saveBigFilePartpara archivos grandes, así que en la práctica el límite es de 10 MB. - Enviar:
messages.sendMediaconinputMediaUploadedPhoto(foto nueva) oinputMediaPhoto(foto que ya está en el servidor). Otros tipos de contenido multimedia devuelvenMEDIA_INVALID. - Recibir:
upload.getFileconinputPhotoFileLocation(foto de un mensaje) oinputPeerPhotoFileLocation(foto de perfil). Como máximo 1 MB por llamada. - Fotos de perfil:
photos.uploadProfilePhoto,photos.updateProfilePhoto,photos.getUserPhotos,photos.deletePhotos. - Las fotos se guardan en un único tamaño, el original (no se generan miniaturas aparte).
Alcance del soporte
El servidor tiene manejadores para 408 métodos de la capa 216 y todos pueden leer las peticiones, pero los que se han comprobado de principio a fin con clientes reales son los métodos de abajo. El resto responde con el formato correcto, pero puede que el contenido llegue vacío o que no se registre.
| Área | Métodos comprobados |
|---|---|
| Conexión | initConnection, invokeWithLayer, help.getConfig, auth.bindTempAuthKey (claves temporales PFS), auth.exportAuthorization/importAuthorization |
| Inicio de sesión | auth.sendCode, auth.signIn, auth.signUp, auth.logOut, auth.importBotAuthorization, account.sendVerifyEmailCode |
| Usuarios y contactos | users.getUsers, users.getFullUser, contacts.resolveUsername, contacts.importContacts, contacts.search |
| Mensajes | messages.sendMessage, messages.sendMedia (fotos), messages.getHistory, messages.getDialogs, messages.getMessages, messages.editMessage, messages.deleteMessages |
| Grupos | messages.createChat, messages.deleteChatUser, messages.editChatTitle (messages.addChatUser solo se ha comprobado con la aplicación oficial) |
| Bots | messages.getBotCallbackAnswer, messages.setBotCallbackAnswer, mensajes con botones (reply_markup) |
| Actualizaciones | updates.getState, updates.getDifference, push en tiempo real |
| Archivos y fotos | upload.saveFilePart, upload.getFile, photos.* (arriba) |
También se ha comprobado que la aplicación oficial Telegram Desktop 6.2.6 funciona sin modificaciones en el registro, el inicio de sesión, los chats, las fotos, los grupos y la reconexión. El formato de las respuestas de los aproximadamente 60 métodos a los que llama la aplicación al arrancar se verifica aparte.
Errores
Los errores llegan como el rpc_error estándar (error_code + error_message). error_message siempre tiene la forma de mayúsculas, números y guiones bajos (PEER_ID_INVALID) y, si hace falta, lleva detrás : descripción.
| Código | Significado |
|---|---|
400 | La petición es incorrecta: PEER_ID_INVALID, MESSAGE_ID_INVALID, MEDIA_INVALID, USERNAME_NOT_OCCUPIED … |
401 | Hace falta iniciar sesión: AUTH_KEY_UNREGISTERED |
403 | Sin permiso: por ejemplo, un grupo del que no eres miembro |
420 | FLOOD_WAIT_n: espera n segundos |
500 | Error interno del servidor |
Error de transporte -404 | El servidor no conoce esta clave de autorización: crea una clave nueva y vuelve a iniciar sesión (clave permanente) o vuelve a vincularla (clave temporal) |
Lo que todavía no existe
- Canales y supergrupos (
channels.*responde, pero no se muestra bien en la aplicación), chats secretos, llamadas - Contenido multimedia distinto de las fotos,
upload.saveBigFilePart, miniaturas - Verificación en dos pasos (SRP): no se puede configurar, y las operaciones que la requieren se rechazan
- Transportes HTTP y WebSocket, MTProxy, envío de
msgs_ack, sustitución debad_server_salt - Repartir la carga entre varios servidores: un solo servidor atiende todos los DC del 1 al 5
Para conectar la aplicación oficial de Telegram a Pabal
La aplicación de Telegram incorpora la dirección del servidor y la clave pública al compilarse. Por eso no se cambia desde la pantalla de ajustes: hay que modificar el código fuente y volver a compilarla. Así es como se hizo la aplicación Pabal (Pabal.app). Si operas un servidor, consulta Instalar el servidor — conectar la aplicación.