chatlab-cli 0.34.3 → 0.35.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/dist/{chat-command-QH7DXCAJ.mjs → chat-command-24RYGJAS.mjs} +8 -8
- package/dist/{chunk-2WYXD5ZP.mjs → chunk-22YJOBA3.mjs} +474 -99
- package/dist/chunk-22YJOBA3.mjs.map +1 -0
- package/dist/{chunk-AWXHYTD5.mjs → chunk-4GWWKHWA.mjs} +42 -14
- package/dist/chunk-4GWWKHWA.mjs.map +1 -0
- package/dist/{chunk-ZVGDOSGG.mjs → chunk-7IGBPZMI.mjs} +38 -18
- package/dist/chunk-7IGBPZMI.mjs.map +1 -0
- package/dist/{chunk-WJVE5Q4Y.mjs → chunk-BRZ6YIBR.mjs} +2 -2
- package/dist/{chunk-XIWVJAXH.mjs → chunk-DC7IUDNV.mjs} +10 -18
- package/dist/chunk-DC7IUDNV.mjs.map +1 -0
- package/dist/{chunk-JYBPJWE2.mjs → chunk-DEFEU567.mjs} +10 -18
- package/dist/chunk-DEFEU567.mjs.map +1 -0
- package/dist/{chunk-C2PD3BS7.mjs → chunk-GQMCDWZQ.mjs} +2 -2
- package/dist/{chunk-AWGXYFIC.mjs → chunk-KIMJXKZO.mjs} +2 -2
- package/dist/{chunk-SWUTRDGZ.mjs → chunk-KXDJ7N6N.mjs} +2 -2
- package/dist/{chunk-3X26JJIP.mjs → chunk-QKEARRFH.mjs} +3 -3
- package/dist/{chunk-6U2QZO3N.mjs → chunk-U36TQMAF.mjs} +2 -2
- package/dist/{chunk-2Q3HHVTS.mjs → chunk-ZBPPLXM4.mjs} +12 -12
- package/dist/cli.mjs +9 -9
- package/dist/contacts-worker.mjs +2 -2
- package/dist/global-insight-worker.mjs +1 -1
- package/dist/{http-D2F4QTQW.mjs → http-3K4YMWDE.mjs} +12 -12
- package/dist/{import-H7X2JU2O.mjs → import-D4LV565A.mjs} +7 -7
- package/dist/index.mjs +12 -12
- package/dist/{mcp-UURXRGDF.mjs → mcp-KL22FKQ2.mjs} +8 -8
- package/dist/people-relationships-worker.mjs +2 -2
- package/dist/semantic-index-worker.mjs +1 -1
- package/dist/{src-MHCO4HYY.mjs → src-COIB6Z4P.mjs} +8 -6
- package/dist/{stream-import-BHXACUCA.mjs → stream-import-FSXCFG4M.mjs} +7 -7
- package/dist/{worker-runtime-JOUCP2L4.mjs → worker-runtime-DPS2LURI.mjs} +5 -5
- package/dist-cli-web/assets/AISettingsTab-rQC2ocFH.js +1 -0
- package/dist-cli-web/assets/AboutTab-DMPziKjj.js +1 -0
- package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-CnRLt_1j.js +110 -0
- package/dist-cli-web/assets/Alert--bTQXRcZ.js +1 -0
- package/dist-cli-web/assets/ApiSettingsTab-CtXl4I28.js +2 -0
- package/dist-cli-web/assets/BatchManageTab-1KaCPweN.js +1 -0
- package/dist-cli-web/assets/{BufferResource-Dak23r3B.js → BufferResource-BXSJADxx.js} +1 -1
- package/dist-cli-web/assets/CanvasRenderer-Cq--8UQ_.js +1 -0
- package/dist-cli-web/assets/ChatRecordDrawer-BC0F7ok1.js +11 -0
- package/dist-cli-web/assets/ChatRecordDrawer-Tt6UduRC.css +1 -0
- package/dist-cli-web/assets/Checkbox-CwajNPGF.js +1 -0
- package/dist-cli-web/assets/DataDirCleanupNotice-D0Oruka2.js +1 -0
- package/dist-cli-web/assets/DatePicker.vue_vue_type_script_setup_true_lang-C4CsoIlq.js +1 -0
- package/dist-cli-web/assets/DebugToolsPanel-DVDwS-wz.js +1 -0
- package/dist-cli-web/assets/EChartWordcloud-KV0MdsDE.js +1 -0
- package/dist-cli-web/assets/{Filter-CHKZVYB3.js → Filter-DVaqcFmI.js} +1 -1
- package/dist-cli-web/assets/GlobalTaskBar-COW_uBX6.css +1 -0
- package/dist-cli-web/assets/GlobalTaskBar-DpPMX0jx.js +1 -0
- package/dist-cli-web/assets/GroupRelationshipGalaxyView-CLnO3phW.js +1 -0
- package/dist-cli-web/assets/Label-Bt2U4lGy.js +1 -0
- package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-Cnshyn0f.js +1 -0
- package/dist-cli-web/assets/MessageExportModal-BozaR9UZ.js +1 -0
- package/dist-cli-web/assets/PageHeader-CEe0xTl3.js +1 -0
- package/dist-cli-web/assets/PageTabs.vue_vue_type_script_setup_true_lang-D2DZrrnv.js +1 -0
- package/dist-cli-web/assets/{RelationshipGalaxyCanvas-mJhEPzsY.js → RelationshipGalaxyCanvas-6EamagxG.js} +3 -3
- package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-Bf76yzmQ.js → RelationshipGalaxyThreeCanvas-B6GB4PWS.js} +166 -166
- package/dist-cli-web/assets/{RelationshipGalaxyThreeCanvas-CMuibfiA.css → RelationshipGalaxyThreeCanvas-TYGOUDiv.css} +1 -1
- package/dist-cli-web/assets/{RenderTargetSystem-1rM7OF-M.js → RenderTargetSystem-mYuj4uBS.js} +1 -1
- package/dist-cli-web/assets/RovingFocusItem-B2y6BG1a.js +1 -0
- package/dist-cli-web/assets/ScreenCaptureModal-BvAtSNNR.js +1 -0
- package/dist-cli-web/assets/SectionTabs.vue_vue_type_script_setup_true_lang-ueEF4zHE.js +1 -0
- package/dist-cli-web/assets/Select-Cm4I8u9n.js +1 -0
- package/dist-cli-web/assets/SettingsModal-CjVax3I6.css +1 -0
- package/dist-cli-web/assets/SettingsModal-Cwshzj7i.js +2 -0
- package/dist-cli-web/assets/StorageTab-D7HzH16Y.js +1 -0
- package/dist-cli-web/assets/Switch-BVw4e_19.js +1 -0
- package/dist-cli-web/assets/Tabs-Dqwkh3Ea.js +1 -0
- package/dist-cli-web/assets/Tabs-OrJkANIA.js +1 -0
- package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-DPIvbq75.js +1 -0
- package/dist-cli-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-dEdo6CqM.js +1 -0
- package/dist-cli-web/assets/VisuallyHiddenInput-BOdkR_GC.js +1 -0
- package/dist-cli-web/assets/{WebGLRenderer-Y0jQtBvK.js → WebGLRenderer-BB8ukBI_.js} +2 -2
- package/dist-cli-web/assets/{WebGPURenderer-BcUvCPhd.js → WebGPURenderer-DDOzF1Kc.js} +2 -2
- package/dist-cli-web/assets/aiChat-CzFSFuOJ.js +8 -0
- package/dist-cli-web/assets/annual-summary-time-range-CmrAqcuj.js +1 -0
- package/dist-cli-web/assets/base-CawUl6ET.js +1 -0
- package/dist-cli-web/assets/{browserAll-BsR1UaCC.js → browserAll-BRqlnNxq.js} +2 -2
- package/dist-cli-web/assets/chatlabSiteLocale-CrCNb8QP.js +1 -0
- package/dist-cli-web/assets/clamp-jFYhBC76.js +1 -0
- package/dist-cli-web/assets/cli-web-ClAJ6Tao.js +1 -0
- package/dist-cli-web/assets/{dateFormat-B6JnNCxp.js → dateFormat-DXuDxIye.js} +1 -1
- package/dist-cli-web/assets/fetch-3_xX8Yi8.js +1 -0
- package/dist-cli-web/assets/{fetch-DGvqctIb.js → fetch-Bcy2T8Fe.js} +1 -1
- package/dist-cli-web/assets/{fetch-BAJ_VjWE.js → fetch-CGu53cxT.js} +1 -1
- package/dist-cli-web/assets/{fetch-bdY0RMM3.js → fetch-CP1afnT0.js} +1 -1
- package/dist-cli-web/assets/{fetch-Clj_RVUD.js → fetch-DWAIk_6_.js} +1 -1
- package/dist-cli-web/assets/{fetch-DtnhbqTN.js → fetch-DijSOvop.js} +1 -1
- package/dist-cli-web/assets/{fetch-D7vgaLpy.js → fetch-Dy38lkAa.js} +1 -1
- package/dist-cli-web/assets/{fetch-bhHY8rO7.js → fetch-DzKlTaLz.js} +1 -1
- package/dist-cli-web/assets/{fetch-CPloHijF.js → fetch-LY-yerw1.js} +1 -1
- package/dist-cli-web/assets/{fetch-BNcHe2-O.js → fetch-q480XYZQ.js} +1 -1
- package/dist-cli-web/assets/index-0dp58uL4.css +1 -0
- package/dist-cli-web/assets/index-1X7zdLgJ.css +1 -0
- package/dist-cli-web/assets/index-4enwdBcq.css +1 -0
- package/dist-cli-web/assets/index-AudHHbMs.js +1 -0
- package/dist-cli-web/assets/{index-DMYFpctK.js → index-BRV_D8dv.js} +3 -3
- package/dist-cli-web/assets/index-CF4DTbvD.js +1 -0
- package/dist-cli-web/assets/index-CMLoht5R.js +1 -0
- package/dist-cli-web/assets/index-CMPHZH6B.css +1 -0
- package/dist-cli-web/assets/index-CNzv7aBW.js +1 -0
- package/dist-cli-web/assets/index-CXUTiy-V.js +1 -0
- package/dist-cli-web/assets/index-D5m0P4ss.js +36 -0
- package/dist-cli-web/assets/index-Di6FmCdk.js +64 -0
- package/dist-cli-web/assets/index-DkOrt7_X.js +2 -0
- package/dist-cli-web/assets/index-Dlb6fC68.js +1 -0
- package/dist-cli-web/assets/index-IA7YUjQX.js +2 -0
- package/dist-cli-web/assets/index-kzU381y1.js +1 -0
- package/dist-cli-web/assets/index-z8Vogc-b.js +137 -0
- package/dist-cli-web/assets/lazy-overlay-visibility-Bd9ZFOgR.js +1 -0
- package/dist-cli-web/assets/member-select-utils-BXZ3oHct.js +1 -0
- package/dist-cli-web/assets/people-page-header-Bh3KXD4g.js +1 -0
- package/dist-cli-web/assets/prompt-DcAcgQiB.js +1 -0
- package/dist-cli-web/assets/relationship-galaxy-connections-Cg4G5yYE.js +1 -0
- package/dist-cli-web/assets/semantic-index-config-builder-CK6v64Y1.js +1 -0
- package/dist-cli-web/assets/service-YZnJyEMq.js +1 -0
- package/dist-cli-web/assets/useFormControl-B-aXrJZN.js +1 -0
- package/dist-cli-web/assets/useTable-DPEMaGby.js +1 -0
- package/dist-cli-web/assets/useToast-B36MzFFy.js +1 -0
- package/dist-cli-web/assets/vendor-echarts-DfAMQxdr.js +11 -0
- package/dist-cli-web/assets/{vendor-echarts-wordcloud-Ca8n6qS6.js → vendor-echarts-wordcloud-BsaEJTPY.js} +1 -1
- package/dist-cli-web/assets/vendor-zrender-PFmZe1EY.js +8 -0
- package/dist-cli-web/assets/{webworkerAll-CfZWlukP.js → webworkerAll-Dwqmy_YO.js} +1 -1
- package/dist-cli-web/index.html +2 -2
- package/package.json +1 -1
- package/dist/chunk-2WYXD5ZP.mjs.map +0 -1
- package/dist/chunk-AWXHYTD5.mjs.map +0 -1
- package/dist/chunk-JYBPJWE2.mjs.map +0 -1
- package/dist/chunk-XIWVJAXH.mjs.map +0 -1
- package/dist/chunk-ZVGDOSGG.mjs.map +0 -1
- package/dist-cli-web/assets/ActionToolsPanel.vue_vue_type_script_setup_true_lang-kN3uxTW8.js +0 -110
- package/dist-cli-web/assets/CanvasRenderer-Cmjtvski.js +0 -1
- package/dist-cli-web/assets/DataDirCleanupNotice-B5tyDzK-.js +0 -1
- package/dist-cli-web/assets/EChartWordcloud-Bd3L3G8I.js +0 -1
- package/dist-cli-web/assets/LoadingState.vue_vue_type_script_setup_true_lang-CKf_gC1k.js +0 -1
- package/dist-cli-web/assets/MessageExportModal-ODYCcSnk.js +0 -1
- package/dist-cli-web/assets/PageHeader-Bn7Jn8JW.js +0 -1
- package/dist-cli-web/assets/ThemeCard.vue_vue_type_script_setup_true_lang-CADZpPia.js +0 -1
- package/dist-cli-web/assets/TimeSelect.vue_vue_type_script_setup_true_lang-I3FYjhfs.js +0 -1
- package/dist-cli-web/assets/annual-summary-time-range-BIADd8qE.js +0 -1
- package/dist-cli-web/assets/base-BpxxC5Z5.js +0 -1
- package/dist-cli-web/assets/cli-web-BiWUOncc.js +0 -1
- package/dist-cli-web/assets/fetch-DSxgaVBO.js +0 -1
- package/dist-cli-web/assets/index-B6Gq11lw.css +0 -1
- package/dist-cli-web/assets/index-BJ2lywiG.js +0 -1
- package/dist-cli-web/assets/index-BT3_fSP6.js +0 -2
- package/dist-cli-web/assets/index-BVZhP1J5.js +0 -1
- package/dist-cli-web/assets/index-Br8r4pu8.js +0 -1
- package/dist-cli-web/assets/index-Byok4VaA.css +0 -1
- package/dist-cli-web/assets/index-CKnnxOci.js +0 -1
- package/dist-cli-web/assets/index-CXnYfvTv.js +0 -137
- package/dist-cli-web/assets/index-CsFLLk3T.css +0 -1
- package/dist-cli-web/assets/index-CzoOQ7zg.js +0 -1
- package/dist-cli-web/assets/index-D-dTTUGj.js +0 -1
- package/dist-cli-web/assets/index-DEbvEKzi.js +0 -2
- package/dist-cli-web/assets/index-KO7uPd8W.js +0 -64
- package/dist-cli-web/assets/index-lkmJ6dI7.js +0 -1
- package/dist-cli-web/assets/index-y7xPDpsW.js +0 -53
- package/dist-cli-web/assets/people-page-header-gjTyODWs.js +0 -1
- package/dist-cli-web/assets/vendor-echarts-B-i_56Av.js +0 -11
- package/dist-cli-web/assets/vendor-zrender-5DcYjSZk.js +0 -8
- /package/dist/{chat-command-QH7DXCAJ.mjs.map → chat-command-24RYGJAS.mjs.map} +0 -0
- /package/dist/{chunk-WJVE5Q4Y.mjs.map → chunk-BRZ6YIBR.mjs.map} +0 -0
- /package/dist/{chunk-C2PD3BS7.mjs.map → chunk-GQMCDWZQ.mjs.map} +0 -0
- /package/dist/{chunk-AWGXYFIC.mjs.map → chunk-KIMJXKZO.mjs.map} +0 -0
- /package/dist/{chunk-SWUTRDGZ.mjs.map → chunk-KXDJ7N6N.mjs.map} +0 -0
- /package/dist/{chunk-3X26JJIP.mjs.map → chunk-QKEARRFH.mjs.map} +0 -0
- /package/dist/{chunk-6U2QZO3N.mjs.map → chunk-U36TQMAF.mjs.map} +0 -0
- /package/dist/{chunk-2Q3HHVTS.mjs.map → chunk-ZBPPLXM4.mjs.map} +0 -0
- /package/dist/{http-D2F4QTQW.mjs.map → http-3K4YMWDE.mjs.map} +0 -0
- /package/dist/{import-H7X2JU2O.mjs.map → import-D4LV565A.mjs.map} +0 -0
- /package/dist/{mcp-UURXRGDF.mjs.map → mcp-KL22FKQ2.mjs.map} +0 -0
- /package/dist/{src-MHCO4HYY.mjs.map → src-COIB6Z4P.mjs.map} +0 -0
- /package/dist/{stream-import-BHXACUCA.mjs.map → stream-import-FSXCFG4M.mjs.map} +0 -0
- /package/dist/{worker-runtime-JOUCP2L4.mjs.map → worker-runtime-DPS2LURI.mjs.map} +0 -0
|
@@ -40,13 +40,17 @@ var cliConfigSchema = z.object({
|
|
|
40
40
|
/** Allow the read-only `sql` query command. */
|
|
41
41
|
allow_sql: z.boolean().default(true)
|
|
42
42
|
});
|
|
43
|
+
var desktopConfigSchema = z.object({
|
|
44
|
+
close_behavior: z.enum(["background", "quit"]).default("background")
|
|
45
|
+
});
|
|
43
46
|
var configSchema = z.object({
|
|
44
47
|
llm: llmConfigSchema.default({}),
|
|
45
48
|
data: dataConfigSchema.default({}),
|
|
46
49
|
api: apiConfigSchema.default({}),
|
|
47
50
|
locale: localeConfigSchema.default({}),
|
|
48
51
|
ui: uiConfigSchema.default({}),
|
|
49
|
-
cli: cliConfigSchema.default({})
|
|
52
|
+
cli: cliConfigSchema.default({}),
|
|
53
|
+
desktop: desktopConfigSchema.default({})
|
|
50
54
|
});
|
|
51
55
|
|
|
52
56
|
// ../../packages/config/src/loader.ts
|
|
@@ -4108,7 +4112,7 @@ function buildMsgConditions(options) {
|
|
|
4108
4112
|
|
|
4109
4113
|
// ../../packages/core/src/query/advanced/social.ts
|
|
4110
4114
|
function getMentionAnalysis(db, filter) {
|
|
4111
|
-
const
|
|
4115
|
+
const emptyResult2 = {
|
|
4112
4116
|
topMentioners: [],
|
|
4113
4117
|
topMentioned: [],
|
|
4114
4118
|
totalMentions: 0
|
|
@@ -4117,7 +4121,7 @@ function getMentionAnalysis(db, filter) {
|
|
|
4117
4121
|
`SELECT id, platform_id as platformId, COALESCE(group_nickname, account_name, platform_id) as name
|
|
4118
4122
|
FROM member WHERE COALESCE(account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'`
|
|
4119
4123
|
).all();
|
|
4120
|
-
if (members.length === 0) return
|
|
4124
|
+
if (members.length === 0) return emptyResult2;
|
|
4121
4125
|
const nameToMemberId = /* @__PURE__ */ new Map();
|
|
4122
4126
|
const memberIdToInfo = /* @__PURE__ */ new Map();
|
|
4123
4127
|
for (const member of members) {
|
|
@@ -4158,7 +4162,7 @@ function getMentionAnalysis(db, filter) {
|
|
|
4158
4162
|
}
|
|
4159
4163
|
}
|
|
4160
4164
|
}
|
|
4161
|
-
if (totalMentions === 0) return
|
|
4165
|
+
if (totalMentions === 0) return emptyResult2;
|
|
4162
4166
|
const topMentioners = [];
|
|
4163
4167
|
for (const [memberId, count] of mentionerCount.entries()) {
|
|
4164
4168
|
const info = memberIdToInfo.get(memberId);
|
|
@@ -4185,87 +4189,13 @@ function getMentionAnalysis(db, filter) {
|
|
|
4185
4189
|
topMentioned.sort((a, b) => b.count - a.count);
|
|
4186
4190
|
return { topMentioners, topMentioned, totalMentions };
|
|
4187
4191
|
}
|
|
4188
|
-
function getMentionGraph(db, filter) {
|
|
4189
|
-
const emptyResult = { nodes: [], links: [], maxLinkValue: 0 };
|
|
4190
|
-
const { clause, params } = buildTimeFilter(filter);
|
|
4191
|
-
const msgFilterBase = clause ? clause.replace("WHERE", "AND") : "";
|
|
4192
|
-
const msgFilterWithSystem = msgFilterBase + " AND COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'";
|
|
4193
|
-
const members = db.prepare(
|
|
4194
|
-
`SELECT m.id, m.platform_id as platformId, COALESCE(m.group_nickname, m.account_name, m.platform_id) as name,
|
|
4195
|
-
COUNT(msg.id) as messageCount
|
|
4196
|
-
FROM member m LEFT JOIN message msg ON m.id = msg.sender_id ${msgFilterWithSystem}
|
|
4197
|
-
WHERE COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F' GROUP BY m.id`
|
|
4198
|
-
).all(...params);
|
|
4199
|
-
if (members.length === 0) return emptyResult;
|
|
4200
|
-
const nameToMemberId = /* @__PURE__ */ new Map();
|
|
4201
|
-
const memberIdToInfo = /* @__PURE__ */ new Map();
|
|
4202
|
-
for (const member of members) {
|
|
4203
|
-
memberIdToInfo.set(member.id, { name: member.name, messageCount: member.messageCount });
|
|
4204
|
-
nameToMemberId.set(member.name, member.id);
|
|
4205
|
-
const history = db.prepare("SELECT name FROM member_name_history WHERE member_id = ?").all(member.id);
|
|
4206
|
-
for (const h of history) {
|
|
4207
|
-
if (!nameToMemberId.has(h.name)) nameToMemberId.set(h.name, member.id);
|
|
4208
|
-
}
|
|
4209
|
-
}
|
|
4210
|
-
let whereClause = clause;
|
|
4211
|
-
if (whereClause.includes("WHERE")) {
|
|
4212
|
-
whereClause += " AND COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'";
|
|
4213
|
-
} else {
|
|
4214
|
-
whereClause = " WHERE COALESCE(m.account_name, '') != '\u7CFB\u7EDF\u6D88\u606F' AND msg.type = 0 AND msg.content IS NOT NULL AND msg.content LIKE '%@%'";
|
|
4215
|
-
}
|
|
4216
|
-
const messages = db.prepare(
|
|
4217
|
-
`SELECT msg.sender_id as senderId, msg.content FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause}`
|
|
4218
|
-
).all(...params);
|
|
4219
|
-
const mentionMatrix = /* @__PURE__ */ new Map();
|
|
4220
|
-
const mentionRegex = /@([^\s@]+)/g;
|
|
4221
|
-
for (const msg of messages) {
|
|
4222
|
-
const matches = msg.content.matchAll(mentionRegex);
|
|
4223
|
-
const mentionedInThisMsg = /* @__PURE__ */ new Set();
|
|
4224
|
-
for (const match of matches) {
|
|
4225
|
-
const mentionedId = nameToMemberId.get(match[1]);
|
|
4226
|
-
if (mentionedId && mentionedId !== msg.senderId && !mentionedInThisMsg.has(mentionedId)) {
|
|
4227
|
-
mentionedInThisMsg.add(mentionedId);
|
|
4228
|
-
if (!mentionMatrix.has(msg.senderId)) mentionMatrix.set(msg.senderId, /* @__PURE__ */ new Map());
|
|
4229
|
-
const fromMap = mentionMatrix.get(msg.senderId);
|
|
4230
|
-
fromMap.set(mentionedId, (fromMap.get(mentionedId) || 0) + 1);
|
|
4231
|
-
}
|
|
4232
|
-
}
|
|
4233
|
-
}
|
|
4234
|
-
const involvedMemberIds = /* @__PURE__ */ new Set();
|
|
4235
|
-
for (const [fromId, toMap] of mentionMatrix.entries()) {
|
|
4236
|
-
involvedMemberIds.add(fromId);
|
|
4237
|
-
for (const toId of toMap.keys()) involvedMemberIds.add(toId);
|
|
4238
|
-
}
|
|
4239
|
-
const maxMessageCount = Math.max(...members.filter((m) => involvedMemberIds.has(m.id)).map((m) => m.messageCount), 1);
|
|
4240
|
-
const nodes = [];
|
|
4241
|
-
for (const memberId of involvedMemberIds) {
|
|
4242
|
-
const info = memberIdToInfo.get(memberId);
|
|
4243
|
-
if (info) {
|
|
4244
|
-
const symbolSize = 20 + info.messageCount / maxMessageCount * 40;
|
|
4245
|
-
nodes.push({ id: memberId, name: info.name, value: info.messageCount, symbolSize: Math.round(symbolSize) });
|
|
4246
|
-
}
|
|
4247
|
-
}
|
|
4248
|
-
const links = [];
|
|
4249
|
-
let maxLinkValue = 0;
|
|
4250
|
-
for (const [fromId, toMap] of mentionMatrix.entries()) {
|
|
4251
|
-
const fromInfo = memberIdToInfo.get(fromId);
|
|
4252
|
-
if (!fromInfo) continue;
|
|
4253
|
-
for (const [toId, count] of toMap.entries()) {
|
|
4254
|
-
const toInfo = memberIdToInfo.get(toId);
|
|
4255
|
-
if (!toInfo) continue;
|
|
4256
|
-
links.push({ source: fromInfo.name, target: toInfo.name, value: count });
|
|
4257
|
-
maxLinkValue = Math.max(maxLinkValue, count);
|
|
4258
|
-
}
|
|
4259
|
-
}
|
|
4260
|
-
return { nodes, links, maxLinkValue };
|
|
4261
|
-
}
|
|
4262
4192
|
function keywordToPattern(keyword) {
|
|
4263
4193
|
const escaped = keyword.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
4264
4194
|
if (keyword === "\u54C8\u54C8") return "\u54C8\u54C8+";
|
|
4265
4195
|
return escaped;
|
|
4266
4196
|
}
|
|
4267
4197
|
function getLaughAnalysis(db, filter, keywords) {
|
|
4268
|
-
const
|
|
4198
|
+
const emptyResult2 = {
|
|
4269
4199
|
rankByRate: [],
|
|
4270
4200
|
rankByCount: [],
|
|
4271
4201
|
typeDistribution: [],
|
|
@@ -4274,7 +4204,7 @@ function getLaughAnalysis(db, filter, keywords) {
|
|
|
4274
4204
|
groupLaughRate: 0
|
|
4275
4205
|
};
|
|
4276
4206
|
const laughKeywords = keywords?.map((keyword) => keyword.trim()).filter(Boolean) ?? [];
|
|
4277
|
-
if (laughKeywords.length === 0) return
|
|
4207
|
+
if (laughKeywords.length === 0) return emptyResult2;
|
|
4278
4208
|
const patterns = laughKeywords.map(keywordToPattern);
|
|
4279
4209
|
const laughRegex = new RegExp(`(${patterns.join("|")})`, "gi");
|
|
4280
4210
|
const { clause, params } = buildTimeFilter(filter);
|
|
@@ -4289,7 +4219,7 @@ function getLaughAnalysis(db, filter, keywords) {
|
|
|
4289
4219
|
COALESCE(m.group_nickname, m.account_name, m.platform_id) as name
|
|
4290
4220
|
FROM message msg JOIN member m ON msg.sender_id = m.id ${whereClause}`
|
|
4291
4221
|
).all(...params);
|
|
4292
|
-
if (messages.length === 0) return
|
|
4222
|
+
if (messages.length === 0) return emptyResult2;
|
|
4293
4223
|
const memberStats = /* @__PURE__ */ new Map();
|
|
4294
4224
|
const typeCount = /* @__PURE__ */ new Map();
|
|
4295
4225
|
let totalLaughs = 0;
|
|
@@ -4322,7 +4252,7 @@ function getLaughAnalysis(db, filter, keywords) {
|
|
|
4322
4252
|
}
|
|
4323
4253
|
}
|
|
4324
4254
|
}
|
|
4325
|
-
if (totalLaughs === 0) return
|
|
4255
|
+
if (totalLaughs === 0) return emptyResult2;
|
|
4326
4256
|
const rankItems = [];
|
|
4327
4257
|
for (const [memberId, stats] of memberStats.entries()) {
|
|
4328
4258
|
if (stats.laughCount > 0) {
|
|
@@ -4412,7 +4342,7 @@ function accumulateCoOccurrencePairs(messages, options) {
|
|
|
4412
4342
|
}
|
|
4413
4343
|
function getClusterGraph(db, filter, options) {
|
|
4414
4344
|
const opts = { ...DEFAULT_CLUSTER_OPTIONS, ...options };
|
|
4415
|
-
const
|
|
4345
|
+
const emptyResult2 = {
|
|
4416
4346
|
nodes: [],
|
|
4417
4347
|
links: [],
|
|
4418
4348
|
maxLinkValue: 0,
|
|
@@ -4424,7 +4354,7 @@ function getClusterGraph(db, filter, options) {
|
|
|
4424
4354
|
(SELECT COUNT(*) FROM message WHERE sender_id = member.id) as messageCount
|
|
4425
4355
|
FROM member WHERE COALESCE(account_name, '') != '\u7CFB\u7EDF\u6D88\u606F'`
|
|
4426
4356
|
).all();
|
|
4427
|
-
if (members.length < 2) return { ...
|
|
4357
|
+
if (members.length < 2) return { ...emptyResult2, stats: { ...emptyResult2.stats, totalMembers: members.length } };
|
|
4428
4358
|
const memberInfo = /* @__PURE__ */ new Map();
|
|
4429
4359
|
for (const m of members)
|
|
4430
4360
|
memberInfo.set(m.id, { name: m.name, platformId: m.platformId, messageCount: m.messageCount });
|
|
@@ -4441,8 +4371,8 @@ function getClusterGraph(db, filter, options) {
|
|
|
4441
4371
|
).all(...params);
|
|
4442
4372
|
if (messages.length < 2)
|
|
4443
4373
|
return {
|
|
4444
|
-
...
|
|
4445
|
-
stats: { ...
|
|
4374
|
+
...emptyResult2,
|
|
4375
|
+
stats: { ...emptyResult2.stats, totalMembers: members.length, totalMessages: messages.length }
|
|
4446
4376
|
};
|
|
4447
4377
|
const memberMsgCount = /* @__PURE__ */ new Map();
|
|
4448
4378
|
for (const msg of messages) memberMsgCount.set(msg.senderId, (memberMsgCount.get(msg.senderId) || 0) + 1);
|
|
@@ -4481,8 +4411,8 @@ function getClusterGraph(db, filter, options) {
|
|
|
4481
4411
|
const keptEdges = edges.slice(0, opts.topEdges);
|
|
4482
4412
|
if (keptEdges.length === 0)
|
|
4483
4413
|
return {
|
|
4484
|
-
...
|
|
4485
|
-
stats: { ...
|
|
4414
|
+
...emptyResult2,
|
|
4415
|
+
stats: { ...emptyResult2.stats, totalMembers: members.length, totalMessages: messages.length }
|
|
4486
4416
|
};
|
|
4487
4417
|
const involvedIds = /* @__PURE__ */ new Set();
|
|
4488
4418
|
for (const edge of keptEdges) {
|
|
@@ -4755,7 +4685,7 @@ function getGroupRelationshipGraphFacts(db, ownerMemberId, options = {}) {
|
|
|
4755
4685
|
ORDER BY msg.ts ASC, msg.id ASC`
|
|
4756
4686
|
).all(...messageTimeFilter.params);
|
|
4757
4687
|
const edgeStats = /* @__PURE__ */ new Map();
|
|
4758
|
-
const
|
|
4688
|
+
const ensureEdge2 = (aId, bId) => {
|
|
4759
4689
|
const sourceId = Math.min(aId, bId);
|
|
4760
4690
|
const targetId = Math.max(aId, bId);
|
|
4761
4691
|
const key = `${sourceId}:${targetId}`;
|
|
@@ -4775,7 +4705,7 @@ function getGroupRelationshipGraphFacts(db, ownerMemberId, options = {}) {
|
|
|
4775
4705
|
};
|
|
4776
4706
|
for (const pair of accumulateCoOccurrencePairs(coOccurrenceRows)) {
|
|
4777
4707
|
if (!contactById.has(pair.sourceId) || !contactById.has(pair.targetId)) continue;
|
|
4778
|
-
const edge =
|
|
4708
|
+
const edge = ensureEdge2(pair.sourceId, pair.targetId);
|
|
4779
4709
|
edge.coOccurrenceCount += pair.coOccurrenceCount;
|
|
4780
4710
|
edge.coOccurrenceRawScore += pair.rawScore;
|
|
4781
4711
|
edge.lastInteractionTs = Math.max(edge.lastInteractionTs ?? 0, pair.lastOccurrenceTs);
|
|
@@ -4797,7 +4727,7 @@ function getGroupRelationshipGraphFacts(db, ownerMemberId, options = {}) {
|
|
|
4797
4727
|
for (const row of replyRows) {
|
|
4798
4728
|
if (!contactById.has(row.replySenderId) || !contactById.has(row.targetSenderId)) continue;
|
|
4799
4729
|
if (row.replySenderId === row.targetSenderId) continue;
|
|
4800
|
-
const edge =
|
|
4730
|
+
const edge = ensureEdge2(row.replySenderId, row.targetSenderId);
|
|
4801
4731
|
if (row.replySenderId === edge.sourceId) edge.repliesFromSourceToTarget++;
|
|
4802
4732
|
else edge.repliesFromTargetToSource++;
|
|
4803
4733
|
edge.lastInteractionTs = Math.max(edge.lastInteractionTs ?? 0, row.replyTs);
|
|
@@ -6005,12 +5935,456 @@ function getCatchphraseAnalysis(db, filter) {
|
|
|
6005
5935
|
return { members };
|
|
6006
5936
|
}
|
|
6007
5937
|
|
|
5938
|
+
// ../../packages/core/src/query/advanced/group-relationship-galaxy.ts
|
|
5939
|
+
var GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION = "group-relationship-galaxy-v1";
|
|
5940
|
+
var REPLY_WEIGHT = 3;
|
|
5941
|
+
var MENTION_WEIGHT = 2;
|
|
5942
|
+
var CO_OCCURRENCE_COUNT_WEIGHT = 0.05;
|
|
5943
|
+
var MIN_EDGE_WEIGHT = 1e-6;
|
|
5944
|
+
var DEFAULT_LIMITS = {
|
|
5945
|
+
nodeLimit: 400,
|
|
5946
|
+
edgeLimit: 1200,
|
|
5947
|
+
perNodeEdgeLimit: 10
|
|
5948
|
+
};
|
|
5949
|
+
var COMMUNITY_COLORS = [
|
|
5950
|
+
"#7dd3fc",
|
|
5951
|
+
"#c4b5fd",
|
|
5952
|
+
"#f0abfc",
|
|
5953
|
+
"#5eead4",
|
|
5954
|
+
"#fda4af",
|
|
5955
|
+
"#fde68a",
|
|
5956
|
+
"#93c5fd",
|
|
5957
|
+
"#6ee7b7",
|
|
5958
|
+
"#d8b4fe",
|
|
5959
|
+
"#fdba74"
|
|
5960
|
+
];
|
|
5961
|
+
function getGroupRelationshipGalaxy(db, filter, options = {}) {
|
|
5962
|
+
const limits = normalizeLimits(options);
|
|
5963
|
+
const allMembers = getNonSystemMembersForContacts(db);
|
|
5964
|
+
const memberById = new Map(allMembers.map((member) => [member.id, member]));
|
|
5965
|
+
const messageRows = queryRelationshipMessages(db, filter).filter((message) => memberById.has(message.senderId));
|
|
5966
|
+
const activeMembers = buildActiveMembers(allMembers, messageRows);
|
|
5967
|
+
const activeMemberById = new Map(activeMembers.map((member) => [member.id, member]));
|
|
5968
|
+
if (activeMembers.length < 2) {
|
|
5969
|
+
return emptyResult(allMembers.length, activeMembers.length);
|
|
5970
|
+
}
|
|
5971
|
+
const edgeByPair = /* @__PURE__ */ new Map();
|
|
5972
|
+
for (const pair of accumulateCoOccurrencePairs(messageRows)) {
|
|
5973
|
+
if (!activeMemberById.has(pair.sourceId) || !activeMemberById.has(pair.targetId)) continue;
|
|
5974
|
+
const edge = ensureEdge(edgeByPair, activeMemberById, pair.sourceId, pair.targetId);
|
|
5975
|
+
edge.coOccurrenceCount += pair.coOccurrenceCount;
|
|
5976
|
+
edge.coOccurrenceRawScore += pair.rawScore;
|
|
5977
|
+
edge.lastInteractionTs = maxTimestamp(edge.lastInteractionTs, pair.lastOccurrenceTs);
|
|
5978
|
+
}
|
|
5979
|
+
accumulateReplies(db, filter, activeMemberById, edgeByPair);
|
|
5980
|
+
accumulateMentions(db, filter, activeMembers, activeMemberById, edgeByPair);
|
|
5981
|
+
const weightedEdges = [...edgeByPair.values()].map((edge) => ({ ...edge, weight: roundNum2(computeEdgeWeight(edge)) })).filter((edge) => edge.weight >= MIN_EDGE_WEIGHT).sort(compareEdges);
|
|
5982
|
+
if (weightedEdges.length === 0) {
|
|
5983
|
+
return emptyResult(allMembers.length, activeMembers.length);
|
|
5984
|
+
}
|
|
5985
|
+
const candidateNodeKeys = selectCandidateNodeKeys(weightedEdges, limits.nodeLimit);
|
|
5986
|
+
const candidateEdges = weightedEdges.filter(
|
|
5987
|
+
(edge) => candidateNodeKeys.has(edge.sourceKey) && candidateNodeKeys.has(edge.targetKey)
|
|
5988
|
+
);
|
|
5989
|
+
const keptEdges = selectEdges(candidateEdges, limits.perNodeEdgeLimit, limits.edgeLimit);
|
|
5990
|
+
const displayedNodeKeys = /* @__PURE__ */ new Set();
|
|
5991
|
+
for (const edge of keptEdges) {
|
|
5992
|
+
displayedNodeKeys.add(edge.sourceKey);
|
|
5993
|
+
displayedNodeKeys.add(edge.targetKey);
|
|
5994
|
+
}
|
|
5995
|
+
const displayedMembers = activeMembers.filter((member) => displayedNodeKeys.has(member.key));
|
|
5996
|
+
const degreeByKey = computeDegreeByKey(keptEdges);
|
|
5997
|
+
const rankedMembers = [...displayedMembers].sort((a, b) => {
|
|
5998
|
+
const degreeDiff = (degreeByKey.get(b.key) ?? 0) - (degreeByKey.get(a.key) ?? 0);
|
|
5999
|
+
return degreeDiff || b.messageCount - a.messageCount || a.key.localeCompare(b.key);
|
|
6000
|
+
});
|
|
6001
|
+
const communityAssignments = detectWeightedCommunities(rankedMembers, keptEdges, degreeByKey);
|
|
6002
|
+
const communityByNodeKey = /* @__PURE__ */ new Map();
|
|
6003
|
+
for (const community of communityAssignments) {
|
|
6004
|
+
for (const nodeKey of community.nodeKeys) communityByNodeKey.set(nodeKey, community);
|
|
6005
|
+
}
|
|
6006
|
+
const maxDegree = Math.max(...degreeByKey.values(), 1);
|
|
6007
|
+
const renderNodes = [];
|
|
6008
|
+
const memberDetails = [];
|
|
6009
|
+
const incidentTotals = buildIncidentTotals(weightedEdges);
|
|
6010
|
+
for (const [index, member] of rankedMembers.entries()) {
|
|
6011
|
+
const degree = degreeByKey.get(member.key) ?? 0;
|
|
6012
|
+
const score = degree / maxDegree;
|
|
6013
|
+
const community = communityByNodeKey.get(member.key);
|
|
6014
|
+
const position = layoutNode(member.key, index, community, rankedMembers, communityByNodeKey);
|
|
6015
|
+
const totals = incidentTotals.get(member.key) ?? createIncidentTotals();
|
|
6016
|
+
const rank = index + 1;
|
|
6017
|
+
renderNodes.push({
|
|
6018
|
+
key: member.key,
|
|
6019
|
+
displayName: member.displayName,
|
|
6020
|
+
avatar: member.avatar,
|
|
6021
|
+
score: roundNum2(score),
|
|
6022
|
+
rank,
|
|
6023
|
+
communityId: community.id,
|
|
6024
|
+
x: position.x,
|
|
6025
|
+
y: position.y,
|
|
6026
|
+
size: roundNum2(18 + Math.sqrt(score) * 30, 2),
|
|
6027
|
+
color: community.color,
|
|
6028
|
+
labelVisibility: rank <= 8 ? 2 : rank <= 30 ? 1 : 0
|
|
6029
|
+
});
|
|
6030
|
+
memberDetails.push({
|
|
6031
|
+
key: member.key,
|
|
6032
|
+
memberId: member.id,
|
|
6033
|
+
platformId: member.platformId,
|
|
6034
|
+
displayName: member.displayName,
|
|
6035
|
+
avatar: member.avatar,
|
|
6036
|
+
messageCount: member.messageCount,
|
|
6037
|
+
lastMessageTs: member.lastMessageTs,
|
|
6038
|
+
relationshipScore: roundNum2(score),
|
|
6039
|
+
rank,
|
|
6040
|
+
communityId: community.id,
|
|
6041
|
+
replyInteractionCount: totals.replyInteractionCount,
|
|
6042
|
+
mentionInteractionCount: totals.mentionInteractionCount,
|
|
6043
|
+
coOccurrenceCount: totals.coOccurrenceCount,
|
|
6044
|
+
coOccurrenceRawScore: roundNum2(totals.coOccurrenceRawScore),
|
|
6045
|
+
lastInteractionTs: totals.lastInteractionTs
|
|
6046
|
+
});
|
|
6047
|
+
}
|
|
6048
|
+
const maxEdgeWeight = Math.max(...keptEdges.map((edge) => edge.weight), 1);
|
|
6049
|
+
const renderEdges = keptEdges.map((edge) => ({
|
|
6050
|
+
id: edge.id,
|
|
6051
|
+
sourceKey: edge.sourceKey,
|
|
6052
|
+
targetKey: edge.targetKey,
|
|
6053
|
+
weight: edge.weight,
|
|
6054
|
+
visibility: edge.weight / maxEdgeWeight >= 0.35 ? 2 : 1,
|
|
6055
|
+
lastInteractionTs: edge.lastInteractionTs
|
|
6056
|
+
}));
|
|
6057
|
+
const edgeDetails = keptEdges.map((edge) => ({
|
|
6058
|
+
id: edge.id,
|
|
6059
|
+
sourceKey: edge.sourceKey,
|
|
6060
|
+
targetKey: edge.targetKey,
|
|
6061
|
+
weight: edge.weight,
|
|
6062
|
+
coOccurrenceCount: edge.coOccurrenceCount,
|
|
6063
|
+
coOccurrenceRawScore: roundNum2(edge.coOccurrenceRawScore),
|
|
6064
|
+
replyInteractionCount: edge.replyInteractionCount,
|
|
6065
|
+
mentionInteractionCount: edge.mentionInteractionCount,
|
|
6066
|
+
lastInteractionTs: edge.lastInteractionTs
|
|
6067
|
+
}));
|
|
6068
|
+
const renderCommunities = communityAssignments.map((community, index) => ({
|
|
6069
|
+
id: community.id,
|
|
6070
|
+
label: `community-${index + 1}`,
|
|
6071
|
+
size: community.nodeKeys.length,
|
|
6072
|
+
x: community.x,
|
|
6073
|
+
y: community.y,
|
|
6074
|
+
color: community.color
|
|
6075
|
+
}));
|
|
6076
|
+
return {
|
|
6077
|
+
graph: { nodes: renderNodes, edges: renderEdges, communities: renderCommunities },
|
|
6078
|
+
members: memberDetails,
|
|
6079
|
+
edges: edgeDetails,
|
|
6080
|
+
stats: {
|
|
6081
|
+
totalMembers: allMembers.length,
|
|
6082
|
+
activeMembers: activeMembers.length,
|
|
6083
|
+
displayedMembers: renderNodes.length,
|
|
6084
|
+
displayedEdges: renderEdges.length,
|
|
6085
|
+
communityCount: renderCommunities.length
|
|
6086
|
+
},
|
|
6087
|
+
algorithmVersion: GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION
|
|
6088
|
+
};
|
|
6089
|
+
}
|
|
6090
|
+
function queryRelationshipMessages(db, filter) {
|
|
6091
|
+
const { clause, params } = buildTimeFilter(filter, "msg");
|
|
6092
|
+
const where = appendConditions(clause, ["msg.type NOT IN (80, 81)"]);
|
|
6093
|
+
return db.prepare(`SELECT msg.sender_id as senderId, msg.ts FROM message msg ${where} ORDER BY msg.ts ASC, msg.id ASC`).all(...params);
|
|
6094
|
+
}
|
|
6095
|
+
function buildActiveMembers(allMembers, messages) {
|
|
6096
|
+
const messageStats = /* @__PURE__ */ new Map();
|
|
6097
|
+
for (const message of messages) {
|
|
6098
|
+
const stats = messageStats.get(message.senderId) ?? { count: 0, lastTs: 0 };
|
|
6099
|
+
stats.count++;
|
|
6100
|
+
stats.lastTs = Math.max(stats.lastTs, message.ts);
|
|
6101
|
+
messageStats.set(message.senderId, stats);
|
|
6102
|
+
}
|
|
6103
|
+
const active = allMembers.filter((member) => messageStats.has(member.id)).map((member) => ({
|
|
6104
|
+
...member,
|
|
6105
|
+
key: memberKey(member.id),
|
|
6106
|
+
displayName: member.name,
|
|
6107
|
+
messageCount: messageStats.get(member.id).count,
|
|
6108
|
+
lastMessageTs: messageStats.get(member.id).lastTs || null
|
|
6109
|
+
}));
|
|
6110
|
+
const nameCounts = /* @__PURE__ */ new Map();
|
|
6111
|
+
for (const member of active) nameCounts.set(member.displayName, (nameCounts.get(member.displayName) ?? 0) + 1);
|
|
6112
|
+
for (const member of active) {
|
|
6113
|
+
if ((nameCounts.get(member.displayName) ?? 0) > 1) {
|
|
6114
|
+
member.displayName = `${member.displayName}#${member.platformId.slice(-4)}`;
|
|
6115
|
+
}
|
|
6116
|
+
}
|
|
6117
|
+
return active;
|
|
6118
|
+
}
|
|
6119
|
+
function accumulateReplies(db, filter, activeMemberById, edgeByPair) {
|
|
6120
|
+
if (!hasColumn(db, "message", "reply_to_message_id") || !hasColumn(db, "message", "platform_message_id")) return;
|
|
6121
|
+
const { clause, params } = buildTimeFilter(filter, "msg");
|
|
6122
|
+
const conditions = ["msg.type NOT IN (80, 81)", "target.type NOT IN (80, 81)", "msg.reply_to_message_id IS NOT NULL"];
|
|
6123
|
+
const targetParams = [];
|
|
6124
|
+
if (filter?.startTs !== void 0) {
|
|
6125
|
+
conditions.push("target.ts >= ?");
|
|
6126
|
+
targetParams.push(filter.startTs);
|
|
6127
|
+
}
|
|
6128
|
+
if (filter?.endTs !== void 0) {
|
|
6129
|
+
conditions.push("target.ts <= ?");
|
|
6130
|
+
targetParams.push(filter.endTs);
|
|
6131
|
+
}
|
|
6132
|
+
const rows = db.prepare(
|
|
6133
|
+
`SELECT msg.sender_id as replySenderId, msg.ts as replyTs, target.sender_id as targetSenderId
|
|
6134
|
+
FROM message msg
|
|
6135
|
+
JOIN message target ON msg.reply_to_message_id = target.platform_message_id
|
|
6136
|
+
${appendConditions(clause, conditions)}`
|
|
6137
|
+
).all(...params, ...targetParams);
|
|
6138
|
+
for (const row of rows) {
|
|
6139
|
+
if (row.replySenderId === row.targetSenderId) continue;
|
|
6140
|
+
if (!activeMemberById.has(row.replySenderId) || !activeMemberById.has(row.targetSenderId)) continue;
|
|
6141
|
+
const edge = ensureEdge(edgeByPair, activeMemberById, row.replySenderId, row.targetSenderId);
|
|
6142
|
+
edge.replyInteractionCount++;
|
|
6143
|
+
edge.lastInteractionTs = maxTimestamp(edge.lastInteractionTs, row.replyTs);
|
|
6144
|
+
}
|
|
6145
|
+
}
|
|
6146
|
+
function accumulateMentions(db, filter, activeMembers, activeMemberById, edgeByPair) {
|
|
6147
|
+
const nameToMemberId = buildMentionNameIndex(db, activeMembers);
|
|
6148
|
+
const { clause, params } = buildTimeFilter(filter, "msg");
|
|
6149
|
+
const where = appendConditions(clause, ["msg.type = 0", "msg.content IS NOT NULL", "msg.content LIKE '%@%'"]);
|
|
6150
|
+
const rows = db.prepare(`SELECT msg.sender_id as senderId, msg.ts, msg.content FROM message msg ${where}`).all(...params);
|
|
6151
|
+
const mentionRegex = /@([^\s@]+)/g;
|
|
6152
|
+
for (const row of rows) {
|
|
6153
|
+
if (!activeMemberById.has(row.senderId)) continue;
|
|
6154
|
+
const mentionedInMessage = /* @__PURE__ */ new Set();
|
|
6155
|
+
for (const match of row.content.matchAll(mentionRegex)) {
|
|
6156
|
+
const mentionedId = nameToMemberId.get(match[1]);
|
|
6157
|
+
if (mentionedId === null || mentionedId === void 0 || mentionedId === row.senderId) continue;
|
|
6158
|
+
if (!activeMemberById.has(mentionedId) || mentionedInMessage.has(mentionedId)) continue;
|
|
6159
|
+
mentionedInMessage.add(mentionedId);
|
|
6160
|
+
const edge = ensureEdge(edgeByPair, activeMemberById, row.senderId, mentionedId);
|
|
6161
|
+
edge.mentionInteractionCount++;
|
|
6162
|
+
edge.lastInteractionTs = maxTimestamp(edge.lastInteractionTs, row.ts);
|
|
6163
|
+
}
|
|
6164
|
+
}
|
|
6165
|
+
}
|
|
6166
|
+
function buildMentionNameIndex(db, members) {
|
|
6167
|
+
const index = /* @__PURE__ */ new Map();
|
|
6168
|
+
const addName = (name, memberId) => {
|
|
6169
|
+
const trimmed = name.trim();
|
|
6170
|
+
if (!trimmed) return;
|
|
6171
|
+
const existing = index.get(trimmed);
|
|
6172
|
+
if (existing === void 0 || existing === memberId) index.set(trimmed, memberId);
|
|
6173
|
+
else index.set(trimmed, null);
|
|
6174
|
+
};
|
|
6175
|
+
for (const member of members) {
|
|
6176
|
+
addName(member.name, member.id);
|
|
6177
|
+
for (const alias of member.aliases) addName(alias, member.id);
|
|
6178
|
+
}
|
|
6179
|
+
if (hasTable(db, "member_name_history")) {
|
|
6180
|
+
const memberIds = new Set(members.map((member) => member.id));
|
|
6181
|
+
const rows = db.prepare("SELECT member_id as memberId, name FROM member_name_history").all();
|
|
6182
|
+
for (const row of rows) {
|
|
6183
|
+
if (memberIds.has(row.memberId)) addName(row.name, row.memberId);
|
|
6184
|
+
}
|
|
6185
|
+
}
|
|
6186
|
+
return index;
|
|
6187
|
+
}
|
|
6188
|
+
function ensureEdge(edgeByPair, memberById, aId, bId) {
|
|
6189
|
+
const sourceId = Math.min(aId, bId);
|
|
6190
|
+
const targetId = Math.max(aId, bId);
|
|
6191
|
+
const id = `${memberKey(sourceId)}--${memberKey(targetId)}`;
|
|
6192
|
+
const existing = edgeByPair.get(id);
|
|
6193
|
+
if (existing) return existing;
|
|
6194
|
+
const created = {
|
|
6195
|
+
id,
|
|
6196
|
+
sourceId,
|
|
6197
|
+
targetId,
|
|
6198
|
+
sourceKey: memberById.get(sourceId).key,
|
|
6199
|
+
targetKey: memberById.get(targetId).key,
|
|
6200
|
+
coOccurrenceCount: 0,
|
|
6201
|
+
coOccurrenceRawScore: 0,
|
|
6202
|
+
replyInteractionCount: 0,
|
|
6203
|
+
mentionInteractionCount: 0,
|
|
6204
|
+
lastInteractionTs: null,
|
|
6205
|
+
weight: 0
|
|
6206
|
+
};
|
|
6207
|
+
edgeByPair.set(id, created);
|
|
6208
|
+
return created;
|
|
6209
|
+
}
|
|
6210
|
+
function computeEdgeWeight(edge) {
|
|
6211
|
+
return edge.coOccurrenceRawScore + edge.replyInteractionCount * REPLY_WEIGHT + edge.mentionInteractionCount * MENTION_WEIGHT + edge.coOccurrenceCount * CO_OCCURRENCE_COUNT_WEIGHT;
|
|
6212
|
+
}
|
|
6213
|
+
function selectCandidateNodeKeys(edges, nodeLimit) {
|
|
6214
|
+
const degree = computeDegreeByKey(edges);
|
|
6215
|
+
return new Set(
|
|
6216
|
+
[...degree.entries()].sort((a, b) => b[1] - a[1] || a[0].localeCompare(b[0])).slice(0, nodeLimit).map(([key]) => key)
|
|
6217
|
+
);
|
|
6218
|
+
}
|
|
6219
|
+
function selectEdges(edges, perNodeLimit, edgeLimit) {
|
|
6220
|
+
const incident = /* @__PURE__ */ new Map();
|
|
6221
|
+
for (const edge of edges) {
|
|
6222
|
+
for (const key of [edge.sourceKey, edge.targetKey]) {
|
|
6223
|
+
const list = incident.get(key) ?? [];
|
|
6224
|
+
list.push(edge);
|
|
6225
|
+
incident.set(key, list);
|
|
6226
|
+
}
|
|
6227
|
+
}
|
|
6228
|
+
const selectedIds = /* @__PURE__ */ new Set();
|
|
6229
|
+
for (const list of incident.values()) {
|
|
6230
|
+
list.sort(compareEdges);
|
|
6231
|
+
for (const edge of list.slice(0, perNodeLimit)) selectedIds.add(edge.id);
|
|
6232
|
+
}
|
|
6233
|
+
return edges.filter((edge) => selectedIds.has(edge.id)).sort(compareEdges).slice(0, edgeLimit);
|
|
6234
|
+
}
|
|
6235
|
+
function computeDegreeByKey(edges) {
|
|
6236
|
+
const degree = /* @__PURE__ */ new Map();
|
|
6237
|
+
for (const edge of edges) {
|
|
6238
|
+
degree.set(edge.sourceKey, (degree.get(edge.sourceKey) ?? 0) + edge.weight);
|
|
6239
|
+
degree.set(edge.targetKey, (degree.get(edge.targetKey) ?? 0) + edge.weight);
|
|
6240
|
+
}
|
|
6241
|
+
return degree;
|
|
6242
|
+
}
|
|
6243
|
+
function detectWeightedCommunities(members, edges, degreeByKey) {
|
|
6244
|
+
const parent = new Map(members.map((member) => [member.key, member.key]));
|
|
6245
|
+
const strongestByKey = /* @__PURE__ */ new Map();
|
|
6246
|
+
for (const edge of edges) {
|
|
6247
|
+
strongestByKey.set(edge.sourceKey, Math.max(strongestByKey.get(edge.sourceKey) ?? 0, edge.weight));
|
|
6248
|
+
strongestByKey.set(edge.targetKey, Math.max(strongestByKey.get(edge.targetKey) ?? 0, edge.weight));
|
|
6249
|
+
}
|
|
6250
|
+
const find = (key) => {
|
|
6251
|
+
const current = parent.get(key) ?? key;
|
|
6252
|
+
if (current === key) return current;
|
|
6253
|
+
const root = find(current);
|
|
6254
|
+
parent.set(key, root);
|
|
6255
|
+
return root;
|
|
6256
|
+
};
|
|
6257
|
+
const union = (a, b) => {
|
|
6258
|
+
const aRoot = find(a);
|
|
6259
|
+
const bRoot = find(b);
|
|
6260
|
+
if (aRoot === bRoot) return;
|
|
6261
|
+
const [source, target] = aRoot.localeCompare(bRoot) <= 0 ? [aRoot, bRoot] : [bRoot, aRoot];
|
|
6262
|
+
parent.set(target, source);
|
|
6263
|
+
};
|
|
6264
|
+
for (const edge of edges) {
|
|
6265
|
+
const sourceThreshold = (strongestByKey.get(edge.sourceKey) ?? edge.weight) * 0.6;
|
|
6266
|
+
const targetThreshold = (strongestByKey.get(edge.targetKey) ?? edge.weight) * 0.6;
|
|
6267
|
+
if (edge.weight >= sourceThreshold && edge.weight >= targetThreshold) union(edge.sourceKey, edge.targetKey);
|
|
6268
|
+
}
|
|
6269
|
+
const groups = /* @__PURE__ */ new Map();
|
|
6270
|
+
for (const member of members) {
|
|
6271
|
+
const root = find(member.key);
|
|
6272
|
+
const list = groups.get(root) ?? [];
|
|
6273
|
+
list.push(member.key);
|
|
6274
|
+
groups.set(root, list);
|
|
6275
|
+
}
|
|
6276
|
+
const sortedGroups = [...groups.values()].map((keys) => keys.sort()).sort((a, b) => {
|
|
6277
|
+
const aWeight = a.reduce((sum, key) => sum + (degreeByKey.get(key) ?? 0), 0);
|
|
6278
|
+
const bWeight = b.reduce((sum, key) => sum + (degreeByKey.get(key) ?? 0), 0);
|
|
6279
|
+
return bWeight - aWeight || a[0].localeCompare(b[0]);
|
|
6280
|
+
});
|
|
6281
|
+
return sortedGroups.map((nodeKeys, index) => {
|
|
6282
|
+
const center = layoutCommunity(index, sortedGroups.length);
|
|
6283
|
+
return {
|
|
6284
|
+
id: `community:${nodeKeys[0]}`,
|
|
6285
|
+
nodeKeys,
|
|
6286
|
+
color: COMMUNITY_COLORS[stableHash(nodeKeys[0]) % COMMUNITY_COLORS.length],
|
|
6287
|
+
x: center.x,
|
|
6288
|
+
y: center.y
|
|
6289
|
+
};
|
|
6290
|
+
});
|
|
6291
|
+
}
|
|
6292
|
+
function layoutCommunity(index, count) {
|
|
6293
|
+
if (index === 0 || count === 1) return { x: 0, y: 0 };
|
|
6294
|
+
const ringIndex = index - 1;
|
|
6295
|
+
const ring = Math.floor(ringIndex / 6);
|
|
6296
|
+
const position = ringIndex % 6;
|
|
6297
|
+
const radius = 380 + ring * 280;
|
|
6298
|
+
const angle = -Math.PI / 2 + position / 6 * Math.PI * 2 + ring * 0.31;
|
|
6299
|
+
return { x: roundNum2(Math.cos(angle) * radius, 2), y: roundNum2(Math.sin(angle) * radius, 2) };
|
|
6300
|
+
}
|
|
6301
|
+
function layoutNode(nodeKey, globalIndex, community, members, communityByNodeKey) {
|
|
6302
|
+
const communityMembers = members.filter((member) => communityByNodeKey.get(member.key)?.id === community.id);
|
|
6303
|
+
const localIndex = communityMembers.findIndex((member) => member.key === nodeKey);
|
|
6304
|
+
const angle = localIndex * 2.399963229728653 + stableHash(nodeKey) % 1e3 / 1e3;
|
|
6305
|
+
const radius = localIndex === 0 ? 0 : 54 + Math.sqrt(localIndex) * 42;
|
|
6306
|
+
const globalJitter = globalIndex % 3 * 2;
|
|
6307
|
+
return {
|
|
6308
|
+
x: roundNum2(community.x + Math.cos(angle) * (radius + globalJitter), 2),
|
|
6309
|
+
y: roundNum2(community.y + Math.sin(angle) * (radius + globalJitter), 2)
|
|
6310
|
+
};
|
|
6311
|
+
}
|
|
6312
|
+
function buildIncidentTotals(edges) {
|
|
6313
|
+
const totals = /* @__PURE__ */ new Map();
|
|
6314
|
+
for (const edge of edges) {
|
|
6315
|
+
for (const key of [edge.sourceKey, edge.targetKey]) {
|
|
6316
|
+
const item = totals.get(key) ?? createIncidentTotals();
|
|
6317
|
+
item.replyInteractionCount += edge.replyInteractionCount;
|
|
6318
|
+
item.mentionInteractionCount += edge.mentionInteractionCount;
|
|
6319
|
+
item.coOccurrenceCount += edge.coOccurrenceCount;
|
|
6320
|
+
item.coOccurrenceRawScore += edge.coOccurrenceRawScore;
|
|
6321
|
+
item.lastInteractionTs = maxTimestamp(item.lastInteractionTs, edge.lastInteractionTs);
|
|
6322
|
+
totals.set(key, item);
|
|
6323
|
+
}
|
|
6324
|
+
}
|
|
6325
|
+
return totals;
|
|
6326
|
+
}
|
|
6327
|
+
function createIncidentTotals() {
|
|
6328
|
+
return {
|
|
6329
|
+
replyInteractionCount: 0,
|
|
6330
|
+
mentionInteractionCount: 0,
|
|
6331
|
+
coOccurrenceCount: 0,
|
|
6332
|
+
coOccurrenceRawScore: 0,
|
|
6333
|
+
lastInteractionTs: null
|
|
6334
|
+
};
|
|
6335
|
+
}
|
|
6336
|
+
function normalizeLimits(options) {
|
|
6337
|
+
return {
|
|
6338
|
+
nodeLimit: normalizeLimit(options.nodeLimit, DEFAULT_LIMITS.nodeLimit),
|
|
6339
|
+
edgeLimit: normalizeLimit(options.edgeLimit, DEFAULT_LIMITS.edgeLimit),
|
|
6340
|
+
perNodeEdgeLimit: normalizeLimit(options.perNodeEdgeLimit, DEFAULT_LIMITS.perNodeEdgeLimit)
|
|
6341
|
+
};
|
|
6342
|
+
}
|
|
6343
|
+
function normalizeLimit(value, fallback) {
|
|
6344
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
6345
|
+
}
|
|
6346
|
+
function appendConditions(clause, conditions) {
|
|
6347
|
+
const existing = clause.trim().replace(/^WHERE\s+/i, "");
|
|
6348
|
+
return `WHERE ${[existing, ...conditions].filter(Boolean).join(" AND ")}`;
|
|
6349
|
+
}
|
|
6350
|
+
function emptyResult(totalMembers, activeMembers) {
|
|
6351
|
+
return {
|
|
6352
|
+
graph: { nodes: [], edges: [], communities: [] },
|
|
6353
|
+
members: [],
|
|
6354
|
+
edges: [],
|
|
6355
|
+
stats: { totalMembers, activeMembers, displayedMembers: 0, displayedEdges: 0, communityCount: 0 },
|
|
6356
|
+
algorithmVersion: GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION
|
|
6357
|
+
};
|
|
6358
|
+
}
|
|
6359
|
+
function compareEdges(a, b) {
|
|
6360
|
+
return b.weight - a.weight || a.id.localeCompare(b.id);
|
|
6361
|
+
}
|
|
6362
|
+
function memberKey(memberId) {
|
|
6363
|
+
return `member:${memberId}`;
|
|
6364
|
+
}
|
|
6365
|
+
function maxTimestamp(current, next) {
|
|
6366
|
+
if (next === null) return current;
|
|
6367
|
+
return Math.max(current ?? 0, next) || null;
|
|
6368
|
+
}
|
|
6369
|
+
function roundNum2(value, digits = 4) {
|
|
6370
|
+
const factor = 10 ** digits;
|
|
6371
|
+
return Math.round(value * factor) / factor;
|
|
6372
|
+
}
|
|
6373
|
+
function stableHash(value) {
|
|
6374
|
+
let hash = 2166136261;
|
|
6375
|
+
for (let index = 0; index < value.length; index++) {
|
|
6376
|
+
hash ^= value.charCodeAt(index);
|
|
6377
|
+
hash = Math.imul(hash, 16777619);
|
|
6378
|
+
}
|
|
6379
|
+
return hash >>> 0;
|
|
6380
|
+
}
|
|
6381
|
+
|
|
6008
6382
|
// ../../packages/core/src/query/advanced/relationship.ts
|
|
6009
6383
|
var ICE_BREAK_THRESHOLD = 24 * 60 * 60;
|
|
6010
6384
|
var DEFAULT_PERSEVERANCE_THRESHOLD = 300;
|
|
6011
6385
|
function getRelationshipStats(db, filter, options) {
|
|
6012
6386
|
const perseveranceThreshold = options?.perseveranceThreshold ?? DEFAULT_PERSEVERANCE_THRESHOLD;
|
|
6013
|
-
const
|
|
6387
|
+
const emptyResult2 = {
|
|
6014
6388
|
months: [],
|
|
6015
6389
|
members: [],
|
|
6016
6390
|
totalSessions: 0,
|
|
@@ -6022,7 +6396,7 @@ function getRelationshipStats(db, filter, options) {
|
|
|
6022
6396
|
monthlyPerseverance: []
|
|
6023
6397
|
};
|
|
6024
6398
|
const sessionCount = db.prepare("SELECT COUNT(*) as count FROM segment").get();
|
|
6025
|
-
if (!sessionCount || sessionCount.count === 0) return
|
|
6399
|
+
if (!sessionCount || sessionCount.count === 0) return emptyResult2;
|
|
6026
6400
|
const timeConditions = [];
|
|
6027
6401
|
const params = [];
|
|
6028
6402
|
if (filter?.startTs !== void 0) {
|
|
@@ -6755,7 +7129,7 @@ function getCheckInAnalysis(db, filter) {
|
|
|
6755
7129
|
return computeCheckIn(dailyActivity);
|
|
6756
7130
|
}
|
|
6757
7131
|
function computeMemeBattle(messages) {
|
|
6758
|
-
const
|
|
7132
|
+
const emptyResult2 = {
|
|
6759
7133
|
topBattles: [],
|
|
6760
7134
|
rankByCount: [],
|
|
6761
7135
|
rankByImageCount: [],
|
|
@@ -6789,7 +7163,7 @@ function computeMemeBattle(messages) {
|
|
|
6789
7163
|
}
|
|
6790
7164
|
}
|
|
6791
7165
|
processChain();
|
|
6792
|
-
if (battles.length === 0) return
|
|
7166
|
+
if (battles.length === 0) return emptyResult2;
|
|
6793
7167
|
const topBattles = battles.map((battle) => ({
|
|
6794
7168
|
startTime: battle.startTime,
|
|
6795
7169
|
endTime: battle.endTime,
|
|
@@ -6868,7 +7242,7 @@ function getMemeBattleAnalysis(db, filter) {
|
|
|
6868
7242
|
return computeMemeBattle(messages);
|
|
6869
7243
|
}
|
|
6870
7244
|
function computeNightOwl(messages) {
|
|
6871
|
-
const
|
|
7245
|
+
const emptyResult2 = {
|
|
6872
7246
|
nightOwlRank: [],
|
|
6873
7247
|
lastSpeakerRank: [],
|
|
6874
7248
|
firstSpeakerRank: [],
|
|
@@ -6876,7 +7250,7 @@ function computeNightOwl(messages) {
|
|
|
6876
7250
|
champions: [],
|
|
6877
7251
|
totalDays: 0
|
|
6878
7252
|
};
|
|
6879
|
-
if (messages.length === 0) return
|
|
7253
|
+
if (messages.length === 0) return emptyResult2;
|
|
6880
7254
|
function getNightOwlTitleByCount(count) {
|
|
6881
7255
|
if (count === 0) return "\u517B\u751F\u8FBE\u4EBA";
|
|
6882
7256
|
if (count <= 20) return "\u5076\u5C14\u5931\u7720";
|
|
@@ -9319,7 +9693,6 @@ export {
|
|
|
9319
9693
|
isValidWord,
|
|
9320
9694
|
buildExcludeKeywordsConditions,
|
|
9321
9695
|
getMentionAnalysis,
|
|
9322
|
-
getMentionGraph,
|
|
9323
9696
|
getLaughAnalysis,
|
|
9324
9697
|
getClusterGraph,
|
|
9325
9698
|
resolveOwnerMember,
|
|
@@ -9366,6 +9739,8 @@ export {
|
|
|
9366
9739
|
deleteMembers,
|
|
9367
9740
|
deleteMember,
|
|
9368
9741
|
getCatchphraseAnalysis,
|
|
9742
|
+
GROUP_RELATIONSHIP_GALAXY_ALGORITHM_VERSION,
|
|
9743
|
+
getGroupRelationshipGalaxy,
|
|
9369
9744
|
getRelationshipStats,
|
|
9370
9745
|
getJourneyStats,
|
|
9371
9746
|
getLanguagePreferenceAnalysis,
|
|
@@ -9448,4 +9823,4 @@ export {
|
|
|
9448
9823
|
StaticPathProvider,
|
|
9449
9824
|
createDatabaseManagerAdapter
|
|
9450
9825
|
};
|
|
9451
|
-
//# sourceMappingURL=chunk-
|
|
9826
|
+
//# sourceMappingURL=chunk-22YJOBA3.mjs.map
|