kakaotalk-chat-analyzer 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +190 -0
- package/dist/src/analysis.d.ts +8 -0
- package/dist/src/analysis.js +406 -0
- package/dist/src/analysis.js.map +1 -0
- package/dist/src/cli.d.ts +2 -0
- package/dist/src/cli.js +177 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +19 -0
- package/dist/src/config.js +59 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/date.d.ts +7 -0
- package/dist/src/date.js +32 -0
- package/dist/src/date.js.map +1 -0
- package/dist/src/encoding.d.ts +5 -0
- package/dist/src/encoding.js +53 -0
- package/dist/src/encoding.js.map +1 -0
- package/dist/src/parser.d.ts +3 -0
- package/dist/src/parser.js +206 -0
- package/dist/src/parser.js.map +1 -0
- package/dist/src/providers/brewpage.d.ts +8 -0
- package/dist/src/providers/brewpage.js +96 -0
- package/dist/src/providers/brewpage.js.map +1 -0
- package/dist/src/providers/cloudflare.d.ts +5 -0
- package/dist/src/providers/cloudflare.js +7 -0
- package/dist/src/providers/cloudflare.js.map +1 -0
- package/dist/src/providers/index.d.ts +3 -0
- package/dist/src/providers/index.js +20 -0
- package/dist/src/providers/index.js.map +1 -0
- package/dist/src/providers/tempfile.d.ts +8 -0
- package/dist/src/providers/tempfile.js +60 -0
- package/dist/src/providers/tempfile.js.map +1 -0
- package/dist/src/providers/types.d.ts +32 -0
- package/dist/src/providers/types.js +2 -0
- package/dist/src/providers/types.js.map +1 -0
- package/dist/src/report.d.ts +2 -0
- package/dist/src/report.js +227 -0
- package/dist/src/report.js.map +1 -0
- package/dist/src/types.d.ts +97 -0
- package/dist/src/types.js +2 -0
- package/dist/src/types.js.map +1 -0
- package/dist/src/version.d.ts +2 -0
- package/dist/src/version.js +3 -0
- package/dist/src/version.js.map +1 -0
- package/package.json +51 -0
package/dist/src/cli.js
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { mkdir, stat, writeFile } from "node:fs/promises";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
import { buildReportData } from "./analysis.js";
|
|
6
|
+
import { clearOwnerToken, getConfigPath, getOwnerToken, saveOwnerToken } from "./config.js";
|
|
7
|
+
import { describeParseResult, parseKakaoExport } from "./parser.js";
|
|
8
|
+
import { createProvider, parseHostName } from "./providers/index.js";
|
|
9
|
+
import { renderReportHtml } from "./report.js";
|
|
10
|
+
import { VERSION } from "./version.js";
|
|
11
|
+
const DEFAULT_NAMESPACE = "kakao-chat-report";
|
|
12
|
+
const DEFAULT_OUT = ".tmp/kca-report";
|
|
13
|
+
const DEFAULT_TOP = 40;
|
|
14
|
+
const program = new Command();
|
|
15
|
+
program.name("kca").description("카카오톡 CSV 보내기 → 리포트 생성 → (선택) 임시 공유까지 한 번에.").version(VERSION);
|
|
16
|
+
program.addHelpText("after", `
|
|
17
|
+
기본 사용법 (서브커맨드 없이):
|
|
18
|
+
kca <보내기.csv> HTML 리포트 생성 후 BrewPage에 업로드
|
|
19
|
+
kca <보내기.csv> --local 업로드 없이 로컬에만 저장 (-o 로 폴더 지정)
|
|
20
|
+
kca <보내기.csv> --dry-run 업로드 생략(미리 생성만)
|
|
21
|
+
npx -y --package=kakaotalk-chat-analyzer@latest kca <보내기.csv> --local
|
|
22
|
+
`);
|
|
23
|
+
const main = program.command("default", { isDefault: true, hidden: true });
|
|
24
|
+
main
|
|
25
|
+
.argument("<csv>", "카카오톡 CSV 보내기 파일 경로")
|
|
26
|
+
.option("--local", "HTML만 만들고 업로드는 하지 않습니다.", false)
|
|
27
|
+
.option("--dry-run", "업로드를 생략하고 리포트만 생성합니다.", false)
|
|
28
|
+
.option("--host <host>", "brewpage, tempfile, cloudflare", "brewpage")
|
|
29
|
+
.option("--ttl <days>", "임시 호스팅 TTL(일)", "30")
|
|
30
|
+
.option("--ns <namespace>", "호스팅 네임스페이스", DEFAULT_NAMESPACE)
|
|
31
|
+
.option("--privacy <mode>", "public-masked | public-anonymous", "public-masked")
|
|
32
|
+
.option("--top <count>", "랭킹·상위 목록 길이", String(DEFAULT_TOP))
|
|
33
|
+
.option("-o, --out <dir>", "리포트 출력 폴더", DEFAULT_OUT)
|
|
34
|
+
.description("기본: 리포트 생성 후 BrewPage로 업로드(로컬만은 --local).")
|
|
35
|
+
.action(async (csv, options) => {
|
|
36
|
+
const host = parseHostName(options.host);
|
|
37
|
+
const ttlDays = parseTtl(options.ttl);
|
|
38
|
+
const namespace = sanitizeNamespace(options.ns);
|
|
39
|
+
const privacy = parsePrivacy(options.privacy);
|
|
40
|
+
const top = parsePositiveInt(options.top, DEFAULT_TOP);
|
|
41
|
+
const htmlPath = await generateReport(csv, { outDir: options.out, privacy, top });
|
|
42
|
+
console.log(`리포트: ${htmlPath}`);
|
|
43
|
+
console.log(`크기: ${await formatFileSize(htmlPath)}`);
|
|
44
|
+
if (options.local) {
|
|
45
|
+
console.log("--local: 업로드를 하지 않습니다.");
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
if (options.dryRun) {
|
|
49
|
+
console.log("드라이런: 업로드를 건너뜁니다.");
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const provider = createProvider(host);
|
|
54
|
+
const owner = await getOwnerToken(host, namespace);
|
|
55
|
+
const html = await readReportHtml(htmlPath);
|
|
56
|
+
const result = await provider.publish({
|
|
57
|
+
html,
|
|
58
|
+
ttlDays,
|
|
59
|
+
namespace,
|
|
60
|
+
title: "카카오톡 대화 리포트",
|
|
61
|
+
ownerToken: owner?.ownerToken,
|
|
62
|
+
});
|
|
63
|
+
if (result.ownerToken) {
|
|
64
|
+
await saveOwnerToken({
|
|
65
|
+
provider: result.provider,
|
|
66
|
+
namespace,
|
|
67
|
+
ownerToken: result.ownerToken,
|
|
68
|
+
ownerLink: result.ownerLink,
|
|
69
|
+
id: result.id,
|
|
70
|
+
link: result.link,
|
|
71
|
+
expiresAt: result.expiresAt,
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
printPublishResult(result, namespace);
|
|
75
|
+
}
|
|
76
|
+
catch (error) {
|
|
77
|
+
console.error(`업로드 실패: ${error instanceof Error ? error.message : String(error)}`);
|
|
78
|
+
console.error(`로컬 리포트는 그대로 있습니다: ${htmlPath}`);
|
|
79
|
+
if (host === "brewpage") {
|
|
80
|
+
console.error(`TempFile로 시도하려면: npx kakaotalk-chat-analyzer "${csv}" --host tempfile --ttl ${ttlDays}`);
|
|
81
|
+
}
|
|
82
|
+
process.exitCode = 1;
|
|
83
|
+
}
|
|
84
|
+
});
|
|
85
|
+
program
|
|
86
|
+
.command("inspect")
|
|
87
|
+
.argument("<csv>", "카카오톡 CSV 보내기")
|
|
88
|
+
.description("보내기 구조만 점검합니다(대화 원문은 출력하지 않음).")
|
|
89
|
+
.action(async (csv) => {
|
|
90
|
+
const result = await parseKakaoExport(resolve(csv));
|
|
91
|
+
console.log(describeParseResult(result));
|
|
92
|
+
if (result.warnings.length > 0) {
|
|
93
|
+
console.log("\n경고 상세:");
|
|
94
|
+
for (const warning of result.warnings.slice(0, 10)) {
|
|
95
|
+
console.log(`- ${warning.line}행: ${warning.code}`);
|
|
96
|
+
}
|
|
97
|
+
if (result.warnings.length > 10) {
|
|
98
|
+
console.log(`- … 외 ${result.warnings.length - 10}건`);
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
const token = program.command("token").description("로컬에 저장된 owner 토큰 관리.");
|
|
103
|
+
token
|
|
104
|
+
.command("clear")
|
|
105
|
+
.option("--host <host>", "brewpage | tempfile | cloudflare", "brewpage")
|
|
106
|
+
.option("--ns <namespace>", "네임스페이스", DEFAULT_NAMESPACE)
|
|
107
|
+
.description("저장된 owner 토큰을 삭제합니다.")
|
|
108
|
+
.action(async (options) => {
|
|
109
|
+
const host = parseHostName(options.host);
|
|
110
|
+
const namespace = sanitizeNamespace(options.ns);
|
|
111
|
+
const cleared = await clearOwnerToken(host, namespace);
|
|
112
|
+
console.log(cleared ? `${host}/${namespace} 토큰을 지웠습니다.` : `${host}/${namespace}에 저장된 토큰이 없습니다.`);
|
|
113
|
+
});
|
|
114
|
+
program.configureHelp({ sortSubcommands: true });
|
|
115
|
+
program.parseAsync(process.argv).catch((error) => {
|
|
116
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
117
|
+
process.exit(1);
|
|
118
|
+
});
|
|
119
|
+
async function generateReport(csv, options) {
|
|
120
|
+
const result = await parseKakaoExport(resolve(csv));
|
|
121
|
+
const data = buildReportData(result, { privacy: options.privacy, top: options.top });
|
|
122
|
+
const html = renderReportHtml(data);
|
|
123
|
+
const outDir = resolve(options.outDir);
|
|
124
|
+
const htmlPath = resolve(outDir, "index.html");
|
|
125
|
+
await mkdir(outDir, { recursive: true });
|
|
126
|
+
await writeFile(htmlPath, html, "utf8");
|
|
127
|
+
return htmlPath;
|
|
128
|
+
}
|
|
129
|
+
async function readReportHtml(htmlPath) {
|
|
130
|
+
const { readFile } = await import("node:fs/promises");
|
|
131
|
+
return readFile(htmlPath, "utf8");
|
|
132
|
+
}
|
|
133
|
+
function parsePrivacy(value) {
|
|
134
|
+
if (value === "public-masked" || value === "public-anonymous")
|
|
135
|
+
return value;
|
|
136
|
+
throw new Error(`지원하지 않는 privacy 모드입니다: "${value}". public-masked 또는 public-anonymous 만 사용할 수 있습니다.`);
|
|
137
|
+
}
|
|
138
|
+
function parseTtl(value) {
|
|
139
|
+
const ttl = parsePositiveInt(value, 30);
|
|
140
|
+
return Math.max(1, Math.min(30, ttl));
|
|
141
|
+
}
|
|
142
|
+
function parsePositiveInt(value, fallback) {
|
|
143
|
+
const parsed = Number.parseInt(value, 10);
|
|
144
|
+
return Number.isFinite(parsed) && parsed > 0 ? parsed : fallback;
|
|
145
|
+
}
|
|
146
|
+
function sanitizeNamespace(value) {
|
|
147
|
+
const normalized = value.toLowerCase().trim();
|
|
148
|
+
if (!/^[a-z0-9-]{3,32}$/.test(normalized)) {
|
|
149
|
+
throw new Error("네임스페이스는 [a-z0-9-]{3,32} 형식이어야 합니다.");
|
|
150
|
+
}
|
|
151
|
+
return normalized;
|
|
152
|
+
}
|
|
153
|
+
async function formatFileSize(filePath) {
|
|
154
|
+
const size = (await stat(filePath)).size;
|
|
155
|
+
if (size < 1024)
|
|
156
|
+
return `${size} B`;
|
|
157
|
+
if (size < 1024 * 1024)
|
|
158
|
+
return `${(size / 1024).toFixed(1)} KiB`;
|
|
159
|
+
return `${(size / 1024 / 1024).toFixed(2)} MiB`;
|
|
160
|
+
}
|
|
161
|
+
function printPublishResult(result, namespace) {
|
|
162
|
+
console.log(`공유 URL: ${result.link}`);
|
|
163
|
+
if (result.expiresAt)
|
|
164
|
+
console.log(`만료: ${result.expiresAt}`);
|
|
165
|
+
if (result.ownerLink)
|
|
166
|
+
console.log(`관리(삭제) 링크: ${result.ownerLink}`);
|
|
167
|
+
if (result.ownerToken)
|
|
168
|
+
console.log(`Owner 토큰: ${maskToken(result.ownerToken)} (${getConfigPath()}에 저장됨)`);
|
|
169
|
+
console.log(`호스트: ${result.provider}`);
|
|
170
|
+
console.log(`네임스페이스: ${namespace}`);
|
|
171
|
+
}
|
|
172
|
+
function maskToken(token) {
|
|
173
|
+
if (token.length <= 10)
|
|
174
|
+
return "********";
|
|
175
|
+
return `${token.slice(0, 4)}...${token.slice(-4)}`;
|
|
176
|
+
}
|
|
177
|
+
//# sourceMappingURL=cli.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../../src/cli.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
export interface StoredOwnerToken {
|
|
2
|
+
provider: string;
|
|
3
|
+
namespace: string;
|
|
4
|
+
ownerToken: string;
|
|
5
|
+
ownerLink?: string;
|
|
6
|
+
id?: string;
|
|
7
|
+
link?: string;
|
|
8
|
+
expiresAt?: string;
|
|
9
|
+
savedAt: string;
|
|
10
|
+
}
|
|
11
|
+
interface KcaConfig {
|
|
12
|
+
ownerTokens?: Record<string, StoredOwnerToken>;
|
|
13
|
+
}
|
|
14
|
+
export declare function getConfigPath(): string;
|
|
15
|
+
export declare function loadConfig(path?: string): Promise<KcaConfig>;
|
|
16
|
+
export declare function saveOwnerToken(token: Omit<StoredOwnerToken, "savedAt">, path?: string): Promise<void>;
|
|
17
|
+
export declare function getOwnerToken(provider: string, namespace: string, path?: string): Promise<StoredOwnerToken | null>;
|
|
18
|
+
export declare function clearOwnerToken(provider: string, namespace: string, path?: string): Promise<boolean>;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { mkdir, readFile, writeFile } from "node:fs/promises";
|
|
2
|
+
import { dirname, join } from "node:path";
|
|
3
|
+
import { homedir } from "node:os";
|
|
4
|
+
export function getConfigPath() {
|
|
5
|
+
const explicit = process.env.KCA_CONFIG_HOME;
|
|
6
|
+
if (explicit)
|
|
7
|
+
return join(explicit, "config.json");
|
|
8
|
+
if (process.platform === "win32" && process.env.APPDATA) {
|
|
9
|
+
return join(process.env.APPDATA, "kakaotalk-chat-analyzer", "config.json");
|
|
10
|
+
}
|
|
11
|
+
const xdg = process.env.XDG_CONFIG_HOME;
|
|
12
|
+
if (xdg)
|
|
13
|
+
return join(xdg, "kakaotalk-chat-analyzer", "config.json");
|
|
14
|
+
return join(homedir(), ".config", "kakaotalk-chat-analyzer", "config.json");
|
|
15
|
+
}
|
|
16
|
+
export async function loadConfig(path = getConfigPath()) {
|
|
17
|
+
try {
|
|
18
|
+
const raw = await readFile(path, "utf8");
|
|
19
|
+
const parsed = JSON.parse(raw);
|
|
20
|
+
return parsed && typeof parsed === "object" ? parsed : {};
|
|
21
|
+
}
|
|
22
|
+
catch (error) {
|
|
23
|
+
if (error && typeof error === "object" && "code" in error && error.code === "ENOENT") {
|
|
24
|
+
return {};
|
|
25
|
+
}
|
|
26
|
+
throw error;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
export async function saveOwnerToken(token, path = getConfigPath()) {
|
|
30
|
+
const config = await loadConfig(path);
|
|
31
|
+
const key = tokenKey(token.provider, token.namespace);
|
|
32
|
+
config.ownerTokens = {
|
|
33
|
+
...(config.ownerTokens ?? {}),
|
|
34
|
+
[key]: {
|
|
35
|
+
...token,
|
|
36
|
+
savedAt: new Date().toISOString(),
|
|
37
|
+
},
|
|
38
|
+
};
|
|
39
|
+
await mkdir(dirname(path), { recursive: true });
|
|
40
|
+
await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
41
|
+
}
|
|
42
|
+
export async function getOwnerToken(provider, namespace, path = getConfigPath()) {
|
|
43
|
+
const config = await loadConfig(path);
|
|
44
|
+
return config.ownerTokens?.[tokenKey(provider, namespace)] ?? null;
|
|
45
|
+
}
|
|
46
|
+
export async function clearOwnerToken(provider, namespace, path = getConfigPath()) {
|
|
47
|
+
const config = await loadConfig(path);
|
|
48
|
+
const key = tokenKey(provider, namespace);
|
|
49
|
+
if (!config.ownerTokens?.[key])
|
|
50
|
+
return false;
|
|
51
|
+
delete config.ownerTokens[key];
|
|
52
|
+
await mkdir(dirname(path), { recursive: true });
|
|
53
|
+
await writeFile(path, `${JSON.stringify(config, null, 2)}\n`, { encoding: "utf8", mode: 0o600 });
|
|
54
|
+
return true;
|
|
55
|
+
}
|
|
56
|
+
function tokenKey(provider, namespace) {
|
|
57
|
+
return `${provider}:${namespace}`;
|
|
58
|
+
}
|
|
59
|
+
//# sourceMappingURL=config.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAC9D,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAiBlC,MAAM,UAAU,aAAa;IAC3B,MAAM,QAAQ,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IAC7C,IAAI,QAAQ;QAAE,OAAO,IAAI,CAAC,QAAQ,EAAE,aAAa,CAAC,CAAC;IAEnD,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,IAAI,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;QACxD,OAAO,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,EAAE,yBAAyB,EAAE,aAAa,CAAC,CAAC;IAC7E,CAAC;IAED,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,CAAC,eAAe,CAAC;IACxC,IAAI,GAAG;QAAE,OAAO,IAAI,CAAC,GAAG,EAAE,yBAAyB,EAAE,aAAa,CAAC,CAAC;IAEpE,OAAO,IAAI,CAAC,OAAO,EAAE,EAAE,SAAS,EAAE,yBAAyB,EAAE,aAAa,CAAC,CAAC;AAC9E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,UAAU,CAAC,IAAI,GAAG,aAAa,EAAE;IACrD,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;QACzC,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAc,CAAC;QAC5C,OAAO,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC;IAC5D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,IAAI,KAAK,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;YACrF,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,cAAc,CAAC,KAAwC,EAAE,IAAI,GAAG,aAAa,EAAE;IACnG,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,KAAK,CAAC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;IACtD,MAAM,CAAC,WAAW,GAAG;QACnB,GAAG,CAAC,MAAM,CAAC,WAAW,IAAI,EAAE,CAAC;QAC7B,CAAC,GAAG,CAAC,EAAE;YACL,GAAG,KAAK;YACR,OAAO,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;SAClC;KACF,CAAC;IAEF,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;AACnG,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,QAAgB,EAAE,SAAiB,EAAE,IAAI,GAAG,aAAa,EAAE;IAC7F,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,OAAO,MAAM,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC,IAAI,IAAI,CAAC;AACrE,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,QAAgB,EAAE,SAAiB,EAAE,IAAI,GAAG,aAAa,EAAE;IAC/F,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,IAAI,CAAC,CAAC;IACtC,MAAM,GAAG,GAAG,QAAQ,CAAC,QAAQ,EAAE,SAAS,CAAC,CAAC;IAC1C,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IAE7C,OAAO,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC;IAC/B,MAAM,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IAChD,MAAM,SAAS,CAAC,IAAI,EAAE,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,IAAI,EAAE,CAAC,CAAC,IAAI,EAAE,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,CAAC,CAAC;IACjG,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,QAAQ,CAAC,QAAgB,EAAE,SAAiB;IACnD,OAAO,GAAG,QAAQ,IAAI,SAAS,EAAE,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { ParsedDateParts } from "./types.js";
|
|
2
|
+
export declare function parseKakaoDate(raw: string): ParsedDateParts | null;
|
|
3
|
+
export declare function formatDateTime(parts: ParsedDateParts): string;
|
|
4
|
+
export declare function formatDate(parts: ParsedDateParts): string;
|
|
5
|
+
export declare function weekdayIndex(parts: ParsedDateParts): number;
|
|
6
|
+
/** UTC 기준 타임스탬프(ms). 연속 일수·응답 간격 계산에 사용합니다. */
|
|
7
|
+
export declare function partsToUtcMs(parts: ParsedDateParts): number;
|
package/dist/src/date.js
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
const DATE_RE = /^(\d{4})-(\d{2})-(\d{2}) (\d{2}):(\d{2}):(\d{2})$/;
|
|
2
|
+
export function parseKakaoDate(raw) {
|
|
3
|
+
const match = DATE_RE.exec(raw);
|
|
4
|
+
if (!match)
|
|
5
|
+
return null;
|
|
6
|
+
const [, year, month, day, hour, minute, second] = match;
|
|
7
|
+
return {
|
|
8
|
+
year: Number(year),
|
|
9
|
+
month: Number(month),
|
|
10
|
+
day: Number(day),
|
|
11
|
+
hour: Number(hour),
|
|
12
|
+
minute: Number(minute),
|
|
13
|
+
second: Number(second),
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
export function formatDateTime(parts) {
|
|
17
|
+
return `${parts.year}-${pad2(parts.month)}-${pad2(parts.day)} ${pad2(parts.hour)}:${pad2(parts.minute)}:${pad2(parts.second)}`;
|
|
18
|
+
}
|
|
19
|
+
export function formatDate(parts) {
|
|
20
|
+
return `${parts.year}-${pad2(parts.month)}-${pad2(parts.day)}`;
|
|
21
|
+
}
|
|
22
|
+
export function weekdayIndex(parts) {
|
|
23
|
+
return new Date(Date.UTC(parts.year, parts.month - 1, parts.day)).getUTCDay();
|
|
24
|
+
}
|
|
25
|
+
/** UTC 기준 타임스탬프(ms). 연속 일수·응답 간격 계산에 사용합니다. */
|
|
26
|
+
export function partsToUtcMs(parts) {
|
|
27
|
+
return Date.UTC(parts.year, parts.month - 1, parts.day, parts.hour, parts.minute, parts.second);
|
|
28
|
+
}
|
|
29
|
+
function pad2(value) {
|
|
30
|
+
return value.toString().padStart(2, "0");
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=date.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"date.js","sourceRoot":"","sources":["../../src/date.ts"],"names":[],"mappings":"AAEA,MAAM,OAAO,GAAG,mDAAmD,CAAC;AAEpE,MAAM,UAAU,cAAc,CAAC,GAAW;IACxC,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAChC,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAC;IAExB,MAAM,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,GAAG,KAAK,CAAC;IACzD,OAAO;QACL,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;QAClB,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC;QACpB,GAAG,EAAE,MAAM,CAAC,GAAG,CAAC;QAChB,IAAI,EAAE,MAAM,CAAC,IAAI,CAAC;QAClB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;QACtB,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;KACvB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,KAAsB;IACnD,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;AACjI,CAAC;AAED,MAAM,UAAU,UAAU,CAAC,KAAsB;IAC/C,OAAO,GAAG,KAAK,CAAC,IAAI,IAAI,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,EAAE,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,KAAsB;IACjD,OAAO,IAAI,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,SAAS,EAAE,CAAC;AAChF,CAAC;AAED,+CAA+C;AAC/C,MAAM,UAAU,YAAY,CAAC,KAAsB;IACjD,OAAO,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,KAAK,GAAG,CAAC,EAAE,KAAK,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,CAAC;AAClG,CAAC;AAED,SAAS,IAAI,CAAC,KAAa;IACzB,OAAO,KAAK,CAAC,QAAQ,EAAE,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;AAC3C,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { TextDecoder } from "node:util";
|
|
2
|
+
import iconv from "iconv-lite";
|
|
3
|
+
const UTF8_BOM = Buffer.from([0xef, 0xbb, 0xbf]);
|
|
4
|
+
export function decodeChatExport(bytes) {
|
|
5
|
+
if (bytes.subarray(0, 3).equals(UTF8_BOM)) {
|
|
6
|
+
return {
|
|
7
|
+
encoding: "utf-8-bom",
|
|
8
|
+
text: bytes.subarray(3).toString("utf8"),
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
const candidates = [];
|
|
12
|
+
const utf8 = decodeUtf8(bytes);
|
|
13
|
+
if (utf8) {
|
|
14
|
+
candidates.push({ encoding: "utf-8", text: utf8, score: scoreDecodedText(utf8) });
|
|
15
|
+
}
|
|
16
|
+
for (const encoding of ["cp949", "euc-kr"]) {
|
|
17
|
+
const text = iconv.decode(bytes, encoding);
|
|
18
|
+
candidates.push({ encoding, text, score: scoreDecodedText(text) });
|
|
19
|
+
}
|
|
20
|
+
candidates.sort((a, b) => b.score - a.score);
|
|
21
|
+
const best = candidates[0];
|
|
22
|
+
if (!best) {
|
|
23
|
+
throw new Error("Unable to decode file with utf-8, cp949, or euc-kr.");
|
|
24
|
+
}
|
|
25
|
+
return { encoding: best.encoding, text: stripBom(best.text) };
|
|
26
|
+
}
|
|
27
|
+
function decodeUtf8(bytes) {
|
|
28
|
+
try {
|
|
29
|
+
return new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
function stripBom(text) {
|
|
36
|
+
return text.charCodeAt(0) === 0xfeff ? text.slice(1) : text;
|
|
37
|
+
}
|
|
38
|
+
function scoreDecodedText(text) {
|
|
39
|
+
const sample = text.slice(0, 128 * 1024);
|
|
40
|
+
let score = 0;
|
|
41
|
+
if (/^Date,User,Message\b/m.test(sample))
|
|
42
|
+
score += 1000;
|
|
43
|
+
if (/^날짜,/.test(sample))
|
|
44
|
+
score += 500;
|
|
45
|
+
const dateMatches = sample.match(/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},/gm)?.length ?? 0;
|
|
46
|
+
score += dateMatches * 10;
|
|
47
|
+
const replacementChars = (sample.match(/\uFFFD/g) ?? []).length;
|
|
48
|
+
score -= replacementChars * 50;
|
|
49
|
+
const hangulChars = (sample.match(/[가-힣]/g) ?? []).length;
|
|
50
|
+
score += Math.min(hangulChars, 500);
|
|
51
|
+
return score;
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=encoding.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"encoding.js","sourceRoot":"","sources":["../../src/encoding.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { readFile } from "node:fs/promises";
|
|
2
|
+
import { basename } from "node:path";
|
|
3
|
+
import { parse as parseCsv } from "csv-parse/sync";
|
|
4
|
+
import { decodeChatExport } from "./encoding.js";
|
|
5
|
+
import { parseKakaoDate } from "./date.js";
|
|
6
|
+
const DATE_LINE_RE = /^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2},/;
|
|
7
|
+
export async function parseKakaoExport(filePath) {
|
|
8
|
+
const bytes = await readFile(filePath);
|
|
9
|
+
const decoded = decodeChatExport(bytes);
|
|
10
|
+
const physicalLines = splitPhysicalLines(decoded.text);
|
|
11
|
+
const warnings = [];
|
|
12
|
+
const headerLine = physicalLines.shift();
|
|
13
|
+
if (!headerLine) {
|
|
14
|
+
throw new Error(`Empty KakaoTalk export: ${filePath}`);
|
|
15
|
+
}
|
|
16
|
+
const header = parseHeader(headerLine, warnings);
|
|
17
|
+
const records = [];
|
|
18
|
+
let current = null;
|
|
19
|
+
for (let index = 0; index < physicalLines.length; index += 1) {
|
|
20
|
+
const lineNumber = index + 2;
|
|
21
|
+
const line = physicalLines[index] ?? "";
|
|
22
|
+
if (DATE_LINE_RE.test(line)) {
|
|
23
|
+
if (current)
|
|
24
|
+
records.push(current);
|
|
25
|
+
current = parseRecordStart(line, lineNumber, warnings);
|
|
26
|
+
continue;
|
|
27
|
+
}
|
|
28
|
+
if (!current) {
|
|
29
|
+
if (line.trim().length > 0) {
|
|
30
|
+
warnings.push({
|
|
31
|
+
line: lineNumber,
|
|
32
|
+
code: "continuation_without_record",
|
|
33
|
+
message: "Found a continuation line before the first dated message.",
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
current.message += `\n${line}`;
|
|
39
|
+
}
|
|
40
|
+
if (current)
|
|
41
|
+
records.push(current);
|
|
42
|
+
return {
|
|
43
|
+
filePath,
|
|
44
|
+
encoding: decoded.encoding,
|
|
45
|
+
physicalLines: physicalLines.length + 1,
|
|
46
|
+
records,
|
|
47
|
+
warnings,
|
|
48
|
+
header,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
function splitPhysicalLines(text) {
|
|
52
|
+
const normalized = text.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
53
|
+
const lines = normalized.split("\n");
|
|
54
|
+
if (normalized.endsWith("\n")) {
|
|
55
|
+
lines.pop();
|
|
56
|
+
}
|
|
57
|
+
return lines;
|
|
58
|
+
}
|
|
59
|
+
function parseHeader(line, warnings) {
|
|
60
|
+
const header = parseCsvLine(line, 1, warnings);
|
|
61
|
+
const normalized = header.map((field) => field.trim());
|
|
62
|
+
const expected = ["Date", "User", "Message"];
|
|
63
|
+
if (normalized.length < 3 || expected.some((value, index) => normalized[index] !== value)) {
|
|
64
|
+
warnings.push({
|
|
65
|
+
line: 1,
|
|
66
|
+
code: "unexpected_header",
|
|
67
|
+
message: `Expected Date,User,Message header but found ${normalized.join(",") || "(empty)"}.`,
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
return normalized;
|
|
71
|
+
}
|
|
72
|
+
function parseRecordStart(line, lineNumber, warnings) {
|
|
73
|
+
const { rawDate, sender, message } = parseRecordStartLine(line, lineNumber, warnings);
|
|
74
|
+
const date = parseKakaoDate(rawDate);
|
|
75
|
+
if (!date) {
|
|
76
|
+
warnings.push({
|
|
77
|
+
line: lineNumber,
|
|
78
|
+
code: "invalid_date",
|
|
79
|
+
message: `Could not parse message date on line ${lineNumber}.`,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
return {
|
|
83
|
+
line: lineNumber,
|
|
84
|
+
rawDate,
|
|
85
|
+
date: date ?? { year: 1970, month: 1, day: 1, hour: 0, minute: 0, second: 0 },
|
|
86
|
+
sender,
|
|
87
|
+
message,
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
function parseRecordStartLine(line, lineNumber, warnings) {
|
|
91
|
+
const firstComma = line.indexOf(",");
|
|
92
|
+
if (firstComma === -1) {
|
|
93
|
+
warnings.push({
|
|
94
|
+
line: lineNumber,
|
|
95
|
+
code: "record_start_parse_error",
|
|
96
|
+
message: "Dated record line did not contain a field delimiter.",
|
|
97
|
+
});
|
|
98
|
+
return { rawDate: line, sender: "", message: "" };
|
|
99
|
+
}
|
|
100
|
+
const rawDate = line.slice(0, firstComma);
|
|
101
|
+
const rest = line.slice(firstComma + 1);
|
|
102
|
+
const senderResult = readCsvField(rest);
|
|
103
|
+
if (!senderResult) {
|
|
104
|
+
warnings.push({
|
|
105
|
+
line: lineNumber,
|
|
106
|
+
code: "sender_parse_error",
|
|
107
|
+
message: "Could not parse sender field from dated record line.",
|
|
108
|
+
});
|
|
109
|
+
const secondComma = rest.indexOf(",");
|
|
110
|
+
return {
|
|
111
|
+
rawDate,
|
|
112
|
+
sender: secondComma === -1 ? rest : rest.slice(0, secondComma),
|
|
113
|
+
message: secondComma === -1 ? "" : rest.slice(secondComma + 1),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
const messageStart = rest[senderResult.nextIndex] === "," ? senderResult.nextIndex + 1 : senderResult.nextIndex;
|
|
117
|
+
return {
|
|
118
|
+
rawDate,
|
|
119
|
+
sender: senderResult.value,
|
|
120
|
+
message: unquoteCompleteCsvField(rest.slice(messageStart)),
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
function readCsvField(input) {
|
|
124
|
+
if (!input.startsWith('"')) {
|
|
125
|
+
const comma = input.indexOf(",");
|
|
126
|
+
return {
|
|
127
|
+
value: comma === -1 ? input : input.slice(0, comma),
|
|
128
|
+
nextIndex: comma === -1 ? input.length : comma,
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
let value = "";
|
|
132
|
+
for (let index = 1; index < input.length; index += 1) {
|
|
133
|
+
const char = input[index];
|
|
134
|
+
if (char !== '"') {
|
|
135
|
+
value += char;
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
if (input[index + 1] === '"') {
|
|
139
|
+
value += '"';
|
|
140
|
+
index += 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
return { value, nextIndex: index + 1 };
|
|
144
|
+
}
|
|
145
|
+
return null;
|
|
146
|
+
}
|
|
147
|
+
function parseCsvLine(line, lineNumber, warnings) {
|
|
148
|
+
try {
|
|
149
|
+
const rows = parseCsv(line, {
|
|
150
|
+
bom: true,
|
|
151
|
+
relax_column_count: true,
|
|
152
|
+
relax_quotes: true,
|
|
153
|
+
skip_empty_lines: false,
|
|
154
|
+
});
|
|
155
|
+
const fields = rows[0] ?? [];
|
|
156
|
+
return fields.map((field) => String(field));
|
|
157
|
+
}
|
|
158
|
+
catch (error) {
|
|
159
|
+
warnings.push({
|
|
160
|
+
line: lineNumber,
|
|
161
|
+
code: "csv_parse_error",
|
|
162
|
+
message: error instanceof Error ? error.message : String(error),
|
|
163
|
+
});
|
|
164
|
+
return fallbackSplit(line);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
function fallbackSplit(line) {
|
|
168
|
+
const firstComma = line.indexOf(",");
|
|
169
|
+
if (firstComma === -1)
|
|
170
|
+
return [line];
|
|
171
|
+
const secondComma = line.indexOf(",", firstComma + 1);
|
|
172
|
+
if (secondComma === -1)
|
|
173
|
+
return [line.slice(0, firstComma), line.slice(firstComma + 1)];
|
|
174
|
+
return [line.slice(0, firstComma), unquote(line.slice(firstComma + 1, secondComma)), line.slice(secondComma + 1)];
|
|
175
|
+
}
|
|
176
|
+
function unquote(value) {
|
|
177
|
+
if (value.startsWith('"') && value.endsWith('"')) {
|
|
178
|
+
return value.slice(1, -1).replace(/""/g, '"');
|
|
179
|
+
}
|
|
180
|
+
return value;
|
|
181
|
+
}
|
|
182
|
+
function unquoteCompleteCsvField(value) {
|
|
183
|
+
const parsed = readCsvField(value);
|
|
184
|
+
if (!parsed || parsed.nextIndex !== value.length) {
|
|
185
|
+
return value;
|
|
186
|
+
}
|
|
187
|
+
return parsed.value;
|
|
188
|
+
}
|
|
189
|
+
export function describeParseResult(result) {
|
|
190
|
+
const participants = new Set(result.records.map((record) => record.sender));
|
|
191
|
+
const continuationRecords = result.records.filter((record) => record.message.includes("\n")).length;
|
|
192
|
+
const first = result.records[0]?.rawDate ?? "(none)";
|
|
193
|
+
const last = result.records.at(-1)?.rawDate ?? "(none)";
|
|
194
|
+
return [
|
|
195
|
+
`File: ${basename(result.filePath)}`,
|
|
196
|
+
`Encoding: ${result.encoding}`,
|
|
197
|
+
`Header: ${result.header.join(",")}`,
|
|
198
|
+
`Physical lines: ${result.physicalLines}`,
|
|
199
|
+
`Messages: ${result.records.length}`,
|
|
200
|
+
`Participants: ${participants.size}`,
|
|
201
|
+
`Range: ${first} -> ${last}`,
|
|
202
|
+
`Multiline messages: ${continuationRecords}`,
|
|
203
|
+
`Warnings: ${result.warnings.length}`,
|
|
204
|
+
].join("\n");
|
|
205
|
+
}
|
|
206
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../../src/parser.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { FetchLike, HostName, PublishProvider, PublishRequest, PublishResult } from "./types.js";
|
|
2
|
+
export declare class BrewPageProvider implements PublishProvider {
|
|
3
|
+
private readonly fetchImpl;
|
|
4
|
+
private readonly endpoint;
|
|
5
|
+
readonly name: HostName;
|
|
6
|
+
constructor(fetchImpl?: FetchLike, endpoint?: string);
|
|
7
|
+
publish(request: PublishRequest): Promise<PublishResult>;
|
|
8
|
+
}
|