Skip to content

Commit

Permalink
Improve mail script with jinja2 template and wetty passwords.
Browse files Browse the repository at this point in the history
  • Loading branch information
henninge committed Oct 10, 2022
1 parent 57369c9 commit 83a7dff
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 79 deletions.
21 changes: 15 additions & 6 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -79,21 +79,30 @@ To do that:

## Sending Mails

Ensure that the [Gmail API](https://developers.google.com/gmail/api/quickstart/python#step_1_turn_on_the) is activated.
### Prepare Gmail API

1. [Create a GCP project](https://developers.google.com/workspace/guides/create-project) (or use an existing one)
2. Enable the GMail API for the project (same link as above)
3. [Create credentials (consent screen & OAuth client ID)](https://developers.google.com/workspace/guides/create-credentials)
Configure the consent screen for a desktop app. You will see it when you first use the credentials.
4. Store the credentials as `credentials.json` in the `mail` directory.

### Prepare Python venv and config files

```bash
virtualenv --python=python3.10 .venv
. .venv/bin/activate
cd mail
pip install -r requirements.txt
python3 -m venv .venv
.venv/bin/pip install -r requirements.txt
```

Adjust the files under `mail`:
Adjust the config and template files:

- Add all your attendees to the `mail_info.yaml` file
- Adjust the mail text in `mail_template.txt`

Finally send the mails and thee attachments with: `python3 send_mails.py`
### Send mails

Finally send the mails and the attachments with: `.venv/bin/python send_mails.py`

## Clean up

Expand Down
13 changes: 8 additions & 5 deletions mail/mail_info.example.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
sender: [email protected]
subject: "LFS458 Course"
sender_email: [email protected]
sender_name: TRAINER
subject: "Kubernetes Administration Training Zugangsdaten"
# Comment out if not configured.
wetty_host: wetty-TRAINER.training-lf-kubernetes.fra.ics.inovex.io
attendees:
- Surname: Mustermann
Mail: [email protected]
Short: mmustermann
- name: Max
email: [email protected]
short: mmustermann
25 changes: 23 additions & 2 deletions mail/mail_template.example.txt
Original file line number Diff line number Diff line change
@@ -1,3 +1,24 @@
Hello {Surname},
Hallo {{name}}!

foo bar !
Im Anhang erhältst du ein Zip-Archiv mit den Zugangsdaten zu deiner Trainingsumgebung.
Nach dem Entpacken findest du folgende Dateien:

* ips/{{short}}.txt enthält die Namen und IP-Adressen der Trainings-VMs.
* keys/{{short}} ist dein privater SSH-Schlüssel im PEM-Format.
* keys/{{short}}.ppk ist der selbe Schlüssel im PuTTY-Format.

Du kannst dich zum Testen gerne schon mit den VMs verbinden, nimm aber bitte noch keine Änderungen vor.

ssh -i keys/{{short}} student@IP-ADRESSE-AUS-TXT
{%-if wetty_host is defined %}

Solltest es mit der SSH-Verbindung nicht klappen, kannst du dich auch über ein Web-Terminal verbinden.
Benutze dazu bitte den folgenden Link. Du wirst dort zwar eine Liste mit allen VMs sehen, kannst aber nur auf deine zugreifen.

https://{{short}}:{{wetty_password}}@{{wetty_host}}/
{%- endif %}

Wir sehen uns dann zum Training!

Viele Grüße,
{{sender_name}}
63 changes: 32 additions & 31 deletions mail/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,36 +1,37 @@
# direct dependencies
google-api-python-client==2.3.0
google-auth-oauthlib==0.4.4
pylint==2.8.2
PyYAML==5.4.1
google-api-python-client==2.57.0
google-auth-oauthlib==0.5.2
pylint==2.14.5
PyYAML==6.0
Jinja2==3.1.2
# indirect dependencies
astroid==2.5.6
cachetools==4.2.2
certifi==2020.12.5
chardet==4.0.0
google-api-core==1.26.3
google-auth==1.30.0
astroid==2.11.7
cachetools==5.2.0
certifi==2022.6.15
charset-normalizer==2.1.1
dill==0.3.5.1
google-api-core==2.8.2
google-auth==2.11.0
google-auth-httplib2==0.1.0
googleapis-common-protos==1.53.0
httplib2==0.19.1
idna==2.10
isort==5.8.0
lazy-object-proxy==1.6.0
mccabe==0.6.1
oauthlib==3.1.0
packaging==20.9
pip==21.1
protobuf==3.18.3
googleapis-common-protos==1.56.4
httplib2==0.20.4
idna==3.3
isort==5.10.1
lazy-object-proxy==1.7.1
MarkupSafe==2.1.1
mccabe==0.7.0
oauthlib==3.2.0
platformdirs==2.5.2
protobuf==4.21.5
pyasn1==0.4.8
pyasn1-modules==0.2.8
pyparsing==2.4.7
pytz==2021.1
requests==2.25.1
requests-oauthlib==1.3.0
rsa==4.7.2
setuptools==47.1.0
six==1.15.0
toml==0.10.2
uritemplate==3.0.1
urllib3==1.26.5
wrapt==1.12.1
pyparsing==3.0.9
requests==2.28.1
requests-oauthlib==1.3.1
rsa==4.9
six==1.16.0
tomli==2.0.1
tomlkit==0.11.4
uritemplate==4.1.1
urllib3==1.26.11
wrapt==1.14.1
83 changes: 48 additions & 35 deletions mail/send_mails.py
Original file line number Diff line number Diff line change
@@ -1,25 +1,29 @@
# -*- coding: utf-8 -*-
from __future__ import print_function
import pickle
import os.path
"""
Send emails to attendees via GMail API
"""
import base64
from email.mime.text import MIMEText
from email.mime.multipart import MIMEMultipart
from email.mime.base import MIMEBase
from email import encoders
import base64
import mimetypes
import os.path
import pickle

from apiclient import errors
from googleapiclient.discovery import build
from google_auth_oauthlib.flow import InstalledAppFlow
from google.auth.transport.requests import Request
from apiclient import errors
from jinja2 import Environment, FileSystemLoader, select_autoescape
import yaml


# If modifying these scopes, delete the file token.pickle.
SCOPES = ["https://www.googleapis.com/auth/gmail.send"]


def create_message_with_attachment(sender, receiver, subject, message_text, file):
def create_message(sender, receiver, subject, message_text, file=None):
"""Create a message for an email.
Args:
Expand All @@ -40,20 +44,21 @@ def create_message_with_attachment(sender, receiver, subject, message_text, file
msg = MIMEText(message_text)
message.attach(msg)

content_type, encoding = mimetypes.guess_type(file)
if content_type is None or encoding is not None:
content_type = "application/octet-stream"
if file is not None:
content_type, encoding = mimetypes.guess_type(file)
if content_type is None or encoding is not None:
content_type = "application/octet-stream"

main_type, sub_type = content_type.split("/", 1)
msg = MIMEBase(main_type, sub_type)
with open(file, "rb") as content:
msg.set_payload(content.read())
main_type, sub_type = content_type.split("/", 1)
msg = MIMEBase(main_type, sub_type)
with open(file, "rb") as content:
msg.set_payload(content.read())

# Fixes malformed content: https://www.w3.org/Protocols/rfc1341/5_Content-Transfer-Encoding.html
encoders.encode_base64(msg)
filename = os.path.basename(file)
msg.add_header("Content-Disposition", "attachment", filename=filename)
message.attach(msg)
# Fixes malformed content: https://www.w3.org/Protocols/rfc1341/5_Content-Transfer-Encoding.html
encoders.encode_base64(msg)
filename = os.path.basename(file)
msg.add_header("Content-Disposition", "attachment", filename=filename)
message.attach(msg)

# the message should converted from string to bytes.
message_as_bytes = message.as_bytes()
Expand All @@ -75,19 +80,27 @@ def send_message(service, user_id, message):
Returns:
Sent Message.
"""
message = None
try:
message = (
service.users().messages().send(userId=user_id, body=message).execute()
)
print("Message Id: {}".format(message["id"]))
return message
print(f"Message Id: {message['id']}")
except errors.HttpError as error:
print("An error occurred: {}".format(error))
print(f"An error occurred: {error}")
return message


def render_body(**kwargs):
"""Use Jinja2 to render the email body."""
env = Environment(loader=FileSystemLoader("."), autoescape=select_autoescape())
template = env.get_template("mail_template.txt")
return template.render(**kwargs)


def main():
"""Shows basic usage of the Gmail API.
Lists the user's Gmail labels.
"""
Send emails to attendees via GMail API
"""
creds = None
# The file token.pickle stores the user's access and refresh tokens, and is
Expand All @@ -110,24 +123,24 @@ def main():
service = build("gmail", "v1", credentials=creds)

# Configure mail boilerplate
mail_info = yaml.safe_load(open("mail_info.yaml"))
with open("mail_info.yaml") as mail_info_file:
mail_info = yaml.safe_load(mail_info_file)
attendees = mail_info["attendees"]
sender = mail_info["sender"]
sender = mail_info["sender_email"]
subject = mail_info["subject"]

# Read Mail template
text_template = ""
with open("mail_template.txt") as temp:
text_template = temp.read()
wetty_host = mail_info.get("wetty_host")

for attendee in attendees:
print("Send mail to: {}".format(attendee["Mail"]))
msg = create_message_with_attachment(
print(f"Send mail to: {attendee['email']}")
if wetty_host is not None:
with open(f"../passwords/{attendee['short']}") as pwfile:
attendee["wetty_password"] = pwfile.read()
msg = create_message(
sender,
attendee["Mail"],
attendee["email"],
subject,
text_template.format(**mail_info, **attendee),
"../packages/{}.zip".format(attendee["Short"]),
render_body(**mail_info, **attendee),
f"../packages/{attendee['short']}.zip",
)
send_message(service, "me", msg)

Expand Down

0 comments on commit 83a7dff

Please sign in to comment.