WhatsApp (Cloud API 및 Web)
WhatsApp Cloud API 웹훅 모드와 네이티브 WhatsApp Web 모드, 그리고 Revka가 백엔드를 선택하는 방법을 설명합니다.
Revka는 WhatsApp과 두 가지 완전히 다른 방식으로 통신하며, 두 방식 모두 ~/.revka/config.toml의 동일한 [channels_config.whatsapp] 섹션에서 설정합니다. Cloud API 모드는 Meta의 공식 WhatsApp Business Cloud API로, 메시지가 공개 HTTPS 웹훅을 통해 수신되고 답신은 Meta의 Graph API를 통해 전송됩니다. Web 모드는 네이티브 WhatsApp Web 클라이언트(wa-rs 라이브러리)로, WhatsApp Web 앱과 동일한 방식으로 전화번호에 연결됩니다. Meta Business 계정이나 공개 포트가 필요 없으며, Signal 프로토콜을 통한 종단 간 암호화를 지원합니다. 이 페이지에서는 두 모드와 WATI 호스팅 대안, 그리고 Revka가 어떤 백엔드를 실행할지 결정하는 규칙을 설명합니다.
모드는 설정으로 선택됩니다. Cloud API는 phone_number_id를, Web은 session_path를 설정하면 됩니다. 플래그로 모드를 지정하는 것이 아니라, Revka가 시작 시 어느 키가 설정되어 있는지를 보고 모드를 결정합니다. 처음 채널을 연결하는 경우 허용 목록 모델에 대한 설명은 메시지 채널 연결을, 공유 트레이트 모델은 채널 개요를 참고하세요.
Revka의 모드 선택 방식
섹션 제목: “Revka의 모드 선택 방식”데몬 시작 시 WhatsApp 설정을 검사하여 백엔드를 선택합니다.
| 조건 | 선택되는 백엔드 |
|---|---|
phone_number_id가 설정된 경우 | Cloud API 모드 (WhatsAppChannel) |
session_path가 설정된 경우 (단, phone_number_id 없음) | Web 모드 (WhatsAppWebChannel) |
| 둘 다 설정된 경우 | Cloud API 모드가 우선 선택되며, 하나를 제거하라는 시작 경고가 기록됩니다 |
| 둘 다 설정되지 않은 경우 | 채널을 건너뛰며 설정 오류 경고가 기록됩니다 |
# Cloud API 모드 — phone_number_id가 선택자[channels_config.whatsapp]phone_number_id = "123456789012345"
# Web 모드 — session_path가 선택자[channels_config.whatsapp]session_path = "~/.revka/whatsapp-session.db"WhatsApp Cloud API (웹훅 모드)
섹션 제목: “WhatsApp Cloud API (웹훅 모드)”Cloud API 모드는 푸시 기반입니다. Meta가 인바운드 메시지를 게이트웨이로 전달하므로 공개 HTTPS 콜백 URL이 필요합니다. 내부적으로 채널의 listen() 메서드는 빈 플레이스홀더이며, 실제 인그레스는 게이트웨이의 /whatsapp 웹훅입니다. 아웃바운드 응답은 https://graph.facebook.com/v18.0/{phone_number_id}/messages로 전송됩니다.
이 모드는 Meta Business 계정과 인증된 WhatsApp Business 전화번호를 보유하고 있으며, 게이트웨이를 공개적으로 노출할 수 있는 경우(직접 또는 터널 경유)에 사용하세요.
[channels_config.whatsapp]access_token = "EAAB..." # 필수, Meta Business Suite에서 발급phone_number_id = "123456789012345" # 필수 — Cloud API 모드 선택자verify_token = "your-verify-token" # 필수 — 직접 정의하며, Meta가 그대로 반환합니다app_secret = "your-app-secret" # 권장 — HMAC 페이로드 검증에 사용allowed_numbers = ["*"] # 테스트 후 범위를 좁히세요# dm_mention_patterns = [] # 선택 사항 — DM 정규식 필터링# group_mention_patterns = [] # 선택 사항 — 그룹 채팅 정규식 필터링# proxy_url = "socks5://127.0.0.1:9050"| 필드 | 타입 / 값 | 기본값 | 설명 |
|---|---|---|---|
access_token | 문자열 (필수) | — | Meta Business Suite의 Cloud API 액세스 토큰. 시작 시 HTTPS 전송만 허용하도록 검증됩니다. |
phone_number_id | 문자열 (필수) | — | Meta Business API의 전화번호 ID. 이 값이 존재하면 Cloud API 모드가 선택됩니다. |
verify_token | 문자열 (필수) | — | 직접 정의하는 토큰. Meta가 웹훅 인증 핸드셰이크 시 반환하며, Revka가 상수 시간 비교로 검증합니다. |
app_secret | 문자열 | — | 인바운드 페이로드의 X-Hub-Signature-256 HMAC 검증에 사용하는 Meta 앱 시크릿. 프로덕션 환경에서는 강력히 권장됩니다. |
allowed_numbers | 목록 | [] (전체 차단) | E.164 형식 전화번호(+15551234567) 또는 "*". |
dm_mention_patterns | 정규식 목록 | [] | 대소문자 구분 없는 패턴. 값이 있을 경우 일치하는 DM만 처리되며 일치 부분은 제거됩니다. ReDoS 보호가 적용됩니다(컴파일된 크기 64 KiB 제한). |
group_mention_patterns | 정규식 목록 | [] | 위와 동일하며 그룹 채팅에 적용됩니다. |
proxy_url | 문자열 | — | 전역 [proxy] 설정을 재정의하는 채널별 HTTP/HTTPS/SOCKS5 프록시. |
웹훅 엔드포인트
섹션 제목: “웹훅 엔드포인트”게이트웨이는 Meta 웹훅을 두 개의 라우트로 노출합니다. Meta 앱의 콜백 URL을 https://<your-gateway>/whatsapp으로 지정하세요.
GET /whatsapp # Meta 허브 인증 핸드셰이크POST /whatsapp # 인바운드 메시지 전달인증 (GET). Meta가 hub.mode, hub.verify_token, hub.challenge 쿼리 파라미터를 포함하여 GET /whatsapp을 호출합니다. hub.mode=subscribe이고 토큰이 설정된 verify_token과 일치하면(상수 시간 비교), 게이트웨이가 hub.challenge 값을 반환하여 구독을 완료합니다.
GET /whatsapp?hub.mode=subscribe&hub.verify_token=your-verify-token&hub.challenge=1234567890→ 200 OK1234567890메시지 전달 (POST). Meta가 표준 WhatsApp Cloud API 페이로드를 POST로 전송합니다. 게이트웨이는 서명을 검증하고(아래 참고), 발신자를 allowed_numbers와 대조하여 필터링한 후 에이전트로 메시지를 라우팅합니다.
앱 시크릿 HMAC 검증
섹션 제목: “앱 시크릿 HMAC 검증”앱 시크릿이 설정된 경우, 게이트웨이는 모든 인바운드 POST를 처리하기 전에 X-Hub-Signature-256 헤더를 검증합니다.
- 헤더 값은
sha256=<hex>형식입니다. - Revka가
HMAC-SHA256(app_secret, raw_request_body)를 계산하고 디코딩된 16진수 값과 상수 시간 비교를 수행합니다. - 헤더가 없거나 형식이 잘못되었거나 서명이 일치하지 않으면 요청이 거부됩니다.
앱 시크릿은 환경 변수가 설정 파일보다 우선 적용됩니다.
# 권장: config.toml에 시크릿을 저장하지 마세요export REVKA_WHATSAPP_APP_SECRET="your-app-secret"REVKA_WHATSAPP_APP_SECRET도 app_secret 설정 키도 설정되어 있지 않으면 서명 검증이 건너뜁니다. 로컬 테스트에서는 허용되지만, 인터넷에 노출되는 배포 환경에서는 앱 시크릿을 반드시 설정하세요.
WhatsApp Web (wa-rs) feature: whatsapp-web
섹션 제목: “WhatsApp Web (wa-rs) ”Web 모드는 네이티브 Rust 기반 WhatsApp Web 클라이언트(wa-rs)를 내장합니다. Baileys와 동등한 기능을 제공하며, QR 코드 또는 페어 코드 연결, Signal 프로토콜을 통한 종단 간 암호화, 그룹, 미디어, 상태 표시, 반응, 메시지 편집/삭제를 지원합니다. WhatsApp Web 브라우저 앱과 동일한 방식으로 전화번호에 연결됩니다. Meta Business 계정과 공개 인바운드 포트가 필요하지 않습니다. 음성 전사 및 TTS 응답은 공유 [transcription]과 [tts] 섹션을 통해 지원됩니다.
Web 모드는 Cargo 기능 플래그로 제어되며 기본 빌드에는 포함되지 않습니다.
cargo build --release --features whatsapp-websession_path가 설정되어 있지만 바이너리가 해당 기능 없이 빌드된 경우, 데몬은 경고를 기록하고 채널을 시작하는 대신 재빌드 안내를 출력합니다.
[channels_config.whatsapp]session_path = "~/.revka/whatsapp-session.db" # 필수 — Web 모드 선택자pair_phone = "15551234567" # 선택 사항 — 페어 코드 연결 활성화pair_code = "" # 선택 사항 — 커스텀 코드; 비워두면 자동 생성allowed_numbers = ["*"] # 테스트 후 범위를 좁히세요mode = "business" # business | personal# dm_policy = "allowlist" # personal 모드 전용# group_policy = "allowlist" # personal 모드 전용# self_chat_mode = false # personal 모드 전용# dm_mention_patterns = []# group_mention_patterns = []# proxy_url = "socks5://127.0.0.1:9050"| 필드 | 타입 / 값 | 기본값 | 설명 |
|---|---|---|---|
session_path | 문자열 (필수) | — | SQLite 세션 저장소 경로. 이 값이 존재하면 Web 모드가 선택됩니다. 영구 저장소에 위치해야 합니다. 파일을 잃으면 재연결이 필요합니다. |
pair_phone | 문자열 | — | 페어 코드 연결에 사용하는 전화번호(국가 코드 + 번호 형식, 예: 15551234567). 생략하면 QR 코드 플로우가 사용됩니다. |
pair_code | 문자열 | — | 커스텀 페어 코드. 비워두면 WhatsApp이 자동으로 생성합니다. |
allowed_numbers | 목록 | [] (전체 차단) | E.164 형식 전화번호 또는 "*". |
mode | "business" | "personal" | "business" | business는 허용 목록을 통과하는 모든 메시지에 응답합니다. personal은 아래 채팅 유형별 정책을 적용합니다. |
dm_policy | "allowlist" | "ignore" | "all" | "allowlist" | mode = "personal"일 때 DM 처리 방식. |
group_policy | "allowlist" | "ignore" | "all" | "allowlist" | mode = "personal"일 때 그룹 채팅 처리 방식. |
self_chat_mode | bool | false | mode = "personal"일 때 자신과의 채팅(셀프 채팅)에서 항상 응답합니다. |
dm_mention_patterns | 정규식 목록 | [] | 대소문자 구분 없는 DM 멘션 필터링. 일치하는 부분은 전달 내용에서 제거됩니다. |
group_mention_patterns | 정규식 목록 | [] | 대소문자 구분 없는 그룹 멘션 필터링. |
proxy_url | 문자열 | — | 채널별 프록시 재정의. |
페어 코드 연결
섹션 제목: “페어 코드 연결”페어 코드 연결을 사용하면 QR 이미지를 스캔하는 대신 WhatsApp 앱에서 짧은 코드를 입력하여 기기를 연결할 수 있습니다. 헤드리스 서버에서 유용합니다.
session_path와pair_phone을 설정합니다(국가 코드 포함 번호 형식,+없음). 필요하면 커스텀pair_code도 설정합니다.- 기능 플래그를 포함하여 빌드하고 데몬을 시작합니다.
cargo build --release --features whatsapp-web후revka daemon을 실행하세요. - 시작 시 클라이언트가
pair_phone에 대한 페어링 코드를 요청합니다. 스마트폰의 WhatsApp에서 설정 → 연결된 기기 → 기기 연결 → 전화번호로 연결을 선택하고 코드를 입력합니다. - 연결이 완료되면 암호화된 세션이
session_path에 저장됩니다. 이후 재시작 시 해당 세션을 재사용하므로 파일이 삭제되지 않는 한 재연결이 필요하지 않습니다.
pair_phone을 생략하면 클라이언트는 QR 코드 플로우로 전환됩니다. 시작 시 표시되는 코드를 WhatsApp 앱의 연결된 기기에서 스캔하세요.
WATI (호스팅 WhatsApp Business API)
섹션 제목: “WATI (호스팅 WhatsApp Business API)”WATI는 별도의 호스팅 WhatsApp Business API 제공업체로, [channels_config.wati] 섹션을 사용하며 whatsapp 선택자 로직과는 무관합니다. Cloud API 모드와 마찬가지로 웹훅 기반이며 공개 HTTPS 엔드포인트가 필요하지만, WATI의 API 형식을 사용합니다. 인바운드는 게이트웨이 /wati 엔드포인트로 수신되고, 아웃바운드 텍스트는 WATI의 conversations 엔드포인트로 전송됩니다. 오디오 전사(최대 25 MB)를 지원하며, 미디어 다운로드는 SSRF 보호가 적용됩니다(미디어 호스트는 설정된 api_url 호스트와 일치해야 합니다).
[channels_config.wati]api_token = "wati-bearer-token" # 필수api_url = "https://live-mt-server.wati.io" # 필수tenant_id = "my-tenant" # 선택 사항 — 수신자 라우팅 접두어allowed_numbers = ["*"] # 테스트 후 범위를 좁히세요# proxy_url = "socks5://127.0.0.1:9050"| 필드 | 타입 / 값 | 기본값 | 설명 |
|---|---|---|---|
api_token | 문자열 (필수) | — | WATI 베어러 토큰. |
api_url | 문자열 (필수) | — | WATI 서버 기본 URL (예: https://live-mt-server.wati.io). |
tenant_id | 문자열 | — | 수신자 대상 지정을 위한 선택적 테넌트 접두어. |
allowed_numbers | 목록 | [] (전체 차단) | E.164 형식 전화번호 또는 "*". |
proxy_url | 문자열 | — | 채널별 프록시 재정의. |
GET /wati # WATI 엔드포인트 인증 (hub.challenge 반환)POST /wati # 인바운드 메시지 전달 (허용 번호 필터링)이미 WATI 플랫폼을 통해 WhatsApp을 운영 중이고 Meta Cloud API에 직접 연결하는 대신 Revka를 활용하고 싶을 때 WATI를 선택하세요.
모드 선택 가이드
섹션 제목: “모드 선택 가이드”| Cloud API | WhatsApp Web (wa-rs) | WATI | |
|---|---|---|---|
| 설정 선택자 | phone_number_id | session_path | [channels_config.wati] |
| 수신 방식 | 웹훅 (푸시) | WebSocket (네이티브 클라이언트) | 웹훅 (푸시) |
| 공개 인바운드 포트 | 필수 | 불필요 | 필수 |
| Meta Business 계정 | 필수 | 불필요 | WATI 경유 |
| 연결 방식 | 해당 없음 | QR 또는 페어 코드 | 해당 없음 |
| 암호화 | TLS (전송 중) | E2EE (Signal 프로토콜) | TLS (전송 중) |
| 기능 플래그 | 없음 (기본 빌드) | whatsapp-web | 없음 (기본 빌드) |
| 페이로드 인증 | app_secret HMAC | 세션 키 | WATI 토큰 |
- Business 계정을 보유하고 HTTPS 웹훅을 노출할 수 있는 경우 공식 지원 통합인 Cloud API를 선택하세요.
- Meta 계정과 공개 포트 없이 기존 개인 또는 비즈니스 번호를 연결하려면 Web 모드를 선택하세요. 단,
whatsapp-web기능을 빌드하고 세션 데이터베이스를 안전하게 관리해야 합니다. - WhatsApp 번호가 이미 WATI를 통해 관리되고 있다면 WATI를 선택하세요.
검증 및 문제 해결
섹션 제목: “검증 및 문제 해결”revka doctor # 채널 설정과 연결 상태를 확인합니다revka status # 설정된 채널과 상태를 표시합니다- Cloud API 웹훅 인증 실패 — 콜백 URL이
https://<gateway>/whatsapp인지 확인하고,verify_token이 Meta 앱에 입력한 값과 정확히 일치하는지, 그리고 게이트웨이가 HTTPS로 접근 가능한지 확인하세요. - 인바운드 POST가 거부됨 — 앱 시크릿을 설정한 경우, 프록시가 Meta의
X-Hub-Signature-256을 변경 없이 그대로 전달하는지 확인하세요. HMAC은 원본 본문을 기준으로 계산됩니다. - Web 모드가 시작되지 않음 — 바이너리를
--features whatsapp-web으로 빌드해야 합니다. 데몬 로그에서 재빌드 안내를 확인하세요. - 발신자 메시지가 무시됨 —
allowed_numbers가 기본적으로 비어 있어 전체 차단 상태입니다. E.164 번호 또는"*"를 추가하세요.