@spectrum-ts/imessage 5.0.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/LICENSE +21 -0
- package/README.md +34 -0
- package/dist/index.d.ts +269 -0
- package/dist/index.js +2153 -0
- package/package.json +46 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License Copyright (c) 2025 Photon AI
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted,
|
|
4
|
+
free of charge, to any person obtaining a copy of this software and associated
|
|
5
|
+
documentation files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use, copy, modify, merge,
|
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice
|
|
12
|
+
(including the next paragraph) shall be included in all copies or substantial
|
|
13
|
+
portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
16
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
18
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
19
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# @spectrum-ts/imessage
|
|
2
|
+
|
|
3
|
+
iMessage provider for [spectrum-ts](https://github.com/photon-hq/spectrum-ts), supporting local (imessage-kit) and remote (advanced-imessage) modes — including tapbacks, special effects, polls, and mini-apps.
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```sh
|
|
8
|
+
bun add spectrum-ts @spectrum-ts/imessage
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Use
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
import { Spectrum } from "spectrum-ts";
|
|
15
|
+
import { imessage } from "@spectrum-ts/imessage";
|
|
16
|
+
|
|
17
|
+
const spectrum = Spectrum({
|
|
18
|
+
providers: [imessage.config({ /* ... */ })],
|
|
19
|
+
});
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
This package also exports the iMessage-specific content helpers `effect`, `read`, `background`, `customizedMiniApp`, and `nativeContactCard`.
|
|
23
|
+
|
|
24
|
+
`nativeContactCard()` shares the bot account's own contact card (Apple's "Share Name and Photo") with a chat — remote mode only:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { nativeContactCard } from "@spectrum-ts/imessage";
|
|
28
|
+
|
|
29
|
+
await space.send(nativeContactCard());
|
|
30
|
+
// or the sugar form, typed on the iMessage space:
|
|
31
|
+
await space.shareContactCard();
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
See the [spectrum-ts documentation](https://photon.codes/spectrum) for the full guide.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { AdvancedIMessage, MessageEffect } from "@photon-ai/advanced-imessage";
|
|
2
|
+
import { IMessageSDK } from "@photon-ai/imessage-kit";
|
|
3
|
+
import { Attachment, ContentBuilder, ContentInput, SchemaMessage, Space, read } from "@spectrum-ts/core";
|
|
4
|
+
import { PhotoInput } from "@spectrum-ts/core/authoring";
|
|
5
|
+
import z from "zod";
|
|
6
|
+
|
|
7
|
+
//#region src/content/background.d.ts
|
|
8
|
+
type BackgroundInput = PhotoInput;
|
|
9
|
+
/**
|
|
10
|
+
* Set or clear the chat background. iMessage-only, remote-only.
|
|
11
|
+
*
|
|
12
|
+
* - `background("clear")` — remove the current chat background.
|
|
13
|
+
* - `background("./photo.jpg")` — set background from a filesystem path.
|
|
14
|
+
* MIME type is inferred from the extension; override with `options.mimeType`.
|
|
15
|
+
* - `background(new URL("https://…/photo.jpg"))` — fetch the background
|
|
16
|
+
* lazily over the network. Bytes stay in memory (safe in read-only
|
|
17
|
+
* environments). MIME type is inferred from the URL pathname extension;
|
|
18
|
+
* override with `options.mimeType` when the URL has no usable extension.
|
|
19
|
+
* - `background(buffer, { mimeType })` — set background from in-memory bytes.
|
|
20
|
+
* `options.mimeType` is required.
|
|
21
|
+
*
|
|
22
|
+
* `"clear"` is a reserved string-literal sentinel. If you have a file literally
|
|
23
|
+
* named `clear` with no extension, pass `"./clear"` or load it as a Buffer.
|
|
24
|
+
*
|
|
25
|
+
* `space.send(background(...))` is the canonical form; `space.background(...)`
|
|
26
|
+
* is sugar attached via `PlatformDef.space.actions` (only typed on
|
|
27
|
+
* `PlatformSpace<IMessageDef>`).
|
|
28
|
+
*
|
|
29
|
+
* `Background` is intentionally not a member of the universal `Content`
|
|
30
|
+
* union — the `as unknown as Content` cast keeps the builder shape compatible
|
|
31
|
+
* with the framework's `ContentBuilder.build(): Promise<Content>` signature.
|
|
32
|
+
* The framework treats it as a fire-and-forget control signal at runtime.
|
|
33
|
+
*/
|
|
34
|
+
declare function background(input: "clear"): ContentBuilder;
|
|
35
|
+
declare function background(input: string | Buffer | URL, options?: {
|
|
36
|
+
mimeType?: string;
|
|
37
|
+
}): ContentBuilder;
|
|
38
|
+
//#endregion
|
|
39
|
+
//#region src/content/contact-card.d.ts
|
|
40
|
+
/**
|
|
41
|
+
* iMessage-only "share contact card" control signal. Pushes the *local
|
|
42
|
+
* account's native contact card* (the name + photo a recipient sees in their
|
|
43
|
+
* Messages app) to a chat via the SDK's `chats.shareContactInfo`.
|
|
44
|
+
*
|
|
45
|
+
* This is Apple's "Share Name and Photo" mechanism — distinct from the
|
|
46
|
+
* universal `contact(...)` content, which uploads an arbitrary person's vCard
|
|
47
|
+
* as a *file* attachment. There is no payload: the card shared is always the
|
|
48
|
+
* bot account's own.
|
|
49
|
+
*
|
|
50
|
+
* Like `background`, it lives entirely under the iMessage provider and never
|
|
51
|
+
* enters the universal `Content` discriminated union. The framework recognizes
|
|
52
|
+
* it via two generic content-level contracts:
|
|
53
|
+
*
|
|
54
|
+
* 1. `__platform: "iMessage"` — `findUnsupportedPlatformContent` in
|
|
55
|
+
* `platform/build.ts` reads this tag and warns-and-skips when a different
|
|
56
|
+
* platform receives it.
|
|
57
|
+
* 2. `__fireAndForget: true` — `dispatchSend`'s fire-and-forget check treats
|
|
58
|
+
* this as a side-effecting send that returns no message id, the same way it
|
|
59
|
+
* treats `read` / `typing`.
|
|
60
|
+
*
|
|
61
|
+
* iMessage's `send` handler narrows back via the `isContactCard` type guard
|
|
62
|
+
* before dispatching to `chats.shareContactInfo`.
|
|
63
|
+
*/
|
|
64
|
+
declare const contactCardSchema: z.ZodObject<{
|
|
65
|
+
type: z.ZodLiteral<"contactCard">;
|
|
66
|
+
__platform: z.ZodLiteral<"iMessage">;
|
|
67
|
+
__fireAndForget: z.ZodLiteral<true>;
|
|
68
|
+
}, z.core.$strip>;
|
|
69
|
+
type ContactCard = z.infer<typeof contactCardSchema>;
|
|
70
|
+
/**
|
|
71
|
+
* Share the bot account's native iMessage contact card (name + photo) with the
|
|
72
|
+
* chat. iMessage-only, remote-only.
|
|
73
|
+
*
|
|
74
|
+
* `space.send(nativeContactCard())` is the canonical form; `space.shareContactCard()`
|
|
75
|
+
* is sugar attached via `PlatformDef.space.actions` (only typed on
|
|
76
|
+
* `PlatformSpace<IMessageDef>`).
|
|
77
|
+
*
|
|
78
|
+
* This is an explicit, on-demand share and always fires — unlike the automatic
|
|
79
|
+
* best-effort share gated behind the `imessageSynced` project profile, which
|
|
80
|
+
* dedupes to once per chat per 24h (see `remote/contact-share.ts`). Works in
|
|
81
|
+
* both DMs and group chats; the recipient chooses whether to accept the card.
|
|
82
|
+
*
|
|
83
|
+
* `ContactCard` is intentionally not a member of the universal `Content`
|
|
84
|
+
* union — the `as unknown as Content` cast keeps the builder shape compatible
|
|
85
|
+
* with the framework's `ContentBuilder.build(): Promise<Content>` signature.
|
|
86
|
+
* The framework treats it as a fire-and-forget control signal at runtime.
|
|
87
|
+
*/
|
|
88
|
+
declare function nativeContactCard(): ContentBuilder;
|
|
89
|
+
//#endregion
|
|
90
|
+
//#region src/content/customized-mini-app.d.ts
|
|
91
|
+
declare const layoutSchema: z.ZodObject<{
|
|
92
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
93
|
+
subcaption: z.ZodOptional<z.ZodString>;
|
|
94
|
+
trailingCaption: z.ZodOptional<z.ZodString>;
|
|
95
|
+
trailingSubcaption: z.ZodOptional<z.ZodString>;
|
|
96
|
+
image: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
|
|
97
|
+
imageTitle: z.ZodOptional<z.ZodString>;
|
|
98
|
+
imageSubtitle: z.ZodOptional<z.ZodString>;
|
|
99
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
100
|
+
}, z.core.$strip>;
|
|
101
|
+
/**
|
|
102
|
+
* iMessage-only mini-app card content. Lives entirely under the iMessage
|
|
103
|
+
* provider — never enters the universal `Content` discriminated union. The
|
|
104
|
+
* framework recognizes it via the generic content-level platform contract:
|
|
105
|
+
*
|
|
106
|
+
* - `__platform: "iMessage"` — `findUnsupportedPlatformContent` reads this tag
|
|
107
|
+
* and warns-and-skips when a different platform receives it.
|
|
108
|
+
*
|
|
109
|
+
* Unlike `background` / `read`, this content is **not** `__fireAndForget`: it
|
|
110
|
+
* produces a real outbound message, so the iMessage `send` handler narrows
|
|
111
|
+
* back to `CustomizedMiniApp` via the `isCustomizedMiniApp` guard and returns
|
|
112
|
+
* the resulting `ProviderMessageRecord` (rather than `void`).
|
|
113
|
+
*/
|
|
114
|
+
declare const customizedMiniAppSchema: z.ZodObject<{
|
|
115
|
+
type: z.ZodLiteral<"customized-mini-app">;
|
|
116
|
+
__platform: z.ZodLiteral<"iMessage">;
|
|
117
|
+
appName: z.ZodString;
|
|
118
|
+
appStoreId: z.ZodOptional<z.ZodNumber>;
|
|
119
|
+
extensionBundleId: z.ZodString;
|
|
120
|
+
layout: z.ZodObject<{
|
|
121
|
+
caption: z.ZodOptional<z.ZodString>;
|
|
122
|
+
subcaption: z.ZodOptional<z.ZodString>;
|
|
123
|
+
trailingCaption: z.ZodOptional<z.ZodString>;
|
|
124
|
+
trailingSubcaption: z.ZodOptional<z.ZodString>;
|
|
125
|
+
image: z.ZodOptional<z.ZodCustom<Uint8Array<ArrayBuffer>, Uint8Array<ArrayBuffer>>>;
|
|
126
|
+
imageTitle: z.ZodOptional<z.ZodString>;
|
|
127
|
+
imageSubtitle: z.ZodOptional<z.ZodString>;
|
|
128
|
+
summary: z.ZodOptional<z.ZodString>;
|
|
129
|
+
}, z.core.$strip>;
|
|
130
|
+
teamId: z.ZodString;
|
|
131
|
+
url: z.ZodURL;
|
|
132
|
+
}, z.core.$strip>;
|
|
133
|
+
type CustomizedMiniApp = z.infer<typeof customizedMiniAppSchema>;
|
|
134
|
+
type CustomizedMiniAppLayout = z.infer<typeof layoutSchema>;
|
|
135
|
+
type CustomizedMiniAppInput = Omit<CustomizedMiniApp, "type" | "__platform">;
|
|
136
|
+
/**
|
|
137
|
+
* Construct a `customized-mini-app` content value. iMessage-only, remote-only.
|
|
138
|
+
*
|
|
139
|
+
* The layout is what recipients see in the bubble. `teamId` and
|
|
140
|
+
* `extensionBundleId` identify the iMessage extension that receives `url` when
|
|
141
|
+
* the recipient taps the card; the server constructs the matching
|
|
142
|
+
* `MSMessageExtensionBalloonPlugin` plugin id from these values. `appStoreId`
|
|
143
|
+
* is optional and only points recipients without the extension at its App
|
|
144
|
+
* Store entry.
|
|
145
|
+
*
|
|
146
|
+
* `space.send(customizedMiniApp(...))` is the canonical form.
|
|
147
|
+
*
|
|
148
|
+
* `CustomizedMiniApp` is intentionally not a member of the universal `Content`
|
|
149
|
+
* union — the `as unknown as Content` cast keeps the builder shape compatible
|
|
150
|
+
* with the framework's `ContentBuilder.build(): Promise<Content>` signature.
|
|
151
|
+
*/
|
|
152
|
+
declare function customizedMiniApp(input: CustomizedMiniAppInput): ContentBuilder;
|
|
153
|
+
//#endregion
|
|
154
|
+
//#region src/content/effect.d.ts
|
|
155
|
+
type IMessageMessageEffect = MessageEffect;
|
|
156
|
+
declare function effect(input: ContentInput, messageEffect: IMessageMessageEffect): ContentBuilder;
|
|
157
|
+
//#endregion
|
|
158
|
+
//#region src/types.d.ts
|
|
159
|
+
interface RemoteClient {
|
|
160
|
+
client: AdvancedIMessage;
|
|
161
|
+
phone: string;
|
|
162
|
+
}
|
|
163
|
+
type IMessageClient = IMessageSDK | RemoteClient[];
|
|
164
|
+
declare const userSchema: z.ZodObject<{}, z.core.$strip>;
|
|
165
|
+
declare const spaceSchema: z.ZodObject<{
|
|
166
|
+
id: z.ZodString;
|
|
167
|
+
type: z.ZodEnum<{
|
|
168
|
+
dm: "dm";
|
|
169
|
+
group: "group";
|
|
170
|
+
}>;
|
|
171
|
+
phone: z.ZodString;
|
|
172
|
+
}, z.core.$strip>;
|
|
173
|
+
type IMessageMessage = SchemaMessage<typeof userSchema, typeof spaceSchema> & {
|
|
174
|
+
direction?: "inbound" | "outbound";
|
|
175
|
+
partIndex?: number;
|
|
176
|
+
parentId?: string;
|
|
177
|
+
};
|
|
178
|
+
//#endregion
|
|
179
|
+
//#region src/index.d.ts
|
|
180
|
+
declare const imessage: import("@spectrum-ts/core").Platform<import("@spectrum-ts/core").PlatformDef<"iMessage", import("zod").ZodUnion<readonly [import("zod").ZodObject<{
|
|
181
|
+
local: import("zod").ZodLiteral<true>;
|
|
182
|
+
}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
|
|
183
|
+
local: import("zod").ZodDefault<import("zod").ZodOptional<import("zod").ZodLiteral<false>>>;
|
|
184
|
+
clients: import("zod").ZodOptional<import("zod").ZodUnion<[import("zod").ZodObject<{
|
|
185
|
+
address: import("zod").ZodString;
|
|
186
|
+
token: import("zod").ZodString;
|
|
187
|
+
phone: import("zod").ZodString;
|
|
188
|
+
}, import("zod/v4/core").$strip>, import("zod").ZodArray<import("zod").ZodObject<{
|
|
189
|
+
address: import("zod").ZodString;
|
|
190
|
+
token: import("zod").ZodString;
|
|
191
|
+
phone: import("zod").ZodString;
|
|
192
|
+
}, import("zod/v4/core").$strip>>]>>;
|
|
193
|
+
}, import("zod/v4/core").$strip>]>, import("zod").ZodType<object, unknown, import("zod/v4/core").$ZodTypeInternals<object, unknown>> | undefined, import("zod").ZodObject<{
|
|
194
|
+
id: import("zod").ZodString;
|
|
195
|
+
type: import("zod").ZodEnum<{
|
|
196
|
+
dm: "dm";
|
|
197
|
+
group: "group";
|
|
198
|
+
}>;
|
|
199
|
+
phone: import("zod").ZodString;
|
|
200
|
+
}, import("zod/v4/core").$strip>, import("zod").ZodObject<{
|
|
201
|
+
phone: import("zod").ZodOptional<import("zod").ZodString>;
|
|
202
|
+
}, import("zod/v4/core").$strip>, IMessageClient, {
|
|
203
|
+
id: string;
|
|
204
|
+
}, {
|
|
205
|
+
id: string;
|
|
206
|
+
type: "dm" | "group";
|
|
207
|
+
phone: string;
|
|
208
|
+
}, import("zod").ZodObject<{
|
|
209
|
+
partIndex: import("zod").ZodOptional<import("zod").ZodNumber>;
|
|
210
|
+
parentId: import("zod").ZodOptional<import("zod").ZodString>;
|
|
211
|
+
}, import("zod/v4/core").$strip>, IMessageMessage, undefined, {
|
|
212
|
+
background: (space: Space, input: BackgroundInput, opts?: {
|
|
213
|
+
mimeType?: string;
|
|
214
|
+
}) => Promise<void>;
|
|
215
|
+
shareContactCard: (space: Space) => Promise<void>;
|
|
216
|
+
}, Record<never, never>, {
|
|
217
|
+
getMessage: ({
|
|
218
|
+
client
|
|
219
|
+
}: {
|
|
220
|
+
client: IMessageClient;
|
|
221
|
+
config: {
|
|
222
|
+
local: true;
|
|
223
|
+
} | {
|
|
224
|
+
local: false;
|
|
225
|
+
clients?: {
|
|
226
|
+
address: string;
|
|
227
|
+
token: string;
|
|
228
|
+
phone: string;
|
|
229
|
+
} | {
|
|
230
|
+
address: string;
|
|
231
|
+
token: string;
|
|
232
|
+
phone: string;
|
|
233
|
+
}[] | undefined;
|
|
234
|
+
};
|
|
235
|
+
store: import("@spectrum-ts/core").Store;
|
|
236
|
+
}, space: {
|
|
237
|
+
id: string;
|
|
238
|
+
type: "dm" | "group";
|
|
239
|
+
phone: string;
|
|
240
|
+
} & {
|
|
241
|
+
id: string;
|
|
242
|
+
__platform: string;
|
|
243
|
+
}, messageId: string) => Promise<IMessageMessage | undefined>;
|
|
244
|
+
getAttachment: ({
|
|
245
|
+
client
|
|
246
|
+
}: {
|
|
247
|
+
client: IMessageClient;
|
|
248
|
+
}, guid: string, phone?: string) => Promise<Attachment | undefined>;
|
|
249
|
+
}>> & Readonly<{
|
|
250
|
+
effect: {
|
|
251
|
+
message: {
|
|
252
|
+
readonly slam: "com.apple.MobileSMS.expressivesend.impact";
|
|
253
|
+
readonly loud: "com.apple.MobileSMS.expressivesend.loud";
|
|
254
|
+
readonly gentle: "com.apple.MobileSMS.expressivesend.gentle";
|
|
255
|
+
readonly invisible: "com.apple.MobileSMS.expressivesend.invisibleink";
|
|
256
|
+
readonly confetti: "com.apple.messages.effect.CKConfettiEffect";
|
|
257
|
+
readonly fireworks: "com.apple.messages.effect.CKFireworksEffect";
|
|
258
|
+
readonly balloons: "com.apple.messages.effect.CKBalloonEffect";
|
|
259
|
+
readonly heart: "com.apple.messages.effect.CKHeartEffect";
|
|
260
|
+
readonly lasers: "com.apple.messages.effect.CKLasersEffect";
|
|
261
|
+
readonly celebration: "com.apple.messages.effect.CKHappyBirthdayEffect";
|
|
262
|
+
readonly sparkles: "com.apple.messages.effect.CKSparklesEffect";
|
|
263
|
+
readonly spotlight: "com.apple.messages.effect.CKSpotlightEffect";
|
|
264
|
+
readonly echo: "com.apple.messages.effect.CKEchoEffect";
|
|
265
|
+
};
|
|
266
|
+
};
|
|
267
|
+
}>;
|
|
268
|
+
//#endregion
|
|
269
|
+
export { type BackgroundInput, type ContactCard, type CustomizedMiniApp, type CustomizedMiniAppInput, type CustomizedMiniAppLayout, type IMessageMessageEffect, background, customizedMiniApp, effect, imessage, nativeContactCard, read };
|