I'm working with django, i need send a mail to many emails, i want to do this with a high level library like python-mailer, but i need use bcc field, any suggestions?
You should look at the EmailMessage class inside of django, supports the bcc.
Complete docs availble here:
http://docs.djangoproject.com/en/dev/topics/email/#the-emailmessage-class
Quick overview:
The EmailMessage class is initialized with the following parameters (in the given order, if positional arguments are used). All parameters are optional and can be set at any time prior to calling the send() method.
subject: The subject line of the e-mail.
body: The body text. This should be a plain text message.
from_email: The sender's address. Both fred#example.com and Fred forms are legal. If omitted, the DEFAULT_FROM_EMAIL setting is used.
to: A list or tuple of recipient addresses.
bcc: A list or tuple of addresses used in the "Bcc" header when sending the e-mail.
connection: An e-mail backend instance. Use this parameter if you want to use the same connection for multiple messages. If omitted, a new connection is created when send() is called.
attachments: A list of attachments to put on the message. These can be either email.MIMEBase.MIMEBase instances, or (filename, content, mimetype) triples.
headers: A dictionary of extra headers to put on the message. The keys are the header name, values are the header values. It's up to the caller to ensure header names and values are in the correct format for an e-mail message.
Related
I'm working with exchangelib and python3 to manage an Exchange mailbox, so far my code works ok, but I wanted to check how many attachments an email has before to go forward.
This is what I have so far:
def send_email(account, subject, body, recipients, attachments=None):
to_recipients = []
for recipient in recipients:
to_recipients.append(Mailbox(email_address=recipient))
# Create message
m = Message(account=account,
folder=account.sent,
subject=subject,
body=body,
to_recipients=to_recipients)
b = attachments.count
log.info(b)
m.attach(attachments)
m.send_and_save()
I'm calling this function to create a new email from a previous one I've received with attachments. When the email has a just one attachment it works fine, however when the received email has more than one attachment it fail. That is why I wanted to check how many attachments the received email has before to proceed.
I found out this attribute for attachments object but the result I got is this:
<built-in method count of list object at 0x10a23be10>
So, how could I check if the attachments object, which is a type FileAttachment,has more than one attachment? Even better, how could I attach more than one attachment to my new email?
For the last question I have this code, which does not work:
for attachment_name, attachment_content in attachments or []:
log.info('loop attachments')
file = FileAttachment(name=attachment_name, content=attachment_content)
m.attach(file)
This is the error I'm receiving:
for attachment_name, attachment_content in attachments or []:
TypeError: cannot unpack non-iterable FileAttachment object
There are multiple misconceptions here.
First, you need to check first which type your attachments argument is. Judging by the output, you are passing a list (the built-in list type). Later on, you are printing attachments.count which is actually the list.count() method (see https://docs.python.org/3.8/tutorial/datastructures.html), which does not make sense to print. If you want to get the size of the attachments argument, use len(attachments), since it's just a list.
A single FileAttachment is one attachment, not a list of attachments. Instead, I assume that your attatchments argument is a list of FileAttachment objects.
Regarding the last TypeError, you are iterating over a list of FileAttachment objects, but then treating each object as if it were a tuple by trying to unpack the object. That won't work. If you want to access the name and content attributes of each FileAttachment, do this instead:
for a in attachments:
print(a.name, a.content)
I am reading an email file stored in my machine,able to extract the headers of the email, but unable to extract the body.
# The following part is working , opening a file and reading the header .
import email
from email.parser import HeaderParser
with open(passedArgument1+filename,"r",encoding="ISO-8859-1") as f:
msg=email.message_from_file(f)
print('message',msg.as_string())
parser = HeaderParser()
h = parser.parsestr(msg.as_string())
print (h.keys())
# The following snippet gives error
msgBody=msg.get_body('text/plain')
Is there any proper way to extract only the body message.Stuck at this point.
For reference the email file can be downloaded from
https://drive.google.com/file/d/0B3XlF206d5UrOW5xZ3FmV3M3Rzg/view
The 3.6 email lib uses an API that is compatible with Python 3.2 by default and that is what is causing you this problem.
Note the default policy in the declaration below from the docs:
email.message_from_file(fp, _class=None, *, policy=policy.compat32)
If you want to use the "new" API that you see in the 3.6 docs, you have to create the message with a different policy.
import email
from email import policy
...
msg=email.message_from_file(f, policy=policy.default)
will give you the new API that you see in the docs which will include the very useful: get_body()
Update
If you are having the AttributeError: 'Message' object has no attribute 'get_body' error, you might want to read what follows.
I did some tests, and it seems the doc is indeed erroneous compared to the current library implementation (July 2017).
What you might be looking for is actually the function get_payload() it seems to do what you want to achieve:
The conceptual model provided by an EmailMessage object is that of an
ordered dictionary of headers coupled with a payload that represents
the RFC 5322 body of the message, which might be a list of
sub-EmailMessage objects
get_payload() is not in current July 2017 Documentation, but the help() says the following:
get_payload(i=None, decode=False) method of email.message.Message instance
Return a reference to the payload.
The payload will either be a list object or a string. If you mutate
the list object, you modify the message's payload in place. Optional
i returns that index into the payload.
Optional decode is a flag indicating whether the payload should be decoded or not, according to the Content-Transfer-Encoding
header (default is False).
When True and the message is not a multipart, the payload will be decoded if this header's value is 'quoted-printable' or 'base64'. If some other encoding is used, or the header is missing, or if the payload has bogus data (i.e. bogus base64 or uuencoded data), the payload is returned as-is.
If the message is a multipart and the decode flag is True, then None is returned.
I am using the IMAPClient library in Python. I am able to download the attached document in the email. I am interested in only Excel files.
I am interested to extract the recipient list from the email. Any idea how to do it in Python ?
Here is the code snippet which might be useful
for ind_mail in emails:
msg_string = ind_mail['RFC822'].decode("utf-8")
#print(msg_string.decode("utf-8"))
email_msg = email.message_from_string(msg_string)
for part in email_msg.walk():
# Download only Excel File
filetype = part.get_content_type()
if(filetype == 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'):
#download
The straightforward answer to your question is to get the corresponding headers' values, i.e.:
to_rcpt = email_msg.get_all('to', [])
cc_rcpt = email_msg.get_all('cc', [])
, inside that first loop. The MIME standard doesn't enforce uniqueness on the headers (though strongly suggests it), thus get_all; if not present, you'll still have an empty list for a consecutive loop.
But as tripleee has rightfully pointed out, the mime headers can be easily censored, spoofed or simply removed.
Yet this is the only info persisted and returned by a server, and all mail clients use to present to us :)
Calling msg.get_all will return a list containing one entry per one header, so if you have multiple header, you'll get a list per header
BUT
If one header has multiple emails in a coma-separated way, you will only get one string and you'll have to split it.
The best way to have the list of all the emails from a specific header is to use getaddresses (https://docs.python.org/3/library/email.utils.html#email.utils.getaddresses)
from email.utils import getaddresses
to_rcpt = getaddresses(email_msg.get_all('to', []))
get_all will return an array of all the "To:" headers, and getaddresses will parse each entry and return as many emails as present on each headers. For instance:
message = """
To: "Bob" <email1#gmail.com>, "John" <email2#gmail.com>
To: email3#gmail.com, email4#gmail.com
"""
to_rcpt = getaddresses(email_msg.get_all('to', []))
=> [('Bob', 'email1#gmail.com'), ('John', 'email2#gmail.com'), ('', 'email3#gmail.com'), ('', 'email4#gmail.com')]
After updating to the latest version of sendgrid's python client, the addresses in the bcc field no longer receive emails.
Here's the code used to send the mail:
headers = json.JSONEncoder().encode({'X-SMTPAPI' : headers.json_string()}) if headers else None
email = sendgrid.Mail(from_email=from_email,
from_name=from_name,
to=to_emails, # list of email addresses
cc=cc, # list of email addresses
bcc=bcc, # list of email addresses
subject=subject,
html=html,
text=text,
headers=headers)
There's an open issue on github, but it doesn't look like any progress has been made:
https://github.com/sendgrid/sendgrid-python/issues/83
Anyone have a fix or workaround that doesn't involve using a very old version?
There is a workaround for the current version you are using. Here is the link to it:
https://github.com/sendgrid/sendgrid-python/issues/83#issuecomment-59974718
The latest version behaves exactly as you wish it to behave. The add_to method used to default to the SMTPAPI header instead of the regular To parameter. Now it has been reverted and in order to add recipients to the SMTPAPI you must explicitly do so. Let me know if you have more questions, be glad to answer them.
Consider a request coming from this url /messages/compose/(?P<recipients>[\+\.\w]+)/ where recipients is usernames separated by + sign. After success (message to recipients successfully sent) i am doing:
#success_url = 'message_send_success'
recipients = '+'.join([obj.username for obj in recipients]) #converting them back to original string
reverse(success_url, kwargs={'recipients': recipients})
This is the url to whom it match:
url(r'^/messages/success/(?P<recipients>[\+\.\w]+)$', 'site.views.message_send_success', name='message_send_success')
But it will show all recipients in the url, is there any away i can hide those recipients string to be displayed in url and can access it in request??
Maybe you want to use base64 library:
>>> base64.b64encode("what is that?")
'd2hhdCBpcyB0aGF0Pw=='
>>> base64.b64decode("d2hhdCBpcyB0aGF0Pw==")
'what is that?'
Note: if you want to have more safety urls, you should do some translations on that string (otherwise other user that know base (en)coding will easily decode your value.
Not if you're using a redirect. Django has a "shared-nothing" architecture, which means that between one request and the next no user state persists on the server. For this reason, Django can't (automatically) "remember" what your recipients were before the redirect, so it can access them in the next HTTP request.
What are your reasons for wanting to hide them? Is there sensitive information you can't send back to the client, or something like that? One option to avoid that is to simply repeat the information the client sent (i.e. the original recipients parameter) and have the success view redo the operations that compose did on them.