This commit is contained in:
Jordan Wages 2025-06-29 02:42:58 -05:00
commit a77e5e68fb
3 changed files with 2 additions and 185 deletions

View file

@ -29,8 +29,10 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "options", "options", "{7372
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{75ED3C1E-D3C7-4546-9F2E-AC85859DDF4B}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "modules", "modules", "{75ED3C1E-D3C7-4546-9F2E-AC85859DDF4B}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
modules\AiClassifier.js = modules\AiClassifier.js
modules\ExpressionSearchFilter.jsm = modules\ExpressionSearchFilter.jsm modules\ExpressionSearchFilter.jsm = modules\ExpressionSearchFilter.jsm
modules\logger.jsm = modules\logger.jsm modules\logger.jsm = modules\logger.jsm
modules\messageUtils.jsm = modules\messageUtils.jsm
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_locales", "_locales", "{D446E5C6-BDDE-4091-BD1A-EC57170003CF}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "_locales", "_locales", "{D446E5C6-BDDE-4091-BD1A-EC57170003CF}"
@ -40,11 +42,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "en-US", "en-US", "{8BEA7793
_locales\en-US\messages.json = _locales\en-US\messages.json _locales\en-US\messages.json = _locales\en-US\messages.json
EndProjectSection EndProjectSection
EndProject EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "content", "content", "{028FDA4B-AC3E-4A0E-9291-978E213F9B78}"
ProjectSection(SolutionItems) = preProject
content\filterEditor.js = content\filterEditor.js
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "prompt_templates", "prompt_templates", "{86516D53-50D4-4FE2-9D8A-977A8F5EBDBD}" Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "prompt_templates", "prompt_templates", "{86516D53-50D4-4FE2-9D8A-977A8F5EBDBD}"
ProjectSection(SolutionItems) = preProject ProjectSection(SolutionItems) = preProject
prompt_templates\mistral.txt = prompt_templates\mistral.txt prompt_templates\mistral.txt = prompt_templates\mistral.txt
@ -79,7 +76,6 @@ Global
{75ED3C1E-D3C7-4546-9F2E-AC85859DDF4B} = {BCC6E6D2-343B-4C48-854D-5FE3BBC3CB70} {75ED3C1E-D3C7-4546-9F2E-AC85859DDF4B} = {BCC6E6D2-343B-4C48-854D-5FE3BBC3CB70}
{D446E5C6-BDDE-4091-BD1A-EC57170003CF} = {BCC6E6D2-343B-4C48-854D-5FE3BBC3CB70} {D446E5C6-BDDE-4091-BD1A-EC57170003CF} = {BCC6E6D2-343B-4C48-854D-5FE3BBC3CB70}
{8BEA7793-3336-40ED-AB96-7FFB09FEB0F6} = {D446E5C6-BDDE-4091-BD1A-EC57170003CF} {8BEA7793-3336-40ED-AB96-7FFB09FEB0F6} = {D446E5C6-BDDE-4091-BD1A-EC57170003CF}
{028FDA4B-AC3E-4A0E-9291-978E213F9B78} = {BCC6E6D2-343B-4C48-854D-5FE3BBC3CB70}
{86516D53-50D4-4FE2-9D8A-977A8F5EBDBD} = {BCC6E6D2-343B-4C48-854D-5FE3BBC3CB70} {86516D53-50D4-4FE2-9D8A-977A8F5EBDBD} = {BCC6E6D2-343B-4C48-854D-5FE3BBC3CB70}
{68A87938-5C2B-49F5-8AAA-8A34FBBFD854} = {BCC6E6D2-343B-4C48-854D-5FE3BBC3CB70} {68A87938-5C2B-49F5-8AAA-8A34FBBFD854} = {BCC6E6D2-343B-4C48-854D-5FE3BBC3CB70}
{F266602F-1755-4A95-A11B-6C90C701C5BF} = {68A87938-5C2B-49F5-8AAA-8A34FBBFD854} {F266602F-1755-4A95-A11B-6C90C701C5BF} = {68A87938-5C2B-49F5-8AAA-8A34FBBFD854}

View file

@ -1,90 +0,0 @@
"use strict";
var { ExtensionParent } = ChromeUtils.importESModule("resource://gre/modules/ExtensionParent.sys.mjs");
var { MailServices } = ChromeUtils.importESModule("resource:///modules/MailServices.sys.mjs");
var { aiLog } = ChromeUtils.import("resource://aifilter/modules/logger.jsm");
var AiClassifier = ChromeUtils.importESModule("resource://aifilter/modules/AiClassifier.js");
var { getPlainText } = ChromeUtils.import("resource://aifilter/modules/messageUtils.jsm");
var EXPORTED_SYMBOLS = ["AIFilter", "ClassificationTerm"];
class CustomerTermBase {
constructor(nameId, operators) {
// Lookup our extension instance using the ID from manifest.json
// so locale strings are resolved correctly.
this.extension = ExtensionParent.GlobalManager.getExtension("ai-filter@jordanwages");
this.id = "aifilter#" + nameId;
this.name = this.extension.localeData.localizeMessage(nameId);
this.operators = operators;
aiLog(`[ExpressionSearchFilter] Initialized term base "${this.id}"`, {debug: true});
}
getEnabled() {
aiLog(`[ExpressionSearchFilter] getEnabled() called on "${this.id}"`, {debug: true});
return true;
}
getAvailable() {
aiLog(`[ExpressionSearchFilter] getAvailable() called on "${this.id}"`, {debug: true});
return true;
}
getAvailableOperators() {
aiLog(`[ExpressionSearchFilter] getAvailableOperators() called on "${this.id}"`, {debug: true});
return this.operators;
}
getAvailableValues() {
aiLog(`[ExpressionSearchFilter] getAvailableValues() called on "${this.id}"`, {debug: true});
return null;
}
get attrib() {
aiLog(`[ExpressionSearchFilter] attrib getter called for "${this.id}"`, {debug: true});
//return Ci.nsMsgSearchAttrib.Custom;
}
}
class ClassificationTerm extends CustomerTermBase {
constructor() {
super("classification", [Ci.nsMsgSearchOp.Matches, Ci.nsMsgSearchOp.DoesntMatch]);
aiLog(`[ExpressionSearchFilter] ClassificationTerm constructed`, {debug: true});
}
needsBody() { return true; }
match(msgHdr, value, op) {
const opName = op === Ci.nsMsgSearchOp.Matches ? "matches" :
op === Ci.nsMsgSearchOp.DoesntMatch ? "doesn't match" : `unknown (${op})`;
aiLog(`[ExpressionSearchFilter] Matching message ${msgHdr.messageId} using op "${opName}" and value "${value}"`, {debug: true});
let key = AiClassifier.buildCacheKeySync(msgHdr.messageId, value);
let body = getPlainText(msgHdr);
let matched = AiClassifier.classifyTextSync(body, value, key);
if (op === Ci.nsMsgSearchOp.DoesntMatch) {
matched = !matched;
aiLog(`[ExpressionSearchFilter] Operator is "doesn't match" → inverting to ${matched}`, {debug: true});
}
aiLog(`[ExpressionSearchFilter] Final match result: ${matched}`, {debug: true});
return matched;
}
}
(function register() {
aiLog(`[ExpressionSearchFilter] Registering custom filter term...`, {debug: true});
let term = new ClassificationTerm();
if (!MailServices.filters.getCustomTerm(term.id)) {
MailServices.filters.addCustomTerm(term);
aiLog(`[ExpressionSearchFilter] Registered term: ${term.id}`, {debug: true});
} else {
aiLog(`[ExpressionSearchFilter] Term already registered: ${term.id}`, {debug: true});
}
})();
var AIFilter = { setConfig: AiClassifier.setConfig };

View file

@ -1,89 +0,0 @@
"use strict";
var { NetUtil } = ChromeUtils.importESModule("resource://gre/modules/NetUtil.sys.mjs");
var { MimeParser } = ChromeUtils.importESModule("resource:///modules/mimeParser.sys.mjs");
var { aiLog } = ChromeUtils.import("resource://aifilter/modules/logger.jsm");
var EXPORTED_SYMBOLS = ["getPlainText"];
function getPlainText(msgHdr) {
aiLog(`[ExpressionSearchFilter] Extracting plain text for message ID ${msgHdr.messageId}`, {debug: true});
let folder = msgHdr.folder;
if (!folder.getMsgInputStream) return "";
let reusable = {};
let stream = folder.getMsgInputStream(msgHdr, reusable);
let data = NetUtil.readInputStreamToString(stream, msgHdr.messageSize);
if (!reusable.value) stream.close();
let parser = Cc["@mozilla.org/parserutils;1"].getService(Ci.nsIParserUtils);
try {
let root = MimeParser.parseSync(data, {strformat: "unicode"});
let parts = [];
function pushPlaceholder(type, info, bytes) {
bytes = bytes || 0;
let prettyType = type.split("/")[1] || type;
parts.push(`[${info}: ${prettyType}, ${bytes} bytes]`);
}
function byteSizeFromBase64(str) {
let clean = str.replace(/[^A-Za-z0-9+/=]/g, "");
return Math.floor(clean.length * 3 / 4);
}
function replaceInlineBase64(text) {
return text.replace(/[A-Za-z0-9+/]{100,}={0,2}/g,
m => `[base64: ${byteSizeFromBase64(m)} bytes]`);
}
function walk(node) {
if (node.parts && node.parts.length) {
for (let child of node.parts) {
walk(child);
}
return;
}
let ct = (node.contentType || "text/plain").toLowerCase();
let cd = (node.headers?.["content-disposition"]?.[0] || "").toLowerCase();
let enc = (node.headers?.["content-transfer-encoding"]?.[0] || "").toLowerCase();
let bodyText = String(node.body || "");
if (cd.includes("attachment")) {
pushPlaceholder(ct, "binary attachment", byteSizeFromBase64(bodyText));
} else if (ct.startsWith("text/plain")) {
if (enc === "base64") {
parts.push(`[base64: ${byteSizeFromBase64(bodyText)} bytes]`);
} else {
parts.push(replaceInlineBase64(bodyText));
}
} else if (ct.startsWith("text/html")) {
if (enc === "base64") {
parts.push(`[base64: ${byteSizeFromBase64(bodyText)} bytes]`);
} else {
let txt = parser.convertToPlainText(bodyText,
Ci.nsIDocumentEncoder.OutputLFLineBreak |
Ci.nsIDocumentEncoder.OutputNoScriptContent |
Ci.nsIDocumentEncoder.OutputNoFramesContent |
Ci.nsIDocumentEncoder.OutputBodyOnly, 0);
parts.push(replaceInlineBase64(txt));
}
} else {
// Other single part types treated as attachments
pushPlaceholder(ct, "binary attachment", byteSizeFromBase64(bodyText));
}
}
walk(root);
return parts.join("\n");
} catch (e) {
// Fallback: convert entire raw message to text
aiLog(`Failed to parse MIME, falling back to raw conversion`, {level: 'warn'}, e);
return parser.convertToPlainText(data,
Ci.nsIDocumentEncoder.OutputLFLineBreak |
Ci.nsIDocumentEncoder.OutputNoScriptContent |
Ci.nsIDocumentEncoder.OutputNoFramesContent |
Ci.nsIDocumentEncoder.OutputBodyOnly, 0);
}
}