Merge remote-tracking branch 'origin/v4.0.0' into pr/yonie/579
This commit is contained in:
commit
2ba069e1be
16 changed files with 594 additions and 279 deletions
|
|
@ -20,7 +20,10 @@
|
|||
<li><i>[All APIs]</i> Added an option to get a calendar event without selecting some text, but using the full text body of the email [<a href="https://github.com/micz/ThunderAI/issues/518">#518</a>].</li>
|
||||
<li><i>[All APIs]</i> Added a new menu item to create a calendar event from the text saved in the clipboard [<a href="https://github.com/micz/ThunderAI/issues/362">#362</a>].</li>
|
||||
<li>Added a button to copy a prompt in the Custom Prompts page [<a href="https://github.com/micz/ThunderAI/issues/598">#598</a>].</li>
|
||||
<li><i>[All APIs]</i> Showing the spam filter info at the top of the message. The data is saved only for the session in which the message has been checked for spam [<a href="https://github.com/micz/ThunderAI/issues/506">#506</a>].</li>
|
||||
<li><i>[All APIs]</i> Showing the spam filter info at the top of the message. The data is saved only for the session in which the message has been checked for spam [<a href="https://github.com/micz/ThunderAI/issues/506">#506</a>, <a href="https://github.com/micz/ThunderAI/issues/658">#658</a>].</li>
|
||||
<li>Fix: Now it's possibile to use multiple <i>additional_text</i> placeholders in a single prompt, also using custom placeholders [<a href="https://github.com/micz/ThunderAI/issues/554">#554</a>].</li>
|
||||
<li>When using the <i>additional_text</i> placeholder is now possibile to specify an ID that will be shown in the form asking for the text [<a href="https://github.com/micz/ThunderAI/issues/525">#525</a>].</li>
|
||||
<li><i>[ChatGPT Web]</i> Added an option to define a custom time to wait for the page load. Sometimes, on slow PCs, the ChatGPT page loads slowly and ThunderAI inject its content too early. With this option you can adjust the waiting time [<a href="https://github.com/micz/ThunderAI/issues/634">#634</a>].</li>
|
||||
<li>...</li>
|
||||
</ul>
|
||||
<h2>Version 3.8.4 - 10/02/2026</h2>
|
||||
|
|
|
|||
|
|
@ -699,6 +699,14 @@
|
|||
"message": "If checked, the temporary chat will be used in the ChatGPT Web Interface.",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_chatgpt_web_load_wait_time": {
|
||||
"message": "Wait time for page load",
|
||||
"description": ""
|
||||
},
|
||||
"prefs_OptionText_chatgpt_web_load_wait_time_info": {
|
||||
"message": "Time in milliseconds to wait for the ChatGPT page to load before loading the additional content. Default is 1000ms. If a Custom GPT or Project is defined, additional 1000ms will be added to this value.",
|
||||
"description": ""
|
||||
},
|
||||
"chatgpt_btn_model": {
|
||||
"message": "Use the current model",
|
||||
"description": ""
|
||||
|
|
@ -1898,5 +1906,9 @@
|
|||
"copy_text": {
|
||||
"message": "copy",
|
||||
"description": ""
|
||||
},
|
||||
"spam_check_in_progress": {
|
||||
"message": "Spam check in progress...",
|
||||
"description": ""
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1430,5 +1430,14 @@
|
|||
},
|
||||
"SpamReport_infoline": {
|
||||
"message": "Denna information sparas endast för den aktuella sessionen."
|
||||
},
|
||||
"prefs_OptionText_chatgpt_web_load_wait_time": {
|
||||
"message": "Väntetid för sidinläsning"
|
||||
},
|
||||
"prefs_OptionText_chatgpt_web_load_wait_time_info": {
|
||||
"message": "Tid i millisekunder att vänta på att ChatGPT-sidan ska laddas innan ytterligare innehåll laddas. Standardvärdet är 1000 ms. Om en anpassad GPT eller ett anpassat projekt definieras läggs ytterligare 1000 ms till detta värde."
|
||||
},
|
||||
"spam_check_in_progress": {
|
||||
"message": "Skräppostkontroll pågår..."
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -256,13 +256,13 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
promptData = message;
|
||||
//send the received prompt to the llm api
|
||||
if(message.do_custom_text=="1") {
|
||||
messageInput._showCustomTextField();
|
||||
messageInput._showCustomTextField(message.prompt_info?.custom_text_array);
|
||||
}else{
|
||||
sendPrompt(message);
|
||||
}
|
||||
break;
|
||||
case 'api_send_custom_text':
|
||||
let userInput = message.custom_text;
|
||||
let userInput = message.custom_text; // From version 4.0.0 this is an array
|
||||
if(userInput !== null) {
|
||||
if(!placeholdersUtils.hasPlaceholder(promptData.prompt, 'additional_text')){
|
||||
// no additional_text placeholder, do as usual
|
||||
|
|
@ -270,7 +270,14 @@ browser.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
}else{
|
||||
// we have the additional_text placeholder, do the magic!
|
||||
let finalSubs = {};
|
||||
finalSubs["additional_text"] = userInput;
|
||||
|
||||
if (Array.isArray(userInput)) {
|
||||
userInput.forEach(obj => {
|
||||
finalSubs[obj.placeholder.replace(/^{%|%}$/g, '').trim()] = obj.custom_text;
|
||||
});
|
||||
} else {
|
||||
finalSubs["additional_text"] = userInput;
|
||||
}
|
||||
promptData.prompt = placeholdersUtils.replacePlaceholders({
|
||||
text: promptData.prompt,
|
||||
replacements: finalSubs,
|
||||
|
|
|
|||
|
|
@ -69,12 +69,14 @@ messagesInputStyle.textContent = `
|
|||
}
|
||||
#mzta-custom_text{
|
||||
padding:10px;
|
||||
width:auto;
|
||||
width:50%;
|
||||
min-width:300px;
|
||||
max-width:80%;
|
||||
height:auto;
|
||||
max-height:80%;
|
||||
border-radius:5px;
|
||||
overflow:auto;
|
||||
overflow-y:auto;
|
||||
overflow-x:hidden;
|
||||
position:fixed;
|
||||
top:50%;
|
||||
left:50%;
|
||||
|
|
@ -84,15 +86,18 @@ messagesInputStyle.textContent = `
|
|||
background:#333;
|
||||
color:white;
|
||||
border:3px solid white;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#mzta-custom_loading{
|
||||
height:50px;display:none;
|
||||
}
|
||||
#mzta-custom_textarea{
|
||||
color:black;
|
||||
padding:1px;
|
||||
padding:5px;
|
||||
font-size:15px;
|
||||
width:100%;
|
||||
box-sizing: border-box;
|
||||
resize: vertical;
|
||||
}
|
||||
#mzta-custom_info{
|
||||
text-align:center;
|
||||
|
|
@ -100,6 +105,19 @@ messagesInputStyle.textContent = `
|
|||
padding-bottom:10px;
|
||||
font-size:15px;
|
||||
}
|
||||
#mzta-custom_info span{
|
||||
font-size:0.8em;
|
||||
}
|
||||
#mzta-custom_step{
|
||||
position: absolute;
|
||||
bottom: 5px;
|
||||
right: 10px;
|
||||
font-size: 12px;
|
||||
color: #ccc;
|
||||
}
|
||||
#mzta-custom_btn{
|
||||
margin-top:7px;
|
||||
}
|
||||
@media (prefers-color-scheme: dark) {
|
||||
#messageInputField {
|
||||
background-color: #303030;
|
||||
|
|
@ -178,6 +196,7 @@ customInfo.textContent = browser.i18n.getMessage("chatgpt_win_custom_text");
|
|||
customDiv.appendChild(customInfo);
|
||||
const customTextArea = document.createElement('textarea');
|
||||
customTextArea.id = 'mzta-custom_textarea';
|
||||
customTextArea.rows = 5;
|
||||
customDiv.appendChild(customTextArea);
|
||||
const customLoading = document.createElement('img');
|
||||
customLoading.src = browser.runtime.getURL("/images/loading.gif");
|
||||
|
|
@ -188,11 +207,16 @@ customBtn.id = 'mzta-custom_btn';
|
|||
customBtn.textContent = browser.i18n.getMessage("chatgpt_win_send");
|
||||
customBtn.classList.add('mzta-btn');
|
||||
customDiv.appendChild(customBtn);
|
||||
const customStep = document.createElement('div');
|
||||
customStep.id = 'mzta-custom_step';
|
||||
customDiv.appendChild(customStep);
|
||||
messageInputTemplate.content.appendChild(customDiv);
|
||||
|
||||
class MessageInput extends HTMLElement {
|
||||
|
||||
model = '';
|
||||
_customTextArray = [];
|
||||
_currentCustomTextIndex = 0;
|
||||
|
||||
constructor() {
|
||||
super();
|
||||
|
|
@ -212,8 +236,14 @@ class MessageInput extends HTMLElement {
|
|||
this._customTextArea = shadowRoot.querySelector('#mzta-custom_textarea');
|
||||
this._customLoading = shadowRoot.querySelector('#mzta-custom_loading');
|
||||
this._customBtn = shadowRoot.querySelector('#mzta-custom_btn');
|
||||
this._customStep = shadowRoot.querySelector('#mzta-custom_step');
|
||||
this._customBtn.addEventListener("click", () => { this._customTextBtnClick({customBtn:this._customBtn,customLoading:this._customLoading,customDiv:this._customText}) });
|
||||
this._customTextArea.addEventListener("keydown", (event) => { if(event.code == "Enter" && event.ctrlKey) this._customTextBtnClick({customBtn:this._customBtn,customLoading:this._customLoading,customDiv:this._customText}) });
|
||||
this._customTextArea.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Enter" && !event.shiftKey) {
|
||||
event.preventDefault();
|
||||
this._customTextBtnClick({customBtn:this._customBtn,customLoading:this._customLoading,customDiv:this._customText});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
connectedCallback() {
|
||||
|
|
@ -306,21 +336,64 @@ class MessageInput extends HTMLElement {
|
|||
this._messageInputField.value = msg;
|
||||
}
|
||||
|
||||
_showCustomTextField(){
|
||||
_showCustomTextField(custom_text_array){
|
||||
this._customTextArray = custom_text_array || [];
|
||||
if (this._customTextArray.length === 0) {
|
||||
this._customTextArray.push({ placeholder: "{%additional_text%}", info: "" });
|
||||
}
|
||||
this._currentCustomTextIndex = 0;
|
||||
this._customText.style.display = 'block';
|
||||
this._renderCustomTextStep();
|
||||
}
|
||||
|
||||
_renderCustomTextStep() {
|
||||
const currentItem = this._customTextArray[this._currentCustomTextIndex];
|
||||
const infoDiv = this.shadowRoot.querySelector('#mzta-custom_info');
|
||||
|
||||
this._customTextArea.value = "";
|
||||
infoDiv.textContent = browser.i18n.getMessage("chatgpt_win_custom_text");
|
||||
|
||||
if (currentItem.info && currentItem.info.trim() !== "") {
|
||||
infoDiv.appendChild(document.createElement("br"));
|
||||
const infoSpan = document.createElement("span");
|
||||
infoSpan.textContent = "[" + browser.i18n.getMessage("customPrompts_form_label_ID") + ": " + currentItem.info + "]";
|
||||
infoDiv.appendChild(infoSpan);
|
||||
}
|
||||
|
||||
if(this._customTextArray.length > 1) {
|
||||
this._customStep.textContent = (this._currentCustomTextIndex + 1) + "/" + this._customTextArray.length;
|
||||
this._customStep.style.display = 'block';
|
||||
} else {
|
||||
this._customStep.style.display = 'none';
|
||||
}
|
||||
|
||||
this._customTextArea.focus();
|
||||
}
|
||||
|
||||
async _customTextBtnClick(args) {
|
||||
const customText = this._customTextArea.value;
|
||||
// console.log(">>>>>>>>>>>>>>>> customText: " + customText);
|
||||
args.customBtn.disabled = true;
|
||||
args.customBtn.classList.add('disabled');
|
||||
args.customLoading.style.display = 'inline-block';
|
||||
args.customLoading.style.display = 'none';
|
||||
let tab = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
browser.runtime.sendMessage({ command: "api_send_custom_text", custom_text: customText, tabId: tab[0].id });
|
||||
args.customDiv.style.display = 'none';
|
||||
|
||||
if (this._customTextArray[this._currentCustomTextIndex]) {
|
||||
this._customTextArray[this._currentCustomTextIndex].custom_text = customText;
|
||||
}
|
||||
|
||||
this._currentCustomTextIndex++;
|
||||
|
||||
if (this._currentCustomTextIndex < this._customTextArray.length) {
|
||||
this._renderCustomTextStep();
|
||||
} else {
|
||||
args.customBtn.disabled = true;
|
||||
args.customBtn.classList.add('disabled');
|
||||
args.customLoading.style.display = 'inline-block';
|
||||
|
||||
let tab = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
browser.runtime.sendMessage({ command: "api_send_custom_text", custom_text: this._customTextArray, tabId: tab[0].id });
|
||||
args.customDiv.style.display = 'none';
|
||||
|
||||
args.customBtn.disabled = false;
|
||||
args.customBtn.classList.remove('disabled');
|
||||
args.customLoading.style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,6 +30,8 @@ let current_mailMessageId = null;
|
|||
let selectionChangeTimeout = null;
|
||||
let isDragging = false;
|
||||
let delay_wait_completion = 7000; // milliseconds
|
||||
let _customTextArray = [];
|
||||
let _currentCustomTextIndex = 0;
|
||||
let lastSelectedHtml = "";
|
||||
|
||||
async function chatgpt_sendMsg(msg, method ='') { // return -1 send button not found, -2 textarea not found
|
||||
|
|
@ -126,6 +128,8 @@ function addCustomDiv(prompt_action,tabId,mailMessageId) {
|
|||
style.textContent += "#mzta-custom_loading{height:50px;display:none;}";
|
||||
style.textContent += "#mzta-custom_textarea{color:black;padding:1px;font-size:15px;width:100%;}";
|
||||
style.textContent += "#mzta-custom_info{text-align:center;width:100%;padding-bottom:10px;font-size:15px;}";
|
||||
style.textContent += "#mzta-custom_info span{font-size:0.8em;}";
|
||||
style.textContent += "#mzta-custom_step{position: absolute;bottom: 5px;right: 10px;font-size: 12px;color: #ccc;}";
|
||||
style.textContent += "#mzta-prompt-name{font-size:13px;font-style:italic;color:#919191;position:fixed;bottom:75px;;left:0;padding-left:5px;}";
|
||||
style.textContent += "#mzta-diff-overlay{position: fixed;top:0;left:0;width:100vw;height:100vh;background: rgba(0, 0, 0, 0.5);display:flex;justify-content:center;align-items:center;z-index:999;}";
|
||||
style.textContent += "#mzta-diff{padding:10px;border:2px solid white;border-radius:1em;position:fixed;top:50%;left:50%;width:80%;height:30em;transform:translate(-50%,-50%);z-index:9999;background-color: #333;color: white;}";
|
||||
|
|
@ -378,6 +382,7 @@ function addCustomDiv(prompt_action,tabId,mailMessageId) {
|
|||
customDiv.appendChild(customInfo);
|
||||
let customTextArea = document.createElement('textarea');
|
||||
customTextArea.id = 'mzta-custom_textarea';
|
||||
customTextArea.rows = 5;
|
||||
customDiv.appendChild(customTextArea);
|
||||
let customLoading = document.createElement('img');
|
||||
customLoading.src = browser.runtime.getURL("/images/loading.gif");
|
||||
|
|
@ -390,6 +395,9 @@ function addCustomDiv(prompt_action,tabId,mailMessageId) {
|
|||
customBtn.addEventListener("click", () => { customTextBtnClick({customBtn:customBtn,customLoading:customLoading,customDiv:customDiv}) });
|
||||
customTextArea.addEventListener("keydown", (event) => { if(event.code == "Enter" && event.ctrlKey) customTextBtnClick({customBtn:customBtn,customLoading:customLoading,customDiv:customDiv}) });
|
||||
customDiv.appendChild(customBtn);
|
||||
let customStep = document.createElement('div');
|
||||
customStep.id = 'mzta-custom_step';
|
||||
customDiv.appendChild(customStep);
|
||||
fixedDiv.appendChild(customDiv);
|
||||
|
||||
// light background hint with diagonal thick arrow
|
||||
|
|
@ -466,14 +474,54 @@ function createReplyToAllIcon() {
|
|||
return svg;
|
||||
}
|
||||
|
||||
function renderCustomTextStep() {
|
||||
const currentItem = _customTextArray[_currentCustomTextIndex];
|
||||
const infoDiv = document.getElementById('mzta-custom_info');
|
||||
const customTextArea = document.getElementById('mzta-custom_textarea');
|
||||
const customStep = document.getElementById('mzta-custom_step');
|
||||
|
||||
customTextArea.value = "";
|
||||
infoDiv.textContent = browser.i18n.getMessage("chatgpt_win_custom_text");
|
||||
|
||||
if (currentItem.info && currentItem.info.trim() !== "") {
|
||||
infoDiv.appendChild(document.createElement("br"));
|
||||
const infoSpan = document.createElement("span");
|
||||
infoSpan.textContent = "[" + browser.i18n.getMessage("customPrompts_form_label_ID") + ": " + currentItem.info + "]";
|
||||
infoDiv.appendChild(infoSpan);
|
||||
}
|
||||
|
||||
if(_customTextArray.length > 1) {
|
||||
customStep.textContent = (_currentCustomTextIndex + 1) + "/" + _customTextArray.length;
|
||||
customStep.style.display = 'block';
|
||||
} else {
|
||||
customStep.style.display = 'none';
|
||||
}
|
||||
|
||||
customTextArea.focus();
|
||||
}
|
||||
|
||||
function customTextBtnClick(args) {
|
||||
const customText = document.getElementById('mzta-custom_textarea').value;
|
||||
args.customBtn.disabled = true;
|
||||
args.customBtn.classList.add('disabled');
|
||||
args.customLoading.style.display = 'inline-block';
|
||||
args.customLoading.style.display = 'none';
|
||||
doProceed(current_message,customText);
|
||||
args.customDiv.style.display = 'none';
|
||||
|
||||
if (_customTextArray[_currentCustomTextIndex]) {
|
||||
_customTextArray[_currentCustomTextIndex].custom_text = customText;
|
||||
}
|
||||
|
||||
_currentCustomTextIndex++;
|
||||
|
||||
if (_currentCustomTextIndex < _customTextArray.length) {
|
||||
renderCustomTextStep();
|
||||
} else {
|
||||
args.customBtn.disabled = true;
|
||||
args.customBtn.classList.add('disabled');
|
||||
args.customLoading.style.display = 'inline-block';
|
||||
args.customLoading.style.display = 'none';
|
||||
doProceed(current_message, _customTextArray);
|
||||
args.customDiv.style.display = 'none';
|
||||
|
||||
args.customBtn.disabled = false;
|
||||
args.customBtn.classList.remove('disabled');
|
||||
}
|
||||
}
|
||||
|
||||
function checkGPTModel(model) {
|
||||
|
|
@ -547,8 +595,14 @@ function checkLoggedIn(){
|
|||
}
|
||||
|
||||
function showCustomTextField(){
|
||||
let rawArray = current_message.prompt_info?.custom_text_array;
|
||||
_customTextArray = Array.isArray(rawArray) ? rawArray : [];
|
||||
if (_customTextArray.length === 0) {
|
||||
_customTextArray.push({ placeholder: "{%additional_text%}", info: "" });
|
||||
}
|
||||
_currentCustomTextIndex = 0;
|
||||
document.getElementById('mzta-custom_text').style.display = 'block';
|
||||
document.getElementById('mzta-custom_textarea').focus();
|
||||
renderCustomTextStep();
|
||||
}
|
||||
|
||||
async function doProceed(message, customText = ''){
|
||||
|
|
@ -558,16 +612,21 @@ async function doProceed(message, customText = ''){
|
|||
await checkGPTModel(_gpt_model);
|
||||
}
|
||||
let final_prompt = message.prompt;
|
||||
// console.log(">>>>>>>>>>>> doProceed customText: " + customText);
|
||||
// console.log(">>>>>>>>>>>> doProceed final_prompt: " + final_prompt);
|
||||
// console.log(">>>>>>>>>>>> doProceed mztaPhDefVal: " + JSON.stringify(mztaPhDefVal));
|
||||
//check if there is the additional_text placeholder
|
||||
if(final_prompt.includes('{%additional_text%}')){
|
||||
// console.log(">>>>>>>>>>>> found ph customText: " + customText);
|
||||
final_prompt = final_prompt.replace('{%additional_text%}', customText || (mztaPhDefVal == '1'?'':'{%additional_text%}'));
|
||||
}else{
|
||||
if(customText != ''){
|
||||
final_prompt += ' '+customText;
|
||||
|
||||
if (Array.isArray(customText)) {
|
||||
customText.forEach(obj => {
|
||||
let escapedPH = obj.placeholder.replace(/[.*+?^{$}()|[\\]\\\\]/g, '\\\\$&');
|
||||
let regex = new RegExp(escapedPH, 'g');
|
||||
final_prompt = final_prompt.replace(regex, obj.custom_text);
|
||||
});
|
||||
} else {
|
||||
//check if there is the additional_text placeholder
|
||||
if(final_prompt.includes('{%additional_text%}')){
|
||||
final_prompt = final_prompt.replace('{%additional_text%}', customText || (mztaPhDefVal == '1'?'':'{%additional_text%}'));
|
||||
}else{
|
||||
if(customText != ''){
|
||||
final_prompt += ' '+customText;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -617,7 +617,40 @@ switch (message.command) {
|
|||
|
||||
break;
|
||||
|
||||
case "showSpamCheckInProgress":
|
||||
const oldBanner = document.getElementById('mzta-spam-report-banner');
|
||||
if(oldBanner) oldBanner.remove();
|
||||
|
||||
if(document.getElementById('mzta-spam-check-progress')) return Promise.resolve(true);
|
||||
|
||||
const containerProgress = document.createElement('div');
|
||||
containerProgress.id = 'mzta-spam-check-progress';
|
||||
|
||||
const isDarkProgress = window.matchMedia && window.matchMedia('(prefers-color-scheme: dark)').matches;
|
||||
|
||||
let bgColorProgress = isDarkProgress ? '#003366' : '#e6f2ff';
|
||||
let textColorProgress = isDarkProgress ? '#cce5ff' : '#004085';
|
||||
let borderColorProgress = isDarkProgress ? '#004085' : '#b8daff';
|
||||
|
||||
containerProgress.style.cssText = `background-color: ${bgColorProgress}; color: ${textColorProgress}; border-bottom: 1px solid ${borderColorProgress}; padding: 8px 12px; font-family: system-ui, -apple-system, sans-serif; font-size: 13px; display: flex; align-items: center; gap: 15px; width: 100%; box-sizing: border-box;`;
|
||||
|
||||
const textProgress = document.createElement('strong');
|
||||
textProgress.textContent = browser.i18n.getMessage("spam_check_in_progress");
|
||||
|
||||
const loadingImg = document.createElement('img');
|
||||
loadingImg.src = browser.runtime.getURL("/images/loading.gif");
|
||||
loadingImg.style.cssText = "height: 16px; width: 16px;";
|
||||
|
||||
containerProgress.appendChild(loadingImg);
|
||||
containerProgress.appendChild(textProgress);
|
||||
|
||||
document.body.insertBefore(containerProgress, document.body.firstChild);
|
||||
return Promise.resolve(true);
|
||||
|
||||
case "showSpamReport":
|
||||
const progressBanner = document.getElementById('mzta-spam-check-progress');
|
||||
if(progressBanner) progressBanner.remove();
|
||||
|
||||
const data = message.data;
|
||||
if(document.getElementById('mzta-spam-report-banner')) return Promise.resolve(true);
|
||||
|
||||
|
|
@ -630,7 +663,11 @@ switch (message.command) {
|
|||
let textColor = '#333';
|
||||
let borderColor = '#ccc';
|
||||
|
||||
if (data.spamValue >= (data.SpamThreshold || 50)) {
|
||||
if (data.spamValue == -999) {
|
||||
bgColor = isDark ? '#332701' : '#fff3cd';
|
||||
textColor = isDark ? '#ffeb80' : '#856404';
|
||||
borderColor = isDark ? '#664d03' : '#ffeeba';
|
||||
} else if (data.spamValue >= (data.SpamThreshold || 50)) {
|
||||
bgColor = isDark ? '#5a1a1a' : '#ffe6e6';
|
||||
textColor = isDark ? '#ffcccc' : '#cc0000';
|
||||
borderColor = '#cc0000';
|
||||
|
|
@ -640,13 +677,21 @@ switch (message.command) {
|
|||
borderColor = '#006600';
|
||||
}
|
||||
|
||||
container.style.cssText = `background-color: ${bgColor}; color: ${textColor}; border-bottom: 1px solid ${borderColor}; padding: 8px 12px; font-family: system-ui, -apple-system, sans-serif; font-size: 13px; display: flex; align-items: center; gap: 15px; width: 100%; box-sizing: border-box;`;
|
||||
container.style.cssText = `background-color: ${bgColor}; color: ${textColor}; border-bottom: 1px solid ${borderColor}; padding: 8px 12px; font-family: system-ui, -apple-system, sans-serif; font-size: 13px; display: flex; align-items: start; gap: 15px; width: 100%; box-sizing: border-box;`;
|
||||
|
||||
const scoreText = document.createElement('strong');
|
||||
scoreText.textContent = ((data.spamValue >= (data.SpamThreshold || 50)) ? browser.i18n.getMessage("Spam") : browser.i18n.getMessage("Valid")) + " [" + data.spamValue + "/100]";
|
||||
if (data.spamValue == -999) {
|
||||
scoreText.textContent = browser.i18n.getMessage("apiwebchat_error");
|
||||
} else {
|
||||
scoreText.textContent = ((data.spamValue >= (data.SpamThreshold || 50)) ? browser.i18n.getMessage("Spam") : browser.i18n.getMessage("Valid")) + " [" + data.spamValue + "/100]";
|
||||
}
|
||||
|
||||
const reasonText = document.createElement('span');
|
||||
reasonText.textContent = browser.i18n.getMessage("Explanation") + ": " + data.explanation;
|
||||
if (data.spamValue == -999) {
|
||||
reasonText.textContent = data.explanation;
|
||||
} else {
|
||||
reasonText.textContent = browser.i18n.getMessage("Explanation") + ": " + data.explanation;
|
||||
}
|
||||
|
||||
const closeBtn = document.createElement('span');
|
||||
closeBtn.textContent = '×';
|
||||
|
|
|
|||
|
|
@ -192,6 +192,13 @@ export class mzta_Menus {
|
|||
only_quoted_text: only_quoted_text,
|
||||
tags_full_list: tags_full_list
|
||||
});
|
||||
|
||||
curr_prompt.custom_text_array = [];
|
||||
|
||||
if(placeholdersUtils.hasPlaceholder(curr_prompt.text, 'additional_text')){
|
||||
curr_prompt.custom_text_array = placeholdersUtils.getPlaceholdersAdditionalTextArray(curr_prompt.text);
|
||||
}
|
||||
// console.log(">>>>>>>>>>>>>>>>>>> curr_prompt.custom_text_array: " + JSON.stringify(curr_prompt.custom_text_array));
|
||||
|
||||
switch(curr_prompt.id){
|
||||
case 'prompt_translate_this':
|
||||
|
|
|
|||
|
|
@ -148,7 +148,7 @@ const defaultPlaceholders = [
|
|||
default_value: "",
|
||||
type: 0,
|
||||
is_default: "1",
|
||||
is_dynamic: "0",
|
||||
is_dynamic: "1",
|
||||
enabled: 1,
|
||||
},
|
||||
{
|
||||
|
|
@ -427,7 +427,7 @@ export const placeholdersUtils = {
|
|||
// console.log(">>>>>>>>>> replacePlaceholders match: " + JSON.stringify(match));
|
||||
// console.log(">>>>>>>>>> replacePlaceholders p1: " + JSON.stringify(p1));
|
||||
// p1 contains the key inside {% %}
|
||||
if (skip_additional_text && (p1 === 'additional_text')) {
|
||||
if (skip_additional_text && ((p1 === 'additional_text') || (p1.startsWith('additional_text:')))) {
|
||||
return match;
|
||||
}
|
||||
const currPlaceholder = defaultPlaceholders.find(ph => (ph.id === p1) || (ph.is_dynamic == 1 && p1.startsWith(ph.id + ':')));
|
||||
|
|
@ -436,7 +436,7 @@ export const placeholdersUtils = {
|
|||
return match;
|
||||
}
|
||||
// Replace if found, otherwise keep the original or substitute with default value
|
||||
return replacements[p1] || (use_default_value ? currPlaceholder.default_value : match);
|
||||
return replacements[p1] || replacements[currPlaceholder.id] || (use_default_value ? currPlaceholder.default_value : match);
|
||||
});
|
||||
},
|
||||
|
||||
|
|
@ -459,7 +459,7 @@ export const placeholdersUtils = {
|
|||
// If a specific placeholder is provided, we search for it
|
||||
if (placeholder !== "") {
|
||||
// Dynamically build the regex for the specific placeholder
|
||||
regex = new RegExp(`{%\s*${placeholder}\s*%}`);
|
||||
regex = new RegExp(`{%\s*${placeholder}(:.*?)?\s*%}`);
|
||||
} else {
|
||||
// Otherwise, we search for any placeholder in the format {% ... %}
|
||||
regex = /{%\s*(.*?)\s*%}/;
|
||||
|
|
@ -485,6 +485,24 @@ export const placeholdersUtils = {
|
|||
return regex.test(text);
|
||||
},
|
||||
|
||||
getPlaceholdersAdditionalTextArray(prompt_text){
|
||||
const regex = /{%\s*additional_text(?::(.*?))?\s*%}/g;
|
||||
let matches = [];
|
||||
let match;
|
||||
let foundIds = new Set();
|
||||
while ((match = regex.exec(prompt_text)) !== null) {
|
||||
let info = match[1] ? match[1].trim() : "";
|
||||
if (!foundIds.has(info)) {
|
||||
foundIds.add(info);
|
||||
matches.push({
|
||||
placeholder: match[0],
|
||||
info: info
|
||||
});
|
||||
}
|
||||
}
|
||||
return matches;
|
||||
},
|
||||
|
||||
async getPlaceholdersValues(args) {
|
||||
const {
|
||||
prompt_text = "",
|
||||
|
|
|
|||
|
|
@ -19,11 +19,35 @@
|
|||
export const taSpamReport = {
|
||||
logger: console,
|
||||
_data_prefix: 'mzta-spam-report-',
|
||||
_processing_prefix: 'mzta-spam-processing-',
|
||||
_max_reports: 100,
|
||||
|
||||
async setProcessing(data_id) {
|
||||
const key = this._processing_prefix + data_id;
|
||||
await browser.storage.session.set({ [key]: true });
|
||||
},
|
||||
|
||||
async isProcessing(data_id) {
|
||||
const key = this._processing_prefix + data_id;
|
||||
let output = await browser.storage.session.get(key);
|
||||
return output[key] || false;
|
||||
},
|
||||
|
||||
async saveReportData(data, data_id) {
|
||||
const key = this._data_prefix + data_id;
|
||||
await browser.storage.session.set({ [key]: data });
|
||||
await browser.storage.session.remove(this._processing_prefix + data_id);
|
||||
},
|
||||
|
||||
async saveError(data_id, error_message) {
|
||||
let data = {
|
||||
spamValue: -999,
|
||||
explanation: error_message,
|
||||
report_date: new Date(),
|
||||
headerMessageId: data_id
|
||||
};
|
||||
await this.saveReportData(data, data_id);
|
||||
return data;
|
||||
},
|
||||
|
||||
async loadReportData(data_id) {
|
||||
|
|
@ -35,6 +59,7 @@ export const taSpamReport = {
|
|||
async removeReportData(data_id) {
|
||||
const key = this._data_prefix + data_id;
|
||||
await browser.storage.session.remove(key);
|
||||
await browser.storage.session.remove(this._processing_prefix + data_id);
|
||||
},
|
||||
|
||||
async getAllReportData() {
|
||||
|
|
@ -52,7 +77,7 @@ export const taSpamReport = {
|
|||
|
||||
async clearReportData() {
|
||||
let allData = await browser.storage.session.get(null);
|
||||
let keysToDelete = Object.keys(allData).filter(key => key.startsWith(this._data_prefix));
|
||||
let keysToDelete = Object.keys(allData).filter(key => key.startsWith(this._data_prefix) || key.startsWith(this._processing_prefix));
|
||||
|
||||
for (let key of keysToDelete) {
|
||||
await browser.storage.session.remove(key);
|
||||
|
|
|
|||
|
|
@ -59,6 +59,13 @@ export const taPromptUtils = {
|
|||
if(placeholdersUtils.hasCustomPlaceholder(curr_prompt.text)){
|
||||
curr_prompt.text = await placeholdersUtils.replaceCustomPlaceholders(curr_prompt.text);
|
||||
}
|
||||
|
||||
// Replace all {%additional_text%} with {%additional_text:N%}
|
||||
let additionalTextCounter = 1;
|
||||
curr_prompt.text = curr_prompt.text.replace(/{%\s*additional_text\s*%}/g, () => {
|
||||
return `{%additional_text:#${additionalTextCounter++}%}`;
|
||||
});
|
||||
|
||||
let finalSubs = await placeholdersUtils.getPlaceholdersValues({
|
||||
prompt_text: curr_prompt.text,
|
||||
curr_message: curr_message,
|
||||
|
|
|
|||
|
|
@ -417,6 +417,8 @@ messenger.runtime.onMessage.addListener((message, sender, sendResponse) => {
|
|||
let report = await taSpamReport.loadReportData(message.headerMessageId);
|
||||
if (report) {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSpamReport", data: report });
|
||||
} else if (await taSpamReport.isProcessing(message.headerMessageId)) {
|
||||
browser.tabs.sendMessage(tabId, { command: "showSpamCheckInProgress" });
|
||||
}
|
||||
} catch (e) {
|
||||
taLog.error("Error in checkSpamReport: " + e);
|
||||
|
|
@ -475,7 +477,7 @@ async function openChatGPT(promptText, action, curr_tabId, prompt_name = '', do_
|
|||
let rand_call_id = '_chatgptweb_' + generateCallID();
|
||||
let call_opt = '';
|
||||
|
||||
let _wait_time = 1000;
|
||||
let _wait_time = prefs.chatgpt_web_load_wait_time;
|
||||
let _base_url = "https://chatgpt.com";
|
||||
let _webproject_set = false;
|
||||
let _custom_gpt_set = false;
|
||||
|
|
@ -503,7 +505,7 @@ async function openChatGPT(promptText, action, curr_tabId, prompt_name = '', do_
|
|||
if(!_use_prompt_info_custom_gpt && ((prompt_info.chatgpt_web_project != '') || (prefs.chatgpt_web_project != ''))){
|
||||
_base_url += _web_project;
|
||||
_webproject_set = true;
|
||||
_wait_time = 2000;
|
||||
_wait_time += 1000;
|
||||
}
|
||||
if(!_webproject_set && ((prompt_info.chatgpt_web_custom_gpt != '') || (prefs.chatgpt_web_custom_gpt != ''))){
|
||||
_base_url += _custom_gpt;
|
||||
|
|
@ -561,7 +563,7 @@ async function openChatGPT(promptText, action, curr_tabId, prompt_name = '', do_
|
|||
let mailMessageId = -1;
|
||||
if(mailMessage) mailMessageId = mailMessage.id;
|
||||
promptText = convertNewlinesToParagraphs(promptText);
|
||||
browser.tabs.sendMessage(createdTab.id, { command: "chatgpt_send", prompt: promptText, action: action, tabId: curr_tabId, mailMessageId: mailMessageId});
|
||||
browser.tabs.sendMessage(createdTab.id, { command: "chatgpt_send", prompt: promptText, action: action, tabId: curr_tabId, mailMessageId: mailMessageId, prompt_info: prompt_info});
|
||||
taLog.log('[ChatGPT Web] Connection succeded!');
|
||||
taLog.log("[ThunderAI] ChatGPT Web script injected successfully");
|
||||
browser.runtime.onMessage.removeListener(listener);
|
||||
|
|
@ -1086,83 +1088,16 @@ browser.menus.onClicked.addListener( (info, tab) => {
|
|||
if(info.menuItemId === contextMenuID_Summarize) {
|
||||
_summarize = true;
|
||||
}
|
||||
if(_add_tags || _spamfilter){
|
||||
processEmails(getMessages(info.selectedMessages), _add_tags, _spamfilter);
|
||||
}
|
||||
if(_summarize) {
|
||||
// info.selectedMessages is of type MessageList
|
||||
summarizeEmails(getMessages(info.selectedMessages));
|
||||
if(_add_tags || _spamfilter || _summarize){
|
||||
processEmails({
|
||||
messages: getMessages(info.selectedMessages),
|
||||
addTagsAuto: _add_tags,
|
||||
spamFilter: _spamfilter,
|
||||
summarize: _summarize
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
async function summarizeEmails(messages) {
|
||||
taWorkingStatus.startWorking();
|
||||
|
||||
// we have three prompts, the actual assignment for the LLM, the email
|
||||
// template prompt, and the email separator prompt
|
||||
const specialPrompts = await getSpecialPrompts();
|
||||
const prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize');
|
||||
const prompt_email = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template');
|
||||
const prompt_email_separator = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_separator');
|
||||
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
const chatgpt_lang = await taPromptUtils.getDefaultLang(prompt);
|
||||
|
||||
// replace placeholders in the prompts the assignment prompt and email
|
||||
// separator prompt do not have a message as context, so there is only
|
||||
// limited things to replace
|
||||
const prompt_string = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: prompt,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
});
|
||||
const prompt_email_separator_string = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: prompt_email_separator,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
});
|
||||
|
||||
|
||||
// assemble all email messages into one string and add the assignment prompt
|
||||
const messages_list = [];
|
||||
for await (let curr_message of messages) {
|
||||
|
||||
// extract body of current message as text
|
||||
const curr_message_full = await browser.messages.getFull(curr_message.id);
|
||||
const curr_body_full_html = getMailBody(curr_message_full);
|
||||
const curr_body_full_text = htmlBodyToPlainText(curr_body_full_html.html);
|
||||
if( curr_body_full_text.length === 0) {
|
||||
taLog.log("No HTML found in the message body, using plain text...");
|
||||
curr_body_full_text = curr_message_full.text;
|
||||
}
|
||||
|
||||
messages_list.push(await taPromptUtils.preparePrompt({
|
||||
curr_prompt: prompt_email,
|
||||
curr_message: curr_message,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
body_text: curr_body_full_text,
|
||||
subject_text: curr_message_full.headers.subject,
|
||||
msg_text: curr_body_full_html,
|
||||
}));
|
||||
};
|
||||
const messages_string = messages_list.join(prompt_email_separator_string);
|
||||
|
||||
const full_prompt = prompt_string + prompt_email_separator_string + messages_string;
|
||||
|
||||
// console.log(full_prompt);
|
||||
|
||||
// send the prompt to the chat interface
|
||||
openChatGPT(
|
||||
full_prompt,
|
||||
prompt.action,
|
||||
tabs[0].id,
|
||||
prompt.name,
|
||||
prompt.need_custom_text,
|
||||
prompt
|
||||
)
|
||||
|
||||
taWorkingStatus.stopWorking();
|
||||
return {ok : '1'};
|
||||
}
|
||||
|
||||
|
||||
// Listening for new received emails
|
||||
const newEmailListener = (folder, messagesList) => {
|
||||
|
|
@ -1181,7 +1116,11 @@ const newEmailListener = (folder, messagesList) => {
|
|||
|
||||
let add_tags_auto_enabled = prefs_init.add_tags && prefs_init.add_tags_auto;
|
||||
|
||||
await processEmails(messages, add_tags_auto_enabled, prefs_init.spamfilter);
|
||||
await processEmails({
|
||||
messages: messages,
|
||||
addTagsAuto: add_tags_auto_enabled,
|
||||
spamFilter: prefs_init.spamfilter
|
||||
});
|
||||
|
||||
if(prefs_init.spamfilter){
|
||||
taSpamReport.truncReportData();
|
||||
|
|
@ -1191,166 +1130,262 @@ const newEmailListener = (folder, messagesList) => {
|
|||
return _newEmailListener();
|
||||
}
|
||||
|
||||
async function processEmails(messages, addTagsAuto, spamFilter) {
|
||||
taWorkingStatus.startWorking();
|
||||
|
||||
let prefs_aats = await browser.storage.sync.get({
|
||||
add_tags_maxnum: prefs_default.add_tags_maxnum,
|
||||
connection_type: prefs_default.connection_type,
|
||||
add_tags_force_lang: prefs_default.add_tags_force_lang,
|
||||
default_chatgpt_lang: prefs_default.default_chatgpt_lang,
|
||||
add_tags_auto_force_existing: prefs_default.add_tags_auto_force_existing,
|
||||
add_tags_enabled_accounts: prefs_default.add_tags_enabled_accounts,
|
||||
add_tags_exclusions_exact_match: prefs_default.add_tags_exclusions_exact_match,
|
||||
add_tags_auto_uselist: prefs_default.add_tags_auto_uselist,
|
||||
add_tags_auto_uselist_list: prefs_default.add_tags_auto_uselist_list,
|
||||
spamfilter_enabled_accounts: prefs_default.spamfilter_enabled_accounts,
|
||||
...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type']),
|
||||
do_debug: prefs_default.do_debug,
|
||||
});
|
||||
// console.log(">>>>>>>>>>>>>>>> prefs_aats: " + JSON.stringify(prefs_aats));
|
||||
for await (let message of messages) {
|
||||
let curr_fullMessage = null;
|
||||
let msg_text = null;
|
||||
let body_text = '';
|
||||
|
||||
if (addTagsAuto || spamFilter) {
|
||||
curr_fullMessage = await browser.messages.getFull(message.id);
|
||||
msg_text = getMailBody(curr_fullMessage);
|
||||
taLog.log("Starting from the HTML body if present and converting to plain text...");
|
||||
body_text = htmlBodyToPlainText(msg_text.html);
|
||||
if( body_text.length == 0 ){
|
||||
taLog.log("No HTML found in the message body, using plain text...");
|
||||
body_text = msg_text.text.replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (addTagsAuto) {
|
||||
if(prefs_aats.add_tags_enabled_accounts.length > 0){
|
||||
let accountId = message.folder.accountId;
|
||||
if(!prefs_aats.add_tags_enabled_accounts.includes(accountId)){
|
||||
taLog.log("Account " + accountId + " not enabled for add_tags, skipping...");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let specialFullPrompt_add_tags = '';
|
||||
let curr_prompt_add_tags = menus.allPrompts.find(p => p.id === 'prompt_add_tags');
|
||||
let tags_full_list = await getTagsList();
|
||||
// console.log(">>>>>>>>>>>>> curr_prompt_add_tags: " + JSON.stringify(curr_prompt_add_tags));
|
||||
let chatgpt_lang = await taPromptUtils.getDefaultLang(curr_prompt_add_tags);
|
||||
specialFullPrompt_add_tags = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: curr_prompt_add_tags,
|
||||
curr_message: message,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
body_text: body_text,
|
||||
subject_text: curr_fullMessage.headers.subject,
|
||||
msg_text: msg_text,
|
||||
tags_full_list: tags_full_list
|
||||
});
|
||||
specialFullPrompt_add_tags = taPromptUtils.finalizePrompt_add_tags(specialFullPrompt_add_tags, prefs_aats.add_tags_maxnum, prefs_aats.add_tags_force_lang, prefs_aats.default_chatgpt_lang, prefs_aats.add_tags_auto_uselist, prefs_aats.add_tags_auto_uselist_list);
|
||||
taLog.log("Special prompt: " + specialFullPrompt_add_tags);
|
||||
// console.log(">>>>>>>>>> curr_prompt_add_tags.model: " + curr_prompt_add_tags.model);
|
||||
// console.log(">>>>>>>>>>>>>>>>> getConnectionType add_tags:" + JSON.stringify(getConnectionType(prefs_aats, curr_prompt_add_tags, 'add_tags')));
|
||||
let cmd_addTags = new mzta_specialCommand({
|
||||
prompt: specialFullPrompt_add_tags,
|
||||
llm: getConnectionType(prefs_aats, curr_prompt_add_tags, 'add_tags'),
|
||||
custom_model: curr_prompt_add_tags.model ? curr_prompt_add_tags.model : '',
|
||||
do_debug: prefs_aats.do_debug,
|
||||
config: curr_prompt_add_tags
|
||||
});
|
||||
await cmd_addTags.initWorker();
|
||||
let tags_current_email = [];
|
||||
try {
|
||||
tags_current_email = taPromptUtils.getTagsFromResponse(await cmd_addTags.sendPrompt(), prefs_aats.add_tags_auto_uselist, prefs_aats.add_tags_auto_uselist_list);
|
||||
} catch (err) {
|
||||
console.error("[ThunderAI | Auto add_tags] Error getting tags: ", err);
|
||||
}
|
||||
taLog.log("tags_current_email: " + JSON.stringify(tags_current_email));
|
||||
let _data = { messageId: message.id, tags: tags_current_email };
|
||||
_assign_tags(_data, !prefs_aats.add_tags_auto_force_existing, prefs_aats.add_tags_exclusions_exact_match);
|
||||
}
|
||||
|
||||
if (spamFilter) {
|
||||
if(prefs_aats.spamfilter_enabled_accounts.length > 0){
|
||||
let accountId = message.folder.accountId;
|
||||
if(!prefs_aats.spamfilter_enabled_accounts.includes(accountId)){
|
||||
taLog.log("Account " + accountId + " not enabled for spamfilter, skipping...");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let curr_prompt_spamfilter = await getSpamFilterPrompt();
|
||||
// console.log(">>>>>>>>>>>>> curr_prompt_spamfilter: " + JSON.stringify(curr_prompt_spamfilter));
|
||||
let chatgpt_lang = await taPromptUtils.getDefaultLang(curr_prompt_spamfilter);
|
||||
let specialFullPrompt_spamfilter = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: curr_prompt_spamfilter,
|
||||
curr_message: message,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
body_text: body_text,
|
||||
subject_text: curr_fullMessage.headers.subject,
|
||||
msg_text: msg_text
|
||||
});
|
||||
taLog.log("Special prompt: " + specialFullPrompt_spamfilter);
|
||||
// console.log(">>>>>>>> Special prompt for spamfilter: " + specialFullPrompt_spamfilter);
|
||||
let cmd_spamfilter = new mzta_specialCommand({
|
||||
prompt: specialFullPrompt_spamfilter,
|
||||
llm: getConnectionType(prefs_aats, curr_prompt_spamfilter, 'spamfilter'),
|
||||
custom_model: curr_prompt_spamfilter.model ? curr_prompt_spamfilter.model : '',
|
||||
do_debug: prefs_aats.do_debug,
|
||||
config: curr_prompt_spamfilter
|
||||
});
|
||||
await cmd_spamfilter.initWorker();
|
||||
let spamfilter_result = '';
|
||||
taLog.log("Sending the prompt...");
|
||||
try {
|
||||
spamfilter_result = (await cmd_spamfilter.sendPrompt()).trim();
|
||||
} catch (err) {
|
||||
console.error("[ThunderAI | SpamFilter] Error getting spamfilter: ", err);
|
||||
}
|
||||
taLog.log("spamfilter_result: " + spamfilter_result);
|
||||
let jsonObj = {};
|
||||
taLog.log("Decoding the AI response...");
|
||||
try {
|
||||
jsonObj = extractJsonObject(spamfilter_result);
|
||||
} catch (e) {
|
||||
console.error("[ThunderAI | SpamFilter] Error extracting JSON from AI response: ", e);
|
||||
}
|
||||
taLog.log("SpamFilter jsonObj: " + JSON.stringify(jsonObj));
|
||||
|
||||
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;
|
||||
|
||||
if (jsonObj.spamValue >= prefs_init.spamfilter_threshold) {
|
||||
taLog.log("Marking as spam [" + message.headerMessageId + "]");
|
||||
messenger.messages.update(message.id, { junk: true });
|
||||
let spamFolder = await messenger.folders.query({ accountId: message.folder.accountId, specialUse: ['junk'] });
|
||||
messenger.messages.move([message.id], spamFolder[0].id);
|
||||
report_data.moved = true;
|
||||
taLog.log("Marked as spam [" + message.headerMessageId + "]");
|
||||
}
|
||||
|
||||
taSpamReport.saveReportData(report_data, message.headerMessageId);
|
||||
|
||||
// Check if the message is currently displayed and update the banner
|
||||
if (prefs_init.spamfilter_show_msg_panel) {
|
||||
let tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
if (tabs.length > 0) {
|
||||
let activeTab = tabs[0];
|
||||
let displayedMessage = await browser.messageDisplay.getDisplayedMessage(activeTab.id);
|
||||
if (displayedMessage && displayedMessage.id === message.id) {
|
||||
browser.tabs.sendMessage(activeTab.id, { command: "showSpamReport", data: report_data });
|
||||
}
|
||||
async function updateSpamPanel(messageId, command, data = null) {
|
||||
if (prefs_init.spamfilter_show_msg_panel) {
|
||||
let tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
if (tabs.length > 0) {
|
||||
let activeTab = tabs[0];
|
||||
let displayedMessage = await browser.messageDisplay.getDisplayedMessage(activeTab.id);
|
||||
if (displayedMessage && displayedMessage.headerMessageId === messageId) {
|
||||
let msg = { command: command };
|
||||
if (data) {
|
||||
msg.data = data;
|
||||
}
|
||||
browser.tabs.sendMessage(activeTab.id, msg);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function processEmails(args) {
|
||||
const {
|
||||
messages,
|
||||
addTagsAuto = false,
|
||||
spamFilter = false,
|
||||
summarize = false
|
||||
} = args;
|
||||
|
||||
taWorkingStatus.startWorking();
|
||||
|
||||
// We keep two different loops, one for addTagsAuto and spamFilter and one for summarize
|
||||
// because summarize is never called when an email is received, but only when using the context menu item
|
||||
|
||||
if (addTagsAuto || spamFilter) {
|
||||
let prefs_aats = await browser.storage.sync.get({
|
||||
add_tags_maxnum: prefs_default.add_tags_maxnum,
|
||||
connection_type: prefs_default.connection_type,
|
||||
add_tags_force_lang: prefs_default.add_tags_force_lang,
|
||||
default_chatgpt_lang: prefs_default.default_chatgpt_lang,
|
||||
add_tags_auto_force_existing: prefs_default.add_tags_auto_force_existing,
|
||||
add_tags_enabled_accounts: prefs_default.add_tags_enabled_accounts,
|
||||
add_tags_exclusions_exact_match: prefs_default.add_tags_exclusions_exact_match,
|
||||
add_tags_auto_uselist: prefs_default.add_tags_auto_uselist,
|
||||
add_tags_auto_uselist_list: prefs_default.add_tags_auto_uselist_list,
|
||||
spamfilter_enabled_accounts: prefs_default.spamfilter_enabled_accounts,
|
||||
...getDynamicSettingsDefaults(['use_specific_integration', 'connection_type']),
|
||||
do_debug: prefs_default.do_debug,
|
||||
});
|
||||
// console.log(">>>>>>>>>>>>>>>> prefs_aats: " + JSON.stringify(prefs_aats));
|
||||
for await (let message of messages) {
|
||||
let curr_fullMessage = null;
|
||||
let msg_text = null;
|
||||
let body_text = '';
|
||||
|
||||
if (addTagsAuto || spamFilter) {
|
||||
curr_fullMessage = await browser.messages.getFull(message.id);
|
||||
msg_text = getMailBody(curr_fullMessage);
|
||||
taLog.log("Starting from the HTML body if present and converting to plain text...");
|
||||
body_text = htmlBodyToPlainText(msg_text.html);
|
||||
if( body_text.length == 0 ){
|
||||
taLog.log("No HTML found in the message body, using plain text...");
|
||||
body_text = msg_text.text.replace(/\s+/g, ' ').trim();
|
||||
}
|
||||
}
|
||||
|
||||
if (addTagsAuto) {
|
||||
if(prefs_aats.add_tags_enabled_accounts.length > 0){
|
||||
let accountId = message.folder.accountId;
|
||||
if(!prefs_aats.add_tags_enabled_accounts.includes(accountId)){
|
||||
taLog.log("Account " + accountId + " not enabled for add_tags, skipping...");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
let specialFullPrompt_add_tags = '';
|
||||
let curr_prompt_add_tags = menus.allPrompts.find(p => p.id === 'prompt_add_tags');
|
||||
let tags_full_list = await getTagsList();
|
||||
// console.log(">>>>>>>>>>>>> curr_prompt_add_tags: " + JSON.stringify(curr_prompt_add_tags));
|
||||
let chatgpt_lang = await taPromptUtils.getDefaultLang(curr_prompt_add_tags);
|
||||
specialFullPrompt_add_tags = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: curr_prompt_add_tags,
|
||||
curr_message: message,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
body_text: body_text,
|
||||
subject_text: curr_fullMessage.headers.subject,
|
||||
msg_text: msg_text,
|
||||
tags_full_list: tags_full_list
|
||||
});
|
||||
specialFullPrompt_add_tags = taPromptUtils.finalizePrompt_add_tags(specialFullPrompt_add_tags, prefs_aats.add_tags_maxnum, prefs_aats.add_tags_force_lang, prefs_aats.default_chatgpt_lang, prefs_aats.add_tags_auto_uselist, prefs_aats.add_tags_auto_uselist_list);
|
||||
taLog.log("Special prompt: " + specialFullPrompt_add_tags);
|
||||
// console.log(">>>>>>>>>> curr_prompt_add_tags.model: " + curr_prompt_add_tags.model);
|
||||
// console.log(">>>>>>>>>>>>>>>>> getConnectionType add_tags:" + JSON.stringify(getConnectionType(prefs_aats, curr_prompt_add_tags, 'add_tags')));
|
||||
let cmd_addTags = new mzta_specialCommand({
|
||||
prompt: specialFullPrompt_add_tags,
|
||||
llm: getConnectionType(prefs_aats, curr_prompt_add_tags, 'add_tags'),
|
||||
custom_model: curr_prompt_add_tags.model ? curr_prompt_add_tags.model : '',
|
||||
do_debug: prefs_aats.do_debug,
|
||||
config: curr_prompt_add_tags
|
||||
});
|
||||
await cmd_addTags.initWorker();
|
||||
let tags_current_email = [];
|
||||
try {
|
||||
tags_current_email = taPromptUtils.getTagsFromResponse(await cmd_addTags.sendPrompt(), prefs_aats.add_tags_auto_uselist, prefs_aats.add_tags_auto_uselist_list);
|
||||
} catch (err) {
|
||||
console.error("[ThunderAI | Auto add_tags] Error getting tags: ", err);
|
||||
}
|
||||
taLog.log("tags_current_email: " + JSON.stringify(tags_current_email));
|
||||
let _data = { messageId: message.id, tags: tags_current_email };
|
||||
_assign_tags(_data, !prefs_aats.add_tags_auto_force_existing, prefs_aats.add_tags_exclusions_exact_match);
|
||||
}
|
||||
|
||||
if (spamFilter) {
|
||||
if(prefs_aats.spamfilter_enabled_accounts.length > 0){
|
||||
let accountId = message.folder.accountId;
|
||||
if(!prefs_aats.spamfilter_enabled_accounts.includes(accountId)){
|
||||
taLog.log("Account " + accountId + " not enabled for spamfilter, skipping...");
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
await taSpamReport.removeReportData(message.headerMessageId);
|
||||
await taSpamReport.setProcessing(message.headerMessageId);
|
||||
|
||||
await updateSpamPanel(message.headerMessageId, "showSpamCheckInProgress");
|
||||
|
||||
let curr_prompt_spamfilter = await getSpamFilterPrompt();
|
||||
// console.log(">>>>>>>>>>>>> curr_prompt_spamfilter: " + JSON.stringify(curr_prompt_spamfilter));
|
||||
let chatgpt_lang = await taPromptUtils.getDefaultLang(curr_prompt_spamfilter);
|
||||
let specialFullPrompt_spamfilter = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: curr_prompt_spamfilter,
|
||||
curr_message: message,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
body_text: body_text,
|
||||
subject_text: curr_fullMessage.headers.subject,
|
||||
msg_text: msg_text
|
||||
});
|
||||
taLog.log("Special prompt: " + specialFullPrompt_spamfilter);
|
||||
// console.log(">>>>>>>> Special prompt for spamfilter: " + specialFullPrompt_spamfilter);
|
||||
let cmd_spamfilter = new mzta_specialCommand({
|
||||
prompt: specialFullPrompt_spamfilter,
|
||||
llm: getConnectionType(prefs_aats, curr_prompt_spamfilter, 'spamfilter'),
|
||||
custom_model: curr_prompt_spamfilter.model ? curr_prompt_spamfilter.model : '',
|
||||
do_debug: prefs_aats.do_debug,
|
||||
config: curr_prompt_spamfilter
|
||||
});
|
||||
await cmd_spamfilter.initWorker();
|
||||
let spamfilter_result = '';
|
||||
taLog.log("Sending the prompt...");
|
||||
try {
|
||||
spamfilter_result = (await cmd_spamfilter.sendPrompt()).trim();
|
||||
} catch (err) {
|
||||
console.error("[ThunderAI | SpamFilter] Error getting spamfilter: ", err);
|
||||
let err_data = await taSpamReport.saveError(message.headerMessageId, err.message || String(err));
|
||||
await updateSpamPanel(message.headerMessageId, "showSpamReport", err_data);
|
||||
continue;
|
||||
}
|
||||
taLog.log("spamfilter_result: " + spamfilter_result);
|
||||
let jsonObj = {};
|
||||
taLog.log("Decoding the AI response...");
|
||||
try {
|
||||
jsonObj = extractJsonObject(spamfilter_result);
|
||||
} catch (e) {
|
||||
console.error("[ThunderAI | SpamFilter] Error extracting JSON from AI response: ", e);
|
||||
let err_data = await taSpamReport.saveError(message.headerMessageId, e.message || String(e));
|
||||
await updateSpamPanel(message.headerMessageId, "showSpamReport", err_data);
|
||||
continue;
|
||||
}
|
||||
taLog.log("SpamFilter jsonObj: " + JSON.stringify(jsonObj));
|
||||
|
||||
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;
|
||||
|
||||
if (jsonObj.spamValue >= prefs_init.spamfilter_threshold) {
|
||||
taLog.log("Marking as spam [" + message.headerMessageId + "]");
|
||||
messenger.messages.update(message.id, { junk: true });
|
||||
let spamFolder = await messenger.folders.query({ accountId: message.folder.accountId, specialUse: ['junk'] });
|
||||
messenger.messages.move([message.id], spamFolder[0].id);
|
||||
report_data.moved = true;
|
||||
taLog.log("Marked as spam [" + message.headerMessageId + "]");
|
||||
}
|
||||
|
||||
taSpamReport.saveReportData(report_data, message.headerMessageId);
|
||||
|
||||
// Check if the message is currently displayed and update the banner
|
||||
await updateSpamPanel(message.headerMessageId, "showSpamReport", report_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (summarize) {
|
||||
// we have three prompts, the actual assignment for the LLM, the email
|
||||
// template prompt, and the email separator prompt
|
||||
const specialPrompts = await getSpecialPrompts();
|
||||
const prompt = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize');
|
||||
const prompt_email = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_template');
|
||||
const prompt_email_separator = specialPrompts.find((prompt) => prompt.id === 'prompt_summarize_email_separator');
|
||||
|
||||
const tabs = await browser.tabs.query({ active: true, currentWindow: true });
|
||||
const chatgpt_lang = await taPromptUtils.getDefaultLang(prompt);
|
||||
|
||||
// replace placeholders in the prompts the assignment prompt and email
|
||||
// separator prompt do not have a message as context, so there is only
|
||||
// limited things to replace
|
||||
const prompt_string = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: prompt,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
});
|
||||
const prompt_email_separator_string = await taPromptUtils.preparePrompt({
|
||||
curr_prompt: prompt_email_separator,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
});
|
||||
|
||||
|
||||
// assemble all email messages into one string and add the assignment prompt
|
||||
const messages_list = [];
|
||||
for await (let curr_message of messages) {
|
||||
|
||||
// extract body of current message as text
|
||||
const curr_message_full = await browser.messages.getFull(curr_message.id);
|
||||
const curr_body_full_html = getMailBody(curr_message_full);
|
||||
let curr_body_full_text = htmlBodyToPlainText(curr_body_full_html.html);
|
||||
if( curr_body_full_text.length === 0) {
|
||||
taLog.log("No HTML found in the message body, using plain text...");
|
||||
curr_body_full_text = curr_message_full.text;
|
||||
}
|
||||
|
||||
messages_list.push(await taPromptUtils.preparePrompt({
|
||||
curr_prompt: prompt_email,
|
||||
curr_message: curr_message,
|
||||
chatgpt_lang: chatgpt_lang,
|
||||
body_text: curr_body_full_text,
|
||||
subject_text: curr_message_full.headers.subject,
|
||||
msg_text: curr_body_full_html,
|
||||
}));
|
||||
};
|
||||
const messages_string = messages_list.join(prompt_email_separator_string);
|
||||
|
||||
const full_prompt = prompt_string + prompt_email_separator_string + messages_string;
|
||||
|
||||
// console.log(full_prompt);
|
||||
|
||||
// send the prompt to the chat interface
|
||||
openChatGPT(
|
||||
full_prompt,
|
||||
prompt.action,
|
||||
tabs[0].id,
|
||||
prompt.name,
|
||||
prompt.need_custom_text,
|
||||
prompt
|
||||
);
|
||||
}
|
||||
|
||||
taWorkingStatus.stopWorking();
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ export const prefs_default = {
|
|||
chatgpt_web_tempchat: false,
|
||||
chatgpt_web_project: '',
|
||||
chatgpt_web_custom_gpt: '',
|
||||
chatgpt_web_load_wait_time: 1000,
|
||||
dynamic_menu_force_enter: false,
|
||||
dynamic_menu_order_alphabet: true,
|
||||
placeholders_use_default_value: false,
|
||||
|
|
|
|||
|
|
@ -23,7 +23,10 @@
|
|||
<li><i>[All APIs]</i> Added an option to get a calendar event without selecting some text, but using the full text body of the email [<a href="https://github.com/micz/ThunderAI/issues/518">#518</a>].</li>
|
||||
<li><i>[All APIs]</i> Added a new menu item to create a calendar event from the text saved in the clipboard [<a href="https://github.com/micz/ThunderAI/issues/362">#362</a>].</li>
|
||||
<li>Added a button to copy a prompt in the Custom Prompts page [<a href="https://github.com/micz/ThunderAI/issues/598">#598</a>].</li>
|
||||
<li><i>[All APIs]</i> Showing the spam filter info at the top of the message. The data is saved only for the session in which the message has been checked for spam [<a href="https://github.com/micz/ThunderAI/issues/506">#506</a>].</li>
|
||||
<li><i>[All APIs]</i> Showing the spam filter info at the top of the message. The data is saved only for the session in which the message has been checked for spam [<a href="https://github.com/micz/ThunderAI/issues/506">#506</a>, <a href="https://github.com/micz/ThunderAI/issues/658">#658</a>].</li>
|
||||
<li>Fix: Now it's possibile to use multiple <i>additional_text</i> placeholders in a single prompt, also using custom placeholders [<a href="https://github.com/micz/ThunderAI/issues/554">#554</a>].</li>
|
||||
<li>When using the <i>additional_text</i> placeholder is now possibile to specify an ID that will be shown in the form asking for the text [<a href="https://github.com/micz/ThunderAI/issues/525">#525</a>].</li>
|
||||
<li><i>[ChatGPT Web]</i> Added an option to define a custom time to wait for the page load. Sometimes, on slow PCs, the ChatGPT page loads slowly and ThunderAI inject its content too early. With this option you can adjust the waiting time [<a href="https://github.com/micz/ThunderAI/issues/634">#634</a>].</li>
|
||||
<li>...</li>
|
||||
</ul>
|
||||
<h2>Version 3.8.4 - 10/02/2026</h2>
|
||||
|
|
|
|||
|
|
@ -153,6 +153,17 @@ export async function injectConnectionUI({
|
|||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="conntype_chatgpt_web${tr_class ? ` ${tr_class}` : ''}">
|
||||
<td><label>
|
||||
<span class="opt_title">__MSG_prefs_OptionText_chatgpt_web_load_wait_time__</span>
|
||||
</label></td>
|
||||
<td>
|
||||
<label>
|
||||
<input type="number" id="chatgpt_web_load_wait_time" name="chatgpt_web_load_wait_time" class="option-input" />
|
||||
<br>__MSG_prefs_OptionText_chatgpt_web_load_wait_time_info__
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
<tr class="conntype_chatgpt_web${tr_class ? ` ${tr_class}` : ''}">
|
||||
<td colspan="2" style="padding:0px 2em;text-align:center;"><span>__MSG_OpenChatGPTTab_Info__</span>
|
||||
<br><br><button id="btnChatGPTWeb_Tab">__MSG_OpenChatGPTTab__</button>
|
||||
|
|
|
|||
|
|
@ -1417,7 +1417,7 @@ async function checkPromptsConfigForPlaceholders(textarea){
|
|||
// check additional_text and selected_text placeholders presence and the corrispondent checkboxes
|
||||
let tr_ancestor = textarea.closest('tr');
|
||||
let need_custom_text_element = tr_ancestor.querySelector('.need_custom_text') || tr_ancestor.querySelector('.need_custom_text_new');
|
||||
if(String(curr_text).indexOf('{%additional_text%}') != -1){
|
||||
if(/{%\s*additional_text(?::.*?)?\s*%}/.test(String(curr_text))){
|
||||
if(!need_custom_text_element.checked){
|
||||
need_custom_text_element.closest('.need_custom_text_span').style.border = '2px solid red';
|
||||
}else{
|
||||
|
|
|
|||
Loading…
Reference in a new issue