Going back to what works.
Manifest v3 and its consequences have been a disaster for the human race.
This commit is contained in:
parent
f9a31415a0
commit
6a85dbb2eb
3 changed files with 144 additions and 153 deletions
283
background.js
283
background.js
|
@ -126,7 +126,7 @@ function buildEmailText(full) {
|
||||||
const attachments = [];
|
const attachments = [];
|
||||||
collectText(full, bodyParts, attachments);
|
collectText(full, bodyParts, attachments);
|
||||||
const headers = Object.entries(full.headers || {})
|
const headers = Object.entries(full.headers || {})
|
||||||
.map(([k,v]) => `${k}: ${v.join(' ')}`)
|
.map(([k, v]) => `${k}: ${v.join(' ')}`)
|
||||||
.join('\n');
|
.join('\n');
|
||||||
const attachInfo = `Attachments: ${attachments.length}` + (attachments.length ? "\n" + attachments.map(a => ` - ${a}`).join('\n') : "");
|
const attachInfo = `Attachments: ${attachments.length}` + (attachments.length ? "\n" + attachments.map(a => ` - ${a}`).join('\n') : "");
|
||||||
const combined = `${headers}\n${attachInfo}\n\n${bodyParts.join('\n')}`.trim();
|
const combined = `${headers}\n${attachInfo}\n\n${bodyParts.join('\n')}`.trim();
|
||||||
|
@ -369,35 +369,14 @@ async function clearCacheForMessages(idsInput) {
|
||||||
icons: { "16": "resources/img/brain.png" }
|
icons: { "16": "resources/img/brain.png" }
|
||||||
});
|
});
|
||||||
|
|
||||||
//for the love of god work please
|
|
||||||
browser.messageDisplayAction.onClicked.addListener(async (tab, info) => {
|
|
||||||
try {
|
|
||||||
let header = await browser.messageDisplay.getDisplayedMessages(tab.id);
|
|
||||||
if (!header) {
|
|
||||||
logger.aiLog("No header, no message loaded?", { debug: true });
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const url = browser.runtime.getURL(`details.html?mid=${header.id}`);
|
|
||||||
await browser.messageDisplayAction.setPopup({ tabId: tab.id, popup: url });
|
|
||||||
await browser.messageDisplayAction.openPopup({ tabId: tab.id });
|
|
||||||
} catch (err) {
|
|
||||||
logger.aiLog("Failed to open details popup", { debug: true });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
browser.messageDisplay.onMessagesDisplayed.addListener(async (tab, displayedMessages) => {
|
|
||||||
logger.aiLog("Messages displayed!", { debug: true }, displayedMessages);
|
|
||||||
});
|
|
||||||
|
|
||||||
browser.menus.onClicked.addListener(async (info, tab) => {
|
browser.menus.onClicked.addListener(async (info, tab) => {
|
||||||
if (info.menuItemId === "apply-ai-rules-list" || info.menuItemId === "apply-ai-rules-display") {
|
if (info.menuItemId === "apply-ai-rules-list" || info.menuItemId === "apply-ai-rules-display") {
|
||||||
const ids = info.selectedMessages?.messages?.map(m => m.id) ||
|
const ids = info.selectedMessages?.messages?.map(m => m.id) ||
|
||||||
(info.messageId ? [info.messageId] : []);
|
(info.messageId ? [info.messageId] : []);
|
||||||
await applyAiRules(ids);
|
await applyAiRules(ids);
|
||||||
} else if (info.menuItemId === "clear-ai-cache-list" || info.menuItemId === "clear-ai-cache-display") {
|
} else if (info.menuItemId === "clear-ai-cache-list" || info.menuItemId === "clear-ai-cache-display") {
|
||||||
const ids = info.selectedMessages?.messages?.map(m => m.id) ||
|
const ids = info.selectedMessages?.messages?.map(m => m.id) ||
|
||||||
(info.messageId ? [info.messageId] : []);
|
(info.messageId ? [info.messageId] : []);
|
||||||
await clearCacheForMessages(ids);
|
await clearCacheForMessages(ids);
|
||||||
} else if (info.menuItemId === "view-ai-reason-list" || info.menuItemId === "view-ai-reason-display") {
|
} else if (info.menuItemId === "view-ai-reason-list" || info.menuItemId === "view-ai-reason-display") {
|
||||||
const [header] = await browser.messageDisplay.getDisplayedMessages(tab.id);
|
const [header] = await browser.messageDisplay.getDisplayedMessages(tab.id);
|
||||||
|
@ -417,141 +396,143 @@ async function clearCacheForMessages(idsInput) {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (msg?.type === "sortana:test") {
|
if (msg?.type === "sortana:test") {
|
||||||
const { text = "", criterion = "" } = msg;
|
const { text = "", criterion = "" } = msg;
|
||||||
logger.aiLog("sortana:test – text", {debug: true}, text);
|
logger.aiLog("sortana:test – text", { debug: true }, text);
|
||||||
logger.aiLog("sortana:test – criterion", {debug: true}, criterion);
|
logger.aiLog("sortana:test – criterion", { debug: true }, criterion);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
logger.aiLog("Calling AiClassifier.classifyText()", {debug: true});
|
logger.aiLog("Calling AiClassifier.classifyText()", { debug: true });
|
||||||
const result = await AiClassifier.classifyText(text, criterion);
|
const result = await AiClassifier.classifyText(text, criterion);
|
||||||
logger.aiLog("classify() returned", {debug: true}, result);
|
logger.aiLog("classify() returned", { debug: true }, result);
|
||||||
return { match: result };
|
return { match: result };
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
logger.aiLog("Error in classify()", {level: 'error'}, err);
|
|
||||||
// rethrow so the caller sees the failure
|
|
||||||
throw err;
|
|
||||||
}
|
|
||||||
} else if (msg?.type === "sortana:clearCacheForDisplayed") {
|
|
||||||
try {
|
|
||||||
const msgs = await browser.messageDisplay.getDisplayedMessages();
|
|
||||||
const ids = msgs.map(m => m.id);
|
|
||||||
await clearCacheForMessages(ids);
|
|
||||||
} catch (e) {
|
|
||||||
logger.aiLog("failed to clear cache from message script", { level: 'error' }, e);
|
|
||||||
}
|
|
||||||
} else if (msg?.type === "sortana:getReasons") {
|
|
||||||
try {
|
|
||||||
const id = msg.id;
|
|
||||||
const hdr = await messenger.messages.get(id);
|
|
||||||
const subject = hdr?.subject || "";
|
|
||||||
if (!aiRules.length) {
|
|
||||||
const { aiRules: stored } = await storage.local.get("aiRules");
|
|
||||||
aiRules = Array.isArray(stored) ? stored.map(r => {
|
|
||||||
if (r.actions) return r;
|
|
||||||
const actions = [];
|
|
||||||
if (r.tag) actions.push({ type: 'tag', tagKey: r.tag });
|
|
||||||
if (r.moveTo) actions.push({ type: 'move', folder: r.moveTo });
|
|
||||||
const rule = { criterion: r.criterion, actions };
|
|
||||||
if (r.stopProcessing) rule.stopProcessing = true;
|
|
||||||
return rule;
|
|
||||||
}) : [];
|
|
||||||
}
|
}
|
||||||
const reasons = [];
|
catch (err) {
|
||||||
for (const rule of aiRules) {
|
logger.aiLog("Error in classify()", { level: 'error' }, err);
|
||||||
const key = await AiClassifier.buildCacheKey(id, rule.criterion);
|
// rethrow so the caller sees the failure
|
||||||
const reason = AiClassifier.getReason(key);
|
throw err;
|
||||||
if (reason) {
|
}
|
||||||
reasons.push({ criterion: rule.criterion, reason });
|
} else if (msg?.type === "sortana:clearCacheForDisplayed") {
|
||||||
|
try {
|
||||||
|
const msgs = await browser.messageDisplay.getDisplayedMessages();
|
||||||
|
const ids = msgs.map(m => m.id);
|
||||||
|
await clearCacheForMessages(ids);
|
||||||
|
} catch (e) {
|
||||||
|
logger.aiLog("failed to clear cache from message script", { level: 'error' }, e);
|
||||||
|
}
|
||||||
|
} else if (msg?.type === "sortana:getReasons") {
|
||||||
|
try {
|
||||||
|
const id = msg.id;
|
||||||
|
const hdr = await messenger.messages.get(id);
|
||||||
|
const subject = hdr?.subject || "";
|
||||||
|
if (!aiRules.length) {
|
||||||
|
const { aiRules: stored } = await storage.local.get("aiRules");
|
||||||
|
aiRules = Array.isArray(stored) ? stored.map(r => {
|
||||||
|
if (r.actions) return r;
|
||||||
|
const actions = [];
|
||||||
|
if (r.tag) actions.push({ type: 'tag', tagKey: r.tag });
|
||||||
|
if (r.moveTo) actions.push({ type: 'move', folder: r.moveTo });
|
||||||
|
const rule = { criterion: r.criterion, actions };
|
||||||
|
if (r.stopProcessing) rule.stopProcessing = true;
|
||||||
|
return rule;
|
||||||
|
}) : [];
|
||||||
}
|
}
|
||||||
}
|
const reasons = [];
|
||||||
return { subject, reasons };
|
for (const rule of aiRules) {
|
||||||
} catch (e) {
|
const key = await AiClassifier.buildCacheKey(id, rule.criterion);
|
||||||
logger.aiLog("failed to collect reasons", { level: 'error' }, e);
|
const reason = AiClassifier.getReason(key);
|
||||||
return { subject: '', reasons: [] };
|
if (reason) {
|
||||||
}
|
reasons.push({ criterion: rule.criterion, reason });
|
||||||
} else if (msg?.type === "sortana:getDetails") {
|
}
|
||||||
try {
|
|
||||||
const id = msg.id;
|
|
||||||
const hdr = await messenger.messages.get(id);
|
|
||||||
const subject = hdr?.subject || "";
|
|
||||||
if (!aiRules.length) {
|
|
||||||
const { aiRules: stored } = await storage.local.get("aiRules");
|
|
||||||
aiRules = Array.isArray(stored) ? stored.map(r => {
|
|
||||||
if (r.actions) return r;
|
|
||||||
const actions = [];
|
|
||||||
if (r.tag) actions.push({ type: 'tag', tagKey: r.tag });
|
|
||||||
if (r.moveTo) actions.push({ type: 'move', folder: r.moveTo });
|
|
||||||
const rule = { criterion: r.criterion, actions };
|
|
||||||
if (r.stopProcessing) rule.stopProcessing = true;
|
|
||||||
return rule;
|
|
||||||
}) : [];
|
|
||||||
}
|
|
||||||
const results = [];
|
|
||||||
for (const rule of aiRules) {
|
|
||||||
const key = await AiClassifier.buildCacheKey(id, rule.criterion);
|
|
||||||
const matched = AiClassifier.getCachedResult(key);
|
|
||||||
const reason = AiClassifier.getReason(key);
|
|
||||||
if (matched !== null || reason) {
|
|
||||||
results.push({ criterion: rule.criterion, matched, reason });
|
|
||||||
}
|
}
|
||||||
|
return { subject, reasons };
|
||||||
|
} catch (e) {
|
||||||
|
logger.aiLog("failed to collect reasons", { level: 'error' }, e);
|
||||||
|
return { subject: '', reasons: [] };
|
||||||
}
|
}
|
||||||
return { subject, results };
|
} else if (msg?.type === "sortana:getDetails") {
|
||||||
} catch (e) {
|
try {
|
||||||
logger.aiLog("failed to collect details", { level: 'error' }, e);
|
const id = msg.id;
|
||||||
return { subject: '', results: [] };
|
const hdr = await messenger.messages.get(id);
|
||||||
}
|
const subject = hdr?.subject || "";
|
||||||
} else if (msg?.type === "sortana:getDisplayedMessages") {
|
if (!aiRules.length) {
|
||||||
try {
|
const { aiRules: stored } = await storage.local.get("aiRules");
|
||||||
const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
|
aiRules = Array.isArray(stored) ? stored.map(r => {
|
||||||
const messages = await browser.messageDisplay.getDisplayedMessages(tab?.id);
|
if (r.actions) return r;
|
||||||
return { messages };
|
const actions = [];
|
||||||
} catch (e) {
|
if (r.tag) actions.push({ type: 'tag', tagKey: r.tag });
|
||||||
logger.aiLog("failed to get displayed messages", { level: 'error' }, e);
|
if (r.moveTo) actions.push({ type: 'move', folder: r.moveTo });
|
||||||
return { messages: [] };
|
const rule = { criterion: r.criterion, actions };
|
||||||
}
|
if (r.stopProcessing) rule.stopProcessing = true;
|
||||||
} else if (msg?.type === "sortana:clearCacheForMessage") {
|
return rule;
|
||||||
try {
|
}) : [];
|
||||||
await clearCacheForMessages([msg.id]);
|
}
|
||||||
return { ok: true };
|
const results = [];
|
||||||
} catch (e) {
|
for (const rule of aiRules) {
|
||||||
logger.aiLog("failed to clear cache for message", { level: 'error' }, e);
|
const key = await AiClassifier.buildCacheKey(id, rule.criterion);
|
||||||
return { ok: false };
|
const matched = AiClassifier.getCachedResult(key);
|
||||||
}
|
const reason = AiClassifier.getReason(key);
|
||||||
} else if (msg?.type === "sortana:getQueueCount") {
|
if (matched !== null || reason) {
|
||||||
return { count: queuedCount + (processing ? 1 : 0) };
|
results.push({ criterion: rule.criterion, matched, reason });
|
||||||
} else if (msg?.type === "sortana:getTiming") {
|
}
|
||||||
const t = timingStats;
|
}
|
||||||
const std = t.count > 1 ? Math.sqrt(t.m2 / (t.count - 1)) : 0;
|
return { subject, results };
|
||||||
return {
|
} catch (e) {
|
||||||
count: queuedCount + (processing ? 1 : 0),
|
logger.aiLog("failed to collect details", { level: 'error' }, e);
|
||||||
current: currentStart ? Date.now() - currentStart : -1,
|
return { subject: '', results: [] };
|
||||||
last: t.last,
|
}
|
||||||
runs: t.count,
|
} else if (msg?.type === "sortana:getDisplayedMessages") {
|
||||||
average: t.mean,
|
try {
|
||||||
total: t.total,
|
const [tab] = await browser.tabs.query({ active: true, currentWindow: true });
|
||||||
stddev: std
|
const messages = await browser.messageDisplay.getDisplayedMessages(tab?.id);
|
||||||
};
|
const ids = messages.map(hdr => hdr.id);
|
||||||
} else {
|
|
||||||
logger.aiLog("Unknown message type, ignoring", {level: 'warn'}, msg?.type);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Automatically classify new messages
|
return { ids };
|
||||||
if (typeof messenger !== "undefined" && messenger.messages?.onNewMailReceived) {
|
} catch (e) {
|
||||||
messenger.messages.onNewMailReceived.addListener(async (folder, messages) => {
|
logger.aiLog("failed to get displayed messages", { level: 'error' }, e);
|
||||||
logger.aiLog("onNewMailReceived", {debug: true}, messages);
|
return { messages: [] };
|
||||||
const ids = (messages?.messages || messages || []).map(m => m.id ?? m);
|
}
|
||||||
await applyAiRules(ids);
|
} else if (msg?.type === "sortana:clearCacheForMessage") {
|
||||||
|
try {
|
||||||
|
await clearCacheForMessages([msg.id]);
|
||||||
|
return { ok: true };
|
||||||
|
} catch (e) {
|
||||||
|
logger.aiLog("failed to clear cache for message", { level: 'error' }, e);
|
||||||
|
return { ok: false };
|
||||||
|
}
|
||||||
|
} else if (msg?.type === "sortana:getQueueCount") {
|
||||||
|
return { count: queuedCount + (processing ? 1 : 0) };
|
||||||
|
} else if (msg?.type === "sortana:getTiming") {
|
||||||
|
const t = timingStats;
|
||||||
|
const std = t.count > 1 ? Math.sqrt(t.m2 / (t.count - 1)) : 0;
|
||||||
|
return {
|
||||||
|
count: queuedCount + (processing ? 1 : 0),
|
||||||
|
current: currentStart ? Date.now() - currentStart : -1,
|
||||||
|
last: t.last,
|
||||||
|
runs: t.count,
|
||||||
|
average: t.mean,
|
||||||
|
total: t.total,
|
||||||
|
stddev: std
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
logger.aiLog("Unknown message type, ignoring", { level: 'warn' }, msg?.type);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
} else {
|
|
||||||
logger.aiLog("messenger.messages API unavailable, skipping new mail listener", { level: 'warn' });
|
|
||||||
}
|
|
||||||
|
|
||||||
// Catch any unhandled rejections
|
// Automatically classify new messages
|
||||||
window.addEventListener("unhandledrejection", ev => {
|
if (typeof messenger !== "undefined" && messenger.messages?.onNewMailReceived) {
|
||||||
logger.aiLog("Unhandled promise rejection", {level: 'error'}, ev.reason);
|
messenger.messages.onNewMailReceived.addListener(async (folder, messages) => {
|
||||||
});
|
logger.aiLog("onNewMailReceived", { debug: true }, messages);
|
||||||
|
const ids = (messages?.messages || messages || []).map(m => m.id ?? m);
|
||||||
|
await applyAiRules(ids);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
logger.aiLog("messenger.messages API unavailable, skipping new mail listener", { level: 'warn' });
|
||||||
|
}
|
||||||
|
|
||||||
|
// Catch any unhandled rejections
|
||||||
|
window.addEventListener("unhandledrejection", ev => {
|
||||||
|
logger.aiLog("Unhandled promise rejection", { level: 'error' }, ev.reason);
|
||||||
|
});
|
||||||
|
|
||||||
browser.runtime.onInstalled.addListener(async ({ reason }) => {
|
browser.runtime.onInstalled.addListener(async ({ reason }) => {
|
||||||
if (reason === "install") {
|
if (reason === "install") {
|
||||||
|
|
11
details.js
11
details.js
|
@ -10,7 +10,16 @@ if (!isNaN(qMid)) {
|
||||||
if (messages && messages[0]) {
|
if (messages && messages[0]) {
|
||||||
loadMessage(messages[0].id);
|
loadMessage(messages[0].id);
|
||||||
} else {
|
} else {
|
||||||
aiLog("Details popup: no displayed message found");
|
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||||
|
const tabId = tabs[0]?.id;
|
||||||
|
const msgs = tabId ? await browser.messageDisplay.getDisplayedMessages(tabId) : [];
|
||||||
|
let id = msgs[0]?.id;
|
||||||
|
if (id) {
|
||||||
|
loadMessage(id);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
aiLog("Details popup: no displayed message found");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -24,7 +24,8 @@
|
||||||
"message_display_action": {
|
"message_display_action": {
|
||||||
"default_icon": "resources/img/brain.png",
|
"default_icon": "resources/img/brain.png",
|
||||||
"default_title": "Details",
|
"default_title": "Details",
|
||||||
"default_label": "Details"
|
"default_label": "Details",
|
||||||
|
"default_popup": "details.html"
|
||||||
},
|
},
|
||||||
"background": { "scripts": [ "background.js" ] },
|
"background": { "scripts": [ "background.js" ] },
|
||||||
"options_ui": {
|
"options_ui": {
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue