chatlab-cli 0.32.0 → 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-3KUWKS53.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-27DWKA3L.mjs → chunk-HGV4MUIW.mjs} +4 -4
- package/dist/{chunk-LNU4RW5G.mjs → chunk-KWJ5FZ74.mjs} +1182 -1042
- package/dist/chunk-KWJ5FZ74.mjs.map +1 -0
- package/dist/{chunk-DV4U34MR.mjs → chunk-NHTOSDYI.mjs} +2 -2
- package/dist/{chunk-CANPJOIE.mjs → chunk-NSWFHMBZ.mjs} +112 -16
- package/dist/chunk-NSWFHMBZ.mjs.map +1 -0
- package/dist/{chunk-UVCH2ICD.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-3IGBPOJM.mjs → chunk-ZERQIKI4.mjs} +5 -4
- package/dist/chunk-ZERQIKI4.mjs.map +1 -0
- package/dist/{chunk-PZM3FHVZ.mjs → chunk-ZTMZ7S7T.mjs} +16 -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-YR26GF7X.mjs → http-Y2WC2UK4.mjs} +11 -11
- package/dist/{import-Q3DUACU4.mjs → import-4HF2UNQI.mjs} +7 -7
- package/dist/index.mjs +11 -11
- package/dist/{mcp-HTVZSGTI.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-HJL7YZZJ.mjs → src-R7MZLKXQ.mjs} +9 -9
- package/dist/{stream-import-MXETSEPW.mjs → stream-import-DX57ANUL.mjs} +7 -7
- package/dist/{worker-runtime-DZQDL2E2.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-BXfwfGam.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-Irxx0f6j.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-ByDU6ZS_.js → dist-cli-web/assets/RelationshipGalaxyCanvas-GEpsKHxV.js} +4 -4
- package/{dist-web/assets/RelationshipGalaxyThreeCanvas-DgAlyKjc.js → dist-cli-web/assets/RelationshipGalaxyThreeCanvas-DHOU66Gj.js} +2 -2
- package/{dist-web/assets/RenderTargetSystem-Cx5U5HAM.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-B3Fnp1WX.js → dist-cli-web/assets/WebGLRenderer-BjP_1do7.js} +2 -2
- package/{dist-web/assets/WebGPURenderer-DZ0X9N86.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-BqC9ELNO.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-CX9atL2g.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-C3KuMfIU.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-CUT9JdXT.js → dist-cli-web/assets/fetch-CtyVktRW.js} +3 -3
- package/{dist-web/assets/fetch-th_egarZ.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-Bm_U0hSr.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-a_fveCLR.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-3IGBPOJM.mjs.map +0 -1
- package/dist/chunk-CANPJOIE.mjs.map +0 -1
- package/dist/chunk-LNU4RW5G.mjs.map +0 -1
- package/dist/chunk-PZM3FHVZ.mjs.map +0 -1
- package/dist/chunk-YVNZ73O5.mjs.map +0 -1
- package/dist-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-DfQa9E2W.js +0 -110
- package/dist-web/assets/CanvasRenderer-Bvpsbxds.js +0 -1
- package/dist-web/assets/DataDirCleanupNotice-C0sCmNL2.js +0 -1
- package/dist-web/assets/EChartWordcloud-C5hLT1vS.js +0 -1
- package/dist-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-8s6XRB3S.js +0 -1
- package/dist-web/assets/MessageExportModal-tTI358HU.js +0 -1
- package/dist-web/assets/PageHeader-D5QQI9io.js +0 -1
- package/dist-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-CpNjFhpm.js +0 -1
- package/dist-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-DtCKfbaz.js +0 -1
- package/dist-web/assets/annual-summary-time-range-CvG74N2l.js +0 -1
- package/dist-web/assets/base-DdQRTliS.js +0 -1
- package/dist-web/assets/electron-ChQkD5ht.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-3Ifr5YJN.js +0 -1
- package/dist-web/assets/fetch-74o_kRZf.js +0 -1
- package/dist-web/assets/fetch-BKFznq4l.js +0 -1
- package/dist-web/assets/fetch-BPj6IlT-.js +0 -1
- package/dist-web/assets/fetch-CoYaXr5K.js +0 -1
- package/dist-web/assets/fetch-Dcg6Q6Ei.js +0 -1
- package/dist-web/assets/fetch-DxWfYzkB.js +0 -1
- package/dist-web/assets/index-B3riRaJA.js +0 -1
- package/dist-web/assets/index-BKFGuwJu.css +0 -1
- package/dist-web/assets/index-BRDdozcm.js +0 -173
- package/dist-web/assets/index-Bg-OiHMa.js +0 -1
- package/dist-web/assets/index-BhewGKey.js +0 -2
- package/dist-web/assets/index-BpeJ40uK.js +0 -1
- package/dist-web/assets/index-C6bwt1H4.js +0 -1
- package/dist-web/assets/index-CEJqRkis.js +0 -2
- package/dist-web/assets/index-CKLCaqdT.js +0 -1
- package/dist-web/assets/index-Ds5PiVyB.js +0 -64
- package/dist-web/assets/index-GXMxhYc9.js +0 -1
- package/dist-web/assets/index-JyB6Cr8r.js +0 -25
- package/dist-web/assets/index-UJl03IYE.css +0 -1
- package/dist-web/assets/index-tx-Oal36.js +0 -1
- package/dist-web/assets/people-page-header-DGkK0AMO.js +0 -1
- package/dist-web/assets/types-CZnACqaB.js +0 -1
- package/dist-web/assets/vendor-nuxt-ui-DMdbdSFy.js +0 -15
- package/dist-web/assets/vendor-reka-ui-8_x7iKfi.js +0 -15
- package/dist-web/assets/web-aZcioccr.js +0 -1
- /package/dist/{chat-command-3KUWKS53.mjs.map → chat-command-IHRNO7J4.mjs.map} +0 -0
- /package/dist/{chunk-3J7WTUEE.mjs.map → chunk-4V6UP7EW.mjs.map} +0 -0
- /package/dist/{chunk-27DWKA3L.mjs.map → chunk-HGV4MUIW.mjs.map} +0 -0
- /package/dist/{chunk-DV4U34MR.mjs.map → chunk-NHTOSDYI.mjs.map} +0 -0
- /package/dist/{chunk-UVCH2ICD.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-YR26GF7X.mjs.map → http-Y2WC2UK4.mjs.map} +0 -0
- /package/dist/{import-Q3DUACU4.mjs.map → import-4HF2UNQI.mjs.map} +0 -0
- /package/dist/{mcp-HTVZSGTI.mjs.map → mcp-DJPDRFTW.mjs.map} +0 -0
- /package/dist/{src-HJL7YZZJ.mjs.map → src-R7MZLKXQ.mjs.map} +0 -0
- /package/dist/{stream-import-MXETSEPW.mjs.map → stream-import-DX57ANUL.mjs.map} +0 -0
- /package/dist/{worker-runtime-DZQDL2E2.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) {
|
|
@@ -3561,7 +3455,9 @@ async function* parseChatLabJsonl(options2) {
|
|
|
3561
3455
|
platformId: parsed.platformId,
|
|
3562
3456
|
accountName: parsed.accountName,
|
|
3563
3457
|
groupNickname: parsed.groupNickname,
|
|
3564
|
-
|
|
3458
|
+
aliases: parsed.aliases,
|
|
3459
|
+
avatar: parsed.avatar,
|
|
3460
|
+
roles: parsed.roles
|
|
3565
3461
|
};
|
|
3566
3462
|
members.push(member);
|
|
3567
3463
|
memberMap.set(parsed.platformId, member);
|
|
@@ -3960,8 +3856,8 @@ function extractJsonObject2(content, key) {
|
|
|
3960
3856
|
return null;
|
|
3961
3857
|
}
|
|
3962
3858
|
function extractNameFromFilePath3(filePath) {
|
|
3963
|
-
const
|
|
3964
|
-
return
|
|
3859
|
+
const basename10 = path6.basename(filePath);
|
|
3860
|
+
return basename10.replace(/\.json$/i, "") || "\u672A\u77E5\u7FA4\u804A";
|
|
3965
3861
|
}
|
|
3966
3862
|
function isPlaceholderSenderId(id) {
|
|
3967
3863
|
return id === "0" || id === "\u672A\u77E5";
|
|
@@ -4561,8 +4457,8 @@ var { pick: pick4 } = import_Pick4.default;
|
|
|
4561
4457
|
var { streamValues: streamValues4 } = import_StreamValues4.default;
|
|
4562
4458
|
var { chain: chain4 } = import_stream_chain4.default;
|
|
4563
4459
|
function extractNameFromFilePath4(filePath) {
|
|
4564
|
-
const
|
|
4565
|
-
const name =
|
|
4460
|
+
const basename10 = path8.basename(filePath);
|
|
4461
|
+
const name = basename10.replace(/\.json$/i, "");
|
|
4566
4462
|
return name || "\u672A\u77E5\u804A\u5929";
|
|
4567
4463
|
}
|
|
4568
4464
|
var feature5 = {
|
|
@@ -5473,13 +5369,16 @@ var module_8 = {
|
|
|
5473
5369
|
var telegram_native_default = module_8;
|
|
5474
5370
|
|
|
5475
5371
|
// ../../packages/parser/src/formats/telegram-native-single.ts
|
|
5476
|
-
var
|
|
5372
|
+
var import_Assembler = __toESM(require_Assembler(), 1);
|
|
5477
5373
|
var import_stream_json7 = __toESM(require_stream_json(), 1);
|
|
5478
|
-
var import_StreamValues7 = __toESM(require_StreamValues(), 1);
|
|
5479
5374
|
import * as fs12 from "fs";
|
|
5480
|
-
|
|
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
|
|
5481
5381
|
var { parser: parser8 } = import_stream_json7.default;
|
|
5482
|
-
var { streamValues: streamValues7 } = import_StreamValues7.default;
|
|
5483
5382
|
var feature9 = {
|
|
5484
5383
|
id: "telegram-native-single",
|
|
5485
5384
|
name: "Telegram \u5355\u804A\u5929\u5BFC\u51FA (JSON)",
|
|
@@ -5487,98 +5386,97 @@ var feature9 = {
|
|
|
5487
5386
|
priority: 23,
|
|
5488
5387
|
extensions: [".json"],
|
|
5489
5388
|
signatures: {
|
|
5490
|
-
|
|
5491
|
-
// 这与全量导出(以 "about" 开头)有效区分,避免嵌套字段误匹配
|
|
5492
|
-
head: [/^\s*\{\s*\r?\n\s*"name"\s*:/],
|
|
5389
|
+
head: TELEGRAM_SINGLE_HEAD_SIGNATURES,
|
|
5493
5390
|
requiredFields: ["messages"],
|
|
5494
|
-
fieldPatterns: {
|
|
5495
|
-
// Telegram 特有的聊天类型值,精确区分
|
|
5496
|
-
telegramChatType: /"type"\s*:\s*"(personal_chat|bot_chat|private_group|private_supergroup|public_group|public_supergroup|public_channel|private_channel|saved_messages)"/
|
|
5497
|
-
}
|
|
5391
|
+
fieldPatterns: { telegramChatType: TELEGRAM_SINGLE_CHAT_TYPE_PATTERN }
|
|
5498
5392
|
}
|
|
5499
5393
|
};
|
|
5500
5394
|
async function* parseTelegramSingle(options2) {
|
|
5501
5395
|
const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
|
|
5502
5396
|
const totalBytes = getFileSize(filePath);
|
|
5503
|
-
let messagesProcessed = 0;
|
|
5504
5397
|
const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
|
|
5505
5398
|
yield { type: "progress", data: initialProgress };
|
|
5506
5399
|
onProgress?.(initialProgress);
|
|
5507
|
-
onLog?.("info",
|
|
5508
|
-
|
|
5509
|
-
const readStream = fs12.createReadStream(filePath, { encoding: "utf-8" });
|
|
5510
|
-
const pipeline2 = chain7([readStream, parser8(), streamValues7()]);
|
|
5511
|
-
let found = false;
|
|
5512
|
-
pipeline2.on("data", ({ value }) => {
|
|
5513
|
-
if (!found) {
|
|
5514
|
-
found = true;
|
|
5515
|
-
resolve4(value);
|
|
5516
|
-
}
|
|
5517
|
-
});
|
|
5518
|
-
pipeline2.on("end", () => {
|
|
5519
|
-
if (!found) resolve4(null);
|
|
5520
|
-
});
|
|
5521
|
-
pipeline2.on("error", reject);
|
|
5522
|
-
});
|
|
5523
|
-
if (!chatData) {
|
|
5524
|
-
onLog?.("error", "\u65E0\u6CD5\u89E3\u6790 Telegram \u5355\u804A\u5929\u6587\u4EF6");
|
|
5525
|
-
yield { type: "error", data: new Error("\u65E0\u6CD5\u89E3\u6790 Telegram \u5355\u804A\u5929\u6587\u4EF6") };
|
|
5526
|
-
return;
|
|
5527
|
-
}
|
|
5528
|
-
onLog?.("info", `\u804A\u5929: "${chatData.name}", \u7C7B\u578B: ${chatData.type}, \u6D88\u606F\u6570: ${chatData.messages?.length || 0}`);
|
|
5529
|
-
const chatType = mapChatType(chatData.type);
|
|
5530
|
-
const meta = {
|
|
5531
|
-
name: chatData.name || `Telegram Chat ${chatData.id}`,
|
|
5532
|
-
platform: KNOWN_PLATFORMS.TELEGRAM,
|
|
5533
|
-
type: chatType,
|
|
5534
|
-
groupId: chatType === "group" /* GROUP */ ? String(chatData.id) : void 0
|
|
5535
|
-
};
|
|
5536
|
-
yield { type: "meta", data: meta };
|
|
5400
|
+
onLog?.("info", `Starting Telegram single-chat JSON parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`);
|
|
5401
|
+
let messagesProcessed = 0;
|
|
5537
5402
|
const memberMap = /* @__PURE__ */ new Map();
|
|
5538
5403
|
const messageBatch = [];
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
let
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
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
|
+
}
|
|
5555
5471
|
}
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
const parsedMsg = {
|
|
5559
|
-
platformMessageId: String(msg.id),
|
|
5560
|
-
senderPlatformId,
|
|
5561
|
-
senderAccountName: senderName,
|
|
5562
|
-
timestamp,
|
|
5563
|
-
type: detectMessageType(msg),
|
|
5564
|
-
content: buildContent(msg),
|
|
5565
|
-
replyToMessageId: msg.reply_to_message_id ? String(msg.reply_to_message_id) : void 0
|
|
5566
|
-
};
|
|
5567
|
-
messageBatch.push(parsedMsg);
|
|
5568
|
-
messagesProcessed++;
|
|
5569
|
-
if (messageBatch.length >= batchSize) {
|
|
5570
|
-
yield { type: "messages", data: [...messageBatch] };
|
|
5571
|
-
messageBatch.length = 0;
|
|
5572
|
-
const progress = createProgress(
|
|
5573
|
-
"parsing",
|
|
5574
|
-
0,
|
|
5575
|
-
totalBytes,
|
|
5576
|
-
messagesProcessed,
|
|
5577
|
-
`\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`
|
|
5578
|
-
);
|
|
5579
|
-
yield { type: "progress", data: progress };
|
|
5580
|
-
onProgress?.(progress);
|
|
5472
|
+
if (!meta || messagesArrayDepth === null || messageAssembler) {
|
|
5473
|
+
throw new Error("Invalid Telegram single-chat JSON export");
|
|
5581
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;
|
|
5582
5480
|
}
|
|
5583
5481
|
yield { type: "members", data: Array.from(memberMap.values()) };
|
|
5584
5482
|
if (messageBatch.length > 0) {
|
|
@@ -5587,77 +5485,63 @@ async function* parseTelegramSingle(options2) {
|
|
|
5587
5485
|
const doneProgress = createProgress("done", totalBytes, totalBytes, messagesProcessed, "");
|
|
5588
5486
|
yield { type: "progress", data: doneProgress };
|
|
5589
5487
|
onProgress?.(doneProgress);
|
|
5590
|
-
onLog?.(
|
|
5488
|
+
onLog?.(
|
|
5489
|
+
"info",
|
|
5490
|
+
`Telegram single-chat JSON parsing completed: ${messagesProcessed} messages, ${memberMap.size} members`
|
|
5491
|
+
);
|
|
5591
5492
|
yield {
|
|
5592
5493
|
type: "done",
|
|
5593
5494
|
data: { messageCount: messagesProcessed, memberCount: memberMap.size }
|
|
5594
5495
|
};
|
|
5595
5496
|
}
|
|
5596
|
-
|
|
5597
|
-
|
|
5598
|
-
|
|
5599
|
-
|
|
5600
|
-
|
|
5601
|
-
|
|
5602
|
-
|
|
5603
|
-
|
|
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 };
|
|
5604
5521
|
var telegram_native_single_default = module_9;
|
|
5605
5522
|
|
|
5606
5523
|
// ../../packages/parser/src/formats/whatsapp-native-txt.ts
|
|
5607
5524
|
import * as fs13 from "fs";
|
|
5608
|
-
import * as path9 from "path";
|
|
5609
5525
|
import * as readline3 from "readline";
|
|
5610
|
-
|
|
5611
|
-
|
|
5612
|
-
|
|
5613
|
-
|
|
5614
|
-
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
}
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
}
|
|
5626
|
-
var feature10 = {
|
|
5627
|
-
id: "whatsapp-native-txt",
|
|
5628
|
-
name: "WhatsApp \u5B98\u65B9\u5BFC\u51FA (TXT)",
|
|
5629
|
-
platform: KNOWN_PLATFORMS.WHATSAPP,
|
|
5630
|
-
priority: 25,
|
|
5631
|
-
extensions: [".txt"],
|
|
5632
|
-
signatures: {
|
|
5633
|
-
// WhatsApp 导出文件的特征(中文/英文)
|
|
5634
|
-
// 注意:仅保留 WhatsApp 独有的特征,避免误匹配其他 TXT 格式(如 LINE)
|
|
5635
|
-
head: [
|
|
5636
|
-
/消息和通话已进行端到端加密/,
|
|
5637
|
-
// 简体中文加密提示(WhatsApp 独有)
|
|
5638
|
-
/訊息與通話已受端對端加密保護/,
|
|
5639
|
-
// 繁体中文加密提示(WhatsApp 独有)
|
|
5640
|
-
/Messages and calls are end-to-end encrypted/i,
|
|
5641
|
-
// 英文加密提示(WhatsApp 独有)
|
|
5642
|
-
/你发送给自己的消息已进行端到端加密/,
|
|
5643
|
-
// 简体中文自己对话提示(WhatsApp 独有)
|
|
5644
|
-
/\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,
|
|
5645
|
-
// 消息行格式特征(无方括号,含 " - " 分隔符,WhatsApp 独有)
|
|
5646
|
-
/\[\d{1,4}\/\d{1,2}\/\d{1,4}[\s,].*\d{1,2}:\d{2}(?::\d{2})?.*\] /
|
|
5647
|
-
// 消息行格式特征(方括号 + 含日期和时间的时间戳,兼容各地区变体)
|
|
5648
|
-
],
|
|
5649
|
-
// 文件名特征:与xxx的 WhatsApp 聊天.txt
|
|
5650
|
-
filename: [/^与.+的\s*WhatsApp\s*聊天\.txt$/i, /^與.+的\s*WhatsApp\s*對話\.txt$/i, /WhatsApp/i]
|
|
5651
|
-
}
|
|
5652
|
-
};
|
|
5653
|
-
function cleanLine(line) {
|
|
5654
|
-
return line.replace(/^(?:\uFEFF|\u200E|\u200F|\u200B|\u200C|\u200D|\u2060)+/, "").trim();
|
|
5655
|
-
}
|
|
5656
|
-
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;
|
|
5657
5542
|
var MESSAGE_LINE_REGEX_V2 = /^\[([^\]]+)\] (.+)$/;
|
|
5658
5543
|
var SENDER_CONTENT_REGEX = /^(.+?):[\s\u200E]*(.*)$/;
|
|
5659
5544
|
var SYSTEM_MESSAGE_PATTERNS = [
|
|
5660
|
-
// 简体中文系统消息
|
|
5661
5545
|
/消息和通话已进行端到端加密/,
|
|
5662
5546
|
/创建了此群组/,
|
|
5663
5547
|
/加入群组/,
|
|
@@ -5669,7 +5553,6 @@ var SYSTEM_MESSAGE_PATTERNS = [
|
|
|
5669
5553
|
/这条消息已删除/,
|
|
5670
5554
|
/限时消息功能/,
|
|
5671
5555
|
/正在等待此消息/,
|
|
5672
|
-
// 繁体中文系统消息
|
|
5673
5556
|
/訊息與通話已受端對端加密保護/,
|
|
5674
5557
|
/建立了此群組/,
|
|
5675
5558
|
/加入了群組/,
|
|
@@ -5679,7 +5562,6 @@ var SYSTEM_MESSAGE_PATTERNS = [
|
|
|
5679
5562
|
/已變更本群組/,
|
|
5680
5563
|
/此訊息已刪除/,
|
|
5681
5564
|
/限時訊息/,
|
|
5682
|
-
// 英文系统消息
|
|
5683
5565
|
/Messages and calls are end-to-end encrypted/i,
|
|
5684
5566
|
/created this group/i,
|
|
5685
5567
|
/joined the group/i,
|
|
@@ -5690,21 +5572,6 @@ var SYSTEM_MESSAGE_PATTERNS = [
|
|
|
5690
5572
|
/This message was deleted/i,
|
|
5691
5573
|
/disappearing messages/i
|
|
5692
5574
|
];
|
|
5693
|
-
function isSystemMessage(content) {
|
|
5694
|
-
return SYSTEM_MESSAGE_PATTERNS.some((pattern) => pattern.test(content));
|
|
5695
|
-
}
|
|
5696
|
-
function detectMessageType2(content) {
|
|
5697
|
-
const trimmed = content.trim();
|
|
5698
|
-
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")
|
|
5699
|
-
return 1 /* IMAGE */;
|
|
5700
|
-
if (trimmed.includes("<\u5DF2\u9644\u52A0:") || trimmed.includes("<\u9644\u4EF6:") || trimmed.includes("<\u5DF2\u9644\u52A0\uFF1A"))
|
|
5701
|
-
return 4 /* FILE */;
|
|
5702
|
-
if (trimmed === "\u8CBC\u5716\u5DF2\u5FFD\u7565" || trimmed === "\u8CBC\u5716\u5DF2\u7565\u53BB") return 5 /* EMOJI */;
|
|
5703
|
-
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"))
|
|
5704
|
-
return 81 /* RECALL */;
|
|
5705
|
-
if (isSystemMessage(trimmed)) return 80 /* SYSTEM */;
|
|
5706
|
-
return 0 /* TEXT */;
|
|
5707
|
-
}
|
|
5708
5575
|
var AMPM_MARKERS = [
|
|
5709
5576
|
[/\bPM\b/i, true],
|
|
5710
5577
|
[/\bP\.M\.(?!\w)/i, true],
|
|
@@ -5717,191 +5584,226 @@ var AMPM_MARKERS = [
|
|
|
5717
5584
|
[/午前/, false],
|
|
5718
5585
|
[/오전/, false]
|
|
5719
5586
|
];
|
|
5720
|
-
|
|
5721
|
-
|
|
5722
|
-
|
|
5723
|
-
|
|
5724
|
-
|
|
5725
|
-
|
|
5726
|
-
|
|
5727
|
-
|
|
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;
|
|
5728
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 });
|
|
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]] };
|
|
5729
5660
|
}
|
|
5730
|
-
|
|
5731
|
-
|
|
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;
|
|
5710
|
+
}
|
|
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})?)$/);
|
|
5732
5713
|
if (!match) return null;
|
|
5733
|
-
const
|
|
5734
|
-
const
|
|
5735
|
-
|
|
5736
|
-
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;
|
|
5737
5719
|
if (dateParts[0] > 31) {
|
|
5738
|
-
|
|
5739
|
-
month = dateParts
|
|
5740
|
-
day = dateParts[2];
|
|
5720
|
+
;
|
|
5721
|
+
[year, month, day] = dateParts;
|
|
5741
5722
|
} else if (dateParts[2] > 31) {
|
|
5742
|
-
|
|
5743
|
-
month = dateParts
|
|
5744
|
-
year = dateParts[2];
|
|
5723
|
+
;
|
|
5724
|
+
[day, month, year] = dateParts;
|
|
5745
5725
|
} else {
|
|
5746
|
-
|
|
5747
|
-
day = dateParts
|
|
5726
|
+
;
|
|
5727
|
+
[month, day] = dateParts;
|
|
5748
5728
|
year = 2e3 + dateParts[2];
|
|
5749
5729
|
}
|
|
5750
5730
|
let hour = timeParts[0];
|
|
5751
5731
|
const minute = timeParts[1];
|
|
5752
5732
|
const second = timeParts[2] ?? 0;
|
|
5753
|
-
if (
|
|
5754
|
-
if (
|
|
5733
|
+
if (isPm === true && hour !== 12) hour += 12;
|
|
5734
|
+
if (isPm === false && hour === 12) hour = 0;
|
|
5755
5735
|
const date = new Date(year, month - 1, day, hour, minute, second);
|
|
5756
|
-
const
|
|
5757
|
-
return isNaN(
|
|
5758
|
-
}
|
|
5759
|
-
function parseV1Timestamp(timeStr) {
|
|
5760
|
-
return parseFlexibleV2Timestamp(timeStr) ?? 0;
|
|
5736
|
+
const timestamp = Math.floor(date.getTime() / 1e3);
|
|
5737
|
+
return Number.isNaN(timestamp) ? null : timestamp;
|
|
5761
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
|
+
};
|
|
5762
5752
|
async function* parseWhatsApp(options2) {
|
|
5763
5753
|
const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
|
|
5764
5754
|
const totalBytes = getFileSize(filePath);
|
|
5755
|
+
const accumulator = new WhatsAppTextAccumulator(filePath);
|
|
5765
5756
|
let bytesRead = 0;
|
|
5766
|
-
let
|
|
5767
|
-
let skippedLines = 0;
|
|
5757
|
+
let lastReportedMessages = 0;
|
|
5768
5758
|
const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
|
|
5769
5759
|
yield { type: "progress", data: initialProgress };
|
|
5770
5760
|
onProgress?.(initialProgress);
|
|
5771
|
-
onLog?.("info",
|
|
5772
|
-
const fileChatName = extractNameFromFilePath5(filePath);
|
|
5773
|
-
const memberMap = /* @__PURE__ */ new Map();
|
|
5774
|
-
const messages = [];
|
|
5775
|
-
let currentMessage = null;
|
|
5776
|
-
const saveCurrentMessage = () => {
|
|
5777
|
-
if (currentMessage) {
|
|
5778
|
-
const content = currentMessage.contentLines.join("\n").trim();
|
|
5779
|
-
const type = detectMessageType2(content);
|
|
5780
|
-
const senderPlatformId = currentMessage.sender || "system";
|
|
5781
|
-
const senderName = currentMessage.sender || "\u7CFB\u7EDF\u6D88\u606F";
|
|
5782
|
-
messages.push({
|
|
5783
|
-
senderPlatformId,
|
|
5784
|
-
senderAccountName: senderName,
|
|
5785
|
-
timestamp: currentMessage.timestamp,
|
|
5786
|
-
type,
|
|
5787
|
-
content: content || null
|
|
5788
|
-
});
|
|
5789
|
-
if (currentMessage.sender) {
|
|
5790
|
-
memberMap.set(senderPlatformId, {
|
|
5791
|
-
platformId: senderPlatformId,
|
|
5792
|
-
nickname: senderName
|
|
5793
|
-
});
|
|
5794
|
-
}
|
|
5795
|
-
messagesProcessed++;
|
|
5796
|
-
}
|
|
5797
|
-
};
|
|
5761
|
+
onLog?.("info", `Starting WhatsApp TXT parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`);
|
|
5798
5762
|
const fileStream = fs13.createReadStream(filePath, { encoding: "utf-8" });
|
|
5799
|
-
const
|
|
5800
|
-
input: fileStream,
|
|
5801
|
-
crlfDelay: Infinity
|
|
5802
|
-
});
|
|
5763
|
+
const lines = readline3.createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
5803
5764
|
fileStream.on("data", (chunk) => {
|
|
5804
5765
|
bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
|
|
5805
5766
|
});
|
|
5806
|
-
for await (const line of
|
|
5807
|
-
|
|
5808
|
-
|
|
5809
|
-
|
|
5810
|
-
|
|
5811
|
-
|
|
5812
|
-
|
|
5813
|
-
|
|
5814
|
-
|
|
5815
|
-
|
|
5816
|
-
|
|
5817
|
-
|
|
5818
|
-
|
|
5819
|
-
} else {
|
|
5820
|
-
currentMessage = {
|
|
5821
|
-
timestamp: parseV1Timestamp(v1Match[1]),
|
|
5822
|
-
sender: null,
|
|
5823
|
-
contentLines: [restContent]
|
|
5824
|
-
};
|
|
5825
|
-
}
|
|
5826
|
-
if (messagesProcessed % 500 === 0) {
|
|
5827
|
-
onProgress?.(
|
|
5828
|
-
createProgress("parsing", bytesRead, totalBytes, messagesProcessed, `\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`)
|
|
5829
|
-
);
|
|
5830
|
-
}
|
|
5831
|
-
continue;
|
|
5832
|
-
}
|
|
5833
|
-
const v2Match = cleanedLine.match(MESSAGE_LINE_REGEX_V2);
|
|
5834
|
-
if (v2Match) {
|
|
5835
|
-
const timestamp = parseFlexibleV2Timestamp(v2Match[1]);
|
|
5836
|
-
if (timestamp !== null) {
|
|
5837
|
-
saveCurrentMessage();
|
|
5838
|
-
const restContent = v2Match[2];
|
|
5839
|
-
const senderMatch = restContent.match(SENDER_CONTENT_REGEX);
|
|
5840
|
-
if (senderMatch && !isSystemMessage(restContent)) {
|
|
5841
|
-
currentMessage = {
|
|
5842
|
-
timestamp,
|
|
5843
|
-
sender: senderMatch[1].trim(),
|
|
5844
|
-
contentLines: [senderMatch[2]]
|
|
5845
|
-
};
|
|
5846
|
-
} else {
|
|
5847
|
-
currentMessage = {
|
|
5848
|
-
timestamp,
|
|
5849
|
-
sender: null,
|
|
5850
|
-
contentLines: [restContent]
|
|
5851
|
-
};
|
|
5852
|
-
}
|
|
5853
|
-
if (messagesProcessed % 500 === 0) {
|
|
5854
|
-
onProgress?.(
|
|
5855
|
-
createProgress("parsing", bytesRead, totalBytes, messagesProcessed, `\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`)
|
|
5856
|
-
);
|
|
5857
|
-
}
|
|
5858
|
-
continue;
|
|
5859
|
-
}
|
|
5860
|
-
}
|
|
5861
|
-
if (currentMessage && cleanedLine) {
|
|
5862
|
-
currentMessage.contentLines.push(cleanedLine);
|
|
5863
|
-
} else if (cleanedLine) {
|
|
5864
|
-
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
|
+
);
|
|
5865
5780
|
}
|
|
5866
5781
|
}
|
|
5867
|
-
|
|
5868
|
-
|
|
5869
|
-
|
|
5870
|
-
|
|
5871
|
-
|
|
5872
|
-
const meta = {
|
|
5873
|
-
name: chatName,
|
|
5874
|
-
platform: KNOWN_PLATFORMS.WHATSAPP,
|
|
5875
|
-
type: chatType
|
|
5876
|
-
};
|
|
5877
|
-
yield { type: "meta", data: meta };
|
|
5878
|
-
const members = Array.from(memberMap.values()).map((m) => ({
|
|
5879
|
-
platformId: m.platformId,
|
|
5880
|
-
accountName: m.nickname
|
|
5881
|
-
}));
|
|
5882
|
-
yield { type: "members", data: members };
|
|
5883
|
-
for (let i = 0; i < messages.length; i += batchSize) {
|
|
5884
|
-
const batch = messages.slice(i, i + batchSize);
|
|
5885
|
-
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) };
|
|
5886
5787
|
}
|
|
5887
|
-
const doneProgress = createProgress("done", totalBytes, totalBytes,
|
|
5788
|
+
const doneProgress = createProgress("done", totalBytes, totalBytes, result.messages.length, "");
|
|
5888
5789
|
yield { type: "progress", data: doneProgress };
|
|
5889
5790
|
onProgress?.(doneProgress);
|
|
5890
5791
|
const typeCounts = /* @__PURE__ */ new Map();
|
|
5891
|
-
for (const
|
|
5892
|
-
typeCounts.set(
|
|
5792
|
+
for (const message of result.messages) {
|
|
5793
|
+
typeCounts.set(message.type, (typeCounts.get(message.type) ?? 0) + 1);
|
|
5893
5794
|
}
|
|
5894
|
-
onLog?.("info", `\u89E3\u6790\u5B8C\u6210: ${messagesProcessed} \u6761\u6D88\u606F, ${memberMap.size} \u4E2A\u6210\u5458, \u7C7B\u578B: ${chatType}`);
|
|
5895
5795
|
onLog?.(
|
|
5896
5796
|
"info",
|
|
5897
|
-
|
|
5797
|
+
`WhatsApp TXT parsing completed: ${result.messages.length} messages, ${result.members.length} members, type ${result.meta.type}`
|
|
5898
5798
|
);
|
|
5899
|
-
|
|
5900
|
-
|
|
5901
|
-
|
|
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`);
|
|
5902
5804
|
yield {
|
|
5903
5805
|
type: "done",
|
|
5904
|
-
data: { messageCount:
|
|
5806
|
+
data: { messageCount: result.messages.length, memberCount: result.members.length }
|
|
5905
5807
|
};
|
|
5906
5808
|
}
|
|
5907
5809
|
var parser_9 = {
|
|
@@ -5911,32 +5813,110 @@ var parser_9 = {
|
|
|
5911
5813
|
var module_10 = {
|
|
5912
5814
|
feature: feature10,
|
|
5913
5815
|
parser: parser_9
|
|
5914
|
-
// TXT 格式不需要预处理器
|
|
5915
5816
|
};
|
|
5916
5817
|
var whatsapp_native_txt_default = module_10;
|
|
5917
5818
|
|
|
5918
5819
|
// ../../packages/parser/src/formats/qq-native-txt.ts
|
|
5919
5820
|
import * as fs14 from "fs";
|
|
5920
|
-
import * as path10 from "path";
|
|
5921
5821
|
import * as readline4 from "readline";
|
|
5922
|
-
|
|
5923
|
-
|
|
5924
|
-
|
|
5925
|
-
return name || "\u672A\u77E5\u7FA4\u804A";
|
|
5926
|
-
}
|
|
5927
|
-
var feature11 = {
|
|
5928
|
-
id: "qq-native-txt",
|
|
5929
|
-
name: "QQ \u5B98\u65B9\u5BFC\u51FA (TXT)",
|
|
5930
|
-
platform: KNOWN_PLATFORMS.QQ,
|
|
5931
|
-
priority: 30,
|
|
5932
|
-
extensions: [".txt"],
|
|
5933
|
-
signatures: {
|
|
5934
|
-
// 支持群聊导出和多人聊天(讨论组)导出
|
|
5935
|
-
head: [/消息记录(此消息记录为文本格式/, /消息对象:/, /多人聊天/]
|
|
5936
|
-
}
|
|
5937
|
-
};
|
|
5822
|
+
|
|
5823
|
+
// ../../packages/parser/src/browser/qq.ts
|
|
5824
|
+
var QQ_HEAD_SIGNATURES = [/消息记录(此消息记录为文本格式/, /消息对象:/, /多人聊天/];
|
|
5938
5825
|
var MESSAGE_HEADER_REGEX = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}) (.+?)(?:\(([^)]+)\)|<([^>]+)>)?$/;
|
|
5939
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
|
+
}
|
|
5940
5920
|
function detectMessageType3(content) {
|
|
5941
5921
|
const trimmed = content.trim();
|
|
5942
5922
|
if (trimmed === "[\u56FE\u7247]") return 1 /* IMAGE */;
|
|
@@ -5949,7 +5929,9 @@ function detectMessageType3(content) {
|
|
|
5949
5929
|
if (trimmed === "[\u7EA2\u5305]" || trimmed.includes("\u53D1\u51FA\u4E86\u7EA2\u5305")) return 20 /* RED_PACKET */;
|
|
5950
5930
|
if (trimmed === "[\u8F6C\u8D26]" || trimmed.includes("\u5411\u4F60\u8F6C\u8D26")) return 21 /* TRANSFER */;
|
|
5951
5931
|
if (trimmed.includes("\u62CD\u4E86\u62CD") || trimmed === "[\u62CD\u4E00\u62CD]") return 22 /* POKE */;
|
|
5952
|
-
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
|
+
}
|
|
5953
5935
|
if (trimmed === "[\u5206\u4EAB]" || trimmed === "[\u97F3\u4E50]" || trimmed === "[\u5C0F\u7A0B\u5E8F]") return 24 /* SHARE */;
|
|
5954
5936
|
if (trimmed.startsWith("[\u56DE\u590D]")) return 25 /* REPLY */;
|
|
5955
5937
|
if (trimmed === "[\u8F6C\u53D1]" || trimmed === "[\u804A\u5929\u8BB0\u5F55]") return 26 /* FORWARD */;
|
|
@@ -5960,150 +5942,64 @@ function detectMessageType3(content) {
|
|
|
5960
5942
|
if (trimmed.startsWith("[") && trimmed.endsWith("]")) return 99 /* OTHER */;
|
|
5961
5943
|
return 0 /* TEXT */;
|
|
5962
5944
|
}
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
5966
|
-
|
|
5967
|
-
|
|
5968
|
-
|
|
5969
|
-
|
|
5970
|
-
|
|
5971
|
-
|
|
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) {
|
|
5972
5956
|
const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
|
|
5973
5957
|
const totalBytes = getFileSize(filePath);
|
|
5974
5958
|
let bytesRead = 0;
|
|
5975
|
-
let messagesProcessed = 0;
|
|
5976
|
-
let skippedLines = 0;
|
|
5977
5959
|
const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
|
|
5978
5960
|
yield { type: "progress", data: initialProgress };
|
|
5979
5961
|
onProgress?.(initialProgress);
|
|
5980
|
-
onLog?.("info",
|
|
5981
|
-
|
|
5982
|
-
const memberMap = /* @__PURE__ */ new Map();
|
|
5983
|
-
const messages = [];
|
|
5984
|
-
let currentMessage = null;
|
|
5985
|
-
const saveCurrentMessage = () => {
|
|
5986
|
-
if (currentMessage) {
|
|
5987
|
-
const content = currentMessage.contentLines.join("\n").trim();
|
|
5988
|
-
const type = detectMessageType3(content);
|
|
5989
|
-
messages.push({
|
|
5990
|
-
senderPlatformId: currentMessage.platformId,
|
|
5991
|
-
senderAccountName: currentMessage.nickname,
|
|
5992
|
-
// QQ TXT 格式只有一个昵称,作为账号名称追踪历史
|
|
5993
|
-
// 不设置 senderGroupNickname,避免同一昵称被重复追踪
|
|
5994
|
-
timestamp: currentMessage.timestamp,
|
|
5995
|
-
type,
|
|
5996
|
-
content: content || null
|
|
5997
|
-
});
|
|
5998
|
-
memberMap.set(currentMessage.platformId, {
|
|
5999
|
-
platformId: currentMessage.platformId,
|
|
6000
|
-
nickname: currentMessage.nickname
|
|
6001
|
-
});
|
|
6002
|
-
messagesProcessed++;
|
|
6003
|
-
}
|
|
6004
|
-
};
|
|
5962
|
+
onLog?.("info", `Starting QQ TXT parsing (${(totalBytes / 1024 / 1024).toFixed(2)} MB)`);
|
|
5963
|
+
const accumulator = new QqTextAccumulator(filePath);
|
|
6005
5964
|
const fileStream = fs14.createReadStream(filePath, { encoding: "utf-8" });
|
|
6006
|
-
const
|
|
6007
|
-
input: fileStream,
|
|
6008
|
-
crlfDelay: Infinity
|
|
6009
|
-
});
|
|
5965
|
+
const lines = readline4.createInterface({ input: fileStream, crlfDelay: Infinity });
|
|
6010
5966
|
fileStream.on("data", (chunk) => {
|
|
6011
5967
|
bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
|
|
6012
5968
|
});
|
|
6013
|
-
|
|
6014
|
-
|
|
6015
|
-
|
|
6016
|
-
|
|
6017
|
-
|
|
6018
|
-
|
|
6019
|
-
|
|
6020
|
-
|
|
6021
|
-
|
|
6022
|
-
|
|
6023
|
-
|
|
6024
|
-
|
|
6025
|
-
|
|
6026
|
-
|
|
6027
|
-
|
|
6028
|
-
|
|
6029
|
-
nickname = previousNickname;
|
|
6030
|
-
}
|
|
6031
|
-
} else if (headerMatch[3] || headerMatch[4]) {
|
|
6032
|
-
lastValidNickname.set(platformId, nickname);
|
|
6033
|
-
}
|
|
6034
|
-
currentMessage = {
|
|
6035
|
-
timestamp: parseLocalTime(timeStr),
|
|
6036
|
-
platformId,
|
|
6037
|
-
nickname,
|
|
6038
|
-
contentLines: []
|
|
6039
|
-
};
|
|
6040
|
-
if (messagesProcessed % 1e3 === 0) {
|
|
6041
|
-
const progress = createProgress(
|
|
6042
|
-
"parsing",
|
|
6043
|
-
bytesRead,
|
|
6044
|
-
totalBytes,
|
|
6045
|
-
messagesProcessed,
|
|
6046
|
-
`\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`
|
|
6047
|
-
);
|
|
6048
|
-
onProgress?.(progress);
|
|
6049
|
-
}
|
|
6050
|
-
continue;
|
|
6051
|
-
}
|
|
6052
|
-
if (currentMessage) {
|
|
6053
|
-
if (line.startsWith("=====")) continue;
|
|
6054
|
-
if (line.startsWith("\u6D88\u606F\u8BB0\u5F55") || line.startsWith("\u6D88\u606F\u5206\u7EC4")) continue;
|
|
6055
|
-
currentMessage.contentLines.push(line);
|
|
6056
|
-
} else {
|
|
6057
|
-
const trimmed = line.trim();
|
|
6058
|
-
if (trimmed && !trimmed.startsWith("=====") && !trimmed.startsWith("\u6D88\u606F\u8BB0\u5F55") && !trimmed.startsWith("\u6D88\u606F\u5206\u7EC4")) {
|
|
6059
|
-
skippedLines++;
|
|
6060
|
-
}
|
|
6061
|
-
}
|
|
6062
|
-
}
|
|
6063
|
-
saveCurrentMessage();
|
|
6064
|
-
const meta = {
|
|
6065
|
-
name: groupName === "\u672A\u77E5\u7FA4\u804A" ? extractNameFromFilePath6(filePath) : groupName,
|
|
6066
|
-
platform: KNOWN_PLATFORMS.QQ,
|
|
6067
|
-
type: "group" /* GROUP */
|
|
6068
|
-
};
|
|
6069
|
-
yield { type: "meta", data: meta };
|
|
6070
|
-
const members = Array.from(memberMap.values()).map((m) => ({
|
|
6071
|
-
platformId: m.platformId,
|
|
6072
|
-
accountName: m.nickname
|
|
6073
|
-
// QQ TXT 格式只有昵称,作为账号名称
|
|
6074
|
-
// 不设置 groupNickname,避免同一昵称被重复追踪
|
|
6075
|
-
}));
|
|
6076
|
-
yield { type: "members", data: members };
|
|
6077
|
-
for (let i = 0; i < messages.length; i += batchSize) {
|
|
6078
|
-
const batch = messages.slice(i, i + batchSize);
|
|
6079
|
-
yield { type: "messages", data: batch };
|
|
6080
|
-
}
|
|
6081
|
-
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, "");
|
|
6082
5985
|
yield { type: "progress", data: doneProgress };
|
|
6083
5986
|
onProgress?.(doneProgress);
|
|
6084
|
-
onLog?.("info",
|
|
6085
|
-
if (skippedLines > 0) {
|
|
6086
|
-
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`);
|
|
6087
5990
|
}
|
|
6088
5991
|
yield {
|
|
6089
5992
|
type: "done",
|
|
6090
|
-
data: { messageCount:
|
|
5993
|
+
data: { messageCount: result.messages.length, memberCount: result.members.length }
|
|
6091
5994
|
};
|
|
6092
5995
|
}
|
|
6093
|
-
var parser_10 = {
|
|
6094
|
-
|
|
6095
|
-
parse: parseTxt
|
|
6096
|
-
};
|
|
6097
|
-
var module_11 = {
|
|
6098
|
-
feature: feature11,
|
|
6099
|
-
parser: parser_10
|
|
6100
|
-
// TXT 格式不需要预处理器
|
|
6101
|
-
};
|
|
5996
|
+
var parser_10 = { feature: feature11, parse: parseQq };
|
|
5997
|
+
var module_11 = { feature: feature11, parser: parser_10 };
|
|
6102
5998
|
var qq_native_txt_default = module_11;
|
|
6103
5999
|
|
|
6104
6000
|
// ../../packages/parser/src/formats/instagram-native.ts
|
|
6105
6001
|
import * as fs15 from "fs";
|
|
6106
|
-
import * as
|
|
6002
|
+
import * as path9 from "path";
|
|
6107
6003
|
var feature12 = {
|
|
6108
6004
|
id: "instagram-native",
|
|
6109
6005
|
name: "Instagram \u5B98\u65B9\u5BFC\u51FA",
|
|
@@ -6128,9 +6024,9 @@ function decodeInstagramText(text) {
|
|
|
6128
6024
|
return text;
|
|
6129
6025
|
}
|
|
6130
6026
|
}
|
|
6131
|
-
function
|
|
6132
|
-
const
|
|
6133
|
-
return
|
|
6027
|
+
function extractNameFromFilePath5(filePath) {
|
|
6028
|
+
const basename10 = path9.basename(filePath);
|
|
6029
|
+
return basename10.replace(/\.json$/i, "") || "\u672A\u77E5\u5BF9\u8BDD";
|
|
6134
6030
|
}
|
|
6135
6031
|
function isSystemMessage2(content) {
|
|
6136
6032
|
const systemPatterns = [
|
|
@@ -6216,7 +6112,7 @@ async function* parseInstagram(options2) {
|
|
|
6216
6112
|
ownerId = createMsg ? decodeInstagramText(createMsg.sender_name) : void 0;
|
|
6217
6113
|
}
|
|
6218
6114
|
const meta = {
|
|
6219
|
-
name: decodeInstagramText(data.title) ||
|
|
6115
|
+
name: decodeInstagramText(data.title) || extractNameFromFilePath5(filePath),
|
|
6220
6116
|
platform: KNOWN_PLATFORMS.INSTAGRAM,
|
|
6221
6117
|
type: chatType,
|
|
6222
6118
|
ownerId
|
|
@@ -6289,16 +6185,16 @@ var module_12 = {
|
|
|
6289
6185
|
var instagram_native_default = module_12;
|
|
6290
6186
|
|
|
6291
6187
|
// ../../packages/parser/src/formats/google-chat-takeout.ts
|
|
6292
|
-
var
|
|
6188
|
+
var import_stream_chain7 = __toESM(require_stream_chain(), 1);
|
|
6293
6189
|
var import_stream_json8 = __toESM(require_stream_json(), 1);
|
|
6294
6190
|
var import_Pick7 = __toESM(require_Pick(), 1);
|
|
6295
|
-
var
|
|
6191
|
+
var import_StreamValues7 = __toESM(require_StreamValues(), 1);
|
|
6296
6192
|
import * as fs16 from "fs";
|
|
6297
|
-
import * as
|
|
6298
|
-
var { chain:
|
|
6193
|
+
import * as path10 from "path";
|
|
6194
|
+
var { chain: chain7 } = import_stream_chain7.default;
|
|
6299
6195
|
var { parser: parser9 } = import_stream_json8.default;
|
|
6300
6196
|
var { pick: pick7 } = import_Pick7.default;
|
|
6301
|
-
var { streamValues:
|
|
6197
|
+
var { streamValues: streamValues7 } = import_StreamValues7.default;
|
|
6302
6198
|
var ENGLISH_MONTHS = /* @__PURE__ */ new Map([
|
|
6303
6199
|
["january", 0],
|
|
6304
6200
|
["february", 1],
|
|
@@ -6380,9 +6276,9 @@ function readJson(filePath) {
|
|
|
6380
6276
|
return JSON.parse(fs16.readFileSync(filePath, "utf8"));
|
|
6381
6277
|
}
|
|
6382
6278
|
function resolveManifestFile(manifestDir, relativePath) {
|
|
6383
|
-
const resolved =
|
|
6384
|
-
const relative2 =
|
|
6385
|
-
if (relative2.startsWith("..") ||
|
|
6279
|
+
const resolved = path10.resolve(manifestDir, relativePath);
|
|
6280
|
+
const relative2 = path10.relative(manifestDir, resolved);
|
|
6281
|
+
if (relative2.startsWith("..") || path10.isAbsolute(relative2)) {
|
|
6386
6282
|
throw new Error(`Google Chat manifest path escapes its directory: ${relativePath}`);
|
|
6387
6283
|
}
|
|
6388
6284
|
return resolved;
|
|
@@ -6395,7 +6291,7 @@ function readManifest2(filePath) {
|
|
|
6395
6291
|
if (manifest.chatType !== "private" && manifest.chatType !== "group") {
|
|
6396
6292
|
throw new Error("Invalid Google Chat chat type");
|
|
6397
6293
|
}
|
|
6398
|
-
const manifestDir =
|
|
6294
|
+
const manifestDir = path10.dirname(filePath);
|
|
6399
6295
|
return {
|
|
6400
6296
|
manifest,
|
|
6401
6297
|
userInfoPath: resolveManifestFile(manifestDir, manifest.userInfoFile),
|
|
@@ -6479,7 +6375,7 @@ async function* parseGoogleChat(options2) {
|
|
|
6479
6375
|
readStream.on("data", (chunk) => {
|
|
6480
6376
|
bytesRead += typeof chunk === "string" ? Buffer.byteLength(chunk) : chunk.length;
|
|
6481
6377
|
});
|
|
6482
|
-
const pipeline2 =
|
|
6378
|
+
const pipeline2 = chain7([readStream, parser9(), pick7({ filter: /^messages\.\d+$/ }), streamValues7()]);
|
|
6483
6379
|
const messageBatch = [];
|
|
6484
6380
|
try {
|
|
6485
6381
|
for await (const item of pipeline2) {
|
|
@@ -6527,418 +6423,270 @@ var google_chat_takeout_default = module_13;
|
|
|
6527
6423
|
|
|
6528
6424
|
// ../../packages/parser/src/formats/line-native-txt.ts
|
|
6529
6425
|
import * as fs17 from "fs";
|
|
6530
|
-
|
|
6531
|
-
|
|
6532
|
-
|
|
6533
|
-
|
|
6534
|
-
|
|
6535
|
-
|
|
6536
|
-
|
|
6537
|
-
|
|
6538
|
-
|
|
6539
|
-
|
|
6540
|
-
|
|
6541
|
-
|
|
6542
|
-
// Tab 分隔的消息格式(支持多种时间格式)
|
|
6543
|
-
/^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?)\t[^\t\n]+\t/m,
|
|
6544
|
-
// 空格分隔的消息格式(部分 LINE 导出)
|
|
6545
|
-
/^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?) [^\s]+ /m,
|
|
6546
|
-
// LINE 独有的日期行格式:YYYY.MM.DD DayOfWeek(英文星期全称)
|
|
6547
|
-
/^\d{4}\.\d{2}\.\d{2}\s+(?:Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)/m,
|
|
6548
|
-
// LINE 日文/中文日期行格式:YYYY/M/D(曜日)
|
|
6549
|
-
/^\d{4}\/\d{1,2}\/\d{1,2}[((][月火水木金土日]/m,
|
|
6550
|
-
// LINE 中文日期行格式:YYYY/M/D周X
|
|
6551
|
-
/^\d{4}\/\d{1,2}\/\d{1,2}周/m
|
|
6552
|
-
],
|
|
6553
|
-
// 文件名特征:[LINE] 出现在文件名中
|
|
6554
|
-
filename: [/\[LINE\]/i]
|
|
6555
|
-
}
|
|
6556
|
-
};
|
|
6557
|
-
function extractNameFromFilePath8(filePath) {
|
|
6558
|
-
const basename13 = path13.basename(filePath, ".txt");
|
|
6559
|
-
const name = basename13.replace(/^\[LINE\]\s*/i, "").trim();
|
|
6560
|
-
return name || "\u672A\u77E5\u804A\u5929";
|
|
6561
|
-
}
|
|
6562
|
-
function extractNameFromHeader(header) {
|
|
6563
|
-
const enPrivateMatch = header.match(/^(?:\[LINE\] )?Chat history with (.+)$/m);
|
|
6564
|
-
if (enPrivateMatch) return { name: enPrivateMatch[1].trim(), isGroup: false };
|
|
6565
|
-
const enGroupMatch = header.match(/^(?:\[LINE\] )?Chat history in (.+)$/m);
|
|
6566
|
-
if (enGroupMatch) return { name: enGroupMatch[1].trim(), isGroup: true };
|
|
6567
|
-
const jaPrivateMatch = header.match(/^\[LINE\] (.+)とのトーク履歴/);
|
|
6568
|
-
if (jaPrivateMatch) return { name: jaPrivateMatch[1].trim(), isGroup: false };
|
|
6569
|
-
const jaGroupMatch = header.match(/^\[LINE\] (.+)のトーク履歴/);
|
|
6570
|
-
if (jaGroupMatch) return { name: jaGroupMatch[1].trim(), isGroup: true };
|
|
6571
|
-
const zhCnPrivateMatch = header.match(/^\[LINE\] 与(.+)的聊天记录/);
|
|
6572
|
-
if (zhCnPrivateMatch) return { name: zhCnPrivateMatch[1].trim(), isGroup: false };
|
|
6573
|
-
const zhCnGroupMatch = header.match(/^\[LINE\] (.+)的聊天记录/);
|
|
6574
|
-
if (zhCnGroupMatch) return { name: zhCnGroupMatch[1].trim(), isGroup: true };
|
|
6575
|
-
const zhTwPrivateMatch = header.match(/^\[LINE\] 與(.+)的聊天記錄/);
|
|
6576
|
-
if (zhTwPrivateMatch) return { name: zhTwPrivateMatch[1].trim(), isGroup: false };
|
|
6577
|
-
const zhTwGroupMatch = header.match(/^\[LINE\] (.+)的聊天記錄/);
|
|
6578
|
-
if (zhTwGroupMatch) return { name: zhTwGroupMatch[1].trim(), isGroup: true };
|
|
6579
|
-
return null;
|
|
6580
|
-
}
|
|
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];
|
|
6581
6438
|
var DATE_PATTERNS = [
|
|
6582
|
-
// 2025.12.10 Wednesday
|
|
6583
6439
|
/^(\d{4})\.(\d{2})\.(\d{2})\s+(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)?/,
|
|
6584
|
-
// 2026/1/30周五 or 2026/1/30(金)
|
|
6585
6440
|
/^(\d{4})\/(\d{1,2})\/(\d{1,2})/,
|
|
6586
|
-
// Fri, 1/30/2026
|
|
6587
6441
|
/^[A-Za-z]+,\s*(\d{1,2})\/(\d{1,2})\/(\d{4})/
|
|
6588
|
-
];
|
|
6589
|
-
|
|
6590
|
-
|
|
6591
|
-
|
|
6592
|
-
if (match) {
|
|
6593
|
-
if (pattern === DATE_PATTERNS[0] || pattern === DATE_PATTERNS[1]) {
|
|
6594
|
-
const year = parseInt(match[1]);
|
|
6595
|
-
const month = parseInt(match[2]) - 1;
|
|
6596
|
-
const day = parseInt(match[3]);
|
|
6597
|
-
return new Date(year, month, day);
|
|
6598
|
-
} else if (pattern === DATE_PATTERNS[2]) {
|
|
6599
|
-
const month = parseInt(match[1]) - 1;
|
|
6600
|
-
const day = parseInt(match[2]);
|
|
6601
|
-
const year = parseInt(match[3]);
|
|
6602
|
-
return new Date(year, month, day);
|
|
6603
|
-
}
|
|
6604
|
-
}
|
|
6605
|
-
}
|
|
6606
|
-
return null;
|
|
6607
|
-
}
|
|
6608
|
-
var PRIVATE_MSG_PATTERN = /^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?)\t([^\t]+)\t(.*)$/;
|
|
6609
|
-
var GROUP_MSG_PATTERN = /^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?) ([^\s]+) (.*)$/;
|
|
6610
|
-
var SYSTEM_MSG_PATTERN = /^((?:上午|下午|午前|午後)?\d{1,2}:\d{2}(?:[AaPp][Mm])?)\t\t(.+)$/;
|
|
6442
|
+
];
|
|
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(.+)$/;
|
|
6611
6446
|
var SPECIAL_MESSAGE_TYPES = {
|
|
6612
|
-
// 图片 / Photo
|
|
6613
6447
|
"[Photo]": 1 /* IMAGE */,
|
|
6614
|
-
// EN
|
|
6615
6448
|
"[\u7167\u7247]": 1 /* IMAGE */,
|
|
6616
|
-
// ZH-CN / ZH-TW
|
|
6617
6449
|
"[\u5199\u771F]": 1 /* IMAGE */,
|
|
6618
|
-
// JA
|
|
6619
6450
|
Photos: 1 /* IMAGE */,
|
|
6620
|
-
// EN (fallback)
|
|
6621
|
-
// 语音 / Voice
|
|
6622
6451
|
"[Voice message]": 2 /* VOICE */,
|
|
6623
|
-
// EN
|
|
6624
6452
|
"[\u8BED\u97F3\u4FE1\u606F]": 2 /* VOICE */,
|
|
6625
|
-
// ZH-CN
|
|
6626
6453
|
"[\u8A9E\u97F3\u8A0A\u606F]": 2 /* VOICE */,
|
|
6627
|
-
// ZH-TW
|
|
6628
6454
|
"[\u30DC\u30A4\u30B9\u30E1\u30C3\u30BB\u30FC\u30B8]": 2 /* VOICE */,
|
|
6629
|
-
// JA
|
|
6630
6455
|
Audio: 2 /* VOICE */,
|
|
6631
|
-
// EN (fallback)
|
|
6632
|
-
// 视频 / Video
|
|
6633
6456
|
"[Video]": 3 /* VIDEO */,
|
|
6634
|
-
// EN
|
|
6635
6457
|
"[\u89C6\u9891]": 3 /* VIDEO */,
|
|
6636
|
-
// ZH-CN
|
|
6637
6458
|
"[\u5F71\u7247]": 3 /* VIDEO */,
|
|
6638
|
-
// ZH-TW
|
|
6639
6459
|
"[\u52D5\u753B]": 3 /* VIDEO */,
|
|
6640
|
-
// JA
|
|
6641
6460
|
Videos: 3 /* VIDEO */,
|
|
6642
|
-
// EN (fallback)
|
|
6643
|
-
// 文件 / File
|
|
6644
6461
|
"[File]": 4 /* FILE */,
|
|
6645
|
-
// EN
|
|
6646
6462
|
"[\u6587\u4EF6]": 4 /* FILE */,
|
|
6647
|
-
// ZH-CN
|
|
6648
6463
|
"[\u6A94\u6848]": 4 /* FILE */,
|
|
6649
|
-
// ZH-TW
|
|
6650
6464
|
"[\u30D5\u30A1\u30A4\u30EB]": 4 /* FILE */,
|
|
6651
|
-
// JA
|
|
6652
|
-
// 贴纸 / Sticker
|
|
6653
6465
|
"[Sticker]": 5 /* EMOJI */,
|
|
6654
|
-
// EN
|
|
6655
6466
|
"[\u8D34\u56FE]": 5 /* EMOJI */,
|
|
6656
|
-
// ZH-CN
|
|
6657
6467
|
"[\u8CBC\u5716]": 5 /* EMOJI */,
|
|
6658
|
-
// ZH-TW
|
|
6659
6468
|
"[\u30B9\u30BF\u30F3\u30D7]": 5 /* EMOJI */,
|
|
6660
|
-
// JA
|
|
6661
6469
|
Stickers: 5 /* EMOJI */,
|
|
6662
|
-
// EN (fallback)
|
|
6663
|
-
// 位置 / Location
|
|
6664
6470
|
"[Location]": 8 /* LOCATION */,
|
|
6665
|
-
// EN
|
|
6666
6471
|
"[\u4F4D\u7F6E]": 8 /* LOCATION */,
|
|
6667
|
-
// ZH-CN / ZH-TW
|
|
6668
6472
|
"[\u4F4D\u7F6E\u60C5\u5831]": 8 /* LOCATION */,
|
|
6669
|
-
// JA
|
|
6670
|
-
// 记事本 / Notes
|
|
6671
6473
|
"[Notes]": 0 /* TEXT */,
|
|
6672
|
-
// EN
|
|
6673
6474
|
"[\u8BB0\u4E8B\u672C]": 0 /* TEXT */,
|
|
6674
|
-
// ZH-CN
|
|
6675
6475
|
"[\u8A18\u4E8B\u672C]": 0 /* TEXT */,
|
|
6676
|
-
// ZH-TW
|
|
6677
6476
|
"[\u30CE\u30FC\u30C8]": 0 /* TEXT */
|
|
6678
|
-
// JA
|
|
6679
6477
|
};
|
|
6680
|
-
function
|
|
6681
|
-
for (const [pattern, type] of Object.entries(SPECIAL_MESSAGE_TYPES)) {
|
|
6682
|
-
if (content === pattern || content.startsWith(pattern)) {
|
|
6683
|
-
return type;
|
|
6684
|
-
}
|
|
6685
|
-
}
|
|
6686
|
-
if (content.startsWith("[null]") && content.includes("maps.google.com")) {
|
|
6687
|
-
return 8 /* LOCATION */;
|
|
6688
|
-
}
|
|
6689
|
-
if (
|
|
6690
|
-
// --- 加入群组 / Join group ---
|
|
6691
|
-
content.includes(" joined the group") || // EN
|
|
6692
|
-
content.includes("\u5DF2\u52A0\u5165\u8BE5\u7FA4") || // ZH-CN
|
|
6693
|
-
content.includes("\u5DF2\u52A0\u5165\u7FA4\u7D44") || // ZH-TW
|
|
6694
|
-
content.includes("\u304C\u30B0\u30EB\u30FC\u30D7\u306B\u53C2\u52A0\u3057\u307E\u3057\u305F") || // JA
|
|
6695
|
-
// --- 拉人进群 / Added to group ---
|
|
6696
|
-
content.includes(" added ") || // EN
|
|
6697
|
-
content.includes(" to the group") || // EN
|
|
6698
|
-
content.includes("\u5DF2\u5C06") || // ZH-CN
|
|
6699
|
-
content.includes("\u6DFB\u52A0\u81F3\u7FA4") || // ZH-CN
|
|
6700
|
-
content.includes("\u6DFB\u52A0\u5230\u7FA4") || // ZH-CN (另一格式)
|
|
6701
|
-
content.includes("\u5DF2\u65B0\u589E") || // ZH-TW
|
|
6702
|
-
content.includes("\u81F3\u7FA4\u7D44") || // ZH-TW
|
|
6703
|
-
content.includes("\u3092\u30B0\u30EB\u30FC\u30D7\u306B\u8FFD\u52A0\u3057\u307E\u3057\u305F") || // JA
|
|
6704
|
-
// --- 退出群组 / Left group ---
|
|
6705
|
-
content.includes(" left the group") || // EN
|
|
6706
|
-
content.includes("\u5DF2\u9000\u7FA4") || // ZH-CN
|
|
6707
|
-
content.includes("\u5DF2\u96E2\u958B\u7FA4\u7D44") || // ZH-TW
|
|
6708
|
-
content.includes("\u304C\u30B0\u30EB\u30FC\u30D7\u3092\u9000\u4F1A\u3057\u307E\u3057\u305F") || // JA
|
|
6709
|
-
// --- 设定公告 / Announcement ---
|
|
6710
|
-
content.includes("made an announcement") || // EN
|
|
6711
|
-
content.includes("\u53D1\u5E03\u4E86\u901A\u544A") || // ZH-CN
|
|
6712
|
-
content.includes("\u5DF2\u8A2D\u5B9A\u516C\u544A") || // ZH-TW
|
|
6713
|
-
content.includes("\u304C\u30A2\u30CA\u30A6\u30F3\u30B9\u3057\u307E\u3057\u305F") || // JA
|
|
6714
|
-
// --- 收回讯息 / Unsent message ---
|
|
6715
|
-
content.includes("unsent a message") || // EN
|
|
6716
|
-
content === "Message unsent." || // EN
|
|
6717
|
-
content.includes("\u64A4\u56DE\u4E86\u4E00\u6761\u6D88\u606F") || // ZH-CN
|
|
6718
|
-
content.includes("\u5DF2\u6536\u56DE\u8A0A\u606F") || // ZH-TW
|
|
6719
|
-
content.includes("\u9001\u4FE1\u3092\u53D6\u308A\u6D88\u3057\u307E\u3057\u305F") || // JA
|
|
6720
|
-
// --- 其他 / Others ---
|
|
6721
|
-
content.startsWith("Auto-reply")
|
|
6722
|
-
) {
|
|
6723
|
-
return 80 /* SYSTEM */;
|
|
6724
|
-
}
|
|
6725
|
-
if (content.match(/^https?:\/\//)) {
|
|
6726
|
-
return 7 /* LINK */;
|
|
6727
|
-
}
|
|
6728
|
-
return 0 /* TEXT */;
|
|
6729
|
-
}
|
|
6730
|
-
async function* parseLINE(options2) {
|
|
6731
|
-
const { filePath, batchSize = 5e3, onProgress, onLog } = options2;
|
|
6732
|
-
const totalBytes = getFileSize(filePath);
|
|
6733
|
-
let messagesProcessed = 0;
|
|
6734
|
-
const initialProgress = createProgress("parsing", 0, totalBytes, 0, "");
|
|
6735
|
-
yield { type: "progress", data: initialProgress };
|
|
6736
|
-
onProgress?.(initialProgress);
|
|
6737
|
-
onLog?.("info", `\u5F00\u59CB\u89E3\u6790 LINE \u5BFC\u51FA\u6587\u4EF6\uFF0C\u5927\u5C0F: ${(totalBytes / 1024 / 1024).toFixed(2)} MB`);
|
|
6738
|
-
const content = fs17.readFileSync(filePath, "utf-8");
|
|
6478
|
+
async function parseLineText(content, fileName, options2 = {}) {
|
|
6739
6479
|
const lines = content.split("\n").map((line) => line.replace(/\r$/, ""));
|
|
6740
|
-
let currentDate = null;
|
|
6741
|
-
let chatName = extractNameFromFilePath8(filePath);
|
|
6742
|
-
let isPrivateChat = false;
|
|
6743
|
-
let useTabSeparator = false;
|
|
6744
6480
|
const memberMap = /* @__PURE__ */ new Map();
|
|
6745
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;
|
|
6746
6485
|
let lastMessage = null;
|
|
6747
|
-
let
|
|
6748
|
-
|
|
6749
|
-
|
|
6750
|
-
|
|
6751
|
-
|
|
6752
|
-
if (headerResult) {
|
|
6753
|
-
chatName = headerResult.name;
|
|
6754
|
-
isPrivateChat = !headerResult.isGroup;
|
|
6755
|
-
useTabSeparator = true;
|
|
6756
|
-
lineIndex = 3;
|
|
6757
|
-
onLog?.("debug", `LINE \u68C0\u6D4B\u5230\u5934\u90E8\uFF0C\u540D\u79F0: ${headerResult.name}, \u7FA4\u804A: ${headerResult.isGroup}`);
|
|
6758
|
-
}
|
|
6759
|
-
}
|
|
6760
|
-
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) {
|
|
6761
6491
|
for (const line of lines) {
|
|
6762
|
-
if (
|
|
6492
|
+
if (TAB_MESSAGE_PATTERN.test(line)) {
|
|
6763
6493
|
useTabSeparator = true;
|
|
6764
|
-
onLog?.("debug", `LINE \u68C0\u6D4B\u5230 Tab \u5206\u9694\u683C\u5F0F`);
|
|
6765
6494
|
break;
|
|
6766
6495
|
}
|
|
6767
|
-
if (
|
|
6496
|
+
if (SPACE_MESSAGE_PATTERN.test(line)) {
|
|
6768
6497
|
useTabSeparator = false;
|
|
6769
|
-
onLog?.("debug", `LINE \u68C0\u6D4B\u5230\u7A7A\u683C\u5206\u9694\u683C\u5F0F`);
|
|
6770
6498
|
break;
|
|
6771
6499
|
}
|
|
6772
6500
|
}
|
|
6773
6501
|
}
|
|
6774
|
-
|
|
6775
|
-
|
|
6776
|
-
|
|
6777
|
-
const
|
|
6778
|
-
|
|
6779
|
-
|
|
6780
|
-
currentDate = dateResult;
|
|
6781
|
-
if (debugLogCount < 5) {
|
|
6782
|
-
onLog?.("debug", `LINE \u65E5\u671F\u884C[${i}]: ${line} -> ${dateResult.toISOString()}`);
|
|
6783
|
-
}
|
|
6784
|
-
continue;
|
|
6785
|
-
}
|
|
6786
|
-
const msgPattern = useTabSeparator ? PRIVATE_MSG_PATTERN : GROUP_MSG_PATTERN;
|
|
6787
|
-
const msgMatch = line.match(msgPattern);
|
|
6788
|
-
if (debugLogCount < 5 && line.trim()) {
|
|
6789
|
-
onLog?.("debug", `LINE \u884C[${i}]: "${line.substring(0, 50)}..." match=${!!msgMatch}`);
|
|
6790
|
-
debugLogCount++;
|
|
6791
|
-
}
|
|
6792
|
-
if (msgMatch) {
|
|
6793
|
-
const [, timeStr, sender, contentRaw] = msgMatch;
|
|
6794
|
-
let content2 = contentRaw.trim();
|
|
6795
|
-
let isQuotedMultiline = false;
|
|
6796
|
-
if (content2.startsWith('"')) {
|
|
6797
|
-
content2 = content2.substring(1);
|
|
6798
|
-
isQuotedMultiline = !content2.endsWith('"');
|
|
6799
|
-
if (content2.endsWith('"')) {
|
|
6800
|
-
content2 = content2.substring(0, content2.length - 1);
|
|
6801
|
-
}
|
|
6802
|
-
}
|
|
6803
|
-
let hours = 0;
|
|
6804
|
-
let minutes = 0;
|
|
6805
|
-
const prefix = timeStr.match(/^(上午|下午|午前|午後)/)?.[1];
|
|
6806
|
-
const cleanTime = timeStr.replace(/^(上午|下午|午前|午後)/, "");
|
|
6807
|
-
const partsMatch = cleanTime.match(/^(\d{1,2}):(\d{2})([AaPp][Mm])?$/i);
|
|
6808
|
-
if (partsMatch) {
|
|
6809
|
-
hours = parseInt(partsMatch[1]);
|
|
6810
|
-
minutes = parseInt(partsMatch[2]);
|
|
6811
|
-
const suffix = partsMatch[3]?.toLowerCase();
|
|
6812
|
-
if (suffix === "pm" && hours < 12) hours += 12;
|
|
6813
|
-
if (suffix === "am" && hours === 12) hours = 0;
|
|
6814
|
-
if ((prefix === "\u4E0B\u5348" || prefix === "\u5348\u5F8C") && hours < 12) hours += 12;
|
|
6815
|
-
if ((prefix === "\u4E0A\u5348" || prefix === "\u5348\u524D") && hours === 12) hours = 0;
|
|
6816
|
-
} else {
|
|
6817
|
-
const parts = timeStr.split(":").map(Number);
|
|
6818
|
-
hours = parts[0];
|
|
6819
|
-
minutes = parts[1];
|
|
6820
|
-
}
|
|
6821
|
-
let timestamp;
|
|
6822
|
-
if (currentDate) {
|
|
6823
|
-
const msgDate = new Date(currentDate);
|
|
6824
|
-
msgDate.setHours(hours, minutes, 0, 0);
|
|
6825
|
-
timestamp = Math.floor(msgDate.getTime() / 1e3);
|
|
6826
|
-
} else {
|
|
6827
|
-
const now = /* @__PURE__ */ new Date();
|
|
6828
|
-
now.setHours(hours, minutes, 0, 0);
|
|
6829
|
-
timestamp = Math.floor(now.getTime() / 1e3);
|
|
6830
|
-
}
|
|
6831
|
-
const msgType = detectMessageType6(content2);
|
|
6832
|
-
if (!memberMap.has(sender)) {
|
|
6833
|
-
memberMap.set(sender, {
|
|
6834
|
-
platformId: sender,
|
|
6835
|
-
accountName: sender
|
|
6836
|
-
});
|
|
6837
|
-
}
|
|
6838
|
-
lastMessage = {
|
|
6839
|
-
senderPlatformId: sender,
|
|
6840
|
-
senderAccountName: sender,
|
|
6841
|
-
timestamp,
|
|
6842
|
-
type: msgType,
|
|
6843
|
-
content: content2 || null,
|
|
6844
|
-
_isQuotedMultiline: isQuotedMultiline
|
|
6845
|
-
// 临时标记,用于追加多行内容时处理结尾引号
|
|
6846
|
-
};
|
|
6847
|
-
messages.push(lastMessage);
|
|
6848
|
-
messagesProcessed++;
|
|
6849
|
-
if (messagesProcessed % 1e3 === 0) {
|
|
6850
|
-
const progress = createProgress(
|
|
6851
|
-
"parsing",
|
|
6852
|
-
i,
|
|
6853
|
-
lines.length,
|
|
6854
|
-
messagesProcessed,
|
|
6855
|
-
`\u5DF2\u5904\u7406 ${messagesProcessed} \u6761\u6D88\u606F...`
|
|
6856
|
-
);
|
|
6857
|
-
onProgress?.(progress);
|
|
6858
|
-
}
|
|
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;
|
|
6859
6508
|
} else {
|
|
6860
|
-
const
|
|
6861
|
-
|
|
6862
|
-
|
|
6863
|
-
const
|
|
6864
|
-
let
|
|
6865
|
-
|
|
6866
|
-
|
|
6867
|
-
|
|
6868
|
-
|
|
6869
|
-
|
|
6870
|
-
hours = parseInt(partsMatch[1]);
|
|
6871
|
-
minutes = parseInt(partsMatch[2]);
|
|
6872
|
-
const suffix = partsMatch[3]?.toLowerCase();
|
|
6873
|
-
if (suffix === "pm" && hours < 12) hours += 12;
|
|
6874
|
-
if (suffix === "am" && hours === 12) hours = 0;
|
|
6875
|
-
if ((prefix === "\u4E0B\u5348" || prefix === "\u5348\u5F8C") && hours < 12) hours += 12;
|
|
6876
|
-
if ((prefix === "\u4E0A\u5348" || prefix === "\u5348\u524D") && hours === 12) hours = 0;
|
|
6877
|
-
} else {
|
|
6878
|
-
const parts = timeStr.split(":").map(Number);
|
|
6879
|
-
hours = parts[0];
|
|
6880
|
-
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);
|
|
6881
6519
|
}
|
|
6882
|
-
|
|
6883
|
-
|
|
6884
|
-
const msgDate = new Date(currentDate);
|
|
6885
|
-
msgDate.setHours(hours, minutes, 0, 0);
|
|
6886
|
-
timestamp = Math.floor(msgDate.getTime() / 1e3);
|
|
6887
|
-
} else {
|
|
6888
|
-
const now = /* @__PURE__ */ new Date();
|
|
6889
|
-
now.setHours(hours, minutes, 0, 0);
|
|
6890
|
-
timestamp = Math.floor(now.getTime() / 1e3);
|
|
6520
|
+
if (!memberMap.has(sender)) {
|
|
6521
|
+
memberMap.set(sender, { platformId: sender, accountName: sender });
|
|
6891
6522
|
}
|
|
6892
6523
|
lastMessage = {
|
|
6893
|
-
senderPlatformId:
|
|
6894
|
-
senderAccountName:
|
|
6895
|
-
timestamp,
|
|
6896
|
-
type:
|
|
6897
|
-
content:
|
|
6524
|
+
senderPlatformId: sender,
|
|
6525
|
+
senderAccountName: sender,
|
|
6526
|
+
timestamp: buildTimestamp(currentDate, timeText, options2.now),
|
|
6527
|
+
type: detectMessageType6(messageContent),
|
|
6528
|
+
content: messageContent || null
|
|
6898
6529
|
};
|
|
6899
6530
|
messages.push(lastMessage);
|
|
6900
|
-
|
|
6901
|
-
|
|
6902
|
-
|
|
6903
|
-
|
|
6904
|
-
|
|
6905
|
-
|
|
6906
|
-
|
|
6907
|
-
|
|
6908
|
-
|
|
6909
|
-
|
|
6910
|
-
|
|
6911
|
-
|
|
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;
|
|
6912
6553
|
}
|
|
6913
6554
|
}
|
|
6914
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
|
+
}
|
|
6915
6561
|
}
|
|
6562
|
+
options2.checkCancelled?.();
|
|
6916
6563
|
const memberCount = memberMap.size;
|
|
6917
|
-
const chatType = memberCount <= 2 ? "private" /* PRIVATE */ : "group" /* GROUP */;
|
|
6918
|
-
const
|
|
6919
|
-
name: chatName,
|
|
6920
|
-
|
|
6921
|
-
|
|
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
|
|
6922
6569
|
};
|
|
6923
|
-
|
|
6924
|
-
|
|
6925
|
-
|
|
6926
|
-
|
|
6927
|
-
|
|
6928
|
-
|
|
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));
|
|
6929
6606
|
}
|
|
6930
|
-
|
|
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, "");
|
|
6931
6679
|
yield { type: "progress", data: doneProgress };
|
|
6932
6680
|
onProgress?.(doneProgress);
|
|
6933
|
-
onLog?.("info",
|
|
6681
|
+
onLog?.("info", `LINE TXT parsing completed: ${result.messages.length} messages, ${result.members.length} members`);
|
|
6934
6682
|
yield {
|
|
6935
6683
|
type: "done",
|
|
6936
|
-
data: { messageCount:
|
|
6684
|
+
data: { messageCount: result.messages.length, memberCount: result.members.length }
|
|
6937
6685
|
};
|
|
6938
6686
|
}
|
|
6939
6687
|
var parser_13 = {
|
|
6940
6688
|
feature: feature14,
|
|
6941
|
-
parse:
|
|
6689
|
+
parse: parseLine2
|
|
6942
6690
|
};
|
|
6943
6691
|
var module_14 = {
|
|
6944
6692
|
feature: feature14,
|
|
@@ -6978,6 +6726,398 @@ var formats = [
|
|
|
6978
6726
|
// 优先级 51 - ChatLab JSONL(流式格式,支持超大文件)
|
|
6979
6727
|
];
|
|
6980
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
|
+
|
|
6981
7121
|
// ../../packages/parser/src/index.ts
|
|
6982
7122
|
var sniffer = createSniffer();
|
|
6983
7123
|
sniffer.registerAll(formats);
|
|
@@ -7051,15 +7191,15 @@ function findEntryFileInDirectory(dirPath, maxDepth = 1) {
|
|
|
7051
7191
|
return null;
|
|
7052
7192
|
}
|
|
7053
7193
|
for (const entry of entries) {
|
|
7054
|
-
const full =
|
|
7055
|
-
if (entry.isFile() && supportedExtensions.has(
|
|
7194
|
+
const full = path11.join(current, entry.name);
|
|
7195
|
+
if (entry.isFile() && supportedExtensions.has(path11.extname(entry.name).toLowerCase())) {
|
|
7056
7196
|
const format = detectFormat(full);
|
|
7057
7197
|
if (format) return full;
|
|
7058
7198
|
}
|
|
7059
7199
|
}
|
|
7060
7200
|
for (const entry of entries) {
|
|
7061
7201
|
if (entry.isDirectory()) {
|
|
7062
|
-
const found = walk(
|
|
7202
|
+
const found = walk(path11.join(current, entry.name), depth + 1);
|
|
7063
7203
|
if (found) return found;
|
|
7064
7204
|
}
|
|
7065
7205
|
}
|
|
@@ -7109,7 +7249,7 @@ function logNativeParserStatus() {
|
|
|
7109
7249
|
|
|
7110
7250
|
// ../../packages/node-runtime/src/import/perf-logger.ts
|
|
7111
7251
|
import * as fs19 from "fs";
|
|
7112
|
-
import * as
|
|
7252
|
+
import * as path12 from "path";
|
|
7113
7253
|
var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
|
|
7114
7254
|
LogLevel2["ERROR"] = "ERROR";
|
|
7115
7255
|
LogLevel2["INFO"] = "INFO";
|
|
@@ -7124,7 +7264,7 @@ function initPerfLog(sessionId, logDir) {
|
|
|
7124
7264
|
if (!fs19.existsSync(logDir)) {
|
|
7125
7265
|
fs19.mkdirSync(logDir, { recursive: true });
|
|
7126
7266
|
}
|
|
7127
|
-
currentLogFile =
|
|
7267
|
+
currentLogFile = path12.join(logDir, `import_${sessionId}_${Date.now()}.log`);
|
|
7128
7268
|
fs19.writeFileSync(currentLogFile, `=== Import Log ===
|
|
7129
7269
|
Start time: ${(/* @__PURE__ */ new Date()).toISOString()}
|
|
7130
7270
|
|
|
@@ -7301,7 +7441,7 @@ function segmentText(text, locale, minLength) {
|
|
|
7301
7441
|
|
|
7302
7442
|
// ../../packages/node-runtime/src/nlp/dict-manager.ts
|
|
7303
7443
|
import * as fs20 from "fs";
|
|
7304
|
-
import * as
|
|
7444
|
+
import * as path13 from "path";
|
|
7305
7445
|
import { createHash } from "crypto";
|
|
7306
7446
|
var DICT_DOWNLOAD_URL_BASE = "https://chatlab.fun/assets/nlp";
|
|
7307
7447
|
var DICT_SHA256 = {
|
|
@@ -7313,7 +7453,7 @@ var AVAILABLE_DICTS = [
|
|
|
7313
7453
|
{ id: "zh-TW", label: "\u7E41\u9AD4\u4E2D\u6587", locale: "zh-TW" }
|
|
7314
7454
|
];
|
|
7315
7455
|
function getDictFilePath(nlpDir, dictId) {
|
|
7316
|
-
return
|
|
7456
|
+
return path13.join(nlpDir, `${dictId}.dict`);
|
|
7317
7457
|
}
|
|
7318
7458
|
function isDictDownloaded(nlpDir, dictId) {
|
|
7319
7459
|
return fs20.existsSync(getDictFilePath(nlpDir, dictId));
|
|
@@ -8779,7 +8919,7 @@ async function analyzeAutoImportFile(filePath, deps, options2 = {}) {
|
|
|
8779
8919
|
|
|
8780
8920
|
// ../../packages/node-runtime/src/import/import-lock.ts
|
|
8781
8921
|
import * as fs22 from "fs";
|
|
8782
|
-
import * as
|
|
8922
|
+
import * as path14 from "path";
|
|
8783
8923
|
import { randomUUID } from "crypto";
|
|
8784
8924
|
var IMPORT_LOCK_FILENAME = ".chatlab-import.lock";
|
|
8785
8925
|
var IMPORT_IN_PROGRESS_ERROR_KEY = "error.import_in_progress";
|
|
@@ -8848,7 +8988,7 @@ function releaseLock(lockPath, fd, owner) {
|
|
|
8848
8988
|
}
|
|
8849
8989
|
async function withDataDirImportLock(userDataDir, task) {
|
|
8850
8990
|
fs22.mkdirSync(userDataDir, { recursive: true });
|
|
8851
|
-
const lockPath =
|
|
8991
|
+
const lockPath = path14.join(userDataDir, IMPORT_LOCK_FILENAME);
|
|
8852
8992
|
let fd = null;
|
|
8853
8993
|
for (let attempt = 0; attempt < 2; attempt++) {
|
|
8854
8994
|
try {
|
|
@@ -8893,7 +9033,7 @@ var ArchiveImportError = class extends Error {
|
|
|
8893
9033
|
// ../../packages/node-runtime/src/import/archive/archive-reader.ts
|
|
8894
9034
|
var import_yauzl = __toESM(require_yauzl(), 1);
|
|
8895
9035
|
import * as fs23 from "fs";
|
|
8896
|
-
import * as
|
|
9036
|
+
import * as path15 from "path";
|
|
8897
9037
|
import { pipeline } from "stream/promises";
|
|
8898
9038
|
var DEFAULT_MAX_ENTRIES = 1e5;
|
|
8899
9039
|
var DEFAULT_MAX_ENTRY_BYTES = 20 * 1024 * 1024 * 1024;
|
|
@@ -9012,7 +9152,7 @@ var ZipArchiveReader = class {
|
|
|
9012
9152
|
await this.visitEntries(async (entry, openStream) => {
|
|
9013
9153
|
const destination = targets.get(entry.name);
|
|
9014
9154
|
if (!destination || entry.isDirectory) return;
|
|
9015
|
-
fs23.mkdirSync(
|
|
9155
|
+
fs23.mkdirSync(path15.dirname(destination), { recursive: true });
|
|
9016
9156
|
await pipeline(await openStream(), fs23.createWriteStream(destination, { flags: "wx" }));
|
|
9017
9157
|
remaining.delete(entry.name);
|
|
9018
9158
|
});
|
|
@@ -9026,16 +9166,16 @@ var ZipArchiveReader = class {
|
|
|
9026
9166
|
};
|
|
9027
9167
|
|
|
9028
9168
|
// ../../packages/node-runtime/src/import/archive/google-chat-resolver.ts
|
|
9029
|
-
var
|
|
9169
|
+
var import_stream_chain8 = __toESM(require_stream_chain(), 1);
|
|
9030
9170
|
var import_stream_json9 = __toESM(require_stream_json(), 1);
|
|
9031
9171
|
var import_Pick8 = __toESM(require_Pick(), 1);
|
|
9032
|
-
var
|
|
9172
|
+
var import_StreamValues8 = __toESM(require_StreamValues(), 1);
|
|
9033
9173
|
import * as fs24 from "fs";
|
|
9034
|
-
import * as
|
|
9035
|
-
var { chain:
|
|
9174
|
+
import * as path16 from "path";
|
|
9175
|
+
var { chain: chain8 } = import_stream_chain8.default;
|
|
9036
9176
|
var { parser: parser10 } = import_stream_json9.default;
|
|
9037
9177
|
var { pick: pick8 } = import_Pick8.default;
|
|
9038
|
-
var { streamValues:
|
|
9178
|
+
var { streamValues: streamValues8 } = import_StreamValues8.default;
|
|
9039
9179
|
var GROUP_FILE = /^Takeout\/Google Chat\/Groups\/((DM|Space) [^/]+)\/(group_info|messages)\.json$/;
|
|
9040
9180
|
var USER_INFO_FILE = /^Takeout\/Google Chat\/Users\/[^/]+\/user_info\.json$/;
|
|
9041
9181
|
var TAKEOUT_ROOT = /^Takeout\/([^/]+)\//;
|
|
@@ -9058,7 +9198,7 @@ async function readSmallJson(stream, maxBytes = MAX_METADATA_BYTES) {
|
|
|
9058
9198
|
}
|
|
9059
9199
|
}
|
|
9060
9200
|
async function countMessages(stream) {
|
|
9061
|
-
const pipeline2 =
|
|
9201
|
+
const pipeline2 = chain8([stream, parser10(), pick8({ filter: /^messages\.\d+$/ }), streamValues8()]);
|
|
9062
9202
|
let count = 0;
|
|
9063
9203
|
try {
|
|
9064
9204
|
for await (const _item of pipeline2) count++;
|
|
@@ -9163,12 +9303,12 @@ var GoogleChatTakeoutResolver = class {
|
|
|
9163
9303
|
fs24.mkdirSync(targetDir, { recursive: true });
|
|
9164
9304
|
await reader.pipeEntries(
|
|
9165
9305
|
/* @__PURE__ */ new Map([
|
|
9166
|
-
[userInfoEntry,
|
|
9167
|
-
[groupInfoEntry,
|
|
9168
|
-
[messagesEntry,
|
|
9306
|
+
[userInfoEntry, path16.join(targetDir, "user_info.json")],
|
|
9307
|
+
[groupInfoEntry, path16.join(targetDir, "group_info.json")],
|
|
9308
|
+
[messagesEntry, path16.join(targetDir, "messages.json")]
|
|
9169
9309
|
])
|
|
9170
9310
|
);
|
|
9171
|
-
const manifestPath =
|
|
9311
|
+
const manifestPath = path16.join(targetDir, "google-chat-import.json");
|
|
9172
9312
|
fs24.writeFileSync(
|
|
9173
9313
|
manifestPath,
|
|
9174
9314
|
JSON.stringify(
|
|
@@ -9193,7 +9333,7 @@ var GoogleChatTakeoutResolver = class {
|
|
|
9193
9333
|
|
|
9194
9334
|
// ../../packages/node-runtime/src/import/archive/source-manager.ts
|
|
9195
9335
|
import * as fs25 from "fs";
|
|
9196
|
-
import * as
|
|
9336
|
+
import * as path17 from "path";
|
|
9197
9337
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
9198
9338
|
var DEFAULT_TTL_MS = 30 * 60 * 1e3;
|
|
9199
9339
|
var ArchiveImportSourceManager = class {
|
|
@@ -9265,7 +9405,7 @@ var ArchiveImportSourceManager = class {
|
|
|
9265
9405
|
`Archive conversation was not found: ${chatId}`
|
|
9266
9406
|
);
|
|
9267
9407
|
}
|
|
9268
|
-
const materializedDir = fs25.mkdtempSync(
|
|
9408
|
+
const materializedDir = fs25.mkdtempSync(path17.join(this.tempRoot, `materialized-${sourceId}-`));
|
|
9269
9409
|
try {
|
|
9270
9410
|
const reader = new ZipArchiveReader(record.archivePath);
|
|
9271
9411
|
const materialized = await record.resolver.materialize(reader, chat, materializedDir);
|
|
@@ -9386,12 +9526,12 @@ ${rawErrorLabel}: ${details}`;
|
|
|
9386
9526
|
}
|
|
9387
9527
|
|
|
9388
9528
|
// ../../packages/node-runtime/src/ai/assistant-parser.ts
|
|
9389
|
-
import * as
|
|
9529
|
+
import * as path18 from "path";
|
|
9390
9530
|
import matter from "gray-matter";
|
|
9391
9531
|
function parseAssistantFile(content, filePath) {
|
|
9392
9532
|
try {
|
|
9393
9533
|
const { data: fm, content: body } = matter(content);
|
|
9394
|
-
const id = fm.id ??
|
|
9534
|
+
const id = fm.id ?? path18.basename(filePath, ".md");
|
|
9395
9535
|
const name = fm.name;
|
|
9396
9536
|
if (!name) return null;
|
|
9397
9537
|
return {
|
|
@@ -9901,12 +10041,12 @@ var AssistantManager = class {
|
|
|
9901
10041
|
};
|
|
9902
10042
|
|
|
9903
10043
|
// ../../packages/node-runtime/src/ai/skill-parser.ts
|
|
9904
|
-
import * as
|
|
10044
|
+
import * as path19 from "path";
|
|
9905
10045
|
import matter2 from "gray-matter";
|
|
9906
10046
|
function parseSkillFile(content, filePath) {
|
|
9907
10047
|
try {
|
|
9908
10048
|
const { data: fm, content: prompt } = matter2(content);
|
|
9909
|
-
const id = fm.id ??
|
|
10049
|
+
const id = fm.id ?? path19.basename(filePath, ".md");
|
|
9910
10050
|
const name = fm.name;
|
|
9911
10051
|
if (!name) return null;
|
|
9912
10052
|
return {
|
|
@@ -9925,7 +10065,7 @@ function parseSkillFile(content, filePath) {
|
|
|
9925
10065
|
function extractSkillId(content, filePath) {
|
|
9926
10066
|
try {
|
|
9927
10067
|
const { data: fm } = matter2(content);
|
|
9928
|
-
return fm.id ??
|
|
10068
|
+
return fm.id ?? path19.basename(filePath, ".md");
|
|
9929
10069
|
} catch {
|
|
9930
10070
|
return null;
|
|
9931
10071
|
}
|
|
@@ -9945,7 +10085,7 @@ function validateChatScope(raw) {
|
|
|
9945
10085
|
// ../../packages/node-runtime/src/ai/chats.ts
|
|
9946
10086
|
import Database from "better-sqlite3";
|
|
9947
10087
|
import * as fs26 from "fs";
|
|
9948
|
-
import * as
|
|
10088
|
+
import * as path20 from "path";
|
|
9949
10089
|
var DEFAULT_GENERAL_ID = DEFAULT_GENERAL_ASSISTANT_ID;
|
|
9950
10090
|
var defaultLogger = {
|
|
9951
10091
|
warn(_category, message, extra) {
|
|
@@ -9971,7 +10111,7 @@ var AIChatManager = class {
|
|
|
9971
10111
|
getDb() {
|
|
9972
10112
|
if (this.db) return this.db;
|
|
9973
10113
|
this.ensureDir(this.aiDataDir);
|
|
9974
|
-
const dbPath =
|
|
10114
|
+
const dbPath = path20.join(this.aiDataDir, "conversations.db");
|
|
9975
10115
|
this.db = this.nativeBinding ? new Database(dbPath, { nativeBinding: this.nativeBinding }) : new Database(dbPath);
|
|
9976
10116
|
this.db.pragma("journal_mode = WAL");
|
|
9977
10117
|
this.db.exec(`
|
|
@@ -10214,16 +10354,16 @@ var AIChatManager = class {
|
|
|
10214
10354
|
if (allRows.length === 0) return [];
|
|
10215
10355
|
const rowMap = new Map(allRows.map((row) => [row.id, row]));
|
|
10216
10356
|
let currentId = leafMessageId ?? this.getActiveMessageId(aiChatId);
|
|
10217
|
-
const
|
|
10357
|
+
const path30 = [];
|
|
10218
10358
|
const seen = /* @__PURE__ */ new Set();
|
|
10219
10359
|
while (currentId && !seen.has(currentId)) {
|
|
10220
10360
|
seen.add(currentId);
|
|
10221
10361
|
const row = rowMap.get(currentId);
|
|
10222
10362
|
if (!row) break;
|
|
10223
|
-
|
|
10363
|
+
path30.push(row);
|
|
10224
10364
|
currentId = row.parentId;
|
|
10225
10365
|
}
|
|
10226
|
-
return
|
|
10366
|
+
return path30.length > 0 ? path30.reverse() : allRows;
|
|
10227
10367
|
}
|
|
10228
10368
|
// ==================== 生命周期 ====================
|
|
10229
10369
|
close() {
|
|
@@ -10960,7 +11100,7 @@ var SkillManagerCore = class {
|
|
|
10960
11100
|
|
|
10961
11101
|
// ../../packages/node-runtime/src/ai/skill-manager.ts
|
|
10962
11102
|
import * as fs27 from "fs";
|
|
10963
|
-
import * as
|
|
11103
|
+
import * as path21 from "path";
|
|
10964
11104
|
var SKILLS_DIR_NAME = "skills";
|
|
10965
11105
|
var defaultLogger2 = {
|
|
10966
11106
|
info: () => {
|
|
@@ -10989,7 +11129,7 @@ var nodeFs = {
|
|
|
10989
11129
|
return fs27.existsSync(filePath);
|
|
10990
11130
|
},
|
|
10991
11131
|
joinPath(...parts) {
|
|
10992
|
-
return
|
|
11132
|
+
return path21.join(...parts);
|
|
10993
11133
|
}
|
|
10994
11134
|
};
|
|
10995
11135
|
var SkillManager = class {
|
|
@@ -10999,7 +11139,7 @@ var SkillManager = class {
|
|
|
10999
11139
|
this.logger = logger ?? defaultLogger2;
|
|
11000
11140
|
this.core = new SkillManagerCore({
|
|
11001
11141
|
fs: nodeFs,
|
|
11002
|
-
skillsDir:
|
|
11142
|
+
skillsDir: path21.join(aiDataDir, SKILLS_DIR_NAME),
|
|
11003
11143
|
builtinRawSkills: [],
|
|
11004
11144
|
logger: {
|
|
11005
11145
|
info: () => {
|
|
@@ -12861,7 +13001,7 @@ var PLAN_INTENTS = [
|
|
|
12861
13001
|
"mixed"
|
|
12862
13002
|
];
|
|
12863
13003
|
var STREAM_MARKER_HOLDBACK_CHARS = Math.max("</draft>".length, "</plan>".length, "<json>".length) - 1;
|
|
12864
|
-
function
|
|
13004
|
+
function isRecord2(value) {
|
|
12865
13005
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
12866
13006
|
}
|
|
12867
13007
|
function isIntent(value) {
|
|
@@ -12883,10 +13023,10 @@ function extractJsonObject4(text) {
|
|
|
12883
13023
|
function parsePlanJson(rawText, availableTools) {
|
|
12884
13024
|
try {
|
|
12885
13025
|
const parsed = JSON.parse(extractJsonObject4(rawText));
|
|
12886
|
-
if (!
|
|
13026
|
+
if (!isRecord2(parsed)) return null;
|
|
12887
13027
|
const title = normalizeString(parsed.title);
|
|
12888
13028
|
if (!title || !isIntent(parsed.intent) || !Array.isArray(parsed.steps)) return null;
|
|
12889
|
-
const steps = parsed.steps.filter(
|
|
13029
|
+
const steps = parsed.steps.filter(isRecord2).map((step) => {
|
|
12890
13030
|
const goal = normalizeString(step.goal);
|
|
12891
13031
|
const evidenceNeeded = normalizeString(step.evidenceNeeded);
|
|
12892
13032
|
if (!goal || !evidenceNeeded) return null;
|
|
@@ -14865,9 +15005,9 @@ var localeMap = {
|
|
|
14865
15005
|
"ja-JP": ja_JP_default,
|
|
14866
15006
|
"zh-TW": zh_TW_default
|
|
14867
15007
|
};
|
|
14868
|
-
function getNestedValue(obj,
|
|
15008
|
+
function getNestedValue(obj, path30) {
|
|
14869
15009
|
let current = obj;
|
|
14870
|
-
for (const segment2 of
|
|
15010
|
+
for (const segment2 of path30.split(".")) {
|
|
14871
15011
|
if (current == null || typeof current !== "object") return void 0;
|
|
14872
15012
|
current = current[segment2];
|
|
14873
15013
|
}
|
|
@@ -15382,19 +15522,19 @@ var LLMConfigStore = class {
|
|
|
15382
15522
|
|
|
15383
15523
|
// ../../packages/node-runtime/src/ai/file-config-storage.ts
|
|
15384
15524
|
import * as fs28 from "fs";
|
|
15385
|
-
import * as
|
|
15525
|
+
import * as path22 from "path";
|
|
15386
15526
|
function createFileConfigStorage(baseDir) {
|
|
15387
15527
|
return {
|
|
15388
15528
|
readJson(key) {
|
|
15389
15529
|
try {
|
|
15390
|
-
return JSON.parse(fs28.readFileSync(
|
|
15530
|
+
return JSON.parse(fs28.readFileSync(path22.join(baseDir, `${key}.json`), "utf-8"));
|
|
15391
15531
|
} catch {
|
|
15392
15532
|
return null;
|
|
15393
15533
|
}
|
|
15394
15534
|
},
|
|
15395
15535
|
writeJson(key, data) {
|
|
15396
15536
|
fs28.mkdirSync(baseDir, { recursive: true });
|
|
15397
|
-
fs28.writeFileSync(
|
|
15537
|
+
fs28.writeFileSync(path22.join(baseDir, `${key}.json`), JSON.stringify(data, null, 2), "utf-8");
|
|
15398
15538
|
}
|
|
15399
15539
|
};
|
|
15400
15540
|
}
|
|
@@ -16133,7 +16273,7 @@ function exportAsJson(db, messages, sessionName, sourceSessionId) {
|
|
|
16133
16273
|
).all();
|
|
16134
16274
|
const data = {
|
|
16135
16275
|
chatlab: {
|
|
16136
|
-
version:
|
|
16276
|
+
version: CHATLAB_FORMAT_VERSION,
|
|
16137
16277
|
exportedAt: Math.floor(Date.now() / 1e3),
|
|
16138
16278
|
generator: "ChatLab"
|
|
16139
16279
|
},
|
|
@@ -16243,7 +16383,7 @@ function exportWithFormat(params, openDatabase) {
|
|
|
16243
16383
|
|
|
16244
16384
|
// ../../packages/node-runtime/src/merger/temp-db.ts
|
|
16245
16385
|
import * as fs29 from "fs";
|
|
16246
|
-
import * as
|
|
16386
|
+
import * as path23 from "path";
|
|
16247
16387
|
var TEMP_DB_SCHEMA = `
|
|
16248
16388
|
CREATE TABLE IF NOT EXISTS meta (
|
|
16249
16389
|
name TEXT NOT NULL,
|
|
@@ -16458,7 +16598,7 @@ function exportSessionToJson(db) {
|
|
|
16458
16598
|
).all();
|
|
16459
16599
|
return {
|
|
16460
16600
|
chatlab: {
|
|
16461
|
-
version:
|
|
16601
|
+
version: CHATLAB_FORMAT_VERSION,
|
|
16462
16602
|
exportedAt: Math.floor(Date.now() / 1e3),
|
|
16463
16603
|
generator: "ChatLab Export",
|
|
16464
16604
|
description: `Exported from session: ${meta.name}`
|
|
@@ -16502,7 +16642,7 @@ function cleanupTempDatabases(tempDir) {
|
|
|
16502
16642
|
const files = fs29.readdirSync(tempDir);
|
|
16503
16643
|
for (const file of files) {
|
|
16504
16644
|
if (file.startsWith("merge_") && file.endsWith(".db")) {
|
|
16505
|
-
deleteTempDatabase(
|
|
16645
|
+
deleteTempDatabase(path23.join(tempDir, file));
|
|
16506
16646
|
}
|
|
16507
16647
|
}
|
|
16508
16648
|
} catch {
|
|
@@ -16888,7 +17028,7 @@ function clearSessionOwner(adapter, sessionId) {
|
|
|
16888
17028
|
|
|
16889
17029
|
// ../../packages/node-runtime/src/services/contacts/overrides.ts
|
|
16890
17030
|
import fs30 from "fs";
|
|
16891
|
-
import
|
|
17031
|
+
import path24 from "path";
|
|
16892
17032
|
var CONTACT_OVERRIDES_FILE_NAME = "contact-overrides.json";
|
|
16893
17033
|
var CONTACT_OVERRIDES_TMP_PREFIX = "contact-overrides.tmp-";
|
|
16894
17034
|
var CONTACT_OVERRIDES_VERSION = 1;
|
|
@@ -16899,7 +17039,7 @@ function createEmptyContactOverridesFile() {
|
|
|
16899
17039
|
};
|
|
16900
17040
|
}
|
|
16901
17041
|
function getContactOverridesPath(snapshotDir) {
|
|
16902
|
-
return
|
|
17042
|
+
return path24.join(snapshotDir, CONTACT_OVERRIDES_FILE_NAME);
|
|
16903
17043
|
}
|
|
16904
17044
|
function readContactOverrides(snapshotDir) {
|
|
16905
17045
|
const filePath = getContactOverridesPath(snapshotDir);
|
|
@@ -16913,16 +17053,16 @@ function readContactOverrides(snapshotDir) {
|
|
|
16913
17053
|
}
|
|
16914
17054
|
function writeContactOverrides(snapshotDir, overrides) {
|
|
16915
17055
|
if (!fs30.existsSync(snapshotDir)) fs30.mkdirSync(snapshotDir, { recursive: true });
|
|
16916
|
-
const tmpPath =
|
|
17056
|
+
const tmpPath = path24.join(snapshotDir, `${CONTACT_OVERRIDES_TMP_PREFIX}${process.pid}-${Date.now()}`);
|
|
16917
17057
|
fs30.writeFileSync(tmpPath, JSON.stringify(normalizeContactOverrides(overrides), null, 2), "utf-8");
|
|
16918
17058
|
fs30.renameSync(tmpPath, getContactOverridesPath(snapshotDir));
|
|
16919
17059
|
}
|
|
16920
17060
|
function normalizeContactOverrides(value) {
|
|
16921
|
-
const input =
|
|
16922
|
-
const manualFriendsInput =
|
|
17061
|
+
const input = isRecord3(value) ? value : {};
|
|
17062
|
+
const manualFriendsInput = isRecord3(input.manualFriends) ? input.manualFriends : {};
|
|
16923
17063
|
const manualFriends = {};
|
|
16924
17064
|
for (const [key, entry] of Object.entries(manualFriendsInput)) {
|
|
16925
|
-
if (!key || !
|
|
17065
|
+
if (!key || !isRecord3(entry)) continue;
|
|
16926
17066
|
const createdAt = Number.isFinite(entry.createdAt) ? Number(entry.createdAt) : Date.now();
|
|
16927
17067
|
const updatedAt = Number.isFinite(entry.updatedAt) ? Number(entry.updatedAt) : createdAt;
|
|
16928
17068
|
manualFriends[key] = { key, createdAt, updatedAt };
|
|
@@ -16932,7 +17072,7 @@ function normalizeContactOverrides(value) {
|
|
|
16932
17072
|
manualFriends
|
|
16933
17073
|
};
|
|
16934
17074
|
}
|
|
16935
|
-
function
|
|
17075
|
+
function isRecord3(value) {
|
|
16936
17076
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
16937
17077
|
}
|
|
16938
17078
|
|
|
@@ -16951,11 +17091,11 @@ function buildContactsSignature(adapter, timeRangePreset) {
|
|
|
16951
17091
|
|
|
16952
17092
|
// ../../packages/node-runtime/src/services/contacts/snapshot.ts
|
|
16953
17093
|
import fs31 from "fs";
|
|
16954
|
-
import
|
|
17094
|
+
import path25 from "path";
|
|
16955
17095
|
var CONTACTS_SNAPSHOT_TMP_PREFIX = "contacts-snapshot.tmp-";
|
|
16956
17096
|
function getContactsSnapshotPath(snapshotDir, timeRangePreset) {
|
|
16957
17097
|
const preset = normalizeContactsTimeRangePreset(timeRangePreset);
|
|
16958
|
-
return
|
|
17098
|
+
return path25.join(snapshotDir, `contacts-snapshot-${preset}.json`);
|
|
16959
17099
|
}
|
|
16960
17100
|
function readContactsSnapshot(snapshotDir, timeRangePreset, options2 = {}) {
|
|
16961
17101
|
const snapshotPath = getContactsSnapshotPath(snapshotDir, timeRangePreset);
|
|
@@ -16964,7 +17104,7 @@ function readContactsSnapshot(snapshotDir, timeRangePreset, options2 = {}) {
|
|
|
16964
17104
|
return JSON.parse(fs31.readFileSync(snapshotPath, "utf-8"));
|
|
16965
17105
|
} catch (error) {
|
|
16966
17106
|
const ts = options2.now?.() ?? Date.now();
|
|
16967
|
-
const backupPath =
|
|
17107
|
+
const backupPath = path25.join(snapshotDir, `contacts-snapshot.corrupt-${ts}.json`);
|
|
16968
17108
|
try {
|
|
16969
17109
|
fs31.renameSync(snapshotPath, backupPath);
|
|
16970
17110
|
} catch (renameError) {
|
|
@@ -16976,7 +17116,7 @@ function readContactsSnapshot(snapshotDir, timeRangePreset, options2 = {}) {
|
|
|
16976
17116
|
}
|
|
16977
17117
|
function writeContactsSnapshot(snapshotDir, snapshot) {
|
|
16978
17118
|
if (!fs31.existsSync(snapshotDir)) fs31.mkdirSync(snapshotDir, { recursive: true });
|
|
16979
|
-
const tmpPath =
|
|
17119
|
+
const tmpPath = path25.join(snapshotDir, `${CONTACTS_SNAPSHOT_TMP_PREFIX}${process.pid}-${Date.now()}`);
|
|
16980
17120
|
fs31.writeFileSync(tmpPath, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
16981
17121
|
fs31.renameSync(tmpPath, getContactsSnapshotPath(snapshotDir, snapshot.timeRange.preset));
|
|
16982
17122
|
}
|
|
@@ -16985,7 +17125,7 @@ function cleanupContactsSnapshotTempFiles(snapshotDir) {
|
|
|
16985
17125
|
for (const name of fs31.readdirSync(snapshotDir)) {
|
|
16986
17126
|
if (!name.startsWith(CONTACTS_SNAPSHOT_TMP_PREFIX)) continue;
|
|
16987
17127
|
try {
|
|
16988
|
-
fs31.rmSync(
|
|
17128
|
+
fs31.rmSync(path25.join(snapshotDir, name), { force: true });
|
|
16989
17129
|
} catch (error) {
|
|
16990
17130
|
appLogger.warn("contacts", "failed to remove contacts snapshot temp file", error);
|
|
16991
17131
|
}
|
|
@@ -17484,11 +17624,11 @@ function buildPeopleRelationshipsSignature(adapter, timeRangePreset) {
|
|
|
17484
17624
|
|
|
17485
17625
|
// ../../packages/node-runtime/src/services/people/relationships/snapshot.ts
|
|
17486
17626
|
import fs32 from "fs";
|
|
17487
|
-
import
|
|
17627
|
+
import path26 from "path";
|
|
17488
17628
|
var PEOPLE_RELATIONSHIPS_SNAPSHOT_TMP_PREFIX = "people-relationships-snapshot.tmp-";
|
|
17489
17629
|
function getPeopleRelationshipsSnapshotPath(snapshotDir, timeRangePreset) {
|
|
17490
17630
|
const preset = normalizePeopleRelationshipsTimeRangePreset(timeRangePreset);
|
|
17491
|
-
return
|
|
17631
|
+
return path26.join(snapshotDir, `graph-snapshot-${preset}.json`);
|
|
17492
17632
|
}
|
|
17493
17633
|
function readPeopleRelationshipsSnapshot(snapshotDir, timeRangePreset, options2 = {}) {
|
|
17494
17634
|
const snapshotPath = getPeopleRelationshipsSnapshotPath(snapshotDir, timeRangePreset);
|
|
@@ -17497,7 +17637,7 @@ function readPeopleRelationshipsSnapshot(snapshotDir, timeRangePreset, options2
|
|
|
17497
17637
|
return JSON.parse(fs32.readFileSync(snapshotPath, "utf-8"));
|
|
17498
17638
|
} catch (error) {
|
|
17499
17639
|
const ts = options2.now?.() ?? Date.now();
|
|
17500
|
-
const backupPath =
|
|
17640
|
+
const backupPath = path26.join(snapshotDir, `graph-snapshot.corrupt-${ts}.json`);
|
|
17501
17641
|
try {
|
|
17502
17642
|
fs32.renameSync(snapshotPath, backupPath);
|
|
17503
17643
|
} catch (renameError) {
|
|
@@ -17509,7 +17649,7 @@ function readPeopleRelationshipsSnapshot(snapshotDir, timeRangePreset, options2
|
|
|
17509
17649
|
}
|
|
17510
17650
|
function writePeopleRelationshipsSnapshot(snapshotDir, snapshot) {
|
|
17511
17651
|
if (!fs32.existsSync(snapshotDir)) fs32.mkdirSync(snapshotDir, { recursive: true });
|
|
17512
|
-
const tmpPath =
|
|
17652
|
+
const tmpPath = path26.join(snapshotDir, `${PEOPLE_RELATIONSHIPS_SNAPSHOT_TMP_PREFIX}${process.pid}-${Date.now()}`);
|
|
17513
17653
|
fs32.writeFileSync(tmpPath, JSON.stringify(snapshot, null, 2), "utf-8");
|
|
17514
17654
|
fs32.renameSync(tmpPath, getPeopleRelationshipsSnapshotPath(snapshotDir, snapshot.timeRange.preset));
|
|
17515
17655
|
}
|
|
@@ -17518,7 +17658,7 @@ function cleanupPeopleRelationshipsSnapshotTempFiles(snapshotDir) {
|
|
|
17518
17658
|
for (const name of fs32.readdirSync(snapshotDir)) {
|
|
17519
17659
|
if (!name.startsWith(PEOPLE_RELATIONSHIPS_SNAPSHOT_TMP_PREFIX)) continue;
|
|
17520
17660
|
try {
|
|
17521
|
-
fs32.rmSync(
|
|
17661
|
+
fs32.rmSync(path26.join(snapshotDir, name), { force: true });
|
|
17522
17662
|
} catch (error) {
|
|
17523
17663
|
appLogger.warn("people-relationships", "failed to remove people relationships snapshot temp file", error);
|
|
17524
17664
|
}
|
|
@@ -18342,7 +18482,7 @@ __export(global_insight_exports, {
|
|
|
18342
18482
|
|
|
18343
18483
|
// ../../packages/node-runtime/src/semantic-index/config.ts
|
|
18344
18484
|
import fs34 from "fs";
|
|
18345
|
-
import
|
|
18485
|
+
import path27 from "path";
|
|
18346
18486
|
var DEFAULT_SEMANTIC_INDEX_MODEL_DOWNLOAD_SOURCE = "huggingface";
|
|
18347
18487
|
var SEMANTIC_INDEX_CONFIG_VERSION = 1;
|
|
18348
18488
|
var SEARCH_MAX_RESULTS_DEFAULT = 10;
|
|
@@ -18441,7 +18581,7 @@ var SemanticIndexConfigStore = class {
|
|
|
18441
18581
|
}
|
|
18442
18582
|
set(config) {
|
|
18443
18583
|
const normalized = normalizeConfig(config);
|
|
18444
|
-
const dir =
|
|
18584
|
+
const dir = path27.dirname(this.filePath);
|
|
18445
18585
|
if (!fs34.existsSync(dir)) fs34.mkdirSync(dir, { recursive: true });
|
|
18446
18586
|
fs34.writeFileSync(this.filePath, JSON.stringify(normalized, null, 2), "utf-8");
|
|
18447
18587
|
return normalized;
|
|
@@ -18452,7 +18592,7 @@ var SemanticIndexConfigStore = class {
|
|
|
18452
18592
|
};
|
|
18453
18593
|
|
|
18454
18594
|
// ../../packages/node-runtime/src/semantic-index/service.ts
|
|
18455
|
-
import
|
|
18595
|
+
import path28 from "path";
|
|
18456
18596
|
|
|
18457
18597
|
// ../../packages/node-runtime/src/semantic-index/chunker-config.ts
|
|
18458
18598
|
import { createHash as createHash2 } from "crypto";
|
|
@@ -19983,16 +20123,16 @@ function resolveSearchToolEvidenceTokens(maxResultTokens) {
|
|
|
19983
20123
|
return maxResultTokens && maxResultTokens > 0 ? Math.min(SEARCH_TOOL_EVIDENCE_TOKENS, maxResultTokens) : SEARCH_TOOL_EVIDENCE_TOKENS;
|
|
19984
20124
|
}
|
|
19985
20125
|
function sessionIdFromDbPath(dbPath) {
|
|
19986
|
-
return
|
|
20126
|
+
return path28.basename(dbPath, ".db");
|
|
19987
20127
|
}
|
|
19988
20128
|
var SEMANTIC_INDEX_DB_FILE = "embedding_index.db";
|
|
19989
20129
|
var SEMANTIC_INDEX_CONFIG_FILE = "semantic-index-config.json";
|
|
19990
20130
|
function createSemanticIndexService(params) {
|
|
19991
20131
|
const { pathProvider } = params;
|
|
19992
20132
|
return new SemanticIndexService({
|
|
19993
|
-
vectorDbPath:
|
|
19994
|
-
configPath:
|
|
19995
|
-
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"),
|
|
19996
20136
|
modelDownloadProxyUrl: params.modelDownloadProxyUrl,
|
|
19997
20137
|
sessionAdapter: params.sessionAdapter,
|
|
19998
20138
|
nativeBinding: params.nativeBinding,
|
|
@@ -20543,7 +20683,7 @@ ${text}`;
|
|
|
20543
20683
|
|
|
20544
20684
|
// ../../packages/node-runtime/src/services/merge-cache.ts
|
|
20545
20685
|
import * as fs35 from "fs";
|
|
20546
|
-
import * as
|
|
20686
|
+
import * as path29 from "path";
|
|
20547
20687
|
import * as crypto from "crypto";
|
|
20548
20688
|
var MergeSessionCache = class {
|
|
20549
20689
|
cache = /* @__PURE__ */ new Map();
|
|
@@ -20560,7 +20700,7 @@ var MergeSessionCache = class {
|
|
|
20560
20700
|
const ts = Date.now();
|
|
20561
20701
|
const rand = Math.random().toString(36).slice(2, 8);
|
|
20562
20702
|
const safeName = filename.replace(/[/\\?%*:|"<>]/g, "_").slice(0, 40);
|
|
20563
|
-
return
|
|
20703
|
+
return path29.join(this.tempDir, `merge_${safeName}_${ts}_${rand}.db`);
|
|
20564
20704
|
}
|
|
20565
20705
|
/** Store a parsed file's temp DB and return a handle for subsequent requests. */
|
|
20566
20706
|
store(filename, tempDbPath) {
|
|
@@ -20619,23 +20759,23 @@ var MergeSessionCache = class {
|
|
|
20619
20759
|
import * as fs36 from "fs";
|
|
20620
20760
|
var SYSTEM_SENDER_ID = "SYSTEM";
|
|
20621
20761
|
var SYSTEM_MEMBER_NAME = "\u7CFB\u7EDF\u6D88\u606F";
|
|
20622
|
-
function
|
|
20762
|
+
function isRecord4(value) {
|
|
20623
20763
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
20624
20764
|
}
|
|
20625
|
-
function
|
|
20765
|
+
function validateOptionalString2(value, field) {
|
|
20626
20766
|
return value !== void 0 && typeof value !== "string" ? `${field} must be a string` : null;
|
|
20627
20767
|
}
|
|
20628
20768
|
function validatePayload(payload, isNew) {
|
|
20629
|
-
if (!
|
|
20769
|
+
if (!isRecord4(payload)) return "payload must be an object";
|
|
20630
20770
|
const messages = payload.messages;
|
|
20631
20771
|
if (!Array.isArray(messages) || messages.length === 0) return "messages is required and must be a non-empty array";
|
|
20632
20772
|
if (payload.chatlab !== void 0) {
|
|
20633
|
-
if (!
|
|
20773
|
+
if (!isRecord4(payload.chatlab)) return "chatlab must be an object";
|
|
20634
20774
|
if (typeof payload.chatlab.version !== "string" || payload.chatlab.version.length === 0)
|
|
20635
20775
|
return "chatlab.version must be a string";
|
|
20636
20776
|
if (typeof payload.chatlab.exportedAt !== "number" || !Number.isFinite(payload.chatlab.exportedAt))
|
|
20637
20777
|
return "chatlab.exportedAt must be a number";
|
|
20638
|
-
const generatorError =
|
|
20778
|
+
const generatorError = validateOptionalString2(payload.chatlab.generator, "chatlab.generator");
|
|
20639
20779
|
if (generatorError) return generatorError;
|
|
20640
20780
|
}
|
|
20641
20781
|
if (isNew) {
|
|
@@ -20643,17 +20783,17 @@ function validatePayload(payload, isNew) {
|
|
|
20643
20783
|
if (!payload.meta) return "meta is required for new sessions";
|
|
20644
20784
|
}
|
|
20645
20785
|
if (payload.meta !== void 0) {
|
|
20646
|
-
if (!
|
|
20786
|
+
if (!isRecord4(payload.meta)) return "meta must be an object";
|
|
20647
20787
|
const metaFields = ["name", "platform", "type", "groupId", "groupAvatar", "ownerId"];
|
|
20648
20788
|
for (const field of metaFields) {
|
|
20649
|
-
const error =
|
|
20789
|
+
const error = validateOptionalString2(payload.meta[field], `meta.${field}`);
|
|
20650
20790
|
if (error) return error;
|
|
20651
20791
|
}
|
|
20652
20792
|
if (isNew && !payload.meta.name) return "meta.name is required";
|
|
20653
20793
|
if (isNew && !payload.meta.platform) return "meta.platform is required";
|
|
20654
20794
|
if (isNew && !payload.meta.type) return "meta.type is required";
|
|
20655
20795
|
}
|
|
20656
|
-
if (payload.options !== void 0 && !
|
|
20796
|
+
if (payload.options !== void 0 && !isRecord4(payload.options)) return "options must be an object";
|
|
20657
20797
|
const { metaUpdateMode, memberUpdateMode } = payload.options ?? {};
|
|
20658
20798
|
if (metaUpdateMode !== void 0 && metaUpdateMode !== "patch" && metaUpdateMode !== "none") {
|
|
20659
20799
|
return `options.metaUpdateMode must be 'patch' or 'none'`;
|
|
@@ -20665,18 +20805,18 @@ function validatePayload(payload, isNew) {
|
|
|
20665
20805
|
if (payload.members) {
|
|
20666
20806
|
for (let i = 0; i < payload.members.length; i++) {
|
|
20667
20807
|
const member = payload.members[i];
|
|
20668
|
-
if (!
|
|
20808
|
+
if (!isRecord4(member)) return `members[${i}] must be an object`;
|
|
20669
20809
|
if (typeof member.platformId !== "string" || member.platformId.length === 0)
|
|
20670
20810
|
return `members[${i}].platformId must be a string`;
|
|
20671
20811
|
for (const field of ["accountName", "groupNickname", "avatar"]) {
|
|
20672
|
-
const error =
|
|
20812
|
+
const error = validateOptionalString2(member[field], `members[${i}].${field}`);
|
|
20673
20813
|
if (error) return error;
|
|
20674
20814
|
}
|
|
20675
20815
|
if (member.roles !== void 0) {
|
|
20676
20816
|
if (!Array.isArray(member.roles)) return `members[${i}].roles must be an array`;
|
|
20677
20817
|
for (let j = 0; j < member.roles.length; j++) {
|
|
20678
20818
|
const role = member.roles[j];
|
|
20679
|
-
if (!
|
|
20819
|
+
if (!isRecord4(role) || typeof role.id !== "string" || role.id.length === 0)
|
|
20680
20820
|
return `members[${i}].roles[${j}].id must be a string`;
|
|
20681
20821
|
}
|
|
20682
20822
|
}
|
|
@@ -20684,13 +20824,13 @@ function validatePayload(payload, isNew) {
|
|
|
20684
20824
|
}
|
|
20685
20825
|
for (let i = 0; i < messages.length; i++) {
|
|
20686
20826
|
const msg = messages[i];
|
|
20687
|
-
if (!
|
|
20827
|
+
if (!isRecord4(msg)) return `messages[${i}] must be an object`;
|
|
20688
20828
|
if (typeof msg.sender !== "string" || msg.sender.length === 0) return `messages[${i}].sender must be a string`;
|
|
20689
20829
|
if (typeof msg.timestamp !== "number" || !Number.isFinite(msg.timestamp) || msg.timestamp <= 0)
|
|
20690
20830
|
return `messages[${i}].timestamp must be a positive number`;
|
|
20691
20831
|
if (typeof msg.type !== "number" || !Number.isFinite(msg.type)) return `messages[${i}].type must be a number`;
|
|
20692
20832
|
for (const field of ["accountName", "groupNickname"]) {
|
|
20693
|
-
const error =
|
|
20833
|
+
const error = validateOptionalString2(msg[field], `messages[${i}].${field}`);
|
|
20694
20834
|
if (error) return error;
|
|
20695
20835
|
}
|
|
20696
20836
|
if (msg.content !== void 0 && msg.content !== null && typeof msg.content !== "string")
|
|
@@ -21089,7 +21229,7 @@ export {
|
|
|
21089
21229
|
getChatLabTempScopeDir,
|
|
21090
21230
|
createChatLabTempDir,
|
|
21091
21231
|
removeChatLabTempDir,
|
|
21092
|
-
|
|
21232
|
+
validateChatLabFile,
|
|
21093
21233
|
detectFormat,
|
|
21094
21234
|
detectAllFormats,
|
|
21095
21235
|
getSupportedFormats,
|
|
@@ -21309,4 +21449,4 @@ export {
|
|
|
21309
21449
|
hashImportBody,
|
|
21310
21450
|
ImportIdempotencyCache
|
|
21311
21451
|
};
|
|
21312
|
-
//# sourceMappingURL=chunk-
|
|
21452
|
+
//# sourceMappingURL=chunk-KWJ5FZ74.mjs.map
|