Code Monkey home page Code Monkey logo

ezgmail's Introduction

EZGmail

A Pythonic interface to the Gmail API that actually works as of July 2024.

EZGmail is an easier than using the official Google API for doing common email tasks, such as sending email, searching your inbox, reading emails, and more.

Also check out EZSheets for editing Google Sheets with Python.

Installation and Setup

In general, you must create a Python virtual environment for your Python program and install packages to it. From the terminal/command line, activate the virtual environment and install EZGmail by running pip install ezgmail (in Windows) or pip3 install ezgmail (in macOS or Linux).

For your Python script to use EZGmail to access Gmail account, you need a credentials JSON file and a token JSON file. This requires a one-time set up on Google Cloud services using your Gmail account. If you have a Gmail account, you can do this and it is free (unless you are making large use of the Gmail API by sending out thousands of emails every day.) There's several steps to this, detailed in the following sections. You only need to do these setup steps once, and then your Python program can use EZGmail effortlessly. These instructions were last updated July 2024.

For testing, I'd advise using a test Gmail account rather than your actual Gmail account so you don't accidentally delete real emails.

General setup outline:

  • Create a new Google Cloud Project from https://console.cloud.google.com
  • Enable the Gmail API from the APIs & Services > Enabled API & services page.
  • Configure the OAuth Consent Screen from the APIs & Services > Oauth consent screen page.
  • Create a new OAuth 2.0 Client ID from the APIs & Services > Credentials page with the https://mail.google.com/ scope.
  • Download the client_secret_*.json credentials file.
  • From the Python interactive shell, run import ezgmail from the same current working directory as the credentials file.
  • This opens your browser to the OAuth consent screen. Authorize the application to generate the token.json file.
  • With the token.json file in the current working directory, your Python program can now use the ezgmail module.

Create a new Google Cloud Project

First, you need to set up a Google Cloud project. In your browser, go to https://console.cloud.google.com and sign in to you Google account with your username and password. (Your app can connect to any Gmail account, but this Google account will be one that manages the Google API access for EZGmail.) You will be taken to a Getting Started page. At the top of the page, click on "Select a project". In the pop-up window that appears, click "New Project".

Navigate to new project

This takes to a new project page. A Project Name like "My Project 23135" will be generated for you. There is also a Project ID like "macro-nuance-362516" randomly generated for you. These names won't be visible to users of your Python scripts and you can use whatever name you want. You can change the Project Name later but not the Project ID. I just use the default names that are generated for me. The Location can be left as "No organization". Note that at the free tier, you can only create 12 projects (as of September 2022) but you only need one for all the Python scripts that you want to create. Click the blue "CREATE" button to create the project.

Create a new project

Free Google accounts can have up to 10 projects.

Click on "Select a project" at the top of the page again, and select the project you just created. You will be taken to the dashboard page for this Google Cloud Project. Next, you need to enable the Gmail API for your project.

Enable the Gmail API for Your Project

On the https://console.cloud.google.com page, click on the Navigation button in the upper left (the icon is three horizontal stripes, often called the "hamburger" icon.) Navigate to "APIs & Services" and then "Library" to go to the API Library page. There are many Google APIs for Gmail, Google Maps, Google Cloud Storage, and other Google services. We need to allow our project to use the Gmail API.

Navigate to library

Scroll down and find the "Gmail API" and click it, or enter "Gmail API" into the search bar to find it. This takes you to the Gmail API page. Click the blue "ENABLE" button to enable your Google Cloud project to use the Gmail API. You'll be redirected to the "APIs & Services > Enabled APIs & Services" page where you can find information about how often your Python scripts are using this API. (You might want to go back to the "APIs & Services > Library" page to enable the Google Sheets API and Google Drive API if you want to use the ezsheets Python module to edit Google Sheets spreadsheets from Python scripts too, while you're here.)

Enable Gmail API

Next, you need to configure your project's OAuth Consent Screen.

Configure the OAuth Consent Screen for Your Project

Click on the Navigation button in the upper left and then navigate to "APIs & Services" and then "OAuth Consent Screen." The consent screen will appear the first time when you or users of your Python script use ezgmail. With a free Gmail account that is not set up as a Google Workspace user, you'll have to select the External User Type option instead of Internal User Type. These are further explained on Google's Setting up your OAuth consent screen help page.

On Step 1 "OAuth consent screen", select External and click the blue "CREATE" button. You'll be taken to a page where you set up what the OAuth consent screen looks like. This screen appears to the user when they first import the ezgmail module. Pick a name for App Name (I use something generic like Python Gmail Script) and enter your email address for the User Support Email and Developer Contact Information. Then click "SAVE AND CONTINUE."

Step 2 "Scopes" involves adding your projects' scopes, which is a term Google chose for "permissions." Click the "ADD OR REMOVE SCOPES" button, and in the new panel that appears, go through the table and check the checkboxes for the scopes https://mail.google.com/ and click the blue "UPDATE" button. Then click "SAVE AND CONTINUE."

Step 3 "Test users" requires you to add the Gmail email addresses your Python script will interact with. Without going through Google's app approval process, your scripts will be limited to interacting with the email addresses you provide in this step. Click the "+ ADD USERS" button and in the new panel that appears, enter the Gmail addresses and click the blue "ADD" button. Then click "SAVE AND CONTINUE."

Step 4 "Summary" provides a summary of the previous steps. If all the information looks right, click the "BACK TO DASHBOARD" button. The next step is to create credentials for your project.

Create Credentials for Your Project

From the Navigation sidebar menu, click on "APIs & Services" and then "Credentials" to go to the Credentials page. Click the "+ CREATE CREDENTIALS" link at the top of the page. A submenu will open asking what kind of credentials you want to create: "API key", "OAuth client ID", or "Service account". Click on "OAuth client ID".

Create credentials

On the new page that appears, select "Desktop app" for the "Application type" and leave "Name" as the default "Desktop client 1." (You can change this to a different name if you want, it doesn't appear to the users of your Python script.) Click the blue "CREATE" button.

The pop up that appears shows your newly created OAuth client data. Click "DOWNLOAD JSON" to download the credentials file. This file will have a name like client_secret_282792235794-p2o9gfcub4htibfg2u207gcomco9nqm7.apps.googleusercontent.com.json. Rename it to credentials.json and place it in the same folder that your Python script will be in.

Log In with the Credentials File

Run the Python interactive shell from the same folder that the client_secret_*.json file is in and run import ezgmail. Or, place a .py Python program in this folder and have it run import ezgmail. EZGmail will load and automatically check this folder for a client_secret_*.json file and, if found, launches your web browser to the OAuth consent screen. Sign in with the Gmail account you want to access from your Python script. This must be the same email address that you gave for the "test user" when configuring the Google Cloud project's OAuth consent screen.

You will get a warning message that reads "Google hasn’t verified this app," but that's fine because this is the app (or project) that you've just created yourself. Click the Continue link. You'll come to another page that says "Python Gmail Script wants access to your Google Account" (or whatever name you gave in the OAuth consent screen setup.) Click Continue.

You'll come to a plain web page that says, "The authentication flow has completed." You can now close the browser window. In the same folder as your client_secret_*.json file, you'll now see a token.json file. Do not share these files: they can be used to log in and access your Gmail account.

Quickstart Guide

After you've set up your credentials and token files, you can import EZGmail with import ezgmail. To see what email address you are sending from, examine ezgmail.EMAIL_ADDRESS (this is configured by the token.json file you're using, and you must first call ezgmail.init() or some other ezgmail function first):

>>> import ezgmail
>>> ezgmail.EMAIL_ADDRESS
'[email protected]'

To send an email from your "[email protected]" account:

>>> import ezgmail
>>> ezgmail.send('[email protected]', 'Subject line', 'Body of the email', ['attachment1.jpg', 'attachment2.mp3'])

The attachments argument is optional, and if you only have one attachment you can just specify the filename string. Also note that Gmail will most likely filter any emails that contain .exe, .zip, or any other suspicious attachments.

The cc and bcc fields are also optional keyword arguments:

>>> import ezgmail
>>> ezgmail.send('[email protected]', 'Subject line', 'Body of the email', cc='[email protected]', bcc='[email protected],[email protected]')

The main classes in ezgmail are GmailThread and GmailMessage. A GmailThread is a chain of emails replying to one another, while a GmailMessage is an individual email in a thread.

To retrieve unread emails:

>>> import ezgmail
>>> unreadThreads = ezgmail.unread()  # Returns a list of GmailThread objects.

The summary() function is an easy way to print out info on a list of thread or message objects:

>>> ezgmail.summary(unreadThreads)
Jon, Al - Remember that old website Hamsterdance? LOL - Dec 09
Al - This is a test email about gerbils. - Dec 09

If you want this info as a data structure, pass printInfo=False to summary():

>>> ezgmail.summary(unreadThreads, printInfo=False)
[(['Jon Smith <[email protected]>', 'Al Sweigart <[email protected]>'], 'Remember that old website Hamsterdance? LOL', datetime.datetime(2018, 12, 9, 13, 29, 17)), (['Al Sweigart <[email protected]>'], 'This is a test email about gerbils.', datetime.datetime(2018, 12, 9, 13, 25, 58))]

The GmailMessage objects of a thread are in the messages list attribute:

>>> ezgmail.summary(unreadThreads[0].messages)
Jon - Remember that old website Hamsterdance? LOL - Dec 09
Al - Haha that&#39;s awesome! On Sun, Dec 9, 2018 at 1:28 PM Jon Smith &lt;[email protected]&gt; wrote: Remember that old website Hamsterdance? LOL - Dec 09

The GmailMessage objects have sender, recipient, subject, body, and timestamp attribues:

>>> msg = unreadThreads[0].messages[0]
>>> msg.sender
'Jon Smith <[email protected]>'
>>> msg.recipient
'Al Sweigart <[email protected]>'
>>> msg.subject
'Hamsterdance'
>>> msg.body
'Remember that old website Hamsterdance? LOL\r\n'
>>> msg.timestamp
datetime.datetime(2018, 12, 9, 13, 28, 48)

You can also call the recent() function to get recent email threads:

>>> import ezgmail
>>> recentThreads = ezgmail.recent()
>>> len(recentThreads)
22

The recent() and unread() functions are just convenient wrappers around search(), which you can pass a query to (just like the query text field in the Gmail.com website):

>>> import ezgmail
>>> threads = ezgmail.search('mancala')
>>> len(threads)
1
>>> ezgmail.summary(threads[0])
Al, Jon - Zanzibar &gt; <b>Mancala</b> is one of the oldest known games to still be widely played today. &gt; <b>Mancala</b> is a generic name for a - Dec 08

The trash() method deletes the message or messages in a GmailMessage or GmailThread object:

>>> import ezgmail
>>> threads = ezgmail.search('mancala')
>>> threads[0].trash()  # Move the entire first thread to the Trash folder.

The search() function can accept search operators just like the query text field:

More are described at https://support.google.com/mail/answer/7190?hl=en

The search(), recent(), and unread() can also accept a maxResults keyword argument that is set to 25 by default. This sets an upper limit on how many threads/messages will be returned. API usage quotas are posted at https://developers.google.com/gmail/api/v1/reference/quota (roughly one million requests a day (and 25 per second) for the free tier).

By default, EZGmail sends messages as plaintext. You can send HTML emails by passing 'html' for the mimeSubtype parameter in send(). (By default, this parameter is set to 'plain'.) This email has "Hello" appear in bold and "body" appear italicized:

>>> ezgmail.send('[email protected]', 'Subject Line', '<strong>Hello</strong>, this is the <em>body</em> of the message.', mimeSubtype='html')

Accessing an email or thread doesn't mark it as unread automatically. You must do that yourself by calling the markAsRead() method of the GmailThread or GmailMessage object. (There is also a corresponding markAsUnread() function.) You can also call ezgmail.markAsRead() and pass it a list of GmailThread or GmailMessage objects.

>>> import ezgmail
>>> unreadThreads = ezgmail.unread()
>>> ezgmail.markAsRead(unreadThreads) # Marks all the GmailThread objects in the unreadThreads list as read.
>>> # Or you can do:
>>> for unreadThread in unreadThreads:
...     unreadThread.markAsRead() # Mark the individual GmailThread objects as read.

These two functions make add/remove the 'UNREAD' label using EZGmail's addLabel() and removeLabel() functions:

>>> import ezgmail
>>> unreadThreads = ezgmail.unread()
>>> ezgmail.removeLabel(unreadThreads, 'UNREAD') # Also marks threads as read.
>>> ezgmail.addLabel(unreadThreads, 'UNREAD') # Marks them as unread again.
>>> # Or you can do:
>>> for unreadThread in unreadThreads:
...     unreadThread.removeLabel(unreadThreads, 'UNREAD') # Mark the individual GmailThread objects as read.

(Currently EZGmail doesn't have functions for adding/deleting/managing custom labels.)

To view the attachments of an email, look at the GmailMessage object's attachments dictionary. The keys are the filenames of the attachments. You can either call the downloadAttachment() or downloadAllAttachments() methods:

>>> import ezgmail
>>> threads = ezgmail.search('See the attached files')
>>> threads[0].messages[0].attachments
>>> import pprint
>>> pprint.pprint(threads[0].messages[0].attachments)
{'a.png': {'id': 'ANGjdJ8eLDbjBpFTfvpuQ2HfR_iwp59XLUIl-IHW8eJcexMsxBYoPCZAXcX16rnqcbJZTknF5r3GmnM1W9n4vAE1oiVgUa4S4zBmNs7rd5PzFwLjO2vU3hp3_9SEZv-KBqVxi9nuNjarxhFqp3mxw6E5mqEYmFOYtT7Gx6CZbLaJuUox9GaWu-W9B4-XPDjwKkEfCdJ21FlOl-CsC6isZgD2Vh-ghh1haZN_2sifccznLv61ZW_KmqPKFcV1j7cXMQVqWU7bkgdH8do4Msc3QsG2ly_PNRid4-7gihsXaLI1ko_j3LSvsoLHFP3edhxh6YKQ2OdMhyZh5lqjmfT1TXgSo7hY16P_ScDO5MnWvmKscf_Hm5y5D4DHfwOq4--Otivoq2WVkVucVUJBkAoB',
           'size': 833609},
 'b.png': {'id': 'ANGjdJ_WYMmPmy2Dd2VBgvVoLAd1p3ARxGXKIzVfKqAiLhvKSBmEowYqFCdHbMJYlDZy4IWBGLg0eQCllMI0icqamM7vfMxBW2irJVogLM6SUT9cIcJFMSF7UhzU2I26bho086J7NjnX5u4kqYj_LHchowO56vTdKLRRsaJ2gfW0esz3cDFZzvthdR4wyBKEIeCJv7OJmFiaJIRf9f1KmFfKPLo9GZSyD2RMXdd6Qa2M3uN9pgT6sZ-OQx3e6aNDAKWh5GCeSiuIt_Z7GsDCdzVJjakMJx5FRFhp5zIck0p04AHnYhKfy1BipWmf7G-DAKzgJHAhFimBVUIBeFsHrqEGxDlevD7lK4ZBeb8cluSmYyEsRkSPSMYMlp-x1GVw25gqMnMVkGMKPfwj38iB',
	       'size': 335911}}
>>> threads[0].messages[0].downloadAttachment('a.png') # Download to current working directory.
>>> threads[0].messages[0].downloadAttachment('b.png', '/path/to/save/in')
>>> threads[0].messages[0].downloadAllAttachments() # Easier way to save all attachments.

Limitations

Currently, EZGmail cannot do the following:

  • Read or set labels. (Including marking emails as read.)
  • Sending emails with cc and bcc fields.
  • A lot of other basic features. This package is just a start!

Contribute

If you'd like to contribute to EZGmail, check out https://github.com/asweigart/ezgmail or email [email protected]

Support

If you find this project helpful and would like to support its development, consider donating to its creator on Patreon.

ezgmail's People

Contributors

arpitgoyalgg avatar asweigart avatar presentformyfriends avatar qqgg231 avatar willdarragh avatar znwilkins avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

ezgmail's Issues

ezgmail.search doesn't work

when running ezgmail.search('hello') this errors out at the following point:

253 elif "body" in messageObj["payload"].keys(): 254 # for header in messageObj['payload']['headers']: 255 # if header['name'].upper() == 'CONTENT-TYPE': 256 # emailEncoding = _parseContentTypeHeaderForEncoding(header['value']) --> 257 self.originalBody = base64.urlsafe_b64decode(messageObj["payload"]["body"]["data"]).decode(emailEncoding) 258 self.body = removeQuotedParts(self.originalBody)

This seems to be due to the commented out code which if I run it in my case returned 'UTF-8' and then was able to run that part of the code

this is a very weird behaviour because sometimes it works and sometimes it doesn't just searching for a different string

search() doesn't work all the time

I fired the query like ezgmail.search("subject:(pay reminders ) after:1/1/2022 ") it returns length of 0, but this same if I fired on Gmail search bar it shows more than 20 emails, even tried after removing "after:"

Has Google just made getting credentials.json more tedious?

A few months back, I implemented the procedure described in the README and it worked perfectly, in particular as regards the way to obtain credentials.json and token.json.
Now coming back to it and going through the whole thing again, I visited https://developers.google.com/gmail/api/quickstart/python as per the README but the 'Enable the Gmail API' button was, this time, nowhere to be found.
After some research, I still managed to get a proper account and to finally send email with Python, but only through non trivial trials in the Google Cloud Console, setting up a project, then Oauth etc.

So, unless I missed a simpler way to proceed, which is possible, is it time to update the README in order to adjust to this fresh, harder Google account setup?

Messages sent using Gmail API marked as suspicious

When I used this in my Python script, the emails received come with a warning "Be careful with this message
The sender hasn't authenticated this message so Gmail can't verify that it actually came from them. Avoid clicking links, downloading attachments, or replying with personal information."

This warning is apparently related to setting the message["from"] attribute, commenting out the following line fixes it so messages are sent without the authentication warning:

message["from"] = sender

Screenshot 2023-10-28 at 3 16 08 PM

Attachments from automated sender (.info email) not recognized

Hello,

When I use ezgmail to read attachments from most emails, it works perfectly. However, when I try to read attachments from emails that I receive on a daily basis as a part of an automated email report, the attachments method returns an empty list.

This automated email is sent to another email address (email #2), which I used to forward to the main email address (email #1). When I try to read attachments from this thread, suddenly ezgmail can read the attachment. Do you know how I can fix this, or why this is an issue?

Thanks.

Not detecting attachment when it can be downloaded by clicking a button.

Hey,
I want to use ezgmail to download an attachment. Now the mail structure is like this, A button, and on clicking the button the attachment gets downloaded automatically, to be more precise it's a sheet. However when I read the documentation and write the section implemented under attachments I get this:

threads = ezgmail.search('Download report')
len(threads)
1
threads[0].messages[0].attachment
Traceback (most recent call last):
File "", line 1, in
AttributeError: 'GmailMessage' object has no attribute 'attachment'

It isnt detecting the attachment. So two questions: is there a way that this can be done using ezgmail? If not then is there any alternate way?

Thankyou for your time:)

Authorization Error

The content in this section has been provided by the app developer. This content has not been reviewed or verified by Google.
It seems that ezgmail doesn't meet the requirement of OAuth2.0

ezgmail.init() error - Cannot access token.json: No such file or directory

Hello,

Following is the code which I am running in Colab. I have downloaded the credentials.json file as well. The ezgmail.init() doesnt open the gmail window. Can you please help.

CODE
import ezgmail, os
os.chdir(r'/content/sample_data')
ezgmail.init()

ERROR
/usr/local/lib/python3.6/dist-packages/oauth2client/_helpers.py:255: UserWarning: Cannot access token.json: No such file or directory. WOuld liek to use ezgmail
warnings.warn(_MISSING_FILE_MESSAGE.format(filename))
usage: ipykernel_launcher.py [--auth_host_name AUTH_HOST_NAME]
[--noauth_local_webserver]
[--auth_host_port [AUTH_HOST_PORT [AUTH_HOST_PORT ...]]]
[--logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
ipykernel_launcher.py: error: unrecognized arguments: -f /root/.local/share/jupyter/runtime/kernel-a77a494c-89e8-479d-8d36-ca423ce91cd9.json
An exception has occurred, use %tb to see the full traceback.

SystemExit: 2
/usr/local/lib/python3.6/dist-packages/IPython/core/interactiveshell.py:2890: UserWarning: To exit: use 'exit', 'quit', or Ctrl-D.
warn("To exit: use 'exit', 'quit', or Ctrl-D.", stacklevel=1)

UnboundLocalError when running ezgmail.unread()

Got this error the first time trying out this lib. I have figured out a temporary solution for this by removing the if statement on line 255 in the __init__.py file but not sure if it breaks the code elsewhere, it is attached below the error. I also apologise for the bad indentation, I do not know how to properly use GitHub for reporting issues.

Code:
import ezgmail

ezgmail.init()
unreadThreads = ezgmail.unread()
ezgmail.summary(unreadThreads)

Error:
Traceback (most recent call last): File "E:\Python\Gmail ecrater orders\ecrater.py", line 5, in <module> ezgmail.summary(unreadThreads) File "C:\Users\Lauris\AppData\Local\Programs\Python\Python38\lib\site-packages\ezgmail\__init__.py", line 641, in summary (obj.senders(), obj.snippet, obj.latestTimestamp()) File "C:\Users\Lauris\AppData\Local\Programs\Python\Python38\lib\site-packages\ezgmail\__init__.py", line 112, in senders for msg in self.messages: File "C:\Users\Lauris\AppData\Local\Programs\Python\Python38\lib\site-packages\ezgmail\__init__.py", line 99, in messages self._messages.append(GmailMessage(msg)) File "C:\Users\Lauris\AppData\Local\Programs\Python\Python38\lib\site-packages\ezgmail\__init__.py", line 258, in __init__ self.originalBody = base64.urlsafe_b64decode(messageObj["payload"]["body"]["data"]).decode(emailEncoding) UnboundLocalError: local variable 'emailEncoding' referenced before assignment

Solution from line 253 to 258 in __init__.py:
elif "body" in messageObj["payload"].keys():
for header in messageObj['payload']['headers']:
#if header['name'].upper() == 'CONTENT-TYPE':
emailEncoding = _parseContentTypeHeaderForEncoding(header['value'])
self.originalBody = base64.urlsafe_b64decode(messageObj["payload"]["body"]["data"]).decode(emailEncoding)
self.body = removeQuotedParts(self.originalBody)

Attachment is corrupted or blank [pdf]

I am having an issue sending documents as an attachment. Every pdf file attached goes corrupted or partly openable.

`
import ezgmail
class EmailSender:

def __init__(self, receiver_name, email, job, gender, pdf):
    self.receiver_name = receiver_name
    self.email = email
    self.job = job
    self.gender = gender
    self.pdf = pdf

def send_email(self):
    body = self.write_body()
    ezgmail.send(
        self.email,
        'Would like to work as ' + self.job,
        body,
        ['Cv.pdf', self.pdf, 'Diploma.pdf']
    )
    print("Sent to the " + self.email + ". Success!")

def write_body(self):
    main_text = "Text"

    signature = " Your friend, Name "

    if self.gender == "Male":
        heading = "Sehr geehrter "
    else:
        heading = "Sehr geehrte "

    return heading + self.receiver_name + ",\r\n" + main_text + "\r\n" + signature

class Main:
test = EmailSender("Mr. White", "[email protected]", "driver", "Male", "my_motivationLetter.pdf")
test.send_email()

`

Emails with attachments produce errors

Hello, I'm following the book (Thank you for the amazing book, btw!) for ezgmail module.

I'm having an issue when I search emails with attachments and try to download it. The module seems to be able to search correct emails and save it to a thread object but when I try to read the summary of threads (ezgmail.summary(threads)) or try to access the attachments (threads[0]messages[0].attachments), it's showing me errors.

These are the codes and outputs I received from the terminal:

>>> import ezgmail
>>> threads = ezgmail.search('(now with attachment)')
>>> len(threads)
3
>>> ezgmail.summary(threads)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/shinsooyun/Library/Python/3.9/lib/python/site-packages/ezgmail/__init__.py", line 704, in summary
    (obj.senders(), obj.snippet, obj.latestTimestamp())
  File "/Users/shinsooyun/Library/Python/3.9/lib/python/site-packages/ezgmail/__init__.py", line 120, in senders
    for msg in self.messages:
  File "/Users/shinsooyun/Library/Python/3.9/lib/python/site-packages/ezgmail/__init__.py", line 104, in messages
    self._messages.append(GmailMessage(msg))
  File "/Users/shinsooyun/Library/Python/3.9/lib/python/site-packages/ezgmail/__init__.py", line 273, in __init__
    attachmentId = part["body"]["attachmentId"]
KeyError: 'attachmentId'
>>> threads[0].messages[0].attachments
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/shinsooyun/Library/Python/3.9/lib/python/site-packages/ezgmail/__init__.py", line 107, in messages
    assert len(self._messages) > 0, "GmailThread object has zero messages; please file a new bug report issue: https://github.com/asweigart/ezgmail/issues"
AssertionError: GmailThread object has zero messages; please file a new bug report issue: https://github.com/asweigart/ezgmail/issues

KeyError 'headers'

I get a KeyError when trying to interact with any message and any function over unreadThreads (summary, unreadThreads[0].message....)

Traceback (most recent call last):
  File "start.py", line 44, in <module>
    logger.info(ezgmail.summary(unreadThreads))
  File "path/to/venv/lib/python3.8/site-packages/ezgmail/__init__.py", line 704, in summary
    (obj.senders(), obj.snippet, obj.latestTimestamp())
  File "path/to/venv/lib/python3.8/site-packages/ezgmail/__init__.py", line 120, in senders
    for msg in self.messages:
  File "path/to/venv/lib/python3.8/site-packages/ezgmail/__init__.py", line 104, in messages
    self._messages.append(GmailMessage(msg))
  File "path/to/venv/lib/python3.8/site-packages/ezgmail/__init__.py", line 247, in __init__
    for header in part["headers"]:
KeyError: 'headers'

Incorrect word

Accessing an email or thread doesn't mark it as unread automatically. You must do that yourself by calling the ``markAsRead()`` method of the ``GmailThread`` or ``GmailMessage`` object. (There is also a corresponding ``markAsUnread()`` function.) You can also call ``ezgmail.markAsRead()`` and pass it a list of ``GmailThread`` or ``GmailMessage`` objects.

unread -> read

Getting InvalidClientSecretsError when trying to run ezgmail.init() when setting up the token file for the first time

I've been using the ezgmail for awhile on some automation stuff and it's been a very handy little module. However, when I went to create a new GMail account to use, I ran into a bizarre issue.

I enabled the account for the gmail python api and then, after I got the credentials file in the right spot, I ran the ezgmail.init method and got a super weird stack trace:
File "F:\Automation Documentation\EmailSender\EmailSender\EmailSender.py", line 3, in <module> ezgmail.init()
File "C:\Users\msmith\AppData\Local\Programs\Python\Python37-32\lib\site-packages\ezgmail\__init__.py", line 298, in init flow = client.flow_from_clientsecrets(credentialsFile, SCOPES)
File "C:\Users\msmith\AppData\Local\Programs\Python\Python37-32\lib\site-packages\oauth2client\_helpers.py", line 133, in positional_wrapper return wrapped(*args, **kwargs)
File "C:\Users\msmith\AppData\Local\Programs\Python\Python37-32\lib\site-packages\oauth2client\client.py", line 2135, in flow_from_clientsecrets cache=cache)
File "C:\Users\msmith\AppData\Local\Programs\Python\Python37-32\lib\site-packages\oauth2client\clientsecrets.py", line 165, in loadfile return _loadfile(filename)
File "C:\Users\msmith\AppData\Local\Programs\Python\Python37-32\lib\site-packages\oauth2client\clientsecrets.py", line 126, in _loadfile return _validate_clientsecrets(obj)
File "C:\Users\msmith\AppData\Local\Programs\Python\Python37-32\lib\site-packages\oauth2client\clientsecrets.py", line 101, in _validate_clientsecrets prop_name, client_type))
oauth2client.clientsecrets.InvalidClientSecretsError: Missing property "redirect_uris" in a client type of "web".

Is there someinitial step I need to do that I'm missing? I've followed this same process multiple times in the past and everything has worked fine, but when irun through it now I keep getting this error. I followed the steps from Automate the Boring Stuff, but I still got the same error.

Any help in this matter would be greatly appreciated

Email body cannot be read when there are attachments

I am able to read the body of an email when there are no attachments. However, when I add in a few attachments like a pdf, jpg or a PNG file I am not able to read the email body using the .body attribute. I get an error instead - 'AttributeError: 'GmailMessage' object has no attribute 'body'(see the picture below) :
error
I have also tried to attach smaller files in the hope that it will work, but to no avail.
May I know if there is any way to solve this problem?
Thank you.

No module named 'oauth2client.contrib.locked_file'

First of all: Thanks for a great package!

When running ezgmail "live" from the python interpreter, everything works just fine, but as I am trying to build
a script and run it from my bash, I get this weird exception, but still correct results.

I could just suppress the errors, but since this could lead to problems later on, I thought I'd report it.

Any idea as to what could cause this?

I am running Python 3.8 on WSL (Ubuntu 20.04) and have the most current version of this package.

WARNING:googleapiclient.discovery_cache:file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth
Traceback (most recent call last):
  File "/home/fridde/.local/lib/python3.8/site-packages/googleapiclient/discovery_cache/file_cache.py", line 33, in <module>
    from oauth2client.contrib.locked_file import LockedFile
ModuleNotFoundError: No module named 'oauth2client.contrib.locked_file'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/fridde/.local/lib/python3.8/site-packages/googleapiclient/discovery_cache/file_cache.py", line 37, in <module>
    from oauth2client.locked_file import LockedFile
ModuleNotFoundError: No module named 'oauth2client.locked_file'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/fridde/.local/lib/python3.8/site-packages/googleapiclient/discovery_cache/__init__.py", line 44, in autodetect
    from . import file_cache
  File "/home/fridde/.local/lib/python3.8/site-packages/googleapiclient/discovery_cache/file_cache.py", line 40, in <module>
    raise ImportError(
ImportError: file_cache is unavailable when using oauth2client >= 4.0.0 or google-auth

PDF Attachment is mangled

Sending basic email to gmail and icloud is working, however, when adding a 16 page PDF file attachment to the message the PDF received is not identical to the originating PDF.

Comparing the sent file to the downloaded received file, both have the same file size.

Opening the downloaded received file in Adobe Acrobat Reader I received the following two (2) messages:

  1. Cannot extract the embedded font 'URZYZB+TimesNewRomanPSMT'. Some characters may not display or print correctly.

Clicking OK in the msgbox I receive the following message:
2. An error exists on this page. Acrobat may not display the page correctly. Please contact the person who created the PDF document to correct the problem.

Reviewing the received file one quickly notices that the files have substantive issues/gaps.
The sent file opens properly.

[BUG] `overwrite=False` is not protecting local files, they are overwritten anyway

    def downloadAllAttachments(self, downloadFolder=".", overwrite=True):
        """Download all of the attachments in this message to the local folder ``downloadFolder``. If ``overwrite`` is
        ``True``, existing local files will be overwritten by attachments with the same filename."""

overwrite=False doesn't overwrite only if duplicate filenames are in attachements to mail. Contrary to doctring it doesn't check if local files exists already or not.

spam folder

is there a way to check the spam folder using this library?

Can't use the module / can't even import it

The Goggle API connection works fine. But trying to import ezgmail with the console gives me:

usage: pydevconsole.py [--auth_host_name AUTH_HOST_NAME]
[--noauth_local_webserver]
[--auth_host_port [AUTH_HOST_PORT ...]]
[--logging_level {DEBUG,INFO,WARNING,ERROR,CRITICAL}]
pydevconsole.py: error: unrecognized arguments: --mode=client --port=65296

thanks for your help

(looks like a PyCharm problem -- any help is welcome though)

ezgmail needs manual authentication once a week

I have been using ezgmail on a raspberry pi 4 for a month now to automatically check and download e-mail attachments every 10 minutes and it's working flawlessly except for an annoying authentication issue. I think I set up the OAuth correctly (according to the latest instructions), but after a week of operation with no issues the script asks me to manually do a new authentication:

Failed to start a local webserver listening on either port 8080 or port 8090. Please check your firewall settings and locally running programs that may be blocking or using those ports.

Falling back to --noauth_local_webserver and continuing with authorization.

Go to the following link in your browser:

After I'm doing so, the script starts to work for another week again.
Is it a limitation of ezgmail or am I missing something?

Max of 25 email for search?

threads = ezgmail.search(f'from:{email_address}')

It seems like this will only return 25 emails at a time. Is this an API limitation?

Blank Body Causes Crash

I have a scheduled process that emails a CSV file, there's no body in it. When I run even a simple script to find and read them as seen below:

import ezgmail, base64
import pprint

threads = ezgmail.search("Who's Using Banner 8")

pprint.pprint(threads[0].messages[0].attachments)

I get the following error:

Traceback (most recent call last):
File "e:/Python/Projects/get-attach/app.py", line 7, in
thread.messages[0].downloadAllAttachments()
File "e:\Python\Projects\get-attach\venv\lib\site-packages\ezgmail_init_.py", line 83, in messages
self.messages.append(GmailMessage(msg))
File "e:\Python\Projects\get-attach\venv\lib\site-packages\ezgmail_init
.py", line 204, in init
self.originalBody = base64.urlsafe_b64decode(messageObj['payload']['body']['data']).decode(emailEncoding)
KeyError: 'data'

I "fixed" it by editing the scheduled process and adding a simple "test" into the body. The script now appears to work as expected by printing out the attachment name.

No message in any thread

Hello,

Is this module currently working for other users? I get through the login steps and everything seem to work fine.

Using unreadThreads = ezgmail.unread(), I manage to get the list of threads but each of them shows Message = {list:0} [].

When trying to any message i get:

unreadThreads[0].messages[0]
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3331, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "", line 1, in
unreadThreads[0].messages[0]
IndexError: list index out of range

Similar issue with the .summary() function:

ezgmail.summary(unreadThreads)
Traceback (most recent call last):
File "/Library/Frameworks/Python.framework/Versions/3.8/lib/python3.8/site-packages/IPython/core/interactiveshell.py", line 3331, in run_code
exec(code_obj, self.user_global_ns, self.user_ns)
File "", line 1, in
ezgmail.summary(unreadThreads)
File "/Users/seb/Library/Python/3.8/lib/python/site-packages/ezgmail/init.py", line 507, in summary
summaryText.append((obj.senders(), obj.snippet, obj.latestTimestamp())) # GmailThread and GmailMessage both have senders() and latestTimestamp() methods.
File "/Users/seb/Library/Python/3.8/lib/python/site-packages/ezgmail/init.py", line 104, in latestTimestamp
return self.messages[-1].timestamp
IndexError: list index out of range

Thank you

ezgmail.init() error & not logged in despite having token.json and credentials.json

I followed the Python quickstart guide for the Gmail API and successfully got my credentials.json and token.json. When I run quickstart.py I do get all my email labels, so that seems to have worked.

I'm running on MacOs and pip3 installed ezgmail, etc but keep running into this error when I run:

import ezgmail
ezgmail.init()

(also ezgmail.init(tokenFile="token.json", credentialsFile="credentials.json") gives me the same error)

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/adott/.pyenv/versions/3.8.3/lib/python3.8/site-packages/ezgmail/__init__.py", line 471, in init
    creds = store.get()
  File "/Users/adott/.pyenv/versions/3.8.3/lib/python3.8/site-packages/oauth2client/client.py", line 407, in get
    return self.locked_get()
  File "/Users/adott/.pyenv/versions/3.8.3/lib/python3.8/site-packages/oauth2client/file.py", line 54, in locked_get
    credentials = client.Credentials.new_from_json(content)
  File "/Users/adott/.pyenv/versions/3.8.3/lib/python3.8/site-packages/oauth2client/client.py", line 302, in new_from_json
    module_name = data['_module']
KeyError: '_module'

In Python when I import and run ezgmail.LOGGED_IN I get false back.

I have also tried running this in a .py file with no luck:

import ezgmail
import os
os.chdir(r'/Users/adott/Documents/PROJECTS/abswpy/')  # location of the .json files
ezgmail.init()
print(ezgmail.LOGGED_IN)

Any help would be greatly appreciated! Thank you :)

Sending emails using an alias or reply-to address

When sending an email message from a valid gmail alias, it shows up as being sent from the primary email address. It would be useful to add a reply-to address, which I believe is supported from the API.

problem with summary

got an error when trying to get a summary of threads:

unreadThreads = ezgmail.unread()
ezgmail.summary(unreadThreads)
Traceback (most recent call last):
File "", line 1, in
File "C:\Users\alex\AppData\Local\Programs\Python\Python38-32\lib\site-packages\ezgmail_init_.py", line 507, in summary
summaryText.append((obj.senders(), obj.snippet, obj.latestTimestamp())) # GmailThread and GmailMessage both have senders() and latestTimestamp() methods.
File "C:\Users\alex\AppData\Local\Programs\Python\Python38-32\lib\site-packages\ezgmail_init_.py", line 95, in senders
for msg in self.messages:
File "C:\Users\alex\AppData\Local\Programs\Python\Python38-32\lib\site-packages\ezgmail_init_.py", line 83, in messages
self.messages.append(GmailMessage(msg))
File "C:\Users\alex\AppData\Local\Programs\Python\Python38-32\lib\site-packages\ezgmail_init
.py", line 177, in init
self.originalBody = base64.urlsafe_b64decode(part['body']['data']).decode(emailEncoding)
LookupError: unknown encoding: UTF-9

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.