diff --git a/_locales/en/messages.json b/_locales/en/messages.json
index ea758ff0..c325d267 100644
--- a/_locales/en/messages.json
+++ b/_locales/en/messages.json
@@ -1099,12 +1099,56 @@
"message": "If the value returned by the AI is above this threshold, the email will be moved to the spam folder.",
"description": ""
},
- "spam_threshold_too_low": {
+ "spamfilter_threshold_too_low": {
"message": "The spam threshold is too low! You will likely mark too much mails as spam!",
"description": ""
},
- "spam_threshold_zero": {
+ "spamfilter_threshold_zero": {
"message": "The spam threshold is zero! You will mark all mails as spam!",
"description": ""
+ },
+ "spamfilter_no_reports": {
+ "message": "No messages have screened for spam yet. Here you'll find a list of the last 100 spam reports for the current session only.",
+ "description": ""
+ },
+ "SpamReport_Title": {
+ "message": "Spam Filter Reports",
+ "description": ""
+ },
+ "Date": {
+ "message": "Date",
+ "description": ""
+ },
+ "From": {
+ "message": "From",
+ "description": ""
+ },
+ "Subject": {
+ "message": "Subject",
+ "description": ""
+ },
+ "Spam_Value": {
+ "message": "Spam Value",
+ "description": ""
+ },
+ "Moved_to_Spam": {
+ "message": "Moved to Spam",
+ "description": ""
+ },
+ "Explanation": {
+ "message": "Explanation",
+ "description": ""
+ },
+ "Report_Date": {
+ "message": "Report Date",
+ "description": ""
+ },
+ "spamfilter_moved": {
+ "message": "Yes",
+ "description": ""
+ },
+ "spamfilter_not_moved": {
+ "message": "No",
+ "description": ""
}
}
\ No newline at end of file
diff --git a/js/mzta-spamreport.js b/js/mzta-spamreport.js
new file mode 100644
index 00000000..308dad7b
--- /dev/null
+++ b/js/mzta-spamreport.js
@@ -0,0 +1,91 @@
+/*
+ * ThunderAI [https://micz.it/thunderbird-addon-thunderai/]
+ * Copyright (C) 2024 - 2025 Mic (m@micz.it)
+
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+
+export const taSpamReport = {
+
+ logger: console,
+ _internal_data_id: 'mzta-spam-report-data',
+ _max_reports: 100,
+
+ async saveReportData(data, data_id){
+ let obj = await browser.storage.session.get(taSpamReport._internal_data_id);
+ //console.log(">>>>>>>> saveReportData obj 1: " + JSON.stringify(obj));
+ if(obj == undefined){
+ obj = {};
+ obj[taSpamReport._internal_data_id] = {};
+ //console.log(">>>>>>>> saveReportData obj 2a: " + JSON.stringify(obj));
+ }
+ if(Object.keys(obj).length === 0){
+ obj[taSpamReport._internal_data_id] = {};
+ //console.log(">>>>>>>> saveReportData obj 2b: " + JSON.stringify(obj));
+ }
+
+ obj[taSpamReport._internal_data_id][data_id] = data;
+ //console.log(">>>>>>>> saveReportData obj 3: " + JSON.stringify(obj));
+ await browser.storage.session.set(obj);
+ },
+
+ async loadReportData(data_id){
+ let output = await browser.storage.session.get(taSpamReport._internal_data_id);
+ return output[taSpamReport._internal_data_id][data_id];
+ },
+
+ async getAllReportData(){
+ let data = await browser.storage.session.get(taSpamReport._internal_data_id);
+ //console.log(">>>>>>>>>>> getAllReportData: " + JSON.stringify(data));
+ return data[taSpamReport._internal_data_id];
+ },
+
+ saveAllData(data){
+ browser.storage.session.set({[taSpamReport._internal_data_id]: data});
+ },
+
+ clearReportData(){
+ browser.storage.session.clear();
+ },
+
+ async truncReportData(){
+ let data = taSpamReport.sortReportsByDate(await taSpamReport.getAllReportData());
+ if(data == undefined) return;
+ //console.log(">>>>>>>>>> truncReportData: " + JSON.stringify(data));
+ if(Object.keys(data).length > taSpamReport._max_reports){
+ let keys = Object.keys(data);
+ for(let i = taSpamReport._max_reports; i < keys.length; i++){
+ // console.log(">>>>>>>>>> truncReportData: " + keys[i]);
+ // console.log(">>>>>>>>>> truncReportData i: " + i);
+ delete data[keys[i]];
+ }
+ }
+ taSpamReport.saveAllData(data);
+ },
+
+ sortReportsByDate(data) {
+ if(data == undefined) return undefined;
+ const reportKeys = Object.keys(data);
+ reportKeys.sort((a, b) => {
+ const dateA = new Date(data[a].report_date);
+ const dateB = new Date(data[b].report_date);
+ return dateB - dateA;
+ });
+ const sortedReports = {};
+ reportKeys.forEach((key) => {
+ sortedReports[key] = data[key];
+ });
+ return sortedReports;
+ }
+}
\ No newline at end of file
diff --git a/mzta-background.js b/mzta-background.js
index 97fa7649..fe4e6878 100644
--- a/mzta-background.js
+++ b/mzta-background.js
@@ -24,6 +24,7 @@ import { getCurrentIdentity, getOriginalBody, replaceBody, setBody, i18nConditio
import { taPromptUtils } from './js/mzta-utils-prompt.js';
import { mzta_specialCommand } from './js/mzta-special-commands.js';
import { getSpamFilterPrompt } from './js/mzta-prompts.js';
+import { taSpamReport } from './js/mzta-spamreport.js';
//browser.permissions.remove({ permissions: ["messagesMove"] }).then(console.log);
@@ -660,34 +661,35 @@ const newEmailListener = (folder, messagesList) => {
async function _newEmailListener(){
let messages = getMessages(messagesList);
+ taSpamReport.logger = taLog;
+
for await (let message of messages) {
- let fullMessage = await browser.messages.getFull(message.id);
+ // let fullMessage = await browser.messages.getFull(message.id);
+ // taLog.log(`From: ${fullMessage.headers.from}`);
+ // taLog.log(`Subject: ${fullMessage.headers.subject}`);
+ // taLog.log(`Name: ${fullMessage.name}`);
+ // taLog.log(`Body: ${fullMessage.body}`);
+ // taLog.log(`contentType: [${fullMessage.contentType}]`);
+ // taLog.log(`fullMessage.parts.length: ${fullMessage.parts.length}`);
- taLog.log(`From: ${fullMessage.headers.from}`);
- taLog.log(`Subject: ${fullMessage.headers.subject}`);
- taLog.log(`Name: ${fullMessage.name}`);
- taLog.log(`Body: ${fullMessage.body}`);
- taLog.log(`contentType: [${fullMessage.contentType}]`);
- taLog.log(`fullMessage.parts.length: ${fullMessage.parts.length}`);
-
- if (fullMessage.parts.length > 0) {
- for (let part of fullMessage.parts) {
- taLog.log(`From: ${part.headers.from}`);
- taLog.log(`Subject: ${part.headers.subject}`);
- taLog.log(`Name: ${part.name}`);
- taLog.log(`Body: ${part.body}`);
- taLog.log(`contentType: [${part.contentType}]`);
- taLog.log('contentType JSON: '+JSON.stringify(part.contentType));
- if (part.contentType.trim().toLowerCase() === "text/plain") {
- taLog.log("Body (text/plain):" + part.body);
- // return part.body;
- }
- if (part.contentType.trim().toLowerCase() === "text/html") {
- taLog.log("Body (text/html):" + part.body);
- // return part.body;
- }
- }
- }
+ // if (fullMessage.parts.length > 0) {
+ // for (let part of fullMessage.parts) {
+ // taLog.log(`From: ${part.headers.from}`);
+ // taLog.log(`Subject: ${part.headers.subject}`);
+ // taLog.log(`Name: ${part.name}`);
+ // taLog.log(`Body: ${part.body}`);
+ // taLog.log(`contentType: [${part.contentType}]`);
+ // taLog.log('contentType JSON: '+JSON.stringify(part.contentType));
+ // if (part.contentType.trim().toLowerCase() === "text/plain") {
+ // taLog.log("Body (text/plain):" + part.body);
+ // // return part.body;
+ // }
+ // if (part.contentType.trim().toLowerCase() === "text/html") {
+ // taLog.log("Body (text/html):" + part.body);
+ // // return part.body;
+ // }
+ // }
+ // }
let curr_fullMessage = null;
let msg_text = null;
@@ -756,10 +758,22 @@ const newEmailListener = (folder, messagesList) => {
console.error("[ThunderAI | SpamFilter] Error extracting JSON from AI response: ", e);
}
taLog.log("SpamFilter jsonObj: " + JSON.stringify(jsonObj));
- //TODO save log
+
+ let report_data = {};
+ report_data.report_date = new Date();
+ report_data.headerMessageId = message.headerMessageId;
+ report_data.spamValue = jsonObj.spamValue;
+ report_data.explanation = jsonObj.explanation;
+ report_data.subject = curr_fullMessage.headers.subject;
+ report_data.from = curr_fullMessage.headers.from;
+ report_data.message_date = new Date(message.date);
+ report_data.moved = false;
+ report_data.SpamThreshold = prefs_init.spamfilter_threshold;
+ //console.log(">>>>>>>>>>>> report_data.SpamThreshold: " + JSON.stringify(report_data.SpamThreshold));
//TODO move email if it's spam
if(jsonObj.spamValue >= prefs_init.spamfilter_threshold){
+ taLog.log("Marking as spam ["+message.headerMessageId+"]");
// mark as spam
messenger.messages.update(message.id, { junk: true });
//get the spam folder
@@ -767,9 +781,18 @@ const newEmailListener = (folder, messagesList) => {
//console.log(">>>>>>>>>>>> spamFolder: " + JSON.stringify(spamFolder));
//move the message
messenger.messages.move([message.id], spamFolder[0].id);
+ report_data.moved = true;
+ taLog.log("Marked as spam ["+message.headerMessageId+"]");
}
+
+ // Save the operation log
+ taSpamReport.saveReportData(report_data,message.headerMessageId);
}
}
+
+ if(prefs_init.spamfilter){
+ taSpamReport.truncReportData();
+ }
}
return _newEmailListener();
diff --git a/pages/spamfilter/mzta-spamfilter.css b/pages/spamfilter/mzta-spamfilter.css
index 6a9bacc0..e62e300d 100644
--- a/pages/spamfilter/mzta-spamfilter.css
+++ b/pages/spamfilter/mzta-spamfilter.css
@@ -91,7 +91,7 @@ table#miczPrefs tr:last-child td {
color: red;
}
-#spam_threshold_too_low{
+#spamfilter_threshold_too_low{
display: none;
color:red;
font-weight: bold;
@@ -99,6 +99,19 @@ table#miczPrefs tr:last-child td {
margin-left: 10px;
}
+table#report_data {
+ width: 100%;
+ border-collapse: collapse;
+ margin-top: 20px;
+}
+table#report_data th, table#report_data td {
+ border: 1px solid #ddd;
+ padding: 8px;
+ text-align: left;
+}
+table#report_data th {
+ background-color: #f4f4f4;
+}
@media (prefers-color-scheme: dark) {
body {
@@ -124,4 +137,18 @@ table#miczPrefs tr:last-child td {
.autocomplete-list li.active {
background-color: #4c4e58;
}
+
+ table#report_data th,
+ table#report_data td {
+ border-color: #444;
+ color: #ddd;
+ }
+
+ table#report_data th {
+ background-color: #333;
+ }
+
+ table#report_data td {
+ background-color: #222;
+ }
}
\ No newline at end of file
diff --git a/pages/spamfilter/mzta-spamfilter.html b/pages/spamfilter/mzta-spamfilter.html
index 293cf3fb..2a2f2846 100644
--- a/pages/spamfilter/mzta-spamfilter.html
+++ b/pages/spamfilter/mzta-spamfilter.html
@@ -17,7 +17,7 @@
__MSG_prefs_OptionText_spamfilter_threshold__ |
|
@@ -35,6 +35,24 @@
+ __MSG_SpamReport_Title__
+
+
+
+ | Message_ID |
+ __MSG_Date__ |
+ __MSG_From__ |
+ __MSG_Subject__ |
+ __MSG_Spam_Value__ |
+ __MSG_Moved_to_Spam__ |
+ __MSG_Explanation__ |
+ __MSG_Report_Date__ |
+
+
+
+
+
+