# Project export: Ezemail

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: Cal Hacks 11.0
- Tagline: A Google Chrome extension that digests and summarizes Gmail emails you received in a latest time period you specified. This tool helps you to grasp the important points from your most recent emails.
- Devpost: https://devpost.com/software/ezemail
- GitHub: https://github.com/ryanhongnguyen/ezemail
- Video: https://www.youtube.com/embed/MReFrbj4wGo?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 4 GitHub contributor(s) — ryanhongnguyen (17 commits), ethanmypan (5 commits), You Wu (2 commits), rsvasudev (1 commits)

## Devpost submission (written by the team)

### Inspiration

With the flood of daily emails, it's easy to lose track of important deadlines and tasks buried within. We needed a solution that could help organize and prioritize emails efficiently, saving time and reducing the risk of missing crucial deadlines. The idea was to build an intuitive email tool that fetches emails, summarizes them, and highlights the most critical information, such as deadlines, while categorizing them based on priority.

### What it does

Ezemail is a Chrome extension that: Connects to your Gmail account using the Gmail API. Fetches emails over a selected time range. Uses the OpenAI API to summarize the content of the emails. Categorizes emails based on urgency, importance, and subject. Extracts and highlights deadlines or time-sensitive information. Sends an email to the user with a summary of all emails within the selected period, including deadlines and categorized insights.

### How we built it

Frontend: We built the Chrome extension UI using HTML, CSS, and JavaScript. We focused on creating a clean and easy-to-navigate interface for reading email summaries and deadlines. Backend: The core logic integrates Gmail's API to retrieve emails and the OpenAI API to process and summarize the email content. After the summarization process is completed, the summary is emailed back to the user with organized insights. Email Categorization: By analyzing the email content, we used the OpenAI API to determine the importance and urgency, automatically grouping emails into categories like "High Priority," "Reminders," "Tasks," etc. Deadline Extraction: By scanning the email text, the tool pulls out dates and times and organizes them into a visual calendar.

### Challenges we ran into

Authentication for Gmail and OpenAI APIs: The procedure involved many settings on the Google Cloud Platform, the Chrome extension, and our code. We visited some online resources but they did not work. This was quite challenging for us as we have never done that. It required deep understanding of the interaction between the user and the extension. API Rate Limits: We encountered challenges with the Gmail and OpenAI API rate limits, which required us to batch requests and handle throttling. Parsing complex emails: Some emails contain attachments, rich formatting, or embedded media, making it difficult to summarize the key points accurately.

### Accomplishments we're proud of

Successfully integrating two powerful APIs (Gmail and OpenAI) to create a seamless email management solution. Creating a user-friendly extension that simplifies email workflows and makes important information (like deadlines) easily accessible. Implementing deadline extraction and reminder features, helping users stay on top of important tasks and dates. Automating the process to send users email summaries, eliminating the need to check emails frequently.

### What we learned

Deepened our understanding of API integrations and handling their limitations. Gained insights into how AI-driven summarization can vastly improve email management. Learned the importance of UX design in productivity tools, making sure the tool is intuitive for users.

### What's next

Smart Notifications: Implement push notifications for approaching deadlines or high-priority emails. Advanced Email Filtering: Improve categorization by learning from the user's interaction with emails (e.g., marking something as high/low priority). Attachment Summarization: Extend the summarization feature to attachments like PDFs and documents. Multiple Email Accounts: Allow users to connect and manage multiple Gmail accounts from within the extension.

## README (from the GitHub repository)

Ezemail is a Google Chrome extension created during Cal Hacks 11.0 that digests and summarizes recent Gmail emails based on a user-specified time range. It helps users quickly grasp the key points from their latest emails by leveraging the Gmail and OpenAi API for efficient summarization.

[![Ezemail Demo](https://img.youtube.com/vi/MReFrbj4wGo/maxresdefault.jpg)](https://youtu.be/MReFrbj4wGo?si=88bX4ztZEgULOvfg)

## Prerequisites

### 1. Clone this repository
Clone this repository to your local environment using the following command:
```bash
git clone <repository-url>
```

### 2. Manually Install the Extension
1. Open Google Chrome and navigate to `chrome://extensions/`.
2. Enable **Developer Mode** (toggle switch in the top right corner).
3. Click **Load unpacked** and select the folder where you cloned the repository.

### 3. Set Up Gmail API in Google Cloud Console
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
2. Create a new project.
3. Navigate to **API & Services** > **Library** and enable **Gmail API**.
4. Go to **Credentials** and create an OAuth client ID.
5. Set the OAuth item ID to the ID of this extension (you can find it in the Chrome Extensions page).
6. On the OAuth consent screen, include the scope `https://www.googleapis.com/auth/gmail.readonly`.
7. Once completed, you will get a `Client ID`. Use it for the `"client_id"` in `manifest.json`.

### 4. Try the Extension
Open the extension in Google Chrome and start using it to summarize your Gmail emails.


## Detected evidence (automated analysis)

Indexed codebase: 17 recognized source files, 16 KB.
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- Python (language) — detected in the code
- OpenAI (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (23 of 23)

```
.DS_Store
.env
.gitignore
app.py
background.js
chatgpt_api/__init__.py
chatgpt_api/categorizer.py
chatgpt_api/summarizer.py
gmail_api/__init__.py
gmail_api/email_extractor.py
gmail_api/email_sender.py
gmail_api/gmail_service.py
LICENSE
main.py
manifest.json
popup.html
popup.js
README.md
requirements.txt
templates/index.html
utils/__init__.py
utils/config.py
utils/logger.py
```

### Dependencies

- requirements.txt: pip@install Flask

### Recent commits (newest first)

- Update README.md
- Update README.md
- Merge branch 'main' of https://github.com/ryanhongnguyen/ezemail
- finished
- Merge branch 'main' of https://github.com/ryanhongnguyen/ezemail
- email send work
- Update README.md
- add scope
- Write README.md
- Merge branch 'main' of https://github.com/ryanhongnguyen/ezemail
- update the gpt api call to summerize
- nearly there w sending email out its v close
- Merge branch 'main' of https://github.com/ryanhongnguyen/ezemail
- update
- update to get the content of emails
- Merge pull request #5 from ryanhongnguyen/ryan
- Merge pull request #4 from ryanhongnguyen/ethan
- images
- update the email_summarizer_gpt.js
- Merge pull request #3 from ryanhongnguyen/ethan

## Key source files (fetched from GitHub, selected and truncated for size)

### requirements.txt

```
pip install simplegmail
pip install Flask


```

### main.py

```python
from flask import Flask, render_template, request, redirect, url_for
from gmail_api.gmail_service import authenticate_gmail, fetch_emails
from datetime import datetime

app = Flask(__name__)

@app.route('/')
def index():
    return render_template('index.html')

@app.route('/fetch_emails', methods=['POST'])
def fetch_emails_view():
    start_time_str = request.form['start_time']
    end_time_str = request.form['end_time']

    start_time = datetime.strptime(start_time_str, "%Y-%m-%dT%H:%M")
    end_time = datetime.strptime(end_time_str, "%Y-%m-%dT%H:%M")

    gmail = authenticate_gmail()
    emails = fetch_emails(gmail, start_time, end_time)

    for email in emails:
        print(f"Subject: {email['subject']}")
        print(f"Sender: {email['sender']}")
        print(f"Date: {email['date']}")
        print(f"Body: {email['body'][:100]}...")
        print("-" * 50)

    return redirect(url_for('index'))

if __name__ == '__main__':
    app.run(debug=True)

```

### app.py

```python
from flask import Flask, request, jsonify
import openai
from flask_cors import CORS

app = Flask(__name__)
CORS(app)
# Set your OpenAI API key
openai.api_key = 'YOUR_OPENAI_API_KEY'

@app.route('/generate-digest', methods=['POST'])
def generate_digest():
    data = request.json
    emails = data.get('emails', [])

    # Combine email subjects and bodies
    email_contents = '\n\n'.join([f"Subject: {email['subject']}\n\n{email['body']}" for email in emails])

    # Call OpenAI API
    response = openai.ChatCompletion.create(
        model='gpt-3.5-turbo',
        messages=[
            {"role": "system", "content": "You are an assistant that summarizes emails."},
            {"role": "user", "content": email_contents}
        ],
        max_tokens=500,
        n=1,
        stop=None,
        temperature=0.7
    )

    digest = response.choices[0].message.content.strip()

    return jsonify({'digest': digest})

if __name__ == '__main__':
    app.run(host='0.0.0.0', port=4000, debug=True)

```

### popup.html

```html
<!DOCTYPE html>
<html>
<head>
  <title>Email Analyzer</title>
  <style>
    body {
      font-family: 'Comic Sans MS', cursive, sans-serif;
      background-color: #f9f9fb;
      color: #333;
      margin: 10px;
      padding: 20px;
      border-radius: 10px;
      width: 300px;
      box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
    }
    h1 {
      font-size: 20px;
      color: #6a0dad;
      text-align: center;
    }
    label {
      font-size: 14px;
      color: #6a0dad;
      margin-bottom: 5px;
    }
    input, button {
      width: 100%;
      padding: 8px;
      border: 1px solid #ccc;
      border-radius: 8px;
      margin-bottom: 10px;
      font-size: 14px;
    }
    input {
      background-color: #f0e5ff;
    }
    button {
      background-color: #c599ff;
      color: white;
      font-weight: bold;
      cursor: pointer;
    }
    button:hover {
      background-color: #b07bff;
    }
    #emailResults {
      max-height: 300px;
      overflow-y: auto;
      margin-top: 10px;
      background-color: #fff0f5;
      padding: 10px;
      border-radius: 8px;
      border: 1px solid #ddd;
    }
    pre {
      white-space: pre-wrap;
      word-wrap: break-word;
    }
  </style>
</head>
<body>
  <h1>Email Analyzer</h1>
  <label for="days">Enter number of days:</label>
  <input type="number" id="days" min="1" max="365" value="7">
  <button id="fetchEmails">Fetch Emails</button>
  <div id="emailResults"></div>
  <script src="popup.js"></script>
</body>
</html>

```

### background.js

```javascript
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.action === 'fetchEmails') {
      const days = request.days;
      chrome.identity.getAuthToken({ interactive: true }, function(token) {
        if (chrome.runtime.lastError) {
          console.error('Auth Error:', chrome.runtime.lastError.message);
          sendResponse({ error: chrome.runtime.lastError.message });
          return;
        }
        fetchEmails(token, days)
          .then(emails => {
            sendResponse({ emails: emails });
          })
          .catch(error => {
            console.error('Error:', error);
            sendResponse({ error: error.message });
          });
      });
      return true;
    }
  });

function fetchEmailContent(messageId, token) {
  return new Promise((resolve, reject) => {
    const url = `https://gmail.googleapis.com/gmail/v1/users/me/messages/${messageId}?format=full`;
    fetch(url, {
      headers: {
        'Authorization': 'Bearer ' + token
      }
    })
      .then(response => response.json())
      .then(message => {
        if (!message.payload) {
          reject(new Error('Message payload is missing'));
          return;
        }
        const headers = message.payload.headers || [];
        const subjectHeader = headers.find(h => h.name === 'Subject');
        const fromHeader = headers.find(h => h.name === 'From');
        const subject = subjectHeader ? subjectHeader.value : '(No Subject)';
        const from = fromHeader ? fromHeader.value : '(Unknown Sender)';

        const body = getEmailBody(message.payload);

        const emailData = {
          subject: subject,
          from: from,
          body: body
        };
        resolve(emailData);
      })
      .catch(error => reject(error));
  });
}

function getEmailBody(payload) {
  let body = '';

  if (payload.parts) {
    for (const part of payload.parts) {
      if (part.mimeType === 'text/plain' && part.body && part.body.data) {
        body = part.body.data;
        break;
      } else if (part.mimeType === 'text/html' && part.body && part.body.data) {
        body = part.body.data;
        break;
      } else if (part.parts) {
        body = getEmailBody(part);
        if (body) {
          break;
        }
      }
    }
  } else if (payload.body && payload.body.data) {
    body = payload.body.data;
  }

  if (body) {

    body = atob(body.replace(/-/g, '+').replace(/_/g, '/'));
  }

  return body;
}

function fetchEmails(token, days) {
  return new Promise((resolve, reject) => {
    const query = `newer_than:${days}d`;
    const url = `https://gmail.googleapis.com/gmail/v1/users/me/messages?q=${encodeURIComponent(query)}`;

    fetch(url, {
      headers: {
        'Authorization': 'Bearer ' + token
      }
    })
      .then(response => response.json())
      .then(data => {
        if (!data.messages || data.messages.length === 0) {
          resolve([]);
          return;
        }
        const messageIds = data.messages.map(msg => msg.id);
        const limitedMessageIds = messageIds.slice(0, 10);
        Promise.all(limitedMessageIds.map(id => fetchEmailContent(id, token)))
          .then(emails => {
            resolve(emails);
          })
          .catch(error => reject(error));
      })
      .catch(error => reject(error));
  });
}
```

### popup.js

```javascript
function displayEmails(emails) {
  const emailResults = document.getElementById('emailResults');
  emailResults.innerHTML = '';
  emails.forEach(email => {
    const emailDiv = document.createElement('div');
    emailDiv.innerHTML = `
      <strong>Subject:</strong> ${email.subject}<br>
      <strong>From:</strong> ${email.from}<br>
      <strong>Body:</strong><br><pre>${escapeHtml(email.body)}</pre>
      <hr>`;
    emailResults.appendChild(emailDiv);
  });
}
function escapeHtml(text) {
  const div = document.createElement('div');
  div.textContent = text;
  return div.innerHTML;
}
document.getElementById('fetchEmails').addEventListener('click', () => {
  const days = document.getElementById('days').value || '1';
  document.getElementById('emailResults').textContent = 'Fetching emails...';
  
  chrome.runtime.sendMessage({ action: 'fetchEmails', days: days }, function(response) {
    if (response && response.emails) {
      displayEmails(response.emails);
      console.log(response);
  
      summarizeEmails(response.emails).then(summary => {
        console.log('Summary Email:\n', summary);
  
        const emailSubject = `Summary of your Emails from the last ${days} days`;
  
        chrome.identity.getAuthToken({ interactive: true }, function(token) {
          if (chrome.runtime.lastError) {
            console.error('Error fetching token:', chrome.runtime.lastError.message);
            return;
          }
          sendEmail(token, emailSubject, summary)
            .then(result => {
              console.log(result);
            })
            .catch(error => {
              console.error('Error sending email:', error);
            });
        });
      });
    } else if (response && response.error) {
      document.getElementById('emailResults').textContent = 'Error: ' + response.error;
    } else {
      document.getElementById('emailResults').textContent = 'Failed to fetch emails.';
    }
  });  
});
const OPENAI_API_KEY = "API_KEY";
const OPENAI_API_URL = 'https://api.openai.com/v1/chat/completions';
async function summarizeEmails(emails) {
    const prompt = `
Help me summarize those emails in terms of the senders, some important information, and deadlines, like a comprehensive summary of it. Return it as a formated html stuff so I can present it nicer, like with bullet points stuff.
Here are the emails:
${emails.map(email => `
Subject: ${email.subject}
Sender: ${email.from}
Body: ${email.body}
`).join('\n')}
`;
    const requestBody = {
        model: 'gpt-3.5-turbo', // or use 'gpt-4' if you wanna spend hella money
        messages: [{ role: 'user', content: prompt }],
    };
    try {
        const response = await fetch(OPENAI_API_URL, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': `Bearer ${OPENAI_API_KEY}`,
            },
            body: JSON.stringify(requestBody),
        });
        if (!response.ok) {
            throw new Error(`Error: ${response.status} ${response.statusText}`);
        }
        const data = await response.json();
        const summary = data.choices[0].message.content.trim();
        return summary;
    } catch (error) {
        console.error('Error fetching summary from OpenAI:', error);
        return null;
    }
}
function sendEmail(token, subject, body) {
  return new Promise((resolve, reject) => {
    const email = `
From: "Ryan Nguyen" <ryan_hpnguyen@berkeley.edu>
To: ryan_hpnguyen@berkeley.edu
Subject: ${subject}
Content-Type: text/html; charset="UTF-8"

${body}
`.trim();

    const base64EncodedEmail = btoa(unescape(encodeURIComponent(email)));

    fetch('https://gmail.googleapis.com/gmail/v1/users/me/messages/send', {
      method: 'POST',
      headers: {
        'Authorization': 'Bearer ' + token,
        'Content-Type': 'application/json',
      },
      body: JSON.stringify({ raw: base64EncodedEmail }),
    })
      .then(response => {
        if (response.ok) {
          resolve('Email sent successfully');
        } else {
          reject(new Error(`Failed to send email: ${response.status} ${response.statusText}`));
        }
      })
      .catch(error => reject(error));
  });
}

```

### templates/index.html

```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Email Fetcher</title>
</head>
<body>
    <h1>Fetch Emails</h1>
    <form action="/fetch_emails" method="POST">
        <label for="start_time">From (Date & Time):</label><br>
        <input type="datetime-local" id="start_time" name="start_time"><br><br>

        <label for="end_time">To (Date & Time):</label><br>
        <input type="datetime-local" id="end_time" name="end_time"><br><br>

        <input type="submit" value="Fetch Emails">
    </form>
</body>
</html>

```

### gmail_api/gmail_service.py

```python
from simplegmail import Gmail
from datetime import datetime

def authenticate_gmail():
    gmail = Gmail()
    return gmail

def fetch_emails(gmail, start_datetime, end_datetime):
    
    query = f'after:{start_datetime.strftime("%Y/%m/%d")} before:{end_datetime.strftime("%Y/%m/%d")}'
    messages = gmail.get_messages(query=query)
    
    emails = []
    for message in messages:
        email_data = {
            "subject": message.subject,
            "sender": message.sender,
            "date": message.date,
            "body": message.plain or message.html,
        }
        emails.append(email_data)
    
    return emails

if __name__ == '__main__':
    
    gmail = authenticate_gmail()

    start_time = datetime(2024, 10, 16)
    end_time = datetime(2024, 10, 17)

    emails = fetch_emails(gmail, start_time, end_time)

    for email in emails:
        print(f"Subject: {email['subject']}")
        print(f"Sender: {email['sender']}")
        print(f"Date: {email['date']}")
        print(f"Body: {email['body']}")
        print("-" * 50)

```

### gmail_api/email_sender.py

```python
from simplegmail import Gmail
from simplegmail.message import Attachment
import os

def authenticate_gmail():
    
    gmail = Gmail()
    return gmail

def send_email(gmail, sender, to, subject, body, attachments=None, signature=True):

    params = {
        "to": to,
        "sender": sender,
        "subject": subject,
        "msg_html": body,
        "msg_plain": body,
        "signature": signature,
    }

    # Send the email
    try:
        message = gmail.send_message(**params)
        print(f"Email sent successfully to {to}")
    except Exception as e:
        print(f"Failed to send email to {to}. Error: {str(e)}")

if __name__ == '__main__':
    gmail = authenticate_gmail()

    sender = "ryan_hpnguyen@berkeley.edu"
    recipient = "ryan_hpnguyen@berkeley.edu"
    subject = "Hello from Ezemail!"
    body = """
    <h2>Email Summaries:</h2>
<ul>
<li>
    <strong>Internship opportunity with Walt Disney Imagineering:</strong>
    <ul>
        <li><strong>Sender:</strong> Bmc_engineering Departmental</li>
        <li><strong>Important Information:</strong> *WDI Imaginations* design competition for students passionate about storytelling, innovation, and design. Virtual info session on Thursday (10/17/24).</li>
        <li><strong>Deadline:</strong> Info session on Thursday (10/17/24)</li>
    </ul>
</li>
<li>
    <strong>EECS 101: Slight change for CS advising location for drop-in today:</strong>
    <ul>
        <li><strong>Sender:</strong> Lydia Raya</li>
        <li><strong>Important Information:</strong> Drop-in hours from 1:30-2PM in 205 Cory today.</li>
        <li><strong>Deadline:</strong> Today for drop-in hours</li>
    </ul>
</li>
</ul>"
"""
    send_email(gmail, sender, recipient, subject, body)

```