Lark, Feishu, DingTalk, WeCom, QQ, Mochat
아시아권 기업용 및 소비자용 메시징 플랫폼 — 지역별 라우팅 및 기능 플래그 안내 포함.
이 페이지는 Revka의 아시아권 기업용·소비자용 메시징 채널에 대한 레퍼런스입니다. 다루는 채널은 Lark(해외) 및 Feishu(중국), DingTalk, WeCom(기업용 WeChat), QQ 공식 봇, 그리고 Mochat입니다. 정확한 설정 키, 지역 라우팅, 수신 모드 동작 방식, Lark/Feishu를 활성화하는 빌드 플래그(channel-lark)가 필요할 때 이 페이지를 참고하세요. 각 플랫폼 개발자 콘솔에서 봇을 생성하는 단계별 절차는 해당 플랫폼의 공식 온보딩 문서를 따른 다음, 여기서 자격 증명을 설정하세요.
이 모든 채널은 ~/.revka/config.toml의 [channels_config] 아래에서 설정합니다. 파일을 직접 편집하거나 revka onboard --channels-only를 실행한 뒤 데몬을 재시작하여 변경 사항을 적용하세요. 공통 채널 트레이트, 전달 모드, 허용 목록 모델에 대해서는 채널 개요를 참고하세요.
한눈에 보기
섹션 제목: “한눈에 보기”| 채널 | 지역 | 수신 모드 | 공개 인바운드 포트 필요 여부 | 빌드 기능 플래그 |
|---|---|---|---|---|
| Lark / Feishu | 해외 / 중국 | WebSocket(기본) 또는 웹훅 | 웹훅 모드만 필요 | channel-lark |
| DingTalk | 중국 | Stream Mode WebSocket | 불필요 | — |
| WeCom | 중국 | 발신 전용(Bot Webhook) | 불필요 | — |
| QQ 공식 봇 | 중국 | 봇 게이트웨이 WebSocket | 불필요 | — |
| Mochat | 자체 호스팅 | HTTP 폴링 | 불필요 | — |
Lark/Feishu만 빌드 시 기능 플래그로 제어됩니다. 나머지 네 채널은 항상 컴파일에 포함됩니다. WebSocket 모드의 Lark/Feishu, DingTalk, QQ, Mochat은 모두 플랫폼으로 외부 연결을 맺으므로 인바운드 포트가 필요 없습니다. 웹훅 모드의 Lark/Feishu만 로컬 HTTP 리스너를 엽니다.
Lark / Feishu
섹션 제목: “Lark / Feishu”Lark와 Feishu는 두 개의 지역 엔드포인트를 사용하는 동일한 제품입니다. Revka는 이를 플랫폼 스위치가 있는 단일 채널 구현으로 모델링합니다.
- **Lark(해외)**는
open.larksuite.com으로 라우팅됩니다. - **Feishu(중국)**는
open.feishu.cn으로 라우팅됩니다.
이 채널은 두 가지 수신 모드(WebSocket vs 웹훅 수신 모드 참고), 로케일 인식 수신 확인 반응, 타이핑 표시기, 오디오 전사(25 MB 제한), 이미지 및 파일 수신, 자동 tenant_access_token 갱신을 지원합니다.
Lark(해외) 설정
섹션 제목: “Lark(해외) 설정”[channels_config.lark]app_id = "cli_xxx"app_secret = "xxx"encrypt_key = "" # optional: AES key for webhook event decryptionverification_token = "" # optional: webhook authenticity checkallowed_users = ["*"]mention_only = falsereceive_mode = "websocket" # websocket | webhookport = 8081 # required for webhook mode onlyuse_feishu = false # legacy: routes this section to Feishu endpointsFeishu 설정(중국에서 권장)
섹션 제목: “Feishu 설정(중국에서 권장)”[channels_config.feishu]app_id = "cli_xxx"app_secret = "xxx"encrypt_key = "" # optionalverification_token = "" # optionalallowed_users = ["*"]receive_mode = "websocket"| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
app_id | string | — | Lark/Feishu 개발자 콘솔의 앱 ID. 필수. |
app_secret | string | — | 개발자 콘솔의 앱 시크릿. 필수. |
encrypt_key | string | — | 암호화된 웹훅 이벤트 페이로드를 복호화하는 데 사용하는 AES 키. 선택 사항; 웹훅 모드 전용. |
verification_token | string | — | 웹훅 URL 인증 챌린지의 token 필드와 대조하는 토큰. 선택 사항; 웹훅 모드 전용. |
allowed_users | array | [] | 상호작용이 허용된 Open ID(또는 Union ID). []는 전체 차단; ["*"]는 전체 허용. |
mention_only | bool | false | 그룹 채팅에서 봇이 @ 멘션될 때만 응답. 다이렉트 메시지는 항상 처리됨. |
receive_mode | string | "websocket" | "websocket"(장기 연결, 인바운드 포트 불필요) 또는 "webhook"(HTTP 콜백, 공개 URL 필요). |
port | number | — | 로컬 HTTP 리스너 포트. receive_mode = "webhook"일 때 필수; WebSocket 모드에서는 무시됨. |
use_feishu | bool | false | Lark 섹션 전용. 레거시 스위치: true로 설정하면 [channels_config.lark]를 Feishu 엔드포인트로 라우팅. 전용 [channels_config.feishu] 섹션 사용을 권장. |
proxy_url | string | — | 채널별 선택적 HTTP/SOCKS5 프록시. |
transcription.* | table | — | 오디오 메시지 음성-텍스트 변환(25 MB 제한). Voice, TTS & 전사 참고. |
Lark vs Feishu: 어떤 섹션을 사용할까
섹션 제목: “Lark vs Feishu: 어떤 섹션을 사용할까”Feishu(중국) 엔드포인트에 접근하는 방법은 두 가지이며, Revka는 정해진 우선순위에 따라 처리합니다.
- 권장 방법: 전용
[channels_config.feishu]섹션을 추가합니다. 이 섹션은 항상 Feishu 엔드포인트를 대상으로 하며 Feishu로 등록됩니다. - 레거시 방법:
[channels_config.lark]아래에use_feishu = true를 설정합니다. 아직 지원되지만 권장하지 않습니다. 시작 시 Revka는Using legacy [channels_config.lark].use_feishu=true compatibility path; prefer [channels_config.feishu]를 로그에 기록합니다.
[channels_config.feishu] 섹션과 Lark 섹션의 use_feishu = true를 동시에 설정하면, 레거시 Lark 측 Feishu 폴백은 무시되고(Revka가 경고를 기록), 전용 Feishu 섹션이 우선합니다. use_feishu = false인 일반 [channels_config.lark] 섹션은 Lark로 등록되어 해외 엔드포인트를 대상으로 합니다.
수신 및 응답
섹션 제목: “수신 및 응답”인바운드 텍스트, 리치 텍스트(post), 이미지, 파일, 오디오 메시지가 디코딩됩니다. 이미지는 base64 데이터 마커로 인라인 처리됩니다(5 MiB 상한, PNG/JPEG/GIF/WebP/BMP 지원). 텍스트형 파일은 인라인 처리(512 KiB 상한)되고, 그보다 크거나 바이너리 파일은 첨부 파일 요약으로 변환됩니다. [transcription] 섹션이 설정된 경우 오디오는 전사됩니다. 수락된 각 메시지마다 봇은 로케일 인식 수신 확인 반응을 추가합니다(zh-CN, zh-TW, en, ja에 대해 각각 다른 이모지 세트 사용).
응답은 인터랙티브 카드 JSON 2.0 마크다운 형식으로 전송되므로 제목, 표, 인용문, 인라인 코드가 렌더링됩니다. 긴 응답은 카드 크기 제한에 맞도록 줄 경계에서 분할됩니다. 봇은 tenant_access_token을 캐시하고 자동으로 갱신합니다. 만료된 토큰 응답(비즈니스 코드 99991663) 또는 401이 반환되면 토큰을 무효화하고 한 번 재시도합니다.
DingTalk
섹션 제목: “DingTalk”DingTalk는 Stream Mode로 동작합니다. Revka는 client_id와 client_secret을 사용해 DingTalk 게이트웨이에 연결을 등록하고, WebSocket 엔드포인트와 티켓을 수신한 뒤 해당 장기 연결을 통해 챗봇 콜백을 수신합니다. 인바운드 포트가 필요 없습니다.
DingTalk에서의 응답 처리 방식은 특수합니다. 전역 전송 API가 없으며, 각 수신 이벤트에는 메시지별 세션 웹훅 URL이 포함됩니다. Revka는 이를 채팅(및 발신자) 단위로 캐시하여 마크다운 메시지 형태로 응답을 전송할 때 재사용합니다. 따라서 봇은 사용자가 먼저 메시지를 보낸 이후에만 해당 대화에 응답할 수 있습니다. 세션 웹훅이 존재하기 전에 전송을 시도하면 No session webhook found for chat <id>. The user must send a message first to establish a session. 오류가 발생합니다.
DingTalk 설정
섹션 제목: “DingTalk 설정”[channels_config.dingtalk]client_id = "ding-app-key"client_secret = "ding-app-secret"allowed_users = ["*"]proxy_url = ""| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
client_id | string | — | DingTalk 개발자 콘솔의 앱 키. 필수. |
client_secret | string | — | 개발자 콘솔의 앱 시크릿. 필수. |
allowed_users | array | [] | 상호작용이 허용된 DingTalk 직원 ID(senderStaffId). []는 전체 차단; ["*"]는 전체 허용. |
proxy_url | string | — | 채널별 선택적 HTTP/SOCKS5 프록시. |
1:1 프라이빗 대화와 그룹 대화를 모두 지원합니다. 프라이빗 채팅은 발신자의 직원 ID로 식별되고, 그룹 채팅은 conversationId로 식별됩니다. 채널은 연결을 유지하기 위해 게이트웨이 SYSTEM 핑에 응답하며, 각 이벤트를 에이전트에 디스패치하기 전에 수신 확인합니다.
WeCom(기업용 WeChat)
섹션 제목: “WeCom(기업용 WeChat)”WeCom은 여기서 Bot Webhook으로 설정되며, 발신 전용입니다. Revka는 https://qyapi.weixin.qq.com/cgi-bin/webhook/send?key=<webhook_key>로 텍스트 메시지를 전송하고 errcode: 0을 성공으로 처리합니다. listen() 측은 연결 유지용 no-op으로, 이 채널을 통해 메시지를 수신하지 않습니다.
WeCom 설정
섹션 제목: “WeCom 설정”[channels_config.wecom]webhook_key = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"allowed_users = ["*"]| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
webhook_key | string | — | WeCom 그룹 봇 웹훅 URL의 key 값. 필수. |
allowed_users | array | [] | 향후 인바운드 지원을 위해 예약됨. 발신 전용인 현재는 사용되지 않음. |
헬스 체크는 웹훅에 소규모 health_check 텍스트 메시지를 전송합니다. 따라서 헬스 체크 통과 시 봇의 채팅에 실제(최소) 메시지가 전송됩니다.
QQ 공식 봇
섹션 제목: “QQ 공식 봇”QQ 공식 봇은 Discord와 유사한 WebSocket 프로토콜인 텐센트의 봇 게이트웨이를 사용합니다. Revka는 app_id와 app_secret으로 getAppAccessToken에 인증하고, 게이트웨이 URL을 가져와 C2C(다이렉트) 및 그룹 @-메시지 인텐트로 식별합니다. 마지막 세션 ID와 시퀀스 번호를 사용한 게이트웨이 재개(opcode 6)를 지원하며, 게이트웨이 Hello(opcode 10) 하트비트를 수신 확인합니다. 인바운드 포트가 필요 없습니다.
QQ 설정
섹션 제목: “QQ 설정”[channels_config.qq]app_id = "qq-app-id"app_secret = "qq-app-secret"allowed_users = ["*"]proxy_url = ""| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
app_id | string | — | QQ 봇 개발자 콘솔의 앱 ID. 필수. |
app_secret | string | — | 개발자 콘솔의 앱 시크릿. 필수. |
allowed_users | array | [] | 상호작용이 허용된 QQ 사용자 ID(C2C user_openid). []는 전체 차단; ["*"]는 전체 허용. |
proxy_url | string | — | 채널별 선택적 HTTP/SOCKS5 프록시. |
미디어 및 응답 제한
섹션 제목: “미디어 및 응답 제한”발신 미디어는 [IMAGE:...], [VIDEO:...], [VOICE:...], [FILE:...] 마커를 사용하며, TTL 내에 동일한 파일의 재업로드를 방지하는 업로드 캐시(최대 500개 항목)를 포함합니다. 미디어 업로드 최대 크기는 10 MB입니다.
QQ 보이스는 기본적으로 .wav, .mp3, .silk만 지원합니다. 다른 형식을 가리키는 [AUDIO:...]/[VOICE:...] 마커는 재생 가능한 음성 메시지 대신 일반 파일 업로드로 처리됩니다.
Mochat
섹션 제목: “Mochat”Mochat은 오픈소스 Mochat 고객 서비스 플랫폼과 HTTP API를 통해 연동합니다. 폴링 방식이며 푸시 방식은 없습니다. Revka는 고정 간격으로 GET <api_url>/api/message/receive를 폴링하고, 메시지를 중복 제거(10,000개 항목 세트)한 뒤 Bearer 토큰을 사용해 POST <api_url>/api/message/send로 응답합니다.
Mochat 설정
섹션 제목: “Mochat 설정”[channels_config.mochat]api_url = "https://mochat.example.com"api_token = "mochat-api-token"allowed_users = ["*"]poll_interval_secs = 5| 필드 | 타입 | 기본값 | 설명 |
|---|---|---|---|
api_url | string | — | Mochat API 기본 URL. 필수. 후행 슬래시는 자동으로 제거됨. |
api_token | string | — | Authorization: Bearer <token>으로 전송되는 API 토큰. 필수. |
allowed_users | array | [] | 상호작용이 허용된 Mochat 사용자 ID(fromUserId). []는 전체 차단; ["*"]는 전체 허용. |
poll_interval_secs | number | 5 | 수신 폴링 간격(초). |
폴러는 마지막으로 확인한 메시지 ID를 추적하여 다음 요청 시 since_id로 전달합니다. 응답 code가 0 또는 200이면 전송 성공으로 인식합니다. 헬스 체크는 GET <api_url>/api/health를 호출합니다.
WebSocket vs 웹훅 수신 모드
섹션 제목: “WebSocket vs 웹훅 수신 모드”설정 가능한 수신 모드를 제공하는 채널은 Lark/Feishu뿐입니다. receive_mode로 설정하며, 인바운드 이벤트가 Revka에 전달되는 방식이 달라집니다.
| 모드 | 동작 방식 | 인바운드 포트 | 사용 시기 |
|---|---|---|---|
websocket(기본) | Revka가 Lark/Feishu 오픈 플랫폼에 지속적인 WSS 장기 연결을 열고 이벤트를 프로토콜 프레임으로 수신합니다. | 없음 | 기본값. 가장 간편하게 운영 가능 — NAT 환경에서 공개 URL이나 리버스 프록시 없이도 동작. |
webhook | Revka가 port에서 로컬 HTTP 콜백 서버를 실행하고, 플랫폼이 URL 인증 챌린지와 im.message.receive_v1 이벤트를 POST합니다. | 있음(port) | 장기 연결을 유지할 수 없거나 조직 정책상 HTTP 콜백이 필요한 경우에만 사용. 공개 HTTPS 엔드포인트 필요. |
각 모드에 대한 참고 사항:
- WebSocket 모드는 서버의
ping_interval보정을 위해 즉시 핑을 전송하고, 플랫폼의 3초 윈도우 내에 각 이벤트 프레임을 수신 확인하며, 다중 프래그먼트 페이로드를 재조립하고, 최근 약 30분간 확인된 메시지 ID를 중복 제거하며, 하트비트 타임아웃(300초) 시 재연결합니다. - 웹훅 모드는
port가 설정되어야 합니다. 설정하지 않으면Lark webhook mode requires port to be set in [channels_config.lark]오류와 함께 시작이 실패합니다. 엔드포인트는 라이브 이벤트를 수신하기 전에 플랫폼의 URL 인증 챌린지에 응답해야 합니다(verification_token이 설정된 경우 유효성 검사 포함). 플랫폼이 게이트웨이에 접근해야 하므로 HTTPS로 노출해야 합니다. 터널로 게이트웨이 노출하기를 참고하세요.
DingTalk와 QQ는 항상 WebSocket(설정 불가)이고, WeCom은 항상 발신 전용이며, Mochat은 항상 폴링 방식입니다. 이 채널들은 receive_mode를 사용하지 않습니다.
channel-lark 기능 플래그
섹션 제목: “channel-lark 기능 플래그”Lark/Feishu 지원은 Cargo 기능 channel-lark로 컴파일됩니다. 이 방식으로 게이팅되는 채널은 Lark/Feishu뿐이며, DingTalk, WeCom, QQ, Mochat은 기능 플래그 없이 항상 사용 가능합니다.
Lark/Feishu를 명시적으로 포함하여 빌드하려면:
cargo build --release --features channel-lark--no-default-features와 직접 지정한 기능 목록으로 빌드하는 경우, Lark 또는 Feishu를 원한다면 channel-lark를 다시 추가하세요. 플래그 없이 빌드했는데 설정에 Lark/Feishu 섹션이 있으면, Revka는 해당 채널을 시작하지 않고 이 빌드에서 지원이 비활성화되어 있다는 메시지를 로그에 기록합니다. Matrix, Lark, Nostr, WhatsApp Web, Voice Wake를 포함한 전체 채널 기능 플래그 목록은 Cargo 기능 플래그 및 ADR을 참고하세요.
허용 목록 의미론
섹션 제목: “허용 목록 의미론”이 페이지의 모든 채널은 allowed_users로 인바운드 발신자를 제어합니다.
- 빈 목록(
[])은 모든 발신자를 차단합니다. "*"단일 항목은 모든 발신자를 허용합니다.- 그 외의 경우, 나열된 ID만 허용됩니다 — 각각 Lark/Feishu Open ID, DingTalk 직원 ID, QQ 사용자 ID, Mochat 사용자 ID.
WeCom은 발신 전용이므로 allowed_users는 예약 필드이며 현재 사용되지 않습니다. 새 채널을 설정할 때는 allowed_users = ["*"]로 시작하여 전달을 확인한 뒤, 명시적인 ID로 좁히세요.
관련 페이지
섹션 제목: “관련 페이지”- 채널 개요 — 채널 트레이트, 전달 모드, 허용 목록 모델.
- 메시징 채널 연결하기 — 채널을 활성화하고 테스트하는 일반적인 절차.
- 터널로 게이트웨이 노출하기 — Lark/Feishu 웹훅 모드에 공개 HTTPS URL 제공.
- 웹훅 인그레스 — 게이트웨이 웹훅 엔드포인트 및 요청 처리.
- Voice, TTS & 전사 — Lark/Feishu 및 QQ에서 사용하는 오디오 첨부 파일 전사.
- 중국 지역 프로바이더 — 이 채널들과 중국 지역 모델 프로바이더 연동.
- Cargo 기능 플래그 및 ADR —
channel-lark플래그 및 기타 빌드 옵션.