
Kakaotalk Mac
Resolve KakaoTalk Mac local database auth and run read-only chat/message CLI operations from an agent on macOS.
Overview
kakaotalk-mac is an agent skill for the Build phase that authenticates to local KakaoTalk Mac databases and exposes read-only chat CLI commands.
Install
npx skills add https://github.com/nomadamas/k-skill --skill kakaotalk-macWhat is this skill?
- Python CLI resolves user_id, UUID, database path, and decryption key from Mac KakaoTalk state
- Read-only commands: chats, messages, search, schema
- Auth cache at ~/.cache/k-skill/kakaotalk-mac-auth.json with chunked user-id detection
- Handles AuthResolutionError and plist-based Mac configuration parsing
- Mac-only workflow tied to local .db message stores
- 4 read-only CLI commands: chats, messages, search, schema
- Default user-id scan chunk size 500000
Adoption & trust: 2.7k installs on skills.sh; 5.4k GitHub stars; 2/3 security scanners passed (skills.sh audits).
What problem does it solve?
You want your agent to read or search your KakaoTalk history on Mac but auth resolution across user IDs and encrypted databases fails opaquely.
Who is it for?
Mac-based solo builders scripting personal KakaoTalk exports, search, or agent context from the desktop app.
Skip if: Windows/Linux Kakao clients, official Kakao Business APIs, or teams that cannot tolerate local DB access on a user machine.
When should I use this skill?
User works with KakaoTalk on Mac and needs auth resolution, chat listing, message read/search, or schema inspection via k-skill.
What do I get? / Deliverables
ResolvedAuth metadata and stable CLI entrypoints let you run chats, messages, search, or schema against the correct local database.
- ResolvedAuth record with database path and key
- CLI output for chats/messages/search/schema
Recommended Skills
Journey fit
How it compares
Local Mac desktop integration—not a cloud webhook or official Kakao Open API connector.
Common Questions / FAQ
Who is kakaotalk-mac for?
Developers on macOS who automate their own KakaoTalk client data for search, archiving, or agent context pipelines.
When should I use kakaotalk-mac?
During Build integrations when you need chats, messages, search, or schema from KakaoTalk Mac and must resolve auth or database paths first.
Is kakaotalk-mac safe to install?
It reads local messaging databases and derived keys—review the Security Audits panel on this page and only use on accounts and machines you own.
SKILL.md
READMESKILL.md - Kakaotalk Mac
#!/usr/bin/env python3 from __future__ import annotations import argparse import base64 import hashlib import json import multiprocessing as mp import os import re import subprocess import sys from dataclasses import dataclass from datetime import datetime, timezone from pathlib import Path from typing import Any, Callable, Iterable, Sequence EMPTY_ACCOUNT_HASH = ( "31bca02094eb78126a517b206a88c73cfa9ec6f704c7030d18212cace820f025" "f00bf0ea68dbf3f3a5436ca63b53bf7bf80ad8d5de7d8359d0b7fed9dbc3ab99" ) HEX_DATABASE_PATTERN = re.compile(r"^[0-9a-f]{78}(?:\.db)?$") DIRECT_USER_ID_KEYS = ("userId", "user_id", "KAKAO_USER_ID", "userID") DEFAULT_MAX_USER_ID = 1_000_000_000 DEFAULT_CHUNK_SIZE = 500_000 DEFAULT_CACHE_PATH = Path.home() / ".cache" / "k-skill" / "kakaotalk-mac-auth.json" READ_ONLY_COMMANDS = ("chats", "messages", "search", "schema") class AuthResolutionError(RuntimeError): pass @dataclass class DetectionState: uuid: str candidate_user_ids: list[int] active_account_hash: str | None database_files: list[Path] @dataclass class ResolvedAuth: user_id: int uuid: str database_path: Path database_name: str key: str source: str @dataclass class DeleteTarget: message_id: int text: str timestamp: str | None is_from_me: bool def parse_plist_xml(xml_text: str) -> Any: tokens = tokenize_plist_xml(xml_text) if not tokens: raise AuthResolutionError("plist XML was empty") index = 0 if tokens[index] != ("start", "plist"): raise AuthResolutionError("plist XML did not start with <plist>") value, index = _parse_plist_tokens(tokens, index + 1) if tokens[index] != ("end", "plist"): raise AuthResolutionError("plist XML did not end with </plist>") return value def tokenize_plist_xml(xml_text: str) -> list[tuple[str, str]]: normalized = re.sub(r"<\?xml[^>]*\?>", "", xml_text) normalized = re.sub(r"<!DOCTYPE[^>]*>", "", normalized) normalized = re.sub(r"<([A-Za-z0-9]+)\s*/>", r"<\1></\1>", normalized) normalized = ( normalized.replace("\r", "") ) tokens: list[tuple[str, str]] = [] position = 0 for match in re.finditer(r"<(/?)([A-Za-z0-9]+)(?: [^>]*)?>", normalized): text = normalized[position : match.start()] stripped = _unescape_xml(text).strip() if stripped: tokens.append(("text", stripped)) token_type = "end" if match.group(1) else "start" tokens.append((token_type, match.group(2))) position = match.end() trailing = _unescape_xml(normalized[position:]).strip() if trailing: tokens.append(("text", trailing)) return [token for token in tokens if token[0] != "text" or token[1]] def _parse_plist_tokens(tokens: list[tuple[str, str]], index: int) -> tuple[Any, int]: token_type, tag = tokens[index] if token_type != "start": raise AuthResolutionError(f"Unexpected token {tokens[index]!r}") if tag == "dict": result: dict[str, Any] = {} index += 1 while tokens[index] != ("end", "dict"): if tokens[index] != ("start", "key"): raise AuthResolutionError(f"Expected dict key, got {tokens[index]!r}") key, index = _parse_scalar(tokens, index, "key", lambda value: value) value, index = _parse_plist_tokens(tokens, index) result[key] = value return result, index + 1 if tag == "array": items: list[Any] = [] index += 1 while tokens[index] != ("end", "array"): value, index = _parse_plist_tokens(tokens, index) items.append(value) return items, index + 1 if tag == "integer": return _parse_scalar(tokens, index, "integer", int) if tag == "real": return _parse_scalar(tokens, index, "real", float) if tag == "string": return _parse_scalar(tokens, index, "string", lambda value: value) if tag == "date": re