@dawnlit/outlook-bridge 1.0.0
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.
- package/LICENSE +190 -0
- package/README.md +253 -0
- package/dist/OutlookService.d.ts +43 -0
- package/dist/OutlookService.js +137 -0
- package/dist/OutlookService.js.map +1 -0
- package/dist/errors.d.ts +112 -0
- package/dist/errors.js +140 -0
- package/dist/errors.js.map +1 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +71 -0
- package/dist/index.js.map +1 -0
- package/dist/mac/accounts.d.ts +5 -0
- package/dist/mac/accounts.js +17 -0
- package/dist/mac/accounts.js.map +1 -0
- package/dist/mac/attachments.d.ts +28 -0
- package/dist/mac/attachments.js +120 -0
- package/dist/mac/attachments.js.map +1 -0
- package/dist/mac/bounces.d.ts +32 -0
- package/dist/mac/bounces.js +285 -0
- package/dist/mac/bounces.js.map +1 -0
- package/dist/mac/cleanup.d.ts +29 -0
- package/dist/mac/cleanup.js +219 -0
- package/dist/mac/cleanup.js.map +1 -0
- package/dist/mac/drafts.d.ts +27 -0
- package/dist/mac/drafts.js +214 -0
- package/dist/mac/drafts.js.map +1 -0
- package/dist/mac/folders.d.ts +28 -0
- package/dist/mac/folders.js +127 -0
- package/dist/mac/folders.js.map +1 -0
- package/dist/mac/index.d.ts +22 -0
- package/dist/mac/index.js +119 -0
- package/dist/mac/index.js.map +1 -0
- package/dist/mac/read.d.ts +46 -0
- package/dist/mac/read.js +406 -0
- package/dist/mac/read.js.map +1 -0
- package/dist/mac/run.d.ts +64 -0
- package/dist/mac/run.js +250 -0
- package/dist/mac/run.js.map +1 -0
- package/dist/mac/scripts.d.ts +137 -0
- package/dist/mac/scripts.js +337 -0
- package/dist/mac/scripts.js.map +1 -0
- package/dist/mac/send.d.ts +23 -0
- package/dist/mac/send.js +135 -0
- package/dist/mac/send.js.map +1 -0
- package/dist/mac/signatures.d.ts +4 -0
- package/dist/mac/signatures.js +36 -0
- package/dist/mac/signatures.js.map +1 -0
- package/dist/mac/templates.d.ts +34 -0
- package/dist/mac/templates.js +266 -0
- package/dist/mac/templates.js.map +1 -0
- package/dist/mail.d.ts +72 -0
- package/dist/mail.js +131 -0
- package/dist/mail.js.map +1 -0
- package/dist/outlookTemplateSections.d.ts +62 -0
- package/dist/outlookTemplateSections.js +313 -0
- package/dist/outlookTemplateSections.js.map +1 -0
- package/dist/runtime.d.ts +76 -0
- package/dist/runtime.js +123 -0
- package/dist/runtime.js.map +1 -0
- package/dist/shared/attachmentMatch.d.ts +32 -0
- package/dist/shared/attachmentMatch.js +50 -0
- package/dist/shared/attachmentMatch.js.map +1 -0
- package/dist/shared/bounceRules.d.ts +33 -0
- package/dist/shared/bounceRules.js +104 -0
- package/dist/shared/bounceRules.js.map +1 -0
- package/dist/shared/capabilities.d.ts +3 -0
- package/dist/shared/capabilities.js +9 -0
- package/dist/shared/capabilities.js.map +1 -0
- package/dist/shared/json.d.ts +11 -0
- package/dist/shared/json.js +45 -0
- package/dist/shared/json.js.map +1 -0
- package/dist/shared/replyBody.d.ts +49 -0
- package/dist/shared/replyBody.js +113 -0
- package/dist/shared/replyBody.js.map +1 -0
- package/dist/types.d.ts +365 -0
- package/dist/types.js +13 -0
- package/dist/types.js.map +1 -0
- package/dist/windows/accounts.d.ts +2 -0
- package/dist/windows/accounts.js +22 -0
- package/dist/windows/accounts.js.map +1 -0
- package/dist/windows/attachments.d.ts +28 -0
- package/dist/windows/attachments.js +141 -0
- package/dist/windows/attachments.js.map +1 -0
- package/dist/windows/bounces.d.ts +34 -0
- package/dist/windows/bounces.js +267 -0
- package/dist/windows/bounces.js.map +1 -0
- package/dist/windows/cleanup.d.ts +29 -0
- package/dist/windows/cleanup.js +165 -0
- package/dist/windows/cleanup.js.map +1 -0
- package/dist/windows/drafts.d.ts +29 -0
- package/dist/windows/drafts.js +253 -0
- package/dist/windows/drafts.js.map +1 -0
- package/dist/windows/folders.d.ts +16 -0
- package/dist/windows/folders.js +88 -0
- package/dist/windows/folders.js.map +1 -0
- package/dist/windows/index.d.ts +27 -0
- package/dist/windows/index.js +100 -0
- package/dist/windows/index.js.map +1 -0
- package/dist/windows/read.d.ts +54 -0
- package/dist/windows/read.js +372 -0
- package/dist/windows/read.js.map +1 -0
- package/dist/windows/run.d.ts +54 -0
- package/dist/windows/run.js +161 -0
- package/dist/windows/run.js.map +1 -0
- package/dist/windows/scripts.d.ts +56 -0
- package/dist/windows/scripts.js +163 -0
- package/dist/windows/scripts.js.map +1 -0
- package/dist/windows/send.d.ts +11 -0
- package/dist/windows/send.js +114 -0
- package/dist/windows/send.js.map +1 -0
- package/dist/windows/signatures.d.ts +14 -0
- package/dist/windows/signatures.js +69 -0
- package/dist/windows/signatures.js.map +1 -0
- package/dist/windows/templates.d.ts +25 -0
- package/dist/windows/templates.js +246 -0
- package/dist/windows/templates.js.map +1 -0
- package/package.json +50 -0
package/dist/mac/read.js
ADDED
|
@@ -0,0 +1,406 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.readInboxEmails = readInboxEmails;
|
|
4
|
+
exports.readEmailBody = readEmailBody;
|
|
5
|
+
exports.searchInboxByFilter = searchInboxByFilter;
|
|
6
|
+
exports.readSelectedEmail = readSelectedEmail;
|
|
7
|
+
exports.openOutlookEmail = openOutlookEmail;
|
|
8
|
+
// Reading mail on macOS: one folder, one item, a whole Inbox tree, or whatever
|
|
9
|
+
// is selected in the running Outlook.
|
|
10
|
+
//
|
|
11
|
+
// Every reader here is two passes. Pass 1 indexes a folder with bulk property
|
|
12
|
+
// reads (one Apple event each); pass 2 fetches the expensive fields — body,
|
|
13
|
+
// attachments, sender — only for the messages that survived. Reading
|
|
14
|
+
// per-message in a loop costs an event per property and is unusably slow on a
|
|
15
|
+
// real mailbox, which is the whole reason for the shape.
|
|
16
|
+
const run_1 = require("./run");
|
|
17
|
+
const scripts_1 = require("./scripts");
|
|
18
|
+
const mail_1 = require("../mail");
|
|
19
|
+
const errors_1 = require("../errors");
|
|
20
|
+
/** How much of a body readInboxEmails previews, matching the Windows reader. */
|
|
21
|
+
const PREVIEW_CHARS = 600;
|
|
22
|
+
/**
|
|
23
|
+
* Read recent messages for the given account, newest first.
|
|
24
|
+
*
|
|
25
|
+
* `entryId` here is Outlook for Mac's small integer message id (e.g. "779"), not
|
|
26
|
+
* the MAPI EntryID string Windows returns. The two are not interchangeable, so
|
|
27
|
+
* don't persist one and look it up on the other platform.
|
|
28
|
+
*/
|
|
29
|
+
async function readInboxEmails(emailAccount, daysBack = 60, limit = 50, folder) {
|
|
30
|
+
const days = Math.max(0, Math.floor(daysBack));
|
|
31
|
+
const cap = Math.max(0, Math.floor(limit));
|
|
32
|
+
if (cap === 0)
|
|
33
|
+
return [];
|
|
34
|
+
const ref = folder ? (0, mail_1.mailFolderRef)(folder) : { rootId: 6, rootLabel: 'Inbox', segments: [] };
|
|
35
|
+
// A folder argument that trims away to nothing ("\\", " ") would otherwise
|
|
36
|
+
// read the Inbox root and look like it had scoped — the exact silent
|
|
37
|
+
// mis-scoping this parameter exists to prevent. Mirrors the Windows check.
|
|
38
|
+
if (folder && folder.trim() && ref.segments.length === 0 && ref.rootId === 6
|
|
39
|
+
&& folder.trim().toLowerCase() !== 'inbox') {
|
|
40
|
+
throw new errors_1.NotFoundError('folder', `Folder '${folder}' does not name a folder under the Inbox.`);
|
|
41
|
+
}
|
|
42
|
+
const resolveScope = (0, scripts_1.mailScopeSnippet)(ref, folder || '');
|
|
43
|
+
const folderPath = (0, scripts_1.macFolderPath)(emailAccount, ref.rootLabel, ref.segments);
|
|
44
|
+
const isOutgoing = (0, mail_1.isOutgoingRoot)(ref.rootId);
|
|
45
|
+
// Pass 1 — index the folder. Folder order isn't documented, so sort here
|
|
46
|
+
// rather than trusting Outlook to hand back newest-first.
|
|
47
|
+
const indexScript = `tell application "Microsoft Outlook"
|
|
48
|
+
${(0, scripts_1.accountLookupSnippet)(emailAccount)}
|
|
49
|
+
${resolveScope}
|
|
50
|
+
set cutoff to (current date) - (${days} * days)
|
|
51
|
+
set idList to id of every message of scopeFolder
|
|
52
|
+
try
|
|
53
|
+
set timeList to ${(0, scripts_1.dateProperty)(ref.rootId)} of every message of scopeFolder
|
|
54
|
+
on error
|
|
55
|
+
set timeList to {}
|
|
56
|
+
end try
|
|
57
|
+
set hasTimes to ((count of timeList) is (count of idList))
|
|
58
|
+
set out to ""
|
|
59
|
+
repeat with i from 1 to (count of idList)
|
|
60
|
+
set d to missing value
|
|
61
|
+
if hasTimes then set d to item i of timeList
|
|
62
|
+
-- An item carrying no timestamp of its own (an unsent draft filed into a
|
|
63
|
+
-- mail folder) is skipped rather than crashing the walk, matching what the
|
|
64
|
+
-- Windows Restrict on the same property does with it.
|
|
65
|
+
if d is not missing value and d is greater than or equal to cutoff then
|
|
66
|
+
set out to out & (item i of idList as string) & tab & my isoDate(d) & linefeed
|
|
67
|
+
end if
|
|
68
|
+
end repeat
|
|
69
|
+
return out
|
|
70
|
+
end tell`;
|
|
71
|
+
const index = (await (0, run_1.runOsaScript)(indexScript, 60000))
|
|
72
|
+
.split('\n')
|
|
73
|
+
.map(line => line.trim())
|
|
74
|
+
.filter(Boolean)
|
|
75
|
+
.map(line => {
|
|
76
|
+
const [id, receivedTime] = line.split('\t');
|
|
77
|
+
return { id, receivedTime: receivedTime || '' };
|
|
78
|
+
});
|
|
79
|
+
// 'yyyy-MM-dd HH:mm' is lexicographically ordered, so plain string compare sorts it.
|
|
80
|
+
index.sort((a, b) => b.receivedTime.localeCompare(a.receivedTime));
|
|
81
|
+
const chosen = index.slice(0, cap);
|
|
82
|
+
if (chosen.length === 0)
|
|
83
|
+
return [];
|
|
84
|
+
// Pass 2 — the expensive reads, only for messages we keep.
|
|
85
|
+
const detailScript = `tell application "Microsoft Outlook"
|
|
86
|
+
set wanted to {${chosen.map(c => c.id).join(', ')}}
|
|
87
|
+
set out to ""
|
|
88
|
+
repeat with k from 1 to (count of wanted)
|
|
89
|
+
set theMsg to missing value
|
|
90
|
+
try
|
|
91
|
+
set theMsg to message id (item k of wanted)
|
|
92
|
+
end try
|
|
93
|
+
if theMsg is not missing value then
|
|
94
|
+
set subj to ""
|
|
95
|
+
try
|
|
96
|
+
set subj to (subject of theMsg) as string
|
|
97
|
+
end try
|
|
98
|
+
${isOutgoing ? (0, scripts_1.firstRecipientSnippet)('theMsg', ' ') : (0, scripts_1.senderSnippet)('theMsg', ' ')}
|
|
99
|
+
set bodyText to ""
|
|
100
|
+
try
|
|
101
|
+
set bodyText to (plain text content of theMsg) as string
|
|
102
|
+
end try
|
|
103
|
+
if (length of bodyText) > ${PREVIEW_CHARS} then set bodyText to text 1 thru ${PREVIEW_CHARS} of bodyText
|
|
104
|
+
set attNames to {}
|
|
105
|
+
try
|
|
106
|
+
set attNames to name of every attachment of theMsg
|
|
107
|
+
end try
|
|
108
|
+
set out to out & ${(0, run_1.asRow)([
|
|
109
|
+
'(id of theMsg as string)',
|
|
110
|
+
'subj',
|
|
111
|
+
'sndName',
|
|
112
|
+
'sndAddr',
|
|
113
|
+
'bodyText',
|
|
114
|
+
'my sanitizeList(attNames)',
|
|
115
|
+
])}
|
|
116
|
+
end if
|
|
117
|
+
end repeat
|
|
118
|
+
return out
|
|
119
|
+
end tell`;
|
|
120
|
+
const byId = new Map(chosen.map(c => [c.id, c.receivedTime]));
|
|
121
|
+
return (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(detailScript, 120000)).map(record => {
|
|
122
|
+
const parts = (0, run_1.splitFields)(record);
|
|
123
|
+
const id = (0, run_1.field)(parts, 0);
|
|
124
|
+
const attachmentNames = (0, run_1.splitList)((0, run_1.field)(parts, 5));
|
|
125
|
+
return {
|
|
126
|
+
entryId: id,
|
|
127
|
+
storeId: '', // macOS AppleScript has no StoreID equivalent
|
|
128
|
+
subject: (0, run_1.field)(parts, 1).trim(),
|
|
129
|
+
senderName: (0, run_1.field)(parts, 2),
|
|
130
|
+
senderEmail: (0, run_1.field)(parts, 3),
|
|
131
|
+
receivedTime: byId.get(id) || '',
|
|
132
|
+
bodyPreview: (0, run_1.field)(parts, 4),
|
|
133
|
+
attachmentNames,
|
|
134
|
+
attachmentCount: attachmentNames.length,
|
|
135
|
+
folderPath,
|
|
136
|
+
};
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Read one email's full plain-text body by message id.
|
|
141
|
+
*
|
|
142
|
+
* `storeId` is accepted for signature parity and ignored: macOS has no StoreID,
|
|
143
|
+
* and `message id N` resolves against the application rather than one folder, so
|
|
144
|
+
* the message is found wherever it currently sits — including a subfolder.
|
|
145
|
+
*
|
|
146
|
+
* Uses `plain text content`, matching the Windows reader's use of `.Body`: the
|
|
147
|
+
* same tradeoff applies, so an HTML table's rows flatten and tabular figures are
|
|
148
|
+
* better read from an attachment.
|
|
149
|
+
*/
|
|
150
|
+
async function readEmailBody(entryId, _storeId, maxChars = 8000, includeQuoted = false) {
|
|
151
|
+
const id = (0, scripts_1.macMessageId)(entryId);
|
|
152
|
+
const script = `tell application "Microsoft Outlook"
|
|
153
|
+
${(0, scripts_1.messageLookupSnippet)(id)}
|
|
154
|
+
${(0, scripts_1.messageDetailSnippet)(true)}
|
|
155
|
+
return ${(0, run_1.asRow)((0, scripts_1.messageDetailFields)(true))}
|
|
156
|
+
end tell`;
|
|
157
|
+
// The body is emitted LAST so a stray separator in earlier fields can't shift it.
|
|
158
|
+
const parts = (0, run_1.summaryFields)(await (0, run_1.runOsaScript)(script, 60000));
|
|
159
|
+
const attachmentNames = (0, run_1.splitList)((0, run_1.field)(parts, scripts_1.MessageDetail.attachmentNames));
|
|
160
|
+
const { body, quoted, separator } = (0, mail_1.splitQuotedOriginal)((0, run_1.field)(parts, scripts_1.MessageDetail.body));
|
|
161
|
+
// The quoted thread is context, never the priced content, so it is capped
|
|
162
|
+
// harder than the reply itself — matching the Windows reader.
|
|
163
|
+
const quotedCap = Math.min(maxChars, 4000);
|
|
164
|
+
return {
|
|
165
|
+
entryId: (0, run_1.field)(parts, scripts_1.MessageDetail.id) || id,
|
|
166
|
+
subject: (0, run_1.field)(parts, scripts_1.MessageDetail.subject).trim(),
|
|
167
|
+
senderName: (0, run_1.field)(parts, scripts_1.MessageDetail.senderName),
|
|
168
|
+
senderEmail: (0, run_1.field)(parts, scripts_1.MessageDetail.senderEmail),
|
|
169
|
+
receivedTime: (0, run_1.field)(parts, scripts_1.MessageDetail.receivedTime),
|
|
170
|
+
body: body.length > maxChars ? body.slice(0, maxChars) : body,
|
|
171
|
+
truncated: body.length > maxChars,
|
|
172
|
+
bodyLength: body.length,
|
|
173
|
+
quoteSeparator: separator,
|
|
174
|
+
quotedLength: quoted.length,
|
|
175
|
+
quotedOriginal: includeQuoted ? quoted.slice(0, quotedCap) : '',
|
|
176
|
+
attachmentNames,
|
|
177
|
+
attachmentCount: attachmentNames.length,
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
/**
|
|
181
|
+
* Translate a Restrict-style `like` pattern to a regex.
|
|
182
|
+
*
|
|
183
|
+
* Windows hands `subjectLike` to Outlook's server-side Restrict; AppleScript has
|
|
184
|
+
* no equivalent, so the same pattern is applied here instead. `%` and `*` are
|
|
185
|
+
* the any-run wildcards, `_` and `?` match one character, and the match is
|
|
186
|
+
* anchored because that is what SQL `like` means.
|
|
187
|
+
*/
|
|
188
|
+
function likePattern(pattern) {
|
|
189
|
+
let source = '';
|
|
190
|
+
for (const ch of pattern) {
|
|
191
|
+
if (ch === '%' || ch === '*')
|
|
192
|
+
source += '.*';
|
|
193
|
+
else if (ch === '_' || ch === '?')
|
|
194
|
+
source += '.';
|
|
195
|
+
else
|
|
196
|
+
source += ch.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
197
|
+
}
|
|
198
|
+
return new RegExp(`^${source}$`, 'i');
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* Walk every folder under the Inbox (recursively) for one account and return the
|
|
202
|
+
* emails matching `filter` — full body included, attachments listed by name but
|
|
203
|
+
* not saved.
|
|
204
|
+
*
|
|
205
|
+
* The filtering happens here rather than in Outlook: `Items.Restrict` is a COM
|
|
206
|
+
* facility with no AppleScript counterpart, so the folder walk indexes each
|
|
207
|
+
* folder in two bulk reads and the subject/date tests run on the index. The
|
|
208
|
+
* observable contract is the same; the cost profile is not, and a `daysBack` of
|
|
209
|
+
* 0 over a large mailbox tree is correspondingly heavier here.
|
|
210
|
+
*
|
|
211
|
+
* `subjectPattern` is applied case-insensitively regardless of the RegExp's own
|
|
212
|
+
* flags, matching PowerShell's `-match`.
|
|
213
|
+
*/
|
|
214
|
+
async function searchInboxByFilter(emailAccount, filter = {}, daysBack = 0) {
|
|
215
|
+
const days = Math.max(0, Math.floor(daysBack));
|
|
216
|
+
// Pass 1 — index every folder under the Inbox: path, id, subject, received.
|
|
217
|
+
const indexScript = `on scanFolder(theFolder, prefix, cutoff, useCutoff)
|
|
218
|
+
set out to ""
|
|
219
|
+
tell application "Microsoft Outlook"
|
|
220
|
+
set idList to id of every message of theFolder
|
|
221
|
+
set subjList to subject of every message of theFolder
|
|
222
|
+
try
|
|
223
|
+
set timeList to time received of every message of theFolder
|
|
224
|
+
on error
|
|
225
|
+
set timeList to {}
|
|
226
|
+
end try
|
|
227
|
+
set subs to mail folders of theFolder
|
|
228
|
+
end tell
|
|
229
|
+
set hasTimes to ((count of timeList) is (count of idList))
|
|
230
|
+
repeat with i from 1 to (count of idList)
|
|
231
|
+
set d to missing value
|
|
232
|
+
if hasTimes then set d to item i of timeList
|
|
233
|
+
set keep to true
|
|
234
|
+
if d is missing value then
|
|
235
|
+
-- A folder whose items carry no readable receive time can only be
|
|
236
|
+
-- included when the caller isn't bounding by date; silently dropping
|
|
237
|
+
-- it would look exactly like an empty folder.
|
|
238
|
+
set keep to not useCutoff
|
|
239
|
+
else if useCutoff and d is less than cutoff then
|
|
240
|
+
set keep to false
|
|
241
|
+
end if
|
|
242
|
+
if keep then
|
|
243
|
+
set out to out & ${(0, run_1.asRow)([
|
|
244
|
+
'prefix',
|
|
245
|
+
'(item i of idList as string)',
|
|
246
|
+
'(item i of subjList)',
|
|
247
|
+
'my isoDate(d)',
|
|
248
|
+
])}
|
|
249
|
+
end if
|
|
250
|
+
end repeat
|
|
251
|
+
repeat with f in subs
|
|
252
|
+
set childName to ""
|
|
253
|
+
try
|
|
254
|
+
set childName to (name of f) as string
|
|
255
|
+
end try
|
|
256
|
+
set out to out & my scanFolder(f, prefix & ${run_1.AS_LIST_SEP} & childName, cutoff, useCutoff)
|
|
257
|
+
end repeat
|
|
258
|
+
return out
|
|
259
|
+
end scanFolder
|
|
260
|
+
|
|
261
|
+
tell application "Microsoft Outlook"
|
|
262
|
+
${(0, scripts_1.accountLookupSnippet)(emailAccount)}
|
|
263
|
+
set rootInbox to inbox of targetAcct
|
|
264
|
+
set rootName to (name of rootInbox) as string
|
|
265
|
+
end tell
|
|
266
|
+
set cutoff to (current date) - (${days} * days)
|
|
267
|
+
return my scanFolder(rootInbox, rootName, cutoff, ${days > 0 ? 'true' : 'false'})`;
|
|
268
|
+
const likeRe = filter.subjectLike ? likePattern(filter.subjectLike) : null;
|
|
269
|
+
// PowerShell's -match is case-insensitive by default, so the pattern is
|
|
270
|
+
// rebuilt with `i` regardless of the flags the caller's RegExp carries.
|
|
271
|
+
const patternRe = filter.subjectPattern
|
|
272
|
+
? new RegExp(filter.subjectPattern.source, 'i')
|
|
273
|
+
: null;
|
|
274
|
+
const candidates = (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(indexScript, 300000))
|
|
275
|
+
.map(record => {
|
|
276
|
+
const parts = (0, run_1.splitFields)(record);
|
|
277
|
+
const segments = (0, run_1.splitList)((0, run_1.field)(parts, 0));
|
|
278
|
+
return {
|
|
279
|
+
folderPath: (0, scripts_1.macFolderPath)(emailAccount, segments[0] || 'Inbox', segments.slice(1)),
|
|
280
|
+
id: (0, run_1.field)(parts, 1),
|
|
281
|
+
subject: (0, run_1.field)(parts, 2).trim(),
|
|
282
|
+
receivedTime: (0, run_1.field)(parts, 3),
|
|
283
|
+
};
|
|
284
|
+
})
|
|
285
|
+
.filter(candidate => {
|
|
286
|
+
if (!candidate.subject)
|
|
287
|
+
return false;
|
|
288
|
+
if (likeRe && !likeRe.test(candidate.subject))
|
|
289
|
+
return false;
|
|
290
|
+
if (patternRe && !patternRe.test(candidate.subject))
|
|
291
|
+
return false;
|
|
292
|
+
if (filter.excludeReplies && /^(re|fwd?)\s*:/i.test(candidate.subject))
|
|
293
|
+
return false;
|
|
294
|
+
return true;
|
|
295
|
+
});
|
|
296
|
+
if (candidates.length === 0)
|
|
297
|
+
return [];
|
|
298
|
+
// Pass 2 — bodies, senders and attachment names for the survivors only.
|
|
299
|
+
const detailScript = `tell application "Microsoft Outlook"
|
|
300
|
+
set wanted to {${candidates.map(c => c.id).join(', ')}}
|
|
301
|
+
set out to ""
|
|
302
|
+
repeat with k from 1 to (count of wanted)
|
|
303
|
+
set theMsg to missing value
|
|
304
|
+
try
|
|
305
|
+
set theMsg to message id (item k of wanted)
|
|
306
|
+
end try
|
|
307
|
+
if theMsg is not missing value then
|
|
308
|
+
${(0, scripts_1.senderSnippet)('theMsg', ' ')}
|
|
309
|
+
set attNames to {}
|
|
310
|
+
try
|
|
311
|
+
set attNames to name of every attachment of theMsg
|
|
312
|
+
end try
|
|
313
|
+
set bodyText to ""
|
|
314
|
+
try
|
|
315
|
+
set bodyText to (plain text content of theMsg) as string
|
|
316
|
+
end try
|
|
317
|
+
set out to out & ${(0, run_1.asRow)([
|
|
318
|
+
'(id of theMsg as string)',
|
|
319
|
+
'sndName',
|
|
320
|
+
'sndAddr',
|
|
321
|
+
'my sanitizeList(attNames)',
|
|
322
|
+
'bodyText',
|
|
323
|
+
])}
|
|
324
|
+
end if
|
|
325
|
+
end repeat
|
|
326
|
+
return out
|
|
327
|
+
end tell`;
|
|
328
|
+
const details = new Map();
|
|
329
|
+
for (const record of (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(detailScript, 300000))) {
|
|
330
|
+
const parts = (0, run_1.splitFields)(record);
|
|
331
|
+
details.set((0, run_1.field)(parts, 0), {
|
|
332
|
+
senderName: (0, run_1.field)(parts, 1),
|
|
333
|
+
senderEmail: (0, run_1.field)(parts, 2),
|
|
334
|
+
attachmentNames: (0, run_1.splitList)((0, run_1.field)(parts, 3)),
|
|
335
|
+
body: (0, run_1.field)(parts, 4),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
const matches = [];
|
|
339
|
+
for (const candidate of candidates) {
|
|
340
|
+
const detail = details.get(candidate.id);
|
|
341
|
+
if (!detail)
|
|
342
|
+
continue;
|
|
343
|
+
// requireAttachment can only be answered once the attachment names are in
|
|
344
|
+
// hand, so it filters here rather than during the index pass.
|
|
345
|
+
if (filter.requireAttachment && detail.attachmentNames.length === 0)
|
|
346
|
+
continue;
|
|
347
|
+
matches.push({
|
|
348
|
+
entryId: candidate.id,
|
|
349
|
+
storeId: '', // macOS AppleScript has no StoreID equivalent
|
|
350
|
+
subject: candidate.subject,
|
|
351
|
+
senderName: detail.senderName,
|
|
352
|
+
senderEmail: detail.senderEmail,
|
|
353
|
+
receivedTime: candidate.receivedTime,
|
|
354
|
+
body: detail.body,
|
|
355
|
+
attachmentNames: detail.attachmentNames,
|
|
356
|
+
folderPath: candidate.folderPath,
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
return matches;
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* Read the email currently selected (or open) in Outlook — full body,
|
|
363
|
+
* attachments listed by name but not saved.
|
|
364
|
+
*
|
|
365
|
+
* `current messages` covers both cases the Windows reader handles separately
|
|
366
|
+
* (an explorer selection and an open item), so there is no second lookup here.
|
|
367
|
+
*/
|
|
368
|
+
async function readSelectedEmail() {
|
|
369
|
+
const script = `tell application "Microsoft Outlook"
|
|
370
|
+
set sel to {}
|
|
371
|
+
try
|
|
372
|
+
set sel to current messages
|
|
373
|
+
end try
|
|
374
|
+
if (count of sel) is 0 then error "No email is selected in Outlook. Open Outlook, select (or open) an email, then try again."
|
|
375
|
+
set theMsg to item 1 of sel
|
|
376
|
+
${(0, scripts_1.messageDetailSnippet)(true)}
|
|
377
|
+
return ${(0, run_1.asRow)((0, scripts_1.messageDetailFields)(true))}
|
|
378
|
+
end tell`;
|
|
379
|
+
const records = (0, run_1.splitRecords)(await (0, run_1.runOsaScript)(script, 30000));
|
|
380
|
+
if (records.length === 0) {
|
|
381
|
+
throw new errors_1.NotFoundError('email', 'Failed to read the selected Outlook email.');
|
|
382
|
+
}
|
|
383
|
+
const parts = (0, run_1.splitFields)(records[0]);
|
|
384
|
+
return {
|
|
385
|
+
entryId: (0, run_1.field)(parts, scripts_1.MessageDetail.id),
|
|
386
|
+
storeId: '', // macOS AppleScript has no StoreID equivalent
|
|
387
|
+
subject: (0, run_1.field)(parts, scripts_1.MessageDetail.subject).trim(),
|
|
388
|
+
senderName: (0, run_1.field)(parts, scripts_1.MessageDetail.senderName),
|
|
389
|
+
senderEmail: (0, run_1.field)(parts, scripts_1.MessageDetail.senderEmail),
|
|
390
|
+
receivedTime: (0, run_1.field)(parts, scripts_1.MessageDetail.receivedTime),
|
|
391
|
+
body: (0, run_1.field)(parts, scripts_1.MessageDetail.body),
|
|
392
|
+
attachmentNames: (0, run_1.splitList)((0, run_1.field)(parts, scripts_1.MessageDetail.attachmentNames)),
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
/** Open an email in Outlook by its message id, and bring Outlook forward. */
|
|
396
|
+
async function openOutlookEmail(entryId) {
|
|
397
|
+
const id = (0, scripts_1.macMessageId)(entryId);
|
|
398
|
+
const script = `
|
|
399
|
+
tell application "Microsoft Outlook"
|
|
400
|
+
${(0, scripts_1.messageLookupSnippet)(id)}
|
|
401
|
+
open theMsg
|
|
402
|
+
activate
|
|
403
|
+
end tell`;
|
|
404
|
+
await (0, run_1.runOsaScript)(script, 30000);
|
|
405
|
+
}
|
|
406
|
+
//# sourceMappingURL=read.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"read.js","sourceRoot":"","sources":["../../src/mac/read.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Run an AppleScript via a temp file (avoids arg-length and quoting limits).
|
|
3
|
+
*
|
|
4
|
+
* `AS_HANDLERS` is prepended here rather than by each caller — the Windows runner
|
|
5
|
+
* already owns its UTF-8 prelude the same way. It matters more on this side: a
|
|
6
|
+
* script missing a handler fails at COMPILE time for the WHOLE script, with a
|
|
7
|
+
* message pointing nowhere near the omission, so "remember to paste the handlers"
|
|
8
|
+
* was a footgun every new script had to survive.
|
|
9
|
+
*
|
|
10
|
+
* `timeout` overrides the configured default for this one call; see `configure()`
|
|
11
|
+
* for that and for the stdout cap.
|
|
12
|
+
*/
|
|
13
|
+
export declare function runOsaScript(source: string, timeout?: number): Promise<string>;
|
|
14
|
+
/** Escape a string for embedding inside an AppleScript double-quoted literal. */
|
|
15
|
+
export declare function asEscape(s: string): string;
|
|
16
|
+
/** AppleScript's boolean literals. */
|
|
17
|
+
export declare function asBool(value: boolean): string;
|
|
18
|
+
export declare const FIELD_SEP = "\u001F";
|
|
19
|
+
export declare const RECORD_SEP = "\u001E";
|
|
20
|
+
export declare const LIST_SEP = "\u001D";
|
|
21
|
+
/** The same three, as AppleScript expressions. */
|
|
22
|
+
export declare const AS_FIELD_SEP = "(character id 31)";
|
|
23
|
+
export declare const AS_RECORD_SEP = "(character id 30)";
|
|
24
|
+
export declare const AS_LIST_SEP = "(character id 29)";
|
|
25
|
+
/**
|
|
26
|
+
* Emit one record: the AppleScript expressions in `fields`, separated and
|
|
27
|
+
* terminated so `splitRecords`/`splitFields` can take them apart again.
|
|
28
|
+
*
|
|
29
|
+
* Every field goes through `sanitize`, which is what makes the framing safe
|
|
30
|
+
* rather than merely unlikely to break. A field holding a LIST must be built
|
|
31
|
+
* with `my sanitizeList(...)`, which cleans the elements instead — `sanitize`
|
|
32
|
+
* deliberately leaves the list separator alone.
|
|
33
|
+
*/
|
|
34
|
+
export declare function asRow(fields: readonly string[]): string;
|
|
35
|
+
/**
|
|
36
|
+
* The AppleScript handlers every script here relies on. Called with `my` from
|
|
37
|
+
* inside a `tell` block.
|
|
38
|
+
*
|
|
39
|
+
* `isoDate` produces the zero-padded 'yyyy-MM-dd HH:mm' both platforms' readers
|
|
40
|
+
* return; `sanitize` strips the framing characters; `joinList` and `replaceText`
|
|
41
|
+
* are the text-item-delimiter dance nobody should write twice.
|
|
42
|
+
*/
|
|
43
|
+
export declare const AS_HANDLERS = "\non pad2(n)\n set s to (n as integer) as string\n if (length of s) < 2 then set s to \"0\" & s\n return s\nend pad2\n\non isoDate(d)\n if d is missing value then return \"\"\n return (year of d as string) & \"-\" & pad2(month of d as integer) & \"-\" & pad2(day of d) & \" \" & pad2(hours of d) & \":\" & pad2(minutes of d)\nend isoDate\n\non joinList(lst, sep)\n set saved to AppleScript's text item delimiters\n set AppleScript's text item delimiters to sep\n set out to lst as string\n set AppleScript's text item delimiters to saved\n return out\nend joinList\n\non replaceText(t, findWhat, replaceWith)\n set saved to AppleScript's text item delimiters\n set AppleScript's text item delimiters to findWhat\n set parts to text items of t\n set AppleScript's text item delimiters to replaceWith\n set out to parts as string\n set AppleScript's text item delimiters to saved\n return out\nend replaceText\n\n-- Strips the FIELD and RECORD separators only. The LIST separator is left\n-- alone on purpose: a field may legitimately BE a list, and stripping 29 here\n-- collapsed every such field into one run-on string.\non sanitize(v)\n if v is missing value then return \"\"\n set t to v as string\n repeat with codePoint in {30, 31}\n set marker to (character id (codePoint as integer))\n if t contains marker then set t to my replaceText(t, marker, \" \")\n end repeat\n return t\nend sanitize\n\n-- A list field: each element loses all three separators, then they are joined\n-- with the LIST separator that splitList() takes them apart on.\non sanitizeList(lst)\n set cleaned to {}\n repeat with item_ in lst\n set t to my sanitize(item_)\n repeat with codePoint in {29}\n set marker to (character id (codePoint as integer))\n if t contains marker then set t to my replaceText(t, marker, \" \")\n end repeat\n set end of cleaned to t\n end repeat\n return my joinList(cleaned, (character id 29))\nend sanitizeList\n\non insertAboveQuoted(c, ins)\n if c is missing value then return ins\n set c to c as string\n if (length of c) is 0 then return ins\n -- Outlook builds a reply as a document whose own empty paragraph sits inside\n -- WordSection1; inserting straight after that tag puts the new text where the\n -- user's cursor would have been. Anything else falls back to just inside <body>.\n -- 'at' is a reserved AppleScript parameter name, so the cut point cannot be\n -- called that however naturally it reads.\n set cutPoint to my tagEnd(c, \"WordSection1>\")\n if cutPoint is 0 then set cutPoint to my tagEnd(c, \"<body\")\n if cutPoint is 0 then return ins & c\n if cutPoint is (length of c) then return c & ins\n return (text 1 thru cutPoint of c) & ins & (text (cutPoint + 1) thru -1 of c)\nend insertAboveQuoted\n\n-- 'rest' is an AppleScript term (rest of list), so the remainder cannot be\n-- called that: assigning to it compiles and then fails at run time.\non tagEnd(c, marker)\n set ix to offset of marker in c\n if ix is 0 then return 0\n if marker ends with \">\" then return ix + (length of marker) - 1\n set tailText to text ix thru -1 of c\n set gt to offset of \">\" in tailText\n if gt is 0 then return 0\n return ix + gt - 1\nend tagEnd\n";
|
|
44
|
+
/** Split a script's output into records, dropping the trailing empty one. */
|
|
45
|
+
export declare function splitRecords(raw: string): string[];
|
|
46
|
+
/** Split one record into its fields. */
|
|
47
|
+
export declare function splitFields(record: string): string[];
|
|
48
|
+
/** Split one field that carries a list. */
|
|
49
|
+
export declare function splitList(value: string | undefined): string[];
|
|
50
|
+
/** A field by position — a missing field reads as ''. */
|
|
51
|
+
export declare function field(fields: readonly string[], index: number): string;
|
|
52
|
+
/**
|
|
53
|
+
* The fields of a script's ONE summary record — the shape every mutation here
|
|
54
|
+
* returns (counts first, then any per-item failures as further records).
|
|
55
|
+
*/
|
|
56
|
+
export declare function summaryFields(raw: string): string[];
|
|
57
|
+
/** A numeric field, defaulting to `fallback` when absent or unparsable. */
|
|
58
|
+
export declare function intField(fields: readonly string[], index: number, fallback?: number): number;
|
|
59
|
+
/**
|
|
60
|
+
* A boolean field. AppleScript renders booleans as the bare words `true`/`false`,
|
|
61
|
+
* so this is the counterpart of `shared/json.ts`'s coercions for the framed
|
|
62
|
+
* output — the Windows side gets those free from ConvertFrom-Json.
|
|
63
|
+
*/
|
|
64
|
+
export declare function boolField(fields: readonly string[], index: number): boolean;
|