imessage
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseiMessage Automation Skill (Final Exhaustive Edition)
iMessage自动化开发指南(最终完整版)
This skill provides a complete, source-accurate reference for building iMessage applications using both the Basic () and Advanced () 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应用,内容全面详尽,覆盖所有导出方法、类型、配置项以及实际开发中的使用模式。
@photon-ai/imessage-kit@photon-ai/advanced-imessage-kitInfrastructure & 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 (): 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.
@photon-ai/imessage-kit -
Production Tier (): 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-ai/advanced-imessage-kit
请务必注意,本技术完全构建于Photon内部基础设施之上,不使用BlueBubbles或任何其他第三方服务。系统为高可靠性和高性能设计,分为两个不同的层级:
-
免费版(): 免费开源库,直接运行在用户自己的Mac设备上,非常适合个人自动化、爱好者项目和开发使用,用户的Mac本身作为服务端。
@photon-ai/imessage-kit -
生产版(): 面向生产应用的商业化客户端/服务端解决方案,对接Photon稳定可靠的托管式iMessage服务器基础设施,提供实时事件、高级特性和企业级可靠性。如需访问,请前往https://photon.codes申请。
@photon-ai/advanced-imessage-kit
Kit Selection Guide
工具包选择指南
First, help the user choose the right tool for the job. Use the table below to guide the decision.
| Feature | Basic Kit ( | Advanced Kit ( |
|---|---|---|
| Use Case | Simple bots, personal automation, scheduled messages | Enterprise apps, multi-user systems, real-time agents |
| Architecture | Library, runs in your Node.js process on your Mac | Client/Server, connects to Photon's managed infra |
| Real-time | Polling (periodic checks) | WebSockets (instant events) |
| Message Sending | Text, images, files | Text, images, files, effects, replies, tapbacks, stickers, polls |
| Message Control | Send only | Send, edit, unsend |
| Group Chats | Send to existing groups | Send, create, rename, add/remove participants, set group icon |
| Advanced | Scheduled messages, simple auto-reply | Typing 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.
首先请根据需求选择合适的工具包,可参考下表进行决策:
| 功能 | 基础版工具包( | 高级版工具包( |
|---|---|---|
| 适用场景 | 简单机器人、个人自动化、定时消息 | 企业级应用、多用户系统、实时Agent |
| 架构 | 工具库形式,运行在你Mac上的Node.js进程中 | 客户端/服务端架构,对接Photon托管基础设施 |
| 实时性 | 轮询(定期检查) | WebSockets(即时事件推送) |
| 消息发送能力 | 文本、图片、文件 | 文本、图片、文件、消息特效、回复、点按反馈、贴纸、投票 |
| 消息管控能力 | 仅支持发送 | 发送、编辑、撤回 |
| 群聊能力 | 向已有群聊发送消息 | 发送消息、创建群聊、重命名、添加/移除参与者、设置群头像 |
| 高级功能 | 定时消息、简单自动回复 | 输入中状态提示、FaceTime链接、查找我的朋友、专注模式状态 |
推荐选择: 如果是快速脚本或个人项目,推荐从基础版工具包开始;如果是需要高级特性的复杂实时应用,请使用高级版工具包。
Basic Kit: Final Exhaustive API Reference
基础版工具包:完整API参考
Initialization (new IMessageSDK
)
new IMessageSDK初始化(new IMessageSDK
)
new IMessageSDKThe constructor accepts a single object.
IMessageConfigtypescript
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();构造函数接收一个类型的配置对象。
IMessageConfigtypescript
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)sdk.send(to, content)
sdk.send(to, content)The primary method for sending. can be a phone number, email, or a from . can be a string or a object.
tochatIdlistChatscontentSendContenttypescript
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);核心发送方法。参数可以是手机号、邮箱,或者从获取的;可以是字符串,或者类型的对象。
tolistChatschatIdcontentSendContenttypescript
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)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)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()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)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实时监听(sdk.startWatching
)
sdk.startWatchingtypescript
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
)
sdk.message自动回复链式API(sdk.message
)
sdk.messageProvides 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
MessageSchedulerMessageScheduler
MessageSchedulerFor 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
RemindersReminders
RemindersFor 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初始化与连接(SDK
)
SDKtypescript
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实时事件(sdk.on
)
sdk.onListen 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消息管理(sdk.messages
)
sdk.messagestypescript
// 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附件管理(sdk.attachments
)
sdk.attachmentstypescript
// 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群聊管理(sdk.chats
)
sdk.chatstypescript
// 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投票功能(sdk.polls
)
sdk.pollstypescript
// 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基础版工具包 — Message
对象
Messagetypescript
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高级版工具包 — MessageResponse
对象
MessageResponsetypescript
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高级版工具包 — FindMyLocationItem
对象
FindMyLocationItemtypescript
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 格式
| Type | Format | Example |
|---|---|---|
| Phone number | | |
| | |
| Group chat (Basic) | | |
| DM (Advanced) | | |
| Group (Advanced) | | |
| 类型 | 格式 | 示例 |
|---|---|---|
| 手机号 | | |
| 邮箱 | | |
| 群聊(基础版) | | |
| 私聊(高级版) | | |
| 群聊(高级版) | | |
Message Effects (Advanced Kit)
消息特效(高级版工具包)
| Effect | |
|---|---|
| Confetti | |
| Fireworks | |
| Balloons | |
| Hearts | |
| Lasers | |
| Shooting Star | |
| Sparkles | |
| Echo | |
| Spotlight | |
| Gentle | |
| Loud | |
| Slam | |
| Invisible Ink | |
| 特效名称 | |
|---|---|
| 彩屑 | |
| 烟花 | |
| 气球 | |
| 爱心 | |
| 激光 | |
| 流星 | |
| 闪粉 | |
| 回声 | |
| 聚光灯 | |
| 轻柔 | |
| 巨响 | |
| 重击 | |
| 隐形墨水 | |
Tapback / Reaction Values
点按反馈/反应值
| Reaction | Add | Remove |
|---|---|---|
| ❤️ Love | | |
| 👍 Like | | |
| 👎 Dislike | | |
| 😂 Laugh | | |
| ‼️ Emphasize | | |
| ❓ Question | | |
| 反应 | 添加 | 移除 |
|---|---|---|
| ❤️ 爱心 | | |
| 👍 点赞 | | |
| 👎 点踩 | | |
| 😂 大笑 | | |
| ‼️ 强调 | | |
| ❓ 疑问 | | |
Reminder Duration Formats (Basic Kit)
提醒时长格式(基础版工具包)
| Format | Example |
|---|---|
| Seconds | |
| Minutes | |
| Hours | |
| Days | |
| Weeks | |
| 格式 | 示例 |
|---|---|
| 秒 | |
| 分钟 | |
| 小时 | |
| 天 | |
| 周 | |
Reminder Time Formats (Basic Kit reminders.at
)
reminders.at提醒时间格式(基础版工具包 reminders.at
)
reminders.at| Format | Example |
|---|---|
| 12-hour | |
| 24-hour | |
| Tomorrow | |
| Day of week | |
| 格式 | 示例 |
|---|---|
| 12小时制 | |
| 24小时制 | |
| 明天 | |
| 星期几 | |
Attachment Helpers (Basic Kit)
附件辅助工具(基础版工具包)
Import from .
@photon-ai/imessage-kit/helperstypescript
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/helperstypescript
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
需要避免的常见错误
| Mistake | Why it's a problem | Fix |
|---|---|---|
Forgetting | Leaks resources (DB connections, file handles) | Always close in |
Forgetting | Keeps timer running after process should exit | Call in SIGINT handler |
Auto-replying without | Creates infinite reply loops | Always add |
| Using relative file paths | AppleScript cannot resolve them | Always use absolute paths |
Not handling errors on | Uncaught rejections crash the process | Wrap all send calls in |
Tight reconnect loop on | Hammers the server | Use bounded retry with |
| Logging full message content | Privacy risk | Log only metadata (GUID, sender, timestamp) |
Calling | Race condition — server not authenticated | Always wait for the |
| 错误 | 问题原因 | 修复方案 |
|---|---|---|
忘记调用 | 资源泄漏(数据库连接、文件句柄) | 始终在 |
忘记调用 | 进程退出后定时器仍在运行 | 在SIGINT事件处理函数中调用 |
自动回复时未添加 | 造成无限回复循环 | 始终将 |
| 使用相对文件路径 | AppleScript无法解析相对路径 | 始终使用绝对路径 |
| 发送消息时未处理错误 | 未捕获的rejection会导致进程崩溃 | 将所有发送调用包裹在 |
| 断开连接时过频繁重试 | 对服务端造成请求冲击 | 使用带 |
| 打印完整消息内容 | 存在隐私风险 | 仅打印元数据(GUID、发送者、时间戳) |
在 | 竞态条件 — 服务端尚未完成鉴权 | 始终等待 |