이 가이드는 서버 구축을 마친 상태에서, 실제 스마트폰에 메신저봇R을 설치하고 카카오톡 부계정(듀얼 메신저)을 생성하여 자동응답 시스템을 완성하는 과정을 담았습니다. 누구나 보고 따라 할 수 있도록 단계별로 정리했습니다.
카톡블로그자동화 봇 구축 실습 가이드 (스마트폰 편)
사전 준비물
삼성 갤럭시 스마트폰 (듀얼 메신저 기능 지원 모델)
두 번째 전화번호 (통신사 투넘버 서비스, 또는 알뜰폰 eSIM 개통 필수)
구글 클라우드 서버 주소 (지난번 구축한 여러분의 IP:
http://**.***.***.***:8000)
1단계: 카카오톡 부계정 만들기 (듀얼 메신저)
하나의 폰에서 '본캐(내 계정)'와 '부캐(봇 계정)'를 동시에 돌리기 위한 환경을 만듭니다.
스마트폰의 [설정] > [유용한 기능]으로 이동합니다.
[듀얼 메신저] 메뉴를 클릭합니다.
KakaoTalk 항목의 스위치를 [켜기]로 설정합니다.
설치 팝업이 뜨면 [설치]를 누릅니다. (연락처 분리 기능은 필요에 따라 선택)
앱 화면에 주황색 고리(체인) 마크가 붙은 두 번째 카카오톡이 생성됩니다.
이 앱을 실행하고, 준비해둔 새로운 전화번호로 가입 및 로그인을 완료합니다.
주의: 절대 원래 쓰던 번호로 로그인하지 마세요. 기존 카톡이 지워집니다.
2단계: 메신저봇R 앱 설치 및 보안 해제
구글 플레이스토어에는 없는 개발자용 앱을 수동으로 설치합니다.
다운로드: 아래 공식 깃허브 링크에서
MessengerBotR_0.7.39a.apk파일을 다운로드합니다.링크:
https://github.com/MessengerBotTeam/msgbot-old-release/releases/tag/0.7.39a
보안 설정 해제 (설치 막힘 해결):
설치 시 "유해한 앱" 경고가 뜨면 [무시하고 설치]를 누릅니다.
설치가 안 된다면 구글 플레이스토어 앱 실행 > 우측 상단 프로필 > [Play 프로텍트] > 설정(톱니바퀴) > [앱 스캔 기능]을 모두 [끄기]로 변경합니다.
3단계: 봇 초기 설정 (중요 ⭐)
최신 자바스크립트 문법을 사용하기 위해 필수적인 설정을 진행합니다.
설치된 메신저봇R 앱을 실행합니다.
새 봇 생성 화면이 나오거나, 우 측 하단 (+) 버튼을 눌러 봇을 생성합니다.
이름을 입력하고(예:
KasyaBot), 아래 체크박스를 확인합니다.[ ] 레거시 API를 사용합니다. (체크 해제)
[x] Console API를 사용합니다 (실험적) (반드시 체크!)
[확인]을 눌러 봇을 생성합니다.
4단계: 자바스크립트 코드 적용 및 컴파일
서버와 통신하는 뇌(Brain)를 심어주는 과정입니다.
생성된 봇(KasyaBot)을 터치하여 스크립트 편집창으로 들어갑니다.
기존 내용을 모두 지우고, 아래 코드를 복사해서 붙여넣습니다.
(무한 루프 방지 코드가 포함된 최종 수정본입니다)
JavaScript
const bot = BotManager.getCurrentBot();
const URL_REGEX = /(https?:\/\/[^\s]+)/g;
// ✅ 서버 주소 (구글 클라우드 외부 IP)
const API_BASE = "http://35.226.157.200:8000";
/* ========= 대화 기억 저장소 (멀티턴) ========= */
const KASYA_MEMORY = {};
const MAX_TURNS = 10;
const MEMORY_TIME = 30 * 60 * 1000;
/* ========= 유틸리티 함수 ========= */
function toStringSafe(val) {
if (val === undefined || val === null) return '';
try { return String(val); } catch (_) { return ''; }
}
function nowMs() { return java.lang.System.currentTimeMillis(); }
/* ========= HTTP 통신 (타임아웃 설정) ========= */
function httpGet(url) {
try {
var conn = new java.net.URL(url).openConnection();
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept", "application/json");
conn.setConnectTimeout(10000);
conn.setReadTimeout(60000);
conn.connect();
if (conn.getResponseCode() !== 200) return null;
var br = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream(), "UTF-8"));
var sb = new java.lang.StringBuilder();
var line;
while ((line = br.readLine()) !== null) sb.append(line);
br.close();
return toStringSafe(sb.toString());
} catch (err) { return null; }
}
function httpPostJson(url, obj) {
var conn = null;
try {
var payload = JSON.stringify(obj);
conn = new java.net.URL(url).openConnection();
conn.setDoOutput(true);
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/json; charset=UTF-8");
conn.setConnectTimeout(10000);
conn.setReadTimeout(60000);
conn.connect();
var os = conn.getOutputStream();
var writer = new java.io.OutputStreamWriter(os, "UTF-8");
writer.write(payload);
writer.flush(); writer.close();
if (conn.getResponseCode() !== 200) return null;
var br = new java.io.BufferedReader(new java.io.InputStreamReader(conn.getInputStream(), "UTF-8"));
var sb = new java.lang.StringBuilder();
var line;
while ((line = br.readLine()) !== null) sb.append(line);
br.close();
return String(sb.toString());
} catch (err) { return null; } finally { try{if(conn)conn.disconnect();}catch(_){} }
}
function send(msg, text) {
App.runOnUiThread(function() { msg.reply(toStringSafe(text)); }, function(_) {});
}
/* ========= 핵심 기능: 도깨비 멀티턴 ========= */
function processKasyaMulti(msg, room, question) {
var mem = KASYA_MEMORY[room];
var now = nowMs();
if (!mem || (now - mem.lastTime > MEMORY_TIME) || question.indexOf("초기화") !== -1 || question.indexOf("잊어") !== -1) {
mem = { history: [], lastTime: now };
if (question.indexOf("초기화") !== -1 || question.indexOf("잊어") !== -1) {
KASYA_MEMORY[room] = mem;
send(msg, "후후, 지난 시나리오는 모두 잊었습니다.");
return;
}
}
mem.history.push({ role: "user", content: question });
mem.lastTime = now;
try {
var resMulti = httpPostJson(API_BASE + "/kasya_multi", { history: mem.history });
var json = JSON.parse(resMulti);
var answer = json.answer || "";
if (answer) {
send(msg, answer);
mem.history.push({ role: "assistant", content: answer });
if (mem.history.length > MAX_TURNS) mem.history = mem.history.slice(-MAX_TURNS);
KASYA_MEMORY[room] = mem;
}
} catch (e) { send(msg, "도깨비 통신 장애 발생 ⚡"); mem.history.pop(); }
}
/* ========= 채팅방 요약 (서기 모드) ========= */
var CHAT_LOG = {};
function pushChat(room, sender, text) {
if (text.startsWith("@") || text.startsWith("카스야")) return;
var arr = CHAT_LOG[room] || (CHAT_LOG[room] = []);
arr.push({ t: nowMs(), sender: sender, text: text });
if (arr.length > 1000) arr.shift();
}
function summarizeRoom(msg, room) {
var arr = CHAT_LOG[room] || [];
var cutoff = nowMs() - 86400000; // 24시간
var recent = arr.filter(function(x) { return x.t >= cutoff; });
if (!recent.length) return send(msg, "기록된 대화가 없습니다.");
var transcript = recent.map(function(x) { return x.sender + ": " + x.text; }).join("\n");
var resRoom = httpPostJson(API_BASE + "/summarize_text", { room: room, window: "24h", transcript: transcript });
if(resRoom) send(msg, "🕰️ [기록 열람]\n\n" + JSON.parse(resRoom).summary);
}
/* ========= URL 처리 ========= */
function processUrl(msg, url) {
setTimeout(function() {
if (/blog\.naver\.com/.test(url)) {
send(msg, "👹 도깨비가 설화(블로그)를 심사 중입니다...");
var resBlog = httpGet(API_BASE + "/analyze_blog?url=" + encodeURIComponent(url));
if(resBlog) send(msg, JSON.parse(resBlog).report);
} else {
var resSummary = httpGet(API_BASE + "/summarize?url=" + encodeURIComponent(url));
if(resSummary) {
var jsonSum = JSON.parse(resSummary);
send(msg, "📜 [히든 시나리오]\n**" + jsonSum.title + "**\n\n" + jsonSum.summary);
}
}
}, 1000);
}
/* ========= 메인 리스너 ========= */
bot.addListener(Event.MESSAGE, function (msg) {
try {
var content = msg.content.trim();
var room = msg.room;
var sender = msg.author.name;
// [중요] 봇 자신이 보낸 메시지 및 자기 이름 필터링 (무한루프 방지)
// 아래 "카스봇" 부분을 실제 부계정 프로필 이름으로 꼭 수정하세요!
if (sender === "카스봇") return;
// 0. 대화 로그 저장
pushChat(room, sender, content);
// 1. [명령어] 키워드 글쓰기
if (content.startsWith("카스야 키워드")) {
var keyword = content.replace("카스야 키워드", "").trim();
if (!keyword) return send(msg, "키워드를 입력하세요.");
send(msg, "🤖 AI 작가가 '" + keyword + "' 집필 중... (15초 소요)");
var resKeyword = httpPostJson(API_BASE + "/relay/auto_blog", { keyword: keyword });
if(resKeyword) {
var jsonKey = JSON.parse(resKeyword);
if(jsonKey.status==="ok") send(msg, "✅ 전송 완료!\n제목: " + jsonKey.generated_title + "\n\n집 PC가 곧 처리합니다.");
else send(msg, "❌ 실패: " + jsonKey.message);
}
return;
}
// 2. [명령어] 원격 글쓰기
if (content.startsWith("카스야 원격")) {
var raw = content.replace("카스야 원격", "").trim();
var idx = raw.indexOf("/");
if (idx === -1) return send(msg, "형식: 카스야 원격 제목 / 본문");
send(msg, "📨 중계소로 원고 발송 중...");
var resRemote = httpPostJson(API_BASE + "/relay/send", { type: "blog", data: { title: raw.substring(0, idx).trim(), content: raw.substring(idx+1).trim() } });
if(resRemote) send(msg, "✅ 중계소 도착 완료!");
return;
}
// 3. [명령어] 도깨비 대화
if (content.startsWith("카스야")) {
processKasyaMulti(msg, room, content.replace("카스야", "").trim());
return;
}
// 4. [명령어] 방 요약
if (content === "@요약") {
summarizeRoom(msg, room);
return;
}
// 5. 링크 감지
var urls = content.match(URL_REGEX);
if (urls) processUrl(msg, urls[0]);
} catch (e) {}
});
명령어 변경 가이드 (메인 리스너 부분)
스크립트의 맨 아래쪽 bot.addListener 내부를 보시면 됩니다.
1. [키워드 글쓰기] 명령어 바꾸기
예를 들어 "카스야 키워드" 대신 "블로그 써줘"로 바꾸고 싶다면?
JavaScript
// [변경 전]
if (content.startsWith("카스야 키워드")) {
var keyword = content.replace("카스야 키워드", "").trim();
// 👇 [변경 후] 두 곳을 똑같이 바꿔야 합니다!
if (content.startsWith("블로그 써줘")) { // 1. 감지할 단어
var keyword = content.replace("블로그 써줘", "").trim(); // 2. 내용만 남기기 위해 지울 단어
주의: 2번
replace부분을 안 바꾸면, 봇이 "블로그 써줘 테슬라"라는 전체 문장을 키워드로 인식해버립니다. 꼭 두 곳 다 바꿔주세요.
2. [원격 글쓰기] 명령어 바꾸기
예를 들어 "카스야 원격" 대신 "전송"으로 바꾸고 싶다면?
JavaScript
// [변경 전]
if (content.startsWith("카스야 원격")) {
var raw = content.replace("카스야 원격", "").trim();
// 👇 [변경 후]
if (content.startsWith("전송")) {
var raw = content.replace("전송", "").trim();
3. [일상 대화] 봇 이름 바꾸기
가장 많이 쓰는 "카스야"라는 호칭을 "자비스"로 바꾸고 싶다면?
JavaScript
// [변경 전]
if (content.startsWith("카스야")) {
processKasyaMulti(msg, room, content.replace("카스야", "").trim());
// 👇 [변경 후]
if (content.startsWith("자비스")) {
processKasyaMulti(msg, room, content.replace("자비스", "").trim());
[중요] 잊지 말아야 할 한 가지! (서기 모드)
위에서 이름을 "자비스"로 바꿨다면, 채팅 로그를 저장하는 pushChat 함수에서도 이름을 바꿔줘야 명령어가 대화 기록에 남지 않습니다.
코드 중간쯤에 있는 pushChat 함수를 찾아주세요.
JavaScript
/* ========= 채팅방 요약 (서기 모드) ========= */
function pushChat(room, sender, text) {
// [변경 전] "카스야"로 시작하는 말은 기록 안 함
if (text.startsWith("@") || text.startsWith("카스야")) return;
// 👇 [변경 후] "자비스"로 시작하는 말은 기록 안 함
if (text.startsWith("@") || text.startsWith("자비스")) return;
// ... (나머지 코드)
}
요약
if (content.startsWith("...")): 봇이 반응할 명령어content.replace("...", ""): 명령어 빼고 알맹이만 남기는 작업pushChat함수: 명령어를 기록에서 뺄 때 쓰는 필터
이 3가지만 기억하시면 당신만의 맞춤형 비서 이름을 지어주실 수 있습니다!
코드 상단
const API_BASE주소가 내 서버 IP가 맞는지 확인합니다.코드 하단
if (sender === "카스봇")부분의 이름을 내 부계정 카톡 프로필 이름과 똑같이 수정합니다.상단 폴더 아이콘(💾)을 눌러 [저장]하고, 그 옆의 회전 아이콘(🔄)을 눌러 [컴파일]합니다. "컴파일 완료" 메시지가 떠야 합니다.
5단계: 최종 권한 부여 및 테스트
봇이 카카오톡 알림을 읽을 수 있도록 허락해 줍니다.
메신저봇R 앱 초기 화면으로 나옵니다.
상단 메뉴바(≡) 혹은 설정에서 [알림 접근 허용]을 찾아 들어갑니다.
MessengerBotR 앱에 권한을 줍니다.
봇 목록에서 내가 만든 봇(KasyaBot)을 켭니다(ON).
봇 설정(톱니바퀴) 등에서 '연동할 앱'을 선택하는 메뉴가 있다면, [KakaoTalk (듀얼 메신저 아이콘)]을 선택합니다.
테스트 해보기
여러분의 원래 카카오톡(본캐)으로 가서, 방금 만든 부계정(카스봇)에게 말을 겁니다.
카스야 안녕
부계정(봇)이 자동으로 답장을 보내면 성공입니다!
"안녕하세요, 저는 카스야입니다..."(서버에서 생성된 답변)
문제 발생 시 체크리스트
반응이 없나요? 메신저봇R 앱의 로그(Log) 탭을 확인하세요. 알림이 들어오는지, 에러 메시지가 뜨는지 봐야 합니다.
설치가 안 되나요?
Play 프로텍트가 꺼져 있는지 다시 확인하세요.컴파일 에러? 코드를 복사할 때 괄호(
}) 하나라도 빠지면 안 됩니다. 전체 복사를 신중히 해주세요.
다음글에서는 집pc에 설치하는 파이썬 파일과 구동방법에 대한 가이드를 드리도록 하겠습니다.
감사합니다:)