chatlab-cli 0.31.3 → 0.33.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/README.md +2 -1
- package/dist/{chat-command-ZIXKNLSE.mjs → chat-command-IHRNO7J4.mjs} +8 -8
- package/dist/{chunk-3J7WTUEE.mjs → chunk-4V6UP7EW.mjs} +2 -2
- package/dist/{chunk-YVNZ73O5.mjs → chunk-C3B7RXES.mjs} +3919 -3773
- package/dist/chunk-C3B7RXES.mjs.map +1 -0
- package/dist/{chunk-FDLLEDNW.mjs → chunk-HGV4MUIW.mjs} +4 -4
- package/dist/{chunk-75FZ26HM.mjs → chunk-KWJ5FZ74.mjs} +1196 -1045
- package/dist/chunk-KWJ5FZ74.mjs.map +1 -0
- package/dist/{chunk-DV4U34MR.mjs → chunk-NHTOSDYI.mjs} +2 -2
- package/dist/{chunk-UIUEG7JB.mjs → chunk-NSWFHMBZ.mjs} +112 -16
- package/dist/chunk-NSWFHMBZ.mjs.map +1 -0
- package/dist/{chunk-EINCXXWY.mjs → chunk-RHA534GU.mjs} +3 -3
- package/dist/{chunk-23AIEWCD.mjs → chunk-S7YPBKQ3.mjs} +2 -2
- package/dist/{chunk-BLOBEOCG.mjs → chunk-U4F6BNGZ.mjs} +2 -2
- package/dist/{chunk-MFOCDGCC.mjs → chunk-ZERQIKI4.mjs} +121 -19
- package/dist/chunk-ZERQIKI4.mjs.map +1 -0
- package/dist/{chunk-GZO3NAHC.mjs → chunk-ZTMZ7S7T.mjs} +65 -16
- package/dist/chunk-ZTMZ7S7T.mjs.map +1 -0
- package/dist/cli.mjs +8 -8
- package/dist/contacts-worker.mjs +2 -2
- package/dist/global-insight-worker.mjs +1 -1
- package/dist/{http-WDG2OHOV.mjs → http-Y2WC2UK4.mjs} +11 -11
- package/dist/{import-4BDC37F7.mjs → import-4HF2UNQI.mjs} +7 -7
- package/dist/index.mjs +11 -11
- package/dist/{mcp-4KUVRGFF.mjs → mcp-DJPDRFTW.mjs} +8 -8
- package/dist/people-relationships-worker.mjs +2 -2
- package/dist/semantic-index-worker.mjs +1 -1
- package/dist/{src-ZOCLFBCJ.mjs → src-R7MZLKXQ.mjs} +17 -9
- package/dist/{stream-import-ERTT7U6V.mjs → stream-import-DX57ANUL.mjs} +7 -7
- package/dist/{worker-runtime-2TUZHV7W.mjs → worker-runtime-QMN7OKOZ.mjs} +5 -5
- package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-hwRVh048.js +110 -0
- package/{dist-web/assets/BufferResource-DgKoy0Ii.js → dist-cli-web/assets/BufferResource-DwfMBlAa.js} +1 -1
- package/dist-cli-web/assets/CanvasRenderer-q312LRsp.js +1 -0
- package/dist-cli-web/assets/DataDirCleanupNotice--ee1dY0d.js +1 -0
- package/dist-cli-web/assets/EChartWordcloud-BaMgUl0Y.js +1 -0
- package/{dist-web/assets/Filter-D32zqY5x.js → dist-cli-web/assets/Filter-BOb5cLBx.js} +1 -1
- package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-DK2z0Jrh.js +1 -0
- package/dist-cli-web/assets/MessageExportModal-CFbovWh1.js +1 -0
- package/dist-cli-web/assets/PageHeader-B3-W9mvh.js +1 -0
- package/{dist-web/assets/RelationshipGalaxyCanvas-DsMOubHV.js → dist-cli-web/assets/RelationshipGalaxyCanvas-GEpsKHxV.js} +4 -4
- package/{dist-web/assets/RelationshipGalaxyThreeCanvas-Dh5HjVHr.js → dist-cli-web/assets/RelationshipGalaxyThreeCanvas-DHOU66Gj.js} +2 -2
- package/{dist-web/assets/RenderTargetSystem-CovK4x_D.js → dist-cli-web/assets/RenderTargetSystem-PEnnW2yb.js} +1 -1
- package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-sNw3hP4c.js +1 -0
- package/dist-cli-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-BtUvMehe.js +1 -0
- package/{dist-web/assets/WebGLRenderer-CawhbcYU.js → dist-cli-web/assets/WebGLRenderer-BjP_1do7.js} +2 -2
- package/{dist-web/assets/WebGPURenderer-B7P1D2sA.js → dist-cli-web/assets/WebGPURenderer-ldVv92u7.js} +2 -2
- package/dist-cli-web/assets/annual-summary-time-range-BHClzNvd.js +1 -0
- package/dist-cli-web/assets/base-DBJ95yXf.js +1 -0
- package/{dist-web/assets/browserAll-CtQkc1DF.js → dist-cli-web/assets/browserAll-CPoMOLKh.js} +1 -1
- package/dist-cli-web/assets/cli-web-B5VYO-Dh.js +1 -0
- package/{dist-web/assets/dateFormat-Cfb89ZjM.js → dist-cli-web/assets/dateFormat-Cf2xuaE-.js} +1 -1
- package/dist-cli-web/assets/fetch-80O3EH4a.js +1 -0
- package/dist-cli-web/assets/fetch-B1km2u3N.js +1 -0
- package/dist-cli-web/assets/fetch-BFDKzf1P.js +1 -0
- package/dist-cli-web/assets/fetch-BuWxo6oK.js +1 -0
- package/{dist-web/assets/fetch-DkfUUq-R.js → dist-cli-web/assets/fetch-C0HHT1Yn.js} +3 -3
- package/dist-cli-web/assets/fetch-CXIdj2-_.js +1 -0
- package/{dist-web/assets/fetch-DVGCzZ0E.js → dist-cli-web/assets/fetch-CtyVktRW.js} +3 -3
- package/{dist-web/assets/fetch-D2hvh7lO.js → dist-cli-web/assets/fetch-DGi_9my2.js} +2 -2
- package/dist-cli-web/assets/fetch-Dr41uuXL.js +1 -0
- package/dist-cli-web/assets/fetch-J67jyI2k.js +1 -0
- package/dist-cli-web/assets/index-5UdyJtVU.js +1 -0
- package/dist-cli-web/assets/index-7jm3YI9u.js +53 -0
- package/dist-cli-web/assets/index-B7R74Yp7.js +1 -0
- package/dist-cli-web/assets/index-BGiTw98l.js +1 -0
- package/dist-cli-web/assets/index-Ba21yU86.js +1 -0
- package/{dist-web/assets/index-CuPE4SSh.js → dist-cli-web/assets/index-Bf3gFaid.js} +4 -4
- package/dist-cli-web/assets/index-ByQSR8Bj.js +2 -0
- package/dist-cli-web/assets/index-CPZYydbc.js +2 -0
- package/dist-cli-web/assets/index-D8Q3U-6e.js +1 -0
- package/dist-cli-web/assets/index-DGtDxRCd.js +173 -0
- package/{dist-web/assets/index-DBb03wVF.css → dist-cli-web/assets/index-DTxnpQiv.css} +1 -1
- package/dist-cli-web/assets/index-DmrrdYXH.css +1 -0
- package/dist-cli-web/assets/index-DtlTo3nt.js +1 -0
- package/dist-cli-web/assets/index-DzQT-qXF.js +1 -0
- package/dist-cli-web/assets/index-gpTDI3sq.js +64 -0
- package/{dist-web/assets/index-D8Uq3_Dc.css → dist-cli-web/assets/index-lwhCMqhd.css} +1 -1
- package/dist-cli-web/assets/index-qsbWn8VP.css +1 -0
- package/dist-cli-web/assets/people-page-header-BvQy1GQj.js +1 -0
- package/{dist-web/assets/webworkerAll-BzRoT-6m.js → dist-cli-web/assets/webworkerAll-DgKQKbNn.js} +1 -1
- package/{dist-web → dist-cli-web}/index.html +2 -4
- package/package.json +4 -4
- package/dist/chunk-75FZ26HM.mjs.map +0 -1
- package/dist/chunk-GZO3NAHC.mjs.map +0 -1
- package/dist/chunk-MFOCDGCC.mjs.map +0 -1
- package/dist/chunk-UIUEG7JB.mjs.map +0 -1
- package/dist/chunk-YVNZ73O5.mjs.map +0 -1
- package/dist-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-GgQ5ueMX.js +0 -110
- package/dist-web/assets/CanvasRenderer-CKpp71xg.js +0 -1
- package/dist-web/assets/EChartWordcloud-CSOuFvyM.js +0 -1
- package/dist-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-DUAtrSZD.js +0 -1
- package/dist-web/assets/MessageExportModal-BYLw4vZ8.js +0 -1
- package/dist-web/assets/PageHeader-Dmu6cdAk.js +0 -1
- package/dist-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-DhUfYrEx.js +0 -1
- package/dist-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-CaumhCAS.js +0 -1
- package/dist-web/assets/annual-summary-time-range-Bya4h2j6.js +0 -1
- package/dist-web/assets/base-ClGWE9aq.js +0 -1
- package/dist-web/assets/electron-Brd7jS6W.js +0 -1
- package/dist-web/assets/electron-DGxVMsvC.js +0 -1
- package/dist-web/assets/electron-hcWiB7Gg.js +0 -1
- package/dist-web/assets/fetch-BJ3lnhzK.js +0 -1
- package/dist-web/assets/fetch-BoB63ns8.js +0 -1
- package/dist-web/assets/fetch-BtK7YvLp.js +0 -1
- package/dist-web/assets/fetch-CeREJiFL.js +0 -1
- package/dist-web/assets/fetch-DWoMmOlr.js +0 -1
- package/dist-web/assets/fetch-Dr_Fjtvp.js +0 -1
- package/dist-web/assets/fetch-u7ivV7Uy.js +0 -1
- package/dist-web/assets/index-BHnQaeQY.js +0 -1
- package/dist-web/assets/index-BKFGuwJu.css +0 -1
- package/dist-web/assets/index-BXaKyn8J.js +0 -2
- package/dist-web/assets/index-BzH9cnfy.js +0 -1
- package/dist-web/assets/index-D-qNxFxp.js +0 -173
- package/dist-web/assets/index-D494nYee.js +0 -64
- package/dist-web/assets/index-DJ8kB4US.js +0 -2
- package/dist-web/assets/index-DNMWR2Vj.js +0 -1
- package/dist-web/assets/index-DSPls1YV.js +0 -1
- package/dist-web/assets/index-DcAqhCpS.js +0 -1
- package/dist-web/assets/index-DnkaYOFI.js +0 -1
- package/dist-web/assets/index-_UKteDZB.js +0 -25
- package/dist-web/assets/index-gi0wgh0M.css +0 -1
- package/dist-web/assets/index-w_1EjXiF.js +0 -1
- package/dist-web/assets/people-page-header-YGQM9G53.js +0 -1
- package/dist-web/assets/types-CZnACqaB.js +0 -1
- package/dist-web/assets/vendor-nuxt-ui-DmkGVKod.js +0 -15
- package/dist-web/assets/vendor-reka-ui-BkhadcDj.js +0 -15
- package/dist-web/assets/web-zXxtk2KZ.js +0 -1
- /package/dist/{chat-command-ZIXKNLSE.mjs.map → chat-command-IHRNO7J4.mjs.map} +0 -0
- /package/dist/{chunk-3J7WTUEE.mjs.map → chunk-4V6UP7EW.mjs.map} +0 -0
- /package/dist/{chunk-FDLLEDNW.mjs.map → chunk-HGV4MUIW.mjs.map} +0 -0
- /package/dist/{chunk-DV4U34MR.mjs.map → chunk-NHTOSDYI.mjs.map} +0 -0
- /package/dist/{chunk-EINCXXWY.mjs.map → chunk-RHA534GU.mjs.map} +0 -0
- /package/dist/{chunk-23AIEWCD.mjs.map → chunk-S7YPBKQ3.mjs.map} +0 -0
- /package/dist/{chunk-BLOBEOCG.mjs.map → chunk-U4F6BNGZ.mjs.map} +0 -0
- /package/dist/{http-WDG2OHOV.mjs.map → http-Y2WC2UK4.mjs.map} +0 -0
- /package/dist/{import-4BDC37F7.mjs.map → import-4HF2UNQI.mjs.map} +0 -0
- /package/dist/{mcp-4KUVRGFF.mjs.map → mcp-DJPDRFTW.mjs.map} +0 -0
- /package/dist/{src-ZOCLFBCJ.mjs.map → src-R7MZLKXQ.mjs.map} +0 -0
- /package/dist/{stream-import-ERTT7U6V.mjs.map → stream-import-DX57ANUL.mjs.map} +0 -0
- /package/dist/{worker-runtime-2TUZHV7W.mjs.map → worker-runtime-QMN7OKOZ.mjs.map} +0 -0
- /package/{dist-web → dist-cli-web}/assets/ActionToolsPanel-Dp-ojXjO.css +0 -0
- /package/{dist-web → dist-cli-web}/assets/PageHeader-XIXMTomn.css +0 -0
- /package/{dist-web → dist-cli-web}/assets/RelationshipGalaxyThreeCanvas-BluPHjCR.css +0 -0
- /package/{dist-web → dist-cli-web}/assets/index-BfO_LpDv.css +0 -0
- /package/{dist-web → dist-cli-web}/assets/index-ByUaiVQK.css +0 -0
- /package/{dist-web → dist-cli-web}/assets/index-ZsZwPLWy.css +0 -0
- /package/{dist-web → dist-cli-web}/assets/logo-DXIuG_WE.svg +0 -0
- /package/{dist-web → dist-cli-web}/assets/relationship-galaxy-viewport-Cwfa51ER.js +0 -0
- /package/{dist-web → dist-cli-web}/assets/vendor-echarts-B-i_56Av.js +0 -0
- /package/{dist-web → dist-cli-web}/assets/vendor-echarts-wordcloud-Ca8n6qS6.js +0 -0
- /package/{dist-web → dist-cli-web}/assets/vendor-snapdom--nZvo8Sg.js +0 -0
- /package/{dist-web → dist-cli-web}/assets/vendor-zrender-5DcYjSZk.js +0 -0
- /package/{dist-web → dist-cli-web}/favicon.ico +0 -0
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
createEmptyContactsDiagnostics,
|
|
10
10
|
normalizeContactsTimeRangePreset,
|
|
11
11
|
resolveContactsTimeRange
|
|
12
|
-
} from "./chunk-
|
|
12
|
+
} from "./chunk-4V6UP7EW.mjs";
|
|
13
13
|
import {
|
|
14
14
|
PEOPLE_RELATIONSHIPS_ALGORITHM_VERSION,
|
|
15
15
|
buildPeopleRelationshipsNeighborhoodGraph,
|
|
@@ -18,7 +18,7 @@ import {
|
|
|
18
18
|
computePeopleRelationshipsSnapshot,
|
|
19
19
|
normalizePeopleRelationshipsTimeRangePreset,
|
|
20
20
|
resolvePeopleRelationshipsTimeRange
|
|
21
|
-
} from "./chunk-
|
|
21
|
+
} from "./chunk-S7YPBKQ3.mjs";
|
|
22
22
|
import {
|
|
23
23
|
ANNUAL_SUMMARY_ALGORITHM_VERSION
|
|
24
24
|
} from "./chunk-YAQLYL52.mjs";
|
|
@@ -27,6 +27,8 @@ import {
|
|
|
27
27
|
BUILTIN_PROVIDERS,
|
|
28
28
|
BUILTIN_TOOL_CATALOG,
|
|
29
29
|
CHART_CAPABILITY_SKILL_ID,
|
|
30
|
+
CHATLAB_FORMAT_VERSION,
|
|
31
|
+
CHATLAB_SUPPORTED_FORMAT_VERSIONS,
|
|
30
32
|
CHAT_DB_INDEXES,
|
|
31
33
|
CHAT_DB_SCHEMA,
|
|
32
34
|
DEFAULT_GENERAL_ASSISTANT_ID,
|
|
@@ -34,6 +36,7 @@ import {
|
|
|
34
36
|
FTS_TABLE_SCHEMA,
|
|
35
37
|
GENERAL_ASSISTANT_IDS,
|
|
36
38
|
KNOWN_PLATFORMS,
|
|
39
|
+
MessageType,
|
|
37
40
|
StreamingThinkTagParser,
|
|
38
41
|
appLogger,
|
|
39
42
|
batchSegmentChineseWithStats,
|
|
@@ -76,6 +79,7 @@ import {
|
|
|
76
79
|
isChatSessionDb,
|
|
77
80
|
isNameMatchPlatform,
|
|
78
81
|
isReasoningModel,
|
|
82
|
+
isSupportedChatLabFormatVersion,
|
|
79
83
|
matchOwnerProfile,
|
|
80
84
|
mergeConfirmedNames,
|
|
81
85
|
mergeMembers,
|
|
@@ -97,8 +101,9 @@ import {
|
|
|
97
101
|
updateMemberAliases,
|
|
98
102
|
updateSessionOwnerId,
|
|
99
103
|
writeAnnualSummarySnapshot,
|
|
100
|
-
writeAuthProfile
|
|
101
|
-
|
|
104
|
+
writeAuthProfile,
|
|
105
|
+
writeParseResultToDb
|
|
106
|
+
} from "./chunk-C3B7RXES.mjs";
|
|
102
107
|
import {
|
|
103
108
|
__commonJS,
|
|
104
109
|
__export,
|
|
@@ -714,8 +719,8 @@ var require_FilterBase = __commonJS({
|
|
|
714
719
|
var FilterBase = class _FilterBase extends Transform {
|
|
715
720
|
static stringFilter(string, separator) {
|
|
716
721
|
return (stack) => {
|
|
717
|
-
const
|
|
718
|
-
return
|
|
722
|
+
const path30 = stack.join(separator);
|
|
723
|
+
return path30.length === string.length && path30 === string || path30.length > string.length && path30.substr(0, string.length) === string && path30.substr(string.length, separator.length) === separator;
|
|
719
724
|
};
|
|
720
725
|
}
|
|
721
726
|
static regExpFilter(regExp, separator) {
|
|
@@ -1057,9 +1062,9 @@ var require_stream_chain = __commonJS({
|
|
|
1057
1062
|
var require_withParser = __commonJS({
|
|
1058
1063
|
"../../node_modules/stream-json/utils/withParser.js"(exports, module) {
|
|
1059
1064
|
"use strict";
|
|
1060
|
-
var { chain:
|
|
1065
|
+
var { chain: chain9 } = require_stream_chain();
|
|
1061
1066
|
var Parser = require_Parser();
|
|
1062
|
-
var withParser = (fn, options2) =>
|
|
1067
|
+
var withParser = (fn, options2) => chain9([new Parser(options2), fn(options2)], Object.assign({}, options2, { writableObjectMode: false, readableObjectMode: true }));
|
|
1063
1068
|
module.exports = withParser;
|
|
1064
1069
|
}
|
|
1065
1070
|
});
|
|
@@ -1137,7 +1142,7 @@ var require_Assembler = __commonJS({
|
|
|
1137
1142
|
this.current = new Ctr();
|
|
1138
1143
|
this.key = null;
|
|
1139
1144
|
};
|
|
1140
|
-
var
|
|
1145
|
+
var Assembler2 = class _Assembler extends EventEmitter {
|
|
1141
1146
|
static connectTo(stream, options2) {
|
|
1142
1147
|
return new _Assembler(options2).connectTo(stream);
|
|
1143
1148
|
}
|
|
@@ -1169,12 +1174,12 @@ var require_Assembler = __commonJS({
|
|
|
1169
1174
|
return (this.stack.length >> 1) + (this.done ? 0 : 1);
|
|
1170
1175
|
}
|
|
1171
1176
|
get path() {
|
|
1172
|
-
const
|
|
1177
|
+
const path30 = [];
|
|
1173
1178
|
for (let i = 0; i < this.stack.length; i += 2) {
|
|
1174
1179
|
const key = this.stack[i + 1];
|
|
1175
|
-
|
|
1180
|
+
path30.push(key === null ? this.stack[i].length : key);
|
|
1176
1181
|
}
|
|
1177
|
-
return
|
|
1182
|
+
return path30;
|
|
1178
1183
|
}
|
|
1179
1184
|
dropToLevel(level) {
|
|
1180
1185
|
if (level < this.depth) {
|
|
@@ -1256,11 +1261,11 @@ var require_Assembler = __commonJS({
|
|
|
1256
1261
|
}
|
|
1257
1262
|
}
|
|
1258
1263
|
};
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
module.exports =
|
|
1264
|
+
Assembler2.prototype.stringValue = Assembler2.prototype._saveValue;
|
|
1265
|
+
Assembler2.prototype.startObject = startObject(Object);
|
|
1266
|
+
Assembler2.prototype.startArray = startObject(Array);
|
|
1267
|
+
Assembler2.prototype.endArray = Assembler2.prototype.endObject;
|
|
1268
|
+
module.exports = Assembler2;
|
|
1264
1269
|
}
|
|
1265
1270
|
});
|
|
1266
1271
|
|
|
@@ -1269,7 +1274,7 @@ var require_StreamBase = __commonJS({
|
|
|
1269
1274
|
"../../node_modules/stream-json/streamers/StreamBase.js"(exports, module) {
|
|
1270
1275
|
"use strict";
|
|
1271
1276
|
var { Transform } = __require("stream");
|
|
1272
|
-
var
|
|
1277
|
+
var Assembler2 = require_Assembler();
|
|
1273
1278
|
var Counter = class {
|
|
1274
1279
|
constructor(initialDepth) {
|
|
1275
1280
|
this.depth = initialDepth;
|
|
@@ -1298,7 +1303,7 @@ var require_StreamBase = __commonJS({
|
|
|
1298
1303
|
this._filter = this._transform;
|
|
1299
1304
|
}
|
|
1300
1305
|
this._transform = this._wait || this._filter;
|
|
1301
|
-
this._assembler = new
|
|
1306
|
+
this._assembler = new Assembler2(options2);
|
|
1302
1307
|
}
|
|
1303
1308
|
_transform(chunk, encoding, callback) {
|
|
1304
1309
|
if (this._assembler[chunk.name]) {
|
|
@@ -1912,9 +1917,9 @@ var require_yauzl = __commonJS({
|
|
|
1912
1917
|
exports.Entry = Entry;
|
|
1913
1918
|
exports.LocalFileHeader = LocalFileHeader;
|
|
1914
1919
|
exports.RandomAccessReader = RandomAccessReader;
|
|
1915
|
-
function openPromise2(
|
|
1920
|
+
function openPromise2(path30, options2) {
|
|
1916
1921
|
return new Promise((resolve4, reject) => {
|
|
1917
|
-
open(
|
|
1922
|
+
open(path30, { ...options2, lazyEntries: true }, function(err, zipfile) {
|
|
1918
1923
|
if (err) return reject(err);
|
|
1919
1924
|
resolve4(zipfile);
|
|
1920
1925
|
});
|
|
@@ -1944,7 +1949,7 @@ var require_yauzl = __commonJS({
|
|
|
1944
1949
|
});
|
|
1945
1950
|
});
|
|
1946
1951
|
}
|
|
1947
|
-
function open(
|
|
1952
|
+
function open(path30, options2, callback) {
|
|
1948
1953
|
if (typeof options2 === "function") {
|
|
1949
1954
|
callback = options2;
|
|
1950
1955
|
options2 = null;
|
|
@@ -1956,7 +1961,7 @@ var require_yauzl = __commonJS({
|
|
|
1956
1961
|
if (options2.validateEntrySizes == null) options2.validateEntrySizes = true;
|
|
1957
1962
|
if (options2.strictFileNames == null) options2.strictFileNames = false;
|
|
1958
1963
|
if (callback == null) callback = defaultCallback;
|
|
1959
|
-
fs37.open(
|
|
1964
|
+
fs37.open(path30, "r", function(err, fd) {
|
|
1960
1965
|
if (err) return callback(err);
|
|
1961
1966
|
fromFd(fd, options2, function(err2, zipfile) {
|
|
1962
1967
|
if (err2) fs37.close(fd, defaultCallback);
|
|
@@ -2810,122 +2815,9 @@ function removeChatLabTempDir(dirPath, scope, options2 = {}) {
|
|
|
2810
2815
|
fs.rmSync(resolvedDir, { recursive: true, force: true });
|
|
2811
2816
|
}
|
|
2812
2817
|
|
|
2813
|
-
// ../../packages/node-runtime/src/import/write-parse-result.ts
|
|
2814
|
-
function writeParseResultToDb(db, meta, members, messages) {
|
|
2815
|
-
let messageCount = 0;
|
|
2816
|
-
let skippedCount = 0;
|
|
2817
|
-
db.transaction(() => {
|
|
2818
|
-
db.prepare(
|
|
2819
|
-
`INSERT INTO meta (name, platform, type, imported_at, group_id, group_avatar, owner_id)
|
|
2820
|
-
VALUES (?, ?, ?, ?, ?, ?, ?)`
|
|
2821
|
-
).run(
|
|
2822
|
-
meta.name,
|
|
2823
|
-
meta.platform,
|
|
2824
|
-
meta.type,
|
|
2825
|
-
Math.floor(Date.now() / 1e3),
|
|
2826
|
-
meta.groupId || null,
|
|
2827
|
-
meta.groupAvatar || null,
|
|
2828
|
-
meta.ownerId || null
|
|
2829
|
-
);
|
|
2830
|
-
const insertMember = db.prepare(
|
|
2831
|
-
`INSERT OR IGNORE INTO member (platform_id, account_name, group_nickname, aliases, avatar, roles) VALUES (?, ?, ?, ?, ?, ?)`
|
|
2832
|
-
);
|
|
2833
|
-
const getMemberId = db.prepare(`SELECT id FROM member WHERE platform_id = ?`);
|
|
2834
|
-
const memberIdMap = /* @__PURE__ */ new Map();
|
|
2835
|
-
for (const member of members) {
|
|
2836
|
-
insertMember.run(
|
|
2837
|
-
member.platformId,
|
|
2838
|
-
member.accountName || null,
|
|
2839
|
-
member.groupNickname || null,
|
|
2840
|
-
member.aliases ? JSON.stringify(member.aliases) : "[]",
|
|
2841
|
-
member.avatar || null,
|
|
2842
|
-
member.roles ? JSON.stringify(member.roles) : "[]"
|
|
2843
|
-
);
|
|
2844
|
-
const row = getMemberId.get(member.platformId);
|
|
2845
|
-
memberIdMap.set(member.platformId, row.id);
|
|
2846
|
-
}
|
|
2847
|
-
const sortedMessages = [...messages].sort((a, b) => a.timestamp - b.timestamp);
|
|
2848
|
-
const accountNameTracker = /* @__PURE__ */ new Map();
|
|
2849
|
-
const groupNicknameTracker = /* @__PURE__ */ new Map();
|
|
2850
|
-
const insertMessage = db.prepare(
|
|
2851
|
-
`INSERT INTO message (sender_id, sender_account_name, sender_group_nickname, ts, type, content, reply_to_message_id, platform_message_id)
|
|
2852
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`
|
|
2853
|
-
);
|
|
2854
|
-
const insertNameHistory = db.prepare(
|
|
2855
|
-
`INSERT INTO member_name_history (member_id, name_type, name, start_ts, end_ts) VALUES (?, ?, ?, ?, ?)`
|
|
2856
|
-
);
|
|
2857
|
-
const updateMemberAccountName = db.prepare(`UPDATE member SET account_name = ? WHERE platform_id = ?`);
|
|
2858
|
-
const updateMemberGroupNickname = db.prepare(`UPDATE member SET group_nickname = ? WHERE platform_id = ?`);
|
|
2859
|
-
const updateNameHistoryEndTs = db.prepare(
|
|
2860
|
-
`UPDATE member_name_history SET end_ts = ? WHERE member_id = ? AND name_type = ? AND end_ts IS NULL`
|
|
2861
|
-
);
|
|
2862
|
-
for (const msg of sortedMessages) {
|
|
2863
|
-
const senderId = memberIdMap.get(msg.senderPlatformId);
|
|
2864
|
-
if (senderId === void 0) {
|
|
2865
|
-
skippedCount++;
|
|
2866
|
-
continue;
|
|
2867
|
-
}
|
|
2868
|
-
insertMessage.run(
|
|
2869
|
-
senderId,
|
|
2870
|
-
msg.senderAccountName || null,
|
|
2871
|
-
msg.senderGroupNickname || null,
|
|
2872
|
-
msg.timestamp,
|
|
2873
|
-
msg.type,
|
|
2874
|
-
msg.content,
|
|
2875
|
-
msg.replyToMessageId || null,
|
|
2876
|
-
msg.platformMessageId || null
|
|
2877
|
-
);
|
|
2878
|
-
messageCount++;
|
|
2879
|
-
const accountName = msg.senderAccountName;
|
|
2880
|
-
if (accountName) {
|
|
2881
|
-
const tracker = accountNameTracker.get(msg.senderPlatformId);
|
|
2882
|
-
if (!tracker) {
|
|
2883
|
-
accountNameTracker.set(msg.senderPlatformId, {
|
|
2884
|
-
currentName: accountName,
|
|
2885
|
-
lastSeenTs: msg.timestamp
|
|
2886
|
-
});
|
|
2887
|
-
insertNameHistory.run(senderId, "account_name", accountName, msg.timestamp, null);
|
|
2888
|
-
} else if (tracker.currentName !== accountName) {
|
|
2889
|
-
updateNameHistoryEndTs.run(msg.timestamp, senderId, "account_name");
|
|
2890
|
-
insertNameHistory.run(senderId, "account_name", accountName, msg.timestamp, null);
|
|
2891
|
-
tracker.currentName = accountName;
|
|
2892
|
-
tracker.lastSeenTs = msg.timestamp;
|
|
2893
|
-
} else {
|
|
2894
|
-
tracker.lastSeenTs = msg.timestamp;
|
|
2895
|
-
}
|
|
2896
|
-
}
|
|
2897
|
-
const groupNickname = msg.senderGroupNickname;
|
|
2898
|
-
if (groupNickname) {
|
|
2899
|
-
const tracker = groupNicknameTracker.get(msg.senderPlatformId);
|
|
2900
|
-
if (!tracker) {
|
|
2901
|
-
groupNicknameTracker.set(msg.senderPlatformId, {
|
|
2902
|
-
currentName: groupNickname,
|
|
2903
|
-
lastSeenTs: msg.timestamp
|
|
2904
|
-
});
|
|
2905
|
-
insertNameHistory.run(senderId, "group_nickname", groupNickname, msg.timestamp, null);
|
|
2906
|
-
} else if (tracker.currentName !== groupNickname) {
|
|
2907
|
-
updateNameHistoryEndTs.run(msg.timestamp, senderId, "group_nickname");
|
|
2908
|
-
insertNameHistory.run(senderId, "group_nickname", groupNickname, msg.timestamp, null);
|
|
2909
|
-
tracker.currentName = groupNickname;
|
|
2910
|
-
tracker.lastSeenTs = msg.timestamp;
|
|
2911
|
-
} else {
|
|
2912
|
-
tracker.lastSeenTs = msg.timestamp;
|
|
2913
|
-
}
|
|
2914
|
-
}
|
|
2915
|
-
}
|
|
2916
|
-
for (const [platformId, tracker] of accountNameTracker.entries()) {
|
|
2917
|
-
updateMemberAccountName.run(tracker.currentName, platformId);
|
|
2918
|
-
}
|
|
2919
|
-
for (const [platformId, tracker] of groupNicknameTracker.entries()) {
|
|
2920
|
-
updateMemberGroupNickname.run(tracker.currentName, platformId);
|
|
2921
|
-
}
|
|
2922
|
-
});
|
|
2923
|
-
return { messageCount, memberCount: members.length, skippedCount };
|
|
2924
|
-
}
|
|
2925
|
-
|
|
2926
2818
|
// ../../packages/parser/src/index.ts
|
|
2927
2819
|
import * as fs18 from "fs";
|
|
2928
|
-
import * as
|
|
2820
|
+
import * as path11 from "path";
|
|
2929
2821
|
|
|
2930
2822
|
// ../../packages/parser/src/sniffer.ts
|
|
2931
2823
|
import * as fs2 from "fs";
|
|
@@ -3315,8 +3207,8 @@ var { pick } = import_Pick.default;
|
|
|
3315
3207
|
var { streamValues } = import_StreamValues.default;
|
|
3316
3208
|
var { chain } = import_stream_chain.default;
|
|
3317
3209
|
function extractNameFromFilePath(filePath) {
|
|
3318
|
-
const
|
|
3319
|
-
const name =
|
|
3210
|
+
const basename10 = path3.basename(filePath);
|
|
3211
|
+
const name = basename10.replace(/\.json$/i, "");
|
|
3320
3212
|
return name || "\u672A\u77E5\u7FA4\u804A";
|
|
3321
3213
|
}
|
|
3322
3214
|
var feature = {
|
|
@@ -3486,8 +3378,8 @@ import * as fs5 from "fs";
|
|
|
3486
3378
|
import * as readline from "readline";
|
|
3487
3379
|
import * as path4 from "path";
|
|
3488
3380
|
function extractNameFromFilePath2(filePath) {
|
|
3489
|
-
const
|
|
3490
|
-
const name =
|
|
3381
|
+
const basename10 = path4.basename(filePath);
|
|
3382
|
+
const name = basename10.replace(/\.jsonl$/i, "");
|
|
3491
3383
|
return name || "\u672A\u77E5\u7FA4\u804A";
|
|
3492
3384
|
}
|
|
3493
3385
|
function parseLine(line) {
|
|
@@ -3509,8 +3401,10 @@ var feature2 = {
|
|
|
3509
3401
|
// 低优先级,让其他格式先匹配
|
|
3510
3402
|
extensions: [".jsonl"],
|
|
3511
3403
|
signatures: {
|
|
3512
|
-
//
|
|
3513
|
-
head: [
|
|
3404
|
+
// 跳过格式允许的空行和注释后,分别匹配首条数据中的 header 类型和 chatlab 对象。
|
|
3405
|
+
head: [
|
|
3406
|
+
/^\uFEFF?(?:[ \t]*(?:#[^\r\n]*)?\r?\n)*[ \t]*\{(?=[^\r\n]*"_type"\s*:\s*"header")(?=[^\r\n]*"chatlab"\s*:)[^\r\n]*\}/
|
|
3407
|
+
]
|
|
3514
3408
|
}
|
|
3515
3409
|
};
|
|
3516
3410
|
async function* parseChatLabJsonl(options2) {
|
|
@@ -3525,6 +3419,7 @@ async function* parseChatLabJsonl(options2) {
|
|
|
3525
3419
|
const members = [];
|
|
3526
3420
|
const memberMap = /* @__PURE__ */ new Map();
|
|
3527
3421
|
const messageBatch = [];
|
|
3422
|
+
let explicitMembersEmitted = false;
|
|
3528
3423
|
let meta = null;
|
|
3529
3424
|
let headerParsed = false;
|
|
3530
3425
|
const fileStream = fs5.createReadStream(filePath, { encoding: "utf-8" });
|
|
@@ -3560,7 +3455,9 @@ async function* parseChatLabJsonl(options2) {
|
|
|
3560
3455
|
platformId: parsed.platformId,
|
|
3561
3456
|
accountName: parsed.accountName,
|
|
3562
3457
|
groupNickname: parsed.groupNickname,
|
|
3563
|
-
|
|
3458
|
+
aliases: parsed.aliases,
|
|
3459
|
+
avatar: parsed.avatar,
|
|
3460
|
+
roles: parsed.roles
|
|
3564
3461
|
};
|
|
3565
3462
|
members.push(member);
|
|
3566
3463
|
memberMap.set(parsed.platformId, member);
|
|
@@ -3595,6 +3492,11 @@ async function* parseChatLabJsonl(options2) {
|
|
|
3595
3492
|
});
|
|
3596
3493
|
messagesProcessed++;
|
|
3597
3494
|
if (messageBatch.length >= batchSize) {
|
|
3495
|
+
if (members.length > 0 && !explicitMembersEmitted) {
|
|
3496
|
+
yield { type: "members", data: members.slice() };
|
|
3497
|
+
explicitMembersEmitted = true;
|
|
3498
|
+
}
|
|
3499
|
+
yield { type: "messages", data: messageBatch.splice(0) };
|
|
3598
3500
|
const progress = createProgress(
|
|
3599
3501
|
"parsing",
|
|
3600
3502
|
bytesRead,
|
|
@@ -3602,6 +3504,7 @@ async function* parseChatLabJsonl(options2) {
|
|
|
3602
3504
|
messagesProcessed,
|
|
3603
3505
|
`\u5DF2\u5904\u7406 ${messagesProcessed.toLocaleString()} \u6761\u6D88\u606F...`
|
|
3604
3506
|
);
|
|
3507
|
+
yield { type: "progress", data: progress };
|
|
3605
3508
|
onProgress?.(progress);
|
|
3606
3509
|
}
|
|
3607
3510
|
break;
|
|
@@ -3616,13 +3519,14 @@ async function* parseChatLabJsonl(options2) {
|
|
|
3616
3519
|
yield { type: "meta", data: meta };
|
|
3617
3520
|
}
|
|
3618
3521
|
if (members.length > 0) {
|
|
3619
|
-
|
|
3522
|
+
if (!explicitMembersEmitted) {
|
|
3523
|
+
yield { type: "members", data: members };
|
|
3524
|
+
}
|
|
3620
3525
|
} else if (memberMap.size > 0) {
|
|
3621
3526
|
yield { type: "members", data: Array.from(memberMap.values()) };
|
|
3622
3527
|
}
|
|
3623
|
-
|
|
3624
|
-
|
|
3625
|
-
yield { type: "messages", data: batch };
|
|
3528
|
+
if (messageBatch.length > 0) {
|
|
3529
|
+
yield { type: "messages", data: messageBatch };
|
|
3626
3530
|
}
|
|
3627
3531
|
const doneProgress = createProgress("done", totalBytes, totalBytes, messagesProcessed, "");
|
|
3628
3532
|
yield { type: "progress", data: doneProgress };
|
|
@@ -3952,8 +3856,8 @@ function extractJsonObject2(content, key) {
|
|
|
3952
3856
|
return null;
|
|
3953
3857
|
}
|
|
3954
3858
|
function extractNameFromFilePath3(filePath) {
|
|
3955
|
-
const
|
|
3956
|
-
return
|
|
3859
|
+
const basename10 = path6.basename(filePath);
|
|
3860
|
+
return basename10.replace(/\.json$/i, "") || "\u672A\u77E5\u7FA4\u804A";
|
|
3957
3861
|
}
|
|
3958
3862
|
function isPlaceholderSenderId(id) {
|
|
3959
3863
|
return id === "0" || id === "\u672A\u77E5";
|
|
@@ -4553,8 +4457,8 @@ var { pick: pick4 } = import_Pick4.default;
|
|
|
4553
4457
|
var { streamValues: streamValues4 } = import_StreamValues4.default;
|
|
4554
4458
|
var { chain: chain4 } = import_stream_chain4.default;
|
|
4555
4459
|
function extractNameFromFilePath4(filePath) {
|
|
4556
|
-
const
|
|
4557
|
-
const name =
|
|
4460
|
+
const basename10 = path8.basename(filePath);
|
|
4461
|
+
const name = basename10.replace(/\.json$/i, "");
|
|
4558
4462
|
return name || "\u672A\u77E5\u804A\u5929";
|
|
4559
4463
|
}
|
|
4560
4464
|
var feature5 = {
|
|
@@ -5465,13 +5369,16 @@ var module_8 = {
|
|
|
5465
5369
|
var telegram_native_default = module_8;
|
|
5466
5370
|
|
|
5467
5371
|
// ../../packages/parser/src/formats/telegram-native-single.ts
|
|
5468
|
-
var
|
|
5372
|
+
var import_Assembler = __toESM(require_Assembler(), 1);
|
|
5469
5373
|
var import_stream_json7 = __toESM(require_stream_json(), 1);
|
|
5470
|
-
var import_StreamValues7 = __toESM(require_StreamValues(), 1);
|
|
5471
5374
|
import * as fs12 from "fs";
|
|
5472
|
-
|
|
5375
|
+
|
|
5376
|
+
// ../../packages/parser/src/browser/telegram.ts
|
|
5377
|
+
var TELEGRAM_SINGLE_CHAT_TYPE_PATTERN = /"type"\s*:\s*"(personal_chat|bot_chat|private_group|private_supergroup|public_group|public_supergroup|public_channel|private_channel|saved_messages)"/;
|
|
5378
|
+
var TELEGRAM_SINGLE_HEAD_SIGNATURES = [/^\s*\{\s*(?:\r?\n\s*)?"name"\s*:/];
|
|
5379
|
+
|
|
5380
|
+
// ../../packages/parser/src/formats/telegram-native-single.ts
|
|
5473
5381
|
var { parser: parser8 } = import_stream_json7.default;
|
|
5474
|
-
var { streamValues: streamValues7 } = import_StreamValues7.default;
|
|
5475
5382
|
var feature9 = {
|
|
5476
5383
|
id: "telegram-native-single",
|
|
5477
5384
|
name: "Telegram \u5355\u804A\u5929\u5BFC\u51FA (JSON)",
|
|
@@ -5479,98 +5386,97 @@ var feature9 = {
|
|
|
5479
5386
|
priority: 23,
|
|
5480
5387
|
extensions: [".json"],
|
|
5481
5388
|
signatures: {
|
|
5482
|
-
|
|
5483
|
-
// 这与全量导出(以 "about" 开头)有效区分,避免嵌套字段误匹配
|
|
5484
|
-
head: [/^\s*\{\s*\r?\n\s*"name"\s*:/],
|
|
5389
|
+
head: TELEGRAM_SINGLE_HEAD_SIGNATURES,
|
|
5485
5390
|
requiredFields: ["messages"],
|
|
5486
|
-
fieldPatterns: {
|
|
5487
|
-
// Telegram 特有的聊天类型值,精确区分
|
|
5488
|
-
telegramChatType: /"type"\s*:\s*"(personal_chat|bot_chat|private_group|private_supergroup|public_group|public_supergroup|public_channel|private_channel|saved_messages)"/
|
|
5489
|
-
}
|
|
5391
|
+
fieldPatterns: { telegramChatType: TELEGRAM_SINGLE_CHAT_TYPE_PATTERN }
|
|
5490
5392
|
}
|
|
5491
5393
|
};
|
|
5492
5394
|
async function* parseTelegramSingle(options2) {
|
|
5493
5395
|
const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
|
|
5494
5396
|
const totalBytes = getFileSize(filePath);
|
|
5495
|
-
let messagesProcessed = 0;
|
|
5496
5397
|
const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
|
|
5497
5398
|
yield { type: "progress", data: initialProgress };
|
|
5498
5399
|
onProgress?.(initialProgress);
|
|
5499
|
-
onLog?.("info",
|
|
5500
|
-
|
|
5501
|
-
const readStream = fs12.createReadStream(filePath, { encoding: "utf-8" });
|
|
5502
|
-
const pipeline2 = chain7([readStream, parser8(), streamValues7()]);
|
|
5503
|
-
let found = false;
|
|
5504
|
-
pipeline2.on("data", ({ value }) => {
|
|
5505
|
-
if (!found) {
|
|
5506
|
-
found = true;
|
|
5507
|
-
resolve4(value);
|
|
5508
|
-
}
|
|
5509
|
-
});
|
|
5510
|
-
pipeline2.on("end", () => {
|
|
5511
|
-
if (!found) resolve4(null);
|
|
5512
|
-
});
|
|
5513
|
-
pipeline2.on("error", reject);
|
|
5514
|
-
});
|
|
5515
|
-
if (!chatData) {
|
|
5516
|
-
onLog?.("error", "\u65E0\u6CD5\u89E3\u6790 Telegram \u5355\u804A\u5929\u6587\u4EF6");
|
|
5517
|
-
yield { type: "error", data: new Error("\u65E0\u6CD5\u89E3\u6790 Telegram \u5355\u804A\u5929\u6587\u4EF6") };
|
|
5518
|
-
return;
|
|
5519
|
-
}
|
|
5520
|
-
onLog?.("info", `\u804A\u5929: "${chatData.name}", \u7C7B\u578B: ${chatData.type}, \u6D88\u606F\u6570: ${chatData.messages?.length || 0}`);
|
|
5521
|
-
const chatType = mapChatType(chatData.type);
|
|
5522
|
-
const meta = {
|
|
5523
|
-
name: chatData.name || `Telegram Chat ${chatData.id}`,
|
|
5524
|
-
platform: KNOWN_PLATFORMS.TELEGRAM,
|
|
5525
|
-
type: chatType,
|
|
5526
|
-
groupId: chatType === "group" /* GROUP */ ? String(chatData.id) : void 0
|
|
5527
|
-
};
|
|
5528
|
-
yield { type: "meta", data: meta };
|
|
5400
|
+
onLog?.("info", `Starting Telegram single-chat JSON parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`);
|
|
5401
|
+
let messagesProcessed = 0;
|
|
5529
5402
|
const memberMap = /* @__PURE__ */ new Map();
|
|
5530
5403
|
const messageBatch = [];
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
let
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5404
|
+
try {
|
|
5405
|
+
const readStream = fs12.createReadStream(filePath, { encoding: "utf-8" });
|
|
5406
|
+
const tokenStream = readStream.pipe(parser8());
|
|
5407
|
+
let depth = 0;
|
|
5408
|
+
let rootKey = null;
|
|
5409
|
+
let chatName = null;
|
|
5410
|
+
let telegramType = null;
|
|
5411
|
+
let chatId = null;
|
|
5412
|
+
let messagesArrayDepth = null;
|
|
5413
|
+
let messageAssembler = null;
|
|
5414
|
+
let meta = null;
|
|
5415
|
+
for await (const rawToken of tokenStream) {
|
|
5416
|
+
const token = rawToken;
|
|
5417
|
+
if (messageAssembler) {
|
|
5418
|
+
messageAssembler.consume(token);
|
|
5419
|
+
if (messageAssembler.done) {
|
|
5420
|
+
const message = messageAssembler.current;
|
|
5421
|
+
messageAssembler = null;
|
|
5422
|
+
const parsedMessage = parseTelegramMessage(message, memberMap);
|
|
5423
|
+
if (parsedMessage) {
|
|
5424
|
+
messageBatch.push(parsedMessage);
|
|
5425
|
+
messagesProcessed++;
|
|
5426
|
+
}
|
|
5427
|
+
if (messageBatch.length >= batchSize) {
|
|
5428
|
+
yield { type: "messages", data: messageBatch.splice(0) };
|
|
5429
|
+
const progress = createProgress(
|
|
5430
|
+
"parsing",
|
|
5431
|
+
readStream.bytesRead,
|
|
5432
|
+
totalBytes,
|
|
5433
|
+
messagesProcessed,
|
|
5434
|
+
`Processed ${messagesProcessed} messages`
|
|
5435
|
+
);
|
|
5436
|
+
yield { type: "progress", data: progress };
|
|
5437
|
+
onProgress?.(progress);
|
|
5438
|
+
}
|
|
5439
|
+
}
|
|
5440
|
+
} else if (token.name === "keyValue" && depth === 1) {
|
|
5441
|
+
rootKey = token.value ?? null;
|
|
5442
|
+
} else if (token.name === "stringValue" && depth === 1) {
|
|
5443
|
+
if (rootKey === "name") chatName = token.value ?? "";
|
|
5444
|
+
if (rootKey === "type") telegramType = token.value ?? "";
|
|
5445
|
+
} else if (token.name === "numberValue" && depth === 1 && rootKey === "id") {
|
|
5446
|
+
const parsedId = Number(token.value);
|
|
5447
|
+
if (Number.isFinite(parsedId)) chatId = parsedId;
|
|
5448
|
+
}
|
|
5449
|
+
if (token.name === "startArray" && depth === 1 && rootKey === "messages") {
|
|
5450
|
+
if (chatName === null || telegramType === null || chatId === null) {
|
|
5451
|
+
throw new Error("Invalid Telegram single-chat JSON export");
|
|
5452
|
+
}
|
|
5453
|
+
const chatType = mapChatType(telegramType);
|
|
5454
|
+
meta = {
|
|
5455
|
+
name: chatName || `Telegram Chat ${chatId}`,
|
|
5456
|
+
platform: KNOWN_PLATFORMS.TELEGRAM,
|
|
5457
|
+
type: chatType,
|
|
5458
|
+
groupId: chatType === "group" /* GROUP */ ? String(chatId) : void 0
|
|
5459
|
+
};
|
|
5460
|
+
messagesArrayDepth = depth + 1;
|
|
5461
|
+
yield { type: "meta", data: meta };
|
|
5462
|
+
} else if (token.name === "startObject" && messagesArrayDepth !== null && depth === messagesArrayDepth) {
|
|
5463
|
+
messageAssembler = new import_Assembler.default();
|
|
5464
|
+
messageAssembler.consume(token);
|
|
5465
|
+
}
|
|
5466
|
+
if (token.name === "startObject" || token.name === "startArray") {
|
|
5467
|
+
depth++;
|
|
5468
|
+
} else if (token.name === "endObject" || token.name === "endArray") {
|
|
5469
|
+
depth--;
|
|
5470
|
+
}
|
|
5547
5471
|
}
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
const parsedMsg = {
|
|
5551
|
-
platformMessageId: String(msg.id),
|
|
5552
|
-
senderPlatformId,
|
|
5553
|
-
senderAccountName: senderName,
|
|
5554
|
-
timestamp,
|
|
5555
|
-
type: detectMessageType(msg),
|
|
5556
|
-
content: buildContent(msg),
|
|
5557
|
-
replyToMessageId: msg.reply_to_message_id ? String(msg.reply_to_message_id) : void 0
|
|
5558
|
-
};
|
|
5559
|
-
messageBatch.push(parsedMsg);
|
|
5560
|
-
messagesProcessed++;
|
|
5561
|
-
if (messageBatch.length >= batchSize) {
|
|
5562
|
-
yield { type: "messages", data: [...messageBatch] };
|
|
5563
|
-
messageBatch.length = 0;
|
|
5564
|
-
const progress = createProgress(
|
|
5565
|
-
"parsing",
|
|
5566
|
-
0,
|
|
5567
|
-
totalBytes,
|
|
5568
|
-
messagesProcessed,
|
|
5569
|
-
`\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`
|
|
5570
|
-
);
|
|
5571
|
-
yield { type: "progress", data: progress };
|
|
5572
|
-
onProgress?.(progress);
|
|
5472
|
+
if (!meta || messagesArrayDepth === null || messageAssembler) {
|
|
5473
|
+
throw new Error("Invalid Telegram single-chat JSON export");
|
|
5573
5474
|
}
|
|
5475
|
+
} catch (error) {
|
|
5476
|
+
const parseError = error instanceof Error ? error : new Error(String(error));
|
|
5477
|
+
onLog?.("error", "Telegram single-chat JSON parsing failed");
|
|
5478
|
+
yield { type: "error", data: parseError };
|
|
5479
|
+
return;
|
|
5574
5480
|
}
|
|
5575
5481
|
yield { type: "members", data: Array.from(memberMap.values()) };
|
|
5576
5482
|
if (messageBatch.length > 0) {
|
|
@@ -5579,77 +5485,63 @@ async function* parseTelegramSingle(options2) {
|
|
|
5579
5485
|
const doneProgress = createProgress("done", totalBytes, totalBytes, messagesProcessed, "");
|
|
5580
5486
|
yield { type: "progress", data: doneProgress };
|
|
5581
5487
|
onProgress?.(doneProgress);
|
|
5582
|
-
onLog?.(
|
|
5488
|
+
onLog?.(
|
|
5489
|
+
"info",
|
|
5490
|
+
`Telegram single-chat JSON parsing completed: ${messagesProcessed} messages, ${memberMap.size} members`
|
|
5491
|
+
);
|
|
5583
5492
|
yield {
|
|
5584
5493
|
type: "done",
|
|
5585
5494
|
data: { messageCount: messagesProcessed, memberCount: memberMap.size }
|
|
5586
5495
|
};
|
|
5587
5496
|
}
|
|
5588
|
-
|
|
5589
|
-
|
|
5590
|
-
|
|
5591
|
-
|
|
5592
|
-
|
|
5593
|
-
|
|
5594
|
-
|
|
5595
|
-
|
|
5497
|
+
function parseTelegramMessage(message, memberMap) {
|
|
5498
|
+
const isService = message.type === "service";
|
|
5499
|
+
const senderPlatformId = extractPlatformId(isService ? message.actor_id : message.from_id);
|
|
5500
|
+
const senderName = isService ? message.actor || "System" : message.from || senderPlatformId;
|
|
5501
|
+
if (!memberMap.has(senderPlatformId) && senderPlatformId !== "unknown") {
|
|
5502
|
+
memberMap.set(senderPlatformId, {
|
|
5503
|
+
platformId: senderPlatformId,
|
|
5504
|
+
accountName: senderName
|
|
5505
|
+
});
|
|
5506
|
+
}
|
|
5507
|
+
const timestamp = Number.parseInt(message.date_unixtime, 10);
|
|
5508
|
+
if (Number.isNaN(timestamp)) return null;
|
|
5509
|
+
return {
|
|
5510
|
+
platformMessageId: String(message.id),
|
|
5511
|
+
senderPlatformId,
|
|
5512
|
+
senderAccountName: senderName,
|
|
5513
|
+
timestamp,
|
|
5514
|
+
type: detectMessageType(message),
|
|
5515
|
+
content: buildContent(message),
|
|
5516
|
+
replyToMessageId: message.reply_to_message_id ? String(message.reply_to_message_id) : void 0
|
|
5517
|
+
};
|
|
5518
|
+
}
|
|
5519
|
+
var parser_8 = { feature: feature9, parse: parseTelegramSingle };
|
|
5520
|
+
var module_9 = { feature: feature9, parser: parser_8 };
|
|
5596
5521
|
var telegram_native_single_default = module_9;
|
|
5597
5522
|
|
|
5598
5523
|
// ../../packages/parser/src/formats/whatsapp-native-txt.ts
|
|
5599
5524
|
import * as fs13 from "fs";
|
|
5600
|
-
import * as path9 from "path";
|
|
5601
5525
|
import * as readline3 from "readline";
|
|
5602
|
-
|
|
5603
|
-
|
|
5604
|
-
|
|
5605
|
-
|
|
5606
|
-
|
|
5607
|
-
|
|
5608
|
-
|
|
5609
|
-
}
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
}
|
|
5618
|
-
var feature10 = {
|
|
5619
|
-
id: "whatsapp-native-txt",
|
|
5620
|
-
name: "WhatsApp \u5B98\u65B9\u5BFC\u51FA (TXT)",
|
|
5621
|
-
platform: KNOWN_PLATFORMS.WHATSAPP,
|
|
5622
|
-
priority: 25,
|
|
5623
|
-
extensions: [".txt"],
|
|
5624
|
-
signatures: {
|
|
5625
|
-
// WhatsApp 导出文件的特征(中文/英文)
|
|
5626
|
-
// 注意:仅保留 WhatsApp 独有的特征,避免误匹配其他 TXT 格式(如 LINE)
|
|
5627
|
-
head: [
|
|
5628
|
-
/消息和通话已进行端到端加密/,
|
|
5629
|
-
// 简体中文加密提示(WhatsApp 独有)
|
|
5630
|
-
/訊息與通話已受端對端加密保護/,
|
|
5631
|
-
// 繁体中文加密提示(WhatsApp 独有)
|
|
5632
|
-
/Messages and calls are end-to-end encrypted/i,
|
|
5633
|
-
// 英文加密提示(WhatsApp 独有)
|
|
5634
|
-
/你发送给自己的消息已进行端到端加密/,
|
|
5635
|
-
// 简体中文自己对话提示(WhatsApp 独有)
|
|
5636
|
-
/\d{1,4}\/\d{1,2}\/\d{2,4},?\s+\d{1,2}:\d{2}(?::\d{2})?(?:[\s\u2009\u202F]*(?:AM|PM|A\.M\.|P\.M\.|上午|下午|午前|午後|오전|오후))?\s+- /i,
|
|
5637
|
-
// 消息行格式特征(无方括号,含 " - " 分隔符,WhatsApp 独有)
|
|
5638
|
-
/\[\d{1,4}\/\d{1,2}\/\d{1,4}[\s,].*\d{1,2}:\d{2}(?::\d{2})?.*\] /
|
|
5639
|
-
// 消息行格式特征(方括号 + 含日期和时间的时间戳,兼容各地区变体)
|
|
5640
|
-
],
|
|
5641
|
-
// 文件名特征:与xxx的 WhatsApp 聊天.txt
|
|
5642
|
-
filename: [/^与.+的\s*WhatsApp\s*聊天\.txt$/i, /^與.+的\s*WhatsApp\s*對話\.txt$/i, /WhatsApp/i]
|
|
5643
|
-
}
|
|
5644
|
-
};
|
|
5645
|
-
function cleanLine(line) {
|
|
5646
|
-
return line.replace(/^(?:\uFEFF|\u200E|\u200F|\u200B|\u200C|\u200D|\u2060)+/, "").trim();
|
|
5647
|
-
}
|
|
5648
|
-
var MESSAGE_LINE_REGEX_V1 = /^(\d{1,4}\/\d{1,2}\/\d{2,4},?\s+\d{1,2}:\d{2}(?::\d{2})?(?:[\s\u2009\u202F]*(?:AM|PM|A\.M\.|P\.M\.|上午|下午|午前|午後|오전|오후))?) - (.+)$/i;
|
|
5526
|
+
|
|
5527
|
+
// ../../packages/parser/src/browser/whatsapp.ts
|
|
5528
|
+
var WHATSAPP_HEAD_SIGNATURES = [
|
|
5529
|
+
/消息和通话已进行端到端加密/,
|
|
5530
|
+
/訊息與通話已受端對端加密保護/,
|
|
5531
|
+
/Messages and calls are end-to-end encrypted/i,
|
|
5532
|
+
/你发送给自己的消息已进行端到端加密/,
|
|
5533
|
+
/\d{1,4}\/\d{1,2}\/\d{2,4},?\s+\d{1,2}:\d{2}(?::\d{2})?(?:[\s\u2009\u202F]*(?:AM|PM|A\.M\.|P\.M\.|\u4e0a午|\u4e0b午|\u5348前|\u5348後|오전|오후))?\s+- /i,
|
|
5534
|
+
/\[\d{1,4}\/\d{1,2}\/\d{1,4}[\s,].*\d{1,2}:\d{2}(?::\d{2})?.*\] /
|
|
5535
|
+
];
|
|
5536
|
+
var WHATSAPP_FILENAME_SIGNATURES = [
|
|
5537
|
+
/^与.+的\s*WhatsApp\s*聊天\.txt$/i,
|
|
5538
|
+
/^與.+的\s*WhatsApp\s*對話\.txt$/i,
|
|
5539
|
+
/WhatsApp/i
|
|
5540
|
+
];
|
|
5541
|
+
var MESSAGE_LINE_REGEX_V1 = /^(\d{1,4}\/\d{1,2}\/\d{2,4},?\s+\d{1,2}:\d{2}(?::\d{2})?(?:[\s\u2009\u202F]*(?:AM|PM|A\.M\.|P\.M\.|\u4e0a午|\u4e0b午|\u5348前|\u5348後|오전|오후))?) - (.+)$/i;
|
|
5649
5542
|
var MESSAGE_LINE_REGEX_V2 = /^\[([^\]]+)\] (.+)$/;
|
|
5650
5543
|
var SENDER_CONTENT_REGEX = /^(.+?):[\s\u200E]*(.*)$/;
|
|
5651
5544
|
var SYSTEM_MESSAGE_PATTERNS = [
|
|
5652
|
-
// 简体中文系统消息
|
|
5653
5545
|
/消息和通话已进行端到端加密/,
|
|
5654
5546
|
/创建了此群组/,
|
|
5655
5547
|
/加入群组/,
|
|
@@ -5661,7 +5553,6 @@ var SYSTEM_MESSAGE_PATTERNS = [
|
|
|
5661
5553
|
/这条消息已删除/,
|
|
5662
5554
|
/限时消息功能/,
|
|
5663
5555
|
/正在等待此消息/,
|
|
5664
|
-
// 繁体中文系统消息
|
|
5665
5556
|
/訊息與通話已受端對端加密保護/,
|
|
5666
5557
|
/建立了此群組/,
|
|
5667
5558
|
/加入了群組/,
|
|
@@ -5671,7 +5562,6 @@ var SYSTEM_MESSAGE_PATTERNS = [
|
|
|
5671
5562
|
/已變更本群組/,
|
|
5672
5563
|
/此訊息已刪除/,
|
|
5673
5564
|
/限時訊息/,
|
|
5674
|
-
// 英文系统消息
|
|
5675
5565
|
/Messages and calls are end-to-end encrypted/i,
|
|
5676
5566
|
/created this group/i,
|
|
5677
5567
|
/joined the group/i,
|
|
@@ -5682,21 +5572,6 @@ var SYSTEM_MESSAGE_PATTERNS = [
|
|
|
5682
5572
|
/This message was deleted/i,
|
|
5683
5573
|
/disappearing messages/i
|
|
5684
5574
|
];
|
|
5685
|
-
function isSystemMessage(content) {
|
|
5686
|
-
return SYSTEM_MESSAGE_PATTERNS.some((pattern) => pattern.test(content));
|
|
5687
|
-
}
|
|
5688
|
-
function detectMessageType2(content) {
|
|
5689
|
-
const trimmed = content.trim();
|
|
5690
|
-
if (trimmed === "<\u7701\u7565\u5F71\u97F3\u5185\u5BB9>" || trimmed === "<\u5DF2\u7701\u7565\u591A\u5A92\u9AD4\u6A94\u6848>" || trimmed === "\u5716\u7247\u5DF2\u7565\u53BB" || trimmed === "\u5F71\u7247\u5DF2\u7565\u53BB" || trimmed === "\u97F3\u8A0A\u5DF2\u7565\u53BB" || trimmed === "image omitted" || trimmed === "video omitted" || trimmed === "audio omitted")
|
|
5691
|
-
return 1 /* IMAGE */;
|
|
5692
|
-
if (trimmed.includes("<\u5DF2\u9644\u52A0:") || trimmed.includes("<\u9644\u4EF6:") || trimmed.includes("<\u5DF2\u9644\u52A0\uFF1A"))
|
|
5693
|
-
return 4 /* FILE */;
|
|
5694
|
-
if (trimmed === "\u8CBC\u5716\u5DF2\u5FFD\u7565" || trimmed === "\u8CBC\u5716\u5DF2\u7565\u53BB") return 5 /* EMOJI */;
|
|
5695
|
-
if (trimmed === "\u8FD9\u6761\u6D88\u606F\u5DF2\u5220\u9664" || trimmed.startsWith("\u6B64\u8A0A\u606F\u5DF2\u522A\u9664") || trimmed.startsWith("\u4F60\u5DF2\u522A\u9664\u6B64\u8A0A\u606F"))
|
|
5696
|
-
return 81 /* RECALL */;
|
|
5697
|
-
if (isSystemMessage(trimmed)) return 80 /* SYSTEM */;
|
|
5698
|
-
return 0 /* TEXT */;
|
|
5699
|
-
}
|
|
5700
5575
|
var AMPM_MARKERS = [
|
|
5701
5576
|
[/\bPM\b/i, true],
|
|
5702
5577
|
[/\bP\.M\.(?!\w)/i, true],
|
|
@@ -5709,191 +5584,226 @@ var AMPM_MARKERS = [
|
|
|
5709
5584
|
[/午前/, false],
|
|
5710
5585
|
[/오전/, false]
|
|
5711
5586
|
];
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5715
|
-
|
|
5716
|
-
|
|
5717
|
-
|
|
5718
|
-
|
|
5719
|
-
|
|
5587
|
+
var WhatsAppTextAccumulator = class {
|
|
5588
|
+
constructor(fileName) {
|
|
5589
|
+
this.fileName = fileName;
|
|
5590
|
+
this.fileChatName = extractNameFromFileName(fileName);
|
|
5591
|
+
}
|
|
5592
|
+
fileName;
|
|
5593
|
+
fileChatName;
|
|
5594
|
+
memberMap = /* @__PURE__ */ new Map();
|
|
5595
|
+
messages = [];
|
|
5596
|
+
currentMessage = null;
|
|
5597
|
+
skippedLines = 0;
|
|
5598
|
+
get messageCount() {
|
|
5599
|
+
return this.messages.length + (this.currentMessage ? 1 : 0);
|
|
5600
|
+
}
|
|
5601
|
+
pushLine(line) {
|
|
5602
|
+
const cleanedLine = cleanLine(line);
|
|
5603
|
+
const v1Match = cleanedLine.match(MESSAGE_LINE_REGEX_V1);
|
|
5604
|
+
if (v1Match) {
|
|
5605
|
+
this.saveCurrentMessage();
|
|
5606
|
+
this.currentMessage = createPendingMessage(parseFlexibleTimestamp(v1Match[1]) ?? 0, v1Match[2]);
|
|
5607
|
+
return;
|
|
5608
|
+
}
|
|
5609
|
+
const v2Match = cleanedLine.match(MESSAGE_LINE_REGEX_V2);
|
|
5610
|
+
if (v2Match) {
|
|
5611
|
+
const timestamp = parseFlexibleTimestamp(v2Match[1]);
|
|
5612
|
+
if (timestamp !== null) {
|
|
5613
|
+
this.saveCurrentMessage();
|
|
5614
|
+
this.currentMessage = createPendingMessage(timestamp, v2Match[2]);
|
|
5615
|
+
return;
|
|
5616
|
+
}
|
|
5617
|
+
}
|
|
5618
|
+
if (this.currentMessage && cleanedLine) {
|
|
5619
|
+
this.currentMessage.contentLines.push(cleanedLine);
|
|
5620
|
+
} else if (cleanedLine) {
|
|
5621
|
+
this.skippedLines += 1;
|
|
5622
|
+
}
|
|
5623
|
+
}
|
|
5624
|
+
finish() {
|
|
5625
|
+
this.saveCurrentMessage();
|
|
5626
|
+
const chatType = this.memberMap.size > 2 ? "group" /* GROUP */ : "private" /* PRIVATE */;
|
|
5627
|
+
const chatName = chatType === "private" /* PRIVATE */ && shouldInferPrivateChatName(this.fileName, this.fileChatName) ? inferPrivateChatName(this.memberMap, this.fileChatName) : this.fileChatName;
|
|
5628
|
+
return {
|
|
5629
|
+
meta: { name: chatName, platform: KNOWN_PLATFORMS.WHATSAPP, type: chatType },
|
|
5630
|
+
members: Array.from(this.memberMap.values(), (member) => ({
|
|
5631
|
+
platformId: member.platformId,
|
|
5632
|
+
accountName: member.nickname
|
|
5633
|
+
})),
|
|
5634
|
+
messages: this.messages,
|
|
5635
|
+
skippedLines: this.skippedLines
|
|
5636
|
+
};
|
|
5637
|
+
}
|
|
5638
|
+
saveCurrentMessage() {
|
|
5639
|
+
if (!this.currentMessage) return;
|
|
5640
|
+
const content = this.currentMessage.contentLines.join("\n").trim();
|
|
5641
|
+
const senderPlatformId = this.currentMessage.sender ?? "system";
|
|
5642
|
+
const senderName = this.currentMessage.sender ?? "\u7CFB\u7EDF\u6D88\u606F";
|
|
5643
|
+
this.messages.push({
|
|
5644
|
+
senderPlatformId,
|
|
5645
|
+
senderAccountName: senderName,
|
|
5646
|
+
timestamp: this.currentMessage.timestamp,
|
|
5647
|
+
type: detectMessageType2(content),
|
|
5648
|
+
content: content || null
|
|
5649
|
+
});
|
|
5650
|
+
if (this.currentMessage.sender) {
|
|
5651
|
+
this.memberMap.set(senderPlatformId, { platformId: senderPlatformId, nickname: senderName });
|
|
5720
5652
|
}
|
|
5653
|
+
this.currentMessage = null;
|
|
5654
|
+
}
|
|
5655
|
+
};
|
|
5656
|
+
function createPendingMessage(timestamp, restContent) {
|
|
5657
|
+
const senderMatch = restContent.match(SENDER_CONTENT_REGEX);
|
|
5658
|
+
if (senderMatch && !isSystemMessage(restContent)) {
|
|
5659
|
+
return { timestamp, sender: senderMatch[1].trim(), contentLines: [senderMatch[2]] };
|
|
5660
|
+
}
|
|
5661
|
+
return { timestamp, sender: null, contentLines: [restContent] };
|
|
5662
|
+
}
|
|
5663
|
+
function extractNameFromFileName(fileName) {
|
|
5664
|
+
const basename10 = getBasename(fileName);
|
|
5665
|
+
const simplifiedChinese = basename10.match(/^与(.+?)的\s*WhatsApp\s*聊天\.txt$/i);
|
|
5666
|
+
if (simplifiedChinese) return simplifiedChinese[1].trim();
|
|
5667
|
+
const traditionalChinese = basename10.match(/^與(.+?)的\s*WhatsApp\s*對話\.txt$/i);
|
|
5668
|
+
if (traditionalChinese) return traditionalChinese[1].trim();
|
|
5669
|
+
return basename10.replace(/\.txt$/i, "") || "\u672A\u77E5\u804A\u5929";
|
|
5670
|
+
}
|
|
5671
|
+
function shouldInferPrivateChatName(fileName, fallbackName) {
|
|
5672
|
+
const basename10 = getBasename(fileName);
|
|
5673
|
+
return /^_chat\.txt$/i.test(basename10) || /^whatsapp(?:[-_\s].*)?$/i.test(fallbackName);
|
|
5674
|
+
}
|
|
5675
|
+
function inferPrivateChatName(memberMap, fallbackName) {
|
|
5676
|
+
return Array.from(memberMap.values()).find((member) => member.platformId !== "system")?.nickname ?? fallbackName;
|
|
5677
|
+
}
|
|
5678
|
+
function getBasename(fileName) {
|
|
5679
|
+
return fileName.split(/[\\/]/).pop() ?? fileName;
|
|
5680
|
+
}
|
|
5681
|
+
function cleanLine(line) {
|
|
5682
|
+
return line.replace(/^(?:\uFEFF|\u200E|\u200F|\u200B|\u200C|\u200D|\u2060)+/, "").trim();
|
|
5683
|
+
}
|
|
5684
|
+
function isSystemMessage(content) {
|
|
5685
|
+
return SYSTEM_MESSAGE_PATTERNS.some((pattern) => pattern.test(content));
|
|
5686
|
+
}
|
|
5687
|
+
function detectMessageType2(content) {
|
|
5688
|
+
const trimmed = content.trim();
|
|
5689
|
+
if (trimmed === "<\u7701\u7565\u5F71\u97F3\u5185\u5BB9>" || trimmed === "<\u5DF2\u7701\u7565\u591A\u5A92\u9AD4\u6A94\u6848>" || trimmed === "\u5716\u7247\u5DF2\u7565\u53BB" || trimmed === "\u5F71\u7247\u5DF2\u7565\u53BB" || trimmed === "\u97F3\u8A0A\u5DF2\u7565\u53BB" || trimmed === "image omitted" || trimmed === "video omitted" || trimmed === "audio omitted") {
|
|
5690
|
+
return 1 /* IMAGE */;
|
|
5691
|
+
}
|
|
5692
|
+
if (trimmed.includes("<\u5DF2\u9644\u52A0:") || trimmed.includes("<\u9644\u4EF6:") || trimmed.includes("<\u5DF2\u9644\u52A0\uFF1A")) {
|
|
5693
|
+
return 4 /* FILE */;
|
|
5694
|
+
}
|
|
5695
|
+
if (trimmed === "\u8CBC\u5716\u5DF2\u5FFD\u7565" || trimmed === "\u8CBC\u5716\u5DF2\u7565\u53BB") return 5 /* EMOJI */;
|
|
5696
|
+
if (trimmed === "\u8FD9\u6761\u6D88\u606F\u5DF2\u5220\u9664" || trimmed.startsWith("\u6B64\u8A0A\u606F\u5DF2\u522A\u9664") || trimmed.startsWith("\u4F60\u5DF2\u522A\u9664\u6B64\u8A0A\u606F")) {
|
|
5697
|
+
return 81 /* RECALL */;
|
|
5698
|
+
}
|
|
5699
|
+
if (isSystemMessage(trimmed)) return 80 /* SYSTEM */;
|
|
5700
|
+
return 0 /* TEXT */;
|
|
5701
|
+
}
|
|
5702
|
+
function parseFlexibleTimestamp(raw) {
|
|
5703
|
+
let value = raw.replace(/(?:\u2009|\u202F|\uFEFF|\u200E|\u200F|\u200B|\u200C|\u200D|\u2060)/g, " ").trim();
|
|
5704
|
+
let isPm = null;
|
|
5705
|
+
for (const [pattern, pm] of AMPM_MARKERS) {
|
|
5706
|
+
if (!pattern.test(value)) continue;
|
|
5707
|
+
isPm = pm;
|
|
5708
|
+
value = value.replace(pattern, "").trim();
|
|
5709
|
+
break;
|
|
5721
5710
|
}
|
|
5722
|
-
|
|
5723
|
-
const match =
|
|
5711
|
+
value = value.replace(/,/g, " ").replace(/\s+/g, " ").trim();
|
|
5712
|
+
const match = value.match(/^(\d{1,4}\/\d{1,2}\/\d{1,4})\s+(\d{1,2}:\d{2}(?::\d{2})?)$/);
|
|
5724
5713
|
if (!match) return null;
|
|
5725
|
-
const
|
|
5726
|
-
const
|
|
5727
|
-
|
|
5728
|
-
let
|
|
5714
|
+
const dateParts = match[1].split("/").map((part) => Number.parseInt(part, 10));
|
|
5715
|
+
const timeParts = match[2].split(":").map((part) => Number.parseInt(part, 10));
|
|
5716
|
+
let year;
|
|
5717
|
+
let month;
|
|
5718
|
+
let day;
|
|
5729
5719
|
if (dateParts[0] > 31) {
|
|
5730
|
-
|
|
5731
|
-
month = dateParts
|
|
5732
|
-
day = dateParts[2];
|
|
5720
|
+
;
|
|
5721
|
+
[year, month, day] = dateParts;
|
|
5733
5722
|
} else if (dateParts[2] > 31) {
|
|
5734
|
-
|
|
5735
|
-
month = dateParts
|
|
5736
|
-
year = dateParts[2];
|
|
5723
|
+
;
|
|
5724
|
+
[day, month, year] = dateParts;
|
|
5737
5725
|
} else {
|
|
5738
|
-
|
|
5739
|
-
day = dateParts
|
|
5726
|
+
;
|
|
5727
|
+
[month, day] = dateParts;
|
|
5740
5728
|
year = 2e3 + dateParts[2];
|
|
5741
5729
|
}
|
|
5742
5730
|
let hour = timeParts[0];
|
|
5743
5731
|
const minute = timeParts[1];
|
|
5744
5732
|
const second = timeParts[2] ?? 0;
|
|
5745
|
-
if (
|
|
5746
|
-
if (
|
|
5733
|
+
if (isPm === true && hour !== 12) hour += 12;
|
|
5734
|
+
if (isPm === false && hour === 12) hour = 0;
|
|
5747
5735
|
const date = new Date(year, month - 1, day, hour, minute, second);
|
|
5748
|
-
const
|
|
5749
|
-
return isNaN(
|
|
5750
|
-
}
|
|
5751
|
-
function parseV1Timestamp(timeStr) {
|
|
5752
|
-
return parseFlexibleV2Timestamp(timeStr) ?? 0;
|
|
5736
|
+
const timestamp = Math.floor(date.getTime() / 1e3);
|
|
5737
|
+
return Number.isNaN(timestamp) ? null : timestamp;
|
|
5753
5738
|
}
|
|
5739
|
+
|
|
5740
|
+
// ../../packages/parser/src/formats/whatsapp-native-txt.ts
|
|
5741
|
+
var feature10 = {
|
|
5742
|
+
id: "whatsapp-native-txt",
|
|
5743
|
+
name: "WhatsApp \u5B98\u65B9\u5BFC\u51FA (TXT)",
|
|
5744
|
+
platform: KNOWN_PLATFORMS.WHATSAPP,
|
|
5745
|
+
priority: 25,
|
|
5746
|
+
extensions: [".txt"],
|
|
5747
|
+
signatures: {
|
|
5748
|
+
head: WHATSAPP_HEAD_SIGNATURES,
|
|
5749
|
+
filename: WHATSAPP_FILENAME_SIGNATURES
|
|
5750
|
+
}
|
|
5751
|
+
};
|
|
5754
5752
|
async function* parseWhatsApp(options2) {
|
|
5755
5753
|
const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
|
|
5756
5754
|
const totalBytes = getFileSize(filePath);
|
|
5755
|
+
const accumulator = new WhatsAppTextAccumulator(filePath);
|
|
5757
5756
|
let bytesRead = 0;
|
|
5758
|
-
let
|
|
5759
|
-
let skippedLines = 0;
|
|
5757
|
+
let lastReportedMessages = 0;
|
|
5760
5758
|
const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
|
|
5761
5759
|
yield { type: "progress", data: initialProgress };
|
|
5762
5760
|
onProgress?.(initialProgress);
|
|
5763
|
-
onLog?.("info",
|
|
5764
|
-
const fileChatName = extractNameFromFilePath5(filePath);
|
|
5765
|
-
const memberMap = /* @__PURE__ */ new Map();
|
|
5766
|
-
const messages = [];
|
|
5767
|
-
let currentMessage = null;
|
|
5768
|
-
const saveCurrentMessage = () => {
|
|
5769
|
-
if (currentMessage) {
|
|
5770
|
-
const content = currentMessage.contentLines.join("\n").trim();
|
|
5771
|
-
const type = detectMessageType2(content);
|
|
5772
|
-
const senderPlatformId = currentMessage.sender || "system";
|
|
5773
|
-
const senderName = currentMessage.sender || "\u7CFB\u7EDF\u6D88\u606F";
|
|
5774
|
-
messages.push({
|
|
5775
|
-
senderPlatformId,
|
|
5776
|
-
senderAccountName: senderName,
|
|
5777
|
-
timestamp: currentMessage.timestamp,
|
|
5778
|
-
type,
|
|
5779
|
-
content: content || null
|
|
5780
|
-
});
|
|
5781
|
-
if (currentMessage.sender) {
|
|
5782
|
-
memberMap.set(senderPlatformId, {
|
|
5783
|
-
platformId: senderPlatformId,
|
|
5784
|
-
nickname: senderName
|
|
5785
|
-
});
|
|
5786
|
-
}
|
|
5787
|
-
messagesProcessed++;
|
|
5788
|
-
}
|
|
5789
|
-
};
|
|
5761
|
+
onLog?.("info", `Starting WhatsApp TXT parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`);
|
|
5790
5762
|
const fileStream = fs13.createReadStream(filePath, { encoding: "utf-8" });
|
|
5791
|
-
const
|
|
5792
|
-
input: fileStream,
|
|
5793
|
-
crlfDelay: Infinity
|
|
5794
|
-
});
|
|
5763
|
+
const lines = readline3.createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
5795
5764
|
fileStream.on("data", (chunk) => {
|
|
5796
5765
|
bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
|
|
5797
5766
|
});
|
|
5798
|
-
for await (const line of
|
|
5799
|
-
|
|
5800
|
-
|
|
5801
|
-
|
|
5802
|
-
|
|
5803
|
-
|
|
5804
|
-
|
|
5805
|
-
|
|
5806
|
-
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
} else {
|
|
5812
|
-
currentMessage = {
|
|
5813
|
-
timestamp: parseV1Timestamp(v1Match[1]),
|
|
5814
|
-
sender: null,
|
|
5815
|
-
contentLines: [restContent]
|
|
5816
|
-
};
|
|
5817
|
-
}
|
|
5818
|
-
if (messagesProcessed % 500 === 0) {
|
|
5819
|
-
onProgress?.(
|
|
5820
|
-
createProgress("parsing", bytesRead, totalBytes, messagesProcessed, `\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`)
|
|
5821
|
-
);
|
|
5822
|
-
}
|
|
5823
|
-
continue;
|
|
5824
|
-
}
|
|
5825
|
-
const v2Match = cleanedLine.match(MESSAGE_LINE_REGEX_V2);
|
|
5826
|
-
if (v2Match) {
|
|
5827
|
-
const timestamp = parseFlexibleV2Timestamp(v2Match[1]);
|
|
5828
|
-
if (timestamp !== null) {
|
|
5829
|
-
saveCurrentMessage();
|
|
5830
|
-
const restContent = v2Match[2];
|
|
5831
|
-
const senderMatch = restContent.match(SENDER_CONTENT_REGEX);
|
|
5832
|
-
if (senderMatch && !isSystemMessage(restContent)) {
|
|
5833
|
-
currentMessage = {
|
|
5834
|
-
timestamp,
|
|
5835
|
-
sender: senderMatch[1].trim(),
|
|
5836
|
-
contentLines: [senderMatch[2]]
|
|
5837
|
-
};
|
|
5838
|
-
} else {
|
|
5839
|
-
currentMessage = {
|
|
5840
|
-
timestamp,
|
|
5841
|
-
sender: null,
|
|
5842
|
-
contentLines: [restContent]
|
|
5843
|
-
};
|
|
5844
|
-
}
|
|
5845
|
-
if (messagesProcessed % 500 === 0) {
|
|
5846
|
-
onProgress?.(
|
|
5847
|
-
createProgress("parsing", bytesRead, totalBytes, messagesProcessed, `\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`)
|
|
5848
|
-
);
|
|
5849
|
-
}
|
|
5850
|
-
continue;
|
|
5851
|
-
}
|
|
5852
|
-
}
|
|
5853
|
-
if (currentMessage && cleanedLine) {
|
|
5854
|
-
currentMessage.contentLines.push(cleanedLine);
|
|
5855
|
-
} else if (cleanedLine) {
|
|
5856
|
-
skippedLines++;
|
|
5767
|
+
for await (const line of lines) {
|
|
5768
|
+
accumulator.pushLine(line);
|
|
5769
|
+
if (accumulator.messageCount >= lastReportedMessages + 500) {
|
|
5770
|
+
lastReportedMessages = accumulator.messageCount;
|
|
5771
|
+
onProgress?.(
|
|
5772
|
+
createProgress(
|
|
5773
|
+
"parsing",
|
|
5774
|
+
bytesRead,
|
|
5775
|
+
totalBytes,
|
|
5776
|
+
accumulator.messageCount,
|
|
5777
|
+
`Processed ${accumulator.messageCount} messages`
|
|
5778
|
+
)
|
|
5779
|
+
);
|
|
5857
5780
|
}
|
|
5858
5781
|
}
|
|
5859
|
-
|
|
5860
|
-
|
|
5861
|
-
|
|
5862
|
-
|
|
5863
|
-
|
|
5864
|
-
const meta = {
|
|
5865
|
-
name: chatName,
|
|
5866
|
-
platform: KNOWN_PLATFORMS.WHATSAPP,
|
|
5867
|
-
type: chatType
|
|
5868
|
-
};
|
|
5869
|
-
yield { type: "meta", data: meta };
|
|
5870
|
-
const members = Array.from(memberMap.values()).map((m) => ({
|
|
5871
|
-
platformId: m.platformId,
|
|
5872
|
-
accountName: m.nickname
|
|
5873
|
-
}));
|
|
5874
|
-
yield { type: "members", data: members };
|
|
5875
|
-
for (let i = 0; i < messages.length; i += batchSize) {
|
|
5876
|
-
const batch = messages.slice(i, i + batchSize);
|
|
5877
|
-
yield { type: "messages", data: batch };
|
|
5782
|
+
const result = accumulator.finish();
|
|
5783
|
+
yield { type: "meta", data: result.meta };
|
|
5784
|
+
yield { type: "members", data: result.members };
|
|
5785
|
+
for (let index = 0; index < result.messages.length; index += batchSize) {
|
|
5786
|
+
yield { type: "messages", data: result.messages.slice(index, index + batchSize) };
|
|
5878
5787
|
}
|
|
5879
|
-
const doneProgress = createProgress("done", totalBytes, totalBytes,
|
|
5788
|
+
const doneProgress = createProgress("done", totalBytes, totalBytes, result.messages.length, "");
|
|
5880
5789
|
yield { type: "progress", data: doneProgress };
|
|
5881
5790
|
onProgress?.(doneProgress);
|
|
5882
5791
|
const typeCounts = /* @__PURE__ */ new Map();
|
|
5883
|
-
for (const
|
|
5884
|
-
typeCounts.set(
|
|
5792
|
+
for (const message of result.messages) {
|
|
5793
|
+
typeCounts.set(message.type, (typeCounts.get(message.type) ?? 0) + 1);
|
|
5885
5794
|
}
|
|
5886
|
-
onLog?.("info", `\u89E3\u6790\u5B8C\u6210: ${messagesProcessed} \u6761\u6D88\u606F, ${memberMap.size} \u4E2A\u6210\u5458, \u7C7B\u578B: ${chatType}`);
|
|
5887
5795
|
onLog?.(
|
|
5888
5796
|
"info",
|
|
5889
|
-
|
|
5797
|
+
`WhatsApp TXT parsing completed: ${result.messages.length} messages, ${result.members.length} members, type ${result.meta.type}`
|
|
5890
5798
|
);
|
|
5891
|
-
|
|
5892
|
-
|
|
5893
|
-
|
|
5799
|
+
onLog?.(
|
|
5800
|
+
"info",
|
|
5801
|
+
`Message types: ${Array.from(typeCounts.entries(), ([type, count]) => `${type}=${count}`).join(", ")}`
|
|
5802
|
+
);
|
|
5803
|
+
if (result.skippedLines > 0) onLog?.("info", `Skipped ${result.skippedLines} unrecognized lines`);
|
|
5894
5804
|
yield {
|
|
5895
5805
|
type: "done",
|
|
5896
|
-
data: { messageCount:
|
|
5806
|
+
data: { messageCount: result.messages.length, memberCount: result.members.length }
|
|
5897
5807
|
};
|
|
5898
5808
|
}
|
|
5899
5809
|
var parser_9 = {
|
|
@@ -5903,32 +5813,110 @@ var parser_9 = {
|
|
|
5903
5813
|
var module_10 = {
|
|
5904
5814
|
feature: feature10,
|
|
5905
5815
|
parser: parser_9
|
|
5906
|
-
// TXT 格式不需要预处理器
|
|
5907
5816
|
};
|
|
5908
5817
|
var whatsapp_native_txt_default = module_10;
|
|
5909
5818
|
|
|
5910
5819
|
// ../../packages/parser/src/formats/qq-native-txt.ts
|
|
5911
5820
|
import * as fs14 from "fs";
|
|
5912
|
-
import * as path10 from "path";
|
|
5913
5821
|
import * as readline4 from "readline";
|
|
5914
|
-
|
|
5915
|
-
|
|
5916
|
-
|
|
5917
|
-
return name || "\u672A\u77E5\u7FA4\u804A";
|
|
5918
|
-
}
|
|
5919
|
-
var feature11 = {
|
|
5920
|
-
id: "qq-native-txt",
|
|
5921
|
-
name: "QQ \u5B98\u65B9\u5BFC\u51FA (TXT)",
|
|
5922
|
-
platform: KNOWN_PLATFORMS.QQ,
|
|
5923
|
-
priority: 30,
|
|
5924
|
-
extensions: [".txt"],
|
|
5925
|
-
signatures: {
|
|
5926
|
-
// 支持群聊导出和多人聊天(讨论组)导出
|
|
5927
|
-
head: [/消息记录(此消息记录为文本格式/, /消息对象:/, /多人聊天/]
|
|
5928
|
-
}
|
|
5929
|
-
};
|
|
5822
|
+
|
|
5823
|
+
// ../../packages/parser/src/browser/qq.ts
|
|
5824
|
+
var QQ_HEAD_SIGNATURES = [/消息记录(此消息记录为文本格式/, /消息对象:/, /多人聊天/];
|
|
5930
5825
|
var MESSAGE_HEADER_REGEX = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (.+?)(?:\(([^)]+)\)|<([^>]+)>)?$/;
|
|
5931
5826
|
var GROUP_NAME_REGEX = /^消息对象:(.+)$/;
|
|
5827
|
+
var QqTextAccumulator = class {
|
|
5828
|
+
fallbackName;
|
|
5829
|
+
memberMap = /* @__PURE__ */ new Map();
|
|
5830
|
+
lastValidNickname = /* @__PURE__ */ new Map();
|
|
5831
|
+
messages = [];
|
|
5832
|
+
groupName = "\u672A\u77E5\u7FA4\u804A";
|
|
5833
|
+
currentMessage = null;
|
|
5834
|
+
skippedLines = 0;
|
|
5835
|
+
constructor(fileName) {
|
|
5836
|
+
this.fallbackName = extractNameFromFileName2(fileName);
|
|
5837
|
+
}
|
|
5838
|
+
get messageCount() {
|
|
5839
|
+
return this.messages.length + (this.currentMessage ? 1 : 0);
|
|
5840
|
+
}
|
|
5841
|
+
pushLine(line) {
|
|
5842
|
+
const groupMatch = line.match(GROUP_NAME_REGEX);
|
|
5843
|
+
if (groupMatch) {
|
|
5844
|
+
this.groupName = groupMatch[1].trim();
|
|
5845
|
+
return;
|
|
5846
|
+
}
|
|
5847
|
+
const headerMatch = line.match(MESSAGE_HEADER_REGEX);
|
|
5848
|
+
if (headerMatch) {
|
|
5849
|
+
this.saveCurrentMessage();
|
|
5850
|
+
const platformId = headerMatch[3] || headerMatch[4] || cleanNickname(headerMatch[2].trim());
|
|
5851
|
+
let nickname = cleanNickname(headerMatch[2].trim());
|
|
5852
|
+
if (nickname === platformId && headerMatch[3]) {
|
|
5853
|
+
nickname = this.lastValidNickname.get(platformId) ?? nickname;
|
|
5854
|
+
} else if (headerMatch[3] || headerMatch[4]) {
|
|
5855
|
+
this.lastValidNickname.set(platformId, nickname);
|
|
5856
|
+
}
|
|
5857
|
+
this.currentMessage = {
|
|
5858
|
+
timestamp: parseLocalTime(headerMatch[1]),
|
|
5859
|
+
platformId,
|
|
5860
|
+
nickname,
|
|
5861
|
+
contentLines: []
|
|
5862
|
+
};
|
|
5863
|
+
return;
|
|
5864
|
+
}
|
|
5865
|
+
if (this.currentMessage) {
|
|
5866
|
+
if (line.startsWith("=====") || line.startsWith("\u6D88\u606F\u8BB0\u5F55") || line.startsWith("\u6D88\u606F\u5206\u7EC4")) return;
|
|
5867
|
+
this.currentMessage.contentLines.push(line);
|
|
5868
|
+
return;
|
|
5869
|
+
}
|
|
5870
|
+
const trimmed = line.trim();
|
|
5871
|
+
if (trimmed && !trimmed.startsWith("=====") && !trimmed.startsWith("\u6D88\u606F\u8BB0\u5F55") && !trimmed.startsWith("\u6D88\u606F\u5206\u7EC4")) {
|
|
5872
|
+
this.skippedLines += 1;
|
|
5873
|
+
}
|
|
5874
|
+
}
|
|
5875
|
+
finish() {
|
|
5876
|
+
this.saveCurrentMessage();
|
|
5877
|
+
return {
|
|
5878
|
+
meta: {
|
|
5879
|
+
name: this.groupName === "\u672A\u77E5\u7FA4\u804A" ? this.fallbackName : this.groupName,
|
|
5880
|
+
platform: KNOWN_PLATFORMS.QQ,
|
|
5881
|
+
type: "group" /* GROUP */
|
|
5882
|
+
},
|
|
5883
|
+
members: Array.from(this.memberMap.values(), (member) => ({
|
|
5884
|
+
platformId: member.platformId,
|
|
5885
|
+
accountName: member.nickname
|
|
5886
|
+
})),
|
|
5887
|
+
messages: this.messages,
|
|
5888
|
+
skippedLines: this.skippedLines
|
|
5889
|
+
};
|
|
5890
|
+
}
|
|
5891
|
+
saveCurrentMessage() {
|
|
5892
|
+
if (!this.currentMessage) return;
|
|
5893
|
+
const content = this.currentMessage.contentLines.join("\n").trim();
|
|
5894
|
+
this.messages.push({
|
|
5895
|
+
senderPlatformId: this.currentMessage.platformId,
|
|
5896
|
+
senderAccountName: this.currentMessage.nickname,
|
|
5897
|
+
timestamp: this.currentMessage.timestamp,
|
|
5898
|
+
type: detectMessageType3(content),
|
|
5899
|
+
content: content || null
|
|
5900
|
+
});
|
|
5901
|
+
this.memberMap.set(this.currentMessage.platformId, {
|
|
5902
|
+
platformId: this.currentMessage.platformId,
|
|
5903
|
+
nickname: this.currentMessage.nickname
|
|
5904
|
+
});
|
|
5905
|
+
this.currentMessage = null;
|
|
5906
|
+
}
|
|
5907
|
+
};
|
|
5908
|
+
function extractNameFromFileName2(fileName) {
|
|
5909
|
+
return getBasename2(fileName).replace(/\.txt$/i, "") || "\u672A\u77E5\u7FA4\u804A";
|
|
5910
|
+
}
|
|
5911
|
+
function getBasename2(fileName) {
|
|
5912
|
+
return fileName.split(/[\\/]/).pop() ?? fileName;
|
|
5913
|
+
}
|
|
5914
|
+
function cleanNickname(nickname) {
|
|
5915
|
+
return nickname.replace(/^(【[^】]*】\s*)+/, "").trim();
|
|
5916
|
+
}
|
|
5917
|
+
function parseLocalTime(timeText) {
|
|
5918
|
+
return Math.floor(new Date(timeText.replace(" ", "T")).getTime() / 1e3);
|
|
5919
|
+
}
|
|
5932
5920
|
function detectMessageType3(content) {
|
|
5933
5921
|
const trimmed = content.trim();
|
|
5934
5922
|
if (trimmed === "[\u56FE\u7247]") return 1 /* IMAGE */;
|
|
@@ -5941,7 +5929,9 @@ function detectMessageType3(content) {
|
|
|
5941
5929
|
if (trimmed === "[\u7EA2\u5305]" || trimmed.includes("\u53D1\u51FA\u4E86\u7EA2\u5305")) return 20 /* RED_PACKET */;
|
|
5942
5930
|
if (trimmed === "[\u8F6C\u8D26]" || trimmed.includes("\u5411\u4F60\u8F6C\u8D26")) return 21 /* TRANSFER */;
|
|
5943
5931
|
if (trimmed.includes("\u62CD\u4E86\u62CD") || trimmed === "[\u62CD\u4E00\u62CD]") return 22 /* POKE */;
|
|
5944
|
-
if (trimmed === "[\u8BED\u97F3\u901A\u8BDD]" || trimmed === "[\u89C6\u9891\u901A\u8BDD]" || trimmed.includes("\u901A\u8BDD\u65F6\u957F"))
|
|
5932
|
+
if (trimmed === "[\u8BED\u97F3\u901A\u8BDD]" || trimmed === "[\u89C6\u9891\u901A\u8BDD]" || trimmed.includes("\u901A\u8BDD\u65F6\u957F")) {
|
|
5933
|
+
return 23 /* CALL */;
|
|
5934
|
+
}
|
|
5945
5935
|
if (trimmed === "[\u5206\u4EAB]" || trimmed === "[\u97F3\u4E50]" || trimmed === "[\u5C0F\u7A0B\u5E8F]") return 24 /* SHARE */;
|
|
5946
5936
|
if (trimmed.startsWith("[\u56DE\u590D]")) return 25 /* REPLY */;
|
|
5947
5937
|
if (trimmed === "[\u8F6C\u53D1]" || trimmed === "[\u804A\u5929\u8BB0\u5F55]") return 26 /* FORWARD */;
|
|
@@ -5952,150 +5942,64 @@ function detectMessageType3(content) {
|
|
|
5952
5942
|
if (trimmed.startsWith("[") && trimmed.endsWith("]")) return 99 /* OTHER */;
|
|
5953
5943
|
return 0 /* TEXT */;
|
|
5954
5944
|
}
|
|
5955
|
-
|
|
5956
|
-
|
|
5957
|
-
|
|
5958
|
-
|
|
5959
|
-
|
|
5960
|
-
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5945
|
+
|
|
5946
|
+
// ../../packages/parser/src/formats/qq-native-txt.ts
|
|
5947
|
+
var feature11 = {
|
|
5948
|
+
id: "qq-native-txt",
|
|
5949
|
+
name: "QQ \u5B98\u65B9\u5BFC\u51FA (TXT)",
|
|
5950
|
+
platform: KNOWN_PLATFORMS.QQ,
|
|
5951
|
+
priority: 30,
|
|
5952
|
+
extensions: [".txt"],
|
|
5953
|
+
signatures: { head: QQ_HEAD_SIGNATURES }
|
|
5954
|
+
};
|
|
5955
|
+
async function* parseQq(options2) {
|
|
5964
5956
|
const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
|
|
5965
5957
|
const totalBytes = getFileSize(filePath);
|
|
5966
5958
|
let bytesRead = 0;
|
|
5967
|
-
let messagesProcessed = 0;
|
|
5968
|
-
let skippedLines = 0;
|
|
5969
5959
|
const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
|
|
5970
5960
|
yield { type: "progress", data: initialProgress };
|
|
5971
5961
|
onProgress?.(initialProgress);
|
|
5972
|
-
onLog?.("info",
|
|
5973
|
-
|
|
5974
|
-
const memberMap = /* @__PURE__ */ new Map();
|
|
5975
|
-
const messages = [];
|
|
5976
|
-
let currentMessage = null;
|
|
5977
|
-
const saveCurrentMessage = () => {
|
|
5978
|
-
if (currentMessage) {
|
|
5979
|
-
const content = currentMessage.contentLines.join("\n").trim();
|
|
5980
|
-
const type = detectMessageType3(content);
|
|
5981
|
-
messages.push({
|
|
5982
|
-
senderPlatformId: currentMessage.platformId,
|
|
5983
|
-
senderAccountName: currentMessage.nickname,
|
|
5984
|
-
// QQ TXT 格式只有一个昵称,作为账号名称追踪历史
|
|
5985
|
-
// 不设置 senderGroupNickname,避免同一昵称被重复追踪
|
|
5986
|
-
timestamp: currentMessage.timestamp,
|
|
5987
|
-
type,
|
|
5988
|
-
content: content || null
|
|
5989
|
-
});
|
|
5990
|
-
memberMap.set(currentMessage.platformId, {
|
|
5991
|
-
platformId: currentMessage.platformId,
|
|
5992
|
-
nickname: currentMessage.nickname
|
|
5993
|
-
});
|
|
5994
|
-
messagesProcessed++;
|
|
5995
|
-
}
|
|
5996
|
-
};
|
|
5962
|
+
onLog?.("info", `Starting QQ TXT parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`);
|
|
5963
|
+
const accumulator = new QqTextAccumulator(filePath);
|
|
5997
5964
|
const fileStream = fs14.createReadStream(filePath, { encoding: "utf-8" });
|
|
5998
|
-
const
|
|
5999
|
-
input: fileStream,
|
|
6000
|
-
crlfDelay: Infinity
|
|
6001
|
-
});
|
|
5965
|
+
const lines = readline4.createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
6002
5966
|
fileStream.on("data", (chunk) => {
|
|
6003
5967
|
bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
|
|
6004
5968
|
});
|
|
6005
|
-
|
|
6006
|
-
|
|
6007
|
-
|
|
6008
|
-
|
|
6009
|
-
|
|
6010
|
-
|
|
6011
|
-
|
|
6012
|
-
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
nickname = previousNickname;
|
|
6022
|
-
}
|
|
6023
|
-
} else if (headerMatch[3] || headerMatch[4]) {
|
|
6024
|
-
lastValidNickname.set(platformId, nickname);
|
|
6025
|
-
}
|
|
6026
|
-
currentMessage = {
|
|
6027
|
-
timestamp: parseLocalTime(timeStr),
|
|
6028
|
-
platformId,
|
|
6029
|
-
nickname,
|
|
6030
|
-
contentLines: []
|
|
6031
|
-
};
|
|
6032
|
-
if (messagesProcessed % 1e3 === 0) {
|
|
6033
|
-
const progress = createProgress(
|
|
6034
|
-
"parsing",
|
|
6035
|
-
bytesRead,
|
|
6036
|
-
totalBytes,
|
|
6037
|
-
messagesProcessed,
|
|
6038
|
-
`\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`
|
|
6039
|
-
);
|
|
6040
|
-
onProgress?.(progress);
|
|
6041
|
-
}
|
|
6042
|
-
continue;
|
|
6043
|
-
}
|
|
6044
|
-
if (currentMessage) {
|
|
6045
|
-
if (line.startsWith("=====")) continue;
|
|
6046
|
-
if (line.startsWith("\u6D88\u606F\u8BB0\u5F55") || line.startsWith("\u6D88\u606F\u5206\u7EC4")) continue;
|
|
6047
|
-
currentMessage.contentLines.push(line);
|
|
6048
|
-
} else {
|
|
6049
|
-
const trimmed = line.trim();
|
|
6050
|
-
if (trimmed && !trimmed.startsWith("=====") && !trimmed.startsWith("\u6D88\u606F\u8BB0\u5F55") && !trimmed.startsWith("\u6D88\u606F\u5206\u7EC4")) {
|
|
6051
|
-
skippedLines++;
|
|
6052
|
-
}
|
|
6053
|
-
}
|
|
6054
|
-
}
|
|
6055
|
-
saveCurrentMessage();
|
|
6056
|
-
const meta = {
|
|
6057
|
-
name: groupName === "\u672A\u77E5\u7FA4\u804A" ? extractNameFromFilePath6(filePath) : groupName,
|
|
6058
|
-
platform: KNOWN_PLATFORMS.QQ,
|
|
6059
|
-
type: "group" /* GROUP */
|
|
6060
|
-
};
|
|
6061
|
-
yield { type: "meta", data: meta };
|
|
6062
|
-
const members = Array.from(memberMap.values()).map((m) => ({
|
|
6063
|
-
platformId: m.platformId,
|
|
6064
|
-
accountName: m.nickname
|
|
6065
|
-
// QQ TXT 格式只有昵称,作为账号名称
|
|
6066
|
-
// 不设置 groupNickname,避免同一昵称被重复追踪
|
|
6067
|
-
}));
|
|
6068
|
-
yield { type: "members", data: members };
|
|
6069
|
-
for (let i = 0; i < messages.length; i += batchSize) {
|
|
6070
|
-
const batch = messages.slice(i, i + batchSize);
|
|
6071
|
-
yield { type: "messages", data: batch };
|
|
6072
|
-
}
|
|
6073
|
-
const doneProgress = createProgress("done", totalBytes, totalBytes, messagesProcessed, "");
|
|
5969
|
+
let lastReportedCount = -1;
|
|
5970
|
+
for await (const line of lines) {
|
|
5971
|
+
accumulator.pushLine(line);
|
|
5972
|
+
const messageCount = accumulator.messageCount;
|
|
5973
|
+
if (messageCount > 0 && messageCount % 1e3 === 0 && messageCount !== lastReportedCount) {
|
|
5974
|
+
lastReportedCount = messageCount;
|
|
5975
|
+
onProgress?.(createProgress("parsing", bytesRead, totalBytes, messageCount, `Processed ${messageCount} messages`));
|
|
5976
|
+
}
|
|
5977
|
+
}
|
|
5978
|
+
const result = accumulator.finish();
|
|
5979
|
+
yield { type: "meta", data: result.meta };
|
|
5980
|
+
yield { type: "members", data: result.members };
|
|
5981
|
+
for (let index = 0; index < result.messages.length; index += batchSize) {
|
|
5982
|
+
yield { type: "messages", data: result.messages.slice(index, index + batchSize) };
|
|
5983
|
+
}
|
|
5984
|
+
const doneProgress = createProgress("done", totalBytes, totalBytes, result.messages.length, "");
|
|
6074
5985
|
yield { type: "progress", data: doneProgress };
|
|
6075
5986
|
onProgress?.(doneProgress);
|
|
6076
|
-
onLog?.("info",
|
|
6077
|
-
if (skippedLines > 0) {
|
|
6078
|
-
onLog?.("info",
|
|
5987
|
+
onLog?.("info", `QQ TXT parsing completed: ${result.messages.length} messages, ${result.members.length} members`);
|
|
5988
|
+
if (result.skippedLines > 0) {
|
|
5989
|
+
onLog?.("info", `Skipped ${result.skippedLines} unrecognized lines while parsing QQ TXT`);
|
|
6079
5990
|
}
|
|
6080
5991
|
yield {
|
|
6081
5992
|
type: "done",
|
|
6082
|
-
data: { messageCount:
|
|
5993
|
+
data: { messageCount: result.messages.length, memberCount: result.members.length }
|
|
6083
5994
|
};
|
|
6084
5995
|
}
|
|
6085
|
-
var parser_10 = {
|
|
6086
|
-
|
|
6087
|
-
parse: parseTxt
|
|
6088
|
-
};
|
|
6089
|
-
var module_11 = {
|
|
6090
|
-
feature: feature11,
|
|
6091
|
-
parser: parser_10
|
|
6092
|
-
// TXT 格式不需要预处理器
|
|
6093
|
-
};
|
|
5996
|
+
var parser_10 = { feature: feature11, parse: parseQq };
|
|
5997
|
+
var module_11 = { feature: feature11, parser: parser_10 };
|
|
6094
5998
|
var qq_native_txt_default = module_11;
|
|
6095
5999
|
|
|
6096
6000
|
// ../../packages/parser/src/formats/instagram-native.ts
|
|
6097
6001
|
import * as fs15 from "fs";
|
|
6098
|
-
import * as
|
|
6002
|
+
import * as path9 from "path";
|
|
6099
6003
|
var feature12 = {
|
|
6100
6004
|
id: "instagram-native",
|
|
6101
6005
|
name: "Instagram \u5B98\u65B9\u5BFC\u51FA",
|
|
@@ -6120,9 +6024,9 @@ function decodeInstagramText(text) {
|
|
|
6120
6024
|
return text;
|
|
6121
6025
|
}
|
|
6122
6026
|
}
|
|
6123
|
-
function
|
|
6124
|
-
const
|
|
6125
|
-
return
|
|
6027
|
+
function extractNameFromFilePath5(filePath) {
|
|
6028
|
+
const basename10 = path9.basename(filePath);
|
|
6029
|
+
return basename10.replace(/\.json$/i, "") || "\u672A\u77E5\u5BF9\u8BDD";
|
|
6126
6030
|
}
|
|
6127
6031
|
function isSystemMessage2(content) {
|
|
6128
6032
|
const systemPatterns = [
|
|
@@ -6208,7 +6112,7 @@ async function* parseInstagram(options2) {
|
|
|
6208
6112
|
ownerId = createMsg ? decodeInstagramText(createMsg.sender_name) : void 0;
|
|
6209
6113
|
}
|
|
6210
6114
|
const meta = {
|
|
6211
|
-
name: decodeInstagramText(data.title) ||
|
|
6115
|
+
name: decodeInstagramText(data.title) || extractNameFromFilePath5(filePath),
|
|
6212
6116
|
platform: KNOWN_PLATFORMS.INSTAGRAM,
|
|
6213
6117
|
type: chatType,
|
|
6214
6118
|
ownerId
|
|
@@ -6281,16 +6185,16 @@ var module_12 = {
|
|
|
6281
6185
|
var instagram_native_default = module_12;
|
|
6282
6186
|
|
|
6283
6187
|
// ../../packages/parser/src/formats/google-chat-takeout.ts
|
|
6284
|
-
var
|
|
6188
|
+
var import_stream_chain7 = __toESM(require_stream_chain(), 1);
|
|
6285
6189
|
var import_stream_json8 = __toESM(require_stream_json(), 1);
|
|
6286
6190
|
var import_Pick7 = __toESM(require_Pick(), 1);
|
|
6287
|
-
var
|
|
6191
|
+
var import_StreamValues7 = __toESM(require_StreamValues(), 1);
|
|
6288
6192
|
import * as fs16 from "fs";
|
|
6289
|
-
import * as
|
|
6290
|
-
var { chain:
|
|
6193
|
+
import * as path10 from "path";
|
|
6194
|
+
var { chain: chain7 } = import_stream_chain7.default;
|
|
6291
6195
|
var { parser: parser9 } = import_stream_json8.default;
|
|
6292
6196
|
var { pick: pick7 } = import_Pick7.default;
|
|
6293
|
-
var { streamValues:
|
|
6197
|
+
var { streamValues: streamValues7 } = import_StreamValues7.default;
|
|
6294
6198
|
var ENGLISH_MONTHS = /* @__PURE__ */ new Map([
|
|
6295
6199
|
["january", 0],
|
|
6296
6200
|
["february", 1],
|
|
@@ -6372,9 +6276,9 @@ function readJson(filePath) {
|
|
|
6372
6276
|
return JSON.parse(fs16.readFileSync(filePath, "utf8"));
|
|
6373
6277
|
}
|
|
6374
6278
|
function resolveManifestFile(manifestDir, relativePath) {
|
|
6375
|
-
const resolved =
|
|
6376
|
-
const relative2 =
|
|
6377
|
-
if (relative2.startsWith("..") ||
|
|
6279
|
+
const resolved = path10.resolve(manifestDir, relativePath);
|
|
6280
|
+
const relative2 = path10.relative(manifestDir, resolved);
|
|
6281
|
+
if (relative2.startsWith("..") || path10.isAbsolute(relative2)) {
|
|
6378
6282
|
throw new Error(`Google Chat manifest path escapes its directory: ${relativePath}`);
|
|
6379
6283
|
}
|
|
6380
6284
|
return resolved;
|
|
@@ -6387,7 +6291,7 @@ function readManifest2(filePath) {
|
|
|
6387
6291
|
if (manifest.chatType !== "private" && manifest.chatType !== "group") {
|
|
6388
6292
|
throw new Error("Invalid Google Chat chat type");
|
|
6389
6293
|
}
|
|
6390
|
-
const manifestDir =
|
|
6294
|
+
const manifestDir = path10.dirname(filePath);
|
|
6391
6295
|
return {
|
|
6392
6296
|
manifest,
|
|
6393
6297
|
userInfoPath: resolveManifestFile(manifestDir, manifest.userInfoFile),
|
|
@@ -6471,7 +6375,7 @@ async function* parseGoogleChat(options2) {
|
|
|
6471
6375
|
readStream.on("data", (chunk) => {
|
|
6472
6376
|
bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
|
|
6473
6377
|
});
|
|
6474
|
-
const pipeline2 =
|
|
6378
|
+
const pipeline2 = chain7([readStream, parser9(), pick7({ filter: /^messages\.\d+$/ }), streamValues7()]);
|
|
6475
6379
|
const messageBatch = [];
|
|
6476
6380
|
try {
|
|
6477
6381
|
for await (const item of pipeline2) {
|
|
@@ -6519,418 +6423,270 @@ var google_chat_takeout_default = module_13;
|
|
|
6519
6423
|
|
|
6520
6424
|
// ../../packages/parser/src/formats/line-native-txt.ts
|
|
6521
6425
|
import * as fs17 from "fs";
|
|
6522
|
-
|
|
6523
|
-
|
|
6524
|
-
|
|
6525
|
-
|
|
6526
|
-
|
|
6527
|
-
|
|
6528
|
-
|
|
6529
|
-
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
// Tab 分隔的消息格式(支持多种时间格式)
|
|
6535
|
-
/^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?)\t[^\t\n]+\t/m,
|
|
6536
|
-
// 空格分隔的消息格式(部分 LINE 导出)
|
|
6537
|
-
/^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?) [^\s]+ /m,
|
|
6538
|
-
// LINE 独有的日期行格式:YYYY.MM.DD DayOfWeek(英文星期全称)
|
|
6539
|
-
/^\d{4}\.\d{2}\.\d{2}\s+(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)/m,
|
|
6540
|
-
// LINE 日文/中文日期行格式:YYYY/M/D(曜日)
|
|
6541
|
-
/^\d{4}\/\d{1,2}\/\d{1,2}[((][月火水木金土日]/m,
|
|
6542
|
-
// LINE 中文日期行格式:YYYY/M/D周X
|
|
6543
|
-
/^\d{4}\/\d{1,2}\/\d{1,2}周/m
|
|
6544
|
-
],
|
|
6545
|
-
// 文件名特征:[LINE] 出现在文件名中
|
|
6546
|
-
filename: [/\[LINE\]/i]
|
|
6547
|
-
}
|
|
6548
|
-
};
|
|
6549
|
-
function extractNameFromFilePath8(filePath) {
|
|
6550
|
-
const basename13 = path13.basename(filePath, ".txt");
|
|
6551
|
-
const name = basename13.replace(/^\[LINE\]\s*/i, "").trim();
|
|
6552
|
-
return name || "\u672A\u77E5\u804A\u5929";
|
|
6553
|
-
}
|
|
6554
|
-
function extractNameFromHeader(header) {
|
|
6555
|
-
const enPrivateMatch = header.match(/^(?:\[LINE\] )?Chat history with (.+)$/m);
|
|
6556
|
-
if (enPrivateMatch) return { name: enPrivateMatch[1].trim(), isGroup: false };
|
|
6557
|
-
const enGroupMatch = header.match(/^(?:\[LINE\] )?Chat history in (.+)$/m);
|
|
6558
|
-
if (enGroupMatch) return { name: enGroupMatch[1].trim(), isGroup: true };
|
|
6559
|
-
const jaPrivateMatch = header.match(/^\[LINE\] (.+)とのトーク履歴/);
|
|
6560
|
-
if (jaPrivateMatch) return { name: jaPrivateMatch[1].trim(), isGroup: false };
|
|
6561
|
-
const jaGroupMatch = header.match(/^\[LINE\] (.+)のトーク履歴/);
|
|
6562
|
-
if (jaGroupMatch) return { name: jaGroupMatch[1].trim(), isGroup: true };
|
|
6563
|
-
const zhCnPrivateMatch = header.match(/^\[LINE\] 与(.+)的聊天记录/);
|
|
6564
|
-
if (zhCnPrivateMatch) return { name: zhCnPrivateMatch[1].trim(), isGroup: false };
|
|
6565
|
-
const zhCnGroupMatch = header.match(/^\[LINE\] (.+)的聊天记录/);
|
|
6566
|
-
if (zhCnGroupMatch) return { name: zhCnGroupMatch[1].trim(), isGroup: true };
|
|
6567
|
-
const zhTwPrivateMatch = header.match(/^\[LINE\] 與(.+)的聊天記錄/);
|
|
6568
|
-
if (zhTwPrivateMatch) return { name: zhTwPrivateMatch[1].trim(), isGroup: false };
|
|
6569
|
-
const zhTwGroupMatch = header.match(/^\[LINE\] (.+)的聊天記錄/);
|
|
6570
|
-
if (zhTwGroupMatch) return { name: zhTwGroupMatch[1].trim(), isGroup: true };
|
|
6571
|
-
return null;
|
|
6572
|
-
}
|
|
6426
|
+
|
|
6427
|
+
// ../../packages/parser/src/browser/line.ts
|
|
6428
|
+
var LINE_HEAD_SIGNATURES = [
|
|
6429
|
+
/^\[LINE\] /m,
|
|
6430
|
+
/^(?:\[LINE\] )?Chat history (?:with|in) /m,
|
|
6431
|
+
/^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?)\t[^\t\n]+\t/m,
|
|
6432
|
+
/^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?) [^\s]+ /m,
|
|
6433
|
+
/^\d{4}\.\d{2}\.\d{2}\s+(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)/m,
|
|
6434
|
+
/^\d{4}\/\d{1,2}\/\d{1,2}[((][月火水木金土日]/m,
|
|
6435
|
+
/^\d{4}\/\d{1,2}\/\d{1,2}周/m
|
|
6436
|
+
];
|
|
6437
|
+
var LINE_FILENAME_SIGNATURES = [/\[LINE\]/i];
|
|
6573
6438
|
var DATE_PATTERNS = [
|
|
6574
|
-
// 2025.12.10 Wednesday
|
|
6575
6439
|
/^(\d{4})\.(\d{2})\.(\d{2})\s+(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)?/,
|
|
6576
|
-
// 2026/1/30周五 or 2026/1/30(金)
|
|
6577
6440
|
/^(\d{4})\/(\d{1,2})\/(\d{1,2})/,
|
|
6578
|
-
// Fri, 1/30/2026
|
|
6579
6441
|
/^[A-Za-z]+,\s*(\d{1,2})\/(\d{1,2})\/(\d{4})/
|
|
6580
6442
|
];
|
|
6581
|
-
|
|
6582
|
-
|
|
6583
|
-
|
|
6584
|
-
if (match) {
|
|
6585
|
-
if (pattern === DATE_PATTERNS[0] || pattern === DATE_PATTERNS[1]) {
|
|
6586
|
-
const year = parseInt(match[1]);
|
|
6587
|
-
const month = parseInt(match[2]) - 1;
|
|
6588
|
-
const day = parseInt(match[3]);
|
|
6589
|
-
return new Date(year, month, day);
|
|
6590
|
-
} else if (pattern === DATE_PATTERNS[2]) {
|
|
6591
|
-
const month = parseInt(match[1]) - 1;
|
|
6592
|
-
const day = parseInt(match[2]);
|
|
6593
|
-
const year = parseInt(match[3]);
|
|
6594
|
-
return new Date(year, month, day);
|
|
6595
|
-
}
|
|
6596
|
-
}
|
|
6597
|
-
}
|
|
6598
|
-
return null;
|
|
6599
|
-
}
|
|
6600
|
-
var PRIVATE_MSG_PATTERN = /^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?)\t([^\t]+)\t(.*)$/;
|
|
6601
|
-
var GROUP_MSG_PATTERN = /^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?) ([^\s]+) (.*)$/;
|
|
6602
|
-
var SYSTEM_MSG_PATTERN = /^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?)\t\t(.+)$/;
|
|
6443
|
+
var TAB_MESSAGE_PATTERN = /^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?)\t([^\t]+)\t(.*)$/;
|
|
6444
|
+
var SPACE_MESSAGE_PATTERN = /^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?) ([^\s]+) (.*)$/;
|
|
6445
|
+
var SYSTEM_MESSAGE_PATTERN = /^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?)\t\t(.+)$/;
|
|
6603
6446
|
var SPECIAL_MESSAGE_TYPES = {
|
|
6604
|
-
// 图片 / Photo
|
|
6605
6447
|
"[Photo]": 1 /* IMAGE */,
|
|
6606
|
-
// EN
|
|
6607
6448
|
"[\u7167\u7247]": 1 /* IMAGE */,
|
|
6608
|
-
// ZH-CN / ZH-TW
|
|
6609
6449
|
"[\u5199\u771F]": 1 /* IMAGE */,
|
|
6610
|
-
// JA
|
|
6611
6450
|
Photos: 1 /* IMAGE */,
|
|
6612
|
-
// EN (fallback)
|
|
6613
|
-
// 语音 / Voice
|
|
6614
6451
|
"[Voice message]": 2 /* VOICE */,
|
|
6615
|
-
// EN
|
|
6616
6452
|
"[\u8BED\u97F3\u4FE1\u606F]": 2 /* VOICE */,
|
|
6617
|
-
// ZH-CN
|
|
6618
6453
|
"[\u8A9E\u97F3\u8A0A\u606F]": 2 /* VOICE */,
|
|
6619
|
-
// ZH-TW
|
|
6620
6454
|
"[\u30DC\u30A4\u30B9\u30E1\u30C3\u30BB\u30FC\u30B8]": 2 /* VOICE */,
|
|
6621
|
-
// JA
|
|
6622
6455
|
Audio: 2 /* VOICE */,
|
|
6623
|
-
// EN (fallback)
|
|
6624
|
-
// 视频 / Video
|
|
6625
6456
|
"[Video]": 3 /* VIDEO */,
|
|
6626
|
-
// EN
|
|
6627
6457
|
"[\u89C6\u9891]": 3 /* VIDEO */,
|
|
6628
|
-
// ZH-CN
|
|
6629
6458
|
"[\u5F71\u7247]": 3 /* VIDEO */,
|
|
6630
|
-
// ZH-TW
|
|
6631
6459
|
"[\u52D5\u753B]": 3 /* VIDEO */,
|
|
6632
|
-
// JA
|
|
6633
6460
|
Videos: 3 /* VIDEO */,
|
|
6634
|
-
// EN (fallback)
|
|
6635
|
-
// 文件 / File
|
|
6636
6461
|
"[File]": 4 /* FILE */,
|
|
6637
|
-
// EN
|
|
6638
6462
|
"[\u6587\u4EF6]": 4 /* FILE */,
|
|
6639
|
-
// ZH-CN
|
|
6640
6463
|
"[\u6A94\u6848]": 4 /* FILE */,
|
|
6641
|
-
// ZH-TW
|
|
6642
6464
|
"[\u30D5\u30A1\u30A4\u30EB]": 4 /* FILE */,
|
|
6643
|
-
// JA
|
|
6644
|
-
// 贴纸 / Sticker
|
|
6645
6465
|
"[Sticker]": 5 /* EMOJI */,
|
|
6646
|
-
// EN
|
|
6647
6466
|
"[\u8D34\u56FE]": 5 /* EMOJI */,
|
|
6648
|
-
// ZH-CN
|
|
6649
6467
|
"[\u8CBC\u5716]": 5 /* EMOJI */,
|
|
6650
|
-
// ZH-TW
|
|
6651
6468
|
"[\u30B9\u30BF\u30F3\u30D7]": 5 /* EMOJI */,
|
|
6652
|
-
// JA
|
|
6653
6469
|
Stickers: 5 /* EMOJI */,
|
|
6654
|
-
// EN (fallback)
|
|
6655
|
-
// 位置 / Location
|
|
6656
6470
|
"[Location]": 8 /* LOCATION */,
|
|
6657
|
-
// EN
|
|
6658
6471
|
"[\u4F4D\u7F6E]": 8 /* LOCATION */,
|
|
6659
|
-
// ZH-CN / ZH-TW
|
|
6660
6472
|
"[\u4F4D\u7F6E\u60C5\u5831]": 8 /* LOCATION */,
|
|
6661
|
-
// JA
|
|
6662
|
-
// 记事本 / Notes
|
|
6663
6473
|
"[Notes]": 0 /* TEXT */,
|
|
6664
|
-
// EN
|
|
6665
6474
|
"[\u8BB0\u4E8B\u672C]": 0 /* TEXT */,
|
|
6666
|
-
// ZH-CN
|
|
6667
6475
|
"[\u8A18\u4E8B\u672C]": 0 /* TEXT */,
|
|
6668
|
-
// ZH-TW
|
|
6669
6476
|
"[\u30CE\u30FC\u30C8]": 0 /* TEXT */
|
|
6670
|
-
// JA
|
|
6671
6477
|
};
|
|
6672
|
-
function
|
|
6673
|
-
for (const [pattern, type] of Object.entries(SPECIAL_MESSAGE_TYPES)) {
|
|
6674
|
-
if (content === pattern || content.startsWith(pattern)) {
|
|
6675
|
-
return type;
|
|
6676
|
-
}
|
|
6677
|
-
}
|
|
6678
|
-
if (content.startsWith("[null]") && content.includes("maps.google.com")) {
|
|
6679
|
-
return 8 /* LOCATION */;
|
|
6680
|
-
}
|
|
6681
|
-
if (
|
|
6682
|
-
// --- 加入群组 / Join group ---
|
|
6683
|
-
content.includes(" joined the group") || // EN
|
|
6684
|
-
content.includes("\u5DF2\u52A0\u5165\u8BE5\u7FA4") || // ZH-CN
|
|
6685
|
-
content.includes("\u5DF2\u52A0\u5165\u7FA4\u7D44") || // ZH-TW
|
|
6686
|
-
content.includes("\u304C\u30B0\u30EB\u30FC\u30D7\u306B\u53C2\u52A0\u3057\u307E\u3057\u305F") || // JA
|
|
6687
|
-
// --- 拉人进群 / Added to group ---
|
|
6688
|
-
content.includes(" added ") || // EN
|
|
6689
|
-
content.includes(" to the group") || // EN
|
|
6690
|
-
content.includes("\u5DF2\u5C06") || // ZH-CN
|
|
6691
|
-
content.includes("\u6DFB\u52A0\u81F3\u7FA4") || // ZH-CN
|
|
6692
|
-
content.includes("\u6DFB\u52A0\u5230\u7FA4") || // ZH-CN (另一格式)
|
|
6693
|
-
content.includes("\u5DF2\u65B0\u589E") || // ZH-TW
|
|
6694
|
-
content.includes("\u81F3\u7FA4\u7D44") || // ZH-TW
|
|
6695
|
-
content.includes("\u3092\u30B0\u30EB\u30FC\u30D7\u306B\u8FFD\u52A0\u3057\u307E\u3057\u305F") || // JA
|
|
6696
|
-
// --- 退出群组 / Left group ---
|
|
6697
|
-
content.includes(" left the group") || // EN
|
|
6698
|
-
content.includes("\u5DF2\u9000\u7FA4") || // ZH-CN
|
|
6699
|
-
content.includes("\u5DF2\u96E2\u958B\u7FA4\u7D44") || // ZH-TW
|
|
6700
|
-
content.includes("\u304C\u30B0\u30EB\u30FC\u30D7\u3092\u9000\u4F1A\u3057\u307E\u3057\u305F") || // JA
|
|
6701
|
-
// --- 设定公告 / Announcement ---
|
|
6702
|
-
content.includes("made an announcement") || // EN
|
|
6703
|
-
content.includes("\u53D1\u5E03\u4E86\u901A\u544A") || // ZH-CN
|
|
6704
|
-
content.includes("\u5DF2\u8A2D\u5B9A\u516C\u544A") || // ZH-TW
|
|
6705
|
-
content.includes("\u304C\u30A2\u30CA\u30A6\u30F3\u30B9\u3057\u307E\u3057\u305F") || // JA
|
|
6706
|
-
// --- 收回讯息 / Unsent message ---
|
|
6707
|
-
content.includes("unsent a message") || // EN
|
|
6708
|
-
content === "Message unsent." || // EN
|
|
6709
|
-
content.includes("\u64A4\u56DE\u4E86\u4E00\u6761\u6D88\u606F") || // ZH-CN
|
|
6710
|
-
content.includes("\u5DF2\u6536\u56DE\u8A0A\u606F") || // ZH-TW
|
|
6711
|
-
content.includes("\u9001\u4FE1\u3092\u53D6\u308A\u6D88\u3057\u307E\u3057\u305F") || // JA
|
|
6712
|
-
// --- 其他 / Others ---
|
|
6713
|
-
content.startsWith("Auto-reply")
|
|
6714
|
-
) {
|
|
6715
|
-
return 80 /* SYSTEM */;
|
|
6716
|
-
}
|
|
6717
|
-
if (content.match(/^https?:\/\//)) {
|
|
6718
|
-
return 7 /* LINK */;
|
|
6719
|
-
}
|
|
6720
|
-
return 0 /* TEXT */;
|
|
6721
|
-
}
|
|
6722
|
-
async function* parseLINE(options2) {
|
|
6723
|
-
const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
|
|
6724
|
-
const totalBytes = getFileSize(filePath);
|
|
6725
|
-
let messagesProcessed = 0;
|
|
6726
|
-
const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
|
|
6727
|
-
yield { type: "progress", data: initialProgress };
|
|
6728
|
-
onProgress?.(initialProgress);
|
|
6729
|
-
onLog?.("info", `\u5F00\u59CB\u89E3\u6790 LINE \u5BFC\u51FA\u6587\u4EF6\uFF0C\u5927\u5C0F: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
6730
|
-
const content = fs17.readFileSync(filePath, "utf-8");
|
|
6478
|
+
async function parseLineText(content, fileName, options2 = {}) {
|
|
6731
6479
|
const lines = content.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
6732
|
-
let currentDate = null;
|
|
6733
|
-
let chatName = extractNameFromFilePath8(filePath);
|
|
6734
|
-
let isPrivateChat = false;
|
|
6735
|
-
let useTabSeparator = false;
|
|
6736
6480
|
const memberMap = /* @__PURE__ */ new Map();
|
|
6737
6481
|
const messages = [];
|
|
6482
|
+
const header = lines.length > 0 ? extractNameFromHeader(lines[0].trim()) : null;
|
|
6483
|
+
const chatName = header?.name ?? extractNameFromFileName3(fileName);
|
|
6484
|
+
let currentDate = null;
|
|
6738
6485
|
let lastMessage = null;
|
|
6739
|
-
let
|
|
6740
|
-
|
|
6741
|
-
|
|
6742
|
-
|
|
6743
|
-
|
|
6744
|
-
if (headerResult) {
|
|
6745
|
-
chatName = headerResult.name;
|
|
6746
|
-
isPrivateChat = !headerResult.isGroup;
|
|
6747
|
-
useTabSeparator = true;
|
|
6748
|
-
lineIndex = 3;
|
|
6749
|
-
onLog?.("debug", `LINE \u68C0\u6D4B\u5230\u5934\u90E8\uFF0C\u540D\u79F0: ${headerResult.name}, \u7FA4\u804A: ${headerResult.isGroup}`);
|
|
6750
|
-
}
|
|
6751
|
-
}
|
|
6752
|
-
if (!isPrivateChat && lines.length > 0) {
|
|
6486
|
+
let quotedMultiline = false;
|
|
6487
|
+
let lineIndex = header ? 3 : 0;
|
|
6488
|
+
let useTabSeparator = Boolean(header);
|
|
6489
|
+
const yieldEvery = Math.max(1, options2.yieldEvery ?? 1e3);
|
|
6490
|
+
if (!header) {
|
|
6753
6491
|
for (const line of lines) {
|
|
6754
|
-
if (
|
|
6492
|
+
if (TAB_MESSAGE_PATTERN.test(line)) {
|
|
6755
6493
|
useTabSeparator = true;
|
|
6756
|
-
onLog?.("debug", `LINE \u68C0\u6D4B\u5230 Tab \u5206\u9694\u683C\u5F0F`);
|
|
6757
6494
|
break;
|
|
6758
6495
|
}
|
|
6759
|
-
if (
|
|
6496
|
+
if (SPACE_MESSAGE_PATTERN.test(line)) {
|
|
6760
6497
|
useTabSeparator = false;
|
|
6761
|
-
onLog?.("debug", `LINE \u68C0\u6D4B\u5230\u7A7A\u683C\u5206\u9694\u683C\u5F0F`);
|
|
6762
6498
|
break;
|
|
6763
6499
|
}
|
|
6764
6500
|
}
|
|
6765
6501
|
}
|
|
6766
|
-
|
|
6767
|
-
|
|
6768
|
-
|
|
6769
|
-
const
|
|
6770
|
-
|
|
6771
|
-
|
|
6772
|
-
currentDate = dateResult;
|
|
6773
|
-
if (debugLogCount < 5) {
|
|
6774
|
-
onLog?.("debug", `LINE \u65E5\u671F\u884C[${i}]: ${line} -> ${dateResult.toISOString()}`);
|
|
6775
|
-
}
|
|
6776
|
-
continue;
|
|
6777
|
-
}
|
|
6778
|
-
const msgPattern = useTabSeparator ? PRIVATE_MSG_PATTERN : GROUP_MSG_PATTERN;
|
|
6779
|
-
const msgMatch = line.match(msgPattern);
|
|
6780
|
-
if (debugLogCount < 5 && line.trim()) {
|
|
6781
|
-
onLog?.("debug", `LINE \u884C[${i}]: "${line.substring(0, 50)}..." match=${!!msgMatch}`);
|
|
6782
|
-
debugLogCount++;
|
|
6783
|
-
}
|
|
6784
|
-
if (msgMatch) {
|
|
6785
|
-
const [, timeStr, sender, contentRaw] = msgMatch;
|
|
6786
|
-
let content2 = contentRaw.trim();
|
|
6787
|
-
let isQuotedMultiline = false;
|
|
6788
|
-
if (content2.startsWith('"')) {
|
|
6789
|
-
content2 = content2.substring(1);
|
|
6790
|
-
isQuotedMultiline = !content2.endsWith('"');
|
|
6791
|
-
if (content2.endsWith('"')) {
|
|
6792
|
-
content2 = content2.substring(0, content2.length - 1);
|
|
6793
|
-
}
|
|
6794
|
-
}
|
|
6795
|
-
let hours = 0;
|
|
6796
|
-
let minutes = 0;
|
|
6797
|
-
const prefix = timeStr.match(/^(上午|下午|午前|午後)/)?.[1];
|
|
6798
|
-
const cleanTime = timeStr.replace(/^(上午|下午|午前|午後)/, "");
|
|
6799
|
-
const partsMatch = cleanTime.match(/^(\d{1,2}):(\d{2})([AaPp][Mm])?$/i);
|
|
6800
|
-
if (partsMatch) {
|
|
6801
|
-
hours = parseInt(partsMatch[1]);
|
|
6802
|
-
minutes = parseInt(partsMatch[2]);
|
|
6803
|
-
const suffix = partsMatch[3]?.toLowerCase();
|
|
6804
|
-
if (suffix === "pm" && hours < 12) hours += 12;
|
|
6805
|
-
if (suffix === "am" && hours === 12) hours = 0;
|
|
6806
|
-
if ((prefix === "\u4E0B\u5348" || prefix === "\u5348\u5F8C") && hours < 12) hours += 12;
|
|
6807
|
-
if ((prefix === "\u4E0A\u5348" || prefix === "\u5348\u524D") && hours === 12) hours = 0;
|
|
6808
|
-
} else {
|
|
6809
|
-
const parts = timeStr.split(":").map(Number);
|
|
6810
|
-
hours = parts[0];
|
|
6811
|
-
minutes = parts[1];
|
|
6812
|
-
}
|
|
6813
|
-
let timestamp;
|
|
6814
|
-
if (currentDate) {
|
|
6815
|
-
const msgDate = new Date(currentDate);
|
|
6816
|
-
msgDate.setHours(hours, minutes, 0, 0);
|
|
6817
|
-
timestamp = Math.floor(msgDate.getTime() / 1e3);
|
|
6818
|
-
} else {
|
|
6819
|
-
const now = /* @__PURE__ */ new Date();
|
|
6820
|
-
now.setHours(hours, minutes, 0, 0);
|
|
6821
|
-
timestamp = Math.floor(now.getTime() / 1e3);
|
|
6822
|
-
}
|
|
6823
|
-
const msgType = detectMessageType6(content2);
|
|
6824
|
-
if (!memberMap.has(sender)) {
|
|
6825
|
-
memberMap.set(sender, {
|
|
6826
|
-
platformId: sender,
|
|
6827
|
-
accountName: sender
|
|
6828
|
-
});
|
|
6829
|
-
}
|
|
6830
|
-
lastMessage = {
|
|
6831
|
-
senderPlatformId: sender,
|
|
6832
|
-
senderAccountName: sender,
|
|
6833
|
-
timestamp,
|
|
6834
|
-
type: msgType,
|
|
6835
|
-
content: content2 || null,
|
|
6836
|
-
_isQuotedMultiline: isQuotedMultiline
|
|
6837
|
-
// 临时标记,用于追加多行内容时处理结尾引号
|
|
6838
|
-
};
|
|
6839
|
-
messages.push(lastMessage);
|
|
6840
|
-
messagesProcessed++;
|
|
6841
|
-
if (messagesProcessed % 1e3 === 0) {
|
|
6842
|
-
const progress = createProgress(
|
|
6843
|
-
"parsing",
|
|
6844
|
-
i,
|
|
6845
|
-
lines.length,
|
|
6846
|
-
messagesProcessed,
|
|
6847
|
-
`\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`
|
|
6848
|
-
);
|
|
6849
|
-
onProgress?.(progress);
|
|
6850
|
-
}
|
|
6502
|
+
for (; lineIndex < lines.length; lineIndex += 1) {
|
|
6503
|
+
options2.checkCancelled?.();
|
|
6504
|
+
const line = lines[lineIndex];
|
|
6505
|
+
const date = parseDateLine(line);
|
|
6506
|
+
if (date) {
|
|
6507
|
+
currentDate = date;
|
|
6851
6508
|
} else {
|
|
6852
|
-
const
|
|
6853
|
-
|
|
6854
|
-
|
|
6855
|
-
const
|
|
6856
|
-
let
|
|
6857
|
-
|
|
6858
|
-
|
|
6859
|
-
|
|
6860
|
-
|
|
6861
|
-
|
|
6862
|
-
hours = parseInt(partsMatch[1]);
|
|
6863
|
-
minutes = parseInt(partsMatch[2]);
|
|
6864
|
-
const suffix = partsMatch[3]?.toLowerCase();
|
|
6865
|
-
if (suffix === "pm" && hours < 12) hours += 12;
|
|
6866
|
-
if (suffix === "am" && hours === 12) hours = 0;
|
|
6867
|
-
if ((prefix === "\u4E0B\u5348" || prefix === "\u5348\u5F8C") && hours < 12) hours += 12;
|
|
6868
|
-
if ((prefix === "\u4E0A\u5348" || prefix === "\u5348\u524D") && hours === 12) hours = 0;
|
|
6869
|
-
} else {
|
|
6870
|
-
const parts = timeStr.split(":").map(Number);
|
|
6871
|
-
hours = parts[0];
|
|
6872
|
-
minutes = parts[1];
|
|
6509
|
+
const messagePattern = useTabSeparator ? TAB_MESSAGE_PATTERN : SPACE_MESSAGE_PATTERN;
|
|
6510
|
+
const messageMatch = line.match(messagePattern);
|
|
6511
|
+
if (messageMatch) {
|
|
6512
|
+
const [, timeText, sender, rawContent] = messageMatch;
|
|
6513
|
+
let messageContent = rawContent.trim();
|
|
6514
|
+
quotedMultiline = false;
|
|
6515
|
+
if (messageContent.startsWith('"')) {
|
|
6516
|
+
messageContent = messageContent.slice(1);
|
|
6517
|
+
quotedMultiline = !messageContent.endsWith('"');
|
|
6518
|
+
if (!quotedMultiline) messageContent = messageContent.slice(0, -1);
|
|
6873
6519
|
}
|
|
6874
|
-
|
|
6875
|
-
|
|
6876
|
-
const msgDate = new Date(currentDate);
|
|
6877
|
-
msgDate.setHours(hours, minutes, 0, 0);
|
|
6878
|
-
timestamp = Math.floor(msgDate.getTime() / 1e3);
|
|
6879
|
-
} else {
|
|
6880
|
-
const now = /* @__PURE__ */ new Date();
|
|
6881
|
-
now.setHours(hours, minutes, 0, 0);
|
|
6882
|
-
timestamp = Math.floor(now.getTime() / 1e3);
|
|
6520
|
+
if (!memberMap.has(sender)) {
|
|
6521
|
+
memberMap.set(sender, { platformId: sender, accountName: sender });
|
|
6883
6522
|
}
|
|
6884
6523
|
lastMessage = {
|
|
6885
|
-
senderPlatformId:
|
|
6886
|
-
senderAccountName:
|
|
6887
|
-
timestamp,
|
|
6888
|
-
type:
|
|
6889
|
-
content:
|
|
6524
|
+
senderPlatformId: sender,
|
|
6525
|
+
senderAccountName: sender,
|
|
6526
|
+
timestamp: buildTimestamp(currentDate, timeText, options2.now),
|
|
6527
|
+
type: detectMessageType6(messageContent),
|
|
6528
|
+
content: messageContent || null
|
|
6890
6529
|
};
|
|
6891
6530
|
messages.push(lastMessage);
|
|
6892
|
-
|
|
6893
|
-
|
|
6894
|
-
|
|
6895
|
-
|
|
6896
|
-
|
|
6897
|
-
|
|
6898
|
-
|
|
6899
|
-
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6531
|
+
} else {
|
|
6532
|
+
const systemMatch = line.match(SYSTEM_MESSAGE_PATTERN);
|
|
6533
|
+
if (systemMatch) {
|
|
6534
|
+
const [, timeText, rawContent] = systemMatch;
|
|
6535
|
+
const messageContent = rawContent.trim();
|
|
6536
|
+
quotedMultiline = false;
|
|
6537
|
+
lastMessage = {
|
|
6538
|
+
senderPlatformId: "system",
|
|
6539
|
+
senderAccountName: "\u7CFB\u7D71",
|
|
6540
|
+
timestamp: buildTimestamp(currentDate, timeText, options2.now),
|
|
6541
|
+
type: 80 /* SYSTEM */,
|
|
6542
|
+
content: messageContent || null
|
|
6543
|
+
};
|
|
6544
|
+
messages.push(lastMessage);
|
|
6545
|
+
} else if (line.trim() && lastMessage) {
|
|
6546
|
+
let continuation = line;
|
|
6547
|
+
if (quotedMultiline && continuation.endsWith('"')) {
|
|
6548
|
+
continuation = continuation.slice(0, -1);
|
|
6549
|
+
quotedMultiline = false;
|
|
6550
|
+
}
|
|
6551
|
+
lastMessage.content = lastMessage.content ? `${lastMessage.content}
|
|
6552
|
+
${continuation}` : continuation;
|
|
6904
6553
|
}
|
|
6905
6554
|
}
|
|
6906
6555
|
}
|
|
6556
|
+
if (lineIndex > 0 && lineIndex % yieldEvery === 0) {
|
|
6557
|
+
options2.onProgress?.({ progress: lineIndex / lines.length, messagesProcessed: messages.length });
|
|
6558
|
+
await new Promise((resolve4) => setTimeout(resolve4, 0));
|
|
6559
|
+
options2.checkCancelled?.();
|
|
6560
|
+
}
|
|
6907
6561
|
}
|
|
6562
|
+
options2.checkCancelled?.();
|
|
6908
6563
|
const memberCount = memberMap.size;
|
|
6909
|
-
const chatType = memberCount <= 2 ? "private" /* PRIVATE */ : "group" /* GROUP */;
|
|
6910
|
-
const
|
|
6911
|
-
name: chatName,
|
|
6912
|
-
|
|
6913
|
-
|
|
6564
|
+
const chatType = header ? header.isGroup ? "group" /* GROUP */ : "private" /* PRIVATE */ : memberCount <= 2 ? "private" /* PRIVATE */ : "group" /* GROUP */;
|
|
6565
|
+
const result = {
|
|
6566
|
+
meta: { name: chatName, platform: KNOWN_PLATFORMS.LINE, type: chatType },
|
|
6567
|
+
members: Array.from(memberMap.values()),
|
|
6568
|
+
messages
|
|
6914
6569
|
};
|
|
6915
|
-
|
|
6916
|
-
|
|
6917
|
-
|
|
6918
|
-
|
|
6919
|
-
|
|
6920
|
-
|
|
6570
|
+
options2.onProgress?.({ progress: 1, messagesProcessed: messages.length });
|
|
6571
|
+
return result;
|
|
6572
|
+
}
|
|
6573
|
+
function extractNameFromFileName3(fileName) {
|
|
6574
|
+
const basename10 = getBasename3(fileName).replace(/\.txt$/, "");
|
|
6575
|
+
return basename10.replace(/^\[LINE\]\s*/i, "").trim() || "\u672A\u77E5\u804A\u5929";
|
|
6576
|
+
}
|
|
6577
|
+
function extractNameFromHeader(header) {
|
|
6578
|
+
const englishPrivate = header.match(/^(?:\[LINE\] )?Chat history with (.+)$/m);
|
|
6579
|
+
if (englishPrivate) return { name: englishPrivate[1].trim(), isGroup: false };
|
|
6580
|
+
const englishGroup = header.match(/^(?:\[LINE\] )?Chat history in (.+)$/m);
|
|
6581
|
+
if (englishGroup) return { name: englishGroup[1].trim(), isGroup: true };
|
|
6582
|
+
const japanesePrivate = header.match(/^\[LINE\] (.+)とのトーク履歴/);
|
|
6583
|
+
if (japanesePrivate) return { name: japanesePrivate[1].trim(), isGroup: false };
|
|
6584
|
+
const japaneseGroup = header.match(/^\[LINE\] (.+)のトーク履歴/);
|
|
6585
|
+
if (japaneseGroup) return { name: japaneseGroup[1].trim(), isGroup: true };
|
|
6586
|
+
const simplifiedPrivate = header.match(/^\[LINE\] 与(.+)的聊天记录/);
|
|
6587
|
+
if (simplifiedPrivate) return { name: simplifiedPrivate[1].trim(), isGroup: false };
|
|
6588
|
+
const simplifiedGroup = header.match(/^\[LINE\] (.+)的聊天记录/);
|
|
6589
|
+
if (simplifiedGroup) return { name: simplifiedGroup[1].trim(), isGroup: true };
|
|
6590
|
+
const traditionalPrivate = header.match(/^\[LINE\] 與(.+)的聊天記錄/);
|
|
6591
|
+
if (traditionalPrivate) return { name: traditionalPrivate[1].trim(), isGroup: false };
|
|
6592
|
+
const traditionalGroup = header.match(/^\[LINE\] (.+)的聊天記錄/);
|
|
6593
|
+
return traditionalGroup ? { name: traditionalGroup[1].trim(), isGroup: true } : null;
|
|
6594
|
+
}
|
|
6595
|
+
function getBasename3(fileName) {
|
|
6596
|
+
return fileName.split(/[\\/]/).pop() ?? fileName;
|
|
6597
|
+
}
|
|
6598
|
+
function parseDateLine(line) {
|
|
6599
|
+
for (let index = 0; index < DATE_PATTERNS.length; index += 1) {
|
|
6600
|
+
const match = line.match(DATE_PATTERNS[index]);
|
|
6601
|
+
if (!match) continue;
|
|
6602
|
+
if (index < 2) {
|
|
6603
|
+
return new Date(Number.parseInt(match[1], 10), Number.parseInt(match[2], 10) - 1, Number.parseInt(match[3], 10));
|
|
6604
|
+
}
|
|
6605
|
+
return new Date(Number.parseInt(match[3], 10), Number.parseInt(match[1], 10) - 1, Number.parseInt(match[2], 10));
|
|
6921
6606
|
}
|
|
6922
|
-
|
|
6607
|
+
return null;
|
|
6608
|
+
}
|
|
6609
|
+
function buildTimestamp(currentDate, timeText, now) {
|
|
6610
|
+
const { hours, minutes } = parseTime(timeText);
|
|
6611
|
+
const value = currentDate ? new Date(currentDate) : now?.() ?? /* @__PURE__ */ new Date();
|
|
6612
|
+
value.setHours(hours, minutes, 0, 0);
|
|
6613
|
+
return Math.floor(value.getTime() / 1e3);
|
|
6614
|
+
}
|
|
6615
|
+
function parseTime(timeText) {
|
|
6616
|
+
const prefix = timeText.match(/^(上午|下午|午前|午後)/)?.[1];
|
|
6617
|
+
const cleanTime = timeText.replace(/^(上午|下午|午前|午後)/, "");
|
|
6618
|
+
const match = cleanTime.match(/^(\d{1,2}):(\d{2})([AaPp][Mm])?$/i);
|
|
6619
|
+
if (!match) {
|
|
6620
|
+
const [hours2, minutes2] = timeText.split(":").map(Number);
|
|
6621
|
+
return { hours: hours2, minutes: minutes2 };
|
|
6622
|
+
}
|
|
6623
|
+
let hours = Number.parseInt(match[1], 10);
|
|
6624
|
+
const minutes = Number.parseInt(match[2], 10);
|
|
6625
|
+
const suffix = match[3]?.toLowerCase();
|
|
6626
|
+
if (suffix === "pm" && hours < 12) hours += 12;
|
|
6627
|
+
if (suffix === "am" && hours === 12) hours = 0;
|
|
6628
|
+
if ((prefix === "\u4E0B\u5348" || prefix === "\u5348\u5F8C") && hours < 12) hours += 12;
|
|
6629
|
+
if ((prefix === "\u4E0A\u5348" || prefix === "\u5348\u524D") && hours === 12) hours = 0;
|
|
6630
|
+
return { hours, minutes };
|
|
6631
|
+
}
|
|
6632
|
+
function detectMessageType6(content) {
|
|
6633
|
+
for (const [pattern, type] of Object.entries(SPECIAL_MESSAGE_TYPES)) {
|
|
6634
|
+
if (content === pattern || content.startsWith(pattern)) return type;
|
|
6635
|
+
}
|
|
6636
|
+
if (content.startsWith("[null]") && content.includes("maps.google.com")) return 8 /* LOCATION */;
|
|
6637
|
+
if (content.includes(" joined the group") || content.includes("\u5DF2\u52A0\u5165\u8BE5\u7FA4") || content.includes("\u5DF2\u52A0\u5165\u7FA4\u7D44") || content.includes("\u304C\u30B0\u30EB\u30FC\u30D7\u306B\u53C2\u52A0\u3057\u307E\u3057\u305F") || content.includes(" added ") || content.includes(" to the group") || content.includes("\u5DF2\u5C06") || content.includes("\u6DFB\u52A0\u81F3\u7FA4") || content.includes("\u6DFB\u52A0\u5230\u7FA4") || content.includes("\u5DF2\u65B0\u589E") || content.includes("\u81F3\u7FA4\u7D44") || content.includes("\u3092\u30B0\u30EB\u30FC\u30D7\u306B\u8FFD\u52A0\u3057\u307E\u3057\u305F") || content.includes(" left the group") || content.includes("\u5DF2\u9000\u7FA4") || content.includes("\u5DF2\u96E2\u958B\u7FA4\u7D44") || content.includes("\u304C\u30B0\u30EB\u30FC\u30D7\u3092\u9000\u4F1A\u3057\u307E\u3057\u305F") || content.includes("made an announcement") || content.includes("\u53D1\u5E03\u4E86\u901A\u544A") || content.includes("\u5DF2\u8A2D\u5B9A\u516C\u544A") || content.includes("\u304C\u30A2\u30CA\u30A6\u30F3\u30B9\u3057\u307E\u3057\u305F") || content.includes("unsent a message") || content === "Message unsent." || content.includes("\u64A4\u56DE\u4E86\u4E00\u6761\u6D88\u606F") || content.includes("\u5DF2\u6536\u56DE\u8A0A\u606F") || content.includes("\u9001\u4FE1\u3092\u53D6\u308A\u6D88\u3057\u307E\u3057\u305F") || content.startsWith("Auto-reply")) {
|
|
6638
|
+
return 80 /* SYSTEM */;
|
|
6639
|
+
}
|
|
6640
|
+
return /^https?:\/\//.test(content) ? 7 /* LINK */ : 0 /* TEXT */;
|
|
6641
|
+
}
|
|
6642
|
+
|
|
6643
|
+
// ../../packages/parser/src/formats/line-native-txt.ts
|
|
6644
|
+
var feature14 = {
|
|
6645
|
+
id: "line-native-txt",
|
|
6646
|
+
name: "LINE \u5B98\u65B9\u5BFC\u51FA TXT",
|
|
6647
|
+
platform: KNOWN_PLATFORMS.LINE,
|
|
6648
|
+
priority: 35,
|
|
6649
|
+
extensions: [".txt"],
|
|
6650
|
+
signatures: {
|
|
6651
|
+
head: LINE_HEAD_SIGNATURES,
|
|
6652
|
+
filename: LINE_FILENAME_SIGNATURES
|
|
6653
|
+
}
|
|
6654
|
+
};
|
|
6655
|
+
async function* parseLine2(options2) {
|
|
6656
|
+
const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
|
|
6657
|
+
const totalBytes = getFileSize(filePath);
|
|
6658
|
+
const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
|
|
6659
|
+
yield { type: "progress", data: initialProgress };
|
|
6660
|
+
onProgress?.(initialProgress);
|
|
6661
|
+
onLog?.("info", `Starting LINE TXT parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`);
|
|
6662
|
+
const result = await parseLineText(fs17.readFileSync(filePath, "utf-8"), filePath, {
|
|
6663
|
+
onProgress: (progress) => onProgress?.(
|
|
6664
|
+
createProgress(
|
|
6665
|
+
"parsing",
|
|
6666
|
+
Math.round(progress.progress * totalBytes),
|
|
6667
|
+
totalBytes,
|
|
6668
|
+
progress.messagesProcessed,
|
|
6669
|
+
`Processed ${progress.messagesProcessed} messages`
|
|
6670
|
+
)
|
|
6671
|
+
)
|
|
6672
|
+
});
|
|
6673
|
+
yield { type: "meta", data: result.meta };
|
|
6674
|
+
yield { type: "members", data: result.members };
|
|
6675
|
+
for (let index = 0; index < result.messages.length; index += batchSize) {
|
|
6676
|
+
yield { type: "messages", data: result.messages.slice(index, index + batchSize) };
|
|
6677
|
+
}
|
|
6678
|
+
const doneProgress = createProgress("done", totalBytes, totalBytes, result.messages.length, "");
|
|
6923
6679
|
yield { type: "progress", data: doneProgress };
|
|
6924
6680
|
onProgress?.(doneProgress);
|
|
6925
|
-
onLog?.("info",
|
|
6681
|
+
onLog?.("info", `LINE TXT parsing completed: ${result.messages.length} messages, ${result.members.length} members`);
|
|
6926
6682
|
yield {
|
|
6927
6683
|
type: "done",
|
|
6928
|
-
data: { messageCount:
|
|
6684
|
+
data: { messageCount: result.messages.length, memberCount: result.members.length }
|
|
6929
6685
|
};
|
|
6930
6686
|
}
|
|
6931
6687
|
var parser_13 = {
|
|
6932
6688
|
feature: feature14,
|
|
6933
|
-
parse:
|
|
6689
|
+
parse: parseLine2
|
|
6934
6690
|
};
|
|
6935
6691
|
var module_14 = {
|
|
6936
6692
|
feature: feature14,
|
|
@@ -6970,6 +6726,398 @@ var formats = [
|
|
|
6970
6726
|
// 优先级 51 - ChatLab JSONL(流式格式,支持超大文件)
|
|
6971
6727
|
];
|
|
6972
6728
|
|
|
6729
|
+
// ../../packages/parser/src/chatlab-validator.ts
|
|
6730
|
+
import { createReadStream as createReadStream13 } from "fs";
|
|
6731
|
+
import { readFile, stat } from "fs/promises";
|
|
6732
|
+
import { extname as extname2 } from "path";
|
|
6733
|
+
import { createInterface as createInterface5 } from "readline";
|
|
6734
|
+
var MAX_REPORTED_ISSUES = 100;
|
|
6735
|
+
var MAX_UNIX_SECONDS = 1e11;
|
|
6736
|
+
var KNOWN_MESSAGE_TYPES = new Set(
|
|
6737
|
+
Object.values(MessageType).filter((value) => typeof value === "number")
|
|
6738
|
+
);
|
|
6739
|
+
function createState(format, fileSize) {
|
|
6740
|
+
return {
|
|
6741
|
+
format,
|
|
6742
|
+
fileSize,
|
|
6743
|
+
errorCount: 0,
|
|
6744
|
+
warningCount: 0,
|
|
6745
|
+
issues: [],
|
|
6746
|
+
stats: {
|
|
6747
|
+
headerCount: 0,
|
|
6748
|
+
memberCount: 0,
|
|
6749
|
+
messageCount: 0,
|
|
6750
|
+
uniqueSenderCount: 0,
|
|
6751
|
+
commentLineCount: 0,
|
|
6752
|
+
blankLineCount: 0
|
|
6753
|
+
},
|
|
6754
|
+
memberIds: /* @__PURE__ */ new Set(),
|
|
6755
|
+
ownerIds: /* @__PURE__ */ new Set(),
|
|
6756
|
+
senderIds: /* @__PURE__ */ new Set(),
|
|
6757
|
+
messageIds: /* @__PURE__ */ new Set(),
|
|
6758
|
+
replyTargetIds: /* @__PURE__ */ new Set()
|
|
6759
|
+
};
|
|
6760
|
+
}
|
|
6761
|
+
function addIssue(state, severity, code, message, location = {}) {
|
|
6762
|
+
if (severity === "error") state.errorCount++;
|
|
6763
|
+
else state.warningCount++;
|
|
6764
|
+
if (state.issues.length < MAX_REPORTED_ISSUES) {
|
|
6765
|
+
state.issues.push({ severity, code, message, ...location });
|
|
6766
|
+
}
|
|
6767
|
+
}
|
|
6768
|
+
function isRecord(value) {
|
|
6769
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
6770
|
+
}
|
|
6771
|
+
function hasOwn(value, key) {
|
|
6772
|
+
return Object.prototype.hasOwnProperty.call(value, key);
|
|
6773
|
+
}
|
|
6774
|
+
function childLocation(location, path30) {
|
|
6775
|
+
return { ...location, path: path30 };
|
|
6776
|
+
}
|
|
6777
|
+
function validateRequiredString(state, value, path30, location) {
|
|
6778
|
+
if (typeof value === "string" && value.trim().length > 0) return true;
|
|
6779
|
+
addIssue(state, "error", "INVALID_REQUIRED_STRING", "Expected a non-empty string.", childLocation(location, path30));
|
|
6780
|
+
return false;
|
|
6781
|
+
}
|
|
6782
|
+
function validateOptionalString(state, object, key, path30, location) {
|
|
6783
|
+
if (!hasOwn(object, key)) return;
|
|
6784
|
+
if (typeof object[key] !== "string") {
|
|
6785
|
+
addIssue(state, "error", "INVALID_OPTIONAL_STRING", "Expected a string.", childLocation(location, path30));
|
|
6786
|
+
}
|
|
6787
|
+
}
|
|
6788
|
+
function validateUnixSeconds(state, value, path30, location) {
|
|
6789
|
+
if (typeof value === "number" && Number.isInteger(value) && value >= 0 && value < MAX_UNIX_SECONDS) {
|
|
6790
|
+
return true;
|
|
6791
|
+
}
|
|
6792
|
+
addIssue(
|
|
6793
|
+
state,
|
|
6794
|
+
"error",
|
|
6795
|
+
"INVALID_UNIX_SECONDS",
|
|
6796
|
+
"Expected a non-negative integer Unix timestamp in seconds, not milliseconds.",
|
|
6797
|
+
childLocation(location, path30)
|
|
6798
|
+
);
|
|
6799
|
+
return false;
|
|
6800
|
+
}
|
|
6801
|
+
function validateHeader(state, chatlab, meta, location, basePath = "") {
|
|
6802
|
+
state.stats.headerCount++;
|
|
6803
|
+
if (!isRecord(chatlab)) {
|
|
6804
|
+
addIssue(state, "error", "INVALID_CHATLAB_HEADER", "Expected a chatlab object.", {
|
|
6805
|
+
...location,
|
|
6806
|
+
path: `${basePath}chatlab`
|
|
6807
|
+
});
|
|
6808
|
+
} else {
|
|
6809
|
+
const versionPath = `${basePath}chatlab.version`;
|
|
6810
|
+
if (typeof chatlab.version !== "string") {
|
|
6811
|
+
addIssue(state, "error", "INVALID_VERSION", "Expected a string format version.", {
|
|
6812
|
+
...location,
|
|
6813
|
+
path: versionPath
|
|
6814
|
+
});
|
|
6815
|
+
} else {
|
|
6816
|
+
state.version = chatlab.version;
|
|
6817
|
+
if (!isSupportedChatLabFormatVersion(chatlab.version)) {
|
|
6818
|
+
addIssue(
|
|
6819
|
+
state,
|
|
6820
|
+
"error",
|
|
6821
|
+
"UNSUPPORTED_VERSION",
|
|
6822
|
+
`Supported ChatLab format versions: ${CHATLAB_SUPPORTED_FORMAT_VERSIONS.join(", ")}.`,
|
|
6823
|
+
{
|
|
6824
|
+
...location,
|
|
6825
|
+
path: versionPath
|
|
6826
|
+
}
|
|
6827
|
+
);
|
|
6828
|
+
}
|
|
6829
|
+
}
|
|
6830
|
+
validateUnixSeconds(state, chatlab.exportedAt, `${basePath}chatlab.exportedAt`, location);
|
|
6831
|
+
validateOptionalString(state, chatlab, "generator", `${basePath}chatlab.generator`, location);
|
|
6832
|
+
validateOptionalString(state, chatlab, "description", `${basePath}chatlab.description`, location);
|
|
6833
|
+
}
|
|
6834
|
+
if (!isRecord(meta)) {
|
|
6835
|
+
addIssue(state, "error", "INVALID_META", "Expected a meta object.", {
|
|
6836
|
+
...location,
|
|
6837
|
+
path: `${basePath}meta`
|
|
6838
|
+
});
|
|
6839
|
+
return;
|
|
6840
|
+
}
|
|
6841
|
+
validateRequiredString(state, meta.name, `${basePath}meta.name`, location);
|
|
6842
|
+
validateRequiredString(state, meta.platform, `${basePath}meta.platform`, location);
|
|
6843
|
+
if (meta.type !== "group" && meta.type !== "private") {
|
|
6844
|
+
addIssue(state, "error", "INVALID_CHAT_TYPE", 'Expected chat type "group" or "private".', {
|
|
6845
|
+
...location,
|
|
6846
|
+
path: `${basePath}meta.type`
|
|
6847
|
+
});
|
|
6848
|
+
}
|
|
6849
|
+
validateOptionalString(state, meta, "groupId", `${basePath}meta.groupId`, location);
|
|
6850
|
+
validateOptionalString(state, meta, "groupAvatar", `${basePath}meta.groupAvatar`, location);
|
|
6851
|
+
if (hasOwn(meta, "ownerId")) {
|
|
6852
|
+
if (validateRequiredString(state, meta.ownerId, `${basePath}meta.ownerId`, location)) {
|
|
6853
|
+
state.ownerIds.add(meta.ownerId);
|
|
6854
|
+
}
|
|
6855
|
+
}
|
|
6856
|
+
validateOptionalString(state, meta, "sourceSessionId", `${basePath}meta.sourceSessionId`, location);
|
|
6857
|
+
}
|
|
6858
|
+
function validateMember(state, value, path30, location) {
|
|
6859
|
+
state.stats.memberCount++;
|
|
6860
|
+
if (!isRecord(value)) {
|
|
6861
|
+
addIssue(state, "error", "INVALID_MEMBER", "Expected a member object.", childLocation(location, path30));
|
|
6862
|
+
return;
|
|
6863
|
+
}
|
|
6864
|
+
if (validateRequiredString(state, value.platformId, `${path30}.platformId`, location)) {
|
|
6865
|
+
if (state.memberIds.has(value.platformId)) {
|
|
6866
|
+
addIssue(state, "error", "DUPLICATE_MEMBER_ID", "Member platformId must be unique.", {
|
|
6867
|
+
...location,
|
|
6868
|
+
path: `${path30}.platformId`
|
|
6869
|
+
});
|
|
6870
|
+
} else {
|
|
6871
|
+
state.memberIds.add(value.platformId);
|
|
6872
|
+
}
|
|
6873
|
+
}
|
|
6874
|
+
validateRequiredString(state, value.accountName, `${path30}.accountName`, location);
|
|
6875
|
+
validateOptionalString(state, value, "groupNickname", `${path30}.groupNickname`, location);
|
|
6876
|
+
validateOptionalString(state, value, "avatar", `${path30}.avatar`, location);
|
|
6877
|
+
if (hasOwn(value, "aliases")) {
|
|
6878
|
+
if (!Array.isArray(value.aliases) || value.aliases.some((alias) => typeof alias !== "string")) {
|
|
6879
|
+
addIssue(state, "error", "INVALID_ALIASES", "Expected aliases to be an array of strings.", {
|
|
6880
|
+
...location,
|
|
6881
|
+
path: `${path30}.aliases`
|
|
6882
|
+
});
|
|
6883
|
+
}
|
|
6884
|
+
}
|
|
6885
|
+
if (hasOwn(value, "roles")) {
|
|
6886
|
+
if (!Array.isArray(value.roles)) {
|
|
6887
|
+
addIssue(state, "error", "INVALID_ROLES", "Expected roles to be an array.", {
|
|
6888
|
+
...location,
|
|
6889
|
+
path: `${path30}.roles`
|
|
6890
|
+
});
|
|
6891
|
+
} else {
|
|
6892
|
+
value.roles.forEach((role, index) => {
|
|
6893
|
+
const rolePath = `${path30}.roles[${index}]`;
|
|
6894
|
+
if (!isRecord(role)) {
|
|
6895
|
+
addIssue(state, "error", "INVALID_ROLE", "Expected a role object.", {
|
|
6896
|
+
...location,
|
|
6897
|
+
path: rolePath
|
|
6898
|
+
});
|
|
6899
|
+
return;
|
|
6900
|
+
}
|
|
6901
|
+
validateRequiredString(state, role.id, `${rolePath}.id`, location);
|
|
6902
|
+
validateOptionalString(state, role, "name", `${rolePath}.name`, location);
|
|
6903
|
+
});
|
|
6904
|
+
}
|
|
6905
|
+
}
|
|
6906
|
+
}
|
|
6907
|
+
function validateMessage(state, value, path30, location) {
|
|
6908
|
+
state.stats.messageCount++;
|
|
6909
|
+
if (!isRecord(value)) {
|
|
6910
|
+
addIssue(state, "error", "INVALID_MESSAGE", "Expected a message object.", childLocation(location, path30));
|
|
6911
|
+
return;
|
|
6912
|
+
}
|
|
6913
|
+
if (validateRequiredString(state, value.sender, `${path30}.sender`, location)) {
|
|
6914
|
+
state.senderIds.add(value.sender);
|
|
6915
|
+
}
|
|
6916
|
+
validateRequiredString(state, value.accountName, `${path30}.accountName`, location);
|
|
6917
|
+
if (validateUnixSeconds(state, value.timestamp, `${path30}.timestamp`, location)) {
|
|
6918
|
+
if (state.lastTimestamp !== void 0 && value.timestamp < state.lastTimestamp) {
|
|
6919
|
+
addIssue(
|
|
6920
|
+
state,
|
|
6921
|
+
"warning",
|
|
6922
|
+
"OUT_OF_ORDER_TIMESTAMP",
|
|
6923
|
+
"Messages should be ordered by timestamp ascending.",
|
|
6924
|
+
childLocation(location, `${path30}.timestamp`)
|
|
6925
|
+
);
|
|
6926
|
+
}
|
|
6927
|
+
state.lastTimestamp = value.timestamp;
|
|
6928
|
+
}
|
|
6929
|
+
if (typeof value.type !== "number" || !Number.isInteger(value.type) || !KNOWN_MESSAGE_TYPES.has(value.type)) {
|
|
6930
|
+
addIssue(state, "error", "INVALID_MESSAGE_TYPE", "Expected a supported numeric ChatLab message type.", {
|
|
6931
|
+
...location,
|
|
6932
|
+
path: `${path30}.type`
|
|
6933
|
+
});
|
|
6934
|
+
}
|
|
6935
|
+
if (!hasOwn(value, "content") || typeof value.content !== "string" && value.content !== null) {
|
|
6936
|
+
addIssue(state, "error", "INVALID_MESSAGE_CONTENT", "Expected content to be a string or null.", {
|
|
6937
|
+
...location,
|
|
6938
|
+
path: `${path30}.content`
|
|
6939
|
+
});
|
|
6940
|
+
}
|
|
6941
|
+
validateOptionalString(state, value, "groupNickname", `${path30}.groupNickname`, location);
|
|
6942
|
+
if (hasOwn(value, "platformMessageId")) {
|
|
6943
|
+
if (validateRequiredString(state, value.platformMessageId, `${path30}.platformMessageId`, location)) {
|
|
6944
|
+
if (state.messageIds.has(value.platformMessageId)) {
|
|
6945
|
+
addIssue(state, "error", "DUPLICATE_MESSAGE_ID", "platformMessageId must be unique.", {
|
|
6946
|
+
...location,
|
|
6947
|
+
path: `${path30}.platformMessageId`
|
|
6948
|
+
});
|
|
6949
|
+
} else {
|
|
6950
|
+
state.messageIds.add(value.platformMessageId);
|
|
6951
|
+
}
|
|
6952
|
+
}
|
|
6953
|
+
}
|
|
6954
|
+
if (hasOwn(value, "replyToMessageId")) {
|
|
6955
|
+
if (validateRequiredString(state, value.replyToMessageId, `${path30}.replyToMessageId`, location)) {
|
|
6956
|
+
state.replyTargetIds.add(value.replyToMessageId);
|
|
6957
|
+
}
|
|
6958
|
+
}
|
|
6959
|
+
}
|
|
6960
|
+
function finalizeState(state, requireExplicitMembers, enforceRequiredSections = true) {
|
|
6961
|
+
if (enforceRequiredSections) {
|
|
6962
|
+
if (state.stats.headerCount === 0) {
|
|
6963
|
+
addIssue(state, "error", "MISSING_HEADER", "A ChatLab header is required.");
|
|
6964
|
+
}
|
|
6965
|
+
if (state.stats.messageCount === 0) {
|
|
6966
|
+
addIssue(state, "error", "MISSING_MESSAGES", "At least one message is required.");
|
|
6967
|
+
}
|
|
6968
|
+
if (requireExplicitMembers && state.stats.memberCount === 0) {
|
|
6969
|
+
addIssue(state, "error", "MISSING_MEMBERS", "The JSON format requires a members array with sender identities.");
|
|
6970
|
+
}
|
|
6971
|
+
}
|
|
6972
|
+
const ownerMemberIds = state.memberIds.size > 0 ? state.memberIds : requireExplicitMembers ? void 0 : state.senderIds;
|
|
6973
|
+
if (ownerMemberIds) {
|
|
6974
|
+
for (const ownerId of state.ownerIds) {
|
|
6975
|
+
if (!ownerMemberIds.has(ownerId)) {
|
|
6976
|
+
addIssue(state, "error", "UNKNOWN_OWNER", "meta.ownerId must reference a member platformId.");
|
|
6977
|
+
}
|
|
6978
|
+
}
|
|
6979
|
+
}
|
|
6980
|
+
if (state.memberIds.size > 0) {
|
|
6981
|
+
for (const senderId of state.senderIds) {
|
|
6982
|
+
if (!state.memberIds.has(senderId)) {
|
|
6983
|
+
addIssue(
|
|
6984
|
+
state,
|
|
6985
|
+
"error",
|
|
6986
|
+
"UNKNOWN_MESSAGE_SENDER",
|
|
6987
|
+
"Every message sender must reference an explicit member platformId."
|
|
6988
|
+
);
|
|
6989
|
+
}
|
|
6990
|
+
}
|
|
6991
|
+
}
|
|
6992
|
+
for (const replyTargetId of state.replyTargetIds) {
|
|
6993
|
+
if (!state.messageIds.has(replyTargetId)) {
|
|
6994
|
+
addIssue(
|
|
6995
|
+
state,
|
|
6996
|
+
"warning",
|
|
6997
|
+
"UNKNOWN_REPLY_TARGET",
|
|
6998
|
+
"A replyToMessageId does not match any platformMessageId in this file."
|
|
6999
|
+
);
|
|
7000
|
+
}
|
|
7001
|
+
}
|
|
7002
|
+
state.stats.uniqueSenderCount = state.senderIds.size;
|
|
7003
|
+
const totalIssueCount = state.errorCount + state.warningCount;
|
|
7004
|
+
return {
|
|
7005
|
+
valid: state.errorCount === 0,
|
|
7006
|
+
format: state.format,
|
|
7007
|
+
version: state.version,
|
|
7008
|
+
fileSize: state.fileSize,
|
|
7009
|
+
errorCount: state.errorCount,
|
|
7010
|
+
warningCount: state.warningCount,
|
|
7011
|
+
issues: state.issues,
|
|
7012
|
+
truncatedIssueCount: Math.max(0, totalIssueCount - state.issues.length),
|
|
7013
|
+
stats: state.stats
|
|
7014
|
+
};
|
|
7015
|
+
}
|
|
7016
|
+
async function validateJson(filePath, fileSize) {
|
|
7017
|
+
const state = createState("json", fileSize);
|
|
7018
|
+
let root;
|
|
7019
|
+
try {
|
|
7020
|
+
root = JSON.parse(await readFile(filePath, "utf8"));
|
|
7021
|
+
} catch {
|
|
7022
|
+
addIssue(state, "error", "INVALID_JSON", "The file is not valid JSON.");
|
|
7023
|
+
return finalizeState(state, true, false);
|
|
7024
|
+
}
|
|
7025
|
+
if (!isRecord(root)) {
|
|
7026
|
+
addIssue(state, "error", "INVALID_ROOT", "Expected a top-level JSON object.");
|
|
7027
|
+
return finalizeState(state, true, false);
|
|
7028
|
+
}
|
|
7029
|
+
validateHeader(state, root.chatlab, root.meta, {});
|
|
7030
|
+
if (!Array.isArray(root.members)) {
|
|
7031
|
+
addIssue(state, "error", "INVALID_MEMBERS", "Expected members to be an array.", { path: "members" });
|
|
7032
|
+
} else {
|
|
7033
|
+
root.members.forEach((member, index) => validateMember(state, member, `members[${index}]`, {}));
|
|
7034
|
+
}
|
|
7035
|
+
if (!Array.isArray(root.messages)) {
|
|
7036
|
+
addIssue(state, "error", "INVALID_MESSAGES", "Expected messages to be an array.", { path: "messages" });
|
|
7037
|
+
} else {
|
|
7038
|
+
root.messages.forEach((message, index) => validateMessage(state, message, `messages[${index}]`, {}));
|
|
7039
|
+
}
|
|
7040
|
+
return finalizeState(state, true);
|
|
7041
|
+
}
|
|
7042
|
+
async function validateJsonl(filePath, fileSize) {
|
|
7043
|
+
const state = createState("jsonl", fileSize);
|
|
7044
|
+
const input = createReadStream13(filePath, { encoding: "utf8" });
|
|
7045
|
+
const lines = createInterface5({ input, crlfDelay: Infinity });
|
|
7046
|
+
let lineNumber = 0;
|
|
7047
|
+
let dataLineCount = 0;
|
|
7048
|
+
let sawMessage = false;
|
|
7049
|
+
for await (const line of lines) {
|
|
7050
|
+
lineNumber++;
|
|
7051
|
+
const trimmed = line.trim();
|
|
7052
|
+
if (!trimmed) {
|
|
7053
|
+
state.stats.blankLineCount++;
|
|
7054
|
+
continue;
|
|
7055
|
+
}
|
|
7056
|
+
if (trimmed.startsWith("#")) {
|
|
7057
|
+
state.stats.commentLineCount++;
|
|
7058
|
+
continue;
|
|
7059
|
+
}
|
|
7060
|
+
dataLineCount++;
|
|
7061
|
+
let value;
|
|
7062
|
+
try {
|
|
7063
|
+
value = JSON.parse(trimmed);
|
|
7064
|
+
} catch {
|
|
7065
|
+
addIssue(state, "error", "INVALID_JSONL_LINE", "The line is not valid JSON.", { line: lineNumber });
|
|
7066
|
+
continue;
|
|
7067
|
+
}
|
|
7068
|
+
if (!isRecord(value)) {
|
|
7069
|
+
addIssue(state, "error", "INVALID_JSONL_VALUE", "Expected a JSON object on every data line.", {
|
|
7070
|
+
line: lineNumber
|
|
7071
|
+
});
|
|
7072
|
+
continue;
|
|
7073
|
+
}
|
|
7074
|
+
if (dataLineCount === 1 && value._type !== "header") {
|
|
7075
|
+
addIssue(state, "error", "HEADER_NOT_FIRST", "The first data line must be a header.", {
|
|
7076
|
+
line: lineNumber,
|
|
7077
|
+
path: "_type"
|
|
7078
|
+
});
|
|
7079
|
+
}
|
|
7080
|
+
switch (value._type) {
|
|
7081
|
+
case "header":
|
|
7082
|
+
if (state.stats.headerCount > 0) {
|
|
7083
|
+
addIssue(state, "error", "DUPLICATE_HEADER", "Only one header line is allowed.", {
|
|
7084
|
+
line: lineNumber,
|
|
7085
|
+
path: "_type"
|
|
7086
|
+
});
|
|
7087
|
+
}
|
|
7088
|
+
validateHeader(state, value.chatlab, value.meta, { line: lineNumber });
|
|
7089
|
+
break;
|
|
7090
|
+
case "member":
|
|
7091
|
+
if (sawMessage) {
|
|
7092
|
+
addIssue(state, "error", "MEMBER_AFTER_MESSAGE", "Member lines must appear before message lines.", {
|
|
7093
|
+
line: lineNumber
|
|
7094
|
+
});
|
|
7095
|
+
}
|
|
7096
|
+
validateMember(state, value, `line[${lineNumber}]`, { line: lineNumber });
|
|
7097
|
+
break;
|
|
7098
|
+
case "message":
|
|
7099
|
+
sawMessage = true;
|
|
7100
|
+
validateMessage(state, value, `line[${lineNumber}]`, { line: lineNumber });
|
|
7101
|
+
break;
|
|
7102
|
+
default:
|
|
7103
|
+
addIssue(state, "error", "UNKNOWN_LINE_TYPE", "Expected _type to be header, member, or message.", {
|
|
7104
|
+
line: lineNumber,
|
|
7105
|
+
path: "_type"
|
|
7106
|
+
});
|
|
7107
|
+
}
|
|
7108
|
+
}
|
|
7109
|
+
return finalizeState(state, false);
|
|
7110
|
+
}
|
|
7111
|
+
async function validateChatLabFile(filePath) {
|
|
7112
|
+
const fileStat = await stat(filePath);
|
|
7113
|
+
const extension = extname2(filePath).toLowerCase();
|
|
7114
|
+
if (extension === ".json") return validateJson(filePath, fileStat.size);
|
|
7115
|
+
if (extension === ".jsonl") return validateJsonl(filePath, fileStat.size);
|
|
7116
|
+
const state = createState("unknown", fileStat.size);
|
|
7117
|
+
addIssue(state, "error", "UNSUPPORTED_EXTENSION", "Expected a .json or .jsonl file.");
|
|
7118
|
+
return finalizeState(state, false, false);
|
|
7119
|
+
}
|
|
7120
|
+
|
|
6973
7121
|
// ../../packages/parser/src/index.ts
|
|
6974
7122
|
var sniffer = createSniffer();
|
|
6975
7123
|
sniffer.registerAll(formats);
|
|
@@ -7031,6 +7179,9 @@ async function* parseFileWithFormat(formatId, options2) {
|
|
|
7031
7179
|
yield* parser11.parse(options2);
|
|
7032
7180
|
}
|
|
7033
7181
|
function findEntryFileInDirectory(dirPath, maxDepth = 1) {
|
|
7182
|
+
const supportedExtensions = new Set(
|
|
7183
|
+
sniffer.getSupportedFormats().flatMap((feature15) => feature15.extensions).map((extension) => extension.toLowerCase())
|
|
7184
|
+
);
|
|
7034
7185
|
function walk(current, depth) {
|
|
7035
7186
|
if (depth > maxDepth) return null;
|
|
7036
7187
|
let entries;
|
|
@@ -7040,15 +7191,15 @@ function findEntryFileInDirectory(dirPath, maxDepth = 1) {
|
|
|
7040
7191
|
return null;
|
|
7041
7192
|
}
|
|
7042
7193
|
for (const entry of entries) {
|
|
7043
|
-
const full =
|
|
7044
|
-
if (entry.isFile() && entry.name.
|
|
7194
|
+
const full = path11.join(current, entry.name);
|
|
7195
|
+
if (entry.isFile() && supportedExtensions.has(path11.extname(entry.name).toLowerCase())) {
|
|
7045
7196
|
const format = detectFormat(full);
|
|
7046
7197
|
if (format) return full;
|
|
7047
7198
|
}
|
|
7048
7199
|
}
|
|
7049
7200
|
for (const entry of entries) {
|
|
7050
7201
|
if (entry.isDirectory()) {
|
|
7051
|
-
const found = walk(
|
|
7202
|
+
const found = walk(path11.join(current, entry.name), depth + 1);
|
|
7052
7203
|
if (found) return found;
|
|
7053
7204
|
}
|
|
7054
7205
|
}
|
|
@@ -7098,7 +7249,7 @@ function logNativeParserStatus() {
|
|
|
7098
7249
|
|
|
7099
7250
|
// ../../packages/node-runtime/src/import/perf-logger.ts
|
|
7100
7251
|
import * as fs19 from "fs";
|
|
7101
|
-
import * as
|
|
7252
|
+
import * as path12 from "path";
|
|
7102
7253
|
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
7103
7254
|
LogLevel2["ERROR"] = "ERROR";
|
|
7104
7255
|
LogLevel2["INFO"] = "INFO";
|
|
@@ -7113,7 +7264,7 @@ function initPerfLog(sessionId, logDir) {
|
|
|
7113
7264
|
if (!fs19.existsSync(logDir)) {
|
|
7114
7265
|
fs19.mkdirSync(logDir, { recursive: true });
|
|
7115
7266
|
}
|
|
7116
|
-
currentLogFile =
|
|
7267
|
+
currentLogFile = path12.join(logDir, `import_${sessionId}_${Date.now()}.log`);
|
|
7117
7268
|
fs19.writeFileSync(currentLogFile, `=== Import Log ===
|
|
7118
7269
|
Start time: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
7119
7270
|
|
|
@@ -7290,7 +7441,7 @@ function segmentText(text, locale, minLength) {
|
|
|
7290
7441
|
|
|
7291
7442
|
// ../../packages/node-runtime/src/nlp/dict-manager.ts
|
|
7292
7443
|
import * as fs20 from "fs";
|
|
7293
|
-
import * as
|
|
7444
|
+
import * as path13 from "path";
|
|
7294
7445
|
import { createHash } from "crypto";
|
|
7295
7446
|
var DICT_DOWNLOAD_URL_BASE = "https://chatlab.fun/assets/nlp";
|
|
7296
7447
|
var DICT_SHA256 = {
|
|
@@ -7302,7 +7453,7 @@ var AVAILABLE_DICTS = [
|
|
|
7302
7453
|
{ id: "zh-TW", label: "\u7E41\u9AD4\u4E2D\u6587", locale: "zh-TW" }
|
|
7303
7454
|
];
|
|
7304
7455
|
function getDictFilePath(nlpDir, dictId) {
|
|
7305
|
-
return
|
|
7456
|
+
return path13.join(nlpDir, `${dictId}.dict`);
|
|
7306
7457
|
}
|
|
7307
7458
|
function isDictDownloaded(nlpDir, dictId) {
|
|
7308
7459
|
return fs20.existsSync(getDictFilePath(nlpDir, dictId));
|
|
@@ -8768,7 +8919,7 @@ async function analyzeAutoImportFile(filePath, deps, options2 = {}) {
|
|
|
8768
8919
|
|
|
8769
8920
|
// ../../packages/node-runtime/src/import/import-lock.ts
|
|
8770
8921
|
import * as fs22 from "fs";
|
|
8771
|
-
import * as
|
|
8922
|
+
import * as path14 from "path";
|
|
8772
8923
|
import { randomUUID } from "crypto";
|
|
8773
8924
|
var IMPORT_LOCK_FILENAME = ".chatlab-import.lock";
|
|
8774
8925
|
var IMPORT_IN_PROGRESS_ERROR_KEY = "error.import_in_progress";
|
|
@@ -8837,7 +8988,7 @@ function releaseLock(lockPath, fd, owner) {
|
|
|
8837
8988
|
}
|
|
8838
8989
|
async function withDataDirImportLock(userDataDir, task) {
|
|
8839
8990
|
fs22.mkdirSync(userDataDir, { recursive: true });
|
|
8840
|
-
const lockPath =
|
|
8991
|
+
const lockPath = path14.join(userDataDir, IMPORT_LOCK_FILENAME);
|
|
8841
8992
|
let fd = null;
|
|
8842
8993
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
8843
8994
|
try {
|
|
@@ -8882,7 +9033,7 @@ var ArchiveImportError = class extends Error {
|
|
|
8882
9033
|
// ../../packages/node-runtime/src/import/archive/archive-reader.ts
|
|
8883
9034
|
var import_yauzl = __toESM(require_yauzl(), 1);
|
|
8884
9035
|
import * as fs23 from "fs";
|
|
8885
|
-
import * as
|
|
9036
|
+
import * as path15 from "path";
|
|
8886
9037
|
import { pipeline } from "stream/promises";
|
|
8887
9038
|
var DEFAULT_MAX_ENTRIES = 1e5;
|
|
8888
9039
|
var DEFAULT_MAX_ENTRY_BYTES = 20 * 1024 * 1024 * 1024;
|
|
@@ -9001,7 +9152,7 @@ var ZipArchiveReader = class {
|
|
|
9001
9152
|
await this.visitEntries(async (entry, openStream) => {
|
|
9002
9153
|
const destination = targets.get(entry.name);
|
|
9003
9154
|
if (!destination || entry.isDirectory) return;
|
|
9004
|
-
fs23.mkdirSync(
|
|
9155
|
+
fs23.mkdirSync(path15.dirname(destination), { recursive: true });
|
|
9005
9156
|
await pipeline(await openStream(), fs23.createWriteStream(destination, { flags: "wx" }));
|
|
9006
9157
|
remaining.delete(entry.name);
|
|
9007
9158
|
});
|
|
@@ -9015,16 +9166,16 @@ var ZipArchiveReader = class {
|
|
|
9015
9166
|
};
|
|
9016
9167
|
|
|
9017
9168
|
// ../../packages/node-runtime/src/import/archive/google-chat-resolver.ts
|
|
9018
|
-
var
|
|
9169
|
+
var import_stream_chain8 = __toESM(require_stream_chain(), 1);
|
|
9019
9170
|
var import_stream_json9 = __toESM(require_stream_json(), 1);
|
|
9020
9171
|
var import_Pick8 = __toESM(require_Pick(), 1);
|
|
9021
|
-
var
|
|
9172
|
+
var import_StreamValues8 = __toESM(require_StreamValues(), 1);
|
|
9022
9173
|
import * as fs24 from "fs";
|
|
9023
|
-
import * as
|
|
9024
|
-
var { chain:
|
|
9174
|
+
import * as path16 from "path";
|
|
9175
|
+
var { chain: chain8 } = import_stream_chain8.default;
|
|
9025
9176
|
var { parser: parser10 } = import_stream_json9.default;
|
|
9026
9177
|
var { pick: pick8 } = import_Pick8.default;
|
|
9027
|
-
var { streamValues:
|
|
9178
|
+
var { streamValues: streamValues8 } = import_StreamValues8.default;
|
|
9028
9179
|
var GROUP_FILE = /^Takeout\/Google Chat\/Groups\/((DM|Space) [^/]+)\/(group_info|messages)\.json$/;
|
|
9029
9180
|
var USER_INFO_FILE = /^Takeout\/Google Chat\/Users\/[^/]+\/user_info\.json$/;
|
|
9030
9181
|
var TAKEOUT_ROOT = /^Takeout\/([^/]+)\//;
|
|
@@ -9047,7 +9198,7 @@ async function readSmallJson(stream, maxBytes = MAX_METADATA_BYTES) {
|
|
|
9047
9198
|
}
|
|
9048
9199
|
}
|
|
9049
9200
|
async function countMessages(stream) {
|
|
9050
|
-
const pipeline2 =
|
|
9201
|
+
const pipeline2 = chain8([stream, parser10(), pick8({ filter: /^messages\.\d+$/ }), streamValues8()]);
|
|
9051
9202
|
let count = 0;
|
|
9052
9203
|
try {
|
|
9053
9204
|
for await (const _item of pipeline2) count++;
|
|
@@ -9152,12 +9303,12 @@ var GoogleChatTakeoutResolver = class {
|
|
|
9152
9303
|
fs24.mkdirSync(targetDir, { recursive: true });
|
|
9153
9304
|
await reader.pipeEntries(
|
|
9154
9305
|
/* @__PURE__ */ new Map([
|
|
9155
|
-
[userInfoEntry,
|
|
9156
|
-
[groupInfoEntry,
|
|
9157
|
-
[messagesEntry,
|
|
9306
|
+
[userInfoEntry, path16.join(targetDir, "user_info.json")],
|
|
9307
|
+
[groupInfoEntry, path16.join(targetDir, "group_info.json")],
|
|
9308
|
+
[messagesEntry, path16.join(targetDir, "messages.json")]
|
|
9158
9309
|
])
|
|
9159
9310
|
);
|
|
9160
|
-
const manifestPath =
|
|
9311
|
+
const manifestPath = path16.join(targetDir, "google-chat-import.json");
|
|
9161
9312
|
fs24.writeFileSync(
|
|
9162
9313
|
manifestPath,
|
|
9163
9314
|
JSON.stringify(
|
|
@@ -9182,7 +9333,7 @@ var GoogleChatTakeoutResolver = class {
|
|
|
9182
9333
|
|
|
9183
9334
|
// ../../packages/node-runtime/src/import/archive/source-manager.ts
|
|
9184
9335
|
import * as fs25 from "fs";
|
|
9185
|
-
import * as
|
|
9336
|
+
import * as path17 from "path";
|
|
9186
9337
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
9187
9338
|
var DEFAULT_TTL_MS = 30 * 60 * 1e3;
|
|
9188
9339
|
var ArchiveImportSourceManager = class {
|
|
@@ -9254,7 +9405,7 @@ var ArchiveImportSourceManager = class {
|
|
|
9254
9405
|
`Archive conversation was not found: ${chatId}`
|
|
9255
9406
|
);
|
|
9256
9407
|
}
|
|
9257
|
-
const materializedDir = fs25.mkdtempSync(
|
|
9408
|
+
const materializedDir = fs25.mkdtempSync(path17.join(this.tempRoot, `materialized-${sourceId}-`));
|
|
9258
9409
|
try {
|
|
9259
9410
|
const reader = new ZipArchiveReader(record.archivePath);
|
|
9260
9411
|
const materialized = await record.resolver.materialize(reader, chat, materializedDir);
|
|
@@ -9375,12 +9526,12 @@ ${rawErrorLabel}: ${details}`;
|
|
|
9375
9526
|
}
|
|
9376
9527
|
|
|
9377
9528
|
// ../../packages/node-runtime/src/ai/assistant-parser.ts
|
|
9378
|
-
import * as
|
|
9529
|
+
import * as path18 from "path";
|
|
9379
9530
|
import matter from "gray-matter";
|
|
9380
9531
|
function parseAssistantFile(content, filePath) {
|
|
9381
9532
|
try {
|
|
9382
9533
|
const { data: fm, content: body } = matter(content);
|
|
9383
|
-
const id = fm.id ??
|
|
9534
|
+
const id = fm.id ?? path18.basename(filePath, ".md");
|
|
9384
9535
|
const name = fm.name;
|
|
9385
9536
|
if (!name) return null;
|
|
9386
9537
|
return {
|
|
@@ -9890,12 +10041,12 @@ var AssistantManager = class {
|
|
|
9890
10041
|
};
|
|
9891
10042
|
|
|
9892
10043
|
// ../../packages/node-runtime/src/ai/skill-parser.ts
|
|
9893
|
-
import * as
|
|
10044
|
+
import * as path19 from "path";
|
|
9894
10045
|
import matter2 from "gray-matter";
|
|
9895
10046
|
function parseSkillFile(content, filePath) {
|
|
9896
10047
|
try {
|
|
9897
10048
|
const { data: fm, content: prompt } = matter2(content);
|
|
9898
|
-
const id = fm.id ??
|
|
10049
|
+
const id = fm.id ?? path19.basename(filePath, ".md");
|
|
9899
10050
|
const name = fm.name;
|
|
9900
10051
|
if (!name) return null;
|
|
9901
10052
|
return {
|
|
@@ -9914,7 +10065,7 @@ function parseSkillFile(content, filePath) {
|
|
|
9914
10065
|
function extractSkillId(content, filePath) {
|
|
9915
10066
|
try {
|
|
9916
10067
|
const { data: fm } = matter2(content);
|
|
9917
|
-
return fm.id ??
|
|
10068
|
+
return fm.id ?? path19.basename(filePath, ".md");
|
|
9918
10069
|
} catch {
|
|
9919
10070
|
return null;
|
|
9920
10071
|
}
|
|
@@ -9934,7 +10085,7 @@ function validateChatScope(raw) {
|
|
|
9934
10085
|
// ../../packages/node-runtime/src/ai/chats.ts
|
|
9935
10086
|
import Database from "better-sqlite3";
|
|
9936
10087
|
import * as fs26 from "fs";
|
|
9937
|
-
import * as
|
|
10088
|
+
import * as path20 from "path";
|
|
9938
10089
|
var DEFAULT_GENERAL_ID = DEFAULT_GENERAL_ASSISTANT_ID;
|
|
9939
10090
|
var defaultLogger = {
|
|
9940
10091
|
warn(_category, message, extra) {
|
|
@@ -9960,7 +10111,7 @@ var AIChatManager = class {
|
|
|
9960
10111
|
getDb() {
|
|
9961
10112
|
if (this.db) return this.db;
|
|
9962
10113
|
this.ensureDir(this.aiDataDir);
|
|
9963
|
-
const dbPath =
|
|
10114
|
+
const dbPath = path20.join(this.aiDataDir, "conversations.db");
|
|
9964
10115
|
this.db = this.nativeBinding ? new Database(dbPath, { nativeBinding: this.nativeBinding }) : new Database(dbPath);
|
|
9965
10116
|
this.db.pragma("journal_mode = WAL");
|
|
9966
10117
|
this.db.exec(`
|
|
@@ -10203,16 +10354,16 @@ var AIChatManager = class {
|
|
|
10203
10354
|
if (allRows.length === 0) return [];
|
|
10204
10355
|
const rowMap = new Map(allRows.map((row) => [row.id, row]));
|
|
10205
10356
|
let currentId = leafMessageId ?? this.getActiveMessageId(aiChatId);
|
|
10206
|
-
const
|
|
10357
|
+
const path30 = [];
|
|
10207
10358
|
const seen = /* @__PURE__ */ new Set();
|
|
10208
10359
|
while (currentId && !seen.has(currentId)) {
|
|
10209
10360
|
seen.add(currentId);
|
|
10210
10361
|
const row = rowMap.get(currentId);
|
|
10211
10362
|
if (!row) break;
|
|
10212
|
-
|
|
10363
|
+
path30.push(row);
|
|
10213
10364
|
currentId = row.parentId;
|
|
10214
10365
|
}
|
|
10215
|
-
return
|
|
10366
|
+
return path30.length > 0 ? path30.reverse() : allRows;
|
|
10216
10367
|
}
|
|
10217
10368
|
// ==================== 生命周期 ====================
|
|
10218
10369
|
close() {
|
|
@@ -10949,7 +11100,7 @@ var SkillManagerCore = class {
|
|
|
10949
11100
|
|
|
10950
11101
|
// ../../packages/node-runtime/src/ai/skill-manager.ts
|
|
10951
11102
|
import * as fs27 from "fs";
|
|
10952
|
-
import * as
|
|
11103
|
+
import * as path21 from "path";
|
|
10953
11104
|
var SKILLS_DIR_NAME = "skills";
|
|
10954
11105
|
var defaultLogger2 = {
|
|
10955
11106
|
info: () => {
|
|
@@ -10978,7 +11129,7 @@ var nodeFs = {
|
|
|
10978
11129
|
return fs27.existsSync(filePath);
|
|
10979
11130
|
},
|
|
10980
11131
|
joinPath(...parts) {
|
|
10981
|
-
return
|
|
11132
|
+
return path21.join(...parts);
|
|
10982
11133
|
}
|
|
10983
11134
|
};
|
|
10984
11135
|
var SkillManager = class {
|
|
@@ -10988,7 +11139,7 @@ var SkillManager = class {
|
|
|
10988
11139
|
this.logger = logger ?? defaultLogger2;
|
|
10989
11140
|
this.core = new SkillManagerCore({
|
|
10990
11141
|
fs: nodeFs,
|
|
10991
|
-
skillsDir:
|
|
11142
|
+
skillsDir: path21.join(aiDataDir, SKILLS_DIR_NAME),
|
|
10992
11143
|
builtinRawSkills: [],
|
|
10993
11144
|
logger: {
|
|
10994
11145
|
info: () => {
|
|
@@ -12850,7 +13001,7 @@ var PLAN_INTENTS = [
|
|
|
12850
13001
|
"mixed"
|
|
12851
13002
|
];
|
|
12852
13003
|
var STREAM_MARKER_HOLDBACK_CHARS = Math.max("</draft>".length, "</plan>".length, "<json>".length) - 1;
|
|
12853
|
-
function
|
|
13004
|
+
function isRecord2(value) {
|
|
12854
13005
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12855
13006
|
}
|
|
12856
13007
|
function isIntent(value) {
|
|
@@ -12872,10 +13023,10 @@ function extractJsonObject4(text) {
|
|
|
12872
13023
|
function parsePlanJson(rawText, availableTools) {
|
|
12873
13024
|
try {
|
|
12874
13025
|
const parsed = JSON.parse(extractJsonObject4(rawText));
|
|
12875
|
-
if (!
|
|
13026
|
+
if (!isRecord2(parsed)) return null;
|
|
12876
13027
|
const title = normalizeString(parsed.title);
|
|
12877
13028
|
if (!title || !isIntent(parsed.intent) || !Array.isArray(parsed.steps)) return null;
|
|
12878
|
-
const steps = parsed.steps.filter(
|
|
13029
|
+
const steps = parsed.steps.filter(isRecord2).map((step) => {
|
|
12879
13030
|
const goal = normalizeString(step.goal);
|
|
12880
13031
|
const evidenceNeeded = normalizeString(step.evidenceNeeded);
|
|
12881
13032
|
if (!goal || !evidenceNeeded) return null;
|
|
@@ -14854,9 +15005,9 @@ var localeMap = {
|
|
|
14854
15005
|
"ja-JP": ja_JP_default,
|
|
14855
15006
|
"zh-TW": zh_TW_default
|
|
14856
15007
|
};
|
|
14857
|
-
function getNestedValue(obj,
|
|
15008
|
+
function getNestedValue(obj, path30) {
|
|
14858
15009
|
let current = obj;
|
|
14859
|
-
for (const segment2 of
|
|
15010
|
+
for (const segment2 of path30.split(".")) {
|
|
14860
15011
|
if (current == null || typeof current !== "object") return void 0;
|
|
14861
15012
|
current = current[segment2];
|
|
14862
15013
|
}
|
|
@@ -15371,19 +15522,19 @@ var LLMConfigStore = class {
|
|
|
15371
15522
|
|
|
15372
15523
|
// ../../packages/node-runtime/src/ai/file-config-storage.ts
|
|
15373
15524
|
import * as fs28 from "fs";
|
|
15374
|
-
import * as
|
|
15525
|
+
import * as path22 from "path";
|
|
15375
15526
|
function createFileConfigStorage(baseDir) {
|
|
15376
15527
|
return {
|
|
15377
15528
|
readJson(key) {
|
|
15378
15529
|
try {
|
|
15379
|
-
return JSON.parse(fs28.readFileSync(
|
|
15530
|
+
return JSON.parse(fs28.readFileSync(path22.join(baseDir, `${key}.json`), "utf-8"));
|
|
15380
15531
|
} catch {
|
|
15381
15532
|
return null;
|
|
15382
15533
|
}
|
|
15383
15534
|
},
|
|
15384
15535
|
writeJson(key, data) {
|
|
15385
15536
|
fs28.mkdirSync(baseDir, { recursive: true });
|
|
15386
|
-
fs28.writeFileSync(
|
|
15537
|
+
fs28.writeFileSync(path22.join(baseDir, `${key}.json`), JSON.stringify(data, null, 2), "utf-8");
|
|
15387
15538
|
}
|
|
15388
15539
|
};
|
|
15389
15540
|
}
|
|
@@ -16122,7 +16273,7 @@ function exportAsJson(db, messages, sessionName, sourceSessionId) {
|
|
|
16122
16273
|
).all();
|
|
16123
16274
|
const data = {
|
|
16124
16275
|
chatlab: {
|
|
16125
|
-
version:
|
|
16276
|
+
version: CHATLAB_FORMAT_VERSION,
|
|
16126
16277
|
exportedAt: Math.floor(Date.now() / 1e3),
|
|
16127
16278
|
generator: "ChatLab"
|
|
16128
16279
|
},
|
|
@@ -16232,7 +16383,7 @@ function exportWithFormat(params, openDatabase) {
|
|
|
16232
16383
|
|
|
16233
16384
|
// ../../packages/node-runtime/src/merger/temp-db.ts
|
|
16234
16385
|
import * as fs29 from "fs";
|
|
16235
|
-
import * as
|
|
16386
|
+
import * as path23 from "path";
|
|
16236
16387
|
var TEMP_DB_SCHEMA = `
|
|
16237
16388
|
CREATE TABLE IF NOT EXISTS meta (
|
|
16238
16389
|
name TEXT NOT NULL,
|
|
@@ -16447,7 +16598,7 @@ function exportSessionToJson(db) {
|
|
|
16447
16598
|
).all();
|
|
16448
16599
|
return {
|
|
16449
16600
|
chatlab: {
|
|
16450
|
-
version:
|
|
16601
|
+
version: CHATLAB_FORMAT_VERSION,
|
|
16451
16602
|
exportedAt: Math.floor(Date.now() / 1e3),
|
|
16452
16603
|
generator: "ChatLab Export",
|
|
16453
16604
|
description: `Exported from session: ${meta.name}`
|
|
@@ -16491,7 +16642,7 @@ function cleanupTempDatabases(tempDir) {
|
|
|
16491
16642
|
const files = fs29.readdirSync(tempDir);
|
|
16492
16643
|
for (const file of files) {
|
|
16493
16644
|
if (file.startsWith("merge_") && file.endsWith(".db")) {
|
|
16494
|
-
deleteTempDatabase(
|
|
16645
|
+
deleteTempDatabase(path23.join(tempDir, file));
|
|
16495
16646
|
}
|
|
16496
16647
|
}
|
|
16497
16648
|
} catch {
|
|
@@ -16877,7 +17028,7 @@ function clearSessionOwner(adapter, sessionId) {
|
|
|
16877
17028
|
|
|
16878
17029
|
// ../../packages/node-runtime/src/services/contacts/overrides.ts
|
|
16879
17030
|
import fs30 from "fs";
|
|
16880
|
-
import
|
|
17031
|
+
import path24 from "path";
|
|
16881
17032
|
var CONTACT_OVERRIDES_FILE_NAME = "contact-overrides.json";
|
|
16882
17033
|
var CONTACT_OVERRIDES_TMP_PREFIX = "contact-overrides.tmp-";
|
|
16883
17034
|
var CONTACT_OVERRIDES_VERSION = 1;
|
|
@@ -16888,7 +17039,7 @@ function createEmptyContactOverridesFile() {
|
|
|
16888
17039
|
};
|
|
16889
17040
|
}
|
|
16890
17041
|
function getContactOverridesPath(snapshotDir) {
|
|
16891
|
-
return
|
|
17042
|
+
return path24.join(snapshotDir, CONTACT_OVERRIDES_FILE_NAME);
|
|
16892
17043
|
}
|
|
16893
17044
|
function readContactOverrides(snapshotDir) {
|
|
16894
17045
|
const filePath = getContactOverridesPath(snapshotDir);
|
|
@@ -16902,16 +17053,16 @@ function readContactOverrides(snapshotDir) {
|
|
|
16902
17053
|
}
|
|
16903
17054
|
function writeContactOverrides(snapshotDir, overrides) {
|
|
16904
17055
|
if (!fs30.existsSync(snapshotDir)) fs30.mkdirSync(snapshotDir, { recursive: true });
|
|
16905
|
-
const tmpPath =
|
|
17056
|
+
const tmpPath = path24.join(snapshotDir, `${CONTACT_OVERRIDES_TMP_PREFIX}${process.pid}-${Date.now()}`);
|
|
16906
17057
|
fs30.writeFileSync(tmpPath, JSON.stringify(normalizeContactOverrides(overrides), null, 2), "utf-8");
|
|
16907
17058
|
fs30.renameSync(tmpPath, getContactOverridesPath(snapshotDir));
|
|
16908
17059
|
}
|
|
16909
17060
|
function normalizeContactOverrides(value) {
|
|
16910
|
-
const input =
|
|
16911
|
-
const manualFriendsInput =
|
|
17061
|
+
const input = isRecord3(value) ? value : {};
|
|
17062
|
+
const manualFriendsInput = isRecord3(input.manualFriends) ? input.manualFriends : {};
|
|
16912
17063
|
const manualFriends = {};
|
|
16913
17064
|
for (const [key, entry] of Object.entries(manualFriendsInput)) {
|
|
16914
|
-
if (!key || !
|
|
17065
|
+
if (!key || !isRecord3(entry)) continue;
|
|
16915
17066
|
const createdAt = Number.isFinite(entry.createdAt) ? Number(entry.createdAt) : Date.now();
|
|
16916
17067
|
const updatedAt = Number.isFinite(entry.updatedAt) ? Number(entry.updatedAt) : createdAt;
|
|
16917
17068
|
manualFriends[key] = { key, createdAt, updatedAt };
|
|
@@ -16921,7 +17072,7 @@ function normalizeContactOverrides(value) {
|
|
|
16921
17072
|
manualFriends
|
|
16922
17073
|
};
|
|
16923
17074
|
}
|
|
16924
|
-
function
|
|
17075
|
+
function isRecord3(value) {
|
|
16925
17076
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16926
17077
|
}
|
|
16927
17078
|
|
|
@@ -16940,11 +17091,11 @@ function buildContactsSignature(adapter, timeRangePreset) {
|
|
|
16940
17091
|
|
|
16941
17092
|
// ../../packages/node-runtime/src/services/contacts/snapshot.ts
|
|
16942
17093
|
import fs31 from "fs";
|
|
16943
|
-
import
|
|
17094
|
+
import path25 from "path";
|
|
16944
17095
|
var CONTACTS_SNAPSHOT_TMP_PREFIX = "contacts-snapshot.tmp-";
|
|
16945
17096
|
function getContactsSnapshotPath(snapshotDir, timeRangePreset) {
|
|
16946
17097
|
const preset = normalizeContactsTimeRangePreset(timeRangePreset);
|
|
16947
|
-
return
|
|
17098
|
+
return path25.join(snapshotDir, `contacts-snapshot-${preset}.json`);
|
|
16948
17099
|
}
|
|
16949
17100
|
function readContactsSnapshot(snapshotDir, timeRangePreset, options2 = {}) {
|
|
16950
17101
|
const snapshotPath = getContactsSnapshotPath(snapshotDir, timeRangePreset);
|
|
@@ -16953,7 +17104,7 @@ function readContactsSnapshot(snapshotDir, timeRangePreset, options2 = {}) {
|
|
|
16953
17104
|
return JSON.parse(fs31.readFileSync(snapshotPath, "utf-8"));
|
|
16954
17105
|
} catch (error) {
|
|
16955
17106
|
const ts = options2.now?.() ?? Date.now();
|
|
16956
|
-
const backupPath =
|
|
17107
|
+
const backupPath = path25.join(snapshotDir, `contacts-snapshot.corrupt-${ts}.json`);
|
|
16957
17108
|
try {
|
|
16958
17109
|
fs31.renameSync(snapshotPath, backupPath);
|
|
16959
17110
|
} catch (renameError) {
|
|
@@ -16965,7 +17116,7 @@ function readContactsSnapshot(snapshotDir, timeRangePreset, options2 = {}) {
|
|
|
16965
17116
|
}
|
|
16966
17117
|
function writeContactsSnapshot(snapshotDir, snapshot) {
|
|
16967
17118
|
if (!fs31.existsSync(snapshotDir)) fs31.mkdirSync(snapshotDir, { recursive: true });
|
|
16968
|
-
const tmpPath =
|
|
17119
|
+
const tmpPath = path25.join(snapshotDir, `${CONTACTS_SNAPSHOT_TMP_PREFIX}${process.pid}-${Date.now()}`);
|
|
16969
17120
|
fs31.writeFileSync(tmpPath, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
16970
17121
|
fs31.renameSync(tmpPath, getContactsSnapshotPath(snapshotDir, snapshot.timeRange.preset));
|
|
16971
17122
|
}
|
|
@@ -16974,7 +17125,7 @@ function cleanupContactsSnapshotTempFiles(snapshotDir) {
|
|
|
16974
17125
|
for (const name of fs31.readdirSync(snapshotDir)) {
|
|
16975
17126
|
if (!name.startsWith(CONTACTS_SNAPSHOT_TMP_PREFIX)) continue;
|
|
16976
17127
|
try {
|
|
16977
|
-
fs31.rmSync(
|
|
17128
|
+
fs31.rmSync(path25.join(snapshotDir, name), { force: true });
|
|
16978
17129
|
} catch (error) {
|
|
16979
17130
|
appLogger.warn("contacts", "failed to remove contacts snapshot temp file", error);
|
|
16980
17131
|
}
|
|
@@ -17473,11 +17624,11 @@ function buildPeopleRelationshipsSignature(adapter, timeRangePreset) {
|
|
|
17473
17624
|
|
|
17474
17625
|
// ../../packages/node-runtime/src/services/people/relationships/snapshot.ts
|
|
17475
17626
|
import fs32 from "fs";
|
|
17476
|
-
import
|
|
17627
|
+
import path26 from "path";
|
|
17477
17628
|
var PEOPLE_RELATIONSHIPS_SNAPSHOT_TMP_PREFIX = "people-relationships-snapshot.tmp-";
|
|
17478
17629
|
function getPeopleRelationshipsSnapshotPath(snapshotDir, timeRangePreset) {
|
|
17479
17630
|
const preset = normalizePeopleRelationshipsTimeRangePreset(timeRangePreset);
|
|
17480
|
-
return
|
|
17631
|
+
return path26.join(snapshotDir, `graph-snapshot-${preset}.json`);
|
|
17481
17632
|
}
|
|
17482
17633
|
function readPeopleRelationshipsSnapshot(snapshotDir, timeRangePreset, options2 = {}) {
|
|
17483
17634
|
const snapshotPath = getPeopleRelationshipsSnapshotPath(snapshotDir, timeRangePreset);
|
|
@@ -17486,7 +17637,7 @@ function readPeopleRelationshipsSnapshot(snapshotDir, timeRangePreset, options2
|
|
|
17486
17637
|
return JSON.parse(fs32.readFileSync(snapshotPath, "utf-8"));
|
|
17487
17638
|
} catch (error) {
|
|
17488
17639
|
const ts = options2.now?.() ?? Date.now();
|
|
17489
|
-
const backupPath =
|
|
17640
|
+
const backupPath = path26.join(snapshotDir, `graph-snapshot.corrupt-${ts}.json`);
|
|
17490
17641
|
try {
|
|
17491
17642
|
fs32.renameSync(snapshotPath, backupPath);
|
|
17492
17643
|
} catch (renameError) {
|
|
@@ -17498,7 +17649,7 @@ function readPeopleRelationshipsSnapshot(snapshotDir, timeRangePreset, options2
|
|
|
17498
17649
|
}
|
|
17499
17650
|
function writePeopleRelationshipsSnapshot(snapshotDir, snapshot) {
|
|
17500
17651
|
if (!fs32.existsSync(snapshotDir)) fs32.mkdirSync(snapshotDir, { recursive: true });
|
|
17501
|
-
const tmpPath =
|
|
17652
|
+
const tmpPath = path26.join(snapshotDir, `${PEOPLE_RELATIONSHIPS_SNAPSHOT_TMP_PREFIX}${process.pid}-${Date.now()}`);
|
|
17502
17653
|
fs32.writeFileSync(tmpPath, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
17503
17654
|
fs32.renameSync(tmpPath, getPeopleRelationshipsSnapshotPath(snapshotDir, snapshot.timeRange.preset));
|
|
17504
17655
|
}
|
|
@@ -17507,7 +17658,7 @@ function cleanupPeopleRelationshipsSnapshotTempFiles(snapshotDir) {
|
|
|
17507
17658
|
for (const name of fs32.readdirSync(snapshotDir)) {
|
|
17508
17659
|
if (!name.startsWith(PEOPLE_RELATIONSHIPS_SNAPSHOT_TMP_PREFIX)) continue;
|
|
17509
17660
|
try {
|
|
17510
|
-
fs32.rmSync(
|
|
17661
|
+
fs32.rmSync(path26.join(snapshotDir, name), { force: true });
|
|
17511
17662
|
} catch (error) {
|
|
17512
17663
|
appLogger.warn("people-relationships", "failed to remove people relationships snapshot temp file", error);
|
|
17513
17664
|
}
|
|
@@ -18331,7 +18482,7 @@ __export(global_insight_exports, {
|
|
|
18331
18482
|
|
|
18332
18483
|
// ../../packages/node-runtime/src/semantic-index/config.ts
|
|
18333
18484
|
import fs34 from "fs";
|
|
18334
|
-
import
|
|
18485
|
+
import path27 from "path";
|
|
18335
18486
|
var DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE = "huggingface";
|
|
18336
18487
|
var SEMANTIC_INDEX_CONFIG_VERSION = 1;
|
|
18337
18488
|
var SEARCH_MAX_RESULTS_DEFAULT = 10;
|
|
@@ -18430,7 +18581,7 @@ var SemanticIndexConfigStore = class {
|
|
|
18430
18581
|
}
|
|
18431
18582
|
set(config) {
|
|
18432
18583
|
const normalized = normalizeConfig(config);
|
|
18433
|
-
const dir =
|
|
18584
|
+
const dir = path27.dirname(this.filePath);
|
|
18434
18585
|
if (!fs34.existsSync(dir)) fs34.mkdirSync(dir, { recursive: true });
|
|
18435
18586
|
fs34.writeFileSync(this.filePath, JSON.stringify(normalized, null, 2), "utf-8");
|
|
18436
18587
|
return normalized;
|
|
@@ -18441,7 +18592,7 @@ var SemanticIndexConfigStore = class {
|
|
|
18441
18592
|
};
|
|
18442
18593
|
|
|
18443
18594
|
// ../../packages/node-runtime/src/semantic-index/service.ts
|
|
18444
|
-
import
|
|
18595
|
+
import path28 from "path";
|
|
18445
18596
|
|
|
18446
18597
|
// ../../packages/node-runtime/src/semantic-index/chunker-config.ts
|
|
18447
18598
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -19972,16 +20123,16 @@ function resolveSearchToolEvidenceTokens(maxResultTokens) {
|
|
|
19972
20123
|
return maxResultTokens && maxResultTokens > 0 ? Math.min(SEARCH_TOOL_EVIDENCE_TOKENS, maxResultTokens) : SEARCH_TOOL_EVIDENCE_TOKENS;
|
|
19973
20124
|
}
|
|
19974
20125
|
function sessionIdFromDbPath(dbPath) {
|
|
19975
|
-
return
|
|
20126
|
+
return path28.basename(dbPath, ".db");
|
|
19976
20127
|
}
|
|
19977
20128
|
var SEMANTIC_INDEX_DB_FILE = "embedding_index.db";
|
|
19978
20129
|
var SEMANTIC_INDEX_CONFIG_FILE = "semantic-index-config.json";
|
|
19979
20130
|
function createSemanticIndexService(params) {
|
|
19980
20131
|
const { pathProvider } = params;
|
|
19981
20132
|
return new SemanticIndexService({
|
|
19982
|
-
vectorDbPath:
|
|
19983
|
-
configPath:
|
|
19984
|
-
modelsCacheDir:
|
|
20133
|
+
vectorDbPath: path28.join(pathProvider.getVectorDir(), SEMANTIC_INDEX_DB_FILE),
|
|
20134
|
+
configPath: path28.join(pathProvider.getAiDataDir(), SEMANTIC_INDEX_CONFIG_FILE),
|
|
20135
|
+
modelsCacheDir: path28.join(pathProvider.getAiDataDir(), "models", "semantic-index"),
|
|
19985
20136
|
modelDownloadProxyUrl: params.modelDownloadProxyUrl,
|
|
19986
20137
|
sessionAdapter: params.sessionAdapter,
|
|
19987
20138
|
nativeBinding: params.nativeBinding,
|
|
@@ -20532,7 +20683,7 @@ ${text}`;
|
|
|
20532
20683
|
|
|
20533
20684
|
// ../../packages/node-runtime/src/services/merge-cache.ts
|
|
20534
20685
|
import * as fs35 from "fs";
|
|
20535
|
-
import * as
|
|
20686
|
+
import * as path29 from "path";
|
|
20536
20687
|
import * as crypto from "crypto";
|
|
20537
20688
|
var MergeSessionCache = class {
|
|
20538
20689
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -20549,7 +20700,7 @@ var MergeSessionCache = class {
|
|
|
20549
20700
|
const ts = Date.now();
|
|
20550
20701
|
const rand = Math.random().toString(36).slice(2, 8);
|
|
20551
20702
|
const safeName = filename.replace(/[/\\?%*:|"<>]/g, "_").slice(0, 40);
|
|
20552
|
-
return
|
|
20703
|
+
return path29.join(this.tempDir, `merge_${safeName}_${ts}_${rand}.db`);
|
|
20553
20704
|
}
|
|
20554
20705
|
/** Store a parsed file's temp DB and return a handle for subsequent requests. */
|
|
20555
20706
|
store(filename, tempDbPath) {
|
|
@@ -20608,23 +20759,23 @@ var MergeSessionCache = class {
|
|
|
20608
20759
|
import * as fs36 from "fs";
|
|
20609
20760
|
var SYSTEM_SENDER_ID = "SYSTEM";
|
|
20610
20761
|
var SYSTEM_MEMBER_NAME = "\u7CFB\u7EDF\u6D88\u606F";
|
|
20611
|
-
function
|
|
20762
|
+
function isRecord4(value) {
|
|
20612
20763
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20613
20764
|
}
|
|
20614
|
-
function
|
|
20765
|
+
function validateOptionalString2(value, field) {
|
|
20615
20766
|
return value !== void 0 && typeof value !== "string" ? `${field} must be a string` : null;
|
|
20616
20767
|
}
|
|
20617
20768
|
function validatePayload(payload, isNew) {
|
|
20618
|
-
if (!
|
|
20769
|
+
if (!isRecord4(payload)) return "payload must be an object";
|
|
20619
20770
|
const messages = payload.messages;
|
|
20620
20771
|
if (!Array.isArray(messages) || messages.length === 0) return "messages is required and must be a non-empty array";
|
|
20621
20772
|
if (payload.chatlab !== void 0) {
|
|
20622
|
-
if (!
|
|
20773
|
+
if (!isRecord4(payload.chatlab)) return "chatlab must be an object";
|
|
20623
20774
|
if (typeof payload.chatlab.version !== "string" || payload.chatlab.version.length === 0)
|
|
20624
20775
|
return "chatlab.version must be a string";
|
|
20625
20776
|
if (typeof payload.chatlab.exportedAt !== "number" || !Number.isFinite(payload.chatlab.exportedAt))
|
|
20626
20777
|
return "chatlab.exportedAt must be a number";
|
|
20627
|
-
const generatorError =
|
|
20778
|
+
const generatorError = validateOptionalString2(payload.chatlab.generator, "chatlab.generator");
|
|
20628
20779
|
if (generatorError) return generatorError;
|
|
20629
20780
|
}
|
|
20630
20781
|
if (isNew) {
|
|
@@ -20632,17 +20783,17 @@ function validatePayload(payload, isNew) {
|
|
|
20632
20783
|
if (!payload.meta) return "meta is required for new sessions";
|
|
20633
20784
|
}
|
|
20634
20785
|
if (payload.meta !== void 0) {
|
|
20635
|
-
if (!
|
|
20786
|
+
if (!isRecord4(payload.meta)) return "meta must be an object";
|
|
20636
20787
|
const metaFields = ["name", "platform", "type", "groupId", "groupAvatar", "ownerId"];
|
|
20637
20788
|
for (const field of metaFields) {
|
|
20638
|
-
const error =
|
|
20789
|
+
const error = validateOptionalString2(payload.meta[field], `meta.${field}`);
|
|
20639
20790
|
if (error) return error;
|
|
20640
20791
|
}
|
|
20641
20792
|
if (isNew && !payload.meta.name) return "meta.name is required";
|
|
20642
20793
|
if (isNew && !payload.meta.platform) return "meta.platform is required";
|
|
20643
20794
|
if (isNew && !payload.meta.type) return "meta.type is required";
|
|
20644
20795
|
}
|
|
20645
|
-
if (payload.options !== void 0 && !
|
|
20796
|
+
if (payload.options !== void 0 && !isRecord4(payload.options)) return "options must be an object";
|
|
20646
20797
|
const { metaUpdateMode, memberUpdateMode } = payload.options ?? {};
|
|
20647
20798
|
if (metaUpdateMode !== void 0 && metaUpdateMode !== "patch" && metaUpdateMode !== "none") {
|
|
20648
20799
|
return `options.metaUpdateMode must be 'patch' or 'none'`;
|
|
@@ -20654,18 +20805,18 @@ function validatePayload(payload, isNew) {
|
|
|
20654
20805
|
if (payload.members) {
|
|
20655
20806
|
for (let i = 0; i < payload.members.length; i++) {
|
|
20656
20807
|
const member = payload.members[i];
|
|
20657
|
-
if (!
|
|
20808
|
+
if (!isRecord4(member)) return `members[${i}] must be an object`;
|
|
20658
20809
|
if (typeof member.platformId !== "string" || member.platformId.length === 0)
|
|
20659
20810
|
return `members[${i}].platformId must be a string`;
|
|
20660
20811
|
for (const field of ["accountName", "groupNickname", "avatar"]) {
|
|
20661
|
-
const error =
|
|
20812
|
+
const error = validateOptionalString2(member[field], `members[${i}].${field}`);
|
|
20662
20813
|
if (error) return error;
|
|
20663
20814
|
}
|
|
20664
20815
|
if (member.roles !== void 0) {
|
|
20665
20816
|
if (!Array.isArray(member.roles)) return `members[${i}].roles must be an array`;
|
|
20666
20817
|
for (let j = 0; j < member.roles.length; j++) {
|
|
20667
20818
|
const role = member.roles[j];
|
|
20668
|
-
if (!
|
|
20819
|
+
if (!isRecord4(role) || typeof role.id !== "string" || role.id.length === 0)
|
|
20669
20820
|
return `members[${i}].roles[${j}].id must be a string`;
|
|
20670
20821
|
}
|
|
20671
20822
|
}
|
|
@@ -20673,13 +20824,13 @@ function validatePayload(payload, isNew) {
|
|
|
20673
20824
|
}
|
|
20674
20825
|
for (let i = 0; i < messages.length; i++) {
|
|
20675
20826
|
const msg = messages[i];
|
|
20676
|
-
if (!
|
|
20827
|
+
if (!isRecord4(msg)) return `messages[${i}] must be an object`;
|
|
20677
20828
|
if (typeof msg.sender !== "string" || msg.sender.length === 0) return `messages[${i}].sender must be a string`;
|
|
20678
20829
|
if (typeof msg.timestamp !== "number" || !Number.isFinite(msg.timestamp) || msg.timestamp <= 0)
|
|
20679
20830
|
return `messages[${i}].timestamp must be a positive number`;
|
|
20680
20831
|
if (typeof msg.type !== "number" || !Number.isFinite(msg.type)) return `messages[${i}].type must be a number`;
|
|
20681
20832
|
for (const field of ["accountName", "groupNickname"]) {
|
|
20682
|
-
const error =
|
|
20833
|
+
const error = validateOptionalString2(msg[field], `messages[${i}].${field}`);
|
|
20683
20834
|
if (error) return error;
|
|
20684
20835
|
}
|
|
20685
20836
|
if (msg.content !== void 0 && msg.content !== null && typeof msg.content !== "string")
|
|
@@ -21078,7 +21229,7 @@ export {
|
|
|
21078
21229
|
getChatLabTempScopeDir,
|
|
21079
21230
|
createChatLabTempDir,
|
|
21080
21231
|
removeChatLabTempDir,
|
|
21081
|
-
|
|
21232
|
+
validateChatLabFile,
|
|
21082
21233
|
detectFormat,
|
|
21083
21234
|
detectAllFormats,
|
|
21084
21235
|
getSupportedFormats,
|
|
@@ -21298,4 +21449,4 @@ export {
|
|
|
21298
21449
|
hashImportBody,
|
|
21299
21450
|
ImportIdempotencyCache
|
|
21300
21451
|
};
|
|
21301
|
-
//# sourceMappingURL=chunk-
|
|
21452
|
+
//# sourceMappingURL=chunk-KWJ5FZ74.mjs.map
|