automating-messages
npx machina-cli add skill SpillwaveSolutions/automating-mac-apps-plugin/automating-messages --openclawAutomating Messages (JXA-first with UI/DB fallbacks)
Contents
- Permissions and scope
- Default workflow
- Quick recipe
- Attachments and UI fallback
- Data access and forensics
- Validation Checklist
- When Not to Use
- What to load
Permissions and scope
- Grants needed: Automation + Accessibility; Full Disk Access for any
chat.dbreads. - Keep automation scoped and auditable; avoid unsolicited sends and DB writes.
- Pairs with
automating-mac-appsfor common setup (permissions, osascript invocation, UI scripting basics).
Default workflow (happy path)
- Resolve transport: pick
serviceType(iMessageorSMS) before targeting a buddy. - Identify recipient: filter buddies by
handle(phone/email). Avoid ambiguous names. - Send via app-level
send: pass Buddy object toMessages.send(). - Verify window context: activate Messages when mixing with UI steps.
- Fallbacks: if send/attachments fail, use UI scripting; for history, use SQL.
Quick recipe (defensive send)
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/catchand 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.
Attachments and UI fallback
- 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.mdfor the full flow and ObjC pasteboard snippet.
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.dbfor history; JXAchat.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 -jsonfor structured results. - See
references/database-forensics.mdfor schema notes, typedstream handling, and export tooling.
Example read query (requires Full Disk Access):
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
launchdnow that on-receive handlers are gone. - Track
rowid, query diffs, dispatch actions, and persist state. - See
references/monitoring-daemons.mdfor the polling pattern and plist notes.
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.dbreads
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)
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
Source
git clone https://github.com/SpillwaveSolutions/automating-mac-apps-plugin/blob/main/plugins/automating-mac-apps-plugin/skills/automating-messages/SKILL.mdView on GitHub Overview
Automating Messages uses JXA to drive the Messages app (iMessage/SMS) with reliable service→buddy resolution. It combines app-level send with UI fallbacks for attachments and chat.db-based history forensics, plus support for launchd polling bots. Proper permissions and scope keep automation auditable and safe.
How This Skill Works
Resolve transport (iMessage or SMS), locate the recipient by handle, and invoke Messages.send() with the buddy. When app-level send fails or attachments are involved, it falls back to UI scripting to paste files and press Send. For history, use direct SQLite queries against ~/Library/Messages/chat.db since chat.messages() is unreliable.
When to Use It
- Automate iMessage or SMS sends from a script for scheduled or triggered messages.
- Send to a specific buddy by handle to avoid ambiguous recipient names.
- Use UI scripting as a fallback for attachments when the standard API is unstable.
- Read message history for logs or forensics via chat.db (requires Full Disk Access).
- Create a launchd polling bot that triggers messages on a schedule or event.
Quick Start
- Step 1: Load the Messages app in JXA (Messages) and choose a serviceType (iMessage or SMS).
- Step 2: Resolve the recipient by handle and call Messages.send(text, {to: buddy}).
- Step 3: If sending fails or attachments are needed, fall back to UI scripting or query chat.db for history (requires Full Disk Access).
Best Practices
- Resolve the serviceType before selecting a buddy to prevent mis-sends.
- Wrap send calls in try/catch and log outcomes for auditability.
- Keep automation scoped with Automation + Accessibility; request Full Disk Access only for chat.db reads.
- Validate recipient handles and avoid ambiguous names.
- Prefer SQL-based history reads from chat.db over unreliable chat.messages().
Example Use Cases
- Automated reminders that DM a buddy when a calendar alert fires.
- Status updates sent via iMessage from a monitoring script.
- UI-based attachment sending when Messages lacks a stable API.
- Exported chat history from chat.db for analytics and auditing.
- Launchd-based bot polling an API and messaging a user on events.