WhatsApp (Web 채널)
상태: WhatsApp Web(Baileys) 기반 프로덕션 레벨. 게이트웨이가 연결 세션을 소유합니다.
빠른 설정
1단계: WhatsApp 접근 정책 설정
{
channels: {
whatsapp: {
dmPolicy: "pairing",
allowFrom: ["+15551234567"],
groupPolicy: "allowlist",
groupAllowFrom: ["+15551234567"],
},
},
}
2단계: WhatsApp 연결 (QR)
openclaw channels login --channel whatsapp
특정 계정의 경우:
openclaw channels login --channel whatsapp --account work
3단계: 게이트웨이 시작
openclaw gateway
4단계: 첫 페어링 요청 승인 (페어링 모드 사용 시)
openclaw pairing list whatsapp
openclaw pairing approve whatsapp <CODE>
페어링 요청은 1시간 후 만료됩니다. 채널당 대기 중인 요청은 최대 3개로 제한됩니다.
참고: WhatsApp은 가능하면 별도의 번호로 운영하는 것을 권장합니다. (채널 메타데이터와 온보딩 흐름이 해당 설정에 최적화되어 있지만, 개인 번호 설정도 지원됩니다.)
배포 패턴
전용 번호 (권장)
가장 깔끔한 운영 모드입니다:
- OpenClaw 전용 WhatsApp 계정 사용
- DM 허용 목록과 라우팅 경계가 명확함
- 셀프 채팅 혼동 가능성이 낮음
최소 정책 패턴:
```json5
{
channels: {
whatsapp: {
dmPolicy: "allowlist",
allowFrom: ["+15551234567"],
},
},
}
```
개인 번호 폴백
온보딩은 개인 번호 모드를 지원하며 셀프 채팅에 적합한 기본 설정을 기록합니다:
- `dmPolicy: "allowlist"`
- `allowFrom`에 개인 번호 포함
- `selfChatMode: true`
런타임에서 셀프 채팅 보호는 연결된 자기 번호와 `allowFrom`을 기반으로 동작합니다.
WhatsApp Web 전용 채널 범위
현재 OpenClaw 채널 아키텍처에서 메시징 플랫폼 채널은 WhatsApp Web 기반(`Baileys`)입니다.
내장 채팅 채널 레지스트리에는 별도의 Twilio WhatsApp 메시징 채널이 없습니다.
런타임 모델
- 게이트웨이가 WhatsApp 소켓과 재연결 루프를 소유합니다.
- 아웃바운드 전송에는 대상 계정의 활성 WhatsApp 리스너가 필요합니다.
- 상태 및 브로드캐스트 채팅은 무시됩니다 (
@status,@broadcast). - 다이렉트 채팅은 DM 세션 규칙을 사용합니다 (
session.dmScope, 기본값main은 DM을 에이전트 메인 세션으로 통합). - 그룹 세션은 격리됩니다 (
agent:<agentId>:whatsapp:group:<jid>).
접근 제어 및 활성화
DM 정책
`channels.whatsapp.dmPolicy`로 다이렉트 채팅 접근을 제어합니다:
- `pairing` (기본값)
- `allowlist`
- `open` (`allowFrom`에 `"*"` 필요)
- `disabled`
`allowFrom`은 E.164 형식 번호를 허용합니다 (내부적으로 정규화됨).
멀티 계정 재정의: `channels.whatsapp.accounts.<id>.dmPolicy` (및 `allowFrom`)가 해당 계정의 채널 수준 기본값보다 우선합니다.
런타임 동작 상세:
- 페어링은 채널 허용 저장소에 유지되며 설정된 `allowFrom`과 병합됩니다
- 허용 목록이 설정되지 않은 경우 연결된 자기 번호가 기본으로 허용됩니다
- 아웃바운드 `fromMe` DM은 자동 페어링되지 않습니다
그룹 정책 + 허용 목록
그룹 접근에는 두 가지 계층이 있습니다:
1. **그룹 멤버십 허용 목록** (`channels.whatsapp.groups`)
- `groups`를 생략하면 모든 그룹이 대상
- `groups`가 있으면 그룹 허용 목록으로 동작 (`"*"` 허용)
2. **그룹 발신자 정책** (`channels.whatsapp.groupPolicy` + `groupAllowFrom`)
- `open`: 발신자 허용 목록 우회
- `allowlist`: 발신자가 `groupAllowFrom`과 일치해야 함 (또는 `*`)
- `disabled`: 모든 그룹 인바운드 차단
발신자 허용 목록 폴백:
- `groupAllowFrom`이 미설정이면 런타임에서 `allowFrom`을 폴백으로 사용
- 발신자 허용 목록은 멘션/답장 활성화 전에 평가됨
참고: `channels.whatsapp` 블록이 전혀 없는 경우, `channels.defaults.groupPolicy`가 설정되어 있어도 런타임 그룹 정책 폴백은 `allowlist`입니다 (경고 로그 포함).
멘션 + 활성화
그룹 응답은 기본적으로 멘션이 필요합니다.
멘션 감지 대상:
- 봇 ID에 대한 명시적 WhatsApp 멘션
- 설정된 멘션 정규식 패턴 (`agents.list[].groupChat.mentionPatterns`, 폴백 `messages.groupChat.mentionPatterns`)
- 암묵적 봇 답장 감지 (답장 발신자가 봇 ID와 일치)
보안 참고:
- 인용/답장은 멘션 게이팅만 충족하며 발신자 인가를 부여하지 **않습니다**
- `groupPolicy: "allowlist"`에서는 허용 목록에 없는 발신자가 허용된 사용자의 메시지에 답장해도 차단됩니다
세션 수준 활성화 명령:
- `/activation mention`
- `/activation always`
`activation`은 세션 상태를 업데이트합니다 (전역 설정이 아님). 소유자 게이팅이 적용됩니다.
개인 번호 및 셀프 채팅 동작
연결된 자기 번호가 allowFrom에도 포함되어 있으면 WhatsApp 셀프 채팅 보호가 활성화됩니다:
- 셀프 채팅 턴에서 읽음 확인 건너뛰기
- 자기 자신에게 핑하는 멘션-JID 자동 트리거 동작 무시
messages.responsePrefix가 미설정이면 셀프 채팅 응답은 기본적으로[{identity.name}]또는[openclaw]로 설정
메시지 정규화 및 컨텍스트
인바운드 봉투 + 답장 컨텍스트
수신 WhatsApp 메시지는 공유 인바운드 봉투로 래핑됩니다.
인용 답장이 있는 경우 컨텍스트가 다음 형식으로 추가됩니다:
```text
[Replying to <sender> id:<stanzaId>]
<quoted body or media placeholder>
[/Replying]
```
답장 메타데이터 필드도 가능한 경우 채워집니다 (`ReplyToId`, `ReplyToBody`, `ReplyToSender`, sender JID/E.164).
미디어 플레이스홀더 및 위치/연락처 추출
미디어 전용 인바운드 메시지는 다음과 같은 플레이스홀더로 정규화됩니다:
- `<media:image>`
- `<media:video>`
- `<media:audio>`
- `<media:document>`
- `<media:sticker>`
위치 및 연락처 페이로드는 라우팅 전에 텍스트 컨텍스트로 정규화됩니다.
대기 중인 그룹 히스토리 주입
그룹의 경우 미처리 메시지가 버퍼링되어 봇이 트리거될 때 컨텍스트로 주입될 수 있습니다.
- 기본 제한: `50`
- 설정: `channels.whatsapp.historyLimit`
- 폴백: `messages.groupChat.historyLimit`
- `0`으로 비활성화
주입 마커:
- `[Chat messages since your last reply - for context]`
- `[Current message - respond to this]`
읽음 확인
수락된 인바운드 WhatsApp 메시지에 대해 읽음 확인이 기본으로 활성화됩니다.
전역 비활성화:
```json5
{
channels: {
whatsapp: {
sendReadReceipts: false,
},
},
}
```
계정별 재정의:
```json5
{
channels: {
whatsapp: {
accounts: {
work: {
sendReadReceipts: false,
},
},
},
},
}
```
셀프 채팅 턴은 전역 활성화 상태에서도 읽음 확인을 건너뜁니다.
전달, 청킹, 미디어
텍스트 청킹
- 기본 청크 제한: `channels.whatsapp.textChunkLimit = 4000`
- `channels.whatsapp.chunkMode = "length" | "newline"`
- `newline` 모드는 단락 경계(빈 줄)를 우선하며, 길이 기반 청킹으로 폴백
아웃바운드 미디어 동작
- 이미지, 동영상, 오디오 (PTT 음성 메모), 문서 페이로드 지원
- `audio/ogg`는 음성 메모 호환을 위해 `audio/ogg; codecs=opus`로 재작성됨
- 애니메이션 GIF 재생은 동영상 전송 시 `gifPlayback: true`로 지원
- 캡션은 멀티 미디어 응답 페이로드 전송 시 첫 번째 미디어 항목에 적용
- 미디어 소스는 HTTP(S), `file://`, 또는 로컬 경로 가능
미디어 크기 제한 및 폴백 동작
- 인바운드 미디어 저장 상한: `channels.whatsapp.mediaMaxMb` (기본 `50`)
- 아웃바운드 미디어 전송 상한: `channels.whatsapp.mediaMaxMb` (기본 `50`)
- 계정별 재정의: `channels.whatsapp.accounts.<accountId>.mediaMaxMb`
- 이미지는 제한에 맞도록 자동 최적화 (리사이즈/품질 조정)
- 미디어 전송 실패 시 응답을 무시하지 않고 첫 번째 항목 폴백으로 텍스트 경고 전송
수신 확인 리액션
WhatsApp은 channels.whatsapp.ackReaction을 통해 인바운드 수신 시 즉각적인 확인 리액션을 지원합니다.
{
channels: {
whatsapp: {
ackReaction: {
emoji: "👀",
direct: true,
group: "mentions", // always | mentions | never
},
},
},
}
동작 참고:
- 인바운드 수락 직후 전송 (응답 전)
- 실패 시 로깅되지만 정상 응답 전달을 차단하지 않음
- 그룹 모드
mentions는 멘션으로 트리거된 턴에 리액션하며, 그룹 활성화always는 이 검사의 우회 역할 - WhatsApp은
channels.whatsapp.ackReaction을 사용 (레거시messages.ackReaction은 여기서 사용되지 않음)
멀티 계정 및 자격 증명
계정 선택 및 기본값
- 계정 ID는 `channels.whatsapp.accounts`에서 가져옴
- 기본 계정 선택: `default`가 있으면 사용, 아니면 첫 번째 설정된 계정 ID (정렬 순)
- 계정 ID는 조회를 위해 내부적으로 정규화됨
자격 증명 경로 및 레거시 호환성
- 현재 인증 경로: `~/.openclaw/credentials/whatsapp/<accountId>/creds.json`
- 백업 파일: `creds.json.bak`
- `~/.openclaw/credentials/`의 레거시 기본 인증은 기본 계정 흐름에서 여전히 인식/마이그레이션됨
로그아웃 동작
`openclaw channels logout --channel whatsapp [--account <id>]`는 해당 계정의 WhatsApp 인증 상태를 초기화합니다.
레거시 인증 디렉토리에서는 `oauth.json`이 보존되고 Baileys 인증 파일은 제거됩니다.
도구, 액션, 설정 쓰기
- 에이전트 도구 지원에는 WhatsApp 리액션 액션 (
react)이 포함됩니다. - 액션 게이트:
channels.whatsapp.actions.reactionschannels.whatsapp.actions.polls
- 채널에서 시작된 설정 쓰기는 기본으로 활성화됩니다 (
channels.whatsapp.configWrites=false로 비활성화).
문제 해결
미연결 (QR 필요)
증상: 채널 상태가 미연결로 표시.
해결:
```bash
openclaw channels login --channel whatsapp
openclaw channels status
```
연결됨 하지만 연결 끊김 / 재연결 반복
증상: 연결된 계정이 반복적으로 연결 끊김 또는 재연결을 시도.
해결:
```bash
openclaw doctor
openclaw logs --follow
```
필요 시 `channels login`으로 재연결합니다.
전송 시 활성 리스너 없음
대상 계정에 활성 게이트웨이 리스너가 없으면 아웃바운드 전송이 즉시 실패합니다.
게이트웨이가 실행 중이고 계정이 연결되어 있는지 확인합니다.
그룹 메시지가 예상치 못하게 무시됨
다음 순서로 확인합니다:
- `groupPolicy`
- `groupAllowFrom` / `allowFrom`
- `groups` 허용 목록 항목
- 멘션 게이팅 (`requireMention` + 멘션 패턴)
- `openclaw.json`(JSON5)의 중복 키: 나중의 항목이 이전 항목을 덮어쓰므로 범위당 단일 `groupPolicy`를 유지
Bun 런타임 경고
WhatsApp 게이트웨이 런타임은 Node를 사용해야 합니다. Bun은 안정적인 WhatsApp/Telegram 게이트웨이 운영에 비호환으로 표시됩니다.
설정 참조 포인터
기본 참조:
주요 WhatsApp 필드:
- 접근:
dmPolicy,allowFrom,groupPolicy,groupAllowFrom,groups - 전달:
textChunkLimit,chunkMode,mediaMaxMb,sendReadReceipts,ackReaction - 멀티 계정:
accounts.<id>.enabled,accounts.<id>.authDir, 계정 수준 재정의 - 운영:
configWrites,debounceMs,web.enabled,web.heartbeatSeconds,web.reconnect.* - 세션 동작:
session.dmScope,historyLimit,dmHistoryLimit,dms.<id>.historyLimit