sending an email with python, html + css, reading an .txt file - python

I am trying to code something very simple. The idea is to read a .txt where a html code is written. Specifically it is a table with some css and style modifiers like:
<td style=""background-color:red"" >-49,2%</td>
I can sucessfully read the content of the file and send the email. However, when the email is sent, no background style is applied. The rest of the table is created perfectly.
My code is something like:
fp = open(r"filepath", 'r',encoding='utf-8-sig')
# Create a text/plain message
msg = MIMEText(fp.read()) #Here I also tried with the argument 'html'
fp.close()
s.sendmail("email1", "email2",msg.as_string())
Is there any way I can also get the style applied? The email provider is gmail and I know it can work because if I try to use the 'msg' variable as a literal string, like:
msg= r"<table...> <td style=""background-color:red"" >-49,2%</td> </table>"
it sends the background format correctly. The issue must be of other sort.
Thank you very much!

Solved.
I hope it helps,
The modifier.as_string() was the issue. Removing it on:
s.sendmail("email1", "email2",msg.as_string())
to
s.sendmail("email1", "email2",msg)
made it work.

Related

Python Multipart E-Mail

I'm trying to build a python mailserver that rewrites certain URLs (given in a text file).
Plaintext messages work without problems, but multipart (HTML) E-Mails are a bit of concern to me. I can't find a proper way to rebuild the message after modifying the body and replace functions do not exist for email in python.
What I have currently (for plaintext messages, currently without replacement):
if(not msg.is_multipart()):
for part in msg.walk():
parttype = part.get_content_type()
partmaintype = parttype.split("/")[0]
partsubtype = parttype.split("/")[1]
parttype=partmaintype + "/" + partsubtype
bodypart = part.get_body(('plain','html'))
#bodypart = bodypart.get_content().encode()
bodypart = bodypart.get_content()
print(bodypart)
newmsg.set_content(bodypart)
#print(newmsg)
newdata = bytes(newmsg)
The _structure(msg) function is in knowledge to me, but I cannot find a way to preserve this message structure (to rebuild the email with a new message object). I also know about the msg.walk() function, but when used with multipart message contents, the subcontents occur twice.
Thanks a lot in advance!

Python email messages not threading in Outlook. Possible header issue? Custom format or line length a possible solution?

An email comes in with the following header from an #outlook.com address
Message-ID:
<DM5PR1101MB2074A897F300D11D32C71B75EC049#DM5PR1101MB2074.namprd11.prod.outlook.com>
To respond with Python I put that message identifier in the In-Reply-To header
from email.message import EmailMessage
msg = EmailMessage()
msg['In-Reply-To'] = '<DM5PR1101MB2074A897F300D11D32C71B75EC049#DM5PR1101MB2074.namprd11.prod.outlook.com>'
However, when I print this message the header appears as follows:
In-Reply-To: =?utf-8?q?=3CDM5PR1101MB2074A897F300D11D32C71B75EC049=40DM5PR11?=
=?utf-8?q?01MB2074=2Enamprd11=2Eprod=2Eoutlook=2Ecom=3E?=
And when it arrives in Outlook it's not put in the same conversation thread as the original message.
Does anyone know why this may be happening and/or know of any work-around?
I suspect that this is happening because Python is trying to split the header across multiple lines since this header is above the RFC-specified line length and when it splits across multiple lines it starts each line with '?utf-8?q and converts the < character to ?=3C, etc... Whereas Outlook is expecting the header to be of the format
In-Reply-To':
<DM5PR1101MB2074A897F300D11D32C71B75EC049#DM5PR1101MB2074.namprd11.prod.outlook.com>'
Perhaps if someone knows how to force the header to be in this format that would suffice as a work-around.
Any help would be greatly appreciated :)

Using a field from CSV errors my previously working code

I am working on sending emails with python and have a decent grasp on that however I am struggling with using info from a CSV File.
I have a piece of code:
s.login('example#gmail.com', 'expassword')
that when used with a preset login and password works, it sends the email to my recipient, which is later set in my code, however as soon as I use the code:(chooses a random email and password from a CSV to send from(for a spam email demo))
with open ('C:/Filepath/randomsender.csv') as file:
reader = csv.reader(file)
sender = random.choice(list(reader))
followed by
s.login(sender)
I get an auth error because it cannot read the chosen line properly even though the result of sender is formatted the same as:
'example#gmail.com', 'expassword'
If you want to pass list elements as position param, you need to unpack it.
this might help
e.g.
s.login(*sender)

Python MIME Base64 Encoding

I need to generate a MIME attachment that contains a Base64 encoded file. However what I need to also allow for is encoding the attachment WITHOUT any new lines. The code I have is as followed:
msg_obj = MIMEMultipart()
msg_atch = MIMEBase(mime_type, mime_subtype)
msg_atch.set_payload(file_data)
Encoders.encode_base64(msg_atch)
msg_obj.attach(msg_atch)
What I have tried to perform to remove the new lines in the attach base64 message was this:
msg_obj = MIMEMultipart()
msg_atch = MIMEBase(mime_type, mime_subtype)
msg_atch.set_payload(file_data)
Encoders.encode_base64(msg_atch)
msg_atch.strip()
msg_obj.attach(msg_atch)
However this failed to change the results of the data. If anyone has any ideas on how to allow for this, it would be great.
I noticed in the penultimate line of your 2nd sample code, you call the msg_atch.strip() function. The problem with this is that there isn't any function strip() of MIMEBase.
What you probably want to do is something along the lines of this:
msg_obj = MIMEMultipart()
msg_atch = MIMEBase(mime_type, mime_subtype)
msg_atch.set_payload(file_data)
Encoders.encode_base64(msg_atch)
msg_atch._payload = msg_atch._payload.replace('\n','')
msg_obj.attach(msg_atch)
The MIMEBase._payload string is the actual (in this case, base64) content used by the attachment.
This code will take the inner content of the MIMEBase attachment and eliminate the extra newlines - including the ones inside to provide nice formatting of base64 text for "human readability" (my question is why they even bother). If you just want to get rid of the newlines at the end, just use msg_atch._payload = msg_atch._payload.rstrip('\n').
Keep in mind that the header of the attachment (Content-Type: application/octet-stream and MIME-Version: 1.0 are parts) require these newlines.
Also, try to remember that you shouldn't normally be editing internal variables in this fashion. However, one of the things I find nice about Python is that there are really no private members of a class, so you can modify whatever you want in a class. We can do whatever we want, especially if it's a special condition.
Happy Coding!

How do I allow a JSON response in a mechanize test?

I have a web service that returns JSON responses when successful. Unfortunately, when I try to test this service via multi-mechanize, I get an error - "not viewing HTML". Obviously it's not viewing HTML, it's getting content clearly marked as JSON. How do I get mechanize to ignore this error and accept the JSON it's getitng back?
It turns out mechanize isn't set up to accept JSON responses out of the box. For a quick and dirty solution to this, update mechanize's _headersutil.py file (check /usr/local/lib/python2.7/dist-packages/mechanize).
In the is_html() method, change the line:
html_types = ["text/html"]
to read:
html_types = ["text/html", "application/json"]

Categories

Resources