@photon-ai/advanced-imessage-kit
v1.4.1
Published
Powerful TypeScript iMessage SDK with real-time message processing
Maintainers
Readme

Advanced iMessage Kit
A powerful TypeScript SDK for iMessage with real-time messaging support
Advanced iMessage Kit is a full-featured iMessage SDK for reading, sending, and automating iMessage conversations on macOS. Perfect for building AI agents, automation tools, and chat applications.
Features
| Feature | Description | Method | Example |
| ------- | ----------- | ------ | ------- |
| Send Messages | Send text messages to any contact | messages.sendMessage() | message-send.ts |
| Reply to Messages | Reply inline to a specific message | messages.sendMessage() | message-reply.ts |
| Message Effects | Send with effects (confetti, fireworks, etc.) | messages.sendMessage() | message-effects.ts |
| Unsend Messages | Retract a sent message | messages.unsendMessage() | message-unsend.ts |
| Send Tapbacks | React with ❤️ 👍 👎 😂 ‼️ ❓ | messages.sendReaction() | message-reaction.ts |
| Query Messages | Search and filter message history | messages.getMessages() | message-search.ts |
| Message History | View messages, reactions, polls, stickers | chats.getChatMessages() | message-history.ts |
| Send Attachments | Send images, files, documents | attachments.sendAttachment() | message-attachment.ts |
| Send Audio Messages | Send voice messages | attachments.sendAttachment() | message-audio.ts |
| Send Stickers | Send sticker as standalone message | attachments.sendSticker() | message-sticker.ts |
| Reply Stickers | Attach sticker to a message bubble | attachments.sendSticker() | message-reply-sticker.ts |
| Download Attachments | Download received files and media | attachments.downloadAttachment() | attachment-download.ts |
| Get Chats | List all conversations | chats.getChats() | chat-fetch.ts |
| Manage Group Chats | Add/remove members, rename groups | chats.addParticipant() | chat-group.ts |
| Typing Indicators | Show "typing..." status | chats.startTyping() | message-typing.ts |
| Get Contacts | Fetch device contacts | contacts.getContacts() | contact-list.ts |
| Share Contact Card | Share your contact info in chat | contacts.shareContactCard() | message-contact-card.ts |
| Check iMessage Availability | Verify if contact uses iMessage | handles.getHandleAvailability() | service-check.ts |
| Server Info | Get server status and config | server.getServerInfo() | server-info.ts |
| Message Statistics | Get message counts and analytics | server.getMessageStats() | message-stats.ts |
| Create Polls | Create interactive polls in chat | polls.create() | poll-create.ts |
| Vote on Polls | Vote or unvote on poll options | polls.vote() | poll-create.ts |
| Add Poll Options | Add options to existing polls | polls.addOption() | poll-add-option.ts |
| Find My Friends (WIP) | Get friends' locations | icloud.getFindMyFriends() | findmy-friends.ts |
| Real-time Events | Listen for new messages, typing, etc. | sdk.on() | listen-simple.ts |
| Auto Reply | Build automated reply bots | sdk.on() | auto-reply-hey.ts |
Quick Start
Installation
npm install @photon-ai/advanced-imessage-kit
# or
bun add @photon-ai/advanced-imessage-kitBasic Usage
import { SDK } from "@photon-ai/advanced-imessage-kit";
const sdk = SDK({
serverUrl: "http://localhost:1234",
});
await sdk.connect();
sdk.on("new-message", (message) => {
console.log("New message:", message.text);
});
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "Hello World!",
});
await sdk.close();Configuration
interface ClientConfig {
serverUrl?: string; // Server URL, defaults to "http://localhost:1234"
apiKey?: string; // API key (if server requires authentication)
logLevel?: "debug" | "info" | "warn" | "error"; // Log level, defaults to "info"
}Core Concepts
chatGuid Format
chatGuid is the unique identifier for a conversation. The format is service;-;address:
- iMessage DM:
iMessage;-;+1234567890oriMessage;-;[email protected] - SMS DM:
SMS;-;+1234567890 - Group chat:
iMessage;+;chat123456789 - Auto-detect:
any;-;+1234567890(SDK automatically detects the service type)
How to Get IDs
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Phone / Email │────▶│ Build chatGuid │────▶│ Send Message │
│ +1234567890 │ │ any;-;+123... │ │ sendMessage() │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ getChats() │────▶│ Get chat.guid │────▶│ Use for other │
│ List chats │ │ │ │ operations │
└─────────────────┘ └─────────────────┘ └─────────────────┘
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ sendMessage() │────▶│ Get message.guid│────▶│ edit/unsend │
│ Send message │ │ │ │ sendReaction │
└─────────────────┘ └─────────────────┘ └─────────────────┘Messages
Examples: message-send.ts | message-unsend.ts | message-reaction.ts | message-search.ts
Send Messages
// Send a text message
const message = await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "Hello!",
});
// With subject and effect
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "Happy Birthday!",
subject: "Wishes",
effectId: "com.apple.messages.effect.CKConfettiEffect",
});
// Reply to a message
await sdk.messages.sendMessage({
chatGuid: "iMessage;-;+1234567890",
message: "This is a reply",
selectedMessageGuid: "original-message-guid",
});Message Effects:
| Effect | effectId |
| ------------- | ------------------------------------------------- |
| Confetti | com.apple.messages.effect.CKConfettiEffect |
| Fireworks | com.apple.messages.effect.CKFireworksEffect |
| Balloons | com.apple.messages.effect.CKBalloonEffect |
| Hearts | com.apple.messages.effect.CKHeartEffect |
| Lasers | com.apple.messages.effect.CKHappyBirthdayEffect |
| Shooting Star | com.apple.messages.effect.CKShootingStarEffect |
| Sparkles | com.apple.messages.effect.CKSparklesEffect |
| Echo | com.apple.messages.effect.CKEchoEffect |
| Spotlight | com.apple.messages.effect.CKSpotlightEffect |
| Gentle | com.apple.MobileSMS.expressivesend.gentle |
| Loud | com.apple.MobileSMS.expressivesend.loud |
| Slam | com.apple.MobileSMS.expressivesend.impact |
| Invisible Ink | com.apple.MobileSMS.expressivesend.invisibleink |
Example: message-effects.ts
Query Messages
// Get a single message
const message = await sdk.messages.getMessage("message-guid");
// Query messages
const messages = await sdk.messages.getMessages({
chatGuid: "iMessage;-;+1234567890",
limit: 50,
offset: 0,
sort: "DESC", // DESC = newest first, ASC = oldest first
before: Date.now(),
after: Date.now() - 86400000, // Last 24 hours
});
// Search messages
const results = await sdk.messages.searchMessages({
query: "keyword",
chatGuid: "iMessage;-;+1234567890", // Optional
limit: 20,
});
// Get counts
const total = await sdk.messages.getMessageCount();
const sent = await sdk.messages.getSentMessageCount();
const updated = await sdk.messages.getUpdatedMessageCount();Unsend Messages
await sdk.messages.unsendMessage({
messageGuid: "message-guid-to-unsend",
partIndex: 0, // Optional
});Example: message-unsend.ts
Send Tapbacks
await sdk.messages.sendReaction({
chatGuid: "iMessage;-;+1234567890",
messageGuid: "target-message-guid",
reaction: "love", // love, like, dislike, laugh, emphasize, question
partIndex: 0, // Optional
});
// Remove a Tapback (prefix with -)
await sdk.messages.sendReaction({
chatGuid: "iMessage;-;+1234567890",
messageGuid: "target-message-guid",
reaction: "-love", // -love, -like, -dislike, -laugh, -emphasize, -question
});Example: message-reaction.ts
Other Message Operations
// Trigger message notification
await sdk.messages.notifyMessage("message-guid");
// Get embedded media
const media = await sdk.messages.getEmbeddedMedia("message-guid");Chats
Examples: chat-fetch.ts | chat-group.ts | message-typing.ts
Get Chats
const chats = await sdk.chats.getChats({
withLastMessage: true, // Include last message
withArchived: false, // Include archived chats
offset: 0,
limit: 50,
});
// Get chat count
const count = await sdk.chats.getChatCount();Get Single Chat
const chat = await sdk.chats.getChat("chat-guid", {
with: ["participants", "lastMessage"],
});Create Chat
const newChat = await sdk.chats.createChat({
addresses: ["+1234567890", "+0987654321"],
message: "Hello everyone!", // Optional initial message
service: "iMessage", // "iMessage" or "SMS"
method: "private-api", // "apple-script" or "private-api"
});Chat Status
// Mark as read/unread
await sdk.chats.markChatRead("chat-guid");
await sdk.chats.markChatUnread("chat-guid");
// Delete chat
await sdk.chats.deleteChat("chat-guid");Typing Indicators
// Show "typing..."
await sdk.chats.startTyping("chat-guid");
// Stop showing
await sdk.chats.stopTyping("chat-guid");Example: message-typing.ts
Get Chat Messages
const messages = await sdk.chats.getChatMessages("chat-guid", {
limit: 100,
offset: 0,
sort: "DESC",
before: Date.now(),
after: Date.now() - 86400000,
});Manage Group Chats
// Rename group
await sdk.chats.updateChat("chat-guid", {
displayName: "New Group Name",
});
// Add participant
await sdk.chats.addParticipant("chat-guid", "+1234567890");
// Remove participant
await sdk.chats.removeParticipant("chat-guid", "+1234567890");
// Leave group
await sdk.chats.leaveChat("chat-guid");Group Icon
// Set group icon
await sdk.chats.setGroupIcon("chat-guid", "/path/to/image.jpg");
// Get group icon
const iconBuffer = await sdk.chats.getGroupIcon("chat-guid");
// Remove group icon
await sdk.chats.removeGroupIcon("chat-guid");Example: chat-group.ts
Attachments
Examples: message-attachment.ts | message-audio.ts | message-reply-sticker.ts | attachment-download.ts
Send Attachments
const message = await sdk.attachments.sendAttachment({
chatGuid: "iMessage;-;+1234567890",
filePath: "/path/to/file.jpg",
fileName: "custom-name.jpg", // Optional
});Send Audio Messages
const message = await sdk.attachments.sendAttachment({
chatGuid: "iMessage;-;+1234567890",
filePath: "/path/to/audio.m4a",
isAudioMessage: true,
});Example: message-audio.ts
Send Stickers
Stickers can be sent in two ways:
Standalone Sticker - Sends as its own message (like sending an image, but with sticker styling):
await sdk.attachments.sendSticker({
chatGuid: "iMessage;-;+1234567890",
filePath: "/path/to/sticker.png",
});Example: message-sticker.ts
Reply Sticker (Tapback Sticker) - Attaches to an existing message bubble:
await sdk.attachments.sendSticker({
chatGuid: "iMessage;-;+1234567890",
filePath: "/path/to/sticker.png",
selectedMessageGuid: "target-message-guid", // Required for reply sticker
stickerX: 0.5, // Position X (0-1), default: 0.5
stickerY: 0.5, // Position Y (0-1), default: 0.5
stickerScale: 0.75, // Scale (0-1), default: 0.75
stickerRotation: 0, // Rotation in radians, default: 0
stickerWidth: 300, // Width in pixels, default: 300
});Example: message-reply-sticker.ts
Get Attachment Info
// Get attachment details
const attachment = await sdk.attachments.getAttachment("attachment-guid");
// Get total count
const count = await sdk.attachments.getAttachmentCount();Download Attachments
// Download attachment
const buffer = await sdk.attachments.downloadAttachment("attachment-guid", {
original: true, // Download original file
force: false, // Force re-download
width: 800, // Image width (for thumbnails)
height: 600, // Image height
quality: 80, // Image quality
});
// Download Live Photo video
const liveBuffer = await sdk.attachments.downloadAttachmentLive(
"attachment-guid"
);
// Get blurhash (for placeholders)
const blurhash = await sdk.attachments.getAttachmentBlurhash("attachment-guid");Example: attachment-download.ts
Contacts
Example: contact-list.ts
Get Contacts
const contacts = await sdk.contacts.getContacts();Get Contact Card
// Get contact card by phone or email
const card = await sdk.contacts.getContactCard("+1234567890");
// {
// firstName: "John",
// lastName: "Doe",
// emails: ["[email protected]"],
// phones: ["+1234567890"],
// ...
// }Share Contact Card
Share your contact card with a chat:
// Check if sharing is recommended
const shouldShare = await sdk.contacts.shouldShareContact("chat-guid");
// Returns false if you've already shared or they haven't shared theirs
// Share your contact card
await sdk.contacts.shareContactCard("chat-guid");Example: message-contact-card.ts
Handles
Examples: service-check.ts | handle-query.ts
A Handle represents a messaging address (phone number or email).
Query Handles
// Query handles
const result = await sdk.handles.queryHandles({
address: "+1234567890", // Optional, filter by address
with: ["chats"], // Optional, include related chats
offset: 0,
limit: 50,
});
// Get single handle
const handle = await sdk.handles.getHandle("handle-guid");
// Get total count
const count = await sdk.handles.getHandleCount();Check Service Availability
Check if a phone/email supports iMessage or FaceTime:
// First parameter is the address (phone or email), not handle guid
const hasIMessage = await sdk.handles.getHandleAvailability(
"+1234567890",
"imessage"
);
const hasFaceTime = await sdk.handles.getHandleAvailability(
"+1234567890",
"facetime"
);
// Choose service based on availability
const chatGuid = hasIMessage ? `iMessage;-;+1234567890` : `SMS;-;+1234567890`;Example: service-check.ts
Get Focus Status
const focusStatus = await sdk.handles.getHandleFocusStatus("handle-guid");Server
Examples: message-stats.ts | server-info.ts
Get Server Info
const info = await sdk.server.getServerInfo();
// {
// os_version: "14.0",
// server_version: "1.0.0",
// private_api: true,
// helper_connected: true,
// detected_icloud: "[email protected]",
// ...
// }Message Statistics
const stats = await sdk.server.getMessageStats();
// {
// total: 12345,
// sent: 5000,
// received: 7345,
// last24h: 50,
// last7d: 300,
// last30d: 1000,
// }Media Statistics
// All media stats
const mediaStats = await sdk.server.getMediaStatistics();
// Per-chat media stats
const chatMediaStats = await sdk.server.getMediaStatisticsByChat();Server Logs
const logs = await sdk.server.getServerLogs(100); // Get last 100 logsPolls
Examples: poll-create.ts | poll-add-option.ts
Create Polls
const pollMessage = await sdk.polls.create({
chatGuid: "iMessage;-;+1234567890",
title: "What should we do?", // Optional
options: ["Option A", "Option B", "Option C"],
});
console.log("Poll GUID:", pollMessage.guid);Example: poll-create.ts
Add Poll Options
await sdk.polls.addOption({
chatGuid: "iMessage;-;+1234567890",
pollMessageGuid: "poll-message-guid",
optionText: "New Option D",
});Example: poll-add-option.ts
Vote on Polls
// Vote on a poll option
await sdk.polls.vote({
chatGuid: "iMessage;-;+1234567890",
pollMessageGuid: "poll-message-guid",
optionIdentifier: "option-uuid", // UUID of the option to vote for
});
// Remove your vote
await sdk.polls.unvote({
chatGuid: "iMessage;-;+1234567890",
pollMessageGuid: "poll-message-guid",
optionIdentifier: "option-uuid",
});Parse Poll Messages
Use the poll-utils helper functions to parse and display poll messages:
import {
isPollMessage,
isPollVote,
parsePollDefinition,
parsePollVotes,
getPollSummary,
getOptionTextById,
} from "@photon-ai/advanced-imessage-kit/lib/poll-utils";
sdk.on("new-message", (message) => {
if (isPollMessage(message)) {
if (isPollVote(message)) {
// Parse vote data
const voteData = parsePollVotes(message);
console.log("Votes:", voteData?.votes);
// Get option text for each vote
voteData?.votes.forEach((vote) => {
const optionText = getOptionTextById(vote.voteOptionIdentifier);
console.log(`${vote.participantHandle} voted for "${optionText}"`);
});
} else {
// Parse poll definition
const pollData = parsePollDefinition(message);
console.log("Poll title:", pollData?.title);
console.log("Options:", pollData?.options);
}
// Or get a formatted summary
console.log(getPollSummary(message));
}
});Note: Poll definitions are automatically cached when received. When a vote arrives, the SDK looks up the corresponding option text from the cache. If you receive a vote for a poll that was created before the SDK started, the option text won't be available and will show the UUID instead.
iCloud (Work in Progress)
Example: findmy-friends.ts
Find My Friends
// Get friends' locations
const friends = await sdk.icloud.getFindMyFriends();
// Refresh location data
await sdk.icloud.refreshFindMyFriends();Example: findmy-friends.ts
Real-time Events
Examples: listen-simple.ts | listen-advanced.ts | auto-reply-hey.ts
The SDK receives real-time events from the server via Socket.IO.
Connection Events
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
// New message
sdk.on("new-message", (message) => {
console.log("New message:", message.text);
console.log("From:", message.handle?.address);
console.log("From me:", message.isFromMe);
});
// Message status update (delivered, read, etc.)
sdk.on("updated-message", (message) => {
if (message.dateRead) console.log("Message read");
else if (message.dateDelivered) console.log("Message delivered");
});
// Send failed
sdk.on("message-send-error", (data) => {
console.error("Send failed:", data);
});Chat Events
// Chat read status changed
sdk.on("chat-read-status-changed", ({ chatGuid, read }) => {
console.log(`Chat ${chatGuid} marked as ${read ? "read" : "unread"}`);
});Typing Indicators
sdk.on("typing-indicator", ({ display, guid }) => {
console.log(`${guid} ${display ? "is typing" : "stopped typing"}`);
});Group Events
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 Friends Events (WIP)
sdk.on("new-findmy-location", (location) => {
console.log(`${location.handle} location updated:`, location.coordinates);
});Remove Event Listeners
const handler = (message) => console.log(message);
sdk.on("new-message", handler);
// Remove specific listener
sdk.off("new-message", handler);
// Remove all listeners
sdk.removeAllListeners("new-message");Best Practices
Resource Management
// Graceful shutdown
process.on("SIGINT", async () => {
await sdk.close();
process.exit(0);
});Message Deduplication
The SDK includes built-in message deduplication to prevent processing duplicates during network instability:
// Clear processed messages (prevent memory leaks)
sdk.clearProcessedMessages(1000);
// Get processed message count
const count = sdk.getProcessedMessageCount();Error Handling
try {
await sdk.messages.sendMessage({
chatGuid: "invalid-guid",
message: "test",
});
} catch (error) {
if (error.response?.status === 404) {
console.error("Chat not found");
} else {
console.error("Send failed:", error.message);
}
}Auto-create Chats
When sending to a contact you've never messaged before, the SDK automatically creates the chat:
// Works even without existing chat history
await sdk.messages.sendMessage({
chatGuid: "any;-;+1234567890",
message: "Hi, this is John",
});
// SDK detects the chat doesn't exist, creates it, then sendsExamples
Run any example with Bun:
bun run examples/<filename>.tsGetting Started
| File | Description | | --------------------------------------------------------- | -------------------------------------- | | listen-simple.ts | Listen with formatted output | | listen-advanced.ts | Listen with full JSON and startup info | | message-send.ts | Send text messages | | message-attachment.ts | Send attachments | | message-audio.ts | Send audio messages |
Message Operations
| File | Description | | ----------------------------------------------------- | ------------------ | | message-reply.ts | Reply to messages | | message-unsend.ts | Unsend messages | | message-reaction.ts | Send Tapbacks | | message-effects.ts | Message effects | | message-search.ts | Search messages | | message-history.ts | Message history |
Chats & Groups
| File | Description | | ----------------------------------------------------- | ------------------- | | chat-fetch.ts | Get chat list | | chat-group.ts | Manage groups | | message-typing.ts | Typing indicators |
Contacts & Services
| File | Description | | --------------------------------------------------------------- | ----------------------------- | | contact-list.ts | Get contacts | | message-contact-card.ts | Share contact card | | service-check.ts | Check iMessage availability | | message-service-check.ts | Monitor message service types |
Attachments & Media
| File | Description | | --------------------------------------------------------------- | ------------------------- | | attachment-download.ts | Download attachments | | message-sticker.ts | Send standalone stickers | | message-reply-sticker.ts | Send reply stickers |
Polls
| File | Description | | --------------------------------------------------- | ---------------- | | poll-create.ts | Create polls | | poll-add-option.ts | Add poll options |
Server & Advanced
| File | Description | | ------------------------------------------------- | ----------------------- | | server-info.ts | Server info and logs | | message-stats.ts | Message statistics | | findmy-friends.ts | Find My Friends (WIP) | | auto-reply-hey.ts | Auto reply bot |
LLMs
Download llms.txt for language model context:
License
MIT License
