kakaotalk-chat-analyzer 0.2.21 → 0.2.24

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.
@@ -0,0 +1,272 @@
1
+ /** 채팅·구어 불용어·접속사·대명사·짧은 응답 */
2
+ export const KOREAN_CHAT_STOPWORDS = new Set([
3
+ "그리고",
4
+ "그냥",
5
+ "근데",
6
+ "그런데",
7
+ "그래서",
8
+ "그러면",
9
+ "그러니까",
10
+ "그럼",
11
+ "그래",
12
+ "저는",
13
+ "제가",
14
+ "우리",
15
+ "너희",
16
+ "자기",
17
+ "본인",
18
+ "오늘",
19
+ "내일",
20
+ "어제",
21
+ "지금",
22
+ "방금",
23
+ "이거",
24
+ "저거",
25
+ "그거",
26
+ "여기",
27
+ "거기",
28
+ "이게",
29
+ "저게",
30
+ "그게",
31
+ "뭔데",
32
+ "뭐야",
33
+ "뭐임",
34
+ "뭐냐",
35
+ "왜요",
36
+ "왜냐",
37
+ "어떻게",
38
+ "어떡해",
39
+ "아니",
40
+ "아냐",
41
+ "맞아",
42
+ "맞지",
43
+ "맞음",
44
+ "맞고",
45
+ "응",
46
+ "웅",
47
+ "네",
48
+ "예",
49
+ "아",
50
+ "어",
51
+ "음",
52
+ "흠",
53
+ "좀",
54
+ "조금",
55
+ "진짜",
56
+ "정말",
57
+ "완전",
58
+ "되게",
59
+ "엄청",
60
+ "약간",
61
+ "그냥",
62
+ "일단",
63
+ "일단은",
64
+ "사실",
65
+ "근본",
66
+ "솔직히",
67
+ "아마",
68
+ "혹시",
69
+ "그냥",
70
+ "수정",
71
+ "확인",
72
+ "가능",
73
+ "입니다",
74
+ "합니다",
75
+ "있습니다",
76
+ "없습니다",
77
+ "했습니다",
78
+ "해요",
79
+ "이에요",
80
+ "예요",
81
+ "거예요",
82
+ "있는",
83
+ "없는",
84
+ "하는",
85
+ "하다",
86
+ "했다",
87
+ "해서",
88
+ "하면",
89
+ "하니",
90
+ "하네",
91
+ "하냐",
92
+ "하냐고",
93
+ "하자",
94
+ "하지",
95
+ "하지마",
96
+ "하지말고",
97
+ "같아",
98
+ "같고",
99
+ "같은",
100
+ "같음",
101
+ "같네",
102
+ "인데",
103
+ "인거",
104
+ "인것",
105
+ "것",
106
+ "거",
107
+ "수",
108
+ "때",
109
+ "중",
110
+ "뿐",
111
+ "정도",
112
+ "정도임",
113
+ "정도네",
114
+ "the",
115
+ "and",
116
+ "for",
117
+ "with",
118
+ "this",
119
+ "that",
120
+ "from",
121
+ "http",
122
+ "https",
123
+ "정치성향",
124
+ "강퇴됩니다",
125
+ "가려짐",
126
+ "초중반",
127
+ "비속어",
128
+ "반가워",
129
+ "닉네임",
130
+ "들어왔습니다",
131
+ "나갔습니다",
132
+ "삭제되었습니다",
133
+ "가렸습니다",
134
+ "보냈습니다",
135
+ "리얼",
136
+ "오케이",
137
+ "인정",
138
+ "노노",
139
+ "죄송",
140
+ "감사",
141
+ "축하",
142
+ "미친",
143
+ "레전드",
144
+ "노답",
145
+ "사람",
146
+ "사람들",
147
+ "내가",
148
+ "생각",
149
+ "같아요",
150
+ "조건",
151
+ "능력",
152
+ "com",
153
+ "youtu",
154
+ "www",
155
+ "node",
156
+ "si",
157
+ "보룸",
158
+ ]);
159
+ /** 단독 토큰으로만 걸러지는 1~2글자 채팅 반응·자모 */
160
+ export const REACTION_ONLY_RE = /^(?:[ㅋㅎㅠㅜㅇㅂㅅㅈㅇㅋㅍㅊ]+|[ㄱ-ㅎㅏ-ㅣ]+|[ㅋㅎ]{1,2})$/u;
161
+ /** 조사 분할·n-gram에서 떨어진 어미 조각 (단독 키워드 금지) */
162
+ export const MORPHOLOGICAL_FRAGMENTS = new Set([
163
+ "니다",
164
+ "습니다",
165
+ "습니",
166
+ "합니",
167
+ "입니다",
168
+ "해요",
169
+ "했어",
170
+ "하세",
171
+ "세요",
172
+ "하세요",
173
+ "으세요",
174
+ "네요",
175
+ "거예",
176
+ "이에",
177
+ "으로",
178
+ "로서",
179
+ "에서",
180
+ "부터",
181
+ "까지",
182
+ "에게",
183
+ "한테",
184
+ "라고",
185
+ "다고",
186
+ "이라",
187
+ "아니",
188
+ "아니라",
189
+ "는데",
190
+ "인데",
191
+ "하게",
192
+ "하지",
193
+ "하면",
194
+ "해서",
195
+ "하니",
196
+ "하네",
197
+ "하냐",
198
+ "적으",
199
+ "적이",
200
+ "적으로",
201
+ "입니",
202
+ "있습",
203
+ "없습",
204
+ "됩니",
205
+ "합니",
206
+ "같습",
207
+ "것입",
208
+ "수있",
209
+ "할수",
210
+ "하는",
211
+ "있는",
212
+ "없는",
213
+ "된다",
214
+ "된다",
215
+ "된다",
216
+ "했던",
217
+ "했음",
218
+ "했을",
219
+ "할것",
220
+ "것같",
221
+ "같은",
222
+ "같아",
223
+ "보이",
224
+ "보는",
225
+ "대한",
226
+ "관련",
227
+ "통해",
228
+ "위해",
229
+ "때문",
230
+ "정도",
231
+ "정말",
232
+ "진짜",
233
+ "너무",
234
+ "완전",
235
+ "이거",
236
+ "그거",
237
+ "저거",
238
+ "하는데",
239
+ "하는거",
240
+ "하는게",
241
+ "하세요",
242
+ "주세요",
243
+ "인가요",
244
+ "이라고",
245
+ "잖아요",
246
+ "그렇게",
247
+ "아님",
248
+ "그건",
249
+ "이제",
250
+ "무슨",
251
+ "많이",
252
+ "사람이",
253
+ "때문에",
254
+ "있는데",
255
+ "같은데",
256
+ "마세요",
257
+ "하시는",
258
+ "생각하",
259
+ "다들",
260
+ "했는데",
261
+ "겠습니",
262
+ "한다고",
263
+ "그런",
264
+ "우리나",
265
+ "공산주",
266
+ "건가요",
267
+ ]);
268
+ /** 동사 활용 꼬리만 남은 3~4글자 (하는X) */
269
+ export const VERB_FRAGMENT_RE = /^하는[a-z가-힣]{0,2}$/u;
270
+ /** 2글자인데 어미/조사 꼬리만 남은 형태 */
271
+ export const FRAGMENT_TAIL_RE = /(?:다|요|음|네|죠|임|함|라|가|는|은|을|를|의|에|와|과|도|만|서|고|니|지|게)$/u;
272
+ //# sourceMappingURL=korean-stopwords.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"korean-stopwords.js","sourceRoot":"","sources":["../../src/korean-stopwords.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,37 @@
1
+ export type SubwordPos = "L" | "R";
2
+ export type SubwordToken = readonly [word: string, pos: SubwordPos];
3
+ export interface KrWordRankOptions {
4
+ minCount?: number;
5
+ maxLength?: number;
6
+ beta?: number;
7
+ maxIter?: number;
8
+ numRset?: number;
9
+ converge?: number;
10
+ }
11
+ export interface KrWordRankExtractOptions {
12
+ stopwords?: ReadonlySet<string>;
13
+ limit?: number;
14
+ }
15
+ /** 메시지 스트림으로 학습 → HITS → L-부분 키워드 */
16
+ export declare class KrWordRankStream {
17
+ private minCount;
18
+ private readonly maxLength;
19
+ private readonly beta;
20
+ private readonly maxIter;
21
+ private readonly numRset;
22
+ private readonly converge;
23
+ private readonly counter;
24
+ private readonly edgeCounts;
25
+ private documents;
26
+ constructor(options?: KrWordRankOptions);
27
+ addDocument(raw: string): void;
28
+ extractKeywords(options?: KrWordRankExtractOptions): Map<string, number>;
29
+ private scanToken;
30
+ private bumpCounter;
31
+ private pruneCounter;
32
+ private bumpEdge;
33
+ private buildVocabulary;
34
+ private buildGraph;
35
+ }
36
+ /** 메시지 수에 따른 min_count (소규모 방 완화) */
37
+ export declare function adaptiveMinCount(messageCount: number): number;
@@ -0,0 +1,329 @@
1
+ /**
2
+ * KR-WordRank (Kim et al. 2014) 스트리밍 포팅
3
+ * @see https://github.com/lovit/KR-WordRank
4
+ * @see https://lovit.github.io/nlp/2018/04/16/krwordrank/
5
+ */
6
+ import { normalizeKoreanText } from "./korean-normalize.js";
7
+ const DEFAULT_MIN_COUNT = 4;
8
+ const DEFAULT_MAX_LENGTH = 10;
9
+ const MAX_VOCAB = 48_000;
10
+ const MAX_EDGE_KEYS = 280_000;
11
+ function tokenKey(t) {
12
+ return `${t[1]}:${t[0]}`;
13
+ }
14
+ function parseTokenKey(key) {
15
+ const i = key.indexOf(":");
16
+ return [key.slice(i + 1), key.charAt(0)];
17
+ }
18
+ /** 메시지 스트림으로 학습 → HITS → L-부분 키워드 */
19
+ export class KrWordRankStream {
20
+ minCount;
21
+ maxLength;
22
+ beta;
23
+ maxIter;
24
+ numRset;
25
+ converge;
26
+ counter = new Map();
27
+ edgeCounts = new Map();
28
+ documents = 0;
29
+ constructor(options = {}) {
30
+ this.minCount = options.minCount ?? DEFAULT_MIN_COUNT;
31
+ this.maxLength = options.maxLength ?? DEFAULT_MAX_LENGTH;
32
+ this.beta = options.beta ?? 0.85;
33
+ this.maxIter = options.maxIter ?? 12;
34
+ this.numRset = options.numRset ?? 280;
35
+ this.converge = options.converge ?? 0.001;
36
+ }
37
+ addDocument(raw) {
38
+ const doc = normalizeKoreanText(raw, { keepEnglish: true, keepNumbers: true });
39
+ if (!doc)
40
+ return;
41
+ this.documents += 1;
42
+ const tokens = doc.split(/\s+/).filter(Boolean);
43
+ if (tokens.length === 0)
44
+ return;
45
+ for (const token of tokens) {
46
+ this.scanToken(token);
47
+ }
48
+ const links = [];
49
+ for (const token of tokens) {
50
+ links.push(...intraLinks(token, this.maxLength));
51
+ }
52
+ if (tokens.length > 1) {
53
+ const padded = [tokens[tokens.length - 1], ...tokens, tokens[0]];
54
+ links.push(...interLinks(padded, this.maxLength));
55
+ }
56
+ for (const [a, b] of links) {
57
+ this.bumpEdge(a, b);
58
+ }
59
+ }
60
+ extractKeywords(options = {}) {
61
+ const vocab = this.buildVocabulary();
62
+ if (vocab.index2token.length < 2)
63
+ return new Map();
64
+ const graph = this.buildGraph(vocab);
65
+ const rank = runHits(graph, {
66
+ beta: this.beta,
67
+ maxIter: this.maxIter,
68
+ converge: this.converge,
69
+ nodeCount: vocab.index2token.length,
70
+ });
71
+ const lset = new Map();
72
+ const rset = new Map();
73
+ for (const [idx, score] of rank) {
74
+ const tok = vocab.index2token[idx];
75
+ if (!tok)
76
+ continue;
77
+ const [word, pos] = tok;
78
+ if (pos === "L")
79
+ lset.set(word, score);
80
+ else
81
+ rset.set(word, score);
82
+ }
83
+ let rsetUse = rset;
84
+ if (this.numRset > 0 && rset.size > this.numRset) {
85
+ rsetUse = new Map([...rset.entries()].sort((a, b) => b[1] - a[1]).slice(0, this.numRset));
86
+ }
87
+ let keywords = selectKeywords(lset, rsetUse);
88
+ keywords = filterCompounds(keywords);
89
+ keywords = filterSubtokens(keywords);
90
+ const stop = options.stopwords;
91
+ const limit = options.limit ?? 100;
92
+ const out = new Map();
93
+ for (const [word, score] of [...keywords.entries()].sort((a, b) => b[1] - a[1])) {
94
+ if (stop?.has(word))
95
+ continue;
96
+ if (word.length < 2)
97
+ continue;
98
+ out.set(word, score);
99
+ if (out.size >= limit)
100
+ break;
101
+ }
102
+ return out;
103
+ }
104
+ scanToken(token) {
105
+ const len = token.length;
106
+ this.bumpCounter([token, "L"]);
107
+ for (let e = 1; e < Math.min(len, this.maxLength); e += 1) {
108
+ if (len - e > this.maxLength)
109
+ continue;
110
+ this.bumpCounter([token.slice(0, e), "L"]);
111
+ this.bumpCounter([token.slice(e), "R"]);
112
+ }
113
+ }
114
+ bumpCounter(token) {
115
+ const k = tokenKey(token);
116
+ this.counter.set(k, (this.counter.get(k) ?? 0) + 1);
117
+ if (this.counter.size > MAX_VOCAB * 3)
118
+ this.pruneCounter();
119
+ }
120
+ pruneCounter() {
121
+ const kept = [...this.counter.entries()]
122
+ .sort((a, b) => b[1] - a[1])
123
+ .slice(0, MAX_VOCAB);
124
+ this.counter.clear();
125
+ for (const [k, v] of kept)
126
+ this.counter.set(k, v);
127
+ }
128
+ bumpEdge(a, b) {
129
+ if (this.edgeCounts.size > MAX_EDGE_KEYS)
130
+ return;
131
+ const k = `${tokenKey(a)}>>${tokenKey(b)}`;
132
+ this.edgeCounts.set(k, (this.edgeCounts.get(k) ?? 0) + 1);
133
+ }
134
+ buildVocabulary() {
135
+ let entries = [...this.counter.entries()].filter(([, c]) => c >= this.minCount);
136
+ entries = pruneDominatedSubwords(entries);
137
+ entries.sort((a, b) => b[1] - a[1] || b[0].length - a[0].length);
138
+ entries = entries.slice(0, MAX_VOCAB);
139
+ const token2idx = new Map();
140
+ const index2token = [];
141
+ for (const [k] of entries) {
142
+ token2idx.set(k, index2token.length);
143
+ index2token.push(parseTokenKey(k));
144
+ }
145
+ return { token2idx, index2token };
146
+ }
147
+ buildGraph(vocab) {
148
+ const raw = new Map();
149
+ for (const [key, weight] of this.edgeCounts) {
150
+ const sep = key.indexOf(">>");
151
+ if (sep < 0)
152
+ continue;
153
+ const a = vocab.token2idx.get(key.slice(0, sep));
154
+ const b = vocab.token2idx.get(key.slice(sep + 2));
155
+ if (a === undefined || b === undefined)
156
+ continue;
157
+ let row = raw.get(a);
158
+ if (!row) {
159
+ row = new Map();
160
+ raw.set(a, row);
161
+ }
162
+ row.set(b, (row.get(b) ?? 0) + weight);
163
+ let rowB = raw.get(b);
164
+ if (!rowB) {
165
+ rowB = new Map();
166
+ raw.set(b, rowB);
167
+ }
168
+ rowB.set(a, (rowB.get(a) ?? 0) + weight);
169
+ }
170
+ const graph = new Map();
171
+ for (const [fromNode, toDict] of raw) {
172
+ const sum = [...toDict.values()].reduce((a, c) => a + c, 0);
173
+ if (sum <= 0)
174
+ continue;
175
+ for (const [toNode, w] of toDict) {
176
+ let col = graph.get(toNode);
177
+ if (!col) {
178
+ col = new Map();
179
+ graph.set(toNode, col);
180
+ }
181
+ col.set(fromNode, (col.get(fromNode) ?? 0) + w / sum);
182
+ }
183
+ }
184
+ return graph;
185
+ }
186
+ }
187
+ function intraLinks(token, maxLength) {
188
+ const out = [];
189
+ const len = token.length;
190
+ for (let e = 1; e < Math.min(len, 10); e += 1) {
191
+ if (len - e > maxLength)
192
+ continue;
193
+ out.push([[token.slice(0, e), "L"], [token.slice(e), "R"]]);
194
+ }
195
+ return out;
196
+ }
197
+ function interLinks(tokens, maxLength) {
198
+ const out = [];
199
+ for (let i = 1; i < tokens.length - 1; i += 1) {
200
+ const left = tokens[i - 1];
201
+ const curr = tokens[i];
202
+ const right = tokens[i + 1];
203
+ for (let b = 1; b < Math.min(10, left.length); b += 1) {
204
+ out.push([[left.slice(-b), "R"], [curr, "L"]]);
205
+ }
206
+ for (let e = 1; e < Math.min(10, right.length); e += 1) {
207
+ out.push([[curr, "L"], [right.slice(0, e), "L"]]);
208
+ }
209
+ }
210
+ return out;
211
+ }
212
+ function pruneDominatedSubwords(entries) {
213
+ const sorted = [...entries].sort((a, b) => b[1] - a[1] || b[0].length - a[0].length);
214
+ const kept = [];
215
+ for (const [key, freq] of sorted) {
216
+ const dominated = kept.some(([k, f]) => {
217
+ if (f !== freq)
218
+ return false;
219
+ const posK = k.charAt(0);
220
+ const posKey = key.charAt(0);
221
+ if (posK !== posKey)
222
+ return false;
223
+ const s = k.slice(2);
224
+ const t = key.slice(2);
225
+ return s.length > t.length && s.includes(t);
226
+ });
227
+ if (!dominated)
228
+ kept.push([key, freq]);
229
+ }
230
+ return kept;
231
+ }
232
+ function runHits(graph, opts) {
233
+ const { beta, maxIter, converge, nodeCount } = opts;
234
+ const dw = 100 / nodeCount;
235
+ const rank = new Map();
236
+ for (const node of graph.keys())
237
+ rank.set(node, dw);
238
+ const sumWeight = 100;
239
+ for (let iter = 1; iter <= maxIter; iter += 1) {
240
+ const next = new Map();
241
+ let diff = 0;
242
+ for (const [toNode, fromDict] of graph) {
243
+ let s = 0;
244
+ for (const [fromNode, w] of fromDict) {
245
+ s += w * (rank.get(fromNode) ?? dw);
246
+ }
247
+ const updated = beta * s + (1 - beta) * dw;
248
+ next.set(toNode, updated);
249
+ diff += Math.abs(updated - (rank.get(toNode) ?? 0));
250
+ }
251
+ for (const [n, v] of next)
252
+ rank.set(n, v);
253
+ if (diff < sumWeight * converge)
254
+ break;
255
+ }
256
+ return rank;
257
+ }
258
+ function selectKeywords(lset, rset) {
259
+ const keywords = new Map();
260
+ for (const [word, r] of [...lset.entries()].sort((a, b) => b[1] - a[1])) {
261
+ if (word.length === 1)
262
+ continue;
263
+ let compound = false;
264
+ for (let e = 2; e < word.length; e += 1) {
265
+ if (keywords.has(word.slice(0, e)) && rset.has(word.slice(0, e))) {
266
+ compound = true;
267
+ break;
268
+ }
269
+ }
270
+ if (!compound)
271
+ keywords.set(word, r);
272
+ }
273
+ return keywords;
274
+ }
275
+ function filterCompounds(keywords) {
276
+ const out = new Map();
277
+ for (const [word, r] of [...keywords.entries()].sort((a, b) => b[1] - a[1])) {
278
+ const len = word.length;
279
+ if (len <= 2) {
280
+ out.set(word, r);
281
+ continue;
282
+ }
283
+ if (len === 3 && out.has(word.slice(0, 2)))
284
+ continue;
285
+ let compound = false;
286
+ for (let e = 2; e < len; e += 1) {
287
+ if (keywords.has(word.slice(0, e)) && keywords.has(word.slice(e))) {
288
+ compound = true;
289
+ break;
290
+ }
291
+ }
292
+ if (!compound)
293
+ out.set(word, r);
294
+ }
295
+ return out;
296
+ }
297
+ function filterSubtokens(keywords) {
298
+ const subtokens = new Set();
299
+ const out = new Map();
300
+ for (const [word, r] of [...keywords.entries()].sort((a, b) => b[1] - a[1])) {
301
+ const subs = new Set();
302
+ for (let e = 2; e <= word.length; e += 1)
303
+ subs.add(word.slice(0, e));
304
+ let isSub = false;
305
+ for (const sub of subs) {
306
+ if (subtokens.has(sub)) {
307
+ isSub = true;
308
+ break;
309
+ }
310
+ }
311
+ if (!isSub) {
312
+ out.set(word, r);
313
+ for (const sub of subs)
314
+ subtokens.add(sub);
315
+ }
316
+ }
317
+ return out;
318
+ }
319
+ /** 메시지 수에 따른 min_count (소규모 방 완화) */
320
+ export function adaptiveMinCount(messageCount) {
321
+ if (messageCount < 200)
322
+ return 2;
323
+ if (messageCount < 2_000)
324
+ return 3;
325
+ if (messageCount < 20_000)
326
+ return 4;
327
+ return 5;
328
+ }
329
+ //# sourceMappingURL=kr-wordrank-stream.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"kr-wordrank-stream.js","sourceRoot":"","sources":["../../src/kr-wordrank-stream.ts"],"names":[],"mappings":"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"}
@@ -1276,7 +1276,7 @@ function renderReactionsPanel(data) {
1276
1276
  return parts.join("");
1277
1277
  }
1278
1278
  function renderKeywordSnapshot(items) {
1279
- const note = '<p class="kw-note"><strong>본문 단어만</strong> 집계해요. 첨부·시스템·운영 알림·샵검색은 <strong>각각 다른 차트</strong>에서 봅니다.</p>';
1279
+ const note = '<p class="kw-note"><strong>KR-WordRank</strong>(비지도·한국어 특화)로 본문 어절에서 단어를 뽑고, 해시태그·슬랭은 보조로 더합니다. 조사·어미 조각은 걸러집니다.</p>';
1280
1280
  if (items.length === 0) {
1281
1281
  return note + '<p style="margin:0;color:var(--muted);font-size:13px">추출된 키워드가 없습니다.</p>';
1282
1282
  }