개발자 문서
한국어

파발 API

파발 API (MTProto)

앱과 같은 방식으로 서버에 붙는 클라이언트를 만들 때: 접속 정보, 서버 공개키, 인증 흐름, 업데이트, 지원 범위.

파발 앱은 서버와 MTProto 2.0 으로 이야기합니다. 같은 방식으로 붙는 프로그램 — 다른 앱, 사람 계정으로 도는 자동화, 연구용 클라이언트 — 을 만들 때 이 문서를 보세요. 봇을 만든다면 더 간단한 Bot API 로 충분합니다.

텔레그램 문서를 함께 보세요

파발은 텔레그램의 공개 프로토콜을 따르므로, 프로토콜과 메서드의 자세한 정의는 MTProtoAPI 메서드 문서가 기준입니다. 이 문서는 파발 서버에 붙을 때 다른 점과 지원 범위를 적습니다.

접속 정보

항목
주소122.34.175.215
포트8443 (TCP)
DC1~5 모두 같은 주소입니다. 어느 DC 로 붙어도 되고, 보통 2 를 씁니다
프로토콜MTProto 2.0, API 레이어 216
전송 방식Abridged, Intermediate, Padded Intermediate, Full — 각각 난독화(obfuscated2) 포함
서버 공개키server-key.pem · fingerprint 8724853375441383205
api_id · api_hash확인하지 않습니다. 아무 값이나 넣으세요

HTTP 전송, WebSocket 전송, MTProxy 는 아직 없습니다. 클라이언트 기기의 시계가 맞아야 합니다 — MTProto 메시지 번호가 시각에서 나오므로, 시계가 크게 틀리면 서버가 메시지를 버립니다.

서버 공개키

MTProto 클라이언트는 처음 접속할 때 서버의 RSA 공개키로 인증 키 교환을 암호화합니다. 텔레그램 클라이언트는 텔레그램의 공개키를 몸속에 넣고 있으므로, 파발에 붙으려면 이 키를 대신(또는 함께) 넣어야 합니다. 이 키가 다른 서버가 파발 서버인 척하는 것을 막습니다.

-----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)          # 파발 서버의 공개키

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("전화번호 (+8210…): "))   # 처음 한 번 코드 입력
    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 를 쓰세요. 레이어 216 을 말하는 판입니다. 더 새 판은 더 높은 레이어로 응답을 읽으려 해 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)입니다. 라이브러리가 위 단계를 알아서 해 줍니다.
  • 로그인은 인증 키에 붙습니다: 한 번 로그인한 키로 여러 세션을 열어도 모두 로그인 상태입니다. 키를 잃으면(세션 파일 삭제) 다시 로그인해야 합니다.
  • 코드가 가는 길은 서버 운영자가 정합니다: SMS·관리자 전달이면 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 를 다섯 번)로 로그인됩니다. 개발용 서버에만 쓰는 기능이며, 운영 서버에서는 꺼져 있습니다.

업데이트 받기

  • 실시간: 연결이 열려 있으면 서버가 새 메시지·수정·삭제를 updateShortMessage, updates 로 바로 보냅니다. 요청을 보낸 세션은 그 결과를 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 부터1:1 대화는 참여자마다 자기 사본과 자기 번호를 가집니다. 같은 메시지라도 두 사람에게 번호가 다를 수 있습니다

access_hash 는 서버가 준 값을 그대로 저장해 두었다가 쓰세요(사용자명 조회·대화 목록·업데이트에 함께 옵니다).

파일

  • 올리기: upload.saveFilePart 로 조각을 올리고 inputFileUploaded… 로 참조합니다. 큰 파일용 upload.saveBigFilePart 는 아직 없어 사실상 10MB 까지입니다.
  • 보내기: messages.sendMediainputMediaUploadedPhoto(새 사진) 또는 inputMediaPhoto(서버에 있는 사진). 다른 미디어는 MEDIA_INVALID.
  • 받기: upload.getFileinputPhotoFileLocation(메시지 사진), inputPeerPhotoFileLocation(프로필 사진). 한 번에 최대 1MB.
  • 프로필 사진: photos.uploadProfilePhoto, photos.updateProfilePhoto, photos.getUserPhotos, photos.deletePhotos.
  • 사진은 원본 한 가지 크기로만 저장합니다(썸네일을 따로 만들지 않음).

지원 범위

서버에는 레이어 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.editChatTitle (messages.addChatUser 는 공식 앱으로만 확인)
messages.getBotCallbackAnswer, messages.setBotCallbackAnswer, 버튼(reply_markup)이 달린 메시지
업데이트updates.getState, updates.getDifference, 실시간 푸시
파일·사진upload.saveFilePart, upload.getFile, photos.*(위)

공식 텔레그램 데스크톱 6.2.6 이 가입·로그인·대화·사진·그룹·재접속까지 수정 없이 동작하는 것도 확인했습니다. 앱이 시작할 때 부르는 약 60개 메서드는 응답 형식을 따로 검사합니다.

오류

오류는 표준 rpc_error(error_code + error_message)로 옵니다. error_message 는 늘 대문자·숫자·밑줄(PEER_ID_INVALID) 꼴이고, 필요하면 : 설명 이 붙습니다.

코드
400요청이 틀림 — PEER_ID_INVALID, MESSAGE_ID_INVALID, MEDIA_INVALID, USERNAME_NOT_OCCUPIED
401로그인 필요 — AUTH_KEY_UNREGISTERED
403권한 없음 — 멤버가 아닌 그룹 등
420FLOOD_WAIT_n — n 초 기다리기
500서버 내부 오류
전송 오류 -404서버가 이 인증 키를 모름 — 새 키를 만들어 다시 로그인(영구 키) 또는 다시 바인딩(임시 키)

아직 없는 것

  • 채널·슈퍼그룹(channels.* 는 답하지만 앱에 제대로 보이지 않음), 비밀 대화, 통화
  • 사진 외 미디어, upload.saveBigFilePart, 썸네일
  • 2단계 인증(SRP) — 설정할 수 없고, 2단계 인증이 필요한 동작은 거절됩니다
  • HTTP·WebSocket 전송, MTProxy, msgs_ack 보내기, bad_server_salt 교체
  • 여러 서버로 나누기 — 서버 한 대가 DC 1~5 를 모두 맡습니다

공식 텔레그램 앱을 파발에 붙이려면

텔레그램 앱은 서버 주소와 공개키를 빌드할 때 몸속에 넣습니다. 그래서 설정 화면이 아니라 소스를 고쳐 다시 빌드해야 합니다. 파발 앱(Pabal.app)이 그렇게 만든 것입니다. 서버 운영자라면 서버 설치 — 앱 연결을 보세요.