imsg-mcp 1.3.2 → 1.5.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/CHANGELOG.md +44 -0
- package/README.md +3 -2
- package/dist/cli.js +126 -20
- package/dist/cli.js.map +1 -1
- package/dist/{imessage-db-Cz8MIIpG.js → imessage-db-BrHJPOq7.js} +147 -19
- package/dist/imessage-db-BrHJPOq7.js.map +1 -0
- package/dist/index.js +847 -73
- package/dist/index.js.map +1 -1
- package/dist/meta-E4VgVmPs.js +165 -0
- package/dist/meta-E4VgVmPs.js.map +1 -0
- package/dist/{setup-BuXuKAHA.js → setup-CysR5gId.js} +2 -2
- package/dist/{setup-BuXuKAHA.js.map → setup-CysR5gId.js.map} +1 -1
- package/dist/{shutdown-pPAPSnk3.js → shutdown-Vh0gjb6m.js} +434 -48
- package/dist/shutdown-Vh0gjb6m.js.map +1 -0
- package/dist/tui.js +226 -64
- package/dist/tui.js.map +1 -1
- package/dist/{watchdog-Cbgm050b.js → watchdog-B3T-dQT-.js} +21 -266
- package/dist/watchdog-B3T-dQT-.js.map +1 -0
- package/native/imsg-native.darwin-arm64.node +0 -0
- package/package.json +1 -1
- package/skills/humans/SKILL.md +136 -0
- package/skills/imsg-mcp/SKILL.md +29 -0
- package/dist/imessage-db-Cz8MIIpG.js.map +0 -1
- package/dist/meta-DzE7CSrZ.js +0 -7
- package/dist/meta-DzE7CSrZ.js.map +0 -1
- package/dist/shutdown-pPAPSnk3.js.map +0 -1
- package/dist/watchdog-Cbgm050b.js.map +0 -1
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { existsSync, readdirSync, readFileSync, appendFileSync, mkdirSync, writeFileSync, unlinkSync } from "node:fs";
|
|
1
|
+
import { existsSync, readdirSync, readFileSync, appendFileSync, mkdirSync, writeFileSync, unlinkSync, statSync, rmSync, copyFileSync } from "node:fs";
|
|
2
2
|
import os, { homedir, freemem, tmpdir } from "node:os";
|
|
3
3
|
import Database from "better-sqlite3";
|
|
4
4
|
import { execFile } from "node:child_process";
|
|
5
5
|
import { randomUUID, randomBytes, createHash } from "node:crypto";
|
|
6
|
-
import { join, sep, dirname, resolve } from "node:path";
|
|
6
|
+
import { join, sep, dirname, resolve, basename } from "node:path";
|
|
7
7
|
import { promisify } from "node:util";
|
|
8
8
|
function resolveEnvPath(value, fallback) {
|
|
9
9
|
if (!value) return fallback;
|
|
@@ -56,10 +56,14 @@ function getContactsDbPaths() {
|
|
|
56
56
|
function getSlugsDbPath() {
|
|
57
57
|
return resolveEnvPath(process.env.VITE_SLUGS_DB_PATH, join(homedir(), ".imsg-mcp", "slugs.db"));
|
|
58
58
|
}
|
|
59
|
+
function getHumansDirPath() {
|
|
60
|
+
return resolveEnvPath(process.env.VITE_HUMANS_DIR, join(homedir(), ".agents", "humans"));
|
|
61
|
+
}
|
|
59
62
|
const config = /* @__PURE__ */ Object.freeze(/* @__PURE__ */ Object.defineProperty({
|
|
60
63
|
__proto__: null,
|
|
61
64
|
getContactsDbPaths,
|
|
62
65
|
getEnv,
|
|
66
|
+
getHumansDirPath,
|
|
63
67
|
getImsgDbPath,
|
|
64
68
|
getSlugsDbPath,
|
|
65
69
|
isAiEnv
|
|
@@ -502,10 +506,30 @@ async function sendMessageReliable(recipient, message, preferredService) {
|
|
|
502
506
|
}
|
|
503
507
|
}
|
|
504
508
|
}
|
|
509
|
+
const ATTACHMENT_STAGING_DIR = join(homedir(), "Library", "Messages", "imsg-mcp-staging");
|
|
510
|
+
const STAGING_SWEEP_AGE_MS = 60 * 60 * 1e3;
|
|
511
|
+
function stageAttachmentForSend(filepath, stagingDir = ATTACHMENT_STAGING_DIR) {
|
|
512
|
+
try {
|
|
513
|
+
mkdirSync(stagingDir, { recursive: true });
|
|
514
|
+
for (const entry of readdirSync(stagingDir)) {
|
|
515
|
+
const p = join(stagingDir, entry);
|
|
516
|
+
try {
|
|
517
|
+
if (Date.now() - statSync(p).mtimeMs > STAGING_SWEEP_AGE_MS) rmSync(p, { force: true });
|
|
518
|
+
} catch {
|
|
519
|
+
}
|
|
520
|
+
}
|
|
521
|
+
const staged = join(stagingDir, `${randomBytes(4).toString("hex")}-${basename(filepath)}`);
|
|
522
|
+
copyFileSync(filepath, staged);
|
|
523
|
+
return staged;
|
|
524
|
+
} catch {
|
|
525
|
+
return filepath;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
505
528
|
async function sendAttachment(recipient, filepath, preferredService) {
|
|
506
529
|
if (MOCK) return mockSend(`[attachment:${filepath}]`, { chatIdentifier: recipient });
|
|
530
|
+
const stagedPath = stageAttachmentForSend(filepath);
|
|
507
531
|
const escapedRecipient = appleScriptEscape(recipient);
|
|
508
|
-
const escapedPath = appleScriptEscape(
|
|
532
|
+
const escapedPath = appleScriptEscape(stagedPath);
|
|
509
533
|
const script = buildParticipantSendScript({
|
|
510
534
|
order: sendServiceOrder(recipient, preferredService),
|
|
511
535
|
escapedRecipient,
|
|
@@ -731,6 +755,362 @@ function generateThreadSlug(input) {
|
|
|
731
755
|
if (!namePart) namePart = "unknown";
|
|
732
756
|
return `${namePart}~${svc}~${hash}`;
|
|
733
757
|
}
|
|
758
|
+
const MS_PER_DAY = 864e5;
|
|
759
|
+
function localDateKey(d) {
|
|
760
|
+
const y = d.getFullYear();
|
|
761
|
+
const m = String(d.getMonth() + 1).padStart(2, "0");
|
|
762
|
+
const day = String(d.getDate()).padStart(2, "0");
|
|
763
|
+
return `${y}-${m}-${day}`;
|
|
764
|
+
}
|
|
765
|
+
function contactKey(m) {
|
|
766
|
+
return m.chatId;
|
|
767
|
+
}
|
|
768
|
+
function byDate(a, b) {
|
|
769
|
+
return a.date.getTime() - b.date.getTime();
|
|
770
|
+
}
|
|
771
|
+
function computeStreaks(messages) {
|
|
772
|
+
const byContact = /* @__PURE__ */ new Map();
|
|
773
|
+
for (const m of messages) {
|
|
774
|
+
if (m.isReaction) continue;
|
|
775
|
+
const c = contactKey(m);
|
|
776
|
+
if (!byContact.has(c)) byContact.set(c, /* @__PURE__ */ new Set());
|
|
777
|
+
byContact.get(c).add(localDateKey(m.date));
|
|
778
|
+
}
|
|
779
|
+
localDateKey(/* @__PURE__ */ new Date());
|
|
780
|
+
const results = [];
|
|
781
|
+
for (const [contact, dayset] of byContact) {
|
|
782
|
+
const days = Array.from(dayset).sort();
|
|
783
|
+
let longest = 0;
|
|
784
|
+
let longestStart = null;
|
|
785
|
+
let longestEnd = null;
|
|
786
|
+
let runLen = 0;
|
|
787
|
+
let runStart = null;
|
|
788
|
+
for (let i = 0; i < days.length; i++) {
|
|
789
|
+
if (i === 0 || new Date(days[i]).getTime() - new Date(days[i - 1]).getTime() !== MS_PER_DAY) {
|
|
790
|
+
runLen = 1;
|
|
791
|
+
runStart = days[i];
|
|
792
|
+
} else {
|
|
793
|
+
runLen++;
|
|
794
|
+
}
|
|
795
|
+
if (runLen > longest) {
|
|
796
|
+
longest = runLen;
|
|
797
|
+
longestStart = runStart;
|
|
798
|
+
longestEnd = days[i];
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
let curLen = 0;
|
|
802
|
+
const cursor = /* @__PURE__ */ new Date();
|
|
803
|
+
while (dayset.has(localDateKey(cursor))) {
|
|
804
|
+
curLen++;
|
|
805
|
+
cursor.setDate(cursor.getDate() - 1);
|
|
806
|
+
}
|
|
807
|
+
results.push({
|
|
808
|
+
contact,
|
|
809
|
+
longestStreakDays: longest,
|
|
810
|
+
longestStreakStart: longestStart,
|
|
811
|
+
longestStreakEnd: longestEnd,
|
|
812
|
+
currentStreakDays: curLen
|
|
813
|
+
});
|
|
814
|
+
}
|
|
815
|
+
results.sort((a, b) => b.longestStreakDays - a.longestStreakDays);
|
|
816
|
+
return results;
|
|
817
|
+
}
|
|
818
|
+
function computeDoubleTexts(messages) {
|
|
819
|
+
const byContact = /* @__PURE__ */ new Map();
|
|
820
|
+
for (const m of messages) {
|
|
821
|
+
if (m.isReaction) continue;
|
|
822
|
+
const c = contactKey(m);
|
|
823
|
+
if (!byContact.has(c)) byContact.set(c, []);
|
|
824
|
+
byContact.get(c).push(m);
|
|
825
|
+
}
|
|
826
|
+
const results = [];
|
|
827
|
+
for (const [contact, msgs] of byContact) {
|
|
828
|
+
msgs.sort(byDate);
|
|
829
|
+
let mineRun = 0;
|
|
830
|
+
let theirsRun = 0;
|
|
831
|
+
let mineDoubles = 0;
|
|
832
|
+
let theirsDoubles = 0;
|
|
833
|
+
for (const m of msgs) {
|
|
834
|
+
if (m.isFromMe) {
|
|
835
|
+
mineRun++;
|
|
836
|
+
theirsRun = 0;
|
|
837
|
+
if (mineRun >= 2) mineDoubles++;
|
|
838
|
+
} else {
|
|
839
|
+
theirsRun++;
|
|
840
|
+
mineRun = 0;
|
|
841
|
+
if (theirsRun >= 2) theirsDoubles++;
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
results.push({
|
|
845
|
+
contact,
|
|
846
|
+
doubleTextsFromMe: mineDoubles,
|
|
847
|
+
doubleTextsFromThem: theirsDoubles
|
|
848
|
+
});
|
|
849
|
+
}
|
|
850
|
+
results.sort(
|
|
851
|
+
(a, b) => b.doubleTextsFromMe + b.doubleTextsFromThem - (a.doubleTextsFromMe + a.doubleTextsFromThem)
|
|
852
|
+
);
|
|
853
|
+
return results;
|
|
854
|
+
}
|
|
855
|
+
function percentile(sorted, p) {
|
|
856
|
+
if (sorted.length === 0) return 0;
|
|
857
|
+
const idx = Math.min(sorted.length - 1, Math.floor(p / 100 * sorted.length));
|
|
858
|
+
return sorted[idx] ?? 0;
|
|
859
|
+
}
|
|
860
|
+
function computeResponseTimes(messages) {
|
|
861
|
+
const byContact = /* @__PURE__ */ new Map();
|
|
862
|
+
for (const m of messages) {
|
|
863
|
+
if (m.isReaction) continue;
|
|
864
|
+
const c = contactKey(m);
|
|
865
|
+
if (!byContact.has(c)) byContact.set(c, []);
|
|
866
|
+
byContact.get(c).push(m);
|
|
867
|
+
}
|
|
868
|
+
const results = [];
|
|
869
|
+
for (const [contact, msgs] of byContact) {
|
|
870
|
+
msgs.sort(byDate);
|
|
871
|
+
const deltas = [];
|
|
872
|
+
for (let i = 1; i < msgs.length; i++) {
|
|
873
|
+
const prev = msgs[i - 1];
|
|
874
|
+
const curr = msgs[i];
|
|
875
|
+
if (!prev.isFromMe && curr.isFromMe) {
|
|
876
|
+
deltas.push(curr.date.getTime() - prev.date.getTime());
|
|
877
|
+
}
|
|
878
|
+
}
|
|
879
|
+
if (deltas.length === 0) continue;
|
|
880
|
+
deltas.sort((a, b) => a - b);
|
|
881
|
+
const mean = deltas.reduce((s, n) => s + n, 0) / deltas.length;
|
|
882
|
+
results.push({
|
|
883
|
+
contact,
|
|
884
|
+
count: deltas.length,
|
|
885
|
+
medianMs: percentile(deltas, 50),
|
|
886
|
+
p95Ms: percentile(deltas, 95),
|
|
887
|
+
meanMs: Math.round(mean)
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
results.sort((a, b) => a.medianMs - b.medianMs);
|
|
891
|
+
return results;
|
|
892
|
+
}
|
|
893
|
+
function computeHeatmap(messages) {
|
|
894
|
+
const grid = Array.from({ length: 7 }, () => Array.from({ length: 24 }, () => 0));
|
|
895
|
+
let total = 0;
|
|
896
|
+
for (const m of messages) {
|
|
897
|
+
if (m.isReaction) continue;
|
|
898
|
+
const dow = m.date.getDay();
|
|
899
|
+
const hr = m.date.getHours();
|
|
900
|
+
const dowRow = grid[dow];
|
|
901
|
+
if (dowRow && hr >= 0 && hr < 24) {
|
|
902
|
+
dowRow[hr] = (dowRow[hr] ?? 0) + 1;
|
|
903
|
+
total++;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
return { grid, total };
|
|
907
|
+
}
|
|
908
|
+
function computeTapbacks(messages) {
|
|
909
|
+
const byContact = /* @__PURE__ */ new Map();
|
|
910
|
+
for (const m of messages) {
|
|
911
|
+
if (!m.isReaction || !m.reaction) continue;
|
|
912
|
+
const c = contactKey(m);
|
|
913
|
+
if (!byContact.has(c)) {
|
|
914
|
+
byContact.set(c, {
|
|
915
|
+
contact: c,
|
|
916
|
+
heart: 0,
|
|
917
|
+
thumbsUp: 0,
|
|
918
|
+
thumbsDown: 0,
|
|
919
|
+
haha: 0,
|
|
920
|
+
exclaim: 0,
|
|
921
|
+
question: 0,
|
|
922
|
+
emoji: 0,
|
|
923
|
+
total: 0
|
|
924
|
+
});
|
|
925
|
+
}
|
|
926
|
+
const r = byContact.get(c);
|
|
927
|
+
const t = (m.reaction.type || "").toLowerCase();
|
|
928
|
+
if (t.includes("heart") || t === "love") r.heart++;
|
|
929
|
+
else if (t.includes("thumb") && t.includes("up") || t === "like") r.thumbsUp++;
|
|
930
|
+
else if (t.includes("thumb") && t.includes("down") || t === "dislike") r.thumbsDown++;
|
|
931
|
+
else if (t.includes("haha") || t === "laugh") r.haha++;
|
|
932
|
+
else if (t.includes("exclaim") || t === "emphasis") r.exclaim++;
|
|
933
|
+
else if (t.includes("question")) r.question++;
|
|
934
|
+
else r.emoji++;
|
|
935
|
+
r.total++;
|
|
936
|
+
}
|
|
937
|
+
const out = Array.from(byContact.values());
|
|
938
|
+
out.sort((a, b) => b.total - a.total);
|
|
939
|
+
return out;
|
|
940
|
+
}
|
|
941
|
+
function computeWrapped(messages) {
|
|
942
|
+
if (messages.length === 0) {
|
|
943
|
+
return {
|
|
944
|
+
windowStart: "",
|
|
945
|
+
windowEnd: "",
|
|
946
|
+
totalSent: 0,
|
|
947
|
+
totalReceived: 0,
|
|
948
|
+
totalReactions: 0,
|
|
949
|
+
topContacts: [],
|
|
950
|
+
peakDay: null,
|
|
951
|
+
longestStreakDays: 0,
|
|
952
|
+
longestStreakContact: null
|
|
953
|
+
};
|
|
954
|
+
}
|
|
955
|
+
let sent = 0;
|
|
956
|
+
let received = 0;
|
|
957
|
+
let reactions = 0;
|
|
958
|
+
const perContact = /* @__PURE__ */ new Map();
|
|
959
|
+
const perDay = /* @__PURE__ */ new Map();
|
|
960
|
+
for (const m of messages) {
|
|
961
|
+
if (m.isReaction) {
|
|
962
|
+
reactions++;
|
|
963
|
+
continue;
|
|
964
|
+
}
|
|
965
|
+
if (m.isFromMe) sent++;
|
|
966
|
+
else received++;
|
|
967
|
+
const c = contactKey(m);
|
|
968
|
+
if (!perContact.has(c)) perContact.set(c, { sent: 0, received: 0, total: 0 });
|
|
969
|
+
const pc = perContact.get(c);
|
|
970
|
+
if (m.isFromMe) pc.sent++;
|
|
971
|
+
else pc.received++;
|
|
972
|
+
pc.total++;
|
|
973
|
+
const dk = localDateKey(m.date);
|
|
974
|
+
perDay.set(dk, (perDay.get(dk) ?? 0) + 1);
|
|
975
|
+
}
|
|
976
|
+
const topContacts = Array.from(perContact.entries()).map(([contact, v]) => ({ contact, ...v })).sort((a, b) => b.total - a.total).slice(0, 5);
|
|
977
|
+
let peakDay = null;
|
|
978
|
+
for (const [d, count] of perDay) {
|
|
979
|
+
if (!peakDay || count > peakDay.count) peakDay = { date: d, count };
|
|
980
|
+
}
|
|
981
|
+
const streaks = computeStreaks(messages);
|
|
982
|
+
const top = streaks[0];
|
|
983
|
+
return {
|
|
984
|
+
windowStart: localDateKey(messages[0].date),
|
|
985
|
+
windowEnd: localDateKey(messages[messages.length - 1].date),
|
|
986
|
+
totalSent: sent,
|
|
987
|
+
totalReceived: received,
|
|
988
|
+
totalReactions: reactions,
|
|
989
|
+
topContacts,
|
|
990
|
+
peakDay,
|
|
991
|
+
longestStreakDays: top?.longestStreakDays ?? 0,
|
|
992
|
+
longestStreakContact: top?.contact ?? null
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
function computeRelationshipLeaderboard(messages) {
|
|
996
|
+
const byChat = /* @__PURE__ */ new Map();
|
|
997
|
+
for (const m of messages) {
|
|
998
|
+
if (m.isReaction) continue;
|
|
999
|
+
if (!m.chatId || isGroupChatIdentifier(m.chatId)) continue;
|
|
1000
|
+
let acc = byChat.get(m.chatId);
|
|
1001
|
+
if (!acc) {
|
|
1002
|
+
acc = { contact: null, handle: m.chatId, sent: 0, received: 0, lastMs: 0 };
|
|
1003
|
+
byChat.set(m.chatId, acc);
|
|
1004
|
+
}
|
|
1005
|
+
if (m.isFromMe) {
|
|
1006
|
+
acc.sent++;
|
|
1007
|
+
} else {
|
|
1008
|
+
acc.received++;
|
|
1009
|
+
if (!acc.contact && m.displayName) acc.contact = m.displayName;
|
|
1010
|
+
if (m.handle && m.handle !== "unknown") acc.handle = m.handle;
|
|
1011
|
+
}
|
|
1012
|
+
if (m.date.getTime() > acc.lastMs) acc.lastMs = m.date.getTime();
|
|
1013
|
+
}
|
|
1014
|
+
const byContact = /* @__PURE__ */ new Map();
|
|
1015
|
+
for (const acc of byChat.values()) {
|
|
1016
|
+
const key = acc.contact ?? acc.handle;
|
|
1017
|
+
if (!key || key === "unknown") continue;
|
|
1018
|
+
const merged = byContact.get(key);
|
|
1019
|
+
if (merged) {
|
|
1020
|
+
merged.sent += acc.sent;
|
|
1021
|
+
merged.received += acc.received;
|
|
1022
|
+
merged.lastMs = Math.max(merged.lastMs, acc.lastMs);
|
|
1023
|
+
} else {
|
|
1024
|
+
byContact.set(key, { ...acc });
|
|
1025
|
+
}
|
|
1026
|
+
}
|
|
1027
|
+
const now = Date.now();
|
|
1028
|
+
const leaderboard = [];
|
|
1029
|
+
for (const [contact, acc] of byContact) {
|
|
1030
|
+
const total = acc.sent + acc.received;
|
|
1031
|
+
if (total < 3) continue;
|
|
1032
|
+
const reciprocity = Math.min(acc.sent, acc.received) / Math.max(Math.max(acc.sent, acc.received), 1);
|
|
1033
|
+
const daysSinceLast = Math.max(0, (now - acc.lastMs) / 864e5);
|
|
1034
|
+
const score = Math.log2(1 + total) * (0.4 + 0.6 * reciprocity) * Math.exp(-daysSinceLast / 45);
|
|
1035
|
+
leaderboard.push({
|
|
1036
|
+
contact,
|
|
1037
|
+
handle: acc.handle,
|
|
1038
|
+
total,
|
|
1039
|
+
sent: acc.sent,
|
|
1040
|
+
received: acc.received,
|
|
1041
|
+
reciprocity: Math.round(reciprocity * 100) / 100,
|
|
1042
|
+
daysSinceLast: Math.round(daysSinceLast * 10) / 10,
|
|
1043
|
+
score
|
|
1044
|
+
});
|
|
1045
|
+
}
|
|
1046
|
+
leaderboard.sort((a, b) => b.score - a.score);
|
|
1047
|
+
for (const row of leaderboard) row.score = Math.round(row.score * 1e6) / 1e6;
|
|
1048
|
+
return { leaderboard: leaderboard.slice(0, 50) };
|
|
1049
|
+
}
|
|
1050
|
+
const IMPLEMENTED_TYPES = [
|
|
1051
|
+
"messaging_streaks",
|
|
1052
|
+
"double_texts",
|
|
1053
|
+
"response_time_stats",
|
|
1054
|
+
"daily_heatmap",
|
|
1055
|
+
"tapback_summary",
|
|
1056
|
+
"year_in_review_wrapped",
|
|
1057
|
+
"relationship_leaderboard"
|
|
1058
|
+
];
|
|
1059
|
+
const ANALYTIC_INFO = {
|
|
1060
|
+
messaging_streaks: {
|
|
1061
|
+
label: "Messaging Streaks",
|
|
1062
|
+
description: "Longest and current daily-message streaks per contact",
|
|
1063
|
+
defaultWindowDays: 365
|
|
1064
|
+
},
|
|
1065
|
+
double_texts: {
|
|
1066
|
+
label: "Double Texts",
|
|
1067
|
+
description: "Consecutive messages sent without a reply",
|
|
1068
|
+
defaultWindowDays: 90
|
|
1069
|
+
},
|
|
1070
|
+
response_time_stats: {
|
|
1071
|
+
label: "Response Times",
|
|
1072
|
+
description: "Reply latency per contact (median, p95, mean)",
|
|
1073
|
+
defaultWindowDays: 90
|
|
1074
|
+
},
|
|
1075
|
+
daily_heatmap: {
|
|
1076
|
+
label: "Daily Heatmap",
|
|
1077
|
+
description: "7×24 grid of activity by weekday × hour",
|
|
1078
|
+
defaultWindowDays: 90
|
|
1079
|
+
},
|
|
1080
|
+
tapback_summary: {
|
|
1081
|
+
label: "Tapback Summary",
|
|
1082
|
+
description: "Tapback reactions sent per contact",
|
|
1083
|
+
defaultWindowDays: 365
|
|
1084
|
+
},
|
|
1085
|
+
year_in_review_wrapped: {
|
|
1086
|
+
label: "Year in Review (Wrapped)",
|
|
1087
|
+
description: "Wrapped summary: top contacts, peak day, totals (pinned to 365d)",
|
|
1088
|
+
defaultWindowDays: 365
|
|
1089
|
+
},
|
|
1090
|
+
relationship_leaderboard: {
|
|
1091
|
+
label: "Relationship Leaderboard",
|
|
1092
|
+
description: "Top relationships by volume × reciprocity × recency",
|
|
1093
|
+
defaultWindowDays: 1825
|
|
1094
|
+
}
|
|
1095
|
+
};
|
|
1096
|
+
function dispatchAnalytic(type, messages) {
|
|
1097
|
+
switch (type) {
|
|
1098
|
+
case "messaging_streaks":
|
|
1099
|
+
return { type, data: computeStreaks(messages) };
|
|
1100
|
+
case "double_texts":
|
|
1101
|
+
return { type, data: computeDoubleTexts(messages) };
|
|
1102
|
+
case "response_time_stats":
|
|
1103
|
+
return { type, data: computeResponseTimes(messages) };
|
|
1104
|
+
case "daily_heatmap":
|
|
1105
|
+
return { type, data: computeHeatmap(messages) };
|
|
1106
|
+
case "tapback_summary":
|
|
1107
|
+
return { type, data: computeTapbacks(messages) };
|
|
1108
|
+
case "year_in_review_wrapped":
|
|
1109
|
+
return { type, data: computeWrapped(messages) };
|
|
1110
|
+
case "relationship_leaderboard":
|
|
1111
|
+
return { type, data: computeRelationshipLeaderboard(messages) };
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
734
1114
|
const registry = /* @__PURE__ */ new Set();
|
|
735
1115
|
let shuttingDown = false;
|
|
736
1116
|
let watchdogTimer = null;
|
|
@@ -822,53 +1202,59 @@ function isShuttingDown() {
|
|
|
822
1202
|
return shuttingDown;
|
|
823
1203
|
}
|
|
824
1204
|
export {
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
1205
|
+
config as $,
|
|
1206
|
+
info as A,
|
|
1207
|
+
startHeapMonitor as B,
|
|
1208
|
+
getLogs as C,
|
|
1209
|
+
setLastSendError as D,
|
|
1210
|
+
logShutdown as E,
|
|
1211
|
+
stopHeapMonitor as F,
|
|
1212
|
+
getLogFilePath as G,
|
|
1213
|
+
getLastSendError as H,
|
|
1214
|
+
IMPLEMENTED_TYPES as I,
|
|
1215
|
+
ANALYTIC_INFO as J,
|
|
1216
|
+
looksLikeThreadSlug as K,
|
|
1217
|
+
enableFileLogging as L,
|
|
1218
|
+
isShuttingDown as M,
|
|
1219
|
+
warn as N,
|
|
1220
|
+
error as O,
|
|
1221
|
+
OBJECT_REPLACEMENT_CHAR as P,
|
|
1222
|
+
isGroupGuid as Q,
|
|
1223
|
+
isGroupChatIdentifier as R,
|
|
1224
|
+
generateThreadSlug as S,
|
|
844
1225
|
Tables as T,
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
1226
|
+
AssociatedMessageType as U,
|
|
1227
|
+
macAutoTimestampToDate as V,
|
|
1228
|
+
isReactionType as W,
|
|
1229
|
+
MAC_EPOCH_OFFSET as X,
|
|
1230
|
+
NANOS_PER_SECOND as Y,
|
|
1231
|
+
macTimestampToDate as Z,
|
|
1232
|
+
parseAssociatedMessageGuid as _,
|
|
1233
|
+
shutdown as a,
|
|
1234
|
+
getImsgDbPath as b,
|
|
849
1235
|
checkLocalAccess as c,
|
|
850
|
-
|
|
851
|
-
|
|
1236
|
+
getContactsDbPaths as d,
|
|
1237
|
+
getSlugsDbPath as e,
|
|
852
1238
|
formatAccessReport as f,
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
1239
|
+
getHumansDirPath as g,
|
|
1240
|
+
appendLog as h,
|
|
1241
|
+
getFileLogLines as i,
|
|
1242
|
+
getLogDirectory as j,
|
|
1243
|
+
checkMessagesAvailable as k,
|
|
1244
|
+
sendToChat as l,
|
|
1245
|
+
sendToChatId as m,
|
|
1246
|
+
sendMessageReliable as n,
|
|
1247
|
+
sendMessageAlt as o,
|
|
862
1248
|
perf as p,
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
1249
|
+
sendAttachment as q,
|
|
1250
|
+
checkImessageAvailability as r,
|
|
1251
|
+
sanitizeSlugPart as s,
|
|
1252
|
+
computeRelationshipLeaderboard as t,
|
|
1253
|
+
dispatchAnalytic as u,
|
|
1254
|
+
installShutdownHandlers as v,
|
|
1255
|
+
registerCleanup as w,
|
|
1256
|
+
enableStdinEofDetection as x,
|
|
1257
|
+
enableOrphanWatchdog as y,
|
|
1258
|
+
logStartup as z
|
|
873
1259
|
};
|
|
874
|
-
//# sourceMappingURL=shutdown-
|
|
1260
|
+
//# sourceMappingURL=shutdown-Vh0gjb6m.js.map
|