From fa62afa50eaed2fb591c545960069629d9ec6c8c Mon Sep 17 00:00:00 2001 From: Jordan Wages Date: Tue, 15 Jul 2025 22:38:53 -0500 Subject: [PATCH] Add delete and archive rule actions --- README.md | 6 +++--- _locales/en-US/messages.json | 4 +++- background.js | 4 ++++ options/options.js | 7 ++++++- 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index 4bcef29..1458d4e 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ message meets a specified criterion. - **Markdown conversion** – optionally convert HTML bodies to Markdown before sending them to the AI service. - **Debug logging** – optional colorized logs help troubleshoot interactions with the AI service. - **Light/Dark themes** – automatically match Thunderbird's appearance with optional manual override. -- **Automatic rules** – create rules that tag, move, copy, mark read/unread or flag/unflag messages based on AI classification. Rules can optionally apply only to unread messages. +- **Automatic rules** – create rules that tag, move, copy, delete, archive, mark read/unread or flag/unflag messages based on AI classification. Rules can optionally apply only to unread messages. - **Rule ordering** – drag rules to prioritize them and optionally stop processing after a match. - **Context menu** – apply AI rules from the message list or the message display action button. - **Status icons** – toolbar icons show when classification is in progress and briefly display success or error states. @@ -70,7 +70,7 @@ Sortana is implemented entirely with standard WebExtension scripts—no custom e 1. Open the add-on's options and set the URL of your classification service. 2. Use the **Classification Rules** section to add a criterion and optional - actions such as tagging or moving a message when it matches. Drag rules to + actions such as tagging, moving, copying, deleting or archiving a message when it matches. Drag rules to reorder them, check *Only apply to unread messages* to skip read mail, and check *Stop after match* to halt further processing. 3. Save your settings. New mail will be evaluated automatically using the @@ -102,7 +102,7 @@ Here are some useful and fun example criteria you can use in your filters. Filte For when you're ready to filter based on vibes. You can define as many filters as you'd like, each using a different prompt and -triggering tags, moves, copies, read/unread changes or flag updates based on the model's classification. +triggering tags, moves, copies, deletes, archives, read/unread changes or flag updates based on the model's classification. ## Required Permissions diff --git a/_locales/en-US/messages.json b/_locales/en-US/messages.json index 6fc5b61..7d8fa37 100644 --- a/_locales/en-US/messages.json +++ b/_locales/en-US/messages.json @@ -21,7 +21,9 @@ "options.collapseWhitespace": { "message": "Collapse long whitespace" } ,"action.read": { "message": "read" } ,"action.flag": { "message": "flag" } - ,"action.copy": { "message": "copy" } + ,"action.copy": { "message": "copy" } + ,"action.delete": { "message": "delete" } + ,"action.archive": { "message": "archive" } ,"param.markRead": { "message": "mark read" } ,"param.markUnread": { "message": "mark unread" } ,"param.flag": { "message": "flag" } diff --git a/background.js b/background.js index 8a26971..3309339 100644 --- a/background.js +++ b/background.js @@ -243,6 +243,10 @@ async function processMessage(id) { await messenger.messages.update(id, { read: !!act.read }); } else if (act.type === 'flag') { await messenger.messages.update(id, { flagged: !!act.flagged }); + } else if (act.type === 'delete') { + await messenger.messages.delete([id]); + } else if (act.type === 'archive') { + await messenger.messages.archive([id]); } } if (rule.stopProcessing) { diff --git a/options/options.js b/options/options.js index 7ec57ee..ce6c81b 100644 --- a/options/options.js +++ b/options/options.js @@ -171,7 +171,7 @@ document.addEventListener('DOMContentLoaded', async () => { const typeWrapper = document.createElement('div'); typeWrapper.className = 'select is-small mr-2'; const typeSelect = document.createElement('select'); - ['tag','move','copy','junk','read','flag'].forEach(t => { + ['tag','move','copy','junk','read','flag','delete','archive'].forEach(t => { const opt = document.createElement('option'); opt.value = t; opt.textContent = t; @@ -242,6 +242,8 @@ document.addEventListener('DOMContentLoaded', async () => { sel.value = String(action.flagged ?? true); wrap.appendChild(sel); paramSpan.appendChild(wrap); + } else if (typeSelect.value === 'delete' || typeSelect.value === 'archive') { + paramSpan.appendChild(document.createElement('span')); } } @@ -373,6 +375,9 @@ document.addEventListener('DOMContentLoaded', async () => { if (type === 'flag') { return { type, flagged: row.querySelector('.flag-select').value === 'true' }; } + if (type === 'delete' || type === 'archive') { + return { type }; + } return { type }; }); const stopProcessing = ruleEl.querySelector('.stop-processing')?.checked;