spectrum-ts 0.7.0 → 0.9.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-6ZOLTQDN.js +141 -0
- package/dist/{chunk-GX3JCGSD.js → chunk-CZIWNTXP.js} +22 -5
- package/dist/{chunk-6URE4AYH.js → chunk-PLJI5FTO.js} +99 -39
- package/dist/index.d.ts +1958 -4
- package/dist/index.js +1949 -10
- package/dist/providers/imessage/index.d.ts +3 -12
- package/dist/providers/imessage/index.js +112 -22
- package/dist/providers/terminal/index.d.ts +1 -1
- package/dist/providers/terminal/index.js +1 -1
- package/dist/providers/whatsapp-business/index.d.ts +2 -2
- package/dist/providers/whatsapp-business/index.js +17 -9
- package/dist/{stream-DGy4geUK.d.ts → stream-B55k7W8-.d.ts} +1 -1
- package/dist/{types-B5tTx5hc.d.ts → types-B8g0pvfg.d.ts} +17 -2
- package/package.json +2 -1
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import {
|
|
2
|
+
bufferToStream,
|
|
3
|
+
readSchema,
|
|
4
|
+
streamSchema
|
|
5
|
+
} from "./chunk-CZIWNTXP.js";
|
|
6
|
+
|
|
7
|
+
// src/content/richlink.ts
|
|
8
|
+
import z from "zod";
|
|
9
|
+
|
|
10
|
+
// src/utils/link-metadata.ts
|
|
11
|
+
import ogs from "open-graph-scraper";
|
|
12
|
+
var DEFAULT_TIMEOUT_MS = 5e3;
|
|
13
|
+
var USER_AGENT = "Mozilla/5.0 (Macintosh; Intel Mac OS X 14_0) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/17.0 Safari/605.1.15 spectrum-ts/richlink";
|
|
14
|
+
var normaliseImageUrl = (raw, base) => {
|
|
15
|
+
try {
|
|
16
|
+
return new URL(raw, base).toString();
|
|
17
|
+
} catch {
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
var cleanString = (v) => {
|
|
22
|
+
if (typeof v !== "string") {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const trimmed = v.trim();
|
|
26
|
+
return trimmed.length > 0 ? trimmed : void 0;
|
|
27
|
+
};
|
|
28
|
+
var fetchLinkMetadata = async (url) => {
|
|
29
|
+
try {
|
|
30
|
+
const result = await ogs({
|
|
31
|
+
url,
|
|
32
|
+
timeout: DEFAULT_TIMEOUT_MS,
|
|
33
|
+
fetchOptions: { headers: { "User-Agent": USER_AGENT } }
|
|
34
|
+
});
|
|
35
|
+
if (result.error) {
|
|
36
|
+
return {};
|
|
37
|
+
}
|
|
38
|
+
const {
|
|
39
|
+
ogTitle,
|
|
40
|
+
ogDescription,
|
|
41
|
+
ogImage,
|
|
42
|
+
twitterTitle,
|
|
43
|
+
twitterDescription,
|
|
44
|
+
twitterImage
|
|
45
|
+
} = result.result;
|
|
46
|
+
const title = cleanString(ogTitle) ?? cleanString(twitterTitle);
|
|
47
|
+
const summary = cleanString(ogDescription) ?? cleanString(twitterDescription);
|
|
48
|
+
const imageCandidate = ogImage?.[0] ?? twitterImage?.[0];
|
|
49
|
+
const resolved = imageCandidate ? normaliseImageUrl(imageCandidate.url, url) : void 0;
|
|
50
|
+
const image = imageCandidate && resolved ? {
|
|
51
|
+
url: resolved,
|
|
52
|
+
mimeType: "type" in imageCandidate && typeof imageCandidate.type === "string" ? imageCandidate.type : void 0
|
|
53
|
+
} : void 0;
|
|
54
|
+
return { title, summary, image };
|
|
55
|
+
} catch {
|
|
56
|
+
return {};
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
var fetchImage = async (url) => {
|
|
60
|
+
const controller = new AbortController();
|
|
61
|
+
const timer = setTimeout(() => controller.abort(), DEFAULT_TIMEOUT_MS);
|
|
62
|
+
try {
|
|
63
|
+
const res = await fetch(url, {
|
|
64
|
+
signal: controller.signal,
|
|
65
|
+
headers: { "User-Agent": USER_AGENT }
|
|
66
|
+
});
|
|
67
|
+
if (!res.ok) {
|
|
68
|
+
throw new Error(`image fetch ${url} returned ${res.status}`);
|
|
69
|
+
}
|
|
70
|
+
const data = Buffer.from(await res.arrayBuffer());
|
|
71
|
+
const mimeType = res.headers.get("content-type") ?? void 0;
|
|
72
|
+
return { data, mimeType };
|
|
73
|
+
} finally {
|
|
74
|
+
clearTimeout(timer);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
// src/content/richlink.ts
|
|
79
|
+
var richlinkCoverSchema = z.object({
|
|
80
|
+
mimeType: z.string().min(1).optional(),
|
|
81
|
+
read: readSchema,
|
|
82
|
+
stream: streamSchema
|
|
83
|
+
});
|
|
84
|
+
var optionalStringAccessor = z.function({
|
|
85
|
+
input: [],
|
|
86
|
+
output: z.promise(z.string().min(1).optional())
|
|
87
|
+
});
|
|
88
|
+
var coverAccessor = z.function({
|
|
89
|
+
input: [],
|
|
90
|
+
output: z.promise(richlinkCoverSchema.optional())
|
|
91
|
+
});
|
|
92
|
+
var richlinkSchema = z.object({
|
|
93
|
+
type: z.literal("richlink"),
|
|
94
|
+
url: z.url(),
|
|
95
|
+
title: optionalStringAccessor,
|
|
96
|
+
summary: optionalStringAccessor,
|
|
97
|
+
cover: coverAccessor
|
|
98
|
+
});
|
|
99
|
+
var memoize = (factory) => {
|
|
100
|
+
let cached;
|
|
101
|
+
return () => {
|
|
102
|
+
cached ??= factory();
|
|
103
|
+
return cached;
|
|
104
|
+
};
|
|
105
|
+
};
|
|
106
|
+
var buildCover = (image) => {
|
|
107
|
+
const read = memoize(
|
|
108
|
+
() => fetchImage(image.url).then((r) => r.data).catch(() => Buffer.alloc(0))
|
|
109
|
+
);
|
|
110
|
+
return {
|
|
111
|
+
mimeType: image.mimeType,
|
|
112
|
+
read,
|
|
113
|
+
stream: async () => bufferToStream(await read())
|
|
114
|
+
};
|
|
115
|
+
};
|
|
116
|
+
var asRichlink = (input) => {
|
|
117
|
+
const getMetadata = memoize(() => fetchLinkMetadata(input.url));
|
|
118
|
+
const getCover = memoize(async () => {
|
|
119
|
+
const { image } = await getMetadata();
|
|
120
|
+
return image ? buildCover(image) : void 0;
|
|
121
|
+
});
|
|
122
|
+
const title = async () => (await getMetadata()).title;
|
|
123
|
+
const summary = async () => (await getMetadata()).summary;
|
|
124
|
+
return richlinkSchema.parse({
|
|
125
|
+
type: "richlink",
|
|
126
|
+
url: input.url,
|
|
127
|
+
title,
|
|
128
|
+
summary,
|
|
129
|
+
cover: getCover
|
|
130
|
+
});
|
|
131
|
+
};
|
|
132
|
+
function richlink(url) {
|
|
133
|
+
return {
|
|
134
|
+
build: async () => asRichlink({ url })
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export {
|
|
139
|
+
asRichlink,
|
|
140
|
+
richlink
|
|
141
|
+
};
|
|
@@ -553,15 +553,30 @@ function custom(raw) {
|
|
|
553
553
|
};
|
|
554
554
|
}
|
|
555
555
|
|
|
556
|
+
// src/content/reaction.ts
|
|
557
|
+
import z5 from "zod";
|
|
558
|
+
var reactionSchema = z5.object({
|
|
559
|
+
type: z5.literal("reaction"),
|
|
560
|
+
emoji: z5.string().min(1),
|
|
561
|
+
target: z5.string().min(1)
|
|
562
|
+
});
|
|
563
|
+
var asReaction = (input) => reactionSchema.parse({ type: "reaction", ...input });
|
|
564
|
+
function reaction(emoji, target) {
|
|
565
|
+
const targetId = typeof target === "string" ? target : target.id;
|
|
566
|
+
return { build: async () => asReaction({ emoji, target: targetId }) };
|
|
567
|
+
}
|
|
568
|
+
|
|
556
569
|
// src/utils/stream.ts
|
|
557
570
|
import { Repeater } from "@repeaterjs/repeater";
|
|
558
571
|
function stream(setup) {
|
|
559
572
|
const repeater = new Repeater(async (push, stop) => {
|
|
560
|
-
const emit = (value) => {
|
|
561
|
-
|
|
573
|
+
const emit = async (value) => {
|
|
574
|
+
try {
|
|
575
|
+
await push(value);
|
|
576
|
+
} catch (error) {
|
|
562
577
|
stop(error);
|
|
563
|
-
|
|
564
|
-
}
|
|
578
|
+
throw error;
|
|
579
|
+
}
|
|
565
580
|
};
|
|
566
581
|
const end = (error) => {
|
|
567
582
|
stop(error);
|
|
@@ -589,7 +604,7 @@ function mergeStreams(streams) {
|
|
|
589
604
|
const workers = streams.map(async (source) => {
|
|
590
605
|
try {
|
|
591
606
|
for await (const value of source) {
|
|
592
|
-
emit(value);
|
|
607
|
+
await emit(value);
|
|
593
608
|
}
|
|
594
609
|
} catch (error) {
|
|
595
610
|
end(error);
|
|
@@ -686,6 +701,8 @@ export {
|
|
|
686
701
|
contact,
|
|
687
702
|
asCustom,
|
|
688
703
|
custom,
|
|
704
|
+
asReaction,
|
|
705
|
+
reaction,
|
|
689
706
|
stream,
|
|
690
707
|
mergeStreams,
|
|
691
708
|
SpectrumCloudError,
|
|
@@ -91,43 +91,71 @@ var warnUnsupported = (err, fallbackPlatform) => {
|
|
|
91
91
|
function buildSpace(params) {
|
|
92
92
|
const { spaceRef, extras, typingCtx, definition, client, config } = params;
|
|
93
93
|
let space;
|
|
94
|
+
async function dispatchReaction(item) {
|
|
95
|
+
try {
|
|
96
|
+
if (!definition.actions.reactToMessage) {
|
|
97
|
+
throw UnsupportedError.action("react", definition.name);
|
|
98
|
+
}
|
|
99
|
+
await definition.actions.reactToMessage({
|
|
100
|
+
space: spaceRef,
|
|
101
|
+
messageId: item.target,
|
|
102
|
+
reaction: item.emoji,
|
|
103
|
+
client,
|
|
104
|
+
config
|
|
105
|
+
});
|
|
106
|
+
} catch (err) {
|
|
107
|
+
if (err instanceof UnsupportedError) {
|
|
108
|
+
warnUnsupported(err, definition.name);
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
throw err;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
async function dispatchSend(item) {
|
|
115
|
+
let sendResult;
|
|
116
|
+
try {
|
|
117
|
+
sendResult = await definition.actions.send({
|
|
118
|
+
...typingCtx,
|
|
119
|
+
content: item
|
|
120
|
+
});
|
|
121
|
+
} catch (err) {
|
|
122
|
+
if (err instanceof UnsupportedError) {
|
|
123
|
+
warnUnsupported(err, definition.name);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
throw err;
|
|
127
|
+
}
|
|
128
|
+
if (!sendResult?.id) {
|
|
129
|
+
throw new Error(
|
|
130
|
+
`Platform "${definition.name}" send did not return a message id`
|
|
131
|
+
);
|
|
132
|
+
}
|
|
133
|
+
return buildMessage({
|
|
134
|
+
id: sendResult.id,
|
|
135
|
+
content: item,
|
|
136
|
+
sender: sendResult.sender,
|
|
137
|
+
timestamp: sendResult.timestamp ?? /* @__PURE__ */ new Date(),
|
|
138
|
+
extras: {},
|
|
139
|
+
spaceRef,
|
|
140
|
+
space,
|
|
141
|
+
definition,
|
|
142
|
+
client,
|
|
143
|
+
config,
|
|
144
|
+
direction: "outbound"
|
|
145
|
+
});
|
|
146
|
+
}
|
|
94
147
|
async function sendImpl(...content) {
|
|
95
148
|
const resolved = await resolveContents(content);
|
|
96
149
|
const results = [];
|
|
97
150
|
for (const item of resolved) {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
...typingCtx,
|
|
102
|
-
content: item
|
|
103
|
-
});
|
|
104
|
-
} catch (err) {
|
|
105
|
-
if (err instanceof UnsupportedError) {
|
|
106
|
-
warnUnsupported(err, definition.name);
|
|
107
|
-
continue;
|
|
108
|
-
}
|
|
109
|
-
throw err;
|
|
151
|
+
if (item.type === "reaction") {
|
|
152
|
+
await dispatchReaction(item);
|
|
153
|
+
continue;
|
|
110
154
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
);
|
|
155
|
+
const sent = await dispatchSend(item);
|
|
156
|
+
if (sent) {
|
|
157
|
+
results.push(sent);
|
|
115
158
|
}
|
|
116
|
-
results.push(
|
|
117
|
-
buildMessage({
|
|
118
|
-
id: sendResult.id,
|
|
119
|
-
content: item,
|
|
120
|
-
sender: sendResult.sender,
|
|
121
|
-
timestamp: sendResult.timestamp ?? /* @__PURE__ */ new Date(),
|
|
122
|
-
extras: {},
|
|
123
|
-
spaceRef,
|
|
124
|
-
space,
|
|
125
|
-
definition,
|
|
126
|
-
client,
|
|
127
|
-
config,
|
|
128
|
-
direction: "outbound"
|
|
129
|
-
})
|
|
130
|
-
);
|
|
131
159
|
}
|
|
132
160
|
if (content.length === 1) {
|
|
133
161
|
return results[0];
|
|
@@ -169,13 +197,21 @@ function buildMessage(params) {
|
|
|
169
197
|
);
|
|
170
198
|
return;
|
|
171
199
|
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
200
|
+
try {
|
|
201
|
+
await definition.actions.reactToMessage({
|
|
202
|
+
space: spaceRef,
|
|
203
|
+
messageId: params.id,
|
|
204
|
+
reaction,
|
|
205
|
+
client,
|
|
206
|
+
config
|
|
207
|
+
});
|
|
208
|
+
} catch (err) {
|
|
209
|
+
if (err instanceof UnsupportedError) {
|
|
210
|
+
warnUnsupported(err, definition.name);
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
throw err;
|
|
214
|
+
}
|
|
179
215
|
};
|
|
180
216
|
async function reply(...content) {
|
|
181
217
|
if (!definition.actions.replyToMessage) {
|
|
@@ -292,6 +328,29 @@ function createPlatformInstance(def, runtime) {
|
|
|
292
328
|
const isPlatformUser = (value) => {
|
|
293
329
|
return typeof value === "object" && value !== null && "__platform" in value && value.__platform === def.name;
|
|
294
330
|
};
|
|
331
|
+
const resolveUserID = async (userID) => {
|
|
332
|
+
const resolved = await def.user.resolve({
|
|
333
|
+
input: { userID },
|
|
334
|
+
client: runtime.client,
|
|
335
|
+
config: runtime.config
|
|
336
|
+
});
|
|
337
|
+
return {
|
|
338
|
+
...resolved,
|
|
339
|
+
__platform: def.name
|
|
340
|
+
};
|
|
341
|
+
};
|
|
342
|
+
const resolveStringUsers = async (args) => {
|
|
343
|
+
const convertArg = async (arg) => {
|
|
344
|
+
if (typeof arg === "string") {
|
|
345
|
+
return await resolveUserID(arg);
|
|
346
|
+
}
|
|
347
|
+
if (Array.isArray(arg)) {
|
|
348
|
+
return await Promise.all(arg.map(convertArg));
|
|
349
|
+
}
|
|
350
|
+
return arg;
|
|
351
|
+
};
|
|
352
|
+
return await Promise.all(args.map(convertArg));
|
|
353
|
+
};
|
|
295
354
|
const normalizeSpaceArgs = (args) => {
|
|
296
355
|
if (args.length === 0) {
|
|
297
356
|
return { users: [], params: void 0 };
|
|
@@ -334,7 +393,8 @@ function createPlatformInstance(def, runtime) {
|
|
|
334
393
|
};
|
|
335
394
|
},
|
|
336
395
|
async space(...args) {
|
|
337
|
-
const
|
|
396
|
+
const convertedArgs = await resolveStringUsers(args);
|
|
397
|
+
const { users, params } = normalizeSpaceArgs(convertedArgs);
|
|
338
398
|
let parsedParams = params;
|
|
339
399
|
if (params !== void 0 && def.space.params) {
|
|
340
400
|
parsedParams = def.space.params.parse(params);
|