I am trying to write a newsletter tool so I can send the same email to multiple users. It should take as input a string containing text, html and some custom BBcode-tags and send the mail offering primary the mail content as Html-Mail and as secondary backup as plain text.
The desired output should look like this:
Lieber Eric
Herzlich willkommen beim 'Eye Contact Experiment'. Wir werden uns am xx.yy.zzzz um 19:00 an XYZ treffen.
Wir freuen uns auf dich.
Liebe Grüsse,
Darth Vader
[Social Freedom Crew](https://www.socialfreedomcrew.com/)
What I actually receive is this:
From: myself@gmail.com
To: recepient@gmail.com
MIME-Version: 1.0
Content-Type: multipart/alternative;
boundary="===============5497262035116171126=="
--===============5497262035116171126==
Content-Type: text/plain; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
Lieber Ace
Herzlich willkommen bei '36 Fragen zum Verlieben'. Wir werden uns am xx.yy.zz=
zz um 19:00 an XYZ treffen.
Wir freuen uns auf dich.
Liebe Gr=C3=BCsse,
Darth Vader
Social Freedom Crew
--===============5497262035116171126==
Content-Type: text/html; charset="utf-8"
Content-Transfer-Encoding: quoted-printable
MIME-Version: 1.0
Lieber Ace
Herzlich willkommen beim 'Eye Contact Experiment'. Wir werden uns am xx.yy.zz=
zz um 19:00 an XYZ treffen.
Wir freuen uns auf dich.
Liebe Gr=C3=BCsse,
Darth Vader
<a href=3D"www.socialfreedomcrew.com/">Social Freedom Crew</a>
--===============5497262035116171126==--
The two issues I face are:
- I only want the HTML version of the email shown, or if html is disabled, the plain text alternative
- I can see issues in the text encoding (Gr=C3=BCsse which should be shown as the German word 'Grüsse')
This is the code I have written so far:
import smtplib as smtp
from email.message import EmailMessage
# some code parsing the initial message and a list of recepients
class IoManager:
def __init__(self) -> None:
pass
def read_csv(self, file_path='ignore/ticketexport.csv') -> list[Contact]:
with open(file_path, mode='r') as file:
# reading the CSV file
csvFile = csv.DictReader(file)
contacts: list[Contact] = []
for line in csvFile:
contacts.append(
Contact(firstname=line['Vorname'], email_address=line['E-Mail']))
return contacts
def read_txt(self, file_path: str = 'input/message.txt') -> str:
with open(file_path, mode='r', encoding='utf-8') as file:
return str(file.read())
class Content_Parser:
def __init__(self) -> None:
pass
def convert(self, content_string):
pass
class BBCode_Parser(Content_Parser):
Tag_Dictionary = {
# Identifier: regex_string
'FIRSTNAME': "\[firstname]"
}
def __init__(self) -> None:
super().__init__()
def convert(self, content_string: str, regex: str, data: str) -> str:
'''
Converts BBCode filtered by regex expression with data provided.
'''
# super().convert()
return re.sub(regex, data, content_string)
class HTML_Remover(Content_Parser):
def __init__(self) -> None:
super().__init__()
def convert(self, content_html: str):
'''
Removes all HTML tags from content_html.
'''
soup = BeautifulSoup(content_html, "html5lib")
return soup.get_text()
class Email:
def __init__(self) -> None:
self.msg = EmailMessage()
def compose_mail(self, subject, sender, contact: Contact):
# Prepare message content:
self.msg['Subject'] = subject
self.msg['From'] = sender
self.msg['To'] = contact.email_address
self.msg.set_content(contact.content_plain)
self.msg.add_alternative(contact.content_html, subtype='html')
return self.msg
class SmtpManager:
EMAIL_USER = credentials.SMPT_ACCESS_CREDENTIALS['GMAIL']['EMAIL_ADDRESS']
EMAIL_PASSWORD = credentials.SMPT_ACCESS_CREDENTIALS['GMAIL']['EMAIL_PASSWORD']
SMTP_SERVER_ADDRESS = smtp_config.SMTP_SERVERS_ADDRESS['GMAIL']['URL']
SMTP_PORT = smtp_config.SMTP_SERVERS_ADDRESS['GMAIL']['SSL_PORT']
def __init__(self) -> None:
self.smtp = smtp
def send_mails(self, emails: list[Email]):
# make smtp url and port selectable
with self.smtp.SMTP_SSL(SmtpManager.SMTP_SERVER_ADDRESS, SmtpManager.SMTP_PORT) as smtp:
smtp.login(SmtpManager.EMAIL_USER, SmtpManager.EMAIL_PASSWORD)
for email in emails:
smtp.send_message(email.msg)
def main():
print('main:')
# Some code to fetch all receivers for the mail
# Convert raw message to html and plain text content of mail
raw_message = IoManager().read_txt()
subject_body: dict = Txt_Splitter().convert(raw_message)
subject = subject_body['subject']
raw_message = subject_body['body']
bbcode_parser = BBCode_Parser()
html_remover = HTML_Remover()
for contact in contacts:
contact.content_html = bbcode_parser.convert(
raw_message,
BBCode_Parser.Tag_Dictionary['FIRSTNAME'], # transforms [firstname]into e.g John
contact.firstname
)
contact.content_plain = html_remover.convert(str(contact.content_html))
# Compose Email
sender = credentials.SMPT_ACCESS_CREDENTIALS['GMAIL']['EMAIL_ADDRESS']
emails: list[Email] = []
for contact in contacts:
mail = Email()
mail.compose_mail(subject, sender, contact)
emails.append(mail)
# send emails
smtp_manager = SmtpManager()
smtp_manager.send_mails(emails)
if __name__ == '__main__':
main()
MIMEMultipart and sendmail() did not work either
I also tried out with using MIMEMultipart
instead of EmailMessage
and sendmail()
instead of send_message()
. This approach has been suggested here and lead to sending only the Html-part of the content but al line breaks in the string were lost, which is undesireable.
What have I overlooked and thus should do to to make it work the way I want?