stranger6667 / postmarker Goto Github PK
View Code? Open in Web Editor NEWPython client library for Postmark API
Home Page: https://postmarker.readthedocs.io/en/stable/
License: MIT License
Python client library for Postmark API
Home Page: https://postmarker.readthedocs.io/en/stable/
License: MIT License
MIME messages may contain HTML content and attachments - it should be properly supported
It will be better to use something simple instead of 2 client classes (and another one for status api)
E.g.
from postmarker.core import PostmarkClient
postmark = PostmarkClient(token='API_TOKEN')
postmark.emails.send(...)
postmark.bounces.all()
postmark.status
postmark.last_incident
Something similar to python-postmark
Another useful feature - use Postmark test API key - POSTMARK_API_TEST
.
Should be very easy to add test builds.
Example - https://github.com/Stranger6667/pyanyapi/blob/master/.travis.yml
Instance returned by Bounce manager:
>>> bounce = server_client.bounces.get(1)
Should provide the following:
>>> bounce.dump # /bounces/{bounceid}/dump
>>> bounce.activate() # /bounces/{bounceid}/activate
along with basic fields like Type
, TypeCode
, etc.
The methods above should also be available for Bounce manager:
>>> server_client.bounces.activate(1)
>>> server_client.bounces.get_dump(1)
It could be split in chunks
Header should be decoded
E.g. signals for some events like email sending. It could be useful
These messages could be transformed to Postmark format, so it could be useful feature
For now maximum is 500 for bounces, domains and possibly for templates (there is no info in the docs)
If large HTML files (particularly those with large lines, for example anything produced using MJML with the --min
minification flag enabled) are sent using Postmarker, the HTML is escaped using quoted-printable
and sent to Postmark's API that way, when it should actually be sent unquoted.
The reason this happens is under the hood, Postmarker calls the Django email backend's message.message()
within EmailBackend.prepare_message()
. This has the message turn itself into MIME, which then is later deconstructed back to a dict using Email.from_mime()
. Since Django thinks it's serializing the email for an SMTP send, it automatically quotes the email or alternative attachment under certain circumstances (utf-8 + long lines, see django.core.mail.message.py:220
) and sets the Content-Transfer-Encoding
to quoted-printable
. When Postmarker turns that back into a dict, it loses the Content-Transfer-Encoding
header and does not unquote, leading to the quoted version being sent directly via Postmark's API and completely incorrect emails being sent, particularly if they include any Outlook-specific markup that uses XML namespaces and colons.
One way to fix this is when converting the MIME email back to a dictionary, to detect if it has been quoted and then to unquote using email.quoprimime.body_decode(...)
. Alternatively, the better way to fix this is to refactor simply not to let Django's mailing backend create the MIME email at all, and simply send the body and headers to Postmark via the API without doing any unnecessary processing. This is how djrill for Mandrill works, and emails I sent with Postmarker that got mangled were not mangled with djrill.
Sorry to be leaving you so many issues!
Looks like there's now an issue when sending messages at least with unicode characters in the body. The message I am sending usings django.template.Template
to render a template to a string and then submit it as the message HTML body.
This problem goes away if you temporarily set sys.setdefaultencoding('utf8')
(which you obviously shouldn't be doing).
Here is the traceback:
/Users/../utils/emails.pyc in send_html_email(subject, from_email, to_emails, reply_to, text_body, html_body, headers, tag, metadata, subaccount, attachments, send_in, extra_params, bcc)
128 setattr(msg, key, value)
129
--> 130 msg.send()
131
132
/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/django/core/mail/message.pyc in send(self, fail_silently)
340 # send to.
341 return 0
--> 342 return self.get_connection(fail_silently).send_messages([self])
343
344 def attach(self, filename=None, content=None, mimetype=None):
/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/django.pyc in send_messages(self, email_messages)
55 client_created = self.open()
56 prepared_messages = [self.prepare_message(message) for message in email_messages]
---> 57 response = self.client.emails.send_batch(*prepared_messages, TrackOpens=self.get_option('TRACK_OPENS'))
58 msg_count = len(response)
59 if client_created:
/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in send_batch(self, *emails, **extra)
322 :param extra: dictionary with extra arguments for every message in the batch.
323 """
--> 324 return self.EmailBatch(*emails).send(**extra)
325
326 # NOTE. The following methods are included here to expose better interface without need to import relevant classes.
/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in send(self, **extra)
242 :rtype: `list`
243 """
--> 244 emails = self.as_dict(**extra)
245 responses = [self._manager._send_batch(*batch) for batch in chunks(emails, self.MAX_SIZE)]
246 return sum(responses, [])
/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in as_dict(self, **extra)
220 :return: List of dictionaries.
221 """
--> 222 return [self._construct_email(email, **extra) for email in self.emails]
223
224 def _construct_email(self, email, **extra):
/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in _construct_email(self, email, **extra)
229 email = Email(manager=self._manager, **email)
230 elif isinstance(email, (MIMEText, MIMEMultipart)):
--> 231 email = Email.from_mime(email, self._manager)
232 elif not isinstance(email, Email):
233 raise ValueError
/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in from_mime(cls, message, manager)
178 """
179 if isinstance(message, MIMEMultipart):
--> 180 text, html, attachments = deconstruct_multipart(message)
181 else:
182 text, html, attachments = message.get_payload(decode=True).decode(), None, []
/Users/../.virtualenvs/TCJ/lib/python2.7/site-packages/postmarker/models/emails.pyc in deconstruct_multipart(message)
85 text = part.get_payload(decode=True).decode()
86 elif content_type == 'text/html':
---> 87 html = part.get_payload(decode=True).decode()
88 elif content_type != 'multipart/alternative':
89 attachments.append(part)
UnicodeDecodeError: 'ascii' codec can't decode byte 0xc2 in position 3495: ordinal not in range(128)
I'm currently getting around this with the following context manager, but obviously this cannot be the case long-term:
@contextlib.contextmanager
def set_default_encoding(encoding='ascii'):
"""\
Temporarily set the default encoding within the span of the context.
NOTE: Using `sys.setdefaultencoding()` is HIGHLY DISCOURAGED and this context
manager should only be used when working with third party libraries that incorrectly
handle unicode/strings/bytestrings and cannot easily be patched, updated, or worked around.
"""
import sys
reload(sys)
original_encoding = sys.getdefaultencoding()
sys.setdefaultencoding(encoding)
yield
sys.setdefaultencoding(original_encoding)
It would be better if get_connection will return PostmarkClient instance
We need support for:
Completely optional
This feature allows users to access to Bounce API in this way:
server_client.bounces.deliverystats
or
bounce = server_client.bounces.get(1) # Get bounce with ID=1
There should be informative error messages instead of default in 'raise_for_status'
It could be useful feature:
>>> server_pool = ServerPool(tokens=['token1', 'token2'])
>>> server_pool.bounces.all()
[<Bounce: 1>, <Bounce: 2>]
It allows users to work with multiple server clients in the same way as with single client
Now send_batch returns list of results, so for <=500 messages length always will be 1
I can't figure out a way to send Tag
information to Postmark when using the Django EmailBackend
. I did dive deep into the code but unfortunately had difficulty figuring out where the right place even to patch it would be.
I know I could use the regular PostmarkClient
, but it's important for us to continue using the built-in Django mechanism so that we can stay vendor agnostic.
Can you point me in the right direction on where a patch would go?
For example - track opens or something else
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.