spectrum-ts 1.2.1 → 1.4.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/{chunk-K3CTEGCZ.js → chunk-66GJ45ZZ.js} +1 -1
- package/dist/{chunk-I7EKZS5C.js → chunk-B4MHPWPZ.js} +1 -1
- package/dist/{chunk-UQPIWAHH.js → chunk-LH4YEBG3.js} +30 -17
- package/dist/index.d.ts +16 -12
- package/dist/index.js +79 -15
- package/dist/providers/imessage/index.d.ts +20 -3
- package/dist/providers/imessage/index.js +263 -156
- package/dist/providers/terminal/index.d.ts +89 -4
- package/dist/providers/terminal/index.js +30 -40
- package/dist/providers/whatsapp-business/index.d.ts +2 -1
- package/dist/providers/whatsapp-business/index.js +26 -36
- package/dist/{types-Dvp0I86h.d.ts → types-BcCLW2VO.d.ts} +58 -20
- package/package.json +1 -1
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
asGroup,
|
|
3
3
|
asRichlink,
|
|
4
4
|
groupSchema
|
|
5
|
-
} from "../../chunk-
|
|
5
|
+
} from "../../chunk-66GJ45ZZ.js";
|
|
6
6
|
import {
|
|
7
7
|
asPoll,
|
|
8
8
|
asPollOption,
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
text,
|
|
24
24
|
textSchema,
|
|
25
25
|
toVCard
|
|
26
|
-
} from "../../chunk-
|
|
26
|
+
} from "../../chunk-LH4YEBG3.js";
|
|
27
27
|
|
|
28
28
|
// src/providers/imessage/index.ts
|
|
29
29
|
import {
|
|
@@ -77,18 +77,62 @@ function effect(input, messageEffect) {
|
|
|
77
77
|
}
|
|
78
78
|
|
|
79
79
|
// src/providers/imessage/auth.ts
|
|
80
|
-
import {
|
|
81
|
-
|
|
82
|
-
|
|
80
|
+
import { createClient } from "@photon-ai/advanced-imessage";
|
|
81
|
+
|
|
82
|
+
// src/providers/imessage/types.ts
|
|
83
|
+
import { IMessageSDK } from "@photon-ai/imessage-kit";
|
|
84
|
+
import z2 from "zod";
|
|
85
|
+
var SHARED_PHONE = "shared";
|
|
86
|
+
var isLocal = (client) => client instanceof IMessageSDK;
|
|
87
|
+
var clientEntry = z2.object({
|
|
88
|
+
address: z2.string(),
|
|
89
|
+
token: z2.string(),
|
|
90
|
+
phone: z2.string()
|
|
91
|
+
});
|
|
92
|
+
var configSchema = z2.union([
|
|
93
|
+
z2.object({ local: z2.literal(true) }),
|
|
94
|
+
z2.object({
|
|
95
|
+
local: z2.literal(false).optional().default(false),
|
|
96
|
+
clients: clientEntry.or(z2.array(clientEntry)).optional()
|
|
97
|
+
})
|
|
98
|
+
]);
|
|
99
|
+
var userSchema = z2.object({});
|
|
100
|
+
var spaceSchema = z2.object({
|
|
101
|
+
id: z2.string(),
|
|
102
|
+
type: z2.enum(["dm", "group"]),
|
|
103
|
+
phone: z2.string()
|
|
104
|
+
});
|
|
105
|
+
var spaceParamsSchema = z2.object({
|
|
106
|
+
phone: z2.string().optional()
|
|
107
|
+
});
|
|
108
|
+
var messageSchema = z2.object({
|
|
109
|
+
partIndex: z2.number().int().nonnegative().optional(),
|
|
110
|
+
parentId: z2.string().optional()
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// src/providers/imessage/auth.ts
|
|
83
114
|
var RENEWAL_RATIO = 0.8;
|
|
84
115
|
var EXPIRY_BUFFER_MS = 3e4;
|
|
85
116
|
var RETRY_DELAY_MS = 3e4;
|
|
86
117
|
var cloudAuthState = /* @__PURE__ */ new WeakMap();
|
|
118
|
+
var requirePhone = (data, instanceId) => {
|
|
119
|
+
const phone = data.numbers?.[instanceId];
|
|
120
|
+
if (!phone) {
|
|
121
|
+
throw new Error(`iMessage instance ${instanceId} has no phone assigned`);
|
|
122
|
+
}
|
|
123
|
+
return phone;
|
|
124
|
+
};
|
|
87
125
|
async function createCloudClients(projectId, projectSecret) {
|
|
88
126
|
let tokenData = await cloud.issueImessageTokens(projectId, projectSecret);
|
|
89
127
|
let tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
90
128
|
let disposed = false;
|
|
91
129
|
let renewalTimer;
|
|
130
|
+
const records = [];
|
|
131
|
+
const syncPhones = (data) => {
|
|
132
|
+
for (const { entry, instanceId } of records) {
|
|
133
|
+
entry.phone = requirePhone(data, instanceId);
|
|
134
|
+
}
|
|
135
|
+
};
|
|
92
136
|
const scheduleRenewal = () => {
|
|
93
137
|
if (disposed) {
|
|
94
138
|
return;
|
|
@@ -99,6 +143,9 @@ async function createCloudClients(projectId, projectSecret) {
|
|
|
99
143
|
try {
|
|
100
144
|
tokenData = await cloud.issueImessageTokens(projectId, projectSecret);
|
|
101
145
|
tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
146
|
+
if (tokenData.type === "dedicated") {
|
|
147
|
+
syncPhones(tokenData);
|
|
148
|
+
}
|
|
102
149
|
scheduleRenewal();
|
|
103
150
|
} catch {
|
|
104
151
|
renewalTimer = setTimeout(() => scheduleRenewal(), RETRY_DELAY_MS);
|
|
@@ -114,13 +161,17 @@ async function createCloudClients(projectId, projectSecret) {
|
|
|
114
161
|
}
|
|
115
162
|
tokenData = await cloud.issueImessageTokens(projectId, projectSecret);
|
|
116
163
|
tokenExpiresAt = Date.now() + tokenData.expiresIn * 1e3;
|
|
164
|
+
if (tokenData.type === "dedicated") {
|
|
165
|
+
syncPhones(tokenData);
|
|
166
|
+
}
|
|
117
167
|
scheduleRenewal();
|
|
118
168
|
};
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
169
|
+
if (tokenData.type === "shared") {
|
|
170
|
+
const address = process.env.SPECTRUM_IMESSAGE_ADDRESS ?? "imessage.spectrum.photon.codes:443";
|
|
171
|
+
const entries2 = [
|
|
172
|
+
{
|
|
173
|
+
phone: SHARED_PHONE,
|
|
174
|
+
client: createClient({
|
|
124
175
|
address,
|
|
125
176
|
tls: true,
|
|
126
177
|
token: async () => {
|
|
@@ -128,10 +179,24 @@ async function createCloudClients(projectId, projectSecret) {
|
|
|
128
179
|
return tokenData.token;
|
|
129
180
|
}
|
|
130
181
|
})
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
(
|
|
182
|
+
}
|
|
183
|
+
];
|
|
184
|
+
cloudAuthState.set(entries2, {
|
|
185
|
+
dispose: () => {
|
|
186
|
+
disposed = true;
|
|
187
|
+
if (renewalTimer !== void 0) {
|
|
188
|
+
clearTimeout(renewalTimer);
|
|
189
|
+
renewalTimer = void 0;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
return entries2;
|
|
194
|
+
}
|
|
195
|
+
const dedicated = tokenData;
|
|
196
|
+
for (const [instanceId, token] of Object.entries(dedicated.auth)) {
|
|
197
|
+
const entry = {
|
|
198
|
+
phone: requirePhone(dedicated, instanceId),
|
|
199
|
+
client: createClient({
|
|
135
200
|
address: `${instanceId}.imsg.photon.codes:443`,
|
|
136
201
|
tls: true,
|
|
137
202
|
token: async () => {
|
|
@@ -140,10 +205,11 @@ async function createCloudClients(projectId, projectSecret) {
|
|
|
140
205
|
return data.auth[instanceId] ?? token;
|
|
141
206
|
}
|
|
142
207
|
})
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
208
|
+
};
|
|
209
|
+
records.push({ entry, instanceId });
|
|
210
|
+
}
|
|
211
|
+
const entries = records.map((r) => r.entry);
|
|
212
|
+
cloudAuthState.set(entries, {
|
|
147
213
|
dispose: () => {
|
|
148
214
|
disposed = true;
|
|
149
215
|
if (renewalTimer !== void 0) {
|
|
@@ -152,7 +218,7 @@ async function createCloudClients(projectId, projectSecret) {
|
|
|
152
218
|
}
|
|
153
219
|
}
|
|
154
220
|
});
|
|
155
|
-
return
|
|
221
|
+
return entries;
|
|
156
222
|
}
|
|
157
223
|
async function disposeCloudAuth(clients) {
|
|
158
224
|
const auth = cloudAuthState.get(clients);
|
|
@@ -230,7 +296,12 @@ var toMessages = async (message) => {
|
|
|
230
296
|
}
|
|
231
297
|
const base = {
|
|
232
298
|
sender: { id: message.participant ?? "" },
|
|
233
|
-
|
|
299
|
+
// Local mode has no concept of "which-of-my-phones"; phone is empty.
|
|
300
|
+
space: {
|
|
301
|
+
id: chatId,
|
|
302
|
+
type: chatKind === "group" ? "group" : "dm",
|
|
303
|
+
phone: ""
|
|
304
|
+
},
|
|
234
305
|
timestamp: message.createdAt
|
|
235
306
|
};
|
|
236
307
|
if (message.attachments.length > 0) {
|
|
@@ -337,17 +408,6 @@ var messages2 = (client) => messages(client);
|
|
|
337
408
|
var send2 = async (client, spaceId, content) => send(client, spaceId, content);
|
|
338
409
|
var getMessage2 = async (client, id) => getMessage(client, id);
|
|
339
410
|
|
|
340
|
-
// src/providers/imessage/remote/client.ts
|
|
341
|
-
var REMOTE_CLIENT_MISSING = "No remote iMessage client available";
|
|
342
|
-
var firstRemoteClient = (clients) => clients[0];
|
|
343
|
-
var primaryRemoteClient = (clients) => {
|
|
344
|
-
const remote = firstRemoteClient(clients);
|
|
345
|
-
if (!remote) {
|
|
346
|
-
throw new Error(REMOTE_CLIENT_MISSING);
|
|
347
|
-
}
|
|
348
|
-
return remote;
|
|
349
|
-
};
|
|
350
|
-
|
|
351
411
|
// src/providers/imessage/remote/inbound.ts
|
|
352
412
|
import {
|
|
353
413
|
messageGuid,
|
|
@@ -520,13 +580,14 @@ var isIMessageMessage = (value) => {
|
|
|
520
580
|
return typeof record.id === "string" && record.id.length > 0 && typeof record.content === "object" && record.content !== null && typeof record.sender === "object" && record.sender !== null && typeof record.space === "object" && record.space !== null;
|
|
521
581
|
};
|
|
522
582
|
var asProviderGroup = (items) => groupSchema.parse({ type: "group", items });
|
|
523
|
-
var buildMessageBase = (message, chatGuidHint, timestamp) => {
|
|
583
|
+
var buildMessageBase = (message, chatGuidHint, timestamp, phone) => {
|
|
524
584
|
const chat = resolveChatGuid(message, chatGuidHint);
|
|
525
585
|
return {
|
|
526
586
|
sender: { id: resolveSenderId(message) },
|
|
527
587
|
space: {
|
|
528
588
|
id: chat,
|
|
529
|
-
type: chat.includes(";+;") ? "group" : "dm"
|
|
589
|
+
type: chat.includes(";+;") ? "group" : "dm",
|
|
590
|
+
phone
|
|
530
591
|
},
|
|
531
592
|
timestamp
|
|
532
593
|
};
|
|
@@ -581,10 +642,10 @@ var toRichlinkMessage = (message, base, id) => {
|
|
|
581
642
|
};
|
|
582
643
|
}
|
|
583
644
|
};
|
|
584
|
-
var rebuildFromAppleMessage = async (client, message, chatGuidHint) => {
|
|
645
|
+
var rebuildFromAppleMessage = async (client, message, phone, chatGuidHint) => {
|
|
585
646
|
const messageGuidStr = message.guid;
|
|
586
647
|
const timestamp = message.dateCreated ?? /* @__PURE__ */ new Date();
|
|
587
|
-
const base = buildMessageBase(message, chatGuidHint, timestamp);
|
|
648
|
+
const base = buildMessageBase(message, chatGuidHint, timestamp, phone);
|
|
588
649
|
if (message.attachments.length === 1) {
|
|
589
650
|
const info = message.attachments[0];
|
|
590
651
|
if (!info) {
|
|
@@ -636,8 +697,13 @@ var cacheMessage = (cache, message) => {
|
|
|
636
697
|
}
|
|
637
698
|
}
|
|
638
699
|
};
|
|
639
|
-
var toInboundMessages = async (client, cache, event) => {
|
|
640
|
-
const base = buildMessageBase(
|
|
700
|
+
var toInboundMessages = async (client, cache, event, phone) => {
|
|
701
|
+
const base = buildMessageBase(
|
|
702
|
+
event.message,
|
|
703
|
+
event.chatGuid,
|
|
704
|
+
event.timestamp,
|
|
705
|
+
phone
|
|
706
|
+
);
|
|
641
707
|
const messageGuidStr = event.message.guid;
|
|
642
708
|
if (getBalloonBundleId(event.message) === URL_BALLOON_BUNDLE_ID) {
|
|
643
709
|
const msg2 = toRichlinkMessage(event.message, base, messageGuidStr);
|
|
@@ -694,7 +760,7 @@ var toInboundMessages = async (client, cache, event) => {
|
|
|
694
760
|
cacheMessage(cache, msg);
|
|
695
761
|
return [msg];
|
|
696
762
|
};
|
|
697
|
-
var getMessage3 = async (remote, spaceId, msgId) => {
|
|
763
|
+
var getMessage3 = async (remote, spaceId, msgId, phone) => {
|
|
698
764
|
const cache = getMessageCache(remote);
|
|
699
765
|
const cached = cache.get(msgId);
|
|
700
766
|
if (cached) {
|
|
@@ -706,7 +772,12 @@ var getMessage3 = async (remote, spaceId, msgId) => {
|
|
|
706
772
|
const fetched = await remote.messages.get(
|
|
707
773
|
messageGuid(childRef.parentGuid)
|
|
708
774
|
);
|
|
709
|
-
const parent = await rebuildFromAppleMessage(
|
|
775
|
+
const parent = await rebuildFromAppleMessage(
|
|
776
|
+
remote,
|
|
777
|
+
fetched,
|
|
778
|
+
phone,
|
|
779
|
+
spaceId
|
|
780
|
+
);
|
|
710
781
|
cacheMessage(cache, parent);
|
|
711
782
|
if (parent.content.type !== "group") {
|
|
712
783
|
return;
|
|
@@ -722,7 +793,12 @@ var getMessage3 = async (remote, spaceId, msgId) => {
|
|
|
722
793
|
}
|
|
723
794
|
try {
|
|
724
795
|
const fetched = await remote.messages.get(messageGuid(msgId));
|
|
725
|
-
const rebuilt = await rebuildFromAppleMessage(
|
|
796
|
+
const rebuilt = await rebuildFromAppleMessage(
|
|
797
|
+
remote,
|
|
798
|
+
fetched,
|
|
799
|
+
phone,
|
|
800
|
+
spaceId
|
|
801
|
+
);
|
|
726
802
|
cacheMessage(cache, rebuilt);
|
|
727
803
|
return rebuilt;
|
|
728
804
|
} catch (err) {
|
|
@@ -785,12 +861,12 @@ var asProviderReaction = (emoji, target) => reactionSchema.parse({
|
|
|
785
861
|
target,
|
|
786
862
|
type: "reaction"
|
|
787
863
|
});
|
|
788
|
-
var resolveReactionTarget = async (client, cache, strippedGuid, partIndex) => {
|
|
864
|
+
var resolveReactionTarget = async (client, cache, strippedGuid, partIndex, phone) => {
|
|
789
865
|
let candidate = cache.get(strippedGuid);
|
|
790
866
|
if (!candidate) {
|
|
791
867
|
try {
|
|
792
868
|
const fetched = await client.messages.get(messageGuid2(strippedGuid));
|
|
793
|
-
candidate = await rebuildFromAppleMessage(client, fetched);
|
|
869
|
+
candidate = await rebuildFromAppleMessage(client, fetched, phone);
|
|
794
870
|
cacheMessage(cache, candidate);
|
|
795
871
|
} catch {
|
|
796
872
|
return;
|
|
@@ -806,7 +882,7 @@ var resolveReactionTarget = async (client, cache, strippedGuid, partIndex) => {
|
|
|
806
882
|
}
|
|
807
883
|
return candidate;
|
|
808
884
|
};
|
|
809
|
-
var toReactionMessages = async (client, cache, event, target) => {
|
|
885
|
+
var toReactionMessages = async (client, cache, event, target, phone) => {
|
|
810
886
|
const type = getAssociatedMessageType(event.message);
|
|
811
887
|
if (type && isTapbackRemoval(type)) {
|
|
812
888
|
return [];
|
|
@@ -823,7 +899,8 @@ var toReactionMessages = async (client, cache, event, target) => {
|
|
|
823
899
|
client,
|
|
824
900
|
cache,
|
|
825
901
|
strippedGuid,
|
|
826
|
-
partIndex
|
|
902
|
+
partIndex,
|
|
903
|
+
phone
|
|
827
904
|
);
|
|
828
905
|
if (!resolved) {
|
|
829
906
|
return [];
|
|
@@ -832,7 +909,12 @@ var toReactionMessages = async (client, cache, event, target) => {
|
|
|
832
909
|
if (typeof messageId !== "string" || messageId.length === 0) {
|
|
833
910
|
return [];
|
|
834
911
|
}
|
|
835
|
-
const base = buildMessageBase(
|
|
912
|
+
const base = buildMessageBase(
|
|
913
|
+
event.message,
|
|
914
|
+
event.chatGuid,
|
|
915
|
+
event.timestamp,
|
|
916
|
+
phone
|
|
917
|
+
);
|
|
836
918
|
return [
|
|
837
919
|
{
|
|
838
920
|
...base,
|
|
@@ -1545,7 +1627,8 @@ var buildPollOptionMessage = (input) => {
|
|
|
1545
1627
|
sender: { id: input.senderAddress },
|
|
1546
1628
|
space: {
|
|
1547
1629
|
id: input.chatGuid,
|
|
1548
|
-
type: input.chatGuid.includes(";+;") ? "group" : "dm"
|
|
1630
|
+
type: input.chatGuid.includes(";+;") ? "group" : "dm",
|
|
1631
|
+
phone: input.phone
|
|
1549
1632
|
},
|
|
1550
1633
|
timestamp: input.event.at,
|
|
1551
1634
|
content: asPollOption({
|
|
@@ -1563,6 +1646,7 @@ var buildPollOptionMessages = (input) => {
|
|
|
1563
1646
|
chatGuid: input.chatGuid,
|
|
1564
1647
|
event: input.event,
|
|
1565
1648
|
optionId: delta.optionId,
|
|
1649
|
+
phone: input.phone,
|
|
1566
1650
|
selected: delta.selected,
|
|
1567
1651
|
senderAddress: input.senderAddress
|
|
1568
1652
|
});
|
|
@@ -1587,7 +1671,7 @@ var refreshPollMetadata = async (client, pollCache, event, fallbackOptionIds) =>
|
|
|
1587
1671
|
poll: refreshed
|
|
1588
1672
|
};
|
|
1589
1673
|
};
|
|
1590
|
-
var toPollVoteMessages = async (client, pollCache, event) => {
|
|
1674
|
+
var toPollVoteMessages = async (client, pollCache, event, phone) => {
|
|
1591
1675
|
const senderAddress = event.actor.address;
|
|
1592
1676
|
if (!senderAddress) {
|
|
1593
1677
|
return [];
|
|
@@ -1630,6 +1714,7 @@ var toPollVoteMessages = async (client, pollCache, event) => {
|
|
|
1630
1714
|
chatGuid: chatGuidStr,
|
|
1631
1715
|
deltas,
|
|
1632
1716
|
event,
|
|
1717
|
+
phone,
|
|
1633
1718
|
senderAddress
|
|
1634
1719
|
});
|
|
1635
1720
|
pollCache.commitActorSelection(
|
|
@@ -1640,7 +1725,7 @@ var toPollVoteMessages = async (client, pollCache, event) => {
|
|
|
1640
1725
|
);
|
|
1641
1726
|
return messages5;
|
|
1642
1727
|
};
|
|
1643
|
-
var toPollUnvoteMessages = async (client, pollCache, event) => {
|
|
1728
|
+
var toPollUnvoteMessages = async (client, pollCache, event, phone) => {
|
|
1644
1729
|
const senderAddress = event.actor.address;
|
|
1645
1730
|
if (!senderAddress) {
|
|
1646
1731
|
return [];
|
|
@@ -1660,17 +1745,18 @@ var toPollUnvoteMessages = async (client, pollCache, event) => {
|
|
|
1660
1745
|
chatGuid: chatGuidStr,
|
|
1661
1746
|
deltas,
|
|
1662
1747
|
event,
|
|
1748
|
+
phone,
|
|
1663
1749
|
senderAddress
|
|
1664
1750
|
});
|
|
1665
1751
|
pollCache.commitActorSelection(pollId, senderAddress, [], event.at);
|
|
1666
1752
|
return messages5;
|
|
1667
1753
|
};
|
|
1668
|
-
var toPollDeltaMessages = async (client, pollCache, event) => {
|
|
1754
|
+
var toPollDeltaMessages = async (client, pollCache, event, phone) => {
|
|
1669
1755
|
if (isVotedPollEvent(event)) {
|
|
1670
|
-
return toPollVoteMessages(client, pollCache, event);
|
|
1756
|
+
return toPollVoteMessages(client, pollCache, event, phone);
|
|
1671
1757
|
}
|
|
1672
1758
|
if (isUnvotedPollEvent(event)) {
|
|
1673
|
-
return toPollUnvoteMessages(client, pollCache, event);
|
|
1759
|
+
return toPollUnvoteMessages(client, pollCache, event, phone);
|
|
1674
1760
|
}
|
|
1675
1761
|
return [];
|
|
1676
1762
|
};
|
|
@@ -1686,43 +1772,43 @@ var isRetryableIMessageStreamError = (error) => {
|
|
|
1686
1772
|
}
|
|
1687
1773
|
return false;
|
|
1688
1774
|
};
|
|
1689
|
-
var toMessageItem = async (client, event, cursor) => {
|
|
1775
|
+
var toMessageItem = async (client, event, phone, cursor) => {
|
|
1690
1776
|
const id = event.message.guid;
|
|
1691
1777
|
if (event.message.isFromMe) {
|
|
1692
1778
|
return { cursor, id, values: [] };
|
|
1693
1779
|
}
|
|
1694
1780
|
const cache = getMessageCache(client);
|
|
1695
1781
|
const target = event.message.associatedMessageGuid;
|
|
1696
|
-
const values = target ? await toReactionMessages(client, cache, event, target) : await toInboundMessages(client, cache, event);
|
|
1782
|
+
const values = target ? await toReactionMessages(client, cache, event, target, phone) : await toInboundMessages(client, cache, event, phone);
|
|
1697
1783
|
return { cursor, id, values };
|
|
1698
1784
|
};
|
|
1699
|
-
var messageStream = (client) => resumableOrderedStream({
|
|
1785
|
+
var messageStream = (client, phone) => resumableOrderedStream({
|
|
1700
1786
|
fetchMissed: (cursor, { limit }) => client.messages.fetchMissed(cursor, { limit }),
|
|
1701
1787
|
isRetryableError: isRetryableIMessageStreamError,
|
|
1702
|
-
processLive: (event) => toMessageItem(client, event, event.cursor),
|
|
1703
|
-
processMissed: (message) => toMessageItem(client, receivedEventFromMessage(message)),
|
|
1788
|
+
processLive: (event) => toMessageItem(client, event, phone, event.cursor),
|
|
1789
|
+
processMissed: (message) => toMessageItem(client, receivedEventFromMessage(message), phone),
|
|
1704
1790
|
subscribeLive: () => client.messages.subscribe("message.received")
|
|
1705
1791
|
});
|
|
1706
1792
|
var logPollStreamError = (error) => {
|
|
1707
1793
|
console.error("[spectrum-ts][imessage][poll] stream failed", error);
|
|
1708
1794
|
};
|
|
1709
|
-
var emitPollMessages = async (client, pollCache, event, emit) => {
|
|
1795
|
+
var emitPollMessages = async (client, pollCache, event, phone, emit) => {
|
|
1710
1796
|
cachePollEvent(pollCache, event);
|
|
1711
1797
|
if (event.actor.isFromMe) {
|
|
1712
1798
|
return;
|
|
1713
1799
|
}
|
|
1714
|
-
const messages5 = await toPollDeltaMessages(client, pollCache, event);
|
|
1800
|
+
const messages5 = await toPollDeltaMessages(client, pollCache, event, phone);
|
|
1715
1801
|
for (const vote of messages5) {
|
|
1716
1802
|
await emit(vote);
|
|
1717
1803
|
}
|
|
1718
1804
|
};
|
|
1719
|
-
var runPollSubscription = async (client, pollCache, subscription, emit, onEvent) => {
|
|
1805
|
+
var runPollSubscription = async (client, pollCache, subscription, phone, emit, onEvent) => {
|
|
1720
1806
|
for await (const event of subscription) {
|
|
1721
1807
|
onEvent();
|
|
1722
|
-
await emitPollMessages(client, pollCache, event, emit);
|
|
1808
|
+
await emitPollMessages(client, pollCache, event, phone, emit);
|
|
1723
1809
|
}
|
|
1724
1810
|
};
|
|
1725
|
-
var pollStream = (client, pollCache) => stream((emit, end) => {
|
|
1811
|
+
var pollStream = (client, pollCache, phone) => stream((emit, end) => {
|
|
1726
1812
|
let active = client.polls.subscribe();
|
|
1727
1813
|
let closed = false;
|
|
1728
1814
|
let retryDelayMs = RECONNECT_INITIAL_DELAY_MS;
|
|
@@ -1750,9 +1836,16 @@ var pollStream = (client, pollCache) => stream((emit, end) => {
|
|
|
1750
1836
|
const pump = (async () => {
|
|
1751
1837
|
while (!closed) {
|
|
1752
1838
|
try {
|
|
1753
|
-
await runPollSubscription(
|
|
1754
|
-
|
|
1755
|
-
|
|
1839
|
+
await runPollSubscription(
|
|
1840
|
+
client,
|
|
1841
|
+
pollCache,
|
|
1842
|
+
active,
|
|
1843
|
+
phone,
|
|
1844
|
+
emit,
|
|
1845
|
+
() => {
|
|
1846
|
+
retryDelayMs = RECONNECT_INITIAL_DELAY_MS;
|
|
1847
|
+
}
|
|
1848
|
+
);
|
|
1756
1849
|
} catch (e) {
|
|
1757
1850
|
if (!closed) {
|
|
1758
1851
|
logPollStreamError(e);
|
|
@@ -1775,12 +1868,17 @@ var pollStream = (client, pollCache) => stream((emit, end) => {
|
|
|
1775
1868
|
await pump;
|
|
1776
1869
|
};
|
|
1777
1870
|
});
|
|
1778
|
-
var clientStream = (client, pollCache) => {
|
|
1779
|
-
return mergeStreams([
|
|
1871
|
+
var clientStream = (client, pollCache, phone) => {
|
|
1872
|
+
return mergeStreams([
|
|
1873
|
+
messageStream(client, phone),
|
|
1874
|
+
pollStream(client, pollCache, phone)
|
|
1875
|
+
]);
|
|
1780
1876
|
};
|
|
1781
1877
|
var messages3 = (clients) => {
|
|
1782
1878
|
const pollCache = getPollCache(clients);
|
|
1783
|
-
return mergeStreams(
|
|
1879
|
+
return mergeStreams(
|
|
1880
|
+
clients.map((entry) => clientStream(entry.client, pollCache, entry.phone))
|
|
1881
|
+
);
|
|
1784
1882
|
};
|
|
1785
1883
|
|
|
1786
1884
|
// src/providers/imessage/remote/typing.ts
|
|
@@ -1794,59 +1892,53 @@ var stopTyping = async (remote, spaceId) => {
|
|
|
1794
1892
|
|
|
1795
1893
|
// src/providers/imessage/remote/api.ts
|
|
1796
1894
|
var messages4 = (clients) => messages3(clients);
|
|
1797
|
-
var startTyping2 = async (
|
|
1798
|
-
const remote = firstRemoteClient(clients);
|
|
1799
|
-
if (!remote) {
|
|
1800
|
-
return;
|
|
1801
|
-
}
|
|
1895
|
+
var startTyping2 = async (remote, spaceId) => {
|
|
1802
1896
|
await startTyping(remote, spaceId);
|
|
1803
1897
|
};
|
|
1804
|
-
var stopTyping2 = async (
|
|
1805
|
-
const remote = firstRemoteClient(clients);
|
|
1806
|
-
if (!remote) {
|
|
1807
|
-
return;
|
|
1808
|
-
}
|
|
1898
|
+
var stopTyping2 = async (remote, spaceId) => {
|
|
1809
1899
|
await stopTyping(remote, spaceId);
|
|
1810
1900
|
};
|
|
1811
|
-
var send4 = async (
|
|
1812
|
-
var replyToMessage2 = async (
|
|
1813
|
-
var editMessage2 = async (
|
|
1814
|
-
var reactToMessage2 = async (
|
|
1815
|
-
const remote = firstRemoteClient(clients);
|
|
1816
|
-
if (!remote) {
|
|
1817
|
-
return;
|
|
1818
|
-
}
|
|
1901
|
+
var send4 = async (remote, spaceId, content) => send3(remote, spaceId, content);
|
|
1902
|
+
var replyToMessage2 = async (remote, spaceId, msgId, content) => replyToMessage(remote, spaceId, msgId, content);
|
|
1903
|
+
var editMessage2 = async (remote, spaceId, msgId, content) => editMessage(remote, spaceId, msgId, content);
|
|
1904
|
+
var reactToMessage2 = async (remote, spaceId, target, reaction) => {
|
|
1819
1905
|
await reactToMessage(remote, spaceId, target, reaction);
|
|
1820
1906
|
};
|
|
1821
|
-
var getMessage4 = async (
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1907
|
+
var getMessage4 = async (remote, spaceId, msgId, phone) => getMessage3(remote, spaceId, msgId, phone);
|
|
1908
|
+
|
|
1909
|
+
// src/providers/imessage/remote/client.ts
|
|
1910
|
+
var isSharedMode = (clients) => clients.length === 1 && clients[0]?.phone === SHARED_PHONE;
|
|
1911
|
+
var availablePhones = (clients) => clients.map((c) => c.phone);
|
|
1912
|
+
var clientForPhone = (clients, phone) => {
|
|
1913
|
+
if (isSharedMode(clients)) {
|
|
1914
|
+
const entry2 = clients[0];
|
|
1915
|
+
if (!entry2) {
|
|
1916
|
+
throw new Error("No iMessage clients configured");
|
|
1917
|
+
}
|
|
1918
|
+
return entry2.client;
|
|
1919
|
+
}
|
|
1920
|
+
const entry = clients.find((c) => c.phone === phone);
|
|
1921
|
+
if (!entry) {
|
|
1922
|
+
const list = availablePhones(clients).join(", ") || "<none>";
|
|
1923
|
+
throw new Error(
|
|
1924
|
+
`No iMessage client serves phone ${phone}. Available: ${list}`
|
|
1925
|
+
);
|
|
1825
1926
|
}
|
|
1826
|
-
return
|
|
1927
|
+
return entry.client;
|
|
1928
|
+
};
|
|
1929
|
+
var randomPhone = (clients) => {
|
|
1930
|
+
if (clients.length === 0) {
|
|
1931
|
+
throw new Error("No iMessage phones configured for this account");
|
|
1932
|
+
}
|
|
1933
|
+
if (isSharedMode(clients)) {
|
|
1934
|
+
return SHARED_PHONE;
|
|
1935
|
+
}
|
|
1936
|
+
const entry = clients[Math.floor(Math.random() * clients.length)];
|
|
1937
|
+
if (!entry) {
|
|
1938
|
+
throw new Error("No iMessage phones configured for this account");
|
|
1939
|
+
}
|
|
1940
|
+
return entry.phone;
|
|
1827
1941
|
};
|
|
1828
|
-
|
|
1829
|
-
// src/providers/imessage/types.ts
|
|
1830
|
-
import { IMessageSDK } from "@photon-ai/imessage-kit";
|
|
1831
|
-
import z2 from "zod";
|
|
1832
|
-
var isLocal = (client) => client instanceof IMessageSDK;
|
|
1833
|
-
var clientEntry = z2.object({ address: z2.string(), token: z2.string() });
|
|
1834
|
-
var configSchema = z2.union([
|
|
1835
|
-
z2.object({ local: z2.literal(true) }),
|
|
1836
|
-
z2.object({
|
|
1837
|
-
local: z2.literal(false).optional().default(false),
|
|
1838
|
-
clients: clientEntry.or(z2.array(clientEntry)).optional()
|
|
1839
|
-
})
|
|
1840
|
-
]);
|
|
1841
|
-
var userSchema = z2.object({});
|
|
1842
|
-
var spaceSchema = z2.object({
|
|
1843
|
-
id: z2.string(),
|
|
1844
|
-
type: z2.enum(["dm", "group"])
|
|
1845
|
-
});
|
|
1846
|
-
var messageSchema = z2.object({
|
|
1847
|
-
partIndex: z2.number().int().nonnegative().optional(),
|
|
1848
|
-
parentId: z2.string().optional()
|
|
1849
|
-
});
|
|
1850
1942
|
|
|
1851
1943
|
// src/providers/imessage/index.ts
|
|
1852
1944
|
var isPollContent = (content) => content.type === "poll" || content.type === "poll_option";
|
|
@@ -1857,11 +1949,48 @@ var imessage = definePlatform("iMessage", {
|
|
|
1857
1949
|
message: MessageEffect2
|
|
1858
1950
|
}
|
|
1859
1951
|
},
|
|
1952
|
+
lifecycle: {
|
|
1953
|
+
createClient: async ({
|
|
1954
|
+
config,
|
|
1955
|
+
projectId,
|
|
1956
|
+
projectSecret
|
|
1957
|
+
}) => {
|
|
1958
|
+
if (config.local) {
|
|
1959
|
+
return new IMessageSDK2();
|
|
1960
|
+
}
|
|
1961
|
+
if (config.clients) {
|
|
1962
|
+
const entries = Array.isArray(config.clients) ? config.clients : [config.clients];
|
|
1963
|
+
return entries.map((e) => ({
|
|
1964
|
+
phone: e.phone,
|
|
1965
|
+
client: createClient2({
|
|
1966
|
+
address: e.address,
|
|
1967
|
+
tls: true,
|
|
1968
|
+
token: e.token
|
|
1969
|
+
})
|
|
1970
|
+
}));
|
|
1971
|
+
}
|
|
1972
|
+
if (!(projectId && projectSecret)) {
|
|
1973
|
+
throw new Error(
|
|
1974
|
+
"iMessage requires projectId and projectSecret. Either pass credentials to Spectrum(), use local mode: imessage.config({ local: true }), or provide explicit client config: imessage.config({ clients: [...] })"
|
|
1975
|
+
);
|
|
1976
|
+
}
|
|
1977
|
+
return await createCloudClients(projectId, projectSecret);
|
|
1978
|
+
},
|
|
1979
|
+
destroyClient: async ({ client }) => {
|
|
1980
|
+
if (isLocal(client)) {
|
|
1981
|
+
await client.close();
|
|
1982
|
+
return;
|
|
1983
|
+
}
|
|
1984
|
+
await disposeCloudAuth(client);
|
|
1985
|
+
await Promise.all(client.map((entry) => entry.client.close()));
|
|
1986
|
+
}
|
|
1987
|
+
},
|
|
1860
1988
|
user: {
|
|
1861
1989
|
resolve: async ({ input }) => ({ id: input.userID })
|
|
1862
1990
|
},
|
|
1863
1991
|
space: {
|
|
1864
1992
|
schema: spaceSchema,
|
|
1993
|
+
params: spaceParamsSchema,
|
|
1865
1994
|
resolve: async ({ input, client }) => {
|
|
1866
1995
|
if (isLocal(client)) {
|
|
1867
1996
|
throw UnsupportedError.action(
|
|
@@ -1873,55 +2002,26 @@ var imessage = definePlatform("iMessage", {
|
|
|
1873
2002
|
if (input.users.length === 0) {
|
|
1874
2003
|
throw new Error("iMessage space creation requires at least one user");
|
|
1875
2004
|
}
|
|
2005
|
+
if (client.length === 0) {
|
|
2006
|
+
throw new Error("No iMessage clients configured");
|
|
2007
|
+
}
|
|
2008
|
+
const phone = isSharedMode(client) ? SHARED_PHONE : input.params?.phone ?? randomPhone(client);
|
|
2009
|
+
const remote = clientForPhone(client, phone);
|
|
1876
2010
|
const addresses = input.users.map((u) => u.id);
|
|
1877
2011
|
if (input.users.length === 1) {
|
|
1878
2012
|
return {
|
|
1879
2013
|
id: directChat(addresses[0] ?? ""),
|
|
1880
|
-
type: "dm"
|
|
2014
|
+
type: "dm",
|
|
2015
|
+
phone
|
|
1881
2016
|
};
|
|
1882
2017
|
}
|
|
1883
|
-
const remote = client[0];
|
|
1884
|
-
if (!remote) {
|
|
1885
|
-
throw new Error("No remote iMessage client available");
|
|
1886
|
-
}
|
|
1887
2018
|
const { chat } = await remote.chats.create(addresses);
|
|
1888
|
-
return { id: chat.guid, type: "group" };
|
|
2019
|
+
return { id: chat.guid, type: "group", phone };
|
|
1889
2020
|
}
|
|
1890
2021
|
},
|
|
1891
2022
|
message: {
|
|
1892
2023
|
schema: messageSchema
|
|
1893
2024
|
},
|
|
1894
|
-
lifecycle: {
|
|
1895
|
-
createClient: async ({
|
|
1896
|
-
config,
|
|
1897
|
-
projectId,
|
|
1898
|
-
projectSecret
|
|
1899
|
-
}) => {
|
|
1900
|
-
if (config.local) {
|
|
1901
|
-
return new IMessageSDK2();
|
|
1902
|
-
}
|
|
1903
|
-
if (config.clients) {
|
|
1904
|
-
const entries = Array.isArray(config.clients) ? config.clients : [config.clients];
|
|
1905
|
-
return entries.map(
|
|
1906
|
-
(e) => createClient2({ address: e.address, tls: true, token: e.token })
|
|
1907
|
-
);
|
|
1908
|
-
}
|
|
1909
|
-
if (!(projectId && projectSecret)) {
|
|
1910
|
-
throw new Error(
|
|
1911
|
-
"iMessage requires projectId and projectSecret. Either pass credentials to Spectrum(), use local mode: imessage.config({ local: true }), or provide explicit client config: imessage.config({ clients: [...] })"
|
|
1912
|
-
);
|
|
1913
|
-
}
|
|
1914
|
-
return await createCloudClients(projectId, projectSecret);
|
|
1915
|
-
},
|
|
1916
|
-
destroyClient: async ({ client }) => {
|
|
1917
|
-
if (isLocal(client)) {
|
|
1918
|
-
await client.close();
|
|
1919
|
-
return;
|
|
1920
|
-
}
|
|
1921
|
-
await disposeCloudAuth(client);
|
|
1922
|
-
await Promise.all(client.map((c) => c.close()));
|
|
1923
|
-
}
|
|
1924
|
-
},
|
|
1925
2025
|
events: {
|
|
1926
2026
|
messages: ({ client }) => isLocal(client) ? messages2(client) : messages4(client)
|
|
1927
2027
|
},
|
|
@@ -1930,19 +2030,22 @@ var imessage = definePlatform("iMessage", {
|
|
|
1930
2030
|
if (isLocal(client)) {
|
|
1931
2031
|
return await send2(client, space.id, content);
|
|
1932
2032
|
}
|
|
1933
|
-
|
|
2033
|
+
const remote = clientForPhone(client, space.phone);
|
|
2034
|
+
return await send4(remote, space.id, content);
|
|
1934
2035
|
},
|
|
1935
2036
|
startTyping: async ({ space, client }) => {
|
|
1936
2037
|
if (isLocal(client)) {
|
|
1937
2038
|
return;
|
|
1938
2039
|
}
|
|
1939
|
-
|
|
2040
|
+
const remote = clientForPhone(client, space.phone);
|
|
2041
|
+
await startTyping2(remote, space.id);
|
|
1940
2042
|
},
|
|
1941
2043
|
stopTyping: async ({ space, client }) => {
|
|
1942
2044
|
if (isLocal(client)) {
|
|
1943
2045
|
return;
|
|
1944
2046
|
}
|
|
1945
|
-
|
|
2047
|
+
const remote = clientForPhone(client, space.phone);
|
|
2048
|
+
await stopTyping2(remote, space.id);
|
|
1946
2049
|
},
|
|
1947
2050
|
reactToMessage: async ({ space, target, reaction, client }) => {
|
|
1948
2051
|
if (isLocal(client)) {
|
|
@@ -1955,8 +2058,9 @@ var imessage = definePlatform("iMessage", {
|
|
|
1955
2058
|
"iMessage polls do not support reactions"
|
|
1956
2059
|
);
|
|
1957
2060
|
}
|
|
2061
|
+
const remote = clientForPhone(client, space.phone);
|
|
1958
2062
|
await reactToMessage2(
|
|
1959
|
-
|
|
2063
|
+
remote,
|
|
1960
2064
|
space.id,
|
|
1961
2065
|
target,
|
|
1962
2066
|
reaction
|
|
@@ -1973,19 +2077,22 @@ var imessage = definePlatform("iMessage", {
|
|
|
1973
2077
|
"iMessage polls do not support replies"
|
|
1974
2078
|
);
|
|
1975
2079
|
}
|
|
1976
|
-
|
|
2080
|
+
const remote = clientForPhone(client, space.phone);
|
|
2081
|
+
return await replyToMessage2(remote, space.id, messageId, content);
|
|
1977
2082
|
},
|
|
1978
2083
|
editMessage: async ({ space, messageId, content, client }) => {
|
|
1979
2084
|
if (isLocal(client)) {
|
|
1980
2085
|
throw UnsupportedError.action("edit", "iMessage (local mode)");
|
|
1981
2086
|
}
|
|
1982
|
-
|
|
2087
|
+
const remote = clientForPhone(client, space.phone);
|
|
2088
|
+
await editMessage2(remote, space.id, messageId, content);
|
|
1983
2089
|
},
|
|
1984
2090
|
getMessage: async ({ space, messageId, client }) => {
|
|
1985
2091
|
if (isLocal(client)) {
|
|
1986
2092
|
return getMessage2(client, messageId);
|
|
1987
2093
|
}
|
|
1988
|
-
|
|
2094
|
+
const remote = clientForPhone(client, space.phone);
|
|
2095
|
+
return getMessage4(remote, space.id, messageId, space.phone);
|
|
1989
2096
|
}
|
|
1990
2097
|
}
|
|
1991
2098
|
});
|