macsync/docs/modules/imessage.md

96 lines
4.3 KiB
Markdown

# iMessage (`IMessageSync`)
## Purpose
Sync iMessage / SMS conversations and send messages from the web back to
contacts via Messages.app.
## Direction
Bidirectional. Inbound: read `chat.db` -> push to server. Outbound: poll the
legacy `icloud.send_queue` table -> AppleScript-driven send via Messages.app.
## OS surface
`~/Library/Messages/chat.db` (SQLite, accessed via GRDB) plus Messages.app via
AppleScript for outbound. Requires **Full Disk Access**; module surfaces
`.fullDiskAccessRequired` when missing
(`@packages/imessage/Sources/IMessageSync/SyncManager.swift:36, 132-133`).
Contacts are loaded via Contacts.framework
(`@packages/imessage/Sources/IMessageSync/Reader.swift:77`).
## Files
- `Reader.swift` (`@packages/imessage/Sources/IMessageSync/Reader.swift`):
`class iMessageReader` (line 64) — `connect()`, `getConversations()`,
`getMessages(conversationId:since:)`, `loadContacts()`. `getMessages` now
invokes `extractTextFromAttributedBody(_:)` (line 371): when chat.db's
`text` column is null/empty and `attributedBody` is non-nil, the byte-scan
heuristic recovers plain text and substitutes it before sending. Limits of
the heuristic — single text run only, no rich formatting — are documented
in [known-limitations](../known-limitations.md#imessage-attributedbody-decoder-is-heuristic-not-a-full-typedstream-parser).
- `SyncManager.swift``final class SyncManager: BaseSyncManager<SyncStats, SyncError>`
(line 44); 30s timer (line 93). Owns `sendQueueClient: SendQueueClient<IMessageSendTransport>`
(lines 70-86), `didStartSync` / `willStopSync` (lines 170-176). Handles
schema-version migrations via `Self.syncSchemaVersion` (line 47) and chunked
payload retries (lines 331-354).
- `APIClient.swift``syncMessages`, `syncContacts`, `getStats`,
`getPendingSends`, `reportSendResult`, `resetSync`
(`@packages/imessage/Sources/IMessageSync/APIClient.swift:14-108`).
- `Sender.swift` — AppleScript driver `SendService`; uses
`AppleScriptEscape` (`@packages/shared/Sources/MacSyncShared/Util/AppleScriptEscape.swift:13`)
for all string interpolation.
- `SendQueueAdapter.swift``IMessageSendTransport` adapting the bespoke
legacy send-queue endpoints into the generic `SendQueueTransport` protocol.
- `ChunkingStrategy.swift` — adaptive batch sizing for large message + attachment payloads.
## Timing
- Read interval: **30s**
(`@packages/imessage/Sources/IMessageSync/SyncManager.swift:93`).
- Outbound poll interval: **30s**
(`@packages/imessage/Sources/IMessageSync/SyncManager.swift:74`).
- Inbound batch size: adaptive via `ChunkingStrategy.createAdaptiveChunks` (line 202).
## Server surface
- Entity tables: `icloud.contacts`, `icloud.conversations`, `icloud.messages`,
`icloud.send_queue` (legacy)
(`src/server/src/app/server.ts:38-49`).
- Client routes (`src/server/src/surfaces/client/imessage.ts`):
- `POST /client/imessage/sync` (line 71)
- `POST /client/imessage/contacts` (line 77)
- `GET /client/imessage/stats` (line 83)
- `GET /client/imessage/send-queue/pending` (line 88)
- `POST /client/imessage/send-queue/:id/result` (line 126)
- `POST /client/imessage/reset` (line 172)
- Web routes (`src/server/src/surfaces/my/messages.ts`):
- `GET /my/messages/conversations` (line 11)
- `GET /my/messages/conversations/:id/messages` (line 15)
- `GET /my/messages/last-sync` (line 21)
- Admin enqueue: `POST /admin/send-queue/enqueue`
(`src/server/src/surfaces/admin/send-queue.ts:15`).
## Web surface
- Tab: `/messages` (`web/src/App.tsx:57`).
- API helpers: `web/src/api/messages.ts`.
## Known limitations
- `attributedBody` is forwarded raw; macOS 13+ messages with no plaintext
appear blank in the dashboard. See
[known-limitations](../known-limitations.md#imessage-attributedbody-is-not-parsed).
- AppleScript send can stall if Messages.app is unresponsive; manual restart
required.
- The send queue is on a bespoke schema (extra columns vs the generic
`<module>_send_queue`); admin tooling must special-case it.
## Tests (`@packages/imessage/Tests/IMessageSyncTests/`)
- `ChunkingTests.swift` — payload sizing/splitting (hermetic).
- `SendValidationTests.swift` — recipient/phone format (hermetic).
- `SyncPayloadTests.swift` — Codable round-trips (hermetic).
Not covered by tests: chat.db reading, AppleScript sending, contacts
permission (all require macOS Privacy permissions).