spectrum-ts 1.0.1 → 1.1.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-2Y5GBI6W.js → chunk-PXX7ISZ6.js} +59 -0
- package/dist/index.d.ts +38 -3
- package/dist/index.js +5 -1
- package/dist/providers/imessage/index.d.ts +1 -1
- package/dist/providers/imessage/index.js +344 -12
- package/dist/providers/terminal/index.d.ts +21 -1
- package/dist/providers/whatsapp-business/index.d.ts +1 -1
- package/dist/providers/whatsapp-business/index.js +93 -5
- package/dist/{types-D5KhSXLy.d.ts → types-DJQLFwWW.d.ts} +20 -0
- package/package.json +2 -2
|
@@ -1,3 +1,58 @@
|
|
|
1
|
+
// src/content/poll.ts
|
|
2
|
+
import z from "zod";
|
|
3
|
+
var pollChoiceSchema = z.object({
|
|
4
|
+
title: z.string().nonempty()
|
|
5
|
+
});
|
|
6
|
+
var pollSchema = z.object({
|
|
7
|
+
type: z.literal("poll"),
|
|
8
|
+
title: z.string().nonempty().max(300),
|
|
9
|
+
options: z.array(pollChoiceSchema).min(2).max(10)
|
|
10
|
+
});
|
|
11
|
+
var pollOptionSchema = z.object({
|
|
12
|
+
type: z.literal("poll_option"),
|
|
13
|
+
option: pollChoiceSchema,
|
|
14
|
+
poll: pollSchema,
|
|
15
|
+
selected: z.boolean(),
|
|
16
|
+
title: z.string().nonempty()
|
|
17
|
+
}).superRefine((value, ctx) => {
|
|
18
|
+
if (value.title !== value.option.title) {
|
|
19
|
+
ctx.addIssue({
|
|
20
|
+
code: "custom",
|
|
21
|
+
message: "poll_option title must match option.title",
|
|
22
|
+
path: ["title"]
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
if (!value.poll.options.some(
|
|
26
|
+
(pollOption) => pollOption.title === value.option.title
|
|
27
|
+
)) {
|
|
28
|
+
ctx.addIssue({
|
|
29
|
+
code: "custom",
|
|
30
|
+
message: "poll_option option must exist in poll.options",
|
|
31
|
+
path: ["option"]
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
var asPoll = (input) => pollSchema.parse({ type: "poll", ...input });
|
|
36
|
+
var asPollOption = (input) => pollOptionSchema.parse({
|
|
37
|
+
type: "poll_option",
|
|
38
|
+
...input,
|
|
39
|
+
title: input.option.title
|
|
40
|
+
});
|
|
41
|
+
var option = (title) => ({ title });
|
|
42
|
+
var normalize = (raw) => typeof raw === "string" ? { title: raw } : { title: raw.title };
|
|
43
|
+
var collectOptions = (args) => {
|
|
44
|
+
const [first] = args;
|
|
45
|
+
if (args.length === 1 && Array.isArray(first)) {
|
|
46
|
+
return first;
|
|
47
|
+
}
|
|
48
|
+
return args;
|
|
49
|
+
};
|
|
50
|
+
function poll(title, ...rest) {
|
|
51
|
+
return {
|
|
52
|
+
build: async () => asPoll({ title, options: collectOptions(rest).map(normalize) })
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
|
|
1
56
|
// src/utils/stream.ts
|
|
2
57
|
import { Repeater } from "@repeaterjs/repeater";
|
|
3
58
|
function stream(setup) {
|
|
@@ -122,6 +177,10 @@ var cloud = {
|
|
|
122
177
|
};
|
|
123
178
|
|
|
124
179
|
export {
|
|
180
|
+
asPoll,
|
|
181
|
+
asPollOption,
|
|
182
|
+
option,
|
|
183
|
+
poll,
|
|
125
184
|
stream,
|
|
126
185
|
mergeStreams,
|
|
127
186
|
SpectrumCloudError,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { C as ContentBuilder, U as User, M as Message, a as ContentInput, b as Content, P as ProviderMessage, c as PlatformDef, d as Platform, e as PlatformProviderConfig, S as SpectrumLike, f as CustomEventStreams, g as Space, I as InboundMessage, O as OutboundMessage } from './types-
|
|
2
|
-
export { A as AnyPlatformDef, E as EventProducer, h as PlatformInstance, i as PlatformMessage, j as PlatformSpace, k as PlatformUser, l as SchemaMessage } from './types-
|
|
1
|
+
import { C as ContentBuilder, U as User, M as Message, a as ContentInput, b as Content, P as ProviderMessage, c as PlatformDef, d as Platform, e as PlatformProviderConfig, S as SpectrumLike, f as CustomEventStreams, g as Space, I as InboundMessage, O as OutboundMessage } from './types-DJQLFwWW.js';
|
|
2
|
+
export { A as AnyPlatformDef, E as EventProducer, h as PlatformInstance, i as PlatformMessage, j as PlatformSpace, k as PlatformUser, l as SchemaMessage } from './types-DJQLFwWW.js';
|
|
3
3
|
import vCard from 'vcf';
|
|
4
4
|
import z__default from 'zod';
|
|
5
5
|
export { M as ManagedStream, m as mergeStreams, s as stream } from './stream-B55k7W8-.js';
|
|
@@ -138,6 +138,41 @@ declare const groupSchema: z__default.ZodObject<{
|
|
|
138
138
|
type Group = z__default.infer<typeof groupSchema>;
|
|
139
139
|
declare function group(...items: [ContentInput, ContentInput, ...ContentInput[]]): ContentBuilder;
|
|
140
140
|
|
|
141
|
+
declare const pollChoiceSchema: z__default.ZodObject<{
|
|
142
|
+
title: z__default.ZodString;
|
|
143
|
+
}, z__default.core.$strip>;
|
|
144
|
+
declare const pollSchema: z__default.ZodObject<{
|
|
145
|
+
type: z__default.ZodLiteral<"poll">;
|
|
146
|
+
title: z__default.ZodString;
|
|
147
|
+
options: z__default.ZodArray<z__default.ZodObject<{
|
|
148
|
+
title: z__default.ZodString;
|
|
149
|
+
}, z__default.core.$strip>>;
|
|
150
|
+
}, z__default.core.$strip>;
|
|
151
|
+
declare const pollOptionSchema: z__default.ZodObject<{
|
|
152
|
+
type: z__default.ZodLiteral<"poll_option">;
|
|
153
|
+
option: z__default.ZodObject<{
|
|
154
|
+
title: z__default.ZodString;
|
|
155
|
+
}, z__default.core.$strip>;
|
|
156
|
+
poll: z__default.ZodObject<{
|
|
157
|
+
type: z__default.ZodLiteral<"poll">;
|
|
158
|
+
title: z__default.ZodString;
|
|
159
|
+
options: z__default.ZodArray<z__default.ZodObject<{
|
|
160
|
+
title: z__default.ZodString;
|
|
161
|
+
}, z__default.core.$strip>>;
|
|
162
|
+
}, z__default.core.$strip>;
|
|
163
|
+
selected: z__default.ZodBoolean;
|
|
164
|
+
title: z__default.ZodString;
|
|
165
|
+
}, z__default.core.$strip>;
|
|
166
|
+
type Poll = z__default.infer<typeof pollSchema>;
|
|
167
|
+
type PollChoice = z__default.infer<typeof pollChoiceSchema>;
|
|
168
|
+
type PollOption = z__default.infer<typeof pollOptionSchema>;
|
|
169
|
+
type PollChoiceInput = string | {
|
|
170
|
+
title: string;
|
|
171
|
+
};
|
|
172
|
+
declare const option: (title: string) => PollChoice;
|
|
173
|
+
declare function poll(title: string, options: PollChoiceInput[]): ContentBuilder;
|
|
174
|
+
declare function poll(title: string, ...options: PollChoiceInput[]): ContentBuilder;
|
|
175
|
+
|
|
141
176
|
declare const reactionSchema: z__default.ZodObject<{
|
|
142
177
|
type: z__default.ZodLiteral<"reaction">;
|
|
143
178
|
emoji: z__default.ZodString;
|
|
@@ -2234,4 +2269,4 @@ declare class UnsupportedError extends Error {
|
|
|
2234
2269
|
declare const fromVCard: (vcf: string) => ContactInput;
|
|
2235
2270
|
declare const toVCard: (contact: Contact) => Promise<string>;
|
|
2236
2271
|
|
|
2237
|
-
export { type CloudPlatform, type Contact, type ContactAddress, type ContactDetails, type ContactEmail, type ContactInput, type ContactName, type ContactOrg, type ContactPhone, Content, ContentBuilder, ContentInput, type DedicatedTokenData, Emoji, type EmojiKey, type Group, type ImessageInfoData, Message, Platform, PlatformDef, PlatformProviderConfig, type PlatformStatus, type PlatformsData, type Reaction, type Richlink, type SharedTokenData, Space, Spectrum, SpectrumCloudError, type SpectrumInstance, type SubscriptionData, type SubscriptionStatus, type TokenData, UnsupportedError, type UnsupportedKind, User, type Voice, attachment, cloud, contact, custom, definePlatform, fromVCard, group, reaction, resolveContents, richlink, text, toVCard, voice };
|
|
2272
|
+
export { type CloudPlatform, type Contact, type ContactAddress, type ContactDetails, type ContactEmail, type ContactInput, type ContactName, type ContactOrg, type ContactPhone, Content, ContentBuilder, ContentInput, type DedicatedTokenData, Emoji, type EmojiKey, type Group, type ImessageInfoData, Message, Platform, PlatformDef, PlatformProviderConfig, type PlatformStatus, type PlatformsData, type Poll, type PollChoice, type PollChoiceInput, type PollOption, type Reaction, type Richlink, type SharedTokenData, Space, Spectrum, SpectrumCloudError, type SpectrumInstance, type SubscriptionData, type SubscriptionStatus, type TokenData, UnsupportedError, type UnsupportedKind, User, type Voice, attachment, cloud, contact, custom, definePlatform, fromVCard, group, option, poll, reaction, resolveContents, richlink, text, toVCard, voice };
|
package/dist/index.js
CHANGED
|
@@ -9,8 +9,10 @@ import {
|
|
|
9
9
|
SpectrumCloudError,
|
|
10
10
|
cloud,
|
|
11
11
|
mergeStreams,
|
|
12
|
+
option,
|
|
13
|
+
poll,
|
|
12
14
|
stream
|
|
13
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-PXX7ISZ6.js";
|
|
14
16
|
import {
|
|
15
17
|
UnsupportedError,
|
|
16
18
|
attachment,
|
|
@@ -2205,6 +2207,8 @@ export {
|
|
|
2205
2207
|
fromVCard,
|
|
2206
2208
|
group,
|
|
2207
2209
|
mergeStreams,
|
|
2210
|
+
option,
|
|
2211
|
+
poll,
|
|
2208
2212
|
reaction,
|
|
2209
2213
|
resolveContents,
|
|
2210
2214
|
richlink,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { M as ManagedStream } from '../../stream-B55k7W8-.js';
|
|
2
|
-
import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-
|
|
2
|
+
import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DJQLFwWW.js';
|
|
3
3
|
import * as zod_v4_core from 'zod/v4/core';
|
|
4
4
|
import * as z from 'zod';
|
|
5
5
|
import z__default from 'zod';
|
|
@@ -3,10 +3,12 @@ import {
|
|
|
3
3
|
asRichlink
|
|
4
4
|
} from "../../chunk-LAGNM6I7.js";
|
|
5
5
|
import {
|
|
6
|
+
asPoll,
|
|
7
|
+
asPollOption,
|
|
6
8
|
cloud,
|
|
7
9
|
mergeStreams,
|
|
8
10
|
stream
|
|
9
|
-
} from "../../chunk-
|
|
11
|
+
} from "../../chunk-PXX7ISZ6.js";
|
|
10
12
|
import {
|
|
11
13
|
UnsupportedError,
|
|
12
14
|
asAttachment,
|
|
@@ -246,6 +248,8 @@ var send = async (client, spaceId, content) => {
|
|
|
246
248
|
);
|
|
247
249
|
return synthSendResult();
|
|
248
250
|
}
|
|
251
|
+
case "poll":
|
|
252
|
+
throw UnsupportedError.content("poll", "iMessage (local mode)");
|
|
249
253
|
default:
|
|
250
254
|
throw UnsupportedError.content(content.type, "iMessage (local mode)");
|
|
251
255
|
}
|
|
@@ -409,12 +413,98 @@ var MessageCache = class {
|
|
|
409
413
|
this.map.clear();
|
|
410
414
|
}
|
|
411
415
|
};
|
|
412
|
-
var
|
|
416
|
+
var PollCache = class {
|
|
417
|
+
map = /* @__PURE__ */ new Map();
|
|
418
|
+
max;
|
|
419
|
+
selectionEventTimesByPoll = /* @__PURE__ */ new Map();
|
|
420
|
+
selectionsByPoll = /* @__PURE__ */ new Map();
|
|
421
|
+
constructor(max = DEFAULT_MAX) {
|
|
422
|
+
this.max = max;
|
|
423
|
+
}
|
|
424
|
+
get(id) {
|
|
425
|
+
return this.map.get(id);
|
|
426
|
+
}
|
|
427
|
+
set(id, poll) {
|
|
428
|
+
if (this.map.has(id)) {
|
|
429
|
+
this.map.delete(id);
|
|
430
|
+
}
|
|
431
|
+
this.map.set(id, poll);
|
|
432
|
+
if (this.map.size > this.max) {
|
|
433
|
+
const first = this.map.keys().next().value;
|
|
434
|
+
if (first !== void 0) {
|
|
435
|
+
this.map.delete(first);
|
|
436
|
+
this.selectionEventTimesByPoll.delete(first);
|
|
437
|
+
this.selectionsByPoll.delete(first);
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
clear() {
|
|
442
|
+
this.map.clear();
|
|
443
|
+
this.selectionEventTimesByPoll.clear();
|
|
444
|
+
this.selectionsByPoll.clear();
|
|
445
|
+
}
|
|
446
|
+
actorSelectionDeltas(pollId, actorId, optionIds) {
|
|
447
|
+
const previous = this.selectionsByPoll.get(pollId)?.get(actorId);
|
|
448
|
+
if (!previous) {
|
|
449
|
+
return optionIds.map((optionId) => ({ optionId, selected: true }));
|
|
450
|
+
}
|
|
451
|
+
const current = new Set(optionIds);
|
|
452
|
+
const selected = optionIds.filter((optionId) => !previous.has(optionId)).map((optionId) => ({ optionId, selected: true }));
|
|
453
|
+
const deselected = [...previous].filter((optionId) => !current.has(optionId)).map((optionId) => ({ optionId, selected: false }));
|
|
454
|
+
return [...selected, ...deselected];
|
|
455
|
+
}
|
|
456
|
+
clearedActorSelectionDeltas(pollId, actorId) {
|
|
457
|
+
const previous = this.selectionsByPoll.get(pollId)?.get(actorId);
|
|
458
|
+
if (!previous) {
|
|
459
|
+
return [];
|
|
460
|
+
}
|
|
461
|
+
return [...previous].map((optionId) => ({ optionId, selected: false }));
|
|
462
|
+
}
|
|
463
|
+
actorSelection(pollId, actorId) {
|
|
464
|
+
const selection = this.selectionsByPoll.get(pollId)?.get(actorId);
|
|
465
|
+
return selection ? [...selection] : void 0;
|
|
466
|
+
}
|
|
467
|
+
commitActorSelection(pollId, actorId, optionIds, at) {
|
|
468
|
+
let selections = this.selectionsByPoll.get(pollId);
|
|
469
|
+
if (!selections) {
|
|
470
|
+
selections = /* @__PURE__ */ new Map();
|
|
471
|
+
this.selectionsByPoll.set(pollId, selections);
|
|
472
|
+
}
|
|
473
|
+
selections.set(actorId, new Set(optionIds));
|
|
474
|
+
if (!at) {
|
|
475
|
+
return;
|
|
476
|
+
}
|
|
477
|
+
let eventTimes = this.selectionEventTimesByPoll.get(pollId);
|
|
478
|
+
if (!eventTimes) {
|
|
479
|
+
eventTimes = /* @__PURE__ */ new Map();
|
|
480
|
+
this.selectionEventTimesByPoll.set(pollId, eventTimes);
|
|
481
|
+
}
|
|
482
|
+
const eventTime = at.getTime();
|
|
483
|
+
const previousTime = eventTimes.get(actorId);
|
|
484
|
+
if (previousTime === void 0 || eventTime >= previousTime) {
|
|
485
|
+
eventTimes.set(actorId, eventTime);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
isStaleActorSelectionEvent(pollId, actorId, at) {
|
|
489
|
+
const previousTime = this.selectionEventTimesByPoll.get(pollId)?.get(actorId);
|
|
490
|
+
return previousTime !== void 0 && at.getTime() < previousTime;
|
|
491
|
+
}
|
|
492
|
+
};
|
|
493
|
+
var messageCaches = /* @__PURE__ */ new WeakMap();
|
|
494
|
+
var pollCaches = /* @__PURE__ */ new WeakMap();
|
|
413
495
|
var getMessageCache = (owner) => {
|
|
414
|
-
let cache =
|
|
496
|
+
let cache = messageCaches.get(owner);
|
|
415
497
|
if (!cache) {
|
|
416
498
|
cache = new MessageCache();
|
|
417
|
-
|
|
499
|
+
messageCaches.set(owner, cache);
|
|
500
|
+
}
|
|
501
|
+
return cache;
|
|
502
|
+
};
|
|
503
|
+
var getPollCache = (owner) => {
|
|
504
|
+
let cache = pollCaches.get(owner);
|
|
505
|
+
if (!cache) {
|
|
506
|
+
cache = new PollCache();
|
|
507
|
+
pollCaches.set(owner, cache);
|
|
418
508
|
}
|
|
419
509
|
return cache;
|
|
420
510
|
};
|
|
@@ -735,13 +825,222 @@ var toMessages2 = async (client, cache, event) => {
|
|
|
735
825
|
cacheMessage(cache, msg);
|
|
736
826
|
return [msg];
|
|
737
827
|
};
|
|
738
|
-
var
|
|
739
|
-
|
|
828
|
+
var isVotedPollEvent = (event) => event.delta.type === "voted";
|
|
829
|
+
var isUnvotedPollEvent = (event) => event.delta.type === "unvoted";
|
|
830
|
+
var toCachedPoll = (input) => {
|
|
831
|
+
const poll = asPoll({
|
|
832
|
+
title: input.title,
|
|
833
|
+
options: input.options.map((optionInfo) => ({
|
|
834
|
+
title: optionInfo.text
|
|
835
|
+
}))
|
|
836
|
+
});
|
|
837
|
+
const optionsByIdentifier = /* @__PURE__ */ new Map();
|
|
838
|
+
for (const [index, optionInfo] of input.options.entries()) {
|
|
839
|
+
const option = poll.options[index];
|
|
840
|
+
if (option && optionInfo.optionIdentifier) {
|
|
841
|
+
optionsByIdentifier.set(optionInfo.optionIdentifier, option);
|
|
842
|
+
}
|
|
843
|
+
}
|
|
844
|
+
return { poll, optionsByIdentifier };
|
|
845
|
+
};
|
|
846
|
+
var cachePollInfo = (cache, info) => {
|
|
847
|
+
const cached = toCachedPoll(info);
|
|
848
|
+
cache.set(info.messageGuid, cached);
|
|
849
|
+
return cached;
|
|
850
|
+
};
|
|
851
|
+
var cachePollEvent = (cache, event) => {
|
|
852
|
+
if (event.delta.type === "created" || event.delta.type === "optionAdded") {
|
|
853
|
+
try {
|
|
854
|
+
const cached = toCachedPoll({
|
|
855
|
+
title: event.delta.title,
|
|
856
|
+
options: event.delta.options
|
|
857
|
+
});
|
|
858
|
+
cache.set(event.pollMessageGuid, cached);
|
|
859
|
+
return cached;
|
|
860
|
+
} catch (e) {
|
|
861
|
+
console.error("[spectrum-ts][imessage][poll] failed to cache poll", e);
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
};
|
|
865
|
+
var fetchPollInfo = async (client, cache, event) => {
|
|
866
|
+
try {
|
|
867
|
+
const info = await client.polls.get(event.pollMessageGuid);
|
|
868
|
+
cachePollInfo(cache, info);
|
|
869
|
+
return info;
|
|
870
|
+
} catch (e) {
|
|
871
|
+
console.error("[spectrum-ts][imessage][poll] failed to fetch poll", e);
|
|
872
|
+
return;
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
var resolvePoll = async (client, cache, event) => {
|
|
876
|
+
const pollId = event.pollMessageGuid;
|
|
877
|
+
const cached = cache.get(pollId);
|
|
878
|
+
if (cached) {
|
|
879
|
+
return cached;
|
|
880
|
+
}
|
|
881
|
+
try {
|
|
882
|
+
const info = await client.polls.get(event.pollMessageGuid);
|
|
883
|
+
return cachePollInfo(cache, info);
|
|
884
|
+
} catch (e) {
|
|
885
|
+
console.error("[spectrum-ts][imessage][poll] failed to resolve poll", e);
|
|
886
|
+
return;
|
|
887
|
+
}
|
|
888
|
+
};
|
|
889
|
+
var buildPollOptionMessage = (input) => {
|
|
890
|
+
const option = input.cached.optionsByIdentifier.get(input.optionId);
|
|
891
|
+
if (!option) {
|
|
892
|
+
return;
|
|
893
|
+
}
|
|
894
|
+
const action = input.selected ? "selected" : "deselected";
|
|
895
|
+
return {
|
|
896
|
+
id: `${input.event.pollMessageGuid}:${input.senderAddress}:${input.optionId}:${action}:${input.event.at.getTime()}`,
|
|
897
|
+
sender: { id: input.senderAddress },
|
|
898
|
+
space: {
|
|
899
|
+
id: input.chatGuid,
|
|
900
|
+
type: input.chatGuid.includes(";+;") ? "group" : "dm"
|
|
901
|
+
},
|
|
902
|
+
timestamp: input.event.at,
|
|
903
|
+
content: asPollOption({
|
|
904
|
+
option,
|
|
905
|
+
poll: input.cached.poll,
|
|
906
|
+
selected: input.selected
|
|
907
|
+
})
|
|
908
|
+
};
|
|
909
|
+
};
|
|
910
|
+
var buildPollOptionMessages = (input) => {
|
|
911
|
+
const messages3 = [];
|
|
912
|
+
for (const delta of input.deltas) {
|
|
913
|
+
const message = buildPollOptionMessage({
|
|
914
|
+
cached: input.cached,
|
|
915
|
+
chatGuid: input.chatGuid,
|
|
916
|
+
event: input.event,
|
|
917
|
+
optionId: delta.optionId,
|
|
918
|
+
selected: delta.selected,
|
|
919
|
+
senderAddress: input.senderAddress
|
|
920
|
+
});
|
|
921
|
+
if (message) {
|
|
922
|
+
messages3.push(message);
|
|
923
|
+
}
|
|
924
|
+
}
|
|
925
|
+
return messages3;
|
|
926
|
+
};
|
|
927
|
+
var allOptionIdsKnown = (cached, optionIds) => optionIds.every((optionId) => cached.optionsByIdentifier.has(optionId));
|
|
928
|
+
var refreshPollMetadata = async (client, pollCache, event, fallbackOptionIds) => {
|
|
929
|
+
const info = await fetchPollInfo(client, pollCache, event);
|
|
930
|
+
if (!info) {
|
|
931
|
+
return;
|
|
932
|
+
}
|
|
933
|
+
const refreshed = pollCache.get(info.messageGuid);
|
|
934
|
+
if (!refreshed) {
|
|
935
|
+
return;
|
|
936
|
+
}
|
|
937
|
+
return {
|
|
938
|
+
optionIds: [...fallbackOptionIds],
|
|
939
|
+
poll: refreshed
|
|
940
|
+
};
|
|
941
|
+
};
|
|
942
|
+
var toPollVoteMessages = async (client, pollCache, event) => {
|
|
943
|
+
const senderAddress = event.actor.address;
|
|
944
|
+
if (!senderAddress) {
|
|
945
|
+
return [];
|
|
946
|
+
}
|
|
947
|
+
const pollId = event.pollMessageGuid;
|
|
948
|
+
if (pollCache.isStaleActorSelectionEvent(pollId, senderAddress, event.at)) {
|
|
949
|
+
return [];
|
|
950
|
+
}
|
|
951
|
+
const cached = await resolvePoll(client, pollCache, event);
|
|
952
|
+
if (!cached) {
|
|
953
|
+
return [];
|
|
954
|
+
}
|
|
955
|
+
const chatGuidStr = event.chatGuid;
|
|
956
|
+
let currentOptionIds = [...event.delta.optionIdentifiers];
|
|
957
|
+
let resolvedPoll = cached;
|
|
958
|
+
if (currentOptionIds.some(
|
|
959
|
+
(optionId) => !resolvedPoll.optionsByIdentifier.has(optionId)
|
|
960
|
+
)) {
|
|
961
|
+
const snapshot = await refreshPollMetadata(
|
|
962
|
+
client,
|
|
963
|
+
pollCache,
|
|
964
|
+
event,
|
|
965
|
+
currentOptionIds
|
|
966
|
+
);
|
|
967
|
+
if (snapshot) {
|
|
968
|
+
currentOptionIds = snapshot.optionIds;
|
|
969
|
+
resolvedPoll = snapshot.poll;
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
if (!allOptionIdsKnown(resolvedPoll, currentOptionIds)) {
|
|
973
|
+
return [];
|
|
974
|
+
}
|
|
975
|
+
const deltas = pollCache.actorSelectionDeltas(
|
|
976
|
+
pollId,
|
|
977
|
+
senderAddress,
|
|
978
|
+
currentOptionIds
|
|
979
|
+
);
|
|
980
|
+
const messages3 = buildPollOptionMessages({
|
|
981
|
+
cached: resolvedPoll,
|
|
982
|
+
chatGuid: chatGuidStr,
|
|
983
|
+
deltas,
|
|
984
|
+
event,
|
|
985
|
+
senderAddress
|
|
986
|
+
});
|
|
987
|
+
pollCache.commitActorSelection(
|
|
988
|
+
pollId,
|
|
989
|
+
senderAddress,
|
|
990
|
+
currentOptionIds,
|
|
991
|
+
event.at
|
|
992
|
+
);
|
|
993
|
+
return messages3;
|
|
994
|
+
};
|
|
995
|
+
var toPollUnvoteMessages = async (client, pollCache, event) => {
|
|
996
|
+
const senderAddress = event.actor.address;
|
|
997
|
+
if (!senderAddress) {
|
|
998
|
+
return [];
|
|
999
|
+
}
|
|
1000
|
+
const pollId = event.pollMessageGuid;
|
|
1001
|
+
if (pollCache.isStaleActorSelectionEvent(pollId, senderAddress, event.at)) {
|
|
1002
|
+
return [];
|
|
1003
|
+
}
|
|
1004
|
+
const cached = await resolvePoll(client, pollCache, event);
|
|
1005
|
+
if (!cached) {
|
|
1006
|
+
return [];
|
|
1007
|
+
}
|
|
1008
|
+
const chatGuidStr = event.chatGuid;
|
|
1009
|
+
const messages3 = [];
|
|
1010
|
+
const deltas = pollCache.clearedActorSelectionDeltas(pollId, senderAddress);
|
|
1011
|
+
for (const delta of deltas) {
|
|
1012
|
+
const message = buildPollOptionMessage({
|
|
1013
|
+
cached,
|
|
1014
|
+
chatGuid: chatGuidStr,
|
|
1015
|
+
event,
|
|
1016
|
+
optionId: delta.optionId,
|
|
1017
|
+
selected: delta.selected,
|
|
1018
|
+
senderAddress
|
|
1019
|
+
});
|
|
1020
|
+
if (message) {
|
|
1021
|
+
messages3.push(message);
|
|
1022
|
+
}
|
|
1023
|
+
}
|
|
1024
|
+
pollCache.commitActorSelection(pollId, senderAddress, [], event.at);
|
|
1025
|
+
return messages3;
|
|
1026
|
+
};
|
|
1027
|
+
var toPollDeltaMessages = async (client, pollCache, event) => {
|
|
1028
|
+
if (isVotedPollEvent(event)) {
|
|
1029
|
+
return toPollVoteMessages(client, pollCache, event);
|
|
1030
|
+
}
|
|
1031
|
+
if (isUnvotedPollEvent(event)) {
|
|
1032
|
+
return toPollUnvoteMessages(client, pollCache, event);
|
|
1033
|
+
}
|
|
1034
|
+
return [];
|
|
1035
|
+
};
|
|
1036
|
+
var clientStream = (client, pollCache) => {
|
|
1037
|
+
const messageSub = client.messages.subscribe("message.received");
|
|
1038
|
+
const pollSub = client.polls.subscribe();
|
|
740
1039
|
const cache = getMessageCache(client);
|
|
741
1040
|
return stream((emit, end) => {
|
|
742
|
-
const
|
|
1041
|
+
const messagePump = (async () => {
|
|
743
1042
|
try {
|
|
744
|
-
for await (const event of
|
|
1043
|
+
for await (const event of messageSub) {
|
|
745
1044
|
if (event.message.isFromMe) {
|
|
746
1045
|
continue;
|
|
747
1046
|
}
|
|
@@ -749,14 +1048,30 @@ var clientStream = (client) => {
|
|
|
749
1048
|
await emit(message);
|
|
750
1049
|
}
|
|
751
1050
|
}
|
|
752
|
-
end();
|
|
753
1051
|
} catch (e) {
|
|
754
1052
|
end(e);
|
|
755
1053
|
}
|
|
756
1054
|
})();
|
|
1055
|
+
const pollPump = (async () => {
|
|
1056
|
+
try {
|
|
1057
|
+
for await (const event of pollSub) {
|
|
1058
|
+
cachePollEvent(pollCache, event);
|
|
1059
|
+
if (event.actor.isFromMe) {
|
|
1060
|
+
continue;
|
|
1061
|
+
}
|
|
1062
|
+
const messages3 = await toPollDeltaMessages(client, pollCache, event);
|
|
1063
|
+
for (const vote of messages3) {
|
|
1064
|
+
await emit(vote);
|
|
1065
|
+
}
|
|
1066
|
+
}
|
|
1067
|
+
} catch (e) {
|
|
1068
|
+
console.error("[spectrum-ts][imessage][poll] stream failed", e);
|
|
1069
|
+
}
|
|
1070
|
+
})();
|
|
757
1071
|
return async () => {
|
|
758
|
-
|
|
759
|
-
|
|
1072
|
+
messageSub.close();
|
|
1073
|
+
pollSub.close();
|
|
1074
|
+
await Promise.all([messagePump, pollPump]);
|
|
760
1075
|
};
|
|
761
1076
|
});
|
|
762
1077
|
};
|
|
@@ -774,7 +1089,10 @@ var sendContactAttachment = async (remote, content) => {
|
|
|
774
1089
|
const upload = await sendVCardAttachment(remote, vcardFileName2(content), vcf);
|
|
775
1090
|
return upload.guid;
|
|
776
1091
|
};
|
|
777
|
-
var messages2 = (clients) =>
|
|
1092
|
+
var messages2 = (clients) => {
|
|
1093
|
+
const pollCache = getPollCache(clients);
|
|
1094
|
+
return mergeStreams(clients.map((client) => clientStream(client, pollCache)));
|
|
1095
|
+
};
|
|
778
1096
|
var startTyping = async (clients, spaceId) => {
|
|
779
1097
|
const remote = clients[0];
|
|
780
1098
|
if (!remote) {
|
|
@@ -828,6 +1146,14 @@ var sendSingle = async (remote, chat, content) => {
|
|
|
828
1146
|
})
|
|
829
1147
|
);
|
|
830
1148
|
}
|
|
1149
|
+
case "poll":
|
|
1150
|
+
return toSendResult(
|
|
1151
|
+
await remote.polls.create(
|
|
1152
|
+
chat,
|
|
1153
|
+
content.title,
|
|
1154
|
+
content.options.map((o) => o.title)
|
|
1155
|
+
)
|
|
1156
|
+
);
|
|
831
1157
|
default:
|
|
832
1158
|
throw unsupportedContent(content.type);
|
|
833
1159
|
}
|
|
@@ -916,6 +1242,12 @@ var replyToMessage = async (clients, spaceId, msgId, content) => {
|
|
|
916
1242
|
})
|
|
917
1243
|
);
|
|
918
1244
|
}
|
|
1245
|
+
case "poll":
|
|
1246
|
+
throw UnsupportedError.content(
|
|
1247
|
+
"poll",
|
|
1248
|
+
PLATFORM,
|
|
1249
|
+
"polls cannot be sent as replies"
|
|
1250
|
+
);
|
|
919
1251
|
default:
|
|
920
1252
|
throw unsupportedContent(content.type);
|
|
921
1253
|
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { d as Platform, c as PlatformDef, P as ProviderMessage, M as Message } from '../../types-
|
|
1
|
+
import { d as Platform, c as PlatformDef, P as ProviderMessage, M as Message } from '../../types-DJQLFwWW.js';
|
|
2
2
|
import z__default from 'zod';
|
|
3
3
|
import 'hotscript';
|
|
4
4
|
|
|
@@ -120,6 +120,26 @@ declare const terminal: Platform<PlatformDef<"terminal", z__default.ZodObject<{
|
|
|
120
120
|
} | {
|
|
121
121
|
type: "group";
|
|
122
122
|
items: Message[];
|
|
123
|
+
} | {
|
|
124
|
+
type: "poll";
|
|
125
|
+
title: string;
|
|
126
|
+
options: {
|
|
127
|
+
title: string;
|
|
128
|
+
}[];
|
|
129
|
+
} | {
|
|
130
|
+
type: "poll_option";
|
|
131
|
+
option: {
|
|
132
|
+
title: string;
|
|
133
|
+
};
|
|
134
|
+
poll: {
|
|
135
|
+
type: "poll";
|
|
136
|
+
title: string;
|
|
137
|
+
options: {
|
|
138
|
+
title: string;
|
|
139
|
+
}[];
|
|
140
|
+
};
|
|
141
|
+
selected: boolean;
|
|
142
|
+
title: string;
|
|
123
143
|
};
|
|
124
144
|
sender: {
|
|
125
145
|
id: string;
|
|
@@ -2,7 +2,7 @@ import { M as ManagedStream } from '../../stream-B55k7W8-.js';
|
|
|
2
2
|
import { WhatsAppClient } from '@photon-ai/whatsapp-business';
|
|
3
3
|
import * as z from 'zod';
|
|
4
4
|
import z__default from 'zod';
|
|
5
|
-
import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-
|
|
5
|
+
import { l as SchemaMessage, d as Platform, c as PlatformDef, P as ProviderMessage } from '../../types-DJQLFwWW.js';
|
|
6
6
|
import * as zod_v4_core from 'zod/v4/core';
|
|
7
7
|
import 'hotscript';
|
|
8
8
|
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
|
+
asPollOption,
|
|
2
3
|
cloud,
|
|
3
4
|
mergeStreams,
|
|
4
5
|
stream
|
|
5
|
-
} from "../../chunk-
|
|
6
|
+
} from "../../chunk-PXX7ISZ6.js";
|
|
6
7
|
import {
|
|
7
8
|
UnsupportedError,
|
|
8
9
|
asAttachment,
|
|
@@ -233,6 +234,31 @@ var resubscribableStream = (state, options) => {
|
|
|
233
234
|
|
|
234
235
|
// src/providers/whatsapp-business/messages.ts
|
|
235
236
|
import { extension as mimeExtension } from "mime-types";
|
|
237
|
+
|
|
238
|
+
// src/providers/whatsapp-business/poll.ts
|
|
239
|
+
import {
|
|
240
|
+
button,
|
|
241
|
+
buttons,
|
|
242
|
+
list
|
|
243
|
+
} from "@photon-ai/whatsapp-business";
|
|
244
|
+
var MAX_BUTTON_OPTIONS = 3;
|
|
245
|
+
var LIST_BUTTON_TEXT = "View options";
|
|
246
|
+
var LIST_SECTION_TITLE = "Options";
|
|
247
|
+
var pollOptionId = (index) => `opt_${index}`;
|
|
248
|
+
var pollToInteractive = (content) => {
|
|
249
|
+
if (content.options.length <= MAX_BUTTON_OPTIONS) {
|
|
250
|
+
return buttons(
|
|
251
|
+
content.title,
|
|
252
|
+
...content.options.map((o, i) => button(pollOptionId(i), o.title))
|
|
253
|
+
);
|
|
254
|
+
}
|
|
255
|
+
return list(content.title, LIST_BUTTON_TEXT).section(
|
|
256
|
+
LIST_SECTION_TITLE,
|
|
257
|
+
content.options.map((o, i) => ({ id: pollOptionId(i), title: o.title }))
|
|
258
|
+
);
|
|
259
|
+
};
|
|
260
|
+
|
|
261
|
+
// src/providers/whatsapp-business/messages.ts
|
|
236
262
|
var primary = (clients) => {
|
|
237
263
|
const client = clients[0];
|
|
238
264
|
if (!client) {
|
|
@@ -243,6 +269,40 @@ var primary = (clients) => {
|
|
|
243
269
|
var toSendResult = (result) => ({
|
|
244
270
|
id: result.messageId
|
|
245
271
|
});
|
|
272
|
+
var MAX_POLL_CACHE_SIZE = 1e3;
|
|
273
|
+
var OPTION_ID_PREFIX = "opt_";
|
|
274
|
+
var pollCaches = /* @__PURE__ */ new WeakMap();
|
|
275
|
+
var getPollCache = (client) => {
|
|
276
|
+
let cache = pollCaches.get(client);
|
|
277
|
+
if (!cache) {
|
|
278
|
+
cache = /* @__PURE__ */ new Map();
|
|
279
|
+
pollCaches.set(client, cache);
|
|
280
|
+
}
|
|
281
|
+
return cache;
|
|
282
|
+
};
|
|
283
|
+
var cachePoll = (client, messageId, poll) => {
|
|
284
|
+
const cache = getPollCache(client);
|
|
285
|
+
if (cache.has(messageId)) {
|
|
286
|
+
cache.delete(messageId);
|
|
287
|
+
}
|
|
288
|
+
cache.set(messageId, poll);
|
|
289
|
+
if (cache.size > MAX_POLL_CACHE_SIZE) {
|
|
290
|
+
const first = cache.keys().next().value;
|
|
291
|
+
if (first !== void 0) {
|
|
292
|
+
cache.delete(first);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
var optionIndexFromId = (id) => {
|
|
297
|
+
if (!id.startsWith(OPTION_ID_PREFIX)) {
|
|
298
|
+
return;
|
|
299
|
+
}
|
|
300
|
+
const index = Number(id.slice(OPTION_ID_PREFIX.length));
|
|
301
|
+
if (!Number.isInteger(index) || index < 0 || pollOptionId(index) !== id) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
return index;
|
|
305
|
+
};
|
|
246
306
|
var mapWaPhoneType = (type) => {
|
|
247
307
|
if (!type) {
|
|
248
308
|
return void 0;
|
|
@@ -384,11 +444,12 @@ var toMessages = (client, msg) => {
|
|
|
384
444
|
{
|
|
385
445
|
...base,
|
|
386
446
|
id: msg.id,
|
|
387
|
-
content: mapContent(client, msg
|
|
447
|
+
content: mapContent(client, msg)
|
|
388
448
|
}
|
|
389
449
|
];
|
|
390
450
|
};
|
|
391
|
-
var mapContent = (client,
|
|
451
|
+
var mapContent = (client, msg) => {
|
|
452
|
+
const { content } = msg;
|
|
392
453
|
switch (content.type) {
|
|
393
454
|
case "text":
|
|
394
455
|
return asText(content.body);
|
|
@@ -411,8 +472,18 @@ var mapContent = (client, content) => {
|
|
|
411
472
|
target: stubTarget
|
|
412
473
|
});
|
|
413
474
|
}
|
|
414
|
-
case "interactive":
|
|
415
|
-
|
|
475
|
+
case "interactive": {
|
|
476
|
+
const inter = content.interactive;
|
|
477
|
+
if (inter.type === "button_reply" || inter.type === "list_reply") {
|
|
478
|
+
const poll = msg.context?.id === void 0 ? void 0 : getPollCache(client).get(msg.context.id);
|
|
479
|
+
const optionIndex = optionIndexFromId(inter.reply.id);
|
|
480
|
+
const option = optionIndex === void 0 ? void 0 : poll?.options[optionIndex];
|
|
481
|
+
if (poll && option) {
|
|
482
|
+
return asPollOption({ poll, option, selected: true });
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
return asCustom({ whatsapp_type: "interactive", ...inter });
|
|
486
|
+
}
|
|
416
487
|
case "button":
|
|
417
488
|
return asCustom({ whatsapp_type: "button", ...content.button });
|
|
418
489
|
case "order":
|
|
@@ -589,6 +660,14 @@ var send = async (clients, spaceId, content) => {
|
|
|
589
660
|
})
|
|
590
661
|
);
|
|
591
662
|
}
|
|
663
|
+
case "poll": {
|
|
664
|
+
const result = await client.messages.send({
|
|
665
|
+
to: spaceId,
|
|
666
|
+
interactive: pollToInteractive(content)
|
|
667
|
+
});
|
|
668
|
+
cachePoll(client, result.messageId, content);
|
|
669
|
+
return toSendResult(result);
|
|
670
|
+
}
|
|
592
671
|
default:
|
|
593
672
|
throw UnsupportedError.content(content.type);
|
|
594
673
|
}
|
|
@@ -648,6 +727,15 @@ var replyToMessage = async (clients, spaceId, messageId, content) => {
|
|
|
648
727
|
})
|
|
649
728
|
);
|
|
650
729
|
}
|
|
730
|
+
case "poll": {
|
|
731
|
+
const result = await client.messages.send({
|
|
732
|
+
to: spaceId,
|
|
733
|
+
replyTo: messageId,
|
|
734
|
+
interactive: pollToInteractive(content)
|
|
735
|
+
});
|
|
736
|
+
cachePoll(client, result.messageId, content);
|
|
737
|
+
return toSendResult(result);
|
|
738
|
+
}
|
|
651
739
|
default:
|
|
652
740
|
throw UnsupportedError.content(content.type);
|
|
653
741
|
}
|
|
@@ -95,6 +95,26 @@ declare const contentSchema: z__default.ZodDiscriminatedUnion<[z__default.ZodObj
|
|
|
95
95
|
}, z__default.core.$strip>, z__default.ZodObject<{
|
|
96
96
|
type: z__default.ZodLiteral<"group">;
|
|
97
97
|
items: z__default.ZodArray<z__default.ZodCustom<Message, Message>>;
|
|
98
|
+
}, z__default.core.$strip>, z__default.ZodObject<{
|
|
99
|
+
type: z__default.ZodLiteral<"poll">;
|
|
100
|
+
title: z__default.ZodString;
|
|
101
|
+
options: z__default.ZodArray<z__default.ZodObject<{
|
|
102
|
+
title: z__default.ZodString;
|
|
103
|
+
}, z__default.core.$strip>>;
|
|
104
|
+
}, z__default.core.$strip>, z__default.ZodObject<{
|
|
105
|
+
type: z__default.ZodLiteral<"poll_option">;
|
|
106
|
+
option: z__default.ZodObject<{
|
|
107
|
+
title: z__default.ZodString;
|
|
108
|
+
}, z__default.core.$strip>;
|
|
109
|
+
poll: z__default.ZodObject<{
|
|
110
|
+
type: z__default.ZodLiteral<"poll">;
|
|
111
|
+
title: z__default.ZodString;
|
|
112
|
+
options: z__default.ZodArray<z__default.ZodObject<{
|
|
113
|
+
title: z__default.ZodString;
|
|
114
|
+
}, z__default.core.$strip>>;
|
|
115
|
+
}, z__default.core.$strip>;
|
|
116
|
+
selected: z__default.ZodBoolean;
|
|
117
|
+
title: z__default.ZodString;
|
|
98
118
|
}, z__default.core.$strip>], "type">;
|
|
99
119
|
type Content = z__default.infer<typeof contentSchema>;
|
|
100
120
|
interface ContentBuilder {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "spectrum-ts",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
}
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
|
-
"@photon-ai/advanced-imessage": "^0.
|
|
22
|
+
"@photon-ai/advanced-imessage": "^0.5.1",
|
|
23
23
|
"@photon-ai/imessage-kit": "^3.0.0",
|
|
24
24
|
"@photon-ai/whatsapp-business": "^0.1.1",
|
|
25
25
|
"@repeaterjs/repeater": "^3.0.6",
|