Automation - How to Send Email Programmatically with Python

Techelopment - Oct 17 - - Dev Community

Even though we live in an increasingly digital world, email communication remains a fundamental tool for many activities, both personal and professional. Automating the sending of emails can be particularly useful, and Python, with its simplicity and flexibility, offers an effective solution to achieve this operation.

In this article, we will explore how to send emails programmatically in Windows using Python, taking advantage of the win32com library, which allows you to create personalized messages and send them easily using the default program installed in Windows.

We will also see how to configure your own script, manage repetitive operations and customize the content of emails, all with simple steps.

🔗 Do you like Techelopment? Check out the site for all the details!

Setup (Windows)

Let's start by installing the library that will allow us to create our email and open it in the default program (e.g. Outlook). Open the terminal (Win+r type cmd then Enter) and launch the following command:

pip install pywin32
Enter fullscreen mode Exit fullscreen mode

If the installation fails, you can try updating pip:

pip install --upgrade pip
Enter fullscreen mode Exit fullscreen mode

or install the main library that pywin32 is part of:

pip install pypiwin32
Enter fullscreen mode Exit fullscreen mode

At this point we are ready to open our favorite IDE and start writing our python code, so let's start by importing the library:

import win32com.client as win32
Enter fullscreen mode Exit fullscreen mode

Utility function

As we go through this article, we will define a series of functions that will help us organize the code to make it customizable and automatable. Let's see the most important one right away.

Build mail object

def build_basic_email(subject, to_list, cc_list):
    outlook = win32.Dispatch('outlook.application')
    mail = outlook.CreateItem(0)
    mail.To = to_list
    mail.CC = cc_list
    mail.Subject = subject
    return mail
Enter fullscreen mode Exit fullscreen mode

build_basic_email is a basic function that does nothing more than return the mail object created with the wind32com library. In this example the program used is Outlook.

Test Email

Let's now define a function to build a simple email so we can start getting familiar with the library:

def build_email_test(to, cc, auto=False):
    subject = "My Subject"
    mail = build_basic_email(subject, to, cc)
    mail.HtmlBody = "test build mail programmatically"
    print("opening the email in outlook, check the program")
    mail.Display(True) # open in outlook
Enter fullscreen mode Exit fullscreen mode

At this point we are ready to generate a test email:

import win32com.client as win32

def build_basic_email(subject, to_list, cc_list):
    outlook = win32.Dispatch('outlook.application')
    mail = outlook.CreateItem(0)
    mail.To = to_list
    mail.CC = cc_list
    mail.Subject = subject
    return mail

def build_email_test(to, cc):
    subject = "My Subject"
    mail = build_basic_email(subject, to, cc)
    mail.HtmlBody = "test build mail programmatically"
    print("opening the email in outlook, check the program")
    mail.Display(True) # open in outlook

if __name__ == "__main__":
    build_email_test("test@test.com", "cc@cc.com")
Enter fullscreen mode Exit fullscreen mode

test email

Real Email to Automate

We can now delve into the creation of a python script that helps us automate our emails.
It often happens that emails have formatted text or even tables. That's why in this article we will see how to build a slightly more complex email like this:

Techelopment email

Automation

Compared to the test example seen before, we will now introduce into our script also portions of code that will allow us to customize our email template without having to modify it by hand.
Let's start by adding a choice of email template to use:

import win32com.client as win32

if __name__ == "__main__":
    email_type = input("Choose what kind of email to build:\n"
                       "  1. Techelopment\n"
                       "  2. test\n"
                       "> ")

    email_type_disptacher(int(email_type))
Enter fullscreen mode Exit fullscreen mode

The input function in Python allows you to interact with the user by requesting input from the keyboard. When our script is complete we will get this result:

email type prompt

The email_type_dispatcher function allows us to forward the request towards the construction of the template chosen by the user:

def email_type_disptacher(email_type):
    to = getEmailAddresses("TO")
    cc = getEmailAddresses("cc")
    auto_send = input("automatic sending? [y/n] (default no): ")
    send = True if auto_send == "y" else False

    match email_type:
        case 1:
            build_email_Techelopment(to, cc, send)
        case 2:
            build_email_test(to, cc, send)

        # In case there is no match
        case _:
            return -1
Enter fullscreen mode Exit fullscreen mode

Let's see in detail what it does:

  • getEmailAddresses(..): this function allows us to ask the user for the email addresses to which to send the email
  • auto_send: we ask the user if (after having built the email) our script should proceed with automatic sending (without opening the classic Outlook window in which to modify the message). auto_send will have a default value so as not to force the user to enter y or n every time
  • match email_type: we use a switch to decide which code to execute based on the chosen template

Before moving on to the function that interests us most (build_email_Techelopment), let's see how the getEmailAddresses(..) function is made:

def getEmailAddresses(type_of_email="TO"):
    emails = input(f"{type_of_email} (blank to use default addresses or 'e' to leave field empty): ")

    if emails != "" and emails != 'e':
        # more than one email address must be separated by ;
        emails_splitted = emails.split("@")
        while len(emails_splitted) > 2 and emails.count(';') < int(len(emails_splitted)/2):
            print("use ; to separate addresses")
            emails = input(f"{type_of_email} (blank to use default addresses or 'e' to leave field empty): ")
            if emails == "" or emails == 'e':
                break

    return emails
Enter fullscreen mode Exit fullscreen mode

This function asks the user to enter email addresses or leave blank (by hitting Enter on the keyboard) to use the default value of the chosen template or type e not to populate email addresses in case the user wants to manually enter them later.

The function handles multiple addresses by requesting and checking that they are separated by the ; character.

Techelopment email template

Let's now see how our email is built with formatted text and the previously seen table.
The function that takes care of building and managing the Techelopment template is build_email_Techelopment:

def build_email_Techelopment(to, cc, auto=False):
    if to == "": # use default adresses
        to = "ironman@ironman.com"
    elif to == 'e': # leave field empty
        to = ""

    if cc == "": # use default adresses
        cc = "avengers@avengers.com"
    elif cc == 'e': # leave field empty
        cc = ""

    default_subject = 'New mission'
    subject = input(f"Subject (blank for default '{default_subject}'): ")
    if subject == "":
        subject = default_subject

    mail = build_basic_email(subject, to, cc)

    body = f"<span style='font-family: Calibri; font-size:11pt;'>Hi, <br><br>NEW MISSION: <strong>follow Techelopment!</strong><br><br>Here the links<br><br>"

    body += table_template.format(site_url='https://www.techelopment.it', fb_url='https://www.facebook.com/techelopment',
                                  ig_url='https://instagram.com/techelopment', x_url='https://twitter.com/techelopment',
                                  tg_url='https://t.me/techelopment_channel', wa_url='https://whatsapp.com/channel/0029VaoJHA1Lo4hkXn1rcn3y',
                                  medium_url='https://medium.com/@techelopment', devto_url='https://dev.to/techelopment',
                                  yt_url='https://youtube.com/@techelopment?si=UehAP3GeMeIkniEZ')
    body += "<br><br>Regards</span><br><br>"
    mail.HtmlBody = body + signature

    manage_display_auto_send(mail, auto)
Enter fullscreen mode Exit fullscreen mode

Let's see step by step how the email is built:

  • The function takes as input the email addresses to be inserted in To and Cc. As you can see, the default case is handled with addresses defined directly in the function. The third parameter auto defines whether to automatically send the email or open the Outlook window to give the user the possibility of a final review.

N.B. auto is the parameter we built in the email_type_dispatcher function

  • The email subject is set with a default and then asks the user if he wants to insert a specific one.
  • The mail object is built with the classic build_basic_email function seen previously
  • The body is the most substantial part (🙂) of our code because we will also have to manage the formatting. Let's see it in detail in the next paragraph.
  • manage_dispaly_auto_send is a function that centralizes the management of automatic sending or opening of the email with Outlook
def manage_display_auto_send(mail, auto=False):
    if auto:
        print("sending email...")
        mail.Send()
        print("email sent 😎")
    else:
        print("opening the email in outlook, check the program")
        mail.Display(True) # open in outlook
Enter fullscreen mode Exit fullscreen mode

Techelopment Email Template Body

Since we intend to fully automate the construction of emails, in addition to the default values, keyboard inputs and automatic sending seen before, we will build the body using placeholders in order to parameterize the content of the email.
First, we initialize the body variable with the introductory phrase of our email. For formatting, we can use CSS styling rules as they are mostly supported by Outlook:

body = (f"<span style='font-family: Calibri; font-size:11pt;'>"
            f"Hi, <br><br>NEW MISSION: "
            f"<strong>follow Techelopment!</strong>"
            f"<br><br>Here the links<br><br>")
Enter fullscreen mode Exit fullscreen mode

After that we move on to building the table:

body += table_template.format(
                site_url='https://www.techelopment.it',
                fb_url='https://www.facebook.com/techelopment',
                ig_url='https://instagram.com/techelopment',
                x_url='https://twitter.com/techelopment',
                tg_url='https://t.me/techelopment_channel',
                wa_url='https://whatsapp.com/channel/0029VaoJHA1Lo4hkXn1rcn3y',
                medium_url='https://medium.com/@techelopment',
                devto_url='https://dev.to/techelopment',
                yt_url='https://youtube.com/@techelopment?si=UehAP3GeMeIkniEZ')
Enter fullscreen mode Exit fullscreen mode

The table_template variable contains the HTML code that builds the table to which we pass the values ​​of the placeholders site_url, fb_url, ig_url, x_url, tg_url, wa_url, medium_url, devto_url, yt_url.

table_template = '''
<table cellspacing="0" cellpadding="2" style="text-align:center;border:1px solid black; border-collapse: collapse; font-family: Calibri; font-size:11pt; vertical-align:top">
    <tr style="border:1px solid black; vertical-align:top; padding:10px">
        <td colspan="4" bgcolor="cadetblue" style="color:white;border:1px solid black; font-weight: bold; font-size:1.2em">
            Techelopment <a href="{site_url}" style="color:white;">{site_url}</a>
        </td>
    </tr>
    <tr style="border:1px solid black; vertical-align:top; padding:7px">        
        <td bgcolor="cadetblue" width="100" style="color:white;border:1px solid black; font-weight: bold;">Social</td>
        <td bgcolor="cadetblue" width="150" style="color:white;border:1px solid black; font-weight: bold;">Instant Messagging</td>
        <td bgcolor="cadetblue" width="130" style="color:white;border:1px solid black; font-weight: bold;">Articles</td>
        <td bgcolor="cadetblue" width="100" style="color:white;border:1px solid black; font-weight: bold;">Video</td>            
    </tr>
    <tr style="border:1px solid black; vertical-align:top; padding:7px">

        <td style="border:1px solid black; font-style: italic; "><a href="{fb_url}">Facebook</a></td>
        <td style="border:1px solid black; font-style: italic; "><a href="{tg_url}">Telegram</a></td>
        <td style="border:1px solid black; font-style: italic; "><a href="{medium_url}">Medium</a> (italian)</td>
        <td style="border:1px solid black; font-style: italic; "><a href="{yt_url}">Youtube</a></td>
    </tr>
    <tr style="border:1px solid black; vertical-align:top; padding:7px">

        <td style="border:1px solid black; font-style: italic;"><a href="{ig_url}">Instagram</a></td>
        <td style="border:1px solid black; font-style: italic; "><a href="{wa_url}">WhatsApp</a></td>
        <td style="border:1px solid black; font-style: italic; "><a href="{devto_url}">Dev.to</a> (english)</td>
        <td style="border:1px solid black; font-style: italic; "></td>
    </tr>
    <tr style="border:1px solid black; vertical-align:top; padding:7px">

        <td style="border:1px solid black; font-style: italic;"><a href="{x_url}">X (ex twitter)</a></td>
        <td style="border:1px solid black; font-style: italic; "></td>
        <td style="border:1px solid black; font-style: italic; "></td>
    </tr>
</table>'''
Enter fullscreen mode Exit fullscreen mode

Finally we close the email construction with a signature and add everything to our mail object:

body += "<br><br>Regards</span><br><br>"
mail.HtmlBody = body + signature
Enter fullscreen mode Exit fullscreen mode
signature = '''
<br>
<div>
    <b><span style='font-size:12.0pt;font-family:"Arial",sans-serif;color:gray;'>Techelopment</span></b>
    <br>
    <b><span style='padding:0px;font-size:10.0pt;font-family:"Arial",sans-serif;color:gray;'>TECHnology and devELOPMENT</span></b>
    <br>
    <span style='font-size:10.0pt;font-family:"Arial",sans-serif;color:gray;'>
        <a href="https://www.techelopment.it" target="_blank"><span style='font-size:10.0pt;font-family:"Arial",sans-serif;color:blue'>https://www.techelopment.it</span></a>
    </span>
    <br>
    <span style='font-size:10.0pt;font-family:"Arial",sans-serif;color:gray;'>E-mail: </span>
    <span style='color:gray;'>
        <a href="info@techelopment.it" target="_blank"><span style='font-size:10.0pt;font-family:"Arial",sans-serif;color:blue'>info@techelopment.it</span></a>
    </span>
    <br>
</div>
'''
Enter fullscreen mode Exit fullscreen mode

Full script

Here is the full code and an example of execution:

import win32com.client as win32

signature = '''
<br>
<div>
    <b><span style='font-size:12.0pt;font-family:"Arial",sans-serif;color:gray;'>Techelopment</span></b>
    <br>
    <b><span style='padding:0px;font-size:10.0pt;font-family:"Arial",sans-serif;color:gray;'>TECHnology and devELOPMENT</span></b>
    <br>
    <span style='font-size:10.0pt;font-family:"Arial",sans-serif;color:gray;'>
        <a href="https://www.techelopment.it" target="_blank"><span style='font-size:10.0pt;font-family:"Arial",sans-serif;color:blue'>https://www.techelopment.it</span></a>
    </span>
    <br>
    <span style='font-size:10.0pt;font-family:"Arial",sans-serif;color:gray;'>E-mail: </span>
    <span style='color:gray;'>
        <a href="info@techelopment.it" target="_blank"><span style='font-size:10.0pt;font-family:"Arial",sans-serif;color:blue'>info@techelopment.it</span></a>
    </span>
    <br>
</div>
'''

table_template = '''
<table cellspacing="0" cellpadding="2" style="text-align:center;border:1px solid black; border-collapse: collapse; font-family: Calibri; font-size:11pt; vertical-align:top">
    <tr style="border:1px solid black; vertical-align:top; padding:10px">
        <td colspan="4" bgcolor="cadetblue" style="color:white;border:1px solid black; font-weight: bold; font-size:1.2em">
            Techelopment <a href="{site_url}" style="color:white;">{site_url}</a>
        </td>
    </tr>
    <tr style="border:1px solid black; vertical-align:top; padding:7px">        
        <td bgcolor="cadetblue" width="100" style="color:white;border:1px solid black; font-weight: bold;">Social</td>
        <td bgcolor="cadetblue" width="150" style="color:white;border:1px solid black; font-weight: bold;">Instant Messagging</td>
        <td bgcolor="cadetblue" width="130" style="color:white;border:1px solid black; font-weight: bold;">Articles</td>
        <td bgcolor="cadetblue" width="100" style="color:white;border:1px solid black; font-weight: bold;">Video</td>            
    </tr>
    <tr style="border:1px solid black; vertical-align:top; padding:7px">

        <td style="border:1px solid black; font-style: italic; "><a href="{fb_url}">Facebook</a></td>
        <td style="border:1px solid black; font-style: italic; "><a href="{tg_url}">Telegram</a></td>
        <td style="border:1px solid black; font-style: italic; "><a href="{medium_url}">Medium</a> (italian)</td>
        <td style="border:1px solid black; font-style: italic; "><a href="{yt_url}">Youtube</a></td>
    </tr>
    <tr style="border:1px solid black; vertical-align:top; padding:7px">

        <td style="border:1px solid black; font-style: italic;"><a href="{ig_url}">Instagram</a></td>
        <td style="border:1px solid black; font-style: italic; "><a href="{wa_url}">WhatsApp</a></td>
        <td style="border:1px solid black; font-style: italic; "><a href="{devto_url}">Dev.to</a> (english)</td>
        <td style="border:1px solid black; font-style: italic; "></td>
    </tr>
    <tr style="border:1px solid black; vertical-align:top; padding:7px">

        <td style="border:1px solid black; font-style: italic;"><a href="{x_url}">X (ex twitter)</a></td>
        <td style="border:1px solid black; font-style: italic; "></td>
        <td style="border:1px solid black; font-style: italic; "></td>
    </tr>
</table>'''

def manage_display_auto_send(mail, auto=False):
    if auto:
        print("sending email...")
        mail.Send()
        print("email sent 😎")
    else:
        print("opening the email in outlook, check the program")
        mail.Display(True) # open in outlook

def build_basic_email(subject, to_list, cc_list):
    outlook = win32.Dispatch('outlook.application')
    mail = outlook.CreateItem(0)
    mail.To = to_list
    mail.CC = cc_list
    mail.Subject = subject
    return mail

def build_email_Techelopment(to, cc, auto=False):
    if to == "": # use default adresses
        to = "ironman@ironman.com"
    elif to == 'e': # leave field empty
        to = ""

    if cc == "": # use default adresses
        cc = "avengers@avengers.com"
    elif cc == 'e': # leave field empty
        cc = ""

    default_subject = 'New mission'
    subject = input(f"Subject (blank for default '{default_subject}'): ")
    if subject == "":
        subject = default_subject

    mail = build_basic_email(subject, to, cc)

    body = (f"<span style='font-family: Calibri; font-size:11pt;'>"
            f"Hi, <br><br>NEW MISSION: "
            f"<strong>follow Techelopment!</strong>"
            f"<br><br>Here the links<br><br>")

    body += table_template.format(
                site_url='https://www.techelopment.it',
                fb_url='https://www.facebook.com/techelopment',
                ig_url='https://instagram.com/techelopment',
                x_url='https://twitter.com/techelopment',
                tg_url='https://t.me/techelopment_channel',
                wa_url='https://whatsapp.com/channel/0029VaoJHA1Lo4hkXn1rcn3y',
                medium_url='https://medium.com/@techelopment',
                devto_url='https://dev.to/techelopment',
                yt_url='https://youtube.com/@techelopment?si=UehAP3GeMeIkniEZ')
    body += "<br><br>Regards</span><br><br>"
    mail.HtmlBody = body + signature

    manage_display_auto_send(mail, auto)

def build_email_test(to, cc, auto=False):
    subject = input("Subject: ")
    mail = build_basic_email(subject, to, cc)
    mail.HtmlBody = "test build mail programmatically"
    manage_display_auto_send(mail, auto)

def getEmailAddresses(type_of_email="TO"):
    emails = input(f"{type_of_email} (blank to use default addresses or 'e' to leave field empty): ")

    if emails != "" and emails != 'e':
        # more than one email address must be separated by ;
        emails_splitted = emails.split("@")
        while len(emails_splitted) > 2 and emails.count(';') < int(len(emails_splitted)/2):
            print("use ; to separate addresses")
            emails = input(f"{type_of_email} (blank to use default addresses or 'e' to leave field empty): ")
            if emails == "" or emails == 'e':
                break

    return emails

def email_type_disptacher(email_type):
    to = getEmailAddresses("TO")
    cc = getEmailAddresses("cc")
    auto_send = input("automatic sending? [y/n] (default no): ")
    send = True if auto_send == "y" else False

    match email_type:
        case 1:
            build_email_Techelopment(to, cc, send)
        case 2:
            build_email_test(to, cc, send)

        # In case there is no match
        case _:
            return -1

if __name__ == "__main__":
    email_type = input("Choose what kind of email to build:\n"
                       "  1. Techelopment\n"
                       "  2. test\n"
                       "> ")

    email_type_disptacher(int(email_type))
Enter fullscreen mode Exit fullscreen mode

usage example

Techelopment email

Remember…

Unfortunately, basically, as long as the email window is open, you will not be able to interact with Outlook 🙄.


Follow me #techelopment

Official site: www.techelopment.it
Medium: @techelopment
Dev.to: Techelopment
facebook: Techelopment
instagram: @techelopment
X: techelopment
telegram: @techelopment_channel
youtube: @techelopment
whatsapp: Techelopment

. . . . . . . . . . . . . . . . . . . . . . . . . .