239 lines
9.9 KiB
JavaScript
239 lines
9.9 KiB
JavaScript
/*
|
|
* ThunderAI [https://micz.it/thunderbird-addon-thunderai/]
|
|
* Copyright (C) 2024 - 2026 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
import { placeholdersUtils } from './mzta-placeholders.js';
|
|
import {
|
|
extractJsonObject,
|
|
getMailBody,
|
|
htmlBodyToPlainText
|
|
} from './mzta-utils.js';
|
|
import { getSpecialPrompts } from './mzta-prompts.js';
|
|
import { prefs_default } from '../options/mzta-options-default.js';
|
|
|
|
export const taPromptUtils = {
|
|
|
|
async getDefaultSignature(){
|
|
let prefs = await browser.storage.sync.get({ default_sign_name: prefs_default.default_sign_name });
|
|
if(prefs.default_sign_name===''){
|
|
return '';
|
|
}else{
|
|
return browser.i18n.getMessage("sign_msg_as") + " " + prefs.default_sign_name + ".";
|
|
}
|
|
},
|
|
|
|
async preparePrompt(args){
|
|
const {
|
|
curr_prompt = {},
|
|
curr_message = {},
|
|
chatgpt_lang = '',
|
|
selection_text = '',
|
|
selection_html = '',
|
|
body_text = '',
|
|
subject_text = '',
|
|
msg_text = {},
|
|
only_typed_text = '',
|
|
only_quoted_text = '',
|
|
tags_full_list = ["", []]
|
|
} = args || {};
|
|
|
|
let fullPrompt = '';
|
|
|
|
if(!placeholdersUtils.hasPlaceholder(curr_prompt.text)){
|
|
// no placeholders, do as usual
|
|
const signature = String(curr_prompt.need_signature) === "1" ? await taPromptUtils.getDefaultSignature() : "";
|
|
const content = selection_text || body_text;
|
|
fullPrompt = [curr_prompt.text, signature, chatgpt_lang, content ? `"${content}"` : ""].filter(Boolean).join(" ");
|
|
}else{
|
|
// we have at least a placeholder, do the magic!
|
|
// check if we have custom placeholders
|
|
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,
|
|
mail_subject: subject_text,
|
|
body_text: body_text,
|
|
msg_text: msg_text,
|
|
only_typed_text: only_typed_text,
|
|
only_quoted_text: only_quoted_text,
|
|
selection_text: selection_text,
|
|
selection_html: selection_html,
|
|
tags_full_list: tags_full_list
|
|
});
|
|
let prefs_ph = await browser.storage.sync.get({ placeholders_use_default_value: prefs_default.placeholders_use_default_value });
|
|
fullPrompt = (placeholdersUtils.replacePlaceholders({
|
|
text: curr_prompt.text,
|
|
replacements: finalSubs,
|
|
use_default_value: prefs_ph.placeholders_use_default_value,
|
|
skip_additional_text: true
|
|
}) + (String(curr_prompt.need_signature) == "1" ? " " + await taPromptUtils.getDefaultSignature():"") + " " + chatgpt_lang).trim();
|
|
}
|
|
|
|
return fullPrompt;
|
|
},
|
|
|
|
finalizePrompt_add_tags(fullPrompt, add_tags_maxnum, add_tags_force_lang, default_chatgpt_lang, add_tags_auto_uselist = false, add_tags_auto_uselist_list = ''){
|
|
if(add_tags_maxnum > 0){
|
|
fullPrompt += " \n" + browser.i18n.getMessage("prompt_add_tags_maxnum") + " " + add_tags_maxnum +".";
|
|
}
|
|
if(add_tags_force_lang && default_chatgpt_lang !== ''){
|
|
fullPrompt += " \n" + browser.i18n.getMessage("prompt_add_tags_force_lang") + " " + default_chatgpt_lang + ".";
|
|
}
|
|
if(add_tags_auto_uselist && add_tags_auto_uselist_list && add_tags_auto_uselist_list.length > 0){
|
|
fullPrompt += " \n" + browser.i18n.getMessage("prompt_add_tags_use_list") + ": " + add_tags_auto_uselist_list + ".";
|
|
}
|
|
|
|
return fullPrompt;
|
|
},
|
|
|
|
finalizePrompt_get_calendar_event(fullPrompt){
|
|
fullPrompt = fullPrompt.replace("{%cc_list%}", "");
|
|
fullPrompt = fullPrompt.replace("{%recipients%}", "");
|
|
|
|
return fullPrompt;
|
|
},
|
|
|
|
async getDefaultLang(curr_prompt){
|
|
let chatgpt_lang = '';
|
|
if(String(curr_prompt.define_response_lang) == "1"){
|
|
let prefs = await browser.storage.sync.get({ default_chatgpt_lang: prefs_default.default_chatgpt_lang });
|
|
chatgpt_lang = prefs.default_chatgpt_lang;
|
|
if(chatgpt_lang === ''){
|
|
chatgpt_lang = browser.i18n.getMessage("reply_same_lang");
|
|
}else{
|
|
chatgpt_lang = browser.i18n.getMessage("prompt_lang") + " " + chatgpt_lang + ".";
|
|
}
|
|
}
|
|
|
|
return chatgpt_lang;
|
|
},
|
|
|
|
|
|
async buildSummaryPrompt(messageDataArray) {
|
|
const specialPrompts = await getSpecialPrompts();
|
|
const prompt = specialPrompts.find(p => p.id === 'prompt_summarize');
|
|
const prompt_email = specialPrompts.find(p => p.id === 'prompt_summarize_email_template');
|
|
const prompt_email_separator = specialPrompts.find(p => p.id === 'prompt_summarize_email_separator');
|
|
|
|
const chatgpt_lang = await taPromptUtils.getDefaultLang(prompt);
|
|
|
|
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,
|
|
});
|
|
|
|
const messages_list = [];
|
|
for (let entry of messageDataArray) {
|
|
const bodyHtml = await getMailBody(entry.fullMessage);
|
|
let bodyText = htmlBodyToPlainText(bodyHtml.html);
|
|
if (bodyText.length === 0) {
|
|
bodyText = bodyHtml.text || '';
|
|
}
|
|
|
|
messages_list.push(await taPromptUtils.preparePrompt({
|
|
curr_prompt: prompt_email,
|
|
curr_message: entry.message,
|
|
chatgpt_lang: chatgpt_lang,
|
|
body_text: bodyText,
|
|
subject_text: entry.fullMessage.headers.subject,
|
|
msg_text: bodyHtml,
|
|
}));
|
|
}
|
|
|
|
const messages_string = messages_list.join(prompt_email_separator_string);
|
|
const promptText = prompt_string + prompt_email_separator_string + messages_string;
|
|
|
|
return { promptText, promptInfo: prompt };
|
|
},
|
|
|
|
async buildTranslationPrompt(fullMessage) {
|
|
const specialPrompts = await getSpecialPrompts();
|
|
const prompt = specialPrompts.find(p => p.id === 'prompt_translate_this');
|
|
|
|
let promptText = prompt.text;
|
|
if (promptText === 'prompt_translate_this_full_text') {
|
|
promptText = browser.i18n.getMessage('prompt_translate_this_full_text');
|
|
}
|
|
|
|
const bodyHtml = await getMailBody(fullMessage);
|
|
const mailSubject = fullMessage.headers?.subject?.[0] || '';
|
|
|
|
const finalSubs = await placeholdersUtils.getPlaceholdersValues({
|
|
prompt_text: promptText,
|
|
msg_text: { html: bodyHtml.html, text: bodyHtml.text },
|
|
mail_subject: mailSubject,
|
|
});
|
|
|
|
const fullPrompt = placeholdersUtils.replacePlaceholders({
|
|
text: promptText,
|
|
replacements: finalSubs,
|
|
use_default_value: false,
|
|
});
|
|
|
|
return { promptText: fullPrompt, promptInfo: prompt };
|
|
},
|
|
|
|
/**
|
|
* Extracts tags from the response text.
|
|
* @param {string} response_text - The response text from which to extract tags.
|
|
* @returns {Array} An array of tags extracted from the response text.
|
|
*
|
|
* The response text should be a JSON with this structure:
|
|
* {
|
|
* "tags": ["tag1", "tag2", ...]
|
|
* }
|
|
*
|
|
* For backwords compatibility, if the response text is not a valid JSON,
|
|
* it will try to split the text by commas and return the resulting array.
|
|
*/
|
|
getTagsFromResponse(response_text, filter_tags = false, filter_tags_list = ''){
|
|
let tags = [];
|
|
if(response_text && response_text.length > 0){
|
|
try {
|
|
// Try to parse the response text as JSON
|
|
let response_json = extractJsonObject(response_text.trim());
|
|
if(response_json && Array.isArray(response_json.tags)){
|
|
tags = response_json.tags;
|
|
} else if(response_json && response_json.tags && typeof response_json.tags === 'string'){
|
|
// If tags is a string, split it by commas
|
|
tags = response_json.tags.split(/,\s*/).map(tag => tag.trim());
|
|
}
|
|
} catch (e) {
|
|
// If parsing fails, fallback to splitting by commas
|
|
tags = response_text.split(/,\s*/).map(tag => tag.trim());
|
|
}
|
|
}
|
|
if(filter_tags && filter_tags_list && filter_tags_list.length > 0){
|
|
const allowedTags = filter_tags_list.split(',').map(tag => tag.trim().toLowerCase()).filter(tag => tag.length > 0);
|
|
tags = tags.filter(tag => allowedTags.includes(tag.toLowerCase()));
|
|
}
|
|
return tags;
|
|
}
|
|
};
|