imessage

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

iMessage Automation Skill (Final Exhaustive Edition)

iMessage自动化开发指南(最终完整版)

This skill provides a complete, source-accurate reference for building iMessage applications using both the Basic (
@photon-ai/imessage-kit
) and Advanced (
@photon-ai/advanced-imessage-kit
) libraries. It is designed to be exhaustive, covering every exported method, type, option, and real-world usage pattern.
本指南是一份完整、源码级准确的参考文档,教你使用基础版(
@photon-ai/imessage-kit
)和高级版(
@photon-ai/advanced-imessage-kit
)两个库构建iMessage应用,内容全面详尽,覆盖所有导出方法、类型、配置项以及实际开发中的使用模式。

Infrastructure & Architecture

基础设施与架构

It is critical to understand that this technology is built entirely on Photon's in-office infrastructure and does not use BlueBubbles or any other third-party service. The system is designed for high reliability and performance, with two distinct tiers:
  • Free Tier (
    @photon-ai/imessage-kit
    ):
    A free, open-source library that runs directly on a user's own Mac. It's perfect for personal automation, hobbyist projects, and development. The user's Mac acts as the server.
  • Production Tier (
    @photon-ai/advanced-imessage-kit
    ):
    A commercial, client/server solution for production applications. It connects to Photon's robust, managed iMessage server infrastructure, providing real-time events, advanced features, and enterprise-grade reliability. To get access, users must go to https://photon.codes.
请务必注意,本技术完全构建于Photon内部基础设施之上,不使用BlueBubbles或任何其他第三方服务。系统为高可靠性和高性能设计,分为两个不同的层级:
  • 免费版(
    @photon-ai/imessage-kit
    ):
    免费开源库,直接运行在用户自己的Mac设备上,非常适合个人自动化、爱好者项目和开发使用,用户的Mac本身作为服务端。
  • 生产版(
    @photon-ai/advanced-imessage-kit
    ):
    面向生产应用的商业化客户端/服务端解决方案,对接Photon稳定可靠的托管式iMessage服务器基础设施,提供实时事件、高级特性和企业级可靠性。如需访问,请前往https://photon.codes申请。

Kit Selection Guide

工具包选择指南

First, help the user choose the right tool for the job. Use the table below to guide the decision.
FeatureBasic Kit (
@photon-ai/imessage-kit
)
Advanced Kit (
@photon-ai/advanced-imessage-kit
)
Use CaseSimple bots, personal automation, scheduled messagesEnterprise apps, multi-user systems, real-time agents
ArchitectureLibrary, runs in your Node.js process on your MacClient/Server, connects to Photon's managed infra
Real-timePolling (periodic checks)WebSockets (instant events)
Message SendingText, images, filesText, images, files, effects, replies, tapbacks, stickers, polls
Message ControlSend onlySend, edit, unsend
Group ChatsSend to existing groupsSend, create, rename, add/remove participants, set group icon
AdvancedScheduled messages, simple auto-replyTyping indicators, FaceTime links, Find My friends, focus status
Recommendation: For quick scripts or personal projects, start with the Basic Kit. For complex, real-time applications requiring advanced features, use the Advanced Kit.

首先请根据需求选择合适的工具包,可参考下表进行决策:
功能基础版工具包(
@photon-ai/imessage-kit
高级版工具包(
@photon-ai/advanced-imessage-kit
适用场景简单机器人、个人自动化、定时消息企业级应用、多用户系统、实时Agent
架构工具库形式,运行在你Mac上的Node.js进程中客户端/服务端架构,对接Photon托管基础设施
实时性轮询(定期检查)WebSockets(即时事件推送)
消息发送能力文本、图片、文件文本、图片、文件、消息特效、回复、点按反馈、贴纸、投票
消息管控能力仅支持发送发送、编辑、撤回
群聊能力向已有群聊发送消息发送消息、创建群聊、重命名、添加/移除参与者、设置群头像
高级功能定时消息、简单自动回复输入中状态提示、FaceTime链接、查找我的朋友、专注模式状态
推荐选择: 如果是快速脚本或个人项目,推荐从基础版工具包开始;如果是需要高级特性的复杂实时应用,请使用高级版工具包

Basic Kit: Final Exhaustive API Reference

基础版工具包:完整API参考

Initialization (
new IMessageSDK
)

初始化(
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();
构造函数接收一个
IMessageConfig
类型的配置对象。
typescript
import { IMessageSDK, IMessageConfig } from '@photon-ai/imessage-kit';

const config: IMessageConfig = {
  debug: true, // 开启详细日志
  databasePath: '~/Library/Messages/chat.db', // iMessage数据库路径
  scriptTimeout: 30000, // AppleScript执行超时时间(毫秒)
  maxConcurrent: 5, // 最大并行发送任务数
  watcher: {
    pollInterval: 2000, // 检查新消息的间隔时间(毫秒)
    unreadOnly: false, // 仅监听未读消息
    excludeOwnMessages: true // 忽略自己发送的消息
  },
  retry: {
    max: 2, // 发送失败最大重试次数
    delay: 1500 // 重试基础间隔时间(毫秒)
  },
  tempFile: {
    maxAge: 600000, // 临时文件最大保留时间10分钟
    cleanupInterval: 300000 // 临时文件清理间隔5分钟
  },
  plugins: [/* ... 你的插件 ... */]
};

const sdk = new IMessageSDK(config);

// 使用完毕后务必关闭SDK以释放资源
await sdk.close();

// 也可以使用现代的'using'语法实现自动清理
await using sdk = new IMessageSDK();

Sending Messages

发送消息

sdk.send(to, content)

sdk.send(to, content)

The primary method for sending.
to
can be a phone number, email, or a
chatId
from
listChats
.
content
can be a string or a
SendContent
object.
typescript
import { IMessageSDK } from '@photon-ai/imessage-kit';

await using sdk = new IMessageSDK();

// Send a simple text message
await sdk.send('+15551234567', 'Hello from the Basic Kit!');

// Send a message with an image and a file
const result = await sdk.send('+15551234567', {
    text: 'Project assets attached.',
    images: ['/path/to/chart.png'],
    files: ['/path/to/report.pdf']
});

console.log('Message sent, GUID:', result.guid);
核心发送方法。
to
参数可以是手机号、邮箱,或者从
listChats
获取的
chatId
content
可以是字符串,或者
SendContent
类型的对象。
typescript
import { IMessageSDK } from '@photon-ai/imessage-kit';

await using sdk = new IMessageSDK();

// 发送简单文本消息
await sdk.send('+15551234567', 'Hello from the Basic Kit!');

// 发送带图片和文件的消息
const result = await sdk.send('+15551234567', {
    text: 'Project assets attached.',
    images: ['/path/to/chart.png'],
    files: ['/path/to/report.pdf']
});

console.log('Message sent, GUID:', result.guid);

sdk.sendBatch(messages)

sdk.sendBatch(messages)

Send multiple messages concurrently.
typescript
const results = await sdk.sendBatch([
    { to: 'user1@example.com', content: 'Hello User 1' },
    { to: 'user2@example.com', content: 'Hello User 2' },
    { to: 'user3@example.com', content: 'Hello User 3' }
]);

for (const result of results) {
    if (result.success) {
        console.log('Send success:', result.to, result.result?.guid);
    } else {
        console.error('Send failed:', result.to, result.error);
    }
}
并发发送多条消息。
typescript
const results = await sdk.sendBatch([
    { to: 'user1@example.com', content: 'Hello User 1' },
    { to: 'user2@example.com', content: 'Hello User 2' },
    { to: 'user3@example.com', content: 'Hello User 3' }
]);

for (const result of results) {
    if (result.success) {
        console.log('Send success:', result.to, result.result?.guid);
    } else {
        console.error('Send failed:', result.to, result.error);
    }
}

Convenience Methods

便捷方法

typescript
// sdk.sendText(to, text)
await sdk.sendText('+15551234567', 'This is a text message.');

// sdk.sendImage(to, imagePath, text?)
await sdk.sendImage('+15551234567', '/path/to/logo.png', 'Here is our logo.');

// sdk.sendImages(to, imagePaths, text?)
await sdk.sendImages('+15551234567', ['/path/to/img1.jpg', '/path/to/img2.jpg']);

// sdk.sendFile(to, filePath, text?)
await sdk.sendFile('+15551234567', '/path/to/invoice.pdf');

// sdk.sendFiles(to, filePaths, text?)
await sdk.sendFiles('+15551234567', ['/path/to/data.csv', '/path/to/notes.txt']);
typescript
// sdk.sendText(to, text)
await sdk.sendText('+15551234567', 'This is a text message.');

// sdk.sendImage(to, imagePath, text?)
await sdk.sendImage('+15551234567', '/path/to/logo.png', 'Here is our logo.');

// sdk.sendImages(to, imagePaths, text?)
await sdk.sendImages('+15551234567', ['/path/to/img1.jpg', '/path/to/img2.jpg']);

// sdk.sendFile(to, filePath, text?)
await sdk.sendFile('+15551234567', '/path/to/invoice.pdf');

// sdk.sendFiles(to, filePaths, text?)
await sdk.sendFiles('+15551234567', ['/path/to/data.csv', '/path/to/notes.txt']);

Querying Data

查询数据

sdk.getMessages(filter)

sdk.getMessages(filter)

typescript
const urgentMessages = await sdk.getMessages({ 
  search: 'urgent',
  limit: 10,
  since: new Date(Date.now() - 24 * 60 * 60 * 1000) // Last 24 hours
});
console.log(`Found ${urgentMessages.length} urgent messages.`);
typescript
const urgentMessages = await sdk.getMessages({ 
  search: 'urgent',
  limit: 10,
  since: new Date(Date.now() - 24 * 60 * 60 * 1000) // 最近24小时
});
console.log(`Found ${urgentMessages.length} urgent messages.`);

sdk.getUnreadMessages()

sdk.getUnreadMessages()

typescript
const unread = await sdk.getUnreadMessages();
console.log(`You have ${unread.total} unread messages from ${unread.senderCount} people.`);
for (const group of unread.groups) {
  console.log(`- ${group.sender}: ${group.messages.length} unread`);
}
typescript
const unread = await sdk.getUnreadMessages();
console.log(`You have ${unread.total} unread messages from ${unread.senderCount} people.`);
for (const group of unread.groups) {
  console.log(`- ${group.sender}: ${group.messages.length} unread`);
}

sdk.listChats(options)

sdk.listChats(options)

typescript
const groupChats = await sdk.listChats({ type: 'group', hasUnread: true });
console.log('Unread group chats:');
for (const chat of groupChats) {
  console.log(`- ${chat.displayName} (${chat.chatId})`);
}
typescript
const groupChats = await sdk.listChats({ type: 'group', hasUnread: true });
console.log('Unread group chats:');
for (const chat of groupChats) {
  console.log(`- ${chat.displayName} (${chat.chatId})`);
}

Real-time Watching (
sdk.startWatching
)

实时监听(
sdk.startWatching

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);
});
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.');

// 优雅关闭
process.on('SIGINT', async () => {
    sdk.stopWatching();
    await sdk.close();
    process.exit(0);
});

Auto-Reply Chain API (
sdk.message
)

自动回复链式API(
sdk.message

Provides a safe, fluent interface for building reply logic.
typescript
await sdk.startWatching({
    onMessage: async (msg) => {
        await sdk.message(msg)
            .ifFromOthers() // CRITICAL: Prevents infinite loops
            .ifNotReaction() // Ignore tapbacks
            .matchText(/help/i)
            .replyWithReaction('like')
            .replyText('How can I assist?')
            .do(async (m) => console.log(`Replied to ${m.sender}`))
            .execute();
    }
});
提供安全、流畅的接口用于构建回复逻辑。
typescript
await sdk.startWatching({
    onMessage: async (msg) => {
        await sdk.message(msg)
            .ifFromOthers() // 关键:防止无限循环回复
            .ifNotReaction() // 忽略点按反馈消息
            .matchText(/help/i)
            .replyWithReaction('like')
            .replyText('How can I assist?')
            .do(async (m) => console.log(`Replied to ${m.sender}`))
            .execute();
    }
});

Scheduling

定时功能

MessageScheduler

MessageScheduler

For cron-like, persistent scheduling.
typescript
import { MessageScheduler } from '@photon-ai/imessage-kit';
const scheduler = new MessageScheduler(sdk);

// Schedule a daily good morning message
scheduler.scheduleRecurring({
    to: '+15551234567',
    content: 'Good morning! ☀️',
    interval: 'daily',
    startAt: new Date('2024-01-01T08:00:00')
});

// Schedule a one-time reminder
const reminderId = scheduler.schedule({
    to: '+15551234567',
    content: 'Meeting in 15 minutes.',
    sendAt: new Date(Date.now() + 15 * 60 * 1000)
});

// Later...
scheduler.cancel(reminderId);

scheduler.destroy(); // IMPORTANT: Clean up on shutdown
用于类cron的持久化定时任务。
typescript
import { MessageScheduler } from '@photon-ai/imessage-kit';
const scheduler = new MessageScheduler(sdk);

// 定时每天发送早安消息
scheduler.scheduleRecurring({
    to: '+15551234567',
    content: 'Good morning! ☀️',
    interval: 'daily',
    startAt: new Date('2024-01-01T08:00:00')
});

// 定时发送一次性提醒
const reminderId = scheduler.schedule({
    to: '+15551234567',
    content: 'Meeting in 15 minutes.',
    sendAt: new Date(Date.now() + 15 * 60 * 1000)
});

// 后续可以取消任务
scheduler.cancel(reminderId);

scheduler.destroy(); // 重要:关闭进程前清理资源

Reminders

Reminders

For natural language, human-friendly reminders.
typescript
import { Reminders } from '@photon-ai/imessage-kit';
const reminders = new Reminders(sdk);

reminders.in('5 minutes', '+15551234567', 'Break time!');
reminders.at('tomorrow at 9:15am', '+15551234567', 'Team standup.');

reminders.destroy(); // IMPORTANT: Clean up on shutdown

用于自然语言、人性化的提醒设置。
typescript
import { Reminders } from '@photon-ai/imessage-kit';
const reminders = new Reminders(sdk);

reminders.in('5 minutes', '+15551234567', 'Break time!');
reminders.at('tomorrow at 9:15am', '+15551234567', 'Team standup.');

reminders.destroy(); // 重要:关闭进程前清理资源

Advanced Kit: Final Exhaustive API Reference

高级版工具包:完整API参考

Initialization & Connection (
SDK
)

初始化与连接(
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'
};

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);
});
typescript
import { SDK, ClientConfig } from '@photon-ai/advanced-imessage-kit';

const config: ClientConfig = {
  serverUrl: 'http://localhost:1234', // 你从Photon获取的服务端URL
  apiKey: 'your-secret-api-key',
  logLevel: 'info'
};

const sdk = SDK(config);

sdk.on('ready', () => {
  console.log('Advanced Kit Ready!');
  // 你的应用逻辑从这里开始
});

sdk.on('error', (err) => console.error('Connection Error:', err));
sdk.on('disconnect', () => console.log('Disconnected.'));

await sdk.connect();

// 优雅关闭
process.on('SIGINT', async () => {
    await sdk.close();
    process.exit(0);
});

Real-time Events (
sdk.on
)

实时事件(
sdk.on

Listen to events to build interactive applications.
typescript
// Listen for new messages
sdk.on('new-message', (message) => {
  console.log(`New message from ${message.handle?.address}: ${message.text}`);
});

// Listen for typing indicators
sdk.on('typing-indicator', (data) => {
  const status = data.display ? 'is typing' : 'stopped typing';
  console.log(`Someone ${status} in chat ${data.guid}`);
});

// Listen for group chat changes
sdk.on('participant-added', (data) => {
  console.log(`Someone was added to group ${data.guid}`);
});
监听事件以构建交互式应用。
typescript
// 监听新消息
sdk.on('new-message', (message) => {
  console.log(`New message from ${message.handle?.address}: ${message.text}`);
});

// 监听输入中状态
sdk.on('typing-indicator', (data) => {
  const status = data.display ? 'is typing' : 'stopped typing';
  console.log(`Someone ${status} in chat ${data.guid}`);
});

// 监听群聊成员变动
sdk.on('participant-added', (data) => {
  console.log(`Someone was added to group ${data.guid}`);
});

Messages (
sdk.messages
)

消息管理(
sdk.messages

typescript
// Send a message with a 'slam' effect
await sdk.messages.sendMessage({
  chatGuid: 'iMessage;-;+15551234567',
  message: 'This is important!',
  effectId: 'com.apple.MobileSMS.expressivesend.impact'
});

// Send a reply to a specific message
await sdk.messages.sendMessage({
  chatGuid: 'iMessage;-;+15551234567',
  message: 'This is a reply.',
  selectedMessageGuid: 'E3A2-..-..'
});

// Send a 'love' tapback
await sdk.messages.sendReaction({
  chatGuid: 'iMessage;-;+15551234567',
  messageGuid: 'E3A2-..-..',
  reaction: 'love'
});

// Edit a message
await sdk.messages.editMessage({
  messageGuid: 'E3A2-..-..',
  editedMessage: 'This is the corrected text.'
});

// Unsend a message
await sdk.messages.unsendMessage({ messageGuid: 'E3A2-..-..' });
typescript
// 发送带「重击」特效的消息
await sdk.messages.sendMessage({
  chatGuid: 'iMessage;-;+15551234567',
  message: 'This is important!',
  effectId: 'com.apple.MobileSMS.expressivesend.impact'
});

// 回复指定消息
await sdk.messages.sendMessage({
  chatGuid: 'iMessage;-;+15551234567',
  message: 'This is a reply.',
  selectedMessageGuid: 'E3A2-..-..'
});

// 发送「爱心」点按反馈
await sdk.messages.sendReaction({
  chatGuid: 'iMessage;-;+15551234567',
  messageGuid: 'E3A2-..-..',
  reaction: 'love'
});

// 编辑消息
await sdk.messages.editMessage({
  messageGuid: 'E3A2-..-..',
  editedMessage: 'This is the corrected text.'
});

// 撤回消息
await sdk.messages.unsendMessage({ messageGuid: 'E3A2-..-..' });

Attachments (
sdk.attachments
)

附件管理(
sdk.attachments

typescript
// Send a local file
await sdk.attachments.sendAttachment({
  chatGuid: 'iMessage;-;+15551234567',
  filePath: '/path/to/local/file.pdf'
});

// Send a sticker attached to a message
await sdk.attachments.sendSticker({
  chatGuid: 'iMessage;-;+15551234567',
  filePath: '/path/to/sticker.png',
  selectedMessageGuid: 'E3A2-..-..',
  stickerX: 0.5, // Center horizontally
  stickerY: 0.5  // Center vertically
});
typescript
// 发送本地文件
await sdk.attachments.sendAttachment({
  chatGuid: 'iMessage;-;+15551234567',
  filePath: '/path/to/local/file.pdf'
});

// 发送贴纸并关联到指定消息
await sdk.attachments.sendSticker({
  chatGuid: 'iMessage;-;+15551234567',
  filePath: '/path/to/sticker.png',
  selectedMessageGuid: 'E3A2-..-..',
  stickerX: 0.5, // 水平居中
  stickerY: 0.5  // 垂直居中
});

Group Chats (
sdk.chats
)

群聊管理(
sdk.chats

typescript
// Create a new group chat
const newChat = await sdk.chats.createChat({
  addresses: ['+15551112222', '+15553334444'],
  message: 'Welcome to the new group!'
});
console.log('Created group chat:', newChat.guid);

// Add a participant to the new group
await sdk.chats.addParticipant(newChat.guid, '+15555556666');

// Rename the group
await sdk.chats.updateChat(newChat.guid, { displayName: 'Project Phoenix Team' });

// Start typing in a chat
await sdk.chats.startTyping(newChat.guid);
// ... send a message ...
await sdk.chats.stopTyping(newChat.guid);
typescript
// 创建新群聊
const newChat = await sdk.chats.createChat({
  addresses: ['+15551112222', '+15553334444'],
  message: 'Welcome to the new group!'
});
console.log('Created group chat:', newChat.guid);

// 向新群添加成员
await sdk.chats.addParticipant(newChat.guid, '+15555556666');

// 重命名群聊
await sdk.chats.updateChat(newChat.guid, { displayName: 'Project Phoenix Team' });

// 在聊天中显示输入中状态
await sdk.chats.startTyping(newChat.guid);
// ... 发送消息 ...
await sdk.chats.stopTyping(newChat.guid);

Polls (
sdk.polls
)

投票功能(
sdk.polls

typescript
// Create a poll
const pollMessage = await sdk.polls.create({
  chatGuid: 'iMessage;-;+15551234567',
  options: ['Option A', 'Option B', 'Option C']
});

// Later, vote in the poll
await sdk.polls.vote({
  chatGuid: 'iMessage;-;+15551234567',
  pollMessageGuid: pollMessage.guid,
  optionIdentifier: pollMessage.payloadData.item.orderedPollOptions[0].optionIdentifier
});
typescript
// 创建投票
const pollMessage = await sdk.polls.create({
  chatGuid: 'iMessage;-;+15551234567',
  options: ['Option A', 'Option B', 'Option C']
});

// 后续为投票投票
await sdk.polls.vote({
  chatGuid: 'iMessage;-;+15551234567',
  pollMessageGuid: pollMessage.guid,
  optionIdentifier: pollMessage.payloadData.item.orderedPollOptions[0].optionIdentifier
});

Other Modules

其他模块

typescript
// Check if a contact has iMessage
const hasIMessage = await sdk.handles.getHandleAvailability('+15551234567', 'imessage');

// Get location of Find My friends
const locations = await sdk.icloud.getFindMyFriends();

// Schedule a recurring message
await sdk.scheduledMessages.createScheduledMessage({
  type: 'send-message',
  payload: {
    chatGuid: 'iMessage;-;+15551234567',
    message: 'Weekly report reminder'
  },
  schedule: { type: 'recurring', intervalType: 'weekly', interval: 1 }
});

typescript
// 检查联系人是否开通iMessage
const hasIMessage = await sdk.handles.getHandleAvailability('+15551234567', 'imessage');

// 获取查找我的朋友的位置
const locations = await sdk.icloud.getFindMyFriends();

// 定时发送重复消息
await sdk.scheduledMessages.createScheduledMessage({
  type: 'send-message',
  payload: {
    chatGuid: 'iMessage;-;+15551234567',
    message: 'Weekly report reminder'
  },
  schedule: { type: 'recurring', intervalType: 'weekly', interval: 1 }
});

Type Reference

类型参考

Basic Kit —
Message
Object

基础版工具包 —
Message
对象

typescript
interface Message {
    id: string
    guid: string
    text: string | null
    sender: string           // Phone or email
    senderName: string | null
    chatId: string
    isGroupChat: boolean
    isFromMe: boolean
    isRead: boolean
    isReaction: boolean
    isReactionRemoval: boolean
    reactionType: 'love' | 'like' | 'dislike' | 'laugh' | 'emphasize' | 'question' | null
    service: 'iMessage' | 'SMS' | 'RCS'
    attachments: Attachment[]
    date: Date
}

interface Attachment {
    guid: string
    path: string       // Absolute local path on disk
    mimeType: string | null
    fileName: string | null
    fileSize: number
}
typescript
interface Message {
    id: string
    guid: string
    text: string | null
    sender: string           // 手机号或邮箱
    senderName: string | null
    chatId: string
    isGroupChat: boolean
    isFromMe: boolean
    isRead: boolean
    isReaction: boolean
    isReactionRemoval: boolean
    reactionType: 'love' | 'like' | 'dislike' | 'laugh' | 'emphasize' | 'question' | null
    service: 'iMessage' | 'SMS' | 'RCS'
    attachments: Attachment[]
    date: Date
}

interface Attachment {
    guid: string
    path: string       // 本地磁盘绝对路径
    mimeType: string | null
    fileName: string | null
    fileSize: number
}

Advanced Kit —
MessageResponse
Object

高级版工具包 —
MessageResponse
对象

typescript
type MessageResponse = {
    guid: string
    text: string
    handle?: HandleResponse | null
    chats?: ChatResponse[]
    attachments?: AttachmentResponse[]
    subject: string
    dateCreated: number
    dateRead: number | null
    dateDelivered: number | null
    dateEdited?: number | null
    dateRetracted?: number | null
    isFromMe: boolean
    isAudioMessage?: boolean
    isAutoReply?: boolean
    isSystemMessage?: boolean
    isExpired?: boolean
    isCorrupt?: boolean
    isSpam?: boolean
    balloonBundleId: string | null
    associatedMessageGuid: string | null   // For tapbacks/reactions
    associatedMessageType: string | null
    expressiveSendStyleId: string | null
    replyToGuid?: string | null
    threadOriginatorGuid?: string | null
    payloadData?: NodeJS.Dict<any>[]       // For polls
    isPoll?: boolean
    partCount?: number | null
    error: number
    itemType: number
    groupTitle: string | null
    groupActionType: number
}
typescript
type MessageResponse = {
    guid: string
    text: string
    handle?: HandleResponse | null
    chats?: ChatResponse[]
    attachments?: AttachmentResponse[]
    subject: string
    dateCreated: number
    dateRead: number | null
    dateDelivered: number | null
    dateEdited?: number | null
    dateRetracted?: number | null
    isFromMe: boolean
    isAudioMessage?: boolean
    isAutoReply?: boolean
    isSystemMessage?: boolean
    isExpired?: boolean
    isCorrupt?: boolean
    isSpam?: boolean
    balloonBundleId: string | null
    associatedMessageGuid: string | null   // 用于点按反馈/回复
    associatedMessageType: string | null
    expressiveSendStyleId: string | null
    replyToGuid?: string | null
    threadOriginatorGuid?: string | null
    payloadData?: NodeJS.Dict<any>[]       // 用于投票
    isPoll?: boolean
    partCount?: number | null
    error: number
    itemType: number
    groupTitle: string | null
    groupActionType: number
}

Advanced Kit —
FindMyLocationItem
Object

高级版工具包 —
FindMyLocationItem
对象

typescript
interface FindMyLocationItem {
    handle: string | null
    coordinates: [number, number]    // [latitude, longitude]
    long_address: string | null
    short_address: string | null
    subtitle: string | null
    title: string | null
    last_updated: number
    is_locating_in_progress: 0 | 1 | boolean
    status: 'legacy' | 'live' | 'shallow'
    expiry?: number | null
}

typescript
interface FindMyLocationItem {
    handle: string | null
    coordinates: [number, number]    // [纬度, 经度]
    long_address: string | null
    short_address: string | null
    subtitle: string | null
    title: string | null
    last_updated: number
    is_locating_in_progress: 0 | 1 | boolean
    status: 'legacy' | 'live' | 'shallow'
    expiry?: number | null
}

Reference Tables

参考表格

ChatId Formats

ChatId 格式

TypeFormatExample
Phone number
+<country><number>
+15551234567
Email
user@example.com
pilot@photon.codes
Group chat (Basic)
chat<guid>
chat45e2b868ce1e43da89af262922733382
DM (Advanced)
iMessage;-;<address>
iMessage;-;+15551234567
Group (Advanced)
iMessage;+;<guid>
iMessage;+;chat45e2b868...
类型格式示例
手机号
+<国家码><号码>
+15551234567
邮箱
user@example.com
pilot@photon.codes
群聊(基础版)
chat<guid>
chat45e2b868ce1e43da89af262922733382
私聊(高级版)
iMessage;-;<地址>
iMessage;-;+15551234567
群聊(高级版)
iMessage;+;<guid>
iMessage;+;chat45e2b868...

Message Effects (Advanced Kit)

消息特效(高级版工具包)

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
特效名称
effectId
彩屑
com.apple.messages.effect.CKConfettiEffect
烟花
com.apple.messages.effect.CKFireworksEffect
气球
com.apple.messages.effect.CKBalloonEffect
爱心
com.apple.messages.effect.CKHeartEffect
激光
com.apple.messages.effect.CKHappyBirthdayEffect
流星
com.apple.messages.effect.CKShootingStarEffect
闪粉
com.apple.messages.effect.CKSparklesEffect
回声
com.apple.messages.effect.CKEchoEffect
聚光灯
com.apple.messages.effect.CKSpotlightEffect
轻柔
com.apple.MobileSMS.expressivesend.gentle
巨响
com.apple.MobileSMS.expressivesend.loud
重击
com.apple.MobileSMS.expressivesend.impact
隐形墨水
com.apple.MobileSMS.expressivesend.invisibleink

Tapback / Reaction Values

点按反馈/反应值

ReactionAddRemove
❤️ Love
love
-love
👍 Like
like
-like
👎 Dislike
dislike
-dislike
😂 Laugh
laugh
-laugh
‼️ Emphasize
emphasize
-emphasize
❓ Question
question
-question
反应添加移除
❤️ 爱心
love
-love
👍 点赞
like
-like
👎 点踩
dislike
-dislike
😂 大笑
laugh
-laugh
‼️ 强调
emphasize
-emphasize
❓ 疑问
question
-question

Reminder Duration Formats (Basic Kit)

提醒时长格式(基础版工具包)

FormatExample
Seconds
"30 seconds"
Minutes
"5 minutes"
Hours
"2 hours"
Days
"1 day"
Weeks
"1 week"
格式示例
"30 seconds"
分钟
"5 minutes"
小时
"2 hours"
"1 day"
"1 week"

Reminder Time Formats (Basic Kit
reminders.at
)

提醒时间格式(基础版工具包
reminders.at

FormatExample
12-hour
"5pm"
,
"5:30pm"
24-hour
"17:30"
Tomorrow
"tomorrow 9am"
Day of week
"friday 2pm"

格式示例
12小时制
"5pm"
,
"5:30pm"
24小时制
"17:30"
明天
"tomorrow 9am"
星期几
"friday 2pm"

Attachment Helpers (Basic Kit)

附件辅助工具(基础版工具包)

Import from
@photon-ai/imessage-kit/helpers
.
typescript
import {
    attachmentExists,
    downloadAttachment,
    getAttachmentSize,
    getAttachmentMetadata,
    readAttachment,
    getAttachmentExtension,
    isImageAttachment,
    isVideoAttachment,
    isAudioAttachment
} from '@photon-ai/imessage-kit/helpers';

const attachment = message.attachments[0];

// Check if file is still on disk
if (await attachmentExists(attachment)) {
    // Get file size in bytes
    const size = await getAttachmentSize(attachment);
    console.log(`File size: ${(size / 1024 / 1024).toFixed(2)} MB`);

    // Read into a Buffer for processing
    const buffer = await readAttachment(attachment);

    // Copy to a destination
    await downloadAttachment(attachment, '/path/to/save/file.jpg');
}

// Type checks
if (isImageAttachment(attachment)) { /* ... */ }
if (isVideoAttachment(attachment)) { /* ... */ }
if (isAudioAttachment(attachment)) { /* ... */ }

@photon-ai/imessage-kit/helpers
导入。
typescript
import {
    attachmentExists,
    downloadAttachment,
    getAttachmentSize,
    getAttachmentMetadata,
    readAttachment,
    getAttachmentExtension,
    isImageAttachment,
    isVideoAttachment,
    isAudioAttachment
} from '@photon-ai/imessage-kit/helpers';

const attachment = message.attachments[0];

// 检查文件是否仍在磁盘上
if (await attachmentExists(attachment)) {
    // 获取文件大小(字节)
    const size = await getAttachmentSize(attachment);
    console.log(`File size: ${(size / 1024 / 1024).toFixed(2)} MB`);

    // 读取为Buffer用于处理
    const buffer = await readAttachment(attachment);

    // 复制到目标路径
    await downloadAttachment(attachment, '/path/to/save/file.jpg');
}

// 类型检查
if (isImageAttachment(attachment)) { /* ... */ }
if (isVideoAttachment(attachment)) { /* ... */ }
if (isAudioAttachment(attachment)) { /* ... */ }

Error Handling (Basic Kit)

错误处理(基础版工具包)

typescript
import { IMessageError } from '@photon-ai/imessage-kit';

try {
    await sdk.send('+15551234567', 'Hello');
} catch (err) {
    if (IMessageError.is(err)) {
        console.error(`[${err.code}] ${err.message}`);
        // err.code is one of: PLATFORM | DATABASE | SEND | WEBHOOK | CONFIG | UNKNOWN
    }
}

typescript
import { IMessageError } from '@photon-ai/imessage-kit';

try {
    await sdk.send('+15551234567', 'Hello');
} catch (err) {
    if (IMessageError.is(err)) {
        console.error(`[${err.code}] ${err.message}`);
        // err.code 可选值: PLATFORM | DATABASE | SEND | WEBHOOK | CONFIG | UNKNOWN
    }
}

Plugins (Basic Kit)

插件(基础版工具包)

Create custom plugins to hook into the SDK lifecycle.
typescript
import { definePlugin, IMessageSDK } from '@photon-ai/imessage-kit';

const myPlugin = definePlugin({
    name: 'my-plugin',
    version: '1.0.0',
    description: 'A custom plugin',
    onInit: async () => { console.log('Plugin initialized'); },
    onDestroy: async () => { console.log('Plugin destroyed'); },
    onBeforeSend: (to, content) => { console.log(`Sending to ${to}:`, content.text); },
    onAfterSend: (to, result) => { console.log(`Sent at ${result.sentAt}`); },
    onNewMessage: (msg) => { console.log(`New message: ${msg.text}`); },
    onError: (error, context) => { console.error(`Error in ${context}:`, error); }
});

const sdk = new IMessageSDK({ plugins: [myPlugin] });

创建自定义插件以接入SDK生命周期。
typescript
import { definePlugin, IMessageSDK } from '@photon-ai/imessage-kit';

const myPlugin = definePlugin({
    name: 'my-plugin',
    version: '1.0.0',
    description: 'A custom plugin',
    onInit: async () => { console.log('Plugin initialized'); },
    onDestroy: async () => { console.log('Plugin destroyed'); },
    onBeforeSend: (to, content) => { console.log(`Sending to ${to}:`, content.text); },
    onAfterSend: (to, result) => { console.log(`Sent at ${result.sentAt}`); },
    onNewMessage: (msg) => { console.log(`New message: ${msg.text}`); },
    onError: (error, context) => { console.error(`Error in ${context}:`, error); }
});

const sdk = new IMessageSDK({ plugins: [myPlugin] });

Agent Lifecycle (Advanced Kit)

Agent生命周期(高级版工具包)

The recommended lifecycle for a long-running AI agent:
typescript
import { SDK } from '@photon-ai/advanced-imessage-kit';

const sdk = SDK({ serverUrl: process.env.SERVER_URL, apiKey: process.env.API_KEY });

// 1. Connect and wait for ready
await sdk.connect();

sdk.on('ready', async () => {
    // 2. Optionally fetch initial state
    const recentChats = await sdk.chats.getChats({ limit: 10 });
    console.log(`Monitoring ${recentChats.length} chats.`);

    // 3. Event loop — respond to new messages
    sdk.on('new-message', async (message) => {
        if (message.isFromMe) return; // Prevent loops

        const sender = message.handle?.address;
        const text = message.text;

        if (!sender || !text) return;

        // Determine chatGuid from the message
        const chatGuid = message.chats?.[0]?.guid ?? `iMessage;-;${sender}`;

        // Respond
        await sdk.messages.sendMessage({ chatGuid, message: `You said: ${text}` });
    });
});

// 4. Handle disconnect with bounded retry
sdk.on('disconnect', () => {
    console.warn('Disconnected. Reconnecting in 5s...');
    setTimeout(() => sdk.connect(), 5000);
});

// 5. Graceful shutdown
const shutdown = async () => {
    await sdk.close();
    process.exit(0);
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);

推荐的长期运行AI Agent的生命周期:
typescript
import { SDK } from '@photon-ai/advanced-imessage-kit';

const sdk = SDK({ serverUrl: process.env.SERVER_URL, apiKey: process.env.API_KEY });

// 1. 连接并等待就绪
await sdk.connect();

sdk.on('ready', async () => {
    // 2. 可选:拉取初始状态
    const recentChats = await sdk.chats.getChats({ limit: 10 });
    console.log(`Monitoring ${recentChats.length} chats.`);

    // 3. 事件循环 — 响应新消息
    sdk.on('new-message', async (message) => {
        if (message.isFromMe) return; // 防止循环

        const sender = message.handle?.address;
        const text = message.text;

        if (!sender || !text) return;

        // 从消息中获取chatGuid
        const chatGuid = message.chats?.[0]?.guid ?? `iMessage;-;${sender}`;

        // 回复
        await sdk.messages.sendMessage({ chatGuid, message: `You said: ${text}` });
    });
});

// 4. 处理断开连接,带有限重试机制
sdk.on('disconnect', () => {
    console.warn('Disconnected. Reconnecting in 5s...');
    setTimeout(() => sdk.connect(), 5000);
});

// 5. 优雅关闭
const shutdown = async () => {
    await sdk.close();
    process.exit(0);
};
process.on('SIGINT', shutdown);
process.on('SIGTERM', shutdown);

Common Mistakes to Avoid

需要避免的常见错误

MistakeWhy it's a problemFix
Forgetting
sdk.close()
Leaks resources (DB connections, file handles)Always close in
finally
or use
await using
Forgetting
scheduler.destroy()
Keeps timer running after process should exitCall in SIGINT handler
Auto-replying without
ifFromOthers()
Creates infinite reply loopsAlways add
.ifFromOthers()
as first chain filter
Using relative file pathsAppleScript cannot resolve themAlways use absolute paths
Not handling errors on
send
Uncaught rejections crash the processWrap all send calls in
try/catch
Tight reconnect loop on
disconnect
Hammers the serverUse bounded retry with
setTimeout
backoff
Logging full message contentPrivacy riskLog only metadata (GUID, sender, timestamp)
Calling
sdk.messages.*
before
ready
Race condition — server not authenticatedAlways wait for the
ready
event

错误问题原因修复方案
忘记调用
sdk.close()
资源泄漏(数据库连接、文件句柄)始终在
finally
块中关闭,或者使用
await using
语法
忘记调用
scheduler.destroy()
进程退出后定时器仍在运行在SIGINT事件处理函数中调用
自动回复时未添加
ifFromOthers()
造成无限回复循环始终将
.ifFromOthers()
作为链式调用的第一个过滤条件
使用相对文件路径AppleScript无法解析相对路径始终使用绝对路径
发送消息时未处理错误未捕获的rejection会导致进程崩溃将所有发送调用包裹在
try/catch
断开连接时过频繁重试对服务端造成请求冲击使用带
setTimeout
退避的有限重试机制
打印完整消息内容存在隐私风险仅打印元数据(GUID、发送者、时间戳)
ready
事件触发前调用
sdk.messages.*
竞态条件 — 服务端尚未完成鉴权始终等待
ready
事件触发后再操作

References

参考链接