파발 API
파발 API (MTProto)
앱과 같은 방식으로 서버에 붙는 클라이언트를 만들 때: 접속 정보, 서버 공개키, 인증 흐름, 업데이트, 지원 범위.
파발 앱은 서버와 MTProto 2.0 으로 이야기합니다. 같은 방식으로 붙는 프로그램 — 다른 앱, 사람 계정으로 도는 자동화, 연구용 클라이언트 — 을 만들 때 이 문서를 보세요. 봇을 만든다면 더 간단한 Bot API 로 충분합니다.
파발은 텔레그램의 공개 프로토콜을 따르므로, 프로토콜과 메서드의 자세한 정의는 MTProto 와 API 메서드 문서가 기준입니다. 이 문서는 파발 서버에 붙을 때 다른 점과 지원 범위를 적습니다.
접속 정보
| 항목 | 값 |
|---|---|
| 주소 | 122.34.175.215 |
| 포트 | 8443 (TCP) |
| DC | 1~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파일에 저장되어 다음부터는 코드를 묻지 않습니다. 이 파일은 계정의 열쇠이니 지키세요.
로그인 흐름
다이어그램 설명
- 두 단계: 위는 암호화에 쓸 인증 키를 만드는 단계(프로토콜), 아래는 그 키에 계정을 붙이는 로그인 단계(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.sendMedia에inputMediaUploadedPhoto(새 사진) 또는inputMediaPhoto(서버에 있는 사진). 다른 미디어는MEDIA_INVALID. - 받기:
upload.getFile에inputPhotoFileLocation(메시지 사진),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 | 권한 없음 — 멤버가 아닌 그룹 등 |
420 | FLOOD_WAIT_n — n 초 기다리기 |
500 | 서버 내부 오류 |
전송 오류 -404 | 서버가 이 인증 키를 모름 — 새 키를 만들어 다시 로그인(영구 키) 또는 다시 바인딩(임시 키) |
아직 없는 것
- 채널·슈퍼그룹(
channels.*는 답하지만 앱에 제대로 보이지 않음), 비밀 대화, 통화 - 사진 외 미디어,
upload.saveBigFilePart, 썸네일 - 2단계 인증(SRP) — 설정할 수 없고, 2단계 인증이 필요한 동작은 거절됩니다
- HTTP·WebSocket 전송, MTProxy,
msgs_ack보내기,bad_server_salt교체 - 여러 서버로 나누기 — 서버 한 대가 DC 1~5 를 모두 맡습니다
공식 텔레그램 앱을 파발에 붙이려면
텔레그램 앱은 서버 주소와 공개키를 빌드할 때 몸속에 넣습니다. 그래서 설정 화면이 아니라 소스를 고쳐 다시 빌드해야 합니다. 파발 앱(Pabal.app)이 그렇게 만든 것입니다. 서버 운영자라면 서버 설치 — 앱 연결을 보세요.