홈으로 돌아가기

Max.ru용 MVP 봇: Long Polling 및 답장 매핑

이 글은 Node.js와 Long Polling, SQLite를 사용한 Max.ru용 MVP 피드백 봇 제작을 설명합니다. 관리자 응답 라우팅을 위한 답장 매핑 로직이 구현되었습니다. DB 초기화 및 이벤트 처리 전체 코드가 제공됩니다.

Max.ru 봇: 설치부터 답장 로직까지
Advertisement 728x90

롱 폴링과 SQLite로 Max.ru MVP 피드백 봇 만들기

Max.ru는 개발자에게 인터랙티브 도구를 만들 수 있도록 봇 API를 제공합니다. 이번 MVP 피드백 봇에서는 Node.js와 @maxhub/max-bot-api 라이브러리, 그리고 SQLite를 사용합니다. 롱 폴링 기술을 통해 웹훅, 서버, HTTPS 없이도 로컬 환경에서 개발할 수 있습니다.

핵심 과제는 관리자가 응답했을 때 해당 요청을 보낸 클라이언트를 식별하는 것입니다. 관리자의 답변 메시지에는 원본 메시지의 mid를 포함한 링크가 포함되어 있어, 데이터베이스를 통해 응답을 사용자에게 정확히 전달할 수 있습니다.

기술 스택 선택 및 프로젝트 설정

@maxhub/max-bot-api 라이브러리는 기본적으로 롱 폴링을 지원하므로, 개발 중 ngrok이나 Cloudflare Tunnel을 사용할 필요가 없습니다.

Google AdInline article slot

SQLite는 MVP에 이상적인 선택입니다. 파일 기반 데이터베이스로 별도의 서버가 필요 없으며, 지연 시간이 낮고 요청 처리가 일관적입니다.

의존성 설치:

npm init -y
npm install @maxhub/max-bot-api sqlite sqlite3 dotenv

.env 파일 생성:

Google AdInline article slot
BOT_TOKEN=your_token_here
OWNER_ID=12345678

토큰은 business.max.ru에서 발급받으며, OWNER_ID는 본인의 Max 사용자 ID입니다.

SQLite 초기화 및 데이터베이스 구조 설정

비동기 연결을 사용하고, 응답 매핑을 위한 reply_map 테이블을 생성합니다:

import { open } from 'sqlite';
import sqlite3 from 'sqlite3';

let db;
(async () => {
  db = await open({ filename: './database.sqlite', driver: sqlite3.Database });
  
  await db.exec(`
    CREATE TABLE IF NOT EXISTS reply_map (
      owner_msg_mid TEXT PRIMARY KEY,
      client_user_id INTEGER
    )
  `);
  console.log('Database ready');
})();

이 테이블은 owner_msg_midclient_user_id로 매핑하여 저장합니다.

Google AdInline article slot

클라이언트로부터의 메시지 처리

message_created 이벤트는 발신자를 확인합니다. 클라이언트 메시지는 마크다운 형식으로 관리자에게 포워딩됩니다:

const ownerId = Number(process.env.OWNER_ID);

bot.on('message_created', async (ctx) => {
  const msg = ctx.message;
  const senderId = msg.sender.user_id;
  const text = msg.body.text;

  if (senderId !== ownerId) {
    
    const forwardText = `📩 **${msg.sender.first_name}님의 메시지** (ID: ${senderId}) :

${text}`;
    
    const sentMsg = await bot.api.sendMessageToUser(ownerId, forwardText, { format: 'markdown' });

    if (sentMsg && sentMsg.body && sentMsg.body.mid) {
        await db.run('INSERT OR REPLACE INTO reply_map (owner_msg_mid, client_user_id) VALUES (?, ?)', 
          [sentMsg.body.mid, senderId]);
    }
    return;
  }
  // 응답 로직
});

전송된 메시지의 mid는 이후 경로 전달을 위해 reply_map에 저장됩니다.

관리자 응답 로직

응답 메시지는 link.type = 'reply'link.message.mid — 즉 원본 메시지의 ID — 를 포함합니다. 클라이언트를 찾기 위해:

  • link.type === 'reply'인 경우, repliedMsgMid를 추출하고 데이터베이스에서 조회합니다.
  • client_user_id를 이용해 클라이언트에게 응답을 전송합니다.
  • 관리자에게 수신 확인을 전달합니다.

샘플 응답 JSON 구조:

{
  "body": { "text": "answer", ... },
  "sender": { ... },
  "link": {
    "type": "reply",
    "message": {
      "mid": "mid.original_message..."
    }
  }
}

이 아키텍처의 장점

  • 로컬 개발 가능: npm start로 바로 실행 가능하며, 포트 설정이나 SSL 구성 없이도 작동합니다.
  • 간편한 응답 인터페이스: 관리자는 표준 응답 기능을 자연스럽게 사용할 수 있습니다.
  • 단순한 데이터베이스: 백업이 쉬운 하나의 파일(database.sqlite)로 운영됩니다.
  • 확장성: 트래픽 증가 시 PostgreSQL으로 쉽게 마이그레이션 가능합니다.

핵심 요약

  • 롱 폴링은 웹훅 없이도 MVP 개발을 간소화합니다.
  • SQLite의 reply_map는 정확한 응답 경로 전달을 보장합니다.
  • 포워딩 메시지에 마크다운을 사용하면 클라이언트 이름과 ID 등 추가 정보가 명확해집니다.
  • 관리자 응답의 백업 전략: 매칭되는 클라이언트가 없을 경우 최근 활성화된 클라이언트를 사용합니다.

— Editorial Team

Advertisement 728x90

다음 읽기