@elizaos/plugin-imessage 2.0.0-beta.1 → 2.0.3-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +13 -18
  3. package/auto-enable.ts +1 -1
  4. package/package.json +21 -4
  5. package/dist/accounts.d.ts +0 -135
  6. package/dist/accounts.d.ts.map +0 -1
  7. package/dist/accounts.js +0 -209
  8. package/dist/accounts.js.map +0 -1
  9. package/dist/api/bluebubbles-routes.d.ts +0 -10
  10. package/dist/api/bluebubbles-routes.d.ts.map +0 -1
  11. package/dist/api/bluebubbles-routes.js +0 -132
  12. package/dist/api/bluebubbles-routes.js.map +0 -1
  13. package/dist/api/imessage-routes.d.ts +0 -80
  14. package/dist/api/imessage-routes.d.ts.map +0 -1
  15. package/dist/api/imessage-routes.js +0 -230
  16. package/dist/api/imessage-routes.js.map +0 -1
  17. package/dist/chatdb-reader.d.ts +0 -240
  18. package/dist/chatdb-reader.d.ts.map +0 -1
  19. package/dist/chatdb-reader.js +0 -647
  20. package/dist/chatdb-reader.js.map +0 -1
  21. package/dist/config.d.ts +0 -60
  22. package/dist/config.d.ts.map +0 -1
  23. package/dist/config.js +0 -8
  24. package/dist/config.js.map +0 -1
  25. package/dist/connector-account-provider.d.ts +0 -18
  26. package/dist/connector-account-provider.d.ts.map +0 -1
  27. package/dist/connector-account-provider.js +0 -83
  28. package/dist/connector-account-provider.js.map +0 -1
  29. package/dist/contacts-reader.d.ts +0 -147
  30. package/dist/contacts-reader.d.ts.map +0 -1
  31. package/dist/contacts-reader.js +0 -481
  32. package/dist/contacts-reader.js.map +0 -1
  33. package/dist/index.d.ts +0 -23
  34. package/dist/index.d.ts.map +0 -1
  35. package/dist/index.js +0 -78
  36. package/dist/index.js.map +0 -1
  37. package/dist/providers/index.d.ts +0 -4
  38. package/dist/providers/index.d.ts.map +0 -1
  39. package/dist/providers/index.js +0 -5
  40. package/dist/providers/index.js.map +0 -1
  41. package/dist/rpc.d.ts +0 -206
  42. package/dist/rpc.d.ts.map +0 -1
  43. package/dist/rpc.js +0 -393
  44. package/dist/rpc.js.map +0 -1
  45. package/dist/service.d.ts +0 -266
  46. package/dist/service.d.ts.map +0 -1
  47. package/dist/service.js +0 -1694
  48. package/dist/service.js.map +0 -1
  49. package/dist/setup-routes.d.ts +0 -38
  50. package/dist/setup-routes.d.ts.map +0 -1
  51. package/dist/setup-routes.js +0 -322
  52. package/dist/setup-routes.js.map +0 -1
  53. package/dist/types.d.ts +0 -192
  54. package/dist/types.d.ts.map +0 -1
  55. package/dist/types.js +0 -138
  56. package/dist/types.js.map +0 -1
@@ -1,647 +0,0 @@
1
- /**
2
- * macOS chat.db reader for @elizaos/plugin-imessage.
3
- *
4
- * iMessage stores every message in a SQLite database at
5
- * `~/Library/Messages/chat.db`. Reading it requires Full Disk Access on
6
- * whichever process hosts the plugin (the Eliza agent, typically). This
7
- * module opens that file read-only and exposes a single `fetchNewMessages`
8
- * method the polling loop uses to walk forward by ROWID.
9
- *
10
- * ---
11
- *
12
- * Backend: runtime SQLite built-ins. Bun exposes `bun:sqlite`; Node 22+
13
- * exposes `node:sqlite`. We normalize both to the small query surface this
14
- * module needs so live chat.db reads keep working in test runners and under
15
- * either runtime.
16
- *
17
- * Prior to this module, the plugin attempted to read messages by running
18
- * AppleScript against Messages.app's `get messages` verb — a verb that
19
- * does not exist in Messages.app's scripting dictionary. That code path
20
- * silently returned an empty list on every poll, so inbound messages
21
- * never reached the agent.
22
- */
23
- import { createRequire } from "node:module";
24
- import { homedir } from "node:os";
25
- import { join } from "node:path";
26
- import { logger } from "@elizaos/core";
27
- /**
28
- * Default path to macOS's iMessage database. Requires Full Disk Access
29
- * on whichever process opens it.
30
- */
31
- export const DEFAULT_CHAT_DB_PATH = join(homedir(), "Library", "Messages", "chat.db");
32
- export const MACOS_FULL_DISK_ACCESS_SETTINGS_URL = "x-apple.systempreferences:com.apple.preference.security?Privacy_AllFiles";
33
- export function createFullDiskAccessAction() {
34
- return {
35
- type: "full_disk_access",
36
- label: "Open Full Disk Access",
37
- url: MACOS_FULL_DISK_ACCESS_SETTINGS_URL,
38
- instructions: [
39
- "Open System Settings > Privacy & Security > Full Disk Access.",
40
- "Enable Eliza. If you run Eliza from a terminal, enable that terminal app too.",
41
- "Quit and relaunch Eliza after changing Full Disk Access.",
42
- ],
43
- };
44
- }
45
- /**
46
- * Apple Cocoa reference date: 2001-01-01T00:00:00Z. The `message.date`
47
- * column stores a delta from this instant. Modern macOS stores the delta
48
- * in nanoseconds; older macOS (< 10.13) stored it in seconds. We detect
49
- * which by magnitude — any plausible seconds-since-2001 value fits in ~10
50
- * digits, any nanoseconds-since-2001 value is at least 13 digits.
51
- */
52
- const APPLE_EPOCH_MS = Date.UTC(2001, 0, 1);
53
- /**
54
- * Convert an Apple Cocoa date delta to JavaScript milliseconds since
55
- * epoch. Handles both legacy (seconds) and modern (nanoseconds) storage.
56
- */
57
- export function appleDateToJsMs(appleDate) {
58
- if (!appleDate || appleDate < 0)
59
- return 0;
60
- // Nanosecond-scale values are enormous (> 1e15 for any date after 2002).
61
- // Second-scale values top out around 1e9 for dates decades from now.
62
- // Split at 1e12 to be safe.
63
- const deltaMs = appleDate > 1e12 ? appleDate / 1e6 : appleDate * 1000;
64
- return APPLE_EPOCH_MS + deltaMs;
65
- }
66
- /**
67
- * Extract the plain UTF-8 text from an `attributedBody` BLOB.
68
- *
69
- * Modern macOS (~10.13+) stores message text as an `NSMutableAttributedString`
70
- * serialised via Apple's legacy `typedstream` / NSArchiver format, because
71
- * Messages.app wants to attach attributes (links, mentions, formatting)
72
- * that plain text can't carry. Empirically, on a fresh macOS chat.db,
73
- * ~97% of message rows have `text=NULL` and their actual readable content
74
- * only exists in `attributedBody`. Reading chat.db without decoding this
75
- * blob means being blind to almost every real message.
76
- *
77
- * Full typedstream parsing is complex (class inheritance chains, object
78
- * references, multiple string encodings) and worth ~500 lines of code.
79
- * The good news: Messages.app uses a narrow, stable subset for its own
80
- * message text, and the text always appears after the same marker
81
- * sequence: `NSString\x00\x01\x94\x84\x01\x2b` (class name, object flags,
82
- * then `+` which is typedstream's "cstring" verb), followed by a length
83
- * byte, followed by the UTF-8 bytes. For strings longer than 254 bytes
84
- * typedstream escapes the length with `0x81` followed by a little-endian
85
- * uint16 length, then the bytes. Everything else we don't care about.
86
- *
87
- * Verified against real blobs from a live chat.db — hit rate is ~100% on
88
- * the messages checked, including short replies like "Yo" and longer
89
- * messages with emoji. Returns null if no marker is found, which the
90
- * caller uses as a signal to fall back to the raw `text` column or
91
- * skip the row.
92
- *
93
- * References:
94
- * - Apple's typedstream format: see darling-gnustep-base, GSTypedStream.m
95
- * - imessage-exporter's Rust implementation (MIT) for the full parser
96
- * - NSAttributedString serialisation in Cocoa Foundation
97
- */
98
- export function decodeAttributedBody(blob) {
99
- if (!blob)
100
- return null;
101
- const buf = blob instanceof Buffer ? blob : Buffer.from(blob);
102
- if (buf.length < 20)
103
- return null;
104
- // Locate the NSString class marker. Messages.app uses either "NSString"
105
- // or (rarely) "NSMutableString" depending on whether the attributed
106
- // string wraps a mutable backing store. Try both.
107
- const MARKERS = [Buffer.from("NSString", "latin1"), Buffer.from("NSMutableString", "latin1")];
108
- let start = -1;
109
- for (const marker of MARKERS) {
110
- const idx = buf.indexOf(marker);
111
- if (idx !== -1) {
112
- start = idx + marker.length;
113
- break;
114
- }
115
- }
116
- if (start === -1)
117
- return null;
118
- // After the class name is a short preamble: the byte sequence varies
119
- // slightly depending on object graph position but always ends with
120
- // `\x2b` (the typedstream `+` cstring verb). We scan forward a small
121
- // bounded window for the `+` so the decoder doesn't drift into the
122
- // attributes dictionary if Apple changes the exact preamble length.
123
- const MAX_PREAMBLE = 16;
124
- let plusAt = -1;
125
- for (let i = start; i < Math.min(start + MAX_PREAMBLE, buf.length); i++) {
126
- if (buf[i] === 0x2b) {
127
- plusAt = i;
128
- break;
129
- }
130
- }
131
- if (plusAt === -1)
132
- return null;
133
- // Read the length. Typedstream uses:
134
- // - single byte length for 0..254
135
- // - 0x81 + little-endian uint16 for 255..65535
136
- // - 0x82 + little-endian uint32 for larger (rare in chat.db)
137
- let cursor = plusAt + 1;
138
- if (cursor >= buf.length)
139
- return null;
140
- let length;
141
- const first = buf[cursor];
142
- if (first < 0x80 || first === 0xff) {
143
- // Direct length byte
144
- length = first;
145
- cursor += 1;
146
- }
147
- else if (first === 0x81 && cursor + 2 < buf.length) {
148
- length = buf.readUInt16LE(cursor + 1);
149
- cursor += 3;
150
- }
151
- else if (first === 0x82 && cursor + 4 < buf.length) {
152
- length = buf.readUInt32LE(cursor + 1);
153
- cursor += 5;
154
- }
155
- else {
156
- // Unknown length encoding; give up.
157
- return null;
158
- }
159
- if (length === 0)
160
- return "";
161
- if (cursor + length > buf.length) {
162
- // Truncated blob — return what we can without overrunning.
163
- length = buf.length - cursor;
164
- if (length <= 0)
165
- return null;
166
- }
167
- // The bytes are UTF-8. Buffer.toString("utf8") silently replaces
168
- // invalid sequences with U+FFFD, which is the right behaviour here —
169
- // the agent would rather see a slightly-mangled message than nothing.
170
- return buf.slice(cursor, cursor + length).toString("utf8");
171
- }
172
- /**
173
- * Parse the reaction fields out of a chat.db row when `associated_message_type`
174
- * is in the reaction range (2000-3005). Returns null for non-reaction rows.
175
- */
176
- function parseReaction(row) {
177
- const type = row.associated_message_type ?? 0;
178
- if (type < 2000 || type >= 4000)
179
- return null;
180
- const isRemove = type >= 3000;
181
- const baseType = isRemove ? type - 1000 : type;
182
- const kind = baseType === 2000
183
- ? "love"
184
- : baseType === 2001
185
- ? "like"
186
- : baseType === 2002
187
- ? "dislike"
188
- : baseType === 2003
189
- ? "laugh"
190
- : baseType === 2004
191
- ? "emphasis"
192
- : baseType === 2005
193
- ? "question"
194
- : baseType === 2006
195
- ? "sticker"
196
- : "unknown";
197
- // associated_message_guid comes back as either a plain guid or prefixed
198
- // with `p:<partIndex>/<guid>` for messages that target a specific part
199
- // of a multipart message. Strip the prefix so downstream handlers get
200
- // a clean guid they can match against other rows.
201
- let targetGuid = row.associated_message_guid ?? "";
202
- const slashIdx = targetGuid.lastIndexOf("/");
203
- if (slashIdx !== -1)
204
- targetGuid = targetGuid.slice(slashIdx + 1);
205
- return {
206
- kind,
207
- add: !isRemove,
208
- rawType: type,
209
- targetGuid,
210
- emoji: row.associated_message_emoji,
211
- };
212
- }
213
- const runtimeRequire = createRequire(import.meta.url);
214
- const loggedChatDbOpenFailures = new Set();
215
- const lastChatDbAccessIssues = new Map();
216
- let loggedSqliteUnavailable = false;
217
- export function getLastChatDbAccessIssue(dbPath = DEFAULT_CHAT_DB_PATH) {
218
- return lastChatDbAccessIssues.get(dbPath) ?? null;
219
- }
220
- /**
221
- * Dynamically resolve a SQLite backend. We keep the specifiers opaque so the
222
- * module still loads under runtimes that only support one of them.
223
- */
224
- async function tryLoadSqlite() {
225
- try {
226
- const mod = runtimeRequire("bun:sqlite");
227
- const Database = mod.Database ?? mod.default;
228
- if (Database) {
229
- return (path, options) => new Database(path, options);
230
- }
231
- }
232
- catch {
233
- // Fall through to Node's built-in SQLite runtime.
234
- }
235
- try {
236
- const mod = (await import("node:sqlite"));
237
- const DatabaseSync = mod.DatabaseSync ?? mod.default?.DatabaseSync;
238
- if (!DatabaseSync) {
239
- return null;
240
- }
241
- return (path, options) => {
242
- const db = new DatabaseSync(path, {
243
- readOnly: options?.readonly ?? false,
244
- });
245
- return {
246
- query(sql) {
247
- const statement = db.prepare(sql);
248
- return {
249
- all(...params) {
250
- return statement.all(...params);
251
- },
252
- };
253
- },
254
- close() {
255
- db.close();
256
- },
257
- };
258
- };
259
- }
260
- catch {
261
- return null;
262
- }
263
- }
264
- /**
265
- * Open macOS chat.db read-only and return a reader bound to it.
266
- *
267
- * Returns `null` — and logs a human-readable reason — in every failure
268
- * mode so the caller can degrade to send-only operation instead of
269
- * crashing the runtime:
270
- *
271
- * - Not running under Bun (bun:sqlite built-in unavailable)
272
- * - chat.db does not exist at the given path
273
- * - chat.db exists but cannot be opened (missing Full Disk Access, etc.)
274
- */
275
- export async function openChatDb(dbPath = DEFAULT_CHAT_DB_PATH, options = {}) {
276
- const diagnosticsLogger = options.diagnosticsLogger ?? logger;
277
- const openDatabase = await tryLoadSqlite();
278
- if (!openDatabase) {
279
- lastChatDbAccessIssues.set(dbPath, {
280
- code: "sqlite_unavailable",
281
- path: dbPath,
282
- reason: "No supported SQLite runtime is available.",
283
- permissionAction: null,
284
- });
285
- if (!loggedSqliteUnavailable) {
286
- loggedSqliteUnavailable = true;
287
- diagnosticsLogger.warn("[imessage] no supported SQLite runtime is available — inbound polling is disabled. " +
288
- "Run the agent under Bun or Node 22+, or disable polling with IMESSAGE_POLL_INTERVAL_MS=0. " +
289
- "Outbound send via AppleScript still works regardless. Further identical startup checks will log at debug.");
290
- }
291
- else {
292
- diagnosticsLogger.debug("[imessage] SQLite runtime still unavailable; inbound polling remains disabled");
293
- }
294
- return null;
295
- }
296
- let db;
297
- try {
298
- db = openDatabase(dbPath, { readonly: true });
299
- }
300
- catch (error) {
301
- const reason = error instanceof Error ? error.message : String(error);
302
- lastChatDbAccessIssues.set(dbPath, {
303
- code: "open_failed",
304
- path: dbPath,
305
- reason,
306
- permissionAction: createFullDiskAccessAction(),
307
- });
308
- const failureKey = `${dbPath}\0${reason}`;
309
- if (!loggedChatDbOpenFailures.has(failureKey)) {
310
- loggedChatDbOpenFailures.add(failureKey);
311
- diagnosticsLogger.warn(`[imessage] Failed to open chat.db at ${dbPath}: ${reason}. ` +
312
- "Ensure the path is correct and the host process has Full Disk Access " +
313
- "(macOS → System Settings → Privacy & Security → Full Disk Access). " +
314
- `Open it directly with ${MACOS_FULL_DISK_ACCESS_SETTINGS_URL}. ` +
315
- "Plugin will continue in send-only mode. Further identical startup failures will log at debug.");
316
- }
317
- else {
318
- diagnosticsLogger.debug(`[imessage] chat.db at ${dbPath} is still unavailable (${reason}); continuing in send-only mode`);
319
- }
320
- return null;
321
- }
322
- lastChatDbAccessIssues.delete(dbPath);
323
- // Prepared statement reused on every poll. We join `message` to
324
- // `handle` (for the sender identity) and to `chat` (for the room
325
- // identity and display name) via the `chat_message_join` edge table.
326
- // We also pull `attributedBody` so the reader can recover the text for
327
- // the ~97% of messages that store their content there instead of in
328
- // the plain `text` column, and enough status columns to surface
329
- // reactions, replies, edits, and read receipts to the caller.
330
- const pollStmt = db.query(`
331
- SELECT
332
- m.ROWID AS row_id,
333
- m.guid AS guid,
334
- m.text AS text,
335
- m.attributedBody AS attributed_body,
336
- m.date AS apple_date,
337
- m.date_read AS apple_date_read,
338
- m.date_edited AS apple_date_edited,
339
- m.date_retracted AS apple_date_retracted,
340
- m.is_from_me AS is_from_me,
341
- m.is_read AS is_read,
342
- m.is_sent AS is_sent,
343
- m.is_delivered AS is_delivered,
344
- m.item_type AS item_type,
345
- m.reply_to_guid AS reply_to_guid,
346
- m.associated_message_guid AS associated_message_guid,
347
- m.associated_message_type AS associated_message_type,
348
- m.associated_message_emoji AS associated_message_emoji,
349
- m.cache_has_attachments AS cache_has_attachments,
350
- m.service AS message_service,
351
- h.id AS handle,
352
- h.service AS handle_service,
353
- c.chat_identifier AS chat_identifier,
354
- c.display_name AS display_name,
355
- c.style AS chat_style
356
- FROM message m
357
- LEFT JOIN handle h ON m.handle_id = h.ROWID
358
- LEFT JOIN chat_message_join cmj ON m.ROWID = cmj.message_id
359
- LEFT JOIN chat c ON cmj.chat_id = c.ROWID
360
- WHERE m.ROWID > ?
361
- ORDER BY m.ROWID ASC
362
- LIMIT ?
363
- `);
364
- // Secondary statement: fetch every attachment row attached to a given
365
- // message ROWID. Called lazily per-message when `cache_has_attachments`
366
- // is set, so zero-attachment polls pay nothing.
367
- const attachmentsStmt = db.query(`
368
- SELECT
369
- a.guid AS guid,
370
- a.transfer_name AS transfer_name,
371
- a.filename AS filename,
372
- a.mime_type AS mime_type,
373
- a.uti AS uti,
374
- a.total_bytes AS total_bytes,
375
- a.is_sticker AS is_sticker
376
- FROM attachment a
377
- JOIN message_attachment_join maj ON a.ROWID = maj.attachment_id
378
- WHERE maj.message_id = ?
379
- `);
380
- // Separate prepared statement for the cheap "what's the tip?" query.
381
- // Used once on service start to seed the polling cursor.
382
- const tipStmt = db.query("SELECT MAX(ROWID) AS max_row_id FROM message");
383
- const latestOwnMessageStmt = db.query("SELECT MAX(date) AS max_apple_date FROM message WHERE is_from_me = 1");
384
- const recentMessagesStmt = db.query(`
385
- SELECT
386
- m.ROWID AS row_id,
387
- m.guid AS guid,
388
- m.text AS text,
389
- m.attributedBody AS attributed_body,
390
- m.date AS apple_date,
391
- m.date_read AS apple_date_read,
392
- m.date_edited AS apple_date_edited,
393
- m.date_retracted AS apple_date_retracted,
394
- m.is_from_me AS is_from_me,
395
- m.is_read AS is_read,
396
- m.is_sent AS is_sent,
397
- m.is_delivered AS is_delivered,
398
- m.item_type AS item_type,
399
- m.reply_to_guid AS reply_to_guid,
400
- m.associated_message_guid AS associated_message_guid,
401
- m.associated_message_type AS associated_message_type,
402
- m.associated_message_emoji AS associated_message_emoji,
403
- m.cache_has_attachments AS cache_has_attachments,
404
- m.service AS message_service,
405
- h.id AS handle,
406
- h.service AS handle_service,
407
- c.chat_identifier AS chat_identifier,
408
- c.display_name AS display_name,
409
- c.style AS chat_style
410
- FROM message m
411
- LEFT JOIN handle h ON m.handle_id = h.ROWID
412
- LEFT JOIN chat_message_join cmj ON m.ROWID = cmj.message_id
413
- LEFT JOIN chat c ON cmj.chat_id = c.ROWID
414
- ORDER BY m.ROWID DESC
415
- LIMIT ?
416
- `);
417
- const recentMessagesByChatStmt = db.query(`
418
- SELECT
419
- m.ROWID AS row_id,
420
- m.guid AS guid,
421
- m.text AS text,
422
- m.attributedBody AS attributed_body,
423
- m.date AS apple_date,
424
- m.date_read AS apple_date_read,
425
- m.date_edited AS apple_date_edited,
426
- m.date_retracted AS apple_date_retracted,
427
- m.is_from_me AS is_from_me,
428
- m.is_read AS is_read,
429
- m.is_sent AS is_sent,
430
- m.is_delivered AS is_delivered,
431
- m.item_type AS item_type,
432
- m.reply_to_guid AS reply_to_guid,
433
- m.associated_message_guid AS associated_message_guid,
434
- m.associated_message_type AS associated_message_type,
435
- m.associated_message_emoji AS associated_message_emoji,
436
- m.cache_has_attachments AS cache_has_attachments,
437
- m.service AS message_service,
438
- h.id AS handle,
439
- h.service AS handle_service,
440
- c.chat_identifier AS chat_identifier,
441
- c.display_name AS display_name,
442
- c.style AS chat_style
443
- FROM message m
444
- LEFT JOIN handle h ON m.handle_id = h.ROWID
445
- LEFT JOIN chat_message_join cmj ON m.ROWID = cmj.message_id
446
- LEFT JOIN chat c ON cmj.chat_id = c.ROWID
447
- WHERE c.chat_identifier = ?
448
- ORDER BY m.ROWID DESC
449
- LIMIT ?
450
- `);
451
- // List-chats statement: every chat joined to handles via
452
- // chat_handle_join, grouped so each chat returns one row with an
453
- // aggregated participant list.
454
- const chatsStmt = db.query(`
455
- SELECT
456
- c.ROWID AS row_id,
457
- c.chat_identifier AS chat_identifier,
458
- c.display_name AS display_name,
459
- c.service_name AS service_name,
460
- c.style AS chat_style,
461
- c.last_read_message_timestamp AS last_read_apple_date,
462
- GROUP_CONCAT(h.id, ',') AS participant_handles
463
- FROM chat c
464
- LEFT JOIN chat_handle_join chj ON c.ROWID = chj.chat_id
465
- LEFT JOIN handle h ON chj.handle_id = h.ROWID
466
- GROUP BY c.ROWID
467
- ORDER BY c.last_read_message_timestamp DESC
468
- `);
469
- let closed = false;
470
- function materializeMessages(rows) {
471
- const out = [];
472
- let undecodable = 0;
473
- for (const row of rows) {
474
- // Resolve the visible text: prefer the plain `text` column, fall
475
- // back to decoding `attributedBody`, then empty string.
476
- let text = "";
477
- if (row.text && row.text.length > 0) {
478
- text = row.text;
479
- }
480
- else if (row.attributed_body) {
481
- const decoded = decodeAttributedBody(row.attributed_body);
482
- if (decoded != null) {
483
- text = decoded;
484
- }
485
- else {
486
- undecodable++;
487
- }
488
- }
489
- // Classify the row. Reactions get their own kind + a parsed
490
- // reaction payload; system messages (group add/remove/rename)
491
- // surface as `"system"` so the caller can log or ignore.
492
- const assocType = row.associated_message_type ?? 0;
493
- let kind = "text";
494
- let reaction = null;
495
- if (assocType >= 2000 && assocType < 4000) {
496
- kind = "reaction";
497
- reaction = parseReaction(row);
498
- }
499
- else if (row.item_type != null && row.item_type !== 0) {
500
- kind = "system";
501
- }
502
- // Attachments — only fetched when the cache bit indicates any.
503
- let attachments = [];
504
- if (row.cache_has_attachments === 1) {
505
- try {
506
- const attRows = attachmentsStmt.all(row.row_id);
507
- attachments = attRows.map((a) => ({
508
- guid: a.guid,
509
- filename: a.transfer_name ?? a.filename ?? null,
510
- uti: a.uti,
511
- mimeType: a.mime_type,
512
- totalBytes: a.total_bytes,
513
- isSticker: a.is_sticker === 1,
514
- }));
515
- }
516
- catch (error) {
517
- logger.debug(`[imessage] attachment query failed for rowid=${row.row_id}: ${error instanceof Error ? error.message : String(error)}`);
518
- }
519
- }
520
- // Service resolution: prefer the message row's own service field,
521
- // fall back to the handle's service (stable across messages from
522
- // the same sender), else unknown.
523
- const service = row.message_service ?? row.handle_service ?? null;
524
- out.push({
525
- rowId: row.row_id,
526
- guid: row.guid,
527
- text,
528
- kind,
529
- handle: row.handle ?? "",
530
- chatId: row.chat_identifier ?? "",
531
- chatType: row.chat_style === 43 ? "group" : "direct",
532
- displayName: row.display_name,
533
- timestamp: appleDateToJsMs(row.apple_date),
534
- isFromMe: row.is_from_me === 1,
535
- service,
536
- isSent: row.is_sent === 1,
537
- isDelivered: row.is_delivered === 1,
538
- isRead: row.is_read === 1,
539
- dateRead: appleDateToJsMs(row.apple_date_read ?? 0),
540
- dateEdited: appleDateToJsMs(row.apple_date_edited ?? 0),
541
- dateRetracted: appleDateToJsMs(row.apple_date_retracted ?? 0),
542
- replyToGuid: row.reply_to_guid,
543
- reaction,
544
- attachments,
545
- });
546
- }
547
- if (undecodable > 0) {
548
- logger.debug(`[imessage] chat.db poll: ${undecodable} row(s) had attributedBody that could not be decoded; their text is empty`);
549
- }
550
- return out;
551
- }
552
- return {
553
- fetchNewMessages(sinceRowId, limit) {
554
- if (closed)
555
- return [];
556
- let rows;
557
- try {
558
- rows = pollStmt.all(sinceRowId, limit);
559
- }
560
- catch (error) {
561
- logger.error(`[imessage] chat.db query failed: ${error instanceof Error ? error.message : String(error)}`);
562
- return [];
563
- }
564
- return materializeMessages(rows);
565
- },
566
- getLatestRowId() {
567
- if (closed)
568
- return 0;
569
- try {
570
- const rows = tipStmt.all();
571
- return rows[0]?.max_row_id ?? 0;
572
- }
573
- catch (error) {
574
- logger.error(`[imessage] chat.db tip query failed: ${error instanceof Error ? error.message : String(error)}`);
575
- return 0;
576
- }
577
- },
578
- getLatestOwnMessageTimestamp() {
579
- if (closed)
580
- return null;
581
- try {
582
- const rows = latestOwnMessageStmt.all();
583
- const appleDate = rows[0]?.max_apple_date ?? null;
584
- return appleDate === null ? null : appleDateToJsMs(appleDate);
585
- }
586
- catch (error) {
587
- logger.error(`[imessage] chat.db latest own message query failed: ${error instanceof Error ? error.message : String(error)}`);
588
- return null;
589
- }
590
- },
591
- listMessages(options = {}) {
592
- if (closed)
593
- return [];
594
- const chatId = options.chatId?.trim();
595
- const requestedLimit = typeof options.limit === "number" && Number.isFinite(options.limit) ? options.limit : 50;
596
- const limit = Math.max(1, Math.trunc(requestedLimit));
597
- let rows;
598
- try {
599
- rows = chatId
600
- ? recentMessagesByChatStmt.all(chatId, limit)
601
- : recentMessagesStmt.all(limit);
602
- }
603
- catch (error) {
604
- logger.error(`[imessage] chat.db listMessages query failed: ${error instanceof Error ? error.message : String(error)}`);
605
- return [];
606
- }
607
- // Queries run DESC for efficiency on "latest N" reads. Reverse
608
- // back to chronological order so API/UI callers can render in the
609
- // natural oldest→newest sequence without a second sort.
610
- return materializeMessages(rows).reverse();
611
- },
612
- listChats() {
613
- if (closed)
614
- return [];
615
- try {
616
- const rows = chatsStmt.all();
617
- return rows.map((row) => ({
618
- chatId: row.chat_identifier ?? `chat-${row.row_id}`,
619
- chatType: row.chat_style === 43 ? "group" : "direct",
620
- displayName: row.display_name,
621
- serviceName: row.service_name,
622
- participants: row.participant_handles
623
- ? row.participant_handles.split(",").filter(Boolean)
624
- : [],
625
- lastReadMessageTimestamp: appleDateToJsMs(row.last_read_apple_date ?? 0),
626
- }));
627
- }
628
- catch (error) {
629
- logger.error(`[imessage] chat.db listChats query failed: ${error instanceof Error ? error.message : String(error)}`);
630
- return [];
631
- }
632
- },
633
- close() {
634
- if (closed)
635
- return;
636
- closed = true;
637
- try {
638
- db.close();
639
- }
640
- catch {
641
- // Closing a read-only handle on a file we don't own should
642
- // never throw in practice, but we swallow to stay idempotent.
643
- }
644
- },
645
- };
646
- }
647
- //# sourceMappingURL=chatdb-reader.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"chatdb-reader.js","sourceRoot":"","sources":["../src/chatdb-reader.ts"],"names":[],"mappings":"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"}