@littlebearapps/outlook-assistant 3.5.2 → 3.7.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/README.md +9 -6
- package/advanced/index.js +5 -5
- package/auth/device-code.js +3 -1
- package/auth/oauth-server.js +4 -4
- package/auth/token-manager.js +2 -2
- package/calendar/list.js +3 -3
- package/categories/index.js +4 -4
- package/contacts/index.js +11 -18
- package/email/conversations.js +19 -17
- package/email/delta.js +1 -1
- package/email/draft.js +482 -0
- package/email/folder-utils.js +1 -1
- package/email/headers.js +10 -10
- package/email/index.js +80 -0
- package/email/list.js +1 -1
- package/email/mime.js +13 -12
- package/email/search.js +3 -2
- package/email/send.js +1 -1
- package/folder/list.js +1 -1
- package/llms.txt +8 -7
- package/package.json +2 -2
- package/rules/create.js +85 -161
- package/rules/index.js +162 -29
- package/rules/list.js +144 -62
- package/rules/rule-builder.js +284 -0
- package/rules/update.js +213 -0
- package/settings/index.js +10 -22
- package/utils/field-presets.js +20 -0
- package/utils/graph-api.js +9 -1
- package/utils/response-formatter.js +8 -6
- package/utils/safety.js +113 -0
package/rules/index.js
CHANGED
|
@@ -3,8 +3,10 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const { handleListRules, getInboxRules } = require('./list');
|
|
5
5
|
const handleCreateRule = require('./create');
|
|
6
|
+
const handleUpdateRule = require('./update');
|
|
6
7
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
7
8
|
const { ensureAuthenticated } = require('../auth');
|
|
9
|
+
const { checkRateLimit } = require('../utils/safety');
|
|
8
10
|
|
|
9
11
|
/**
|
|
10
12
|
* Delete rule handler
|
|
@@ -14,6 +16,10 @@ const { ensureAuthenticated } = require('../auth');
|
|
|
14
16
|
async function handleDeleteRule(args) {
|
|
15
17
|
const { ruleName, ruleId } = args;
|
|
16
18
|
|
|
19
|
+
// Rate limit
|
|
20
|
+
const rateLimitError = checkRateLimit('manage-rules');
|
|
21
|
+
if (rateLimitError) return rateLimitError;
|
|
22
|
+
|
|
17
23
|
if (!ruleName && !ruleId) {
|
|
18
24
|
return {
|
|
19
25
|
content: [
|
|
@@ -116,13 +122,9 @@ async function handleEditRuleSequence(args) {
|
|
|
116
122
|
}
|
|
117
123
|
|
|
118
124
|
try {
|
|
119
|
-
// Get access token
|
|
120
125
|
const accessToken = await ensureAuthenticated();
|
|
121
|
-
|
|
122
|
-
// Get all rules
|
|
123
126
|
const rules = await getInboxRules(accessToken);
|
|
124
127
|
|
|
125
|
-
// Find the rule by name
|
|
126
128
|
const rule = rules.find((r) => r.displayName === ruleName);
|
|
127
129
|
if (!rule) {
|
|
128
130
|
return {
|
|
@@ -135,14 +137,11 @@ async function handleEditRuleSequence(args) {
|
|
|
135
137
|
};
|
|
136
138
|
}
|
|
137
139
|
|
|
138
|
-
|
|
139
|
-
const _updateResult = await callGraphAPI(
|
|
140
|
+
await callGraphAPI(
|
|
140
141
|
accessToken,
|
|
141
142
|
'PATCH',
|
|
142
143
|
`me/mailFolders/inbox/messageRules/${rule.id}`,
|
|
143
|
-
{
|
|
144
|
-
sequence: sequence,
|
|
145
|
-
}
|
|
144
|
+
{ sequence }
|
|
146
145
|
);
|
|
147
146
|
|
|
148
147
|
return {
|
|
@@ -181,7 +180,7 @@ const rulesTools = [
|
|
|
181
180
|
{
|
|
182
181
|
name: 'manage-rules',
|
|
183
182
|
description:
|
|
184
|
-
'Manage inbox rules. action=list (default) lists rules. action=create creates a new rule. action=reorder changes rule execution priority. action=delete removes a rule.',
|
|
183
|
+
'Manage inbox rules. action=list (default) lists rules. action=create creates a new rule with conditions, actions, and optional exceptions. action=update modifies an existing rule. action=reorder changes rule execution priority. action=delete removes a rule.',
|
|
185
184
|
annotations: {
|
|
186
185
|
title: 'Inbox Rules',
|
|
187
186
|
readOnlyHint: false,
|
|
@@ -193,59 +192,190 @@ const rulesTools = [
|
|
|
193
192
|
properties: {
|
|
194
193
|
action: {
|
|
195
194
|
type: 'string',
|
|
196
|
-
enum: ['list', 'create', 'reorder', 'delete'],
|
|
195
|
+
enum: ['list', 'create', 'update', 'reorder', 'delete'],
|
|
197
196
|
description: 'Action to perform (default: list)',
|
|
198
197
|
},
|
|
199
|
-
|
|
198
|
+
|
|
199
|
+
// === List params ===
|
|
200
200
|
includeDetails: {
|
|
201
201
|
type: 'boolean',
|
|
202
202
|
description:
|
|
203
|
-
'Include detailed
|
|
203
|
+
'Include detailed conditions, actions, and exceptions (action=list)',
|
|
204
204
|
},
|
|
205
|
-
|
|
205
|
+
|
|
206
|
+
// === Create + Update shared params ===
|
|
206
207
|
name: {
|
|
207
208
|
type: 'string',
|
|
208
|
-
description:
|
|
209
|
+
description:
|
|
210
|
+
'Rule name (action=create required, action=update to rename)',
|
|
211
|
+
},
|
|
212
|
+
dryRun: {
|
|
213
|
+
type: 'boolean',
|
|
214
|
+
description:
|
|
215
|
+
'Preview rule without creating/updating (action=create, action=update)',
|
|
209
216
|
},
|
|
217
|
+
isEnabled: {
|
|
218
|
+
type: 'boolean',
|
|
219
|
+
description:
|
|
220
|
+
'Enable/disable rule (action=create default: true, action=update)',
|
|
221
|
+
},
|
|
222
|
+
sequence: {
|
|
223
|
+
type: 'number',
|
|
224
|
+
description:
|
|
225
|
+
'Execution order, lower = higher priority (action=create default: auto, action=reorder required)',
|
|
226
|
+
},
|
|
227
|
+
|
|
228
|
+
// --- Conditions (comma-separated strings become OR arrays) ---
|
|
210
229
|
fromAddresses: {
|
|
211
230
|
type: 'string',
|
|
212
|
-
description:
|
|
231
|
+
description:
|
|
232
|
+
'Comma-separated sender emails to match (action=create/update)',
|
|
213
233
|
},
|
|
214
234
|
containsSubject: {
|
|
215
235
|
type: 'string',
|
|
216
|
-
description:
|
|
236
|
+
description:
|
|
237
|
+
'Comma-separated subject keywords (OR logic). e.g. "invoice, receipt, payment" (action=create/update)',
|
|
238
|
+
},
|
|
239
|
+
bodyContains: {
|
|
240
|
+
type: 'string',
|
|
241
|
+
description:
|
|
242
|
+
'Comma-separated body text keywords (OR logic) (action=create/update)',
|
|
243
|
+
},
|
|
244
|
+
bodyOrSubjectContains: {
|
|
245
|
+
type: 'string',
|
|
246
|
+
description:
|
|
247
|
+
'Comma-separated keywords matching body OR subject (OR logic) (action=create/update)',
|
|
248
|
+
},
|
|
249
|
+
senderContains: {
|
|
250
|
+
type: 'string',
|
|
251
|
+
description:
|
|
252
|
+
'Comma-separated partial sender matches (action=create/update)',
|
|
253
|
+
},
|
|
254
|
+
recipientContains: {
|
|
255
|
+
type: 'string',
|
|
256
|
+
description:
|
|
257
|
+
'Comma-separated partial recipient matches (action=create/update)',
|
|
258
|
+
},
|
|
259
|
+
sentToAddresses: {
|
|
260
|
+
type: 'string',
|
|
261
|
+
description:
|
|
262
|
+
'Comma-separated recipient emails to match (action=create/update)',
|
|
217
263
|
},
|
|
218
264
|
hasAttachments: {
|
|
219
265
|
type: 'boolean',
|
|
220
|
-
description: '
|
|
266
|
+
description: 'Match emails with attachments (action=create/update)',
|
|
267
|
+
},
|
|
268
|
+
importance: {
|
|
269
|
+
type: 'string',
|
|
270
|
+
enum: ['low', 'normal', 'high'],
|
|
271
|
+
description:
|
|
272
|
+
'Match emails with this importance (action=create/update)',
|
|
221
273
|
},
|
|
274
|
+
sensitivity: {
|
|
275
|
+
type: 'string',
|
|
276
|
+
enum: ['normal', 'personal', 'private', 'confidential'],
|
|
277
|
+
description:
|
|
278
|
+
'Match emails with this sensitivity (action=create/update)',
|
|
279
|
+
},
|
|
280
|
+
sentToMe: {
|
|
281
|
+
type: 'boolean',
|
|
282
|
+
description: 'Match emails sent to me (action=create/update)',
|
|
283
|
+
},
|
|
284
|
+
sentOnlyToMe: {
|
|
285
|
+
type: 'boolean',
|
|
286
|
+
description:
|
|
287
|
+
'Match emails where I am the only recipient (action=create/update)',
|
|
288
|
+
},
|
|
289
|
+
sentCcMe: {
|
|
290
|
+
type: 'boolean',
|
|
291
|
+
description: 'Match emails where I am in CC (action=create/update)',
|
|
292
|
+
},
|
|
293
|
+
isAutomaticReply: {
|
|
294
|
+
type: 'boolean',
|
|
295
|
+
description: 'Match automatic reply emails (action=create/update)',
|
|
296
|
+
},
|
|
297
|
+
|
|
298
|
+
// --- Actions ---
|
|
222
299
|
moveToFolder: {
|
|
223
300
|
type: 'string',
|
|
224
|
-
description:
|
|
301
|
+
description:
|
|
302
|
+
'Folder name to move matching emails to (action=create/update)',
|
|
303
|
+
},
|
|
304
|
+
copyToFolder: {
|
|
305
|
+
type: 'string',
|
|
306
|
+
description:
|
|
307
|
+
'Folder name to copy matching emails to (action=create/update)',
|
|
225
308
|
},
|
|
226
309
|
markAsRead: {
|
|
227
310
|
type: 'boolean',
|
|
228
|
-
description: 'Mark matching emails as read (action=create)',
|
|
311
|
+
description: 'Mark matching emails as read (action=create/update)',
|
|
229
312
|
},
|
|
230
|
-
|
|
313
|
+
markImportance: {
|
|
314
|
+
type: 'string',
|
|
315
|
+
enum: ['low', 'normal', 'high'],
|
|
316
|
+
description:
|
|
317
|
+
'Set importance on matching emails (action=create/update)',
|
|
318
|
+
},
|
|
319
|
+
forwardTo: {
|
|
320
|
+
type: 'string',
|
|
321
|
+
description:
|
|
322
|
+
'Comma-separated emails to forward matching messages to (action=create/update)',
|
|
323
|
+
},
|
|
324
|
+
redirectTo: {
|
|
325
|
+
type: 'string',
|
|
326
|
+
description:
|
|
327
|
+
'Comma-separated emails to redirect matching messages to (action=create/update)',
|
|
328
|
+
},
|
|
329
|
+
assignCategories: {
|
|
330
|
+
type: 'string',
|
|
331
|
+
description:
|
|
332
|
+
'Comma-separated Outlook categories to assign (action=create/update)',
|
|
333
|
+
},
|
|
334
|
+
stopProcessingRules: {
|
|
231
335
|
type: 'boolean',
|
|
232
336
|
description:
|
|
233
|
-
'
|
|
337
|
+
'Stop evaluating subsequent rules (action=create/update)',
|
|
234
338
|
},
|
|
235
|
-
|
|
236
|
-
type: '
|
|
339
|
+
deleteMessage: {
|
|
340
|
+
type: 'boolean',
|
|
237
341
|
description:
|
|
238
|
-
'
|
|
342
|
+
'Move matching emails to Deleted Items (action=create/update)',
|
|
239
343
|
},
|
|
240
|
-
|
|
241
|
-
|
|
344
|
+
|
|
345
|
+
// --- Exceptions (rule skipped when these match) ---
|
|
346
|
+
exceptFromAddresses: {
|
|
347
|
+
type: 'string',
|
|
348
|
+
description:
|
|
349
|
+
'Comma-separated sender emails to exclude (action=create/update)',
|
|
350
|
+
},
|
|
351
|
+
exceptSubjectContains: {
|
|
352
|
+
type: 'string',
|
|
353
|
+
description:
|
|
354
|
+
'Comma-separated subject keywords to exclude (action=create/update)',
|
|
355
|
+
},
|
|
356
|
+
exceptSenderContains: {
|
|
357
|
+
type: 'string',
|
|
358
|
+
description:
|
|
359
|
+
'Comma-separated partial sender matches to exclude (action=create/update)',
|
|
360
|
+
},
|
|
361
|
+
exceptBodyContains: {
|
|
242
362
|
type: 'string',
|
|
243
363
|
description:
|
|
244
|
-
'
|
|
364
|
+
'Comma-separated body keywords to exclude (action=create/update)',
|
|
365
|
+
},
|
|
366
|
+
exceptHasAttachments: {
|
|
367
|
+
type: 'boolean',
|
|
368
|
+
description: 'Exclude emails with attachments (action=create/update)',
|
|
369
|
+
},
|
|
370
|
+
|
|
371
|
+
// === Update + Reorder + Delete params ===
|
|
372
|
+
ruleName: {
|
|
373
|
+
type: 'string',
|
|
374
|
+
description: 'Name of existing rule (action=update/reorder/delete)',
|
|
245
375
|
},
|
|
246
376
|
ruleId: {
|
|
247
377
|
type: 'string',
|
|
248
|
-
description: 'ID of
|
|
378
|
+
description: 'ID of existing rule (action=update/delete)',
|
|
249
379
|
},
|
|
250
380
|
},
|
|
251
381
|
required: [],
|
|
@@ -255,6 +385,8 @@ const rulesTools = [
|
|
|
255
385
|
switch (action) {
|
|
256
386
|
case 'create':
|
|
257
387
|
return handleCreateRule(args);
|
|
388
|
+
case 'update':
|
|
389
|
+
return handleUpdateRule(args);
|
|
258
390
|
case 'reorder':
|
|
259
391
|
return handleEditRuleSequence(args);
|
|
260
392
|
case 'delete':
|
|
@@ -271,6 +403,7 @@ module.exports = {
|
|
|
271
403
|
rulesTools,
|
|
272
404
|
handleListRules,
|
|
273
405
|
handleCreateRule,
|
|
406
|
+
handleUpdateRule,
|
|
274
407
|
handleEditRuleSequence,
|
|
275
408
|
handleDeleteRule,
|
|
276
409
|
};
|
package/rules/list.js
CHANGED
|
@@ -13,13 +13,8 @@ async function handleListRules(args) {
|
|
|
13
13
|
const includeDetails = args.includeDetails === true;
|
|
14
14
|
|
|
15
15
|
try {
|
|
16
|
-
// Get access token
|
|
17
16
|
const accessToken = await ensureAuthenticated();
|
|
18
|
-
|
|
19
|
-
// Get all inbox rules
|
|
20
17
|
const rules = await getInboxRules(accessToken);
|
|
21
|
-
|
|
22
|
-
// Format the rules based on detail level
|
|
23
18
|
const formattedRules = formatRulesList(rules, includeDetails);
|
|
24
19
|
|
|
25
20
|
return {
|
|
@@ -36,7 +31,7 @@ async function handleListRules(args) {
|
|
|
36
31
|
content: [
|
|
37
32
|
{
|
|
38
33
|
type: 'text',
|
|
39
|
-
text: "Authentication required. Please use the '
|
|
34
|
+
text: "Authentication required. Please use the 'auth' tool with action=authenticate first.",
|
|
40
35
|
},
|
|
41
36
|
],
|
|
42
37
|
};
|
|
@@ -82,45 +77,43 @@ async function getInboxRules(accessToken) {
|
|
|
82
77
|
*/
|
|
83
78
|
function formatRulesList(rules, includeDetails) {
|
|
84
79
|
if (!rules || rules.length === 0) {
|
|
85
|
-
return
|
|
80
|
+
return 'No inbox rules found.\n\nTip: You can create rules using manage-rules with action=create. Rules are processed in order of their sequence number (lower numbers are processed first).';
|
|
86
81
|
}
|
|
87
82
|
|
|
88
|
-
// Sort rules by sequence to show execution order
|
|
89
83
|
const sortedRules = [...rules].sort((a, b) => {
|
|
90
84
|
return (a.sequence || 9999) - (b.sequence || 9999);
|
|
91
85
|
});
|
|
92
86
|
|
|
93
|
-
// Format rules based on detail level
|
|
94
87
|
if (includeDetails) {
|
|
95
|
-
// Detailed format
|
|
96
88
|
const detailedRules = sortedRules.map((rule) => {
|
|
97
|
-
// Format rule header with sequence
|
|
98
89
|
let ruleText = `[${rule.sequence || 'N/A'}] ${rule.displayName}${rule.isEnabled ? '' : ' (Disabled)'}`;
|
|
99
90
|
|
|
100
|
-
// Format conditions
|
|
101
91
|
const conditions = formatRuleConditions(rule);
|
|
102
92
|
if (conditions) {
|
|
103
93
|
ruleText += `\n Conditions: ${conditions}`;
|
|
104
94
|
}
|
|
105
95
|
|
|
106
|
-
// Format actions
|
|
107
96
|
const actions = formatRuleActions(rule);
|
|
108
97
|
if (actions) {
|
|
109
98
|
ruleText += `\n Actions: ${actions}`;
|
|
110
99
|
}
|
|
111
100
|
|
|
101
|
+
const exceptions = formatRuleExceptions(rule);
|
|
102
|
+
if (exceptions) {
|
|
103
|
+
ruleText += `\n Exceptions: ${exceptions}`;
|
|
104
|
+
}
|
|
105
|
+
|
|
112
106
|
return ruleText;
|
|
113
107
|
});
|
|
114
108
|
|
|
115
109
|
return `Found ${rules.length} inbox rules (sorted by execution order):\n\n${detailedRules.join('\n\n')}\n\nRules are processed in order of their sequence number. You can change rule order using manage-rules with action=reorder.`;
|
|
116
|
-
} else {
|
|
117
|
-
// Simple format
|
|
118
|
-
const simpleRules = sortedRules.map((rule) => {
|
|
119
|
-
return `[${rule.sequence || 'N/A'}] ${rule.displayName}${rule.isEnabled ? '' : ' (Disabled)'}`;
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
return `Found ${rules.length} inbox rules (sorted by execution order):\n\n${simpleRules.join('\n')}\n\nTip: Use 'list-rules with includeDetails=true' to see more information about each rule.`;
|
|
123
110
|
}
|
|
111
|
+
|
|
112
|
+
const simpleRules = sortedRules.map((rule) => {
|
|
113
|
+
return `[${rule.sequence || 'N/A'}] ${rule.displayName}${rule.isEnabled ? '' : ' (Disabled)'}`;
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
return `Found ${rules.length} inbox rules (sorted by execution order):\n\n${simpleRules.join('\n')}\n\nTip: Use manage-rules with includeDetails=true to see conditions, actions, and exceptions for each rule.`;
|
|
124
117
|
}
|
|
125
118
|
|
|
126
119
|
/**
|
|
@@ -130,37 +123,76 @@ function formatRulesList(rules, includeDetails) {
|
|
|
130
123
|
*/
|
|
131
124
|
function formatRuleConditions(rule) {
|
|
132
125
|
const conditions = [];
|
|
126
|
+
const c = rule.conditions;
|
|
127
|
+
if (!c) return '';
|
|
133
128
|
|
|
134
|
-
//
|
|
135
|
-
if (
|
|
136
|
-
const senders =
|
|
137
|
-
.map((addr) => addr.emailAddress
|
|
129
|
+
// Recipient-based
|
|
130
|
+
if (c.fromAddresses?.length > 0) {
|
|
131
|
+
const senders = c.fromAddresses
|
|
132
|
+
.map((addr) => addr.emailAddress?.address)
|
|
138
133
|
.join(', ');
|
|
139
134
|
conditions.push(`From: ${senders}`);
|
|
140
135
|
}
|
|
136
|
+
if (c.sentToAddresses?.length > 0) {
|
|
137
|
+
const recipients = c.sentToAddresses
|
|
138
|
+
.map((addr) => addr.emailAddress?.address)
|
|
139
|
+
.join(', ');
|
|
140
|
+
conditions.push(`Sent to: ${recipients}`);
|
|
141
|
+
}
|
|
141
142
|
|
|
142
|
-
//
|
|
143
|
-
if (
|
|
143
|
+
// String collections
|
|
144
|
+
if (c.subjectContains?.length > 0) {
|
|
145
|
+
conditions.push(`Subject contains: "${c.subjectContains.join('", "')}"`);
|
|
146
|
+
}
|
|
147
|
+
if (c.bodyContains?.length > 0) {
|
|
148
|
+
conditions.push(`Body contains: "${c.bodyContains.join('", "')}"`);
|
|
149
|
+
}
|
|
150
|
+
if (c.bodyOrSubjectContains?.length > 0) {
|
|
144
151
|
conditions.push(
|
|
145
|
-
`
|
|
152
|
+
`Body/subject contains: "${c.bodyOrSubjectContains.join('", "')}"`
|
|
146
153
|
);
|
|
147
154
|
}
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
155
|
+
if (c.senderContains?.length > 0) {
|
|
156
|
+
conditions.push(`Sender contains: "${c.senderContains.join('", "')}"`);
|
|
157
|
+
}
|
|
158
|
+
if (c.recipientContains?.length > 0) {
|
|
151
159
|
conditions.push(
|
|
152
|
-
`
|
|
160
|
+
`Recipient contains: "${c.recipientContains.join('", "')}"`
|
|
153
161
|
);
|
|
154
162
|
}
|
|
163
|
+
if (c.headerContains?.length > 0) {
|
|
164
|
+
conditions.push(`Header contains: "${c.headerContains.join('", "')}"`);
|
|
165
|
+
}
|
|
155
166
|
|
|
156
|
-
//
|
|
157
|
-
if (
|
|
158
|
-
|
|
167
|
+
// Booleans
|
|
168
|
+
if (c.hasAttachment === true) conditions.push('Has attachment');
|
|
169
|
+
if (c.sentToMe === true) conditions.push('Sent to me');
|
|
170
|
+
if (c.sentOnlyToMe === true) conditions.push('Sent only to me');
|
|
171
|
+
if (c.sentCcMe === true) conditions.push('I am in CC');
|
|
172
|
+
if (c.sentToOrCcMe === true) conditions.push('Sent to me or CC');
|
|
173
|
+
if (c.isAutomaticReply === true) conditions.push('Is automatic reply');
|
|
174
|
+
if (c.isAutomaticForward === true) conditions.push('Is automatic forward');
|
|
175
|
+
if (c.isMeetingRequest === true) conditions.push('Is meeting request');
|
|
176
|
+
if (c.isMeetingResponse === true) conditions.push('Is meeting response');
|
|
177
|
+
if (c.isReadReceipt === true) conditions.push('Is read receipt');
|
|
178
|
+
if (c.isEncrypted === true) conditions.push('Is encrypted');
|
|
179
|
+
if (c.notSentToMe === true) conditions.push('Not sent to me');
|
|
180
|
+
|
|
181
|
+
// Enums
|
|
182
|
+
if (c.importance) conditions.push(`Importance: ${c.importance}`);
|
|
183
|
+
if (c.sensitivity) conditions.push(`Sensitivity: ${c.sensitivity}`);
|
|
184
|
+
if (c.messageActionFlag) conditions.push(`Flag: ${c.messageActionFlag}`);
|
|
185
|
+
|
|
186
|
+
// Categories
|
|
187
|
+
if (c.categories?.length > 0) {
|
|
188
|
+
conditions.push(`Categories: ${c.categories.join(', ')}`);
|
|
159
189
|
}
|
|
160
190
|
|
|
161
|
-
//
|
|
162
|
-
if (
|
|
163
|
-
|
|
191
|
+
// Size range
|
|
192
|
+
if (c.withinSizeRange) {
|
|
193
|
+
const min = c.withinSizeRange.minimumSize || 0;
|
|
194
|
+
const max = c.withinSizeRange.maximumSize || '∞';
|
|
195
|
+
conditions.push(`Size: ${min}–${max} bytes`);
|
|
164
196
|
}
|
|
165
197
|
|
|
166
198
|
return conditions.join('; ');
|
|
@@ -173,43 +205,93 @@ function formatRuleConditions(rule) {
|
|
|
173
205
|
*/
|
|
174
206
|
function formatRuleActions(rule) {
|
|
175
207
|
const actions = [];
|
|
208
|
+
const a = rule.actions;
|
|
209
|
+
if (!a) return '';
|
|
176
210
|
|
|
177
|
-
|
|
178
|
-
if (
|
|
179
|
-
|
|
180
|
-
}
|
|
211
|
+
if (a.moveToFolder) actions.push(`Move to folder: ${a.moveToFolder}`);
|
|
212
|
+
if (a.copyToFolder) actions.push(`Copy to folder: ${a.copyToFolder}`);
|
|
213
|
+
if (a.markAsRead === true) actions.push('Mark as read');
|
|
214
|
+
if (a.markImportance) actions.push(`Mark importance: ${a.markImportance}`);
|
|
181
215
|
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// Mark as read
|
|
188
|
-
if (rule.actions?.markAsRead === true) {
|
|
189
|
-
actions.push('Mark as read');
|
|
216
|
+
if (a.forwardTo?.length > 0) {
|
|
217
|
+
const recipients = a.forwardTo
|
|
218
|
+
.map((r) => r.emailAddress?.address)
|
|
219
|
+
.join(', ');
|
|
220
|
+
actions.push(`Forward to: ${recipients}`);
|
|
190
221
|
}
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
222
|
+
if (a.forwardAsAttachmentTo?.length > 0) {
|
|
223
|
+
const recipients = a.forwardAsAttachmentTo
|
|
224
|
+
.map((r) => r.emailAddress?.address)
|
|
225
|
+
.join(', ');
|
|
226
|
+
actions.push(`Forward as attachment to: ${recipients}`);
|
|
195
227
|
}
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
const recipients = rule.actions.forwardTo
|
|
200
|
-
.map((r) => r.emailAddress.address)
|
|
228
|
+
if (a.redirectTo?.length > 0) {
|
|
229
|
+
const recipients = a.redirectTo
|
|
230
|
+
.map((r) => r.emailAddress?.address)
|
|
201
231
|
.join(', ');
|
|
202
|
-
actions.push(`
|
|
232
|
+
actions.push(`Redirect to: ${recipients}`);
|
|
203
233
|
}
|
|
204
234
|
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
actions.push('Delete');
|
|
235
|
+
if (a.assignCategories?.length > 0) {
|
|
236
|
+
actions.push(`Assign categories: ${a.assignCategories.join(', ')}`);
|
|
208
237
|
}
|
|
238
|
+
if (a.stopProcessingRules === true) actions.push('Stop processing rules');
|
|
239
|
+
if (a.delete === true) actions.push('Delete (move to Deleted Items)');
|
|
240
|
+
if (a.permanentDelete === true) actions.push('Permanently delete');
|
|
209
241
|
|
|
210
242
|
return actions.join('; ');
|
|
211
243
|
}
|
|
212
244
|
|
|
245
|
+
/**
|
|
246
|
+
* Format rule exceptions for display
|
|
247
|
+
* @param {object} rule - Rule object
|
|
248
|
+
* @returns {string} - Formatted exceptions
|
|
249
|
+
*/
|
|
250
|
+
function formatRuleExceptions(rule) {
|
|
251
|
+
const parts = [];
|
|
252
|
+
const e = rule.exceptions;
|
|
253
|
+
if (!e) return '';
|
|
254
|
+
|
|
255
|
+
if (e.fromAddresses?.length > 0) {
|
|
256
|
+
const senders = e.fromAddresses
|
|
257
|
+
.map((addr) => addr.emailAddress?.address)
|
|
258
|
+
.join(', ');
|
|
259
|
+
parts.push(`From: ${senders}`);
|
|
260
|
+
}
|
|
261
|
+
if (e.sentToAddresses?.length > 0) {
|
|
262
|
+
const recipients = e.sentToAddresses
|
|
263
|
+
.map((addr) => addr.emailAddress?.address)
|
|
264
|
+
.join(', ');
|
|
265
|
+
parts.push(`Sent to: ${recipients}`);
|
|
266
|
+
}
|
|
267
|
+
if (e.subjectContains?.length > 0) {
|
|
268
|
+
parts.push(`Subject contains: "${e.subjectContains.join('", "')}"`);
|
|
269
|
+
}
|
|
270
|
+
if (e.bodyContains?.length > 0) {
|
|
271
|
+
parts.push(`Body contains: "${e.bodyContains.join('", "')}"`);
|
|
272
|
+
}
|
|
273
|
+
if (e.bodyOrSubjectContains?.length > 0) {
|
|
274
|
+
parts.push(
|
|
275
|
+
`Body/subject contains: "${e.bodyOrSubjectContains.join('", "')}"`
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
if (e.senderContains?.length > 0) {
|
|
279
|
+
parts.push(`Sender contains: "${e.senderContains.join('", "')}"`);
|
|
280
|
+
}
|
|
281
|
+
if (e.recipientContains?.length > 0) {
|
|
282
|
+
parts.push(`Recipient contains: "${e.recipientContains.join('", "')}"`);
|
|
283
|
+
}
|
|
284
|
+
if (e.hasAttachment === true) parts.push('Has attachment');
|
|
285
|
+
if (e.importance) parts.push(`Importance: ${e.importance}`);
|
|
286
|
+
if (e.sensitivity) parts.push(`Sensitivity: ${e.sensitivity}`);
|
|
287
|
+
if (e.sentToMe === true) parts.push('Sent to me');
|
|
288
|
+
if (e.sentOnlyToMe === true) parts.push('Sent only to me');
|
|
289
|
+
if (e.sentCcMe === true) parts.push('I am in CC');
|
|
290
|
+
if (e.isAutomaticReply === true) parts.push('Is automatic reply');
|
|
291
|
+
|
|
292
|
+
return parts.join('; ');
|
|
293
|
+
}
|
|
294
|
+
|
|
213
295
|
module.exports = {
|
|
214
296
|
handleListRules,
|
|
215
297
|
getInboxRules,
|