### Quickstart Spectrum App Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/getting-started.md Initialize a Spectrum app with project credentials and the iMessage provider. This example demonstrates receiving text messages and echoing them back. ```typescript import { Spectrum } from "spectrum-ts"; import { imessage } from "spectrum-ts/providers/imessage"; const app = await Spectrum({ projectId: "your-project-id", projectSecret: "your-project-secret", providers: [imessage.config()], }); for await (const [space, message] of app.messages) { if (message.content.type === "text") { await space.send(`echo: ${message.content.text}`); } } ``` -------------------------------- ### Install Self-Hosted iMessage Kit (Bun) Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Install the self-hosted iMessage kit using Bun. This is the recommended method as it has zero dependencies. ```bash bun add @photon-ai/imessage-kit ``` -------------------------------- ### Install chat-adapter-imessage and related kits Source: https://github.com/photon-hq/skills/blob/main/skills/chat-adapter-imessage/SKILL.md Install the necessary packages for chat-adapter-imessage. Include the advanced kit for remote mode. ```bash pnpm install chat-adapter-imessage @photon-ai/imessage-kit # For remote mode, also install the advanced kit pnpm install @photon-ai/advanced-imessage-kit ``` -------------------------------- ### Install Advanced iMessage Kit (Bun) Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Install the advanced iMessage kit using Bun. This kit connects to Photon's managed service. ```bash bun add @photon-ai/advanced-imessage-kit ``` -------------------------------- ### Install and Initialize IMessageSDK Source: https://context7.com/photon-hq/skills/llms.txt Install the iMessage kit and configure the SDK for self-hosted iMessage access. Ensure Full Disk Access is granted and restart your IDE. Use 'await using' for automatic resource cleanup. ```typescript // Bun (zero dependencies) // bun add @photon-ai/imessage-kit // Node: npm install @photon-ai/imessage-kit better-sqlite3 import { IMessageSDK, IMessageConfig } from '@photon-ai/imessage-kit'; const config: IMessageConfig = { debug: true, databasePath: '~/Library/Messages/chat.db', scriptTimeout: 30000, maxConcurrent: 5, watcher: { pollInterval: 2000, unreadOnly: false, excludeOwnMessages: true }, retry: { max: 2, delay: 1500 }, }; // Use 'await using' for automatic cleanup (or call sdk.close() in finally) await using sdk = new IMessageSDK(config); await sdk.send('+15551234567', 'Hello from iMessage Kit!'); ``` -------------------------------- ### Install Spectrum Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/getting-started.md Install the Spectrum TypeScript library using npm, pnpm, yarn, or bun. Requires TypeScript 5 or later. ```bash npm install spectrum-ts # or pnpm / yarn / bun add ``` -------------------------------- ### Install Self-Hosted iMessage Kit (Node.js) Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Install the self-hosted iMessage kit using npm. This requires the 'better-sqlite3' package. ```bash npm install @photon-ai/imessage-kit better-sqlite3 ``` -------------------------------- ### Start a Conversation with WhatsApp Business Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/providers/whatsapp-business.md Initiate a conversation with a user by resolving them via their WhatsApp phone number and then sending a message. This example demonstrates resolving a user and sending an initial message within a 1:1 space. ```typescript const wa = whatsappBusiness(app); const customer = await wa.user("15551234567"); const space = await wa.space(customer); await space.send("Thanks for reaching out."); ``` -------------------------------- ### Install Advanced iMessage Kit (npm) Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Install the advanced iMessage kit using npm. This kit connects to Photon's managed service. ```bash npm install @photon-ai/advanced-imessage-kit ``` -------------------------------- ### Verify Self-Hosted iMessage Kit Setup Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Verify the setup of the self-hosted iMessage kit by initializing the SDK and sending a test message. Ensure Full Disk Access is granted. ```typescript import { IMessageSDK } from '@photon-ai/imessage-kit'; const sdk = new IMessageSDK({ debug: true }); try { await sdk.send('+1234567890', 'Hello from iMessage Kit!'); } finally { await sdk.close(); } ``` -------------------------------- ### Verify Advanced iMessage Kit Setup Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Verify the setup of the advanced iMessage kit by initializing the SDK, connecting to the server, and sending a test message. Obtain your API key and endpoint from photon.codes. ```typescript import { SDK } from '@photon-ai/advanced-imessage-kit'; const sdk = SDK({ serverUrl: 'http://localhost:1234', apiKey: 'your-api-key', logLevel: 'info' }); await sdk.connect(); sdk.on('ready', async () => { await sdk.messages.sendMessage({ chatGuid: 'iMessage;-;+1234567890', message: 'Hello from Advanced Kit!' }); }); sdk.on('error', (error) => { console.error('SDK error:', error); }); await sdk.close(); ``` -------------------------------- ### IMessageSDK Initialization Source: https://context7.com/photon-hq/skills/llms.txt Demonstrates how to install and initialize the IMessageSDK with a custom configuration for self-hosted iMessage access. ```APIDOC ## IMessageSDK Initialization ### Description Install and configure `IMessageSDK` for self-hosted, on-Mac iMessage access. Grant Full Disk Access in System Settings → Privacy & Security → Full Disk Access, then restart your IDE. ### Code Example ```typescript // Bun (zero dependencies) // bun add @photon-ai/imessage-kit // Node: npm install @photon-ai/imessage-kit better-sqlite3 import { IMessageSDK, IMessageConfig } from '@photon-ai/imessage-kit'; const config: IMessageConfig = { debug: true, databasePath: '~/Library/Messages/chat.db', scriptTimeout: 30000, maxConcurrent: 5, watcher: { pollInterval: 2000, unreadOnly: false, excludeOwnMessages: true }, retry: { max: 2, delay: 1500 }, }; // Use 'await using' for automatic cleanup (or call sdk.close() in finally) await using sdk = new IMessageSDK(config); await sdk.send('+15551234567', 'Hello from iMessage Kit!'); ``` ``` -------------------------------- ### sdk.startWatching(callbacks) Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Starts watching for new messages and other events in real-time. ```APIDOC ## `sdk.startWatching(callbacks)` ### Description Starts watching for new messages and other events in real-time. Requires a graceful shutdown using `stopWatching`. ### Parameters #### Path Parameters - **callbacks** (object) - Required - An object containing callback functions for different events. - **onDirectMessage** (function) - Callback for direct messages. - **onGroupMessage** (function) - Callback for group messages. - **onError** (function) - Callback for errors. ### Request Example ```typescript await sdk.startWatching({ onDirectMessage: (msg) => { console.log(`[DM from ${msg.sender}]: ${msg.text}`); }, onGroupMessage: (msg) => { console.log(`[Group ${msg.chatId}]: ${msg.text}`); }, onError: (error) => { console.error('Watcher error:', error); } }); console.log('Watching for new messages... Press Ctrl+C to stop.'); // Graceful shutdown process.on('SIGINT', async () => { sdk.stopWatching(); await sdk.close(); process.exit(0); }); ``` ``` -------------------------------- ### SDK Initialization and Connection Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Demonstrates how to initialize the SDK with configuration and establish a connection to the iMessage service. Includes setup for event listeners for connection status. ```APIDOC ## Initialization & Connection (`SDK`) ```typescript import { SDK, ClientConfig } from '@photon-ai/advanced-imessage-kit'; const config: ClientConfig = { serverUrl: 'http://localhost:1234', // Your server URL from Photon apiKey: 'your-secret-api-key', logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error' logToFile: true // Write logs to ~/Library/Logs/AdvancedIMessageKit (default: true) }; const sdk = SDK(config); sdk.on('ready', () => { console.log('Advanced Kit Ready!'); // Your application logic starts here }); sdk.on('error', (err) => console.error('Connection Error:', err)); sdk.on('disconnect', () => console.log('Disconnected.')); await sdk.connect(); // Graceful shutdown process.on('SIGINT', async () => { await sdk.close(); process.exit(0); }); ``` ``` -------------------------------- ### adapter.startGatewayListener(options) Source: https://github.com/photon-hq/skills/blob/main/skills/chat-adapter-imessage/SKILL.md Starts a listener that ingests messages from both local and remote SDKs and forwards them to the Chat instance. This is the primary method for receiving messages. ```APIDOC ## adapter.startGatewayListener(options) ### Description This is the primary method for receiving messages. It starts a listener that ingests messages from both local and remote SDKs and forwards them to the `Chat` instance. ### Method Starts a message listener. ### Parameters #### Query Parameters - **options** (object) - Optional - Configuration options for the listener. ### Request Example ```typescript // In your main application file const adapter = createiMessageAdapter({ local: true }); const chat = new Chat({ adapter }); // Start listening for incoming iMessages adapter.startGatewayListener(); // Now, messages sent to your iMessage will be processed by the Chat SDK chat.on('message', (message) => { console.log(`[${message.author.userName}]: ${message.text}`); // Your AI logic here... }); ``` ``` -------------------------------- ### Self-Hosted Kit Basic Usage Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md A simple example of sending a message using the Self-Hosted iMessage SDK. ```APIDOC ## Sending Messages (Self-Hosted) ```typescript import { IMessageSDK } from '@photon-ai/imessage-kit'; const sdk = new IMessageSDK({ debug: true }); try { await sdk.send('+1234567890', 'Hello from iMessage Kit!'); } finally { await sdk.close(); } ``` ``` -------------------------------- ### Avoid Echoing Raw Content Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Demonstrates safe and unsafe patterns for responding to messages. The 'BAD' example shows how echoing raw content can be exploited, while the 'GOOD' example shows how to respond with agent-generated content only. ```typescript // BAD — echoes attacker-controlled content await sdk.messages.sendMessage({ chatGuid, message: `You said: ${message.text}` }); ``` ```typescript // GOOD — respond with agent-generated content only await sdk.messages.sendMessage({ chatGuid, message: 'Message received. Processing your request.' }); ``` -------------------------------- ### Get Server Information Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Retrieve information about the iMessage server. Authentication is required. ```bash curl https://imessage-swagger.photon.codes/server \ -H "Authorization: Bearer $TOKEN" ``` -------------------------------- ### Multi-Platform Spectrum App Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/getting-started.md Initialize a Spectrum app with multiple providers (iMessage, WhatsApp Business, Terminal) to handle messages from various platforms. This example replies to all incoming messages. ```typescript import { Spectrum } from "spectrum-ts"; import { imessage } from "spectrum-ts/providers/imessage"; import { terminal } from "spectrum-ts/providers/terminal"; import { whatsappBusiness } from "spectrum-ts/providers/whatsapp-business"; const app = await Spectrum({ projectId: process.env.PROJECT_ID!, projectSecret: process.env.PROJECT_SECRET!, providers: [ imessage.config(), whatsappBusiness.config({ accessToken: process.env.WA_TOKEN!, phoneNumberId: process.env.WA_NUMBER_ID!, appSecret: process.env.WA_SECRET!, }), terminal.config(), ], }); for await (const [space, message] of app.messages) { await space.responding(async () => { await message.reply("Hello from Spectrum."); }); } ``` -------------------------------- ### Starting a Conversation Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/providers/whatsapp-business.md Initiate a conversation with a user by resolving them via their WhatsApp phone number and then sending a message. ```APIDOC ## Starting a conversation Resolve a user by their WhatsApp phone number (international format, digits only): ```ts const wa = whatsappBusiness(app); const customer = await wa.user("15551234567"); const space = await wa.space(customer); await space.send("Thanks for reaching out."); ``` ``` -------------------------------- ### Send and Download Attachments with Photon SDK Source: https://context7.com/photon-hq/skills/llms.txt Use `sdk.attachments` to send local files, audio messages, and stickers. Download attachments or get blurhash placeholders for UI. ```typescript await sdk.attachments.sendAttachment({ chatGuid: 'iMessage;-;+15551234567', filePath: '/path/to/report.pdf', fileName: 'Q1-Report.pdf', }); ``` ```typescript await sdk.attachments.sendAttachment({ chatGuid: 'iMessage;-;+15551234567', filePath: '/path/to/note.m4a', isAudioMessage: true, }); ``` ```typescript await sdk.attachments.sendSticker({ chatGuid: 'iMessage;-;+15551234567', filePath: '/path/to/sticker.png', selectedMessageGuid: 'target-message-guid', stickerX: 0.5, stickerY: 0.5, stickerScale: 0.75, stickerRotation: 0, stickerWidth: 300, }); ``` ```typescript const buffer = await sdk.attachments.downloadAttachment('attachment-guid', { original: true, width: 800, height: 600, quality: 80, }); ``` ```typescript const hash = await sdk.attachments.getAttachmentBlurhash('attachment-guid'); ``` -------------------------------- ### Real-time Event Listeners Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Provides examples of how to subscribe to various real-time events emitted by the SDK, including connection status, message updates, chat events, group events, and Find My location updates. ```APIDOC ## Real-time Events (`sdk.on`) Listen to events to build interactive applications. #### Connection Events ```typescript sdk.on('ready', () => { console.log('SDK connected and ready'); }); sdk.on('disconnect', () => { console.log('Disconnected'); }); sdk.on('error', (error) => { console.error('Error:', error); }); ``` #### Message Events ```typescript sdk.on('new-message', (message) => { console.log(`New message from ${message.handle?.address}: ${message.text}`); }); sdk.on('updated-message', (message) => { if (message.dateRead) console.log('Message read'); else if (message.dateDelivered) console.log('Message delivered'); }); sdk.on('message-send-error', (data) => { console.error('Send failed:', data); }); ``` #### Chat Events ```typescript sdk.on('typing-indicator', ({ display, guid }) => { console.log(`${guid} ${display ? 'is typing' : 'stopped typing'}`); }); sdk.on('chat-read-status-changed', ({ chatGuid, read }) => { console.log(`Chat ${chatGuid} marked as ${read ? 'read' : 'unread'}`); }); ``` #### Group Events ```typescript sdk.on('group-name-change', (message) => { console.log('Group renamed to:', message.groupTitle); }); sdk.on('participant-added', (message) => { console.log('Someone joined the group'); }); sdk.on('participant-removed', (message) => { console.log('Someone was removed from the group'); }); sdk.on('participant-left', (message) => { console.log('Someone left the group'); }); sdk.on('group-icon-changed', (message) => { console.log('Group icon changed'); }); sdk.on('group-icon-removed', (message) => { console.log('Group icon removed'); }); ``` #### Find My Events ```typescript sdk.on('new-findmy-location', (location) => { console.log(`${location.handle} location updated:`, location.coordinates); }); ``` #### Removing Event Listeners ```typescript const handler = (message) => console.log(message); sdk.on('new-message', handler); sdk.off('new-message', handler); sdk.removeAllListeners('new-message'); ``` ``` -------------------------------- ### Get Attachment Info Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Retrieve metadata for a specific attachment using its GUID. ```APIDOC ## Get Attachment Info ### Description Retrieve metadata for a specific attachment using its GUID. ### Method GET ### Endpoint /attachments/{GUID}/info ### Parameters #### Path Parameters - **GUID** (string) - Required - The unique identifier of the attachment. ### Response #### Success Response (200) - **filename** (string) - The name of the attachment file. - **contentType** (string) - The MIME type of the attachment. - **size** (integer) - The size of the attachment in bytes. #### Response Example ```json { "filename": "photo.jpg", "contentType": "image/jpeg", "size": 102400 } ``` ``` -------------------------------- ### Get Single Message Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Retrieve details for a specific message using its GUID. ```APIDOC ## Get Single Message ### Description Retrieve details for a specific message using its GUID. ### Method GET ### Endpoint /messages/{MESSAGE_GUID} ### Parameters #### Path Parameters - **MESSAGE_GUID** (string) - Required - The unique identifier of the message to retrieve. ### Response #### Success Response (200) - **guid** (string) - The unique identifier of the message. - **text** (string) - The content of the message. - **timestamp** (string) - The time the message was sent. #### Response Example ```json { "guid": "message-guid-3", "text": "A specific message.", "timestamp": "2023-10-27T10:10:00Z" } ``` ``` -------------------------------- ### Initialize and Connect SDK Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Configure and establish a connection with the SDK. Ensure your server URL and API key are correctly set. Logs are written to a file by default. ```typescript import { SDK, ClientConfig } from '@photon-ai/advanced-imessage-kit'; const config: ClientConfig = { serverUrl: 'http://localhost:1234', // Your server URL from Photon apiKey: 'your-secret-api-key', logLevel: 'info', // 'debug' | 'info' | 'warn' | 'error' logToFile: true // Write logs to ~/Library/Logs/AdvancedIMessageKit (default: true) }; const sdk = SDK(config); sdk.on('ready', () => { console.log('Advanced Kit Ready!'); // Your application logic starts here }); sdk.on('error', (err) => console.error('Connection Error:', err)); sdk.on('disconnect', () => console.log('Disconnected.')); await sdk.connect(); // Graceful shutdown process.on('SIGINT', async () => { await sdk.close(); process.exit(0); }); ``` -------------------------------- ### Get attachment info using cURL Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Fetch metadata and information about a specific attachment using its GUID. ```bash curl https://imessage-swagger.photon.codes/attachments/GUID/info \ -H "Authorization: Bearer $TOKEN" ``` -------------------------------- ### SDK Initialization and Event Handling Source: https://context7.com/photon-hq/skills/llms.txt Initialize the SDK, set up event listeners for ready, disconnect, error, and message events, and implement exponential backoff for reconnection. ```APIDOC ## SDK Initialization, Events, and Reconnection Connect to Photon's managed infrastructure via WebSocket. Always use exponential backoff reconnection — never a fixed-delay loop. ```typescript import { SDK } from '@photon-ai/advanced-imessage-kit'; const sdk = SDK({ serverUrl: process.env.SERVER_URL!, apiKey: process.env.API_KEY!, logLevel: 'info', logToFile: true, }); // Exponential backoff reconnect (REQUIRED — never use fixed setTimeout) function createReconnect(sdk: any, opts?: { maxRetries?: number; baseMs?: number; maxMs?: number }) { const maxRetries = opts?.maxRetries ?? 10; const baseMs = opts?.baseMs ?? 1_000; const maxMs = opts?.maxMs ?? 60_000; let attempt = 0; return { retry() { if (attempt >= maxRetries) { console.error('Giving up'); process.exit(1); } const delay = Math.min(baseMs * 2 ** attempt, maxMs); const jitter = delay * (0.5 + Math.random() * 0.5); attempt++; setTimeout(() => sdk.connect(), jitter); }, reset() { attempt = 0; } }; } const reconnect = createReconnect(sdk); sdk.on('ready', () => reconnect.reset()); sdk.on('disconnect', () => reconnect.retry()); sdk.on('error', (err) => console.error('[SDK Error]', err)); sdk.on('message-send-error', (data) => console.error('[Delivery Failed]', data)); // Listen for new messages sdk.on('new-message', async (message) => { if (message.isFromMe) return; const chatGuid = message.chats?.[0]?.guid; if (!chatGuid || !message.text) return; console.log(`${message.handle?.address}: ${message.text}`); }); // Typing events, group events, Find My sdk.on('typing-indicator', ({ display, guid }) => console.log(`${guid} ${display ? 'typing…' : 'stopped'}`)); sdk.on('group-name-change', (msg) => console.log('Renamed to:', msg.groupTitle)); sdk.on('new-findmy-location', (loc) => console.log(`${loc.handle}: ${loc.coordinates}`)); await sdk.connect(); const shutdown = async () => { await sdk.close(); process.exit(0); }; process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); ``` ``` -------------------------------- ### Get a single message using cURL Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Retrieve the details of a specific message by its GUID. This is useful for inspecting individual messages. ```bash curl https://imessage-swagger.photon.codes/messages/MESSAGE_GUID \ -H "Authorization: Bearer $TOKEN" ``` -------------------------------- ### Initialize Spectrum App with Providers Source: https://context7.com/photon-hq/skills/llms.txt Set up a Spectrum app instance, optionally configuring multiple platform providers. All message sources are merged into a single `app.messages` async iterable. Ensure project credentials are set in environment variables. ```typescript import { Spectrum } from 'spectrum-ts'; import { imessage } from 'spectrum-ts/providers/imessage'; import { terminal } from 'spectrum-ts/providers/terminal'; import { whatsappBusiness } from 'spectrum-ts/providers/whatsapp-business'; // Single platform (iMessage cloud mode) const app = await Spectrum({ projectId: process.env.PROJECT_ID!, projectSecret: process.env.PROJECT_SECRET!, providers: [imessage.config()], }); // Multi-platform — all sources merge into app.messages const multiApp = await Spectrum({ projectId: process.env.PROJECT_ID!, projectSecret: process.env.PROJECT_SECRET!, providers: [ imessage.config(), whatsappBusiness.config({ accessToken: process.env.WA_TOKEN!, phoneNumberId: process.env.WA_NUMBER_ID!, appSecret: process.env.WA_SECRET!, }), terminal.config(), // No credentials needed — great for local dev/testing ], }); // Core message loop for await (const [space, message] of multiApp.messages) { if (message.content.type !== 'text') continue; console.log(`[${message.platform}] ${message.sender.id}: ${message.content.text}`); await space.responding(async () => { await space.send(`Echo on ${message.platform}: ${message.content.text}`); }); } // Graceful shutdown (SIGINT/SIGTERM are auto-registered) await multiApp.stop(); ``` -------------------------------- ### Handles API Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Enables checking iMessage and FaceTime availability for a given handle, querying handles with filters, retrieving a single handle by GUID, getting the total handle count, and checking a handle's focus status. ```APIDOC ## Handles (`sdk.handles`) ### Description Enables checking iMessage and FaceTime availability for a given handle, querying handles with filters, retrieving a single handle by GUID, getting the total handle count, and checking a handle's focus status. ### Methods #### `getHandleAvailability(address: string, service: 'imessage' | 'facetime')` - **Description**: Checks if a contact has iMessage or FaceTime service available. - **Parameters**: - `address` (string) - The phone number or email address of the contact. - `service` ('imessage' | 'facetime') - The service to check availability for. - **Returns**: A promise that resolves to a boolean indicating availability. #### `queryHandles(options: object)` - **Description**: Queries handles with filtering options. - **Parameters**: - `options` (object) - An object containing query parameters: - `address` (string) - The address to query. - `with` (array) - An array of related data to include (e.g., ['chats']). - `offset` (number) - The offset for pagination. - `limit` (number) - The limit for the number of results. - **Returns**: A promise that resolves to an array of handle objects matching the query. #### `getHandle(handleGuid: string)` - **Description**: Retrieves a single handle by its unique GUID. - **Parameters**: - `handleGuid` (string) - The GUID of the handle. - **Returns**: A promise that resolves to the handle object. #### `getHandleCount()` - **Description**: Gets the total count of handles. - **Returns**: A promise that resolves to the total number of handles. ``` -------------------------------- ### Self-Hosted Kit Initialization Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Demonstrates how to initialize the Self-Hosted IMessageSDK with various configuration options. ```APIDOC ## Initialization (`new IMessageSDK`) The constructor accepts a single `IMessageConfig` object. ```typescript import { IMessageSDK, IMessageConfig } from '@photon-ai/imessage-kit'; const config: IMessageConfig = { debug: true, // Verbose logging databasePath: '~/Library/Messages/chat.db', // Path to iMessage DB scriptTimeout: 30000, // AppleScript execution timeout (ms) maxConcurrent: 5, // Max parallel send operations watcher: { pollInterval: 2000, // How often to check for new messages (ms) unreadOnly: false, // Only watch for unread messages excludeOwnMessages: true // Ignore messages you send }, retry: { max: 2, // Max retries on send failure delay: 1500 // Base delay between retries (ms) }, tempFile: { maxAge: 600000, // 10 minutes cleanupInterval: 300000 // 5 minutes }, plugins: [/* ... your plugins ... */] }; const sdk = new IMessageSDK(config); // Always close the SDK to release resources await sdk.close(); // Or use the modern 'using' syntax for automatic cleanup await using sdk = new IMessageSDK(); ``` ``` -------------------------------- ### Stable client GUIDs for deduplication Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/best-practices.md Assign a deterministic `clientGuid` to each message at enqueue time. The transport uses this GUID for deduplication, discarding any message with a previously seen GUID to prevent duplicates during retries. ```typescript const messages = reply.map((text, index) => ({ text, clientGuid: `${jobId}-${index}`, })); ``` -------------------------------- ### Log Safely Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Illustrates secure logging practices. The 'BAD' example shows how logging full message content can leak private conversations, while the 'GOOD' example logs only metadata. ```typescript // BAD console.log(`Message from ${sender}: ${message.text}`); ``` ```typescript // GOOD console.log(`Message received — guid: ${message.guid}, sender: ${sender}, length: ${message.text?.length ?? 0}`); ``` -------------------------------- ### Typing Indicators with `responding` Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/spaces-and-users.md Demonstrates the recommended pattern for using the `responding` method to manage typing indicators during asynchronous operations. ```APIDOC ## Typing Indicators ### Description Use the `responding` pattern to manage typing indicators, ensuring they are cleared properly. ### Usage ```ts await space.responding(async () => { const result = await generateResponse(message); await space.send(result); }); ``` Alternatively, use the `app` helper: ```ts await app.responding(space, async () => { ... }); ``` ``` -------------------------------- ### Advanced Kit Initialization and Connection Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Shows how to initialize and connect to the Advanced iMessage Kit using server URL and API key. ```APIDOC ## Advanced Kit Setup ```typescript import { SDK } from '@photon-ai/advanced-imessage-kit'; const sdk = SDK({ serverUrl: 'http://localhost:1234', apiKey: 'your-api-key', logLevel: 'info' }); await sdk.connect(); sdk.on('ready', async () => { await sdk.messages.sendMessage({ chatGuid: 'iMessage;-;+1234567890', message: 'Hello from Advanced Kit!' }); }); sdk.on('error', (error) => { console.error('SDK error:', error); }); await sdk.close(); ``` ``` -------------------------------- ### Worker and Transport interaction for resume cursor Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/best-practices.md Illustrates how a worker assigns client GUIDs, sends messages to the transport, and persists a `startIndex` to resume from after a crash. The transport acknowledges messages and discards duplicates based on client GUIDs. ```mermaid sequenceDiagram participant W as Worker participant Tx as Transport W->>W: assign clientGuids [A, B, C, D] W->>Tx: send msg[0] (guid A) Tx-->>W: ack W->>W: startIndex = 1 (persisted) W->>Tx: send msg[1] (guid B) Tx-->>W: ack W->>W: startIndex = 2 (persisted) W-x W: ⚡ crash Note over W,Tx: retry — load startIndex=2 W->>Tx: send msg[2] (guid C) W->>Tx: send msg[3] (guid D) ``` -------------------------------- ### Download Attachment Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Download a specific attachment using its GUID. ```APIDOC ## Download Attachment ### Description Download a specific attachment using its GUID. ### Method GET ### Endpoint /attachments/{GUID} ### Parameters #### Path Parameters - **GUID** (string) - Required - The unique identifier of the attachment to download. ### Response #### Success Response (200) - The raw content of the attachment file. ### Response Example (Returns the file content directly, e.g., image data) ``` -------------------------------- ### Initialize SDK, Handle Events, and Reconnect Source: https://context7.com/photon-hq/skills/llms.txt Connects to Photon's managed infrastructure using WebSockets. It includes a required exponential backoff reconnection strategy. Listen for various events like message delivery, errors, and typing indicators. ```typescript import { SDK } from '@photon-ai/advanced-imessage-kit'; const sdk = SDK({ serverUrl: process.env.SERVER_URL!, apiKey: process.env.API_KEY!, logLevel: 'info', logToFile: true, }); // Exponential backoff reconnect (REQUIRED — never use fixed setTimeout) function createReconnect(sdk: any, opts?: { maxRetries?: number; baseMs?: number; maxMs?: number }) { const maxRetries = opts?.maxRetries ?? 10; const baseMs = opts?.baseMs ?? 1_000; const maxMs = opts?.maxMs ?? 60_000; let attempt = 0; return { retry() { if (attempt >= maxRetries) { console.error('Giving up'); process.exit(1); } const delay = Math.min(baseMs * 2 ** attempt, maxMs); const jitter = delay * (0.5 + Math.random() * 0.5); attempt++; setTimeout(() => sdk.connect(), jitter); }, reset() { attempt = 0; } }; } const reconnect = createReconnect(sdk); sdk.on('ready', () => reconnect.reset()); sdk.on('disconnect', () => reconnect.retry()); sdk.on('error', (err) => console.error('[SDK Error]', err)); sdk.on('message-send-error', (data) => console.error('[Delivery Failed]', data)); // Listen for new messages sdk.on('new-message', async (message) => { if (message.isFromMe) return; const chatGuid = message.chats?.[0]?.guid; if (!chatGuid || !message.text) return; console.log(`${message.handle?.address}: ${message.text}`); }); // Typing events, group events, Find My sdk.on('typing-indicator', ({ display, guid }) => console.log(`${guid} ${display ? 'typing…' : 'stopped'}`)); sdk.on('group-name-change', (msg) => console.log('Renamed to:', msg.groupTitle)); sdk.on('new-findmy-location', (loc) => console.log(`${loc.handle}: ${loc.coordinates}`)); await sdk.connect(); const shutdown = async () => { await sdk.close(); process.exit(0); }; process.on('SIGINT', shutdown); process.on('SIGTERM', shutdown); ``` -------------------------------- ### Get Chat Participants Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Retrieve the participants of a specific group chat. ```APIDOC ## Get Chat Participants ### Description Retrieve the participants of a specific group chat. ### Method GET ### Endpoint /chats/{chatId}/participants ### Parameters #### Path Parameters - **chatId** (string) - Required - The unique identifier of the group chat. ### Response #### Success Response (200) - **participants** (array) - An array of participant identifiers. #### Response Example ```json { "participants": [ "user1@example.com", "user2@example.com" ] } ``` ``` -------------------------------- ### adapter.initialize(chat) Source: https://github.com/photon-hq/skills/blob/main/skills/chat-adapter-imessage/SKILL.md Called by the Chat instance to link the adapter. In remote mode, this also establishes the WebSocket connection to the Photon server. ```APIDOC ## adapter.initialize(chat) ### Description Initializes the adapter and links it with the provided `Chat` instance. For remote mode, this method establishes the necessary WebSocket connection to the Photon server. ### Method `initialize` ### Parameters #### Path Parameters None #### Query Parameters None #### Request Body - **chat** (Chat) - Required - The Chat instance from the Vercel AI SDK. ### Request Example ```typescript // Assuming 'adapter' is an initialized iMessageAdapter instance // and 'chat' is a Chat instance from Vercel AI SDK await adapter.initialize(chat); ``` ### Response #### Success Response (200) This method does not return a value, but performs initialization actions. #### Response Example None ``` -------------------------------- ### Get Chat Details Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Retrieve detailed information about a specific chat. ```APIDOC ## Get Chat Details ### Description Retrieve detailed information about a specific chat. ### Method GET ### Endpoint /chats/{chatId} ### Parameters #### Path Parameters - **chatId** (string) - Required - The unique identifier of the chat. ### Response #### Success Response (200) - **id** (string) - The unique identifier for the chat. - **displayName** (string) - The display name of the chat. - **lastMessageTimestamp** (string) - The timestamp of the last message in the chat. #### Response Example ```json { "id": "user@example.com", "displayName": "Jane Smith", "lastMessageTimestamp": "2023-10-27T11:00:00Z" } ``` ``` -------------------------------- ### Initialize Spectrum with Terminal Provider Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/providers/terminal.md Initialize Spectrum with the terminal provider configuration. The `terminal.config()` function spawns the `tuichat` binary. ```typescript const app = await Spectrum({ providers: [terminal.config()] }); ``` -------------------------------- ### Trigger Message Notification Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Triggers a notification for a specific message using its GUID. ```APIDOC ## Trigger Message Notification ### Description Triggers a notification for a specific message. ### Method `sdk.messages.notifyMessage(messageGuid: string)` ### Parameters #### Path Parameters - **messageGuid** (string) - Required - The GUID of the message to trigger a notification for. ``` -------------------------------- ### Add Photon Skills Source: https://github.com/photon-hq/skills/blob/main/README.md Use this command to add Photon skills to your project. Replace '' with the desired skill. ```bash npx skills add photon-hq/skills --skill ``` -------------------------------- ### Configuration Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/providers/whatsapp-business.md Configure the WhatsApp Business provider with your access token, phone number ID, and app secret obtained from Meta for Developers. ```APIDOC ## Config ```ts whatsappBusiness.config({ accessToken: "...", // permanent or system-user token from Meta for Developers phoneNumberId: "...", // sender phone number ID appSecret: "...", // verifies webhook payload signatures }); ``` Find these values in your WhatsApp Business app settings on [Meta for Developers](https://developers.facebook.com/). ``` -------------------------------- ### Register and Use Custom Platform Source: https://github.com/photon-hq/skills/blob/main/skills/spectrum/custom-platforms.md Register a custom platform with Spectrum by providing its configuration to the `Spectrum` constructor via the `providers` array. Instantiate the platform and use its methods to interact with users and spaces. ```typescript const app = await Spectrum({ providers: [myPlatform.config({ apiKey: process.env.MY_KEY! })], }); const mine = myPlatform(app); const space = await mine.space(await mine.user("user-123")); await space.send("Hello from my custom platform."); ``` -------------------------------- ### Get Poll Details Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Retrieve details for a specific poll using its ID. ```APIDOC ## Get Poll Details ### Description Retrieve details for a specific poll using its ID. ### Method GET ### Endpoint /polls/{pollId} ### Parameters #### Path Parameters - **pollId** (string) - Required - The unique identifier of the poll. ### Response #### Success Response (200) - **pollId** (string) - The unique identifier of the poll. - **question** (string) - The poll question. - **options** (array) - An array of poll options with their IDs and vote counts. - **optionId** (string) - The ID of the option. - **text** (string) - The text of the option. - **votes** (integer) - The number of votes for this option. #### Response Example ```json { "pollId": "poll-guid-8", "question": "Favorite color?", "options": [ { "optionId": "opt1", "text": "Blue", "votes": 15 }, { "optionId": "opt2", "text": "Green", "votes": 10 } ] } ``` ``` -------------------------------- ### Get Message Counts Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Provides methods to retrieve different counts of messages. ```APIDOC ## Get Message Counts ### Description Retrieves various counts related to messages. ### Methods - `sdk.messages.getMessageCount()`: Gets the total number of messages. - `sdk.messages.getSentMessageCount()`: Gets the number of sent messages. - `sdk.messages.getUpdatedMessageCount()`: Gets the number of updated messages. ``` -------------------------------- ### Start Real-time Message Watching Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Initiate real-time message monitoring using `sdk.startWatching`, providing callback functions for direct messages, group messages, and errors. Remember to call `sdk.stopWatching()` and `sdk.close()` for graceful shutdown. ```typescript await sdk.startWatching({ onDirectMessage: (msg) => { console.log(`[DM from ${msg.sender}]: ${msg.text}`); }, onGroupMessage: (msg) => { console.log(`[Group ${msg.chatId}]: ${msg.text}`); }, onError: (error) => { console.error('Watcher error:', error); } }); console.log('Watching for new messages... Press Ctrl+C to stop.'); // Graceful shutdown process.on('SIGINT', async () => { sdk.stopWatching(); await sdk.close(); process.exit(0); }); ``` -------------------------------- ### Get Embedded Media Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Retrieves embedded media content from a specific message. ```APIDOC ## Get Embedded Media ### Description Retrieves embedded media from a message. ### Method `sdk.messages.getEmbeddedMedia(messageGuid: string)` ### Parameters #### Path Parameters - **messageGuid** (string) - Required - The GUID of the message from which to retrieve media. ``` -------------------------------- ### Define Custom Platform Provider with `definePlatform` Source: https://context7.com/photon-hq/skills/llms.txt Implement the full provider contract to connect any messaging backend to Spectrum's unified interface. This example shows configuration, user/space resolution, client lifecycle management, event handling, and actions. ```typescript import { Spectrum, definePlatform } from 'spectrum-ts'; import z from 'zod'; export const myPlatform = definePlatform('my-platform', { config: z.object({ apiKey: z.string(), webhookSecret: z.string() }), user: { resolve: async ({ input, client }) => ({ id: input.userID, displayName: await client.lookupUser(input.userID), }), }, space: { resolve: async ({ input, client }) => ({ id: await client.findOrCreateConversation(input.users.map(u => u.id)), }), }, lifecycle: { createClient: async ({ config }) => new MyPlatformClient(config.apiKey), destroyClient: async ({ client }) => { await client.disconnect(); }, }, events: { async *messages({ client }) { for await (const msg of client.onMessage()) { yield { id: msg.id, content: { type: 'text' as const, text: msg.body }, sender: { id: msg.authorId }, space: { id: msg.channelId }, timestamp: new Date(msg.ts), }; } }, async *typing({ client }) { for await (const ev of client.onTyping()) { yield { spaceId: ev.chatId, userId: ev.user }; } }, }, actions: { send: async ({ space, content, client }) => { if (content.type === 'text') await client.send(space.id, content.text); }, startTyping: async ({ space, client }) => { await client.setTyping(space.id, true); }, stopTyping: async ({ space, client }) => { await client.setTyping(space.id, false); }, reactToMessage: async ({ message, reaction, client }) => { await client.react(message.id, reaction); }, }, static: { reactions: { thumbsUp: '+1', thumbsDown: '-1' } as const, }, }); // Register and use const app = await Spectrum({ providers: [myPlatform.config({ apiKey: process.env.MY_KEY!, webhookSecret: process.env.MY_SECRET! })], }); const mine = myPlatform(app); const space = await mine.space(await mine.user('user-123')); await space.send('Hello from my custom platform.'); ``` -------------------------------- ### Group Icon Management Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Allows setting, getting, and removing group icons for chats. ```APIDOC ## Group Icon Management ### Description Manage the icon for group chats by setting it from a local file, retrieving it, or removing it. ### Methods #### `sdk.chats.setGroupIcon(chatGuid: string, filePath: string): Promise #### `sdk.chats.getGroupIcon(chatGuid: string): Promise #### `sdk.chats.removeGroupIcon(chatGuid: string): Promise ### Parameters - **chatGuid** (string) - Required - The unique identifier for the chat. - **filePath** (string) - Required - The local path to the image file for `setGroupIcon`. ``` -------------------------------- ### Fetch and Manage Contacts with SDK Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Demonstrates fetching all device contacts, retrieving a specific contact card by phone or email, and checking recommendation to share contact cards in a chat. ```typescript // Fetch all device contacts const contacts = await sdk.contacts.getContacts(); // Get a specific contact card by phone or email const card = await sdk.contacts.getContactCard('+1234567890'); // { firstName, lastName, emails, phones, ... } // Check whether you should share your contact card in this chat // Returns true when recommended (e.g., other side shared theirs, you haven't yet) const shouldShare = await sdk.contacts.shouldShareContact('chat-guid'); if (shouldShare) { await sdk.contacts.shareContactCard('chat-guid'); } ``` -------------------------------- ### Get Chat Messages Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Retrieve messages for a specific chat, with options to limit the number of results. ```APIDOC ## Get Chat Messages ### Description Retrieve messages for a specific chat, with options to limit the number of results. ### Method GET ### Endpoint /chats/{chatId}/messages ### Parameters #### Path Parameters - **chatId** (string) - Required - The unique identifier of the chat. #### Query Parameters - **limit** (integer) - Optional - The maximum number of messages to return. ### Response #### Success Response (200) - **messages** (array) - An array of message objects. - **guid** (string) - The unique identifier of the message. - **text** (string) - The content of the message. - **timestamp** (string) - The time the message was sent. #### Response Example ```json { "messages": [ { "guid": "chat-message-guid-6", "text": "Hi there!", "timestamp": "2023-10-27T11:05:00Z" } ] } ``` ``` -------------------------------- ### Chat Background Management Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Provides functionality to get, set, and remove custom backgrounds for chats. ```APIDOC ## Chat Background Management ### Description Retrieve information about the current chat background, set a new background from a file or base64 data, or remove the background. ### Methods #### `sdk.chats.getBackground(chatGuid: string): Promise<{ hasBackground: boolean, backgroundId?: string, imageUrl?: string }> #### `sdk.chats.setBackground(chatGuid: string, options: { filePath?: string, fileData?: string }): Promise #### `sdk.chats.removeBackground(chatGuid: string): Promise ### Parameters - **chatGuid** (string) - Required - The unique identifier for the chat. #### `setBackground` Options - **filePath** (string) - Optional - The local path to the background image file. - **fileData** (string) - Optional - The background image data in base64 format. ``` -------------------------------- ### Unsend a message using cURL Source: https://github.com/photon-hq/skills/blob/main/skills/imessage/SKILL.md Remove a previously sent message by specifying its GUID. This action is irreversible. ```bash curl -X DELETE https://imessage-swagger.photon.codes/messages/MESSAGE_GUID \ -H "Authorization: Bearer $TOKEN" ```