npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

kakaoforge

v1.0.4

Published

<div align="center">

Readme

Libaray License Last Commit Commits npm version npm downloads TypeScript Issues Pull Requests

KakaoForge

카카오톡 LOCO 프로토콜 기반 Node.js 봇 라이브러리

다른 언어로 된 문서가 필요하신가요? 아래 표를 확인해 주세요.

| 언어 | 위치 | | ---- | --- | | Korean | README.md | | English | docs/README-en.md |

목차


설치

npm install kakaoforge

빠른 시작

1. QR 코드 로그인

처음 사용 시 QR 코드를 통해 인증해야 합니다. 기본적으로 ./auth.json 위치에 로그인 정보가 저장됩니다.

const { createAuthByQR } = require('kakaoforge');

// QR 코드가 터미널에 표시됩니다
// 카카오톡 앱에서 QR 코드를 스캔하세요
await createAuthByQR();

저장 위치를 변경하려면 authPath 옵션을 사용하세요:

await createAuthByQR({
    authPath: './config/my-auth.json'  // 원하는 경로 지정
});

2. 봇 클라이언트 생성

const { createClient } = require('kakaoforge');

const client = createClient();

옵션을 지정하여 클라이언트를 생성할 수 있습니다:

const client = createClient({
    authPath: './auth.json',      // 인증 파일 경로 (기본: './auth.json')
    debug: true,                  // 디버그 로깅 활성화 (기본: false)
    autoConnect: true,            // 자동 연결 (기본: true)
    autoReconnect: true,          // 연결 끊김 시 자동 재연결 (기본: true)
    sendIntervalMs: 400,          // 메시지 전송 간격 제한 (기본: 400ms)
    pingIntervalMs: 60000,        // Ping 간격 (기본: 60초)
});

3. 메시지 수신 및 응답

client.onReady((chat) => {
    console.log('KakaoForge 준비 완료!');
});

client.onMessage(async (chat, msg) => {
    // 자신의 메시지는 무시
    if (msg.sender.id === client.userId) return;

    console.log(`[${msg.sender.name}] ${msg.message.text}`);

    // "안녕"이라고 보내면 "안녕하세요!"로 응답
    if (msg.message.text === '안녕') {
        await chat.sendText(msg.room.id, '안녕하세요!');
    }
});

주요 기능

메시지 송수신

// 텍스트 메시지
await chat.sendText(roomId, '안녕하세요');

// 답장
await chat.sendReply(roomId, '답장입니다', msg);

// 스레드 댓글
await chat.sendThreadReply(roomId, msg.message.id, '댓글');

// 스레드 댓글 + 채팅방에도 전송
await chat.sendThreadReply(roomId, msg.message.id, '댓글', { sendToChatRoom: true });

미디어 전송

// 사진
await chat.sendPhoto(roomId, '/path/to/image.jpg', { text: '사진 설명' });

// 동영상
await chat.sendVideo(roomId, '/path/to/video.mp4', { text: '동영상 설명' });

// 음성
await chat.sendAudio(roomId, '/path/to/audio.mp3');

// 파일
await chat.sendFile(roomId, '/path/to/file.txt');

스레드에 미디어 전송

await chat.sendPhotoAtThread(roomId, msg.message.id, '/path/to/image.jpg');
await chat.sendVideoAtThread(roomId, msg.message.id, '/path/to/video.mp4');
await chat.sendAudioAtThread(roomId, msg.message.id, '/path/to/audio.mp3');
await chat.sendFileAtThread(roomId, msg.message.id, '/path/to/file.txt');

멘션 및 스포일러

const { Mention, Spoiler } = require('kakaoforge');

// 사용자 멘션
await chat.sendText(roomId, `${Mention(userId)} 안녕하세요!`);

// 스포일러 텍스트
await chat.sendText(roomId, `스포일러: ${Spoiler('비밀 내용')}`);

이모지 반응

const { Reactions } = require('kakaoforge');

await chat.sendReaction(roomId, msg, Reactions.HEART);    // 하트
await chat.sendReaction(roomId, msg, Reactions.LIKE);     // 좋아요
await chat.sendReaction(roomId, msg, Reactions.CHECK);    // 체크
await chat.sendReaction(roomId, msg, Reactions.LAUGH);    // 웃음
await chat.sendReaction(roomId, msg, Reactions.SURPRISE); // 놀람
await chat.sendReaction(roomId, msg, Reactions.SAD);      // 슬픔
await chat.sendReaction(roomId, msg, Reactions.CANCEL);   // 반응 취소

특수 메시지

// 연락처
await chat.sendContact(roomId, {
    name: '홍길동',
    phone: '01012345678',
    email: '[email protected]'
});

// 카카오 프로필
await chat.sendKakaoProfile(roomId, { userId: 123456789 }); 

// 위치
await chat.sendLocation(roomId, {
    lat: 37.4979,
    lng: 127.0276,
    address: '서울시 강남구'
});

// 일정
await chat.sendSchedule(roomId, {
    eventAt: new Date('2025-02-15'),
    title: '회의',
    location: '회의실'
});

// 링크
await chat.sendLink(roomId, {
    url: 'https://example.com',
    text: '링크 설명'
});

메시지 수정 및 삭제

// 메시지 수정
const sentMsg = await chat.sendText(roomId, '원본 메시지');
await chat.editMessage(roomId, sentMsg, '수정된 메시지');

// 메시지 삭제
await chat.deleteMessage(roomId, sentMsg);

오픈채팅 관리

const { MemberType } = require('kakaoforge');

// 권한 확인
if (client.type === MemberType.OpenChat.Owner ||
    client.type === MemberType.OpenChat.Manager) {

    // 강제퇴장
    await chat.openChatKick(roomId, memberId);
}

메시지 조회

// 특정 메시지 조회
const message = await chat.fetchMessage(roomId, logId);

// 특정 사용자의 메시지 조회
const messages = await chat.fetchMessagesByUser(roomId, userId, {
    since: 0,
    count: 50,
    maxPages: 5,
});

// 사용자명 조회
const username = await chat.getUsernameById(roomId, userId);

API 레퍼런스

인증

createAuthByQR(options?)

QR 코드를 통한 인증을 수행합니다.

await createAuthByQR({
    onQrUrl: (url) => console.log('QR URL:', url),
    onPasscode: (code) => console.log('Passcode:', code),
    save: true,  // auth.json에 저장
    authPath: './auth.json'
});

클라이언트

createClient(config)

KakaoForge 클라이언트를 생성합니다.

const client = createClient({
    authPath: './auth.json',
    debug: true,
    autoConnect: true,
    autoReconnect: true,
    sendIntervalMs: 400,
});

이벤트 핸들러

client.onReady(callback)

연결이 완료되면 호출됩니다.

client.onReady((chat) => {
    console.log('준비 완료!');
    console.log('User ID:', client.userId);
});

client.onMessage(callback)

메시지를 수신하면 호출됩니다.

client.onMessage(async (chat, msg) => {
    console.log('메시지:', msg.message.text);
    console.log('발신자:', msg.sender.name);
    console.log('채팅방:', msg.room.name);
});

client.onJoin(callback)

사용자가 입장하면 호출됩니다.

client.onJoin((chat, evt) => {
    console.log('입장:', evt.member.names);
});

client.onLeave(callback)

사용자가 퇴장하면 호출됩니다.

client.onLeave((chat, evt) => {
    console.log('퇴장:', evt.member.names);
});

client.onInvite(callback)

사용자가 초대되면 호출됩니다.

client.onInvite((chat, evt) => {
    console.log('초대:', evt.member.names);
    console.log('초대한 사람:', evt.actor.name);
});

client.onKick(callback)

사용자가 강제퇴장되면 호출됩니다.

client.onKick((chat, evt) => {
    console.log('강제퇴장:', evt.member.names);
});

client.onDelete(callback)

메시지가 삭제되면 호출됩니다.

client.onDelete((chat, evt) => {
    console.log('삭제된 메시지');
});

client.onHide(callback)

메시지가 숨겨지면 호출됩니다 (오픈채팅).

client.onHide((chat, evt) => {
    console.log('숨겨진 메시지');
});

client.onPush(method, callback)

특정 LOCO push를 직접 수신합니다.

client.onPush('SYNCREWR', (payload) => {
    console.log('Raw push:', payload);
});

메시지 전송

chat.sendText(roomId, text)

텍스트 메시지를 전송합니다.

const sentMsg = await chat.sendText(roomId, '안녕하세요');

chat.sendReply(roomId, text, msg)

메시지에 답장합니다.

await chat.sendReply(roomId, '답장입니다', msg);

chat.sendThreadReply(roomId, msgId, text, options?)

스레드에 댓글을 답니다.

await chat.sendThreadReply(roomId, msgId, '댓글');

// 채팅방에도 전송
await chat.sendThreadReply(roomId, msgId, '댓글', { sendToChatRoom: true });

chat.sendPhoto(roomId, path, options?)

사진을 전송합니다.

await chat.sendPhoto(roomId, '/path/to/image.jpg', { text: '사진' });

chat.sendVideo(roomId, path, options?)

동영상을 전송합니다.

await chat.sendVideo(roomId, '/path/to/video.mp4', { text: '동영상' });

chat.sendAudio(roomId, path)

음성 파일을 전송합니다.

await chat.sendAudio(roomId, '/path/to/audio.mp3');

chat.sendFile(roomId, path)

파일을 전송합니다.

await chat.sendFile(roomId, '/path/to/file.txt');

chat.sendReaction(roomId, msg, reactionId)

이모지 반응을 보냅니다.

await chat.sendReaction(roomId, msg, Reactions.HEART);

chat.sendContact(roomId, contact)

연락처를 전송합니다.

await chat.sendContact(roomId, {
    name: '홍길동',
    phone: '01012345678',
    email: '[email protected]'
});

chat.sendKakaoProfile(roomId, options)

카카오 프로필을 전송합니다.

await chat.sendKakaoProfile(roomId, { userId: 123456789 });

chat.sendLocation(roomId, location)

위치를 전송합니다.

await chat.sendLocation(roomId, {
    lat: 37.4979,
    lng: 127.0276,
    address: '서울시 강남구',
    title: '장소 이름'
});

chat.sendSchedule(roomId, schedule)

일정을 전송합니다.

await chat.sendSchedule(roomId, {
    eventAt: new Date('2025-02-15T10:00:00'),
    title: '회의',
    location: '회의실'
});

chat.sendLink(roomId, link)

링크를 전송합니다.

await chat.sendLink(roomId, {
    url: 'https://example.com',
    text: '링크 설명'
});

메시지 관리

chat.editMessage(roomId, msg, text)

메시지를 수정합니다.

await chat.editMessage(roomId, prevMsg, '수정된 내용');

chat.deleteMessage(roomId, msg)

메시지를 삭제합니다.

await chat.deleteMessage(roomId, msg);

chat.fetchMessage(roomId, logId)

특정 메시지를 조회합니다.

const message = await chat.fetchMessage(roomId, logId);

chat.fetchMessagesByUser(roomId, userId, options)

특정 사용자의 메시지를 조회합니다.

const messages = await chat.fetchMessagesByUser(roomId, userId, {
    since: 0,        // 시작 시점
    count: 50,       // 조회 개수
    maxPages: 5,     // 최대 페이지 수
});

오픈채팅 관리

chat.openChatKick(roomId, memberId)

오픈채팅에서 멤버를 강제퇴장시킵니다.

await chat.openChatKick(roomId, memberId);

사용자 정보

chat.getUsernameById(roomId, userId)

사용자 ID로 닉네임을 조회합니다.

const username = await chat.getUsernameById(roomId, userId);

설정 옵션

createClient(config)에서 사용할 수 있는 설정 옵션입니다.

interface KakaoForgeConfig {
    // 인증
    authPath?: string;           // auth.json 경로 (기본: './auth.json')
    userId?: number;             // 사용자 ID (auth.json에서 로드)
    oauthToken?: string;         // OAuth 토큰 (auth.json에서 로드)
    deviceUuid?: string;         // 디바이스 UUID (auth.json에서 로드)
    refreshToken?: string;       // 리프레시 토큰 (auth.json에서 로드)

    // 연결
    autoConnect?: boolean;       // 자동 연결 (기본: true)
    autoReconnect?: boolean;     // 자동 재연결 (기본: true)
    sendIntervalMs?: number;     // 메시지 전송 간격 제한 (기본: 400)
    reconnectMinDelayMs?: number; // 재연결 최소 대기 시간
    reconnectMaxDelayMs?: number; // 재연결 최대 대기 시간

    // 성능
    pingIntervalMs?: number;     // Ping 간격 (기본: 60000)
    socketKeepAliveMs?: number;  // 소켓 Keep-alive 간격
    memberCacheTtlMs?: number;   // 멤버 캐시 유효 시간
    memberRefreshIntervalMs?: number; // 멤버 새로고침 간격
    memberLookupTimeoutMs?: number;   // 멤버 조회 타임아웃

    // 비디오 설정
    videoQuality?: 'low' | 'high'; // 비디오 품질
    transcodeVideos?: boolean;     // 비디오 트랜스코딩 여부
    ffmpegPath?: string;           // FFmpeg 경로
    ffprobePath?: string;          // FFprobe 경로

    // 디바이스 정보
    deviceId?: string;           // 디바이스 ID
    os?: string;                 // OS 버전
    appVer?: string;             // 앱 버전
    lang?: string;               // 언어 (기본: 'ko')

    // 기타
    debug?: boolean;             // 디버그 로깅 (기본: false)
    timeZone?: string;           // 시간대
}

타입 정의

MessageEvent

메시지 이벤트의 구조입니다.

interface MessageEvent {
    message: {
        id: number | string;        // 메시지 ID
        text: string;               // 메시지 텍스트
        type: number;               // 메시지 타입 (MessageType)
        logId: number | string;     // 로그 ID
    };
    sender: {
        id: number | string;        // 발신자 ID
        name: string;               // 발신자 이름
        type: number;               // 멤버 타입 (오픈채팅)
    };
    room: {
        id: number | string;        // 채팅방 ID
        name: string;               // 채팅방 이름
        isGroupChat: boolean;       // 단체 채팅 여부
        isOpenChat: boolean;        // 오픈채팅 여부
        openLinkId?: number | string; // 오픈채팅 링크 ID
    };
    attachmentsRaw: any[];          // 첨부물 원본 데이터
    raw: any;                       // 원본 LOCO 데이터
}

MessageType

메시지 타입입니다.

const { MessageType } = require('kakaoforge');

MessageType.Text;      // 1  - 텍스트
MessageType.Photo;     // 2  - 사진
MessageType.Video;     // 3  - 동영상
MessageType.Contact;   // 4  - 연락처
MessageType.Audio;     // 5  - 음성
MessageType.Link;      // 9  - 링크
MessageType.Schedule;  // 13 - 일정
MessageType.Location;  // 16 - 위치
MessageType.Profile;   // 17 - 프로필
MessageType.File;      // 18 - 파일
MessageType.Reply;     // 26 - 답장

MemberType

오픈채팅 멤버 타입입니다.

const { MemberType } = require('kakaoforge');

MemberType.OpenChat.Owner;   // 1 - 방장
MemberType.OpenChat.Member;  // 2 - 일반 멤버
MemberType.OpenChat.Manager; // 4 - 매니저

Reactions

이모지 반응 타입입니다.

const { Reactions } = require('kakaoforge');

Reactions.CANCEL;   // 0 - 반응 취소
Reactions.HEART;    // 1 - 하트
Reactions.LIKE;     // 2 - 좋아요
Reactions.CHECK;    // 3 - 체크
Reactions.LAUGH;    // 4 - 웃음
Reactions.SURPRISE; // 5 - 놀람
Reactions.SAD;      // 6 - 슬픔

예제

에코 봇

const { createClient } = require('kakaoforge');

const client = createClient();

client.onReady(() => {
    console.log('에코 봇 준비 완료!');
});

client.onMessage(async (chat, msg) => {
    if (msg.sender.id === client.userId) return;

    // 받은 메시지를 그대로 전송
    await chat.sendText(msg.room.id, msg.message.text);
});

명령어 봇

const { createClient, Reactions, MemberType, Mention } = require('kakaoforge');

const client = createClient({ authPath: './auth.json' });

client.onMessage(async (chat, msg) => {
    if (msg.sender.id === client.userId) return;

    const text = msg.message.text;
    if (!text) return;

    // !ping 명령어
    if (text === '!ping') {
        await chat.sendText(msg.room.id, 'pong!');
        await chat.sendReaction(msg.room.id, msg, Reactions.CHECK);
    }

    // !info 명령어
    if (text === '!info') {
        await chat.sendText(msg.room.id, `채팅방: ${msg.room.name}\n그룹채팅: ${msg.room.isGroupChat}\n오픈채팅: ${msg.room.isOpenChat}`)
    }

    // !hello
    if (text === '!hello') {
        await chat.sendText(msg.room.id, `${Mention(msg.sender.id)} 안녕하세요!`);
    }
});

주의사항

오픈채팅

  • 오픈채팅 관리 기능(강제퇴장)을 사용하려면 봇이 방장 또는 매니저 권한이 있어야 합니다.
  • client.type으로 봇의 권한을 확인할 수 있습니다.

인증 정보

  • 로그인 후 인증 정보는 auth.json 파일에 저장됩니다.
  • 이 파일에는 OAuth 토큰이 포함되어 있으므로 절대 공개하지 마세요.
  • .gitignoreauth.json을 추가하는 것을 권장합니다.

연결 유지

  • 봇은 자동으로 Ping을 전송하여 연결을 유지합니다.
  • 연결이 끊어지면 autoReconnect 옵션에 따라 자동 재연결을 시도합니다.

메시지 전송 속도 제한

  • LOCO 서버는 짧은 시간에 연속 전송 시 status: -303으로 WRITE를 거부할 수 있습니다.
  • 라이브러리는 기본적으로 sendIntervalMs(기본 400ms) 간격으로 전송 큐를 처리합니다.

미디어 전송

  • 동영상 전송 시 transcodeVideos 옵션을 사용하면 자동으로 트랜스코딩됩니다.
  • 트랜스코딩을 위해서는 FFmpeg가 설치되어 있어야 합니다.

라이선스

Non-Commercial / No Abuse

  • 이 라이브러리는 비상업적 용도로만 사용할 수 있습니다.
  • 스팸, 사기, 악의적인 목적으로 사용하는 것은 금지됩니다.
  • 자세한 내용은 LICENSE 를 확인하세요.

Github Issue

  • 이 라이브러리를 사용하는 중 문제가 발생하였거나 질문이 생겼나요?
  • https://github.com/aodjo/KakaoForge/issues