automating-messages

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Automating Messages (JXA-first with UI/DB fallbacks)

自动化Messages(优先使用JXA,辅以UI/DB备选方案)

Contents

目录

Permissions and scope

权限与适用范围

  • Grants needed: Automation + Accessibility; Full Disk Access for any
    chat.db
    reads.
  • Keep automation scoped and auditable; avoid unsolicited sends and DB writes.
  • Pairs with
    automating-mac-apps
    for common setup (permissions, osascript invocation, UI scripting basics).
  • 所需权限:自动化+辅助功能;读取
    chat.db
    需要完全磁盘访问权限。
  • 保持自动化的范围可控且可审计;避免未经请求的消息发送和数据库写入操作。
  • 可与
    automating-mac-apps
    配合使用,完成通用设置(权限配置、osascript调用、UI脚本基础操作)。

Default workflow (happy path)

默认工作流程(顺畅路径)

  1. Resolve transport: pick
    serviceType
    (
    iMessage
    or
    SMS
    ) before targeting a buddy.
  2. Identify recipient: filter buddies by
    handle
    (phone/email). Avoid ambiguous names.
  3. Send via app-level
    send
    : pass Buddy object to
    Messages.send()
    .
  4. Verify window context: activate Messages when mixing with UI steps.
  5. Fallbacks: if send/attachments fail, use UI scripting; for history, use SQL.
  1. 解析传输服务:在定位联系人之前,选择
    serviceType
    iMessage
    SMS
    )。
  2. 识别收件人:通过
    handle
    (电话/邮箱)筛选联系人。避免使用模糊的名称。
  3. 通过应用级
    send
    发送:将Buddy对象传入
    Messages.send()
  4. 验证窗口上下文:当混合使用UI步骤时,激活Messages应用。
  5. 备选方案:如果发送/附件操作失败,使用UI脚本;读取历史记录则使用SQL。

Quick recipe (defensive send)

快速实现方案(防御式发送)

javascript
const Messages = Application('Messages');
Messages.includeStandardAdditions = true;

function safeSend(text, handle, svcType = 'iMessage') {
  const svc = Messages.services.whose({ serviceType: svcType })[0];
  if (!svc) throw new Error(`Service ${svcType} missing`);
  const buddy = svc.buddies.whose({ handle })[0];
  if (!buddy) throw new Error(`Buddy ${handle} missing on ${svcType}`);
  Messages.send(text, { to: buddy });
}
  • Wrap with
    try/catch
    and log; add small delays when activating UI.
  • For groups, target an existing chat by GUID or fall back to UI scripting; array sends are unreliable.
javascript
const Messages = Application('Messages');
Messages.includeStandardAdditions = true;

function safeSend(text, handle, svcType = 'iMessage') {
  const svc = Messages.services.whose({ serviceType: svcType })[0];
  if (!svc) throw new Error(`Service ${svcType} missing`);
  const buddy = svc.buddies.whose({ handle })[0];
  if (!buddy) throw new Error(`Buddy ${handle} missing on ${svcType}`);
  Messages.send(text, { to: buddy });
}
  • try/catch
    包裹并添加日志;激活UI时添加短暂延迟。
  • 针对群组消息,通过GUID定位现有聊天窗口,或退而使用UI脚本;数组发送不可靠。

Attachments and UI fallback

附件处理与UI备选方案

  • Messages lacks a stable JXA attachment API; use clipboard + System Events paste/send.
  • Ensure Accessibility permission, bring app forward, paste file, press Enter.
  • See
    references/ui-scripting-attachments.md
    for the full flow and ObjC pasteboard snippet.
  • Messages缺乏稳定的JXA附件API;使用剪贴板+System Events粘贴/发送。
  • 确保已获取辅助功能权限,将应用前置,粘贴文件,按Enter键发送。
  • 完整流程及ObjC剪贴板代码片段请参考
    references/ui-scripting-attachments.md

Data access and forensics

数据访问与取证

Reading messages limitation: The AppleScript/JXA API for Messages is effectively write-only. While
send()
works reliably, reading messages via
chat.messages()
or similar methods is broken/unsupported in modern macOS. The only reliable way to read message history is via direct SQLite access to
~/Library/Messages/chat.db
.
Security consideration: Reading
chat.db
requires Full Disk Access permission, which grants broad filesystem access beyond just Messages. This is a significant security trade-off - granting Full Disk Access to scripts or applications exposes all user data. Consider whether reading message history is truly necessary before enabling this permission.
  • Use SQL against
    chat.db
    for history; JXA
    chat.messages()
    is unreliable/non-functional.
  • Requires: System Settings > Privacy & Security > Full Disk Access for your terminal/script.
  • Remember Cocoa epoch conversion (nanoseconds since 2001-01-01); use
    sqlite3 -json
    for structured results.
  • See
    references/database-forensics.md
    for schema notes, typedstream handling, and export tooling.
Example read query (requires Full Disk Access):
sql
sqlite3 ~/Library/Messages/chat.db "SELECT
  CASE WHEN m.is_from_me = 1 THEN 'Me' ELSE 'Them' END as sender,
  m.text,
  datetime(m.date/1000000000 + 978307200, 'unixepoch', 'localtime') as date
FROM message m
JOIN handle h ON m.handle_id = h.rowid
WHERE h.id LIKE '%PHONE_NUMBER%'
ORDER BY m.date DESC LIMIT 10;"
读取消息限制: Messages的AppleScript/JXA API实际上是仅可写入的。虽然
send()
功能可靠,但在现代macOS中,通过
chat.messages()
或类似方法读取消息的功能已损坏/不受支持。读取消息历史记录的唯一可靠方式是直接通过SQLite访问
~/Library/Messages/chat.db
安全注意事项: 读取
chat.db
需要完全磁盘访问权限,该权限会授予超出Messages范围的广泛文件系统访问权限。这是一个重大的安全权衡——向脚本或应用授予完全磁盘访问权限会暴露所有用户数据。在启用此权限之前,请务必考虑是否真的需要读取消息历史记录。
  • 使用SQL查询
    chat.db
    获取历史记录;JXA的
    chat.messages()
    不可靠/无法正常工作。
  • 所需权限:系统设置 > 隐私与安全性 > 完全磁盘访问,需为你的终端/脚本开启。
  • 注意Cocoa时间戳转换(自2001-01-01起的纳秒数);使用
    sqlite3 -json
    获取结构化结果。
  • 模式说明、typedstream处理及导出工具请参考
    references/database-forensics.md
读取查询示例(需要完全磁盘访问权限):
sql
sqlite3 ~/Library/Messages/chat.db "SELECT
  CASE WHEN m.is_from_me = 1 THEN 'Me' ELSE 'Them' END as sender,
  m.text,
  datetime(m.date/1000000000 + 978307200, 'unixepoch', 'localtime') as date
FROM message m
JOIN handle h ON m.handle_id = h.rowid
WHERE h.id LIKE '%PHONE_NUMBER%'
ORDER BY m.date DESC LIMIT 10;"

Bots and monitoring

机器人与监控

  • Implement polling daemons with
    launchd
    now that on-receive handlers are gone.
  • Track
    rowid
    , query diffs, dispatch actions, and persist state.
  • See
    references/monitoring-daemons.md
    for the polling pattern and plist notes.
  • 由于接收事件处理器已移除,现在需通过
    launchd
    实现轮询守护进程。
  • 跟踪
    rowid
    ,查询差异,分发操作并持久化状态。
  • 轮询模式及plist配置说明请参考
    references/monitoring-daemons.md

Validation Checklist

验证清单

  • Automation + Accessibility permissions granted
  • Service resolves:
    Messages.services.whose({ serviceType: 'iMessage' })[0]
    returns object
  • Buddy lookup works:
    svc.buddies.whose({ handle })[0]
    returns target
  • Test send completes without errors
  • Full Disk Access granted if using
    chat.db
    reads
  • 已授予自动化+辅助功能权限
  • 服务解析正常:
    Messages.services.whose({ serviceType: 'iMessage' })[0]
    返回对象
  • 联系人查找正常:
    svc.buddies.whose({ handle })[0]
    返回目标联系人
  • 测试发送无错误完成
  • 若使用
    chat.db
    读取,已授予完全磁盘访问权限

When Not to Use

不适用场景

  • For reading message history without Full Disk Access (AppleScript/JXA cannot read messages)
  • For cross-platform messaging (use platform APIs or third-party services)
  • For business SMS automation (use Twilio or similar APIs)
  • When iMessage/SMS features are not available on the target system
  • For bulk messaging (rate limits and security restrictions apply)
  • When security policy prohibits Full Disk Access grants (required for any read operations)
  • 无完全磁盘访问权限时读取消息历史记录(AppleScript/JXA无法读取消息)
  • 跨平台消息自动化(使用平台API或第三方服务)
  • 商业SMS自动化(使用Twilio或类似API)
  • 目标系统不支持iMessage/SMS功能
  • 批量消息发送(存在速率限制和安全限制)
  • 安全策略禁止授予完全磁盘访问权限(读取操作必须使用该权限)

What to load

推荐加载的参考文档

  • Control plane and send reliability:
    references/control-plane.md
  • UI scripting + attachments fallback:
    references/ui-scripting-attachments.md
  • SQL/history access:
    references/database-forensics.md
  • Polling bots/launchd:
    references/monitoring-daemons.md
  • 控制平面与发送可靠性:
    references/control-plane.md
  • UI脚本+附件备选方案:
    references/ui-scripting-attachments.md
  • SQL/历史记录访问:
    references/database-forensics.md
  • 轮询机器人/launchd:
    references/monitoring-daemons.md