Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 5 additions & 10 deletions gmail-sentiment-analysis/Cards.gs
Original file line number Diff line number Diff line change
Expand Up @@ -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")
Expand All @@ -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")
Expand Down
50 changes: 33 additions & 17 deletions gmail-sentiment-analysis/Gmail.gs
Original file line number Diff line number Diff line change
Expand Up @@ -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';
}
27 changes: 18 additions & 9 deletions gmail-sentiment-analysis/Vertex.gs
Original file line number Diff line number Diff line change
Expand Up @@ -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';

/**
Expand All @@ -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": [{
Expand All @@ -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",
]
}
}
}
}
};

Expand All @@ -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;
}
3 changes: 1 addition & 2 deletions gmail-sentiment-analysis/appsscript.json
Original file line number Diff line number Diff line change
Expand Up @@ -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": {
Expand Down
Loading