diff --git a/gmail-sentiment-analysis/Cards.gs b/gmail-sentiment-analysis/Cards.gs index 6a07c735d..f0360318d 100644 --- a/gmail-sentiment-analysis/Cards.gs +++ b/gmail-sentiment-analysis/Cards.gs @@ -16,28 +16,24 @@ limitations under the License. /** - * Builds the card for to display in the sidepanel of gmail. + * Builds the card to display in the sidepanel of gmail. * @return {CardService.Card} The card to show to the user. */ function buildCard_GmailHome(notifyOk = false) { - const imageUrl = 'https://icons.iconarchive.com/icons/roundicons/100-free-solid/48/spy-icon.png'; - const image = CardService.newImage() - .setImageUrl(imageUrl); + const imageUrl = 'https://fonts.gstatic.com/s/i/googlematerialicons/dynamic_feed/v6/black-24dp/1x/gm_dynamic_feed_black_24dp.png'; const cardHeader = CardService.newCardHeader() .setImageUrl(imageUrl) .setImageStyle(CardService.ImageStyle.CIRCLE) .setTitle("Analyze your GMail"); - const action = CardService.newAction() - .setFunctionName('analyzeSentiment'); + const action = CardService.newAction().setFunctionName('analyzeSentiment'); const button = CardService.newTextButton() .setText('Identify angry customers') .setOnClickAction(action) .setTextButtonStyle(CardService.TextButtonStyle.FILLED); - const buttonSet = CardService.newButtonSet() - .addButton(button); + const buttonSet = CardService.newButtonSet().addButton(button); const section = CardService.newCardSection() .setHeader("Emails sentiment analysis") @@ -51,9 +47,8 @@ function buildCard_GmailHome(notifyOk = false) { * This builds the card that contains the footer that informs * the user about the successful execution of the Add-on. */ - if (notifyOk == true) { - let fixedFooter = CardService.newFixedFooter() + const fixedFooter = CardService.newFixedFooter() .setPrimaryButton( CardService.newTextButton() .setText("Analysis complete") diff --git a/gmail-sentiment-analysis/Gmail.gs b/gmail-sentiment-analysis/Gmail.gs index 7e4b3c621..d4e6fe2b2 100644 --- a/gmail-sentiment-analysis/Gmail.gs +++ b/gmail-sentiment-analysis/Gmail.gs @@ -20,30 +20,46 @@ limitations under the License. */ function analyzeSentiment() { - emailSentiment(); + analyzeAndLabelEmailSentiment(); return buildCard_GmailHome(true); } /** - * Gets the last 10 threads in the inbox and the corresponding messages. - * Fetches the label that should be applied to negative messages. - * The processSentiment is called on each message - * and tested with RegExp to check for a negative answer from the model + * Analyzes the sentiment of recent emails in the inbox and labels threads with + * negative sentiment as "UPSET TONE 😡". */ +function analyzeAndLabelEmailSentiment() { + const labelName = "UPSET TONE 😡"; + const maxThreads = 10; -function emailSentiment() { - const threads = GmailApp.getInboxThreads(0, 10); - const msgs = GmailApp.getMessagesForThreads(threads); - const label_upset = GmailApp.getUserLabelByName("UPSET TONE 😡"); - let currentPrediction; - - for (let i = 0; i < msgs.length; i++) { - for (let j = 0; j < msgs[i].length; j++) { - let emailText = msgs[i][j].getPlainBody(); - currentPrediction = processSentiment(emailText); - if (currentPrediction === true) { - label_upset.addToThread(msgs[i][j].getThread()); + // Get the label, or create it if it doesn't exist. + const label = GmailApp.getUserLabelByName(labelName) || GmailApp.createLabel(labelName); + + // Get the first 'maxThreads' threads from the inbox. + const threads = GmailApp.getInboxThreads(0, maxThreads); + + // Process each thread. + for (const thread of threads) { + const messages = thread.getMessages(); + + // Process each message within the thread. + for (const message of messages) { + const emailText = message.getPlainBody(); + const isUpset = isNegativeSentiment(emailText); + + if (isUpset) { + label.addToThread(thread); } } } } + +/** + * Determines if the given text has a negative sentiment. + * + * @param {string} text - The text to analyze. + * @returns {boolean} True if the sentiment is negative, false otherwise. + */ +function isNegativeSentiment(text) { + return processSentiment(text) === 'negative'; +} diff --git a/gmail-sentiment-analysis/Vertex.gs b/gmail-sentiment-analysis/Vertex.gs index 25d1994a3..3f02fefff 100644 --- a/gmail-sentiment-analysis/Vertex.gs +++ b/gmail-sentiment-analysis/Vertex.gs @@ -15,7 +15,7 @@ limitations under the License. */ const PROJECT_ID = '[ADD YOUR GCP PROJECT ID HERE]'; -const VERTEX_AI_LOCATION = 'europe-west2'; +const VERTEX_AI_LOCATION = 'us-central1'; const MODEL_ID = 'gemini-1.5-pro-002'; /** @@ -28,11 +28,7 @@ const MODEL_ID = 'gemini-1.5-pro-002'; */ function processSentiment(emailText) { - const prompt = ` - Analyze the following message: ${emailText}. - If the sentiment of this message is negative, answer with FALSE. - If the sentiment of this message is neutral or positive, answer with TRUE. - Do not use any other words than the ones requested in this prompt as a response!`; + const prompt = `Analyze the sentiment of the following message: ${emailText}`; const request = { "contents": [{ @@ -44,6 +40,20 @@ function processSentiment(emailText) { "generationConfig": { "temperature": 0.9, "maxOutputTokens": 1024, + "responseMimeType": "application/json", + "responseSchema": { + "type": "object", + "properties": { + "response": { + "type": "string", + "enum": [ + "positive", + "negative", + "neutral", + ] + } + } + } } }; @@ -62,8 +72,7 @@ function processSentiment(emailText) { const response = UrlFetchApp.fetch(url, fetchOptions); const payload = JSON.parse(response.getContentText()); + const text = JSON.parse(payload.candidates[0].content.parts[0].text); - const regex = /FALSE/; - - return regex.test(payload.candidates[0].content.parts[0].text); + return text.response; } diff --git a/gmail-sentiment-analysis/appsscript.json b/gmail-sentiment-analysis/appsscript.json index cf6273f63..9b2be96ac 100644 --- a/gmail-sentiment-analysis/appsscript.json +++ b/gmail-sentiment-analysis/appsscript.json @@ -5,14 +5,13 @@ "https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/script.locale", "https://www.googleapis.com/auth/gmail.addons.execute", - "https://mail.google.com/", "https://www.googleapis.com/auth/gmail.labels", "https://www.googleapis.com/auth/gmail.modify" ], "addOns": { "common": { "name": "Productivity toolbox", - "logoUrl": "https://icons.iconarchive.com/icons/roundicons/100-free-solid/64/spy-icon.png", + "logoUrl": "https://fonts.gstatic.com/s/i/googlematerialicons/dynamic_feed/v6/black-24dp/1x/gm_dynamic_feed_black_24dp.png", "useLocaleFromApp": true }, "gmail": {