TL;DR

  • macOS에서 Emacs로 WhatsApp을 이용하는 클라이언트를 구축했으며, 공식 앱의 로컬 데이터베이스와 단축어를 활용하는 방식임.
  • Emacs 29의 내장 SQLite 지원으로 채팅 기록을 읽고, Core Data 날짜 기준과 고정된 채팅의 저장 방식 등 데이터베이스의 예외를 처리함.
  • 메시지 전송에는 macOS 단축어(Shortcuts)의 공식 WhatsApp 동작을 사용하고, 임시 vCard 연락처와 JSON 파일을 전달함.
  • 클라이언트는 Elisp 몇 줄과 외부 의존성 없이 구성되며, 대화별 버퍼와 채팅 목록을 제공함.
  • macOS 전용이며 그룹 전송·일부 전화번호가 없는 대화·알림을 지원하지 않고, 새 메시지는 직접 가져와야 함.

연결 경로의 부재

  • Emacs에서 다른 소프트웨어용 인터페이스를 만드는 일은 유용하고, 다른 도구와 연결해 더 편리한 작업 흐름을 구성할 수 있음.
  • macOS에서 Emacs로 WhatsApp 채팅을 할 수 있는지가 핵심 질문이며, 일부 제약을 감수하면 구현 가능함.
  • Meta는 이를 위한 API를 제공하지 않으며, macOS 데스크톱 앱에도 열린 포트, gRPC, AppleScript 사전 등 연결 수단이 없음.
  • whatsmeow 같은 비공식 클라이언트는 기기인 것처럼 가장해 WhatsApp 서버에 직접 연결하지만, Meta가 승인하지 않은 소프트웨어에 계정이 의존하고 유지해야 할 프로세스가 추가됨.
  • Meta가 이런 클라이언트를 감지해 전화번호를 차단할 수 있으므로, Emacs에서 채팅하려고 계정을 위험에 노출하는 방식은 선택하지 않음.

읽기: 데이터베이스

  • WhatsApp 앱은 모든 대화를 로컬 SQLite 파일에 저장함: ~/Library/Group Containers/group.net.whatsapp.WhatsApp.shared/ChatStorage.sqlite
  • Emacs 29에는 SQLite 지원이 내장되어 있으며, 데이터베이스를 읽기 전용으로 여는 데 외부 라이브러리가 필요하지 않음.
  • 스키마 문서는 공개되어 있지 않아 직접 살펴봐야 하며, 대화는 ZWACHATSESSION, 메시지는 ZWAMESSAGE, 그룹 구성원 이름은 ZWAGROUPMEMBER와 ZWAPROFILEPUSHNAME에 저장됨.
  • 테이블 이름 앞의 Z는 데이터베이스가 Core Data 형식임을 드러냄.
  • Core Data의 날짜는 1970년이 아니라 2001년 1월 1일부터 계산하므로, 날짜 기준을 보정하지 않으면 처음 가져온 메시지가 1995년으로 표시됨.
  • Unix 에포크와 Core Data 에포크의 차이인 978307200초를 상수로 더해 날짜를 보정함.
  • 고정된 대화에는 상단에 유지되도록 미래 날짜가 저장되며, 대화의 마지막 메시지 텍스트도 인코딩되어 있음. 두 값 모두 대화 세션이 아니라 메시지 자체에서 읽는 편이 적절함.
  • macOS는 해당 디렉터리에 대한 접근을 보호하므로, 개인정보 보호 설정에서 Emacs에 전체 디스크 접근 권한(Full Disk Access)을 부여해야 함.

보내기: 단축어 활용

  • 데이터베이스를 수정해도 WhatsApp 앱이 이를 알아차리지 못하고 메시지가 네트워크로 전송되지 않으므로, 데이터베이스 직접 수정은 전송 방법이 아님.
  • WhatsApp 앱은 macOS 단축어에 Send Message 동작을 제공하며, 단축어는 터미널에서 실행할 수 있음.
  • 해당 동작은 전화번호가 아니라 주소록 연락처를 요구하므로, 대화의 전화번호를 담은 vCard를 즉석에서 만들어 단축어에 전달함. 단축어는 이를 임시 연락처로 처리하며 주소록에는 저장하지 않음.
  • Emacs는 vCard와 메시지 텍스트가 담긴 JSON 파일을 작성한 뒤, 프로세스를 시작하고 완료를 기다림.
  • 전송 흐름은 Emacs → ChatStorage.sqlite 읽기, Emacs → vCard가 포함된 JSON → Send WhatsApp 단축어 → Send Message 동작 → 공식 WhatsApp 앱 → 데이터베이스 저장 순서임.
  • 단축어가 실행되는 동안 메시지는 (sending...)으로 표시되며, WhatsApp이 메시지를 데이터베이스에 저장하면 Emacs가 이를 실제 메시지로 바꿈. 따라서 화면에는 Emacs가 전송했다고 여기는 내용이 아니라 WhatsApp이 실제로 보낸 내용이 표시됨.

결과

  • 결과물은 채팅 목록과 대화별 버퍼를 갖춘 ERC 또는 rcirc 스타일 인터페이스임.
  • 클라이언트에서 가장 단순한 부분은 인터페이스이며, Emacs에는 이런 문제를 해결하는 기능이 이미 오래전부터 있음.
  • 클라이언트 전체는 외부 의존성 없이 몇 줄의 Elisp로 구성됨.
  • 제한 사항은 다음과 같음.
  • macOS에서만 작동함.
  • 단축어의 전송 동작이 사람 연락처만 받으므로 그룹에는 메시지를 쓸 수 없음.
  • 일부 대화는 전화번호를 로컬에 저장하지 않아 읽기 전용으로 남음.
  • 알림이 없으며, C-c C-u를 눌러 새 메시지를 가져와야 함.
  • Emacs를 벗어나지 않고 가족과 대화할 수 있음.

출처

  • whatsapp.el: 코드와 단축어 생성 안내.
  • Emacs의 SQLite: Emacs 29부터 제공되는 내장 지원.
  • 명령줄에서 단축어 실행하기: shortcuts 명령에 관한 Apple 문서.
  • vCard 3.0(RFC 2426): 임시 연락처 방식에 사용되는 연락처 형식.