1. 项目概述一个为AI Agent和自动化而生的iMessage SDK如果你是一名在macOS上工作的开发者尤其是那些正在捣鼓AI Agent、自动化工具或者任何需要与iMessage/SMS打交道的应用那么你很可能遇到过这个痛点苹果没有提供一个官方的、好用的API来让你程序化地读取或发送iMessage。过去大家要么得去逆向私有框架要么得依赖一些不太稳定的AppleScript脚本来模拟点击过程繁琐且容易出错。photon-ai/imessage-kit的出现就是为了彻底解决这个问题。它是一个类型安全、设计优雅的iMessage SDK让你能用几行TypeScript代码就轻松实现消息的发送、接收、查询和实时监听。它的核心价值在于将原本需要通过复杂系统调用或GUI自动化才能完成的操作抽象成了一组简洁、可靠的开发者接口。无论是想做一个自动回复消息的机器人一个定时发送提醒的工具还是一个需要分析聊天记录的数据分析应用这个工具包都能让你事半功倍。它原生支持Bun和Node.js并且通过直接读取系统数据库和调用底层服务的方式实现了高效和稳定。2. 核心设计思路与技术选型解析2.1 为什么选择直接操作数据库与系统服务在macOS上与iMessage交互的传统思路主要有两种一是通过UI自动化如AppleScript触发“信息”应用二是尝试调用未公开的私有框架。imessage-kit选择了第三条也是我认为更稳健的路径组合拳。它的核心操作主要依赖于两个部分读取操作直接读取~/Library/Messages/chat.db这个SQLite数据库。这是“信息”应用存储所有聊天记录、联系人、附件元数据的地方。通过better-sqlite3这个高性能的本地模块进行只读查询可以极快地获取历史消息、聊天列表等信息完全绕过图形界面。发送操作通过执行AppleScript脚本来调用macOS系统内建的Messages应用服务。虽然听起来像是UI自动化但这里调用的是系统级的Apple事件Apple Events它比模拟鼠标键盘点击要稳定和高效得多。这种方式实际上是“请求系统服务执行一个发送消息的任务”只要权限正确成功率很高。这种设计的巧妙之处在于读写分离。读用最高效的本地数据库查询写则通过系统认可的服务接口。既保证了数据获取的性能和灵活性可以执行复杂的SQL查询又确保了发送动作的相对可靠性和对系统工作流的尊重。2.2 类型安全与跨运行时支持的意义项目使用TypeScript开发并提供了完整的类型定义这不仅仅是“写起来舒服”而已。对于操作像iMessage这样涉及复杂数据结构消息、附件、聊天会话的系统类型安全能极大避免运行时错误。例如当你查询消息时返回的Message对象会明确告诉你attachments是一个Attachment[]数组每个附件对象里有哪些字段IDE会自动补全和进行类型检查这在构建复杂逻辑时至关重要。跨运行时支持同时支持Node.js和Bun则体现了其作为现代开发工具的实用性。Bun以其启动速度和零依赖安装对于这个SDK的Bun版本而言著称非常适合需要快速响应的自动化脚本或CLI工具。Node.js则拥有更庞大的生态和部署场景。开发者可以根据项目上下文自由选择而无需重写代码。2.3 权限模型Full Disk Access的必要性这是使用这个SDK前必须理解且正确配置的一点。为什么需要“完全磁盘访问”权限因为它需要读取~/Library/Messages/chat.db数据库文件。这个文件包含了你的所有iMessage和SMS历史系统对其保护非常严格。从macOS Catalina开始苹果加强了隐私保护任何尝试访问用户敏感数据的应用都必须显式获得用户授权。注意授予权限时务必添加你实际运行代码的终端或IDE。比如如果你在VS Code的内置终端里运行脚本就需要将“Visual Studio Code”添加到“完全磁盘访问”列表中。如果是在系统自带的“终端”或“iTerm”中运行则添加对应的应用。授权错误是新手最常见的运行失败原因。3. 从安装到发送第一条消息完整实操指南3.1 环境准备与安装首先确保你的系统是macOS并且已经安装了Node.js18或Bun1.0。我个人更推荐使用Bun来体验因为它的安装和依赖管理过程更简洁。使用Bun安装推荐# 初始化一个新项目如果还没有 mkdir my-imessage-bot cd my-imessage-bot bun init # 安装SDK bun add photon-ai/imessage-kit这个过程是“零依赖”的因为Bun可以直接运行TypeScript且SDK的Bun版本可能内嵌了必要的SQLite驱动。使用Node.js安装# 初始化项目 mkdir my-imessage-bot cd my-imessage-bot npm init -y # 安装SDK及其必需的数据库驱动 npm install photon-ai/imessage-kit better-sqlite3这里需要注意better-sqlite3是一个本地模块native addon安装时可能会需要编译工具链比如Xcode Command Line Tools。如果安装失败通常按照终端提示安装相关工具即可。3.2 配置系统权限安装完成后先别急着写代码。打开系统设置 隐私与安全性 完全磁盘访问。点击左下角的锁图标解锁。点击列表下方的“”按钮。在弹出的Finder窗口中按下CmdShiftG输入/System/Applications/Utilities/找到并添加“终端”。关键步骤如果你使用VS Code、Cursor、Warp等第三方终端或IDE你需要找到它们的实际应用位置并添加。例如对于VS Code通常是在/Applications/Visual Studio Code.app。确保你添加的是你即将运行脚本的那个环境。添加后务必勾选该应用旁边的复选框。3.3 编写并运行第一个脚本创建一个名为send-first-message.ts的文件。import { IMessageSDK } from photon-ai/imessage-kit; // 初始化SDK实例 const sdk new IMessageSDK({ debug: true, // 首次运行时开启debug方便查看日志 }); async function main() { try { // 发送一条文本消息 // 参数1收件人可以是手机号带国家代码如8613812345678或邮箱地址 // 参数2消息内容 const result await sdk.send(1234567890, 你好这是来自 iMessage Kit 的第一条测试消息); console.log(✅ 消息发送成功发送时间, result.sentAt); } catch (error) { console.error(❌ 消息发送失败, error); // 这里可以更精细地处理错误例如权限错误、无效号码等 } finally { // 重要关闭SDK释放数据库连接等资源 await sdk.close(); } } main();运行脚本# 如果用Bun bun run send-first-message.ts # 如果用Node.js需要先编译TypeScript或者使用ts-node # 假设已安装ts-nodenpm install -D ts-node typescript npx ts-node send-first-message.ts如果一切配置正确你应该会在终端看到成功日志并且对应的联系人或群组会收到这条消息。如果遇到权限错误请返回3.2节检查权限设置并确保在修改权限后完全退出并重启了你的终端或IDE新的权限才会生效。3.4 基础配置项详解初始化IMessageSDK时可以传入一个配置对象理解这些配置有助于优化你的应用行为const sdk new IMessageSDK({ debug: false, // 默认false。设为true会在控制台打印详细的内部日志调试时非常有用。 maxConcurrent: 5, // 默认5。控制批量发送时的最大并发数避免过快发送触发系统限制。 scriptTimeout: 30000, // 默认30000ms。执行AppleScript发送消息的超时时间。网络不佳或发送大文件时可适当调高。 databasePath: ~/Library/Messages/chat.db, // 默认路径。一般无需修改除非你的数据库位置特殊。 watcher: { pollInterval: 2000, // 默认2000ms。监听新消息时的轮询间隔。间隔越短实时性越高但CPU占用也略高。 unreadOnly: false, // 默认false。监听器是否只关注未读消息。设为true可以过滤掉已读的历史消息。 excludeOwnMessages: true, // 默认true。监听时是否排除自己发送的消息。通常我们只关心他人发来的消息。 }, // Webhook配置高级功能当监听到新消息时可以自动POST到一个URL webhook: { url: https://your-server.com/webhook, headers: { Authorization: Bearer your-token } } });4. 核心功能深度解析与实战应用4.1 消息的发送不仅仅是文本发送功能是SDK最基础也是最重要的部分。它设计得非常灵活支持多种内容类型。纯文本发送是最直接的前面已经演示过。但实际应用中我们经常需要发送更丰富的内容。发送图片与文件// 1. 发送本地图片 await sdk.send(1234567890, { text: 这是我们上次会议的照片, images: [/Users/you/Photos/meeting.jpg, /Users/you/Photos/whiteboard.png] }); // 2. 发送网络图片SDK会自动下载临时文件并发送 await sdk.send(1234567890, { text: 看看这个有趣的图, images: [https://example.com/funny-cat.gif] }); // 3. 发送文档文件 await sdk.send(1234567890, { text: 季度报告草案请查收。, files: [ /Users/you/Documents/report.pdf, /Users/you/Documents/data.xlsx ] }); // 4. 使用便捷方法发送单个文件 await sdk.sendFile(1234567890, /path/to/document.pdf, 这是你要的文件);实操心得发送网络图片或大文件时务必要考虑scriptTimeout配置。因为下载和传输需要时间默认的30秒可能不够。另外发送大量附件时建议使用sendBatch进行并发控制避免同时触发太多系统进程。批量发送与联系人列表管理假设你有一个团队成员列表需要发送相同的通知。const teamMembers [ { name: Alice, phone: 12345678901 }, { name: Bob, phone: 12345678902 }, { name: Charlie, phone: 12345678903 }, ]; const messages teamMembers.map(member ({ to: member.phone, content: Hi ${member.name}, 别忘了下午三点的站会。 })); // 使用批量发送SDK会根据maxConcurrent配置控制并发 const results await sdk.sendBatch(messages); for (const result of results) { if (result.status fulfilled) { console.log(✅ 发送给 ${result.value.to} 成功); } else { console.error(❌ 发送给 ${result.reason.to} 失败:, result.reason.error.message); } }4.2 消息查询从数据库中挖掘信息读取能力是SDK的另一大亮点。你可以执行复杂的查询来获取你需要的数据。基础查询// 获取最近20条消息 const recentMessages await sdk.getMessages({ limit: 20 }); // 获取来自特定号码的未读消息 const unreadFromBob await sdk.getMessages({ sender: 1234567890, unreadOnly: true, since: new Date(Date.now() - 24 * 60 * 60 * 1000) // 最近24小时内 }); // 在群聊中搜索关键词 const projectChats await sdk.getMessages({ isGroupChat: true, search: 项目截止日期, limit: 50 });高级查询与数据处理通常我们可能需要更聚合的数据。例如统计每个联系人的未读消息数。const unreadSummary await sdk.getUnreadMessages(); console.log(你有 ${unreadSummary.total} 条未读消息来自 ${unreadSummary.senderCount} 个联系人/群组); for (const [sender, messages] of Object.entries(unreadSummary.messagesBySender)) { console.log(- ${sender}: ${messages.length} 条未读); // 可以在这里触发针对该联系人的自动回复逻辑 }处理消息中的附件消息对象中的attachments数组包含了附件的元信息如文件名、类型、在磁盘上的唯一标识guid。SDK提供了一系列工具函数来处理它们。import { attachmentExists, downloadAttachment, isImageAttachment } from photon-ai/imessage-kit; const messagesWithFiles await sdk.getMessages({ hasAttachments: true, limit: 5 }); for (const msg of messagesWithFiles.messages) { for (const att of msg.attachments) { console.log(附件: ${att.filename}, 类型: ${att.mimeType}); // 检查附件文件是否还存在用户可能已清理 if (await attachmentExists(att)) { // 如果是图片可以下载到指定位置 if (isImageAttachment(att)) { const savePath /tmp/downloaded_${att.filename}; await downloadAttachment(att, savePath); console.log(图片已保存至: ${savePath}); } } else { console.warn(附件文件可能已被删除: ${att.filename}); } } }4.3 聊天会话管理在iMessage中与一个联系人的所有对话无论是单人还是群组构成一个“聊天”Chat。listChats方法让你能管理这些会话。获取与筛选聊天列表// 获取所有聊天 const allChats await sdk.listChats(); // 常用筛选只获取有未读消息的群聊并按最近活动排序 const activeGroupChats await sdk.listChats({ type: group, // group 或 individual hasUnread: true, sortBy: recent, // recent 或 name limit: 10 }); for (const chat of activeGroupChats) { console.log(群聊: ${chat.displayName || 未命名群组}); console.log( - Chat ID: ${chat.chatId}); console.log( - 未读: ${chat.unreadCount}); console.log( - 最后消息: ${chat.lastMessageText?.substring(0, 50)}...); // chat.chatId 是发送消息到该群组的关键标识 }向群组发送消息向个人发送消息使用电话号码或邮箱向群组发送则需要使用上面获取到的chatId。const groups await sdk.listChats({ type: group, search: 家庭 }); if (groups.length 0) { const familyChatId groups[0].chatId; await sdk.send(familyChatId, { text: 今晚七点家庭聚餐别忘了, images: [/Users/you/Photos/restaurant.jpg] }); }5. 构建自动化与实时响应系统5.1 实时消息监听与自动回复这是将SDK能力从“工具”提升到“智能助理”的关键。通过startWatching方法你可以监听新消息并实时响应。基础监听await sdk.startWatching({ // 收到任何新消息包括自己发的如果excludeOwnMessages为false onMessage: (message) { console.log([${new Date().toLocaleTimeString()}] 新消息:, { 来自: message.isFromMe ? 我 : message.senderName || message.sender, 内容: message.text || [${message.attachments.length}个附件], 是否群聊: message.isGroupChat ? 是 : 否 }); }, // 仅收到私聊消息时触发 onDirectMessage: async (message) { // 这是一个自动回复的简单示例如果对方说“你好”就回复“你好” if (message.text message.text.toLowerCase().includes(你好)) { // 注意避免在监听回调中直接进行可能耗时的操作而阻塞后续消息处理 // 更好的做法是将其放入队列或异步执行 setTimeout(async () { await sdk.send(message.sender, 你好我是自动回复机器人。); }, 0); } }, // 仅收到群聊消息时触发 onGroupMessage: (message) { console.log(群「${message.chatId}」有新消息); }, onError: (error) { console.error(监听器出错:, error); } }); console.log(✅ 已开始监听iMessage消息... (按 CtrlC 停止)); // 保持进程运行 process.on(SIGINT, async () { await sdk.stopWatching(); await sdk.close(); process.exit(0); });使用链式API进行条件化自动回复SDK提供了一个更声明式、更强大的message()链式API来处理消息。await sdk.startWatching({ onDirectMessage: async (incomingMsg) { await sdk.message(incomingMsg) .ifUnread() // 只处理未读消息 .ifNotReaction() // 忽略点赞、爱心等Tapback反应 .matchText(/^(hi|hello|你好)/i) // 匹配以问候语开头的消息 .replyText((msg) { // 回复文本可以是一个函数 const name msg.senderName || 朋友; return 你好 ${name}很高兴收到你的消息。; }) .execute(); // 执行整个处理链 // 另一个处理链如果有人发“照片”这个词自动回复一张预设图片 await sdk.message(incomingMsg) .matchText(/照片|picture|photo/i) .replyImage([/Users/you/Photos/default_reply.jpg]) .execute(); } });这个链式API的可读性和可组合性非常强你可以轻松地构建复杂的消息处理规则。5.2 消息调度与智能提醒对于定时发送消息的需求SDK内置了MessageScheduler和更友好的Reminders包装器。使用MessageScheduler进行精确调度import { IMessageSDK, MessageScheduler } from photon-ai/imessage-kit; const sdk new IMessageSDK(); const scheduler new MessageScheduler(sdk, { debug: true }); // 安排一个一次性消息5分钟后发送 const jobId scheduler.schedule({ to: 1234567890, content: 五分钟到了该休息一下了, sendAt: new Date(Date.now() 5 * 60 * 1000), }); console.log(定时任务已创建ID: ${jobId}); // 安排一个每日重复的提醒 scheduler.scheduleRecurring({ to: 1234567890, content: 记得写日报哦, startAt: new Date(2024-06-01T18:00:00), // 从6月1日开始 interval: daily, // 也可以是 hourly, weekly, monthly或具体的毫秒数 endAt: new Date(2024-12-31T23:59:59), // 年底结束 }); // 获取所有待发送任务 const pendingJobs scheduler.getPending(); console.log(当前有 ${pendingJobs.length} 个待发送任务); // 在应用退出前确保销毁调度器释放资源 // scheduler.destroy();使用Reminders进行自然语言调度更人性化import { IMessageSDK, Reminders } from photon-ai/imessage-kit; const sdk new IMessageSDK(); const reminders new Reminders(sdk); // 使用自然语言描述时间 reminders.in(30 minutes, 1234567890, 烤箱里的蛋糕快好了); reminders.in(2 hours, 1234567890, 两小时后有个视频会议。); reminders.at(tomorrow 9am, 1234567890, 早安今天天气不错。); reminders.at(friday 5pm, group-chat-id-here, 周末快乐本周工作总结已发群邮件。); // 查看和管理提醒 const allReminders reminders.list(); console.log(所有待办提醒:, allReminders); // 取消某个提醒 if (allReminders.length 0) { reminders.cancel(allReminders[0].id); }注意事项调度功能依赖于你的脚本进程持续运行。如果你关闭了运行脚本的终端定时任务将不会触发。在生产环境中你需要将其作为一个常驻的后台服务如使用pm2、launchd或systemd来运行。6. 高级特性与插件系统6.1 插件系统扩展SDK行为插件系统允许你在SDK的生命周期和操作流程中注入自定义逻辑非常适合用于日志记录、监控、消息预处理或后处理。使用内置日志插件import { IMessageSDK, loggerPlugin } from photon-ai/imessage-kit; const sdk new IMessageSDK(); sdk.use(loggerPlugin({ level: debug, // debug, info, warn, error colored: true, // 在终端使用彩色输出 // 可以自定义日志格式 formatter: (level, message, meta) [${level.toUpperCase()}] ${message} })); // 现在所有SDK的操作都会被记录 await sdk.send(1234567890, 这条消息会被日志插件记录);创建自定义插件假设你想在所有发送的消息末尾自动添加一个签名。const signaturePlugin { name: signature-plugin, // SDK初始化时调用 onInit: async () { console.log(签名插件已加载); }, // 在发送消息前调用可以修改消息内容 onBeforeSend: async (to, content) { let finalContent; if (typeof content string) { finalContent content \n\n-- 来自自动发送系统; } else if (content typeof content object) { // 处理对象形式的content包含text, images等 finalContent { ...content, text: content.text ? content.text \n\n-- 来自自动发送系统 : -- 来自自动发送系统 }; } console.log(即将发送消息给 ${to}); return { to, content: finalContent }; }, // 在消息发送成功后调用 onAfterSend: async (result) { console.log(消息发送成功时间: ${result.sentAt}); // 这里可以将发送记录存入自己的数据库 }, // 在发送失败时调用 onSendError: async (error, to, content) { console.error(发送给 ${to} 失败:, error.message); // 这里可以实现重试逻辑或报警 }, // SDK关闭时调用 onDestroy: async () { console.log(签名插件已卸载); } }; const sdk new IMessageSDK(); sdk.use(signaturePlugin); // 现在发送的任何消息都会自动加上签名 await sdk.send(1234567890, 这是正文内容); // 接收方会看到“这是正文内容\n\n-- 来自自动发送系统”6.2 健壮的错误处理任何与外部系统尤其是操作系统服务交互的库健壮的错误处理都至关重要。SDK定义了清晰的错误类型。import { SendError, DatabaseError, PlatformError } from photon-ai/imessage-kit; async function safeSend(phone, message) { try { const result await sdk.send(phone, message); return { success: true, data: result }; } catch (error) { // 根据错误类型采取不同策略 if (error instanceof SendError) { // 发送错误可能是号码无效、无网络、iMessage服务未开启等 console.error(发送失败 (SendError): ${error.message}); // 可以在这里加入重试逻辑 if (error.message.includes(timeout)) { console.log(发送超时10秒后重试...); await new Promise(resolve setTimeout(resolve, 10000)); return safeSend(phone, message); // 递归重试需注意最大重试次数 } return { success: false, type: SEND, error: error.message }; } else if (error instanceof DatabaseError) { // 数据库错误可能是权限不足、数据库文件损坏、路径错误等 console.error(数据库访问失败 (DatabaseError): ${error.message}); // 检查权限或数据库文件 return { success: false, type: DB, error: error.message }; } else if (error instanceof PlatformError) { // 平台错误不支持的macOS版本、运行时环境问题等 console.error(平台不兼容 (PlatformError): ${error.message}); return { success: false, type: PLATFORM, error: error.message }; } else { // 未知错误 console.error(未知错误:, error); return { success: false, type: UNKNOWN, error: An unknown error occurred }; } } } // 使用封装好的函数 const response await safeSend(1234567890, 测试消息); if (!response.success) { // 根据错误类型通知用户或执行备用方案 }7. 常见问题排查与实战技巧在实际使用中你可能会遇到一些典型问题。以下是我在多次实践中总结的排查清单和技巧。7.1 权限与初始化问题问题运行脚本时报错提示“无法访问数据库”或“权限被拒绝”。检查1确认已按照3.2节的步骤将正确的终端或IDE应用添加到了“完全磁盘访问”权限列表中。检查2修改权限后必须完全退出终端或IDE并重新启动。macOS的权限缓存机制可能导致新设置不立即生效。检查3如果你通过SSH远程连接到Mac或者使用像tmux、screen这样的终端复用器权限环境可能不同。尝试直接在图形界面下的原生终端应用中运行一次。检查4确保你的用户账户对~/Library/Messages/目录有读取权限通常默认是有的。问题发送消息时长时间无响应然后超时。检查1确认macOS的“信息”应用是正常登录状态的。可以手动打开“信息”应用看看是否能正常收发。检查2检查网络连接。iMessage发送需要网络。检查3尝试发送给一个已知的、活跃的iMessage联系人蓝色气泡。有时发送给未注册iMessage的手机号绿色SMS气泡会因为运营商问题而延迟。解决适当增加初始化配置中的scriptTimeout值例如设为60000毫秒。7.2 消息发送与接收问题问题消息显示发送成功但对方没收到。排查首先在你自己Mac的“信息”应用中查看这条消息是否出现在与对方的对话里并且旁边是否有红色的感叹号⚠️这表示发送失败。原因1对方可能关闭了iMessage或者你输入的不是对方的Apple ID注册邮箱/手机号。原因2对于手机号确保包含了国家代码例如中国是86。1234567890只是一个示例。技巧可以先手动在“信息”应用里给对方发一条确认地址有效然后使用sdk.listChats()找到这个对话获取其chatId或正确的发送地址。问题监听器 (startWatching) 收不到我自己发送的消息。原因这是默认行为。初始化SDK时watcher.excludeOwnMessages默认为true目的是避免处理自己消息的循环。解决如果需要监听自己发送的消息例如确认发送成功在配置中将其设为false。const sdk new IMessageSDK({ watcher: { excludeOwnMessages: false } });问题getMessages查询不到非常旧的消息。原因macOS的chat.db数据库可能只保存最近一段时间的完整消息例如几个月更早的消息可能被归档或只保留摘要。这是系统行为。技巧可以尝试在“信息”应用的设置中调整“保留信息”的时间为“永久”但这不保证能通过数据库API访问到所有历史消息。7.3 性能与最佳实践场景需要处理大量历史消息进行分析。技巧避免一次性使用getMessages({})获取全部消息这可能导致内存占用过高。使用分页查询结合limit和offset参数或者使用since和before按时间范围分批查询。示例let allMessages []; let batchSize 500; let offset 0; let batch; do { batch await sdk.getMessages({ limit: batchSize, offset: offset }); allMessages allMessages.concat(batch.messages); offset batchSize; console.log(已获取 ${allMessages.length} 条消息); // 处理当前批次... } while (batch.messages.length batchSize);场景构建7x24小时运行的自动回复机器人。稳定性将核心监听逻辑包装在一个while循环或使用setInterval并添加完善的错误捕获和重启机制。资源管理确保在进程退出或崩溃前调用sdk.stopWatching()和sdk.close()。日志与监控务必启用loggerPlugin或将日志写入文件便于问题追踪。部署使用进程管理工具如pm2来守护你的脚本。# 使用PM2运行你的脚本 pm2 start your-bot.ts --name imessage-bot --interpreter bun pm2 logs imessage-bot # 查看日志 pm2 save # 保存进程列表 pm2 startup # 设置开机自启可选7.4 安全与隐私提醒这是一个强大的工具因此安全使用至关重要。代码安全你的脚本文件会包含SDK的访问权限。确保不要将包含敏感操作如自动发送消息的脚本暴露在公开可访问的地方。隐私考量chat.db包含所有聊天记录。你的脚本应仅处理必要的数据并考虑对读取到的消息内容进行加密存储如果你需要持久化避免隐私泄露。遵守条款此SDK用于教育和开发目的。请勿用于发送垃圾信息、骚扰他人或任何违反Apple服务条款的行为。自动化工具应尊重接收者的意愿。备份在对聊天数据库进行任何高级操作尽管此SDK主要是只读的前建议备份~/Library/Messages/chat.db文件。最后这个SDK打开了在macOS上进行iMessage自动化集成的一扇大门。从简单的定时提醒到复杂的基于AI的聊天分析机器人可能性非常多。关键在于理解其原理妥善处理权限和错误并在此基础上构建负责任、有价值的应用。