@littlebearapps/outlook-assistant 3.6.0 → 3.7.1
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 +1 -1
- 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 +6 -8
- package/email/folder-utils.js +1 -1
- package/email/headers.js +10 -10
- package/email/list.js +1 -1
- package/email/mime.js +13 -12
- package/email/search.js +280 -24
- package/email/send.js +1 -1
- package/folder/list.js +1 -1
- package/llms.txt +2 -1
- package/package.json +1 -1
- 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/graph-api.js +1 -1
- package/utils/response-formatter.js +8 -6
- package/utils/safety.js +113 -0
package/rules/create.js
CHANGED
|
@@ -3,8 +3,16 @@
|
|
|
3
3
|
*/
|
|
4
4
|
const { callGraphAPI } = require('../utils/graph-api');
|
|
5
5
|
const { ensureAuthenticated } = require('../auth');
|
|
6
|
-
const {
|
|
6
|
+
const { checkRateLimit } = require('../utils/safety');
|
|
7
|
+
const { formatRuleDryRunPreview } = require('../utils/safety');
|
|
7
8
|
const { getInboxRules } = require('./list');
|
|
9
|
+
const {
|
|
10
|
+
buildConditions,
|
|
11
|
+
buildActions,
|
|
12
|
+
buildExceptions,
|
|
13
|
+
hasAnyCondition,
|
|
14
|
+
hasAnyAction,
|
|
15
|
+
} = require('./rule-builder');
|
|
8
16
|
|
|
9
17
|
/**
|
|
10
18
|
* Create rule handler
|
|
@@ -12,18 +20,13 @@ const { getInboxRules } = require('./list');
|
|
|
12
20
|
* @returns {object} - MCP response
|
|
13
21
|
*/
|
|
14
22
|
async function handleCreateRule(args) {
|
|
15
|
-
const {
|
|
16
|
-
name,
|
|
17
|
-
fromAddresses,
|
|
18
|
-
containsSubject,
|
|
19
|
-
hasAttachments,
|
|
20
|
-
moveToFolder,
|
|
21
|
-
markAsRead,
|
|
22
|
-
isEnabled = true,
|
|
23
|
-
sequence,
|
|
24
|
-
} = args;
|
|
23
|
+
const { name, isEnabled = true, sequence, dryRun } = args;
|
|
25
24
|
|
|
26
|
-
//
|
|
25
|
+
// Rate limit rule creation
|
|
26
|
+
const rateLimitError = checkRateLimit('manage-rules');
|
|
27
|
+
if (rateLimitError) return rateLimitError;
|
|
28
|
+
|
|
29
|
+
// Validate sequence parameter
|
|
27
30
|
if (sequence !== undefined && (isNaN(sequence) || sequence < 1)) {
|
|
28
31
|
return {
|
|
29
32
|
content: [
|
|
@@ -46,202 +49,96 @@ async function handleCreateRule(args) {
|
|
|
46
49
|
};
|
|
47
50
|
}
|
|
48
51
|
|
|
49
|
-
|
|
50
|
-
const hasCondition =
|
|
51
|
-
fromAddresses || containsSubject || hasAttachments === true;
|
|
52
|
-
const hasAction = moveToFolder || markAsRead === true;
|
|
53
|
-
|
|
54
|
-
if (!hasCondition) {
|
|
52
|
+
if (!hasAnyCondition(args)) {
|
|
55
53
|
return {
|
|
56
54
|
content: [
|
|
57
55
|
{
|
|
58
56
|
type: 'text',
|
|
59
|
-
text: 'At least one condition is required.
|
|
57
|
+
text: 'At least one condition is required. Available conditions: fromAddresses, containsSubject, bodyContains, bodyOrSubjectContains, senderContains, recipientContains, sentToAddresses, hasAttachments, importance, sensitivity, sentToMe, sentOnlyToMe, sentCcMe, isAutomaticReply.',
|
|
60
58
|
},
|
|
61
59
|
],
|
|
62
60
|
};
|
|
63
61
|
}
|
|
64
62
|
|
|
65
|
-
if (!
|
|
63
|
+
if (!hasAnyAction(args)) {
|
|
66
64
|
return {
|
|
67
65
|
content: [
|
|
68
66
|
{
|
|
69
67
|
type: 'text',
|
|
70
|
-
text: 'At least one action is required.
|
|
68
|
+
text: 'At least one action is required. Available actions: moveToFolder, copyToFolder, markAsRead, markImportance, forwardTo, redirectTo, assignCategories, stopProcessingRules, deleteMessage.',
|
|
71
69
|
},
|
|
72
70
|
],
|
|
73
71
|
};
|
|
74
72
|
}
|
|
75
73
|
|
|
76
74
|
try {
|
|
77
|
-
// Get access token
|
|
78
75
|
const accessToken = await ensureAuthenticated();
|
|
79
76
|
|
|
80
|
-
//
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
markAsRead,
|
|
88
|
-
isEnabled,
|
|
89
|
-
sequence,
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
let responseText = result.message;
|
|
77
|
+
// Build rule components
|
|
78
|
+
const { conditions, warnings: condWarnings } = buildConditions(args);
|
|
79
|
+
const { actions, warnings: actWarnings } = await buildActions(
|
|
80
|
+
args,
|
|
81
|
+
accessToken
|
|
82
|
+
);
|
|
83
|
+
const { exceptions, warnings: excWarnings } = buildExceptions(args);
|
|
93
84
|
|
|
94
|
-
|
|
95
|
-
if (!sequence && !result.error) {
|
|
96
|
-
responseText +=
|
|
97
|
-
"\n\nTip: You can specify a 'sequence' parameter when creating rules to control their execution order. Lower sequence numbers run first.";
|
|
98
|
-
}
|
|
85
|
+
const allWarnings = [...condWarnings, ...actWarnings, ...excWarnings];
|
|
99
86
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
type: 'text',
|
|
104
|
-
text: responseText,
|
|
105
|
-
},
|
|
106
|
-
],
|
|
107
|
-
};
|
|
108
|
-
} catch (error) {
|
|
109
|
-
if (error.message === 'Authentication required') {
|
|
87
|
+
// Check for fatal warnings (folder not found = no valid action)
|
|
88
|
+
const folderNotFound = actWarnings.some((w) => w.includes('not found'));
|
|
89
|
+
if (folderNotFound && Object.keys(actions).length === 0) {
|
|
110
90
|
return {
|
|
111
91
|
content: [
|
|
112
92
|
{
|
|
113
93
|
type: 'text',
|
|
114
|
-
text:
|
|
94
|
+
text: actWarnings.filter((w) => w.includes('not found')).join('\n'),
|
|
115
95
|
},
|
|
116
96
|
],
|
|
117
97
|
};
|
|
118
98
|
}
|
|
119
99
|
|
|
120
|
-
|
|
121
|
-
content: [
|
|
122
|
-
{
|
|
123
|
-
type: 'text',
|
|
124
|
-
text: `Error creating rule: ${error.message}`,
|
|
125
|
-
},
|
|
126
|
-
],
|
|
127
|
-
};
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Create a new inbox rule
|
|
133
|
-
* @param {string} accessToken - Access token
|
|
134
|
-
* @param {object} ruleOptions - Rule creation options
|
|
135
|
-
* @returns {Promise<object>} - Result object with status and message
|
|
136
|
-
*/
|
|
137
|
-
async function createInboxRule(accessToken, ruleOptions) {
|
|
138
|
-
try {
|
|
139
|
-
const {
|
|
140
|
-
name,
|
|
141
|
-
fromAddresses,
|
|
142
|
-
containsSubject,
|
|
143
|
-
hasAttachments,
|
|
144
|
-
moveToFolder,
|
|
145
|
-
markAsRead,
|
|
146
|
-
isEnabled,
|
|
147
|
-
sequence,
|
|
148
|
-
} = ruleOptions;
|
|
149
|
-
|
|
150
|
-
// Get existing rules to determine sequence if not provided
|
|
100
|
+
// Determine sequence
|
|
151
101
|
let ruleSequence = sequence;
|
|
152
102
|
if (!ruleSequence) {
|
|
103
|
+
ruleSequence = 100;
|
|
153
104
|
try {
|
|
154
|
-
// Default to 100 if we can't get existing rules
|
|
155
|
-
ruleSequence = 100;
|
|
156
|
-
|
|
157
|
-
// Get existing rules to find highest sequence
|
|
158
105
|
const existingRules = await getInboxRules(accessToken);
|
|
159
106
|
if (existingRules && existingRules.length > 0) {
|
|
160
|
-
// Find the highest sequence
|
|
161
107
|
const highestSequence = Math.max(
|
|
162
108
|
...existingRules.map((r) => r.sequence || 0)
|
|
163
109
|
);
|
|
164
|
-
// Set new rule sequence to be higher
|
|
165
110
|
ruleSequence = Math.max(highestSequence + 1, 100);
|
|
166
|
-
console.error(
|
|
167
|
-
`Auto-generated sequence: ${ruleSequence} (based on highest existing: ${highestSequence})`
|
|
168
|
-
);
|
|
169
111
|
}
|
|
170
|
-
} catch (
|
|
171
|
-
console.error(
|
|
172
|
-
`Error determining rule sequence: ${sequenceError.message}`
|
|
173
|
-
);
|
|
174
|
-
// Fall back to default value
|
|
112
|
+
} catch (_sequenceError) {
|
|
175
113
|
ruleSequence = 100;
|
|
176
114
|
}
|
|
177
115
|
}
|
|
178
|
-
|
|
179
|
-
console.error(`Using rule sequence: ${ruleSequence}`);
|
|
180
|
-
|
|
181
|
-
// Make sure sequence is a positive integer
|
|
182
116
|
ruleSequence = Math.max(1, Math.floor(ruleSequence));
|
|
183
117
|
|
|
184
|
-
// Build rule object
|
|
118
|
+
// Build the rule object
|
|
185
119
|
const rule = {
|
|
186
120
|
displayName: name,
|
|
187
121
|
isEnabled: isEnabled === true,
|
|
188
122
|
sequence: ruleSequence,
|
|
189
|
-
conditions
|
|
190
|
-
actions
|
|
123
|
+
conditions,
|
|
124
|
+
actions,
|
|
191
125
|
};
|
|
192
126
|
|
|
193
|
-
// Add
|
|
194
|
-
if (
|
|
195
|
-
|
|
196
|
-
const emailAddresses = fromAddresses
|
|
197
|
-
.split(',')
|
|
198
|
-
.map((email) => email.trim())
|
|
199
|
-
.filter((email) => email)
|
|
200
|
-
.map((email) => ({
|
|
201
|
-
emailAddress: {
|
|
202
|
-
address: email,
|
|
203
|
-
},
|
|
204
|
-
}));
|
|
205
|
-
|
|
206
|
-
if (emailAddresses.length > 0) {
|
|
207
|
-
rule.conditions.fromAddresses = emailAddresses;
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
if (containsSubject) {
|
|
212
|
-
rule.conditions.subjectContains = [containsSubject];
|
|
213
|
-
}
|
|
214
|
-
|
|
215
|
-
if (hasAttachments === true) {
|
|
216
|
-
rule.conditions.hasAttachment = true;
|
|
127
|
+
// Add exceptions if any were specified
|
|
128
|
+
if (Object.keys(exceptions).length > 0) {
|
|
129
|
+
rule.exceptions = exceptions;
|
|
217
130
|
}
|
|
218
131
|
|
|
219
|
-
//
|
|
220
|
-
if (
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
return {
|
|
226
|
-
success: false,
|
|
227
|
-
message: `Target folder "${moveToFolder}" not found. Please specify a valid folder name.`,
|
|
228
|
-
};
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
rule.actions.moveToFolder = folderId;
|
|
232
|
-
} catch (folderError) {
|
|
233
|
-
console.error(
|
|
234
|
-
`Error resolving folder "${moveToFolder}": ${folderError.message}`
|
|
235
|
-
);
|
|
236
|
-
return {
|
|
237
|
-
success: false,
|
|
238
|
-
message: `Error resolving folder "${moveToFolder}": ${folderError.message}`,
|
|
239
|
-
};
|
|
132
|
+
// Dry-run: preview without creating
|
|
133
|
+
if (dryRun) {
|
|
134
|
+
const preview = formatRuleDryRunPreview(rule);
|
|
135
|
+
let text = `DRY RUN — Rule preview (not created):\n\n${preview}`;
|
|
136
|
+
if (allWarnings.length > 0) {
|
|
137
|
+
text += `\n\nWarnings:\n${allWarnings.map((w) => `- ${w}`).join('\n')}`;
|
|
240
138
|
}
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
rule.actions.markAsRead = true;
|
|
139
|
+
return {
|
|
140
|
+
content: [{ type: 'text', text }],
|
|
141
|
+
};
|
|
245
142
|
}
|
|
246
143
|
|
|
247
144
|
// Create the rule
|
|
@@ -253,20 +150,47 @@ async function createInboxRule(accessToken, ruleOptions) {
|
|
|
253
150
|
);
|
|
254
151
|
|
|
255
152
|
if (response && response.id) {
|
|
153
|
+
let text = `Successfully created rule "${name}" with sequence ${ruleSequence}.`;
|
|
154
|
+
if (allWarnings.length > 0) {
|
|
155
|
+
text += `\n\nNotes:\n${allWarnings.map((w) => `- ${w}`).join('\n')}`;
|
|
156
|
+
}
|
|
157
|
+
if (!sequence) {
|
|
158
|
+
text +=
|
|
159
|
+
"\n\nTip: You can specify a 'sequence' parameter when creating rules to control their execution order. Lower sequence numbers run first.";
|
|
160
|
+
}
|
|
256
161
|
return {
|
|
257
|
-
|
|
258
|
-
message: `Successfully created rule "${name}" with sequence ${ruleSequence}.`,
|
|
259
|
-
ruleId: response.id,
|
|
162
|
+
content: [{ type: 'text', text }],
|
|
260
163
|
};
|
|
261
|
-
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
return {
|
|
167
|
+
content: [
|
|
168
|
+
{
|
|
169
|
+
type: 'text',
|
|
170
|
+
text: "Failed to create rule. The server didn't return a rule ID.",
|
|
171
|
+
},
|
|
172
|
+
],
|
|
173
|
+
};
|
|
174
|
+
} catch (error) {
|
|
175
|
+
if (error.message === 'Authentication required') {
|
|
262
176
|
return {
|
|
263
|
-
|
|
264
|
-
|
|
177
|
+
content: [
|
|
178
|
+
{
|
|
179
|
+
type: 'text',
|
|
180
|
+
text: "Authentication required. Please use the 'auth' tool with action=authenticate first.",
|
|
181
|
+
},
|
|
182
|
+
],
|
|
265
183
|
};
|
|
266
184
|
}
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
185
|
+
|
|
186
|
+
return {
|
|
187
|
+
content: [
|
|
188
|
+
{
|
|
189
|
+
type: 'text',
|
|
190
|
+
text: `Error creating rule: ${error.message}`,
|
|
191
|
+
},
|
|
192
|
+
],
|
|
193
|
+
};
|
|
270
194
|
}
|
|
271
195
|
}
|
|
272
196
|
|
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
|
};
|