NotificationListener가 알림 수신SessionManager가 방 세션 캐싱BotManager가 JS 스크립트 관리SessionReplier가 실제 회신 전송이 글은 이 4개를 역할, 동작 순서, 실패 지점, 개선 포인트 기준으로 자세히 설명한다.

카톡봇 메시지 1건이 처리되는 흐름은 보통 이렇게 간다.
NotificationListener가 알림 이벤트 수신SessionManager가 해당 방의 답장 세션 저장/갱신BotManager가 활성화된 JS 스크립트 목록 조회responseFix(...) 실행replier.reply(...) 호출SessionReplier가 캐시된 세션으로 실제 전송핵심은 “로직 실행”보다 세션 확보 + 전송 안정성이다.
NotificationListener는 시스템 알림 이벤트를 받아서 카톡 메시지 입력으로 변환하는 컴포넌트다.
여기서 파싱이 틀리면 뒤 단계는 전부 잘못된다.
알림 포맷 변화
안드로이드 버전/앱 업데이트에 따라 extras 구조가 달라진다.
중복 이벤트
같은 알림이 update/repost 되며 다시 들어올 수 있다.
그룹채팅 파싱 혼선
room/sender 판단이 애매한 경우가 있다.
EXTRA_TEXT, EXTRA_BIG_TEXT, EXTRA_MESSAGES)를 fallback으로 조회notification key + postTime 기반 중복 차단SessionManager는 방별로 reply 가능한 세션 액션을 캐싱한다.
카톡봇이 실제로 망가지는 가장 흔한 이유는 “답장 통로 없음”이다.
그래서 세션 매니저는 사실상 봇의 심장에 가깝다.
room -> cachedSession 갱신세션은 영구가 아니다
앱 상태/OS 정책/알림 업데이트로 무효화될 수 있다.
방 이름 키 충돌 가능성
동명이방/특수케이스 고려 필요
초기 콜드스타트 공백
세션이 아직 없는 방은 첫 답장이 실패할 수 있다.
BotManager는 JS 스크립트 파일을 관리하고 활성 봇 목록을 제공한다.
앱 코드와 봇 로직을 분리해야 운영이 빨라진다.
SessionReplier는 캐시된 세션을 이용해 메시지를 실제로 전송한다.
reply(message) 또는 replyToRoom(room, message) 실행여기가 실패하면 사용자 입장에서는 “봇이 안 된다”로 보인다.
즉, 체감 품질의 마지막 책임이 replier에 있다.
no session: 세션 자체 없음pendingIntent null: 액션 무효no remoteInput: 답장 가능한 입력 없음exception: 기타 실행 예외실무에서 중요한 건 “실패한다”가 아니라 왜 실패했는지 구분하는 것이다.
실전 세션이 없을 때도 시뮬레이션 응답을 낼 수 있으면,
가 가능해진다.
이 구조의 핵심 아이디어는 역할 분리다.
한 컴포넌트가 여러 역할을 동시에 맡으면 디버깅이 급격히 어려워진다.
반대로 분리가 잘 되어 있으면 장애 원인 분리가 빠르다.
이 지표를 같이 보면 어디가 병목인지 보인다.
요청한 4개 컴포넌트는 각각 독립 기능처럼 보이지만, 실제로는 하나의 체인이다.
카톡봇 품질은 결국 이 4개를 얼마나 안정적으로 연결했느냐에서 결정된다.