Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
gotlium committed May 19, 2016
2 parents 60484b6 + 7e54016 commit fe302f7
Show file tree
Hide file tree
Showing 27 changed files with 868 additions and 53 deletions.
60 changes: 57 additions & 3 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ Django-Db-Mailer
.. image:: https://api.codacy.com/project/badge/grade/ad1442e15215494499ed08b80d4c41c5
:target: https://www.codacy.com/app/gotlium/django-db-mailer
:alt: Codacy
.. image:: https://img.shields.io/badge/python-2.6,2.7,3.4+,pypy,pypy3-blue.svg
:alt: Python 2.6, 2.7, 3.4+, pypy, pypy3
.. image:: https://img.shields.io/badge/python-2.7,3.4+,pypy,pypy3-blue.svg
:alt: Python 2.7, 3.4+, pypy, pypy3
:target: https://pypi.python.org/pypi/django-db-mailer/
.. image:: https://img.shields.io/pypi/v/django-db-mailer.svg
:alt: Current version on PyPi
Expand Down Expand Up @@ -267,6 +267,48 @@ By default ``django-dbmail`` used 4 built-in backends (Mail/Sms/Tts/Push).
But nothing prevents to write your own backend to work with all that you want.


DBMail Providers
----------------
Battery have some built-in providers for most popular services, which will be
used without any dependencies with built-in backends.

**Push notifications for mobile apps:**

* Apple APNs/APNs2
* Google GCM
* Microsoft Tile/Toast/Raw
* BoxCar
* Parse

**Notifications for team:**

* Slack/Mattermost
* Boxcar
* Prowl
* Pushover
* PushAll

**Browser notifications:**

* Centrifugo
* PubNub
* BoxCar
* PushAll

**SMS notifications:**

* Nexmo
* Twilio
* IQsms
* SmsAero

**Mail notifications:**

* SendinBlue

*You can find providers settings on docs.*


Demo installation
-----------------

Expand Down Expand Up @@ -448,14 +490,26 @@ Install ``django-ses`` app via pip. Configure your settings:
*Note: You can use any backends designed as django email backend*

**Tracking**:
**Tracking**

.. code-block:: bash
$ pip install httpagentparser django-ipware
For track information about user, or about mail is read, you must be enable logging, and enable tracking on settings.

**MJML**

MJML is a markup language designed to reduce the pain of coding a responsive email.
Install ``django-mjml`` app via pip and ``mjml`` via npm. And configure your settings:

.. code-block:: python
INSTALLED_APPS = (
...,
'mjml',
)
**Older versions**

Expand Down
9 changes: 6 additions & 3 deletions dbmail/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,23 +79,26 @@ def send_db_mail(*args, **kwargs):


def send_db_sms(*args, **kwargs):
from dbmail.defaults import BACKEND
from dbmail.defaults import BACKEND, SMS_QUEUE

kwargs['backend'] = kwargs.pop('backend', BACKEND['sms'])
kwargs['queue'] = kwargs.pop('queue', SMS_QUEUE)
return db_sender(*args, **kwargs)


def send_db_tts(*args, **kwargs):
from dbmail.defaults import BACKEND
from dbmail.defaults import BACKEND, TTS_QUEUE

kwargs['backend'] = kwargs.pop('backend', BACKEND['tts'])
kwargs['queue'] = kwargs.pop('queue', TTS_QUEUE)
return db_sender(*args, **kwargs)


def send_db_push(*args, **kwargs):
from dbmail.defaults import BACKEND
from dbmail.defaults import BACKEND, PUSH_QUEUE

kwargs['backend'] = kwargs.pop('backend', BACKEND['push'])
kwargs['queue'] = kwargs.pop('queue', PUSH_QUEUE)
return db_sender(*args, **kwargs)


Expand Down
3 changes: 3 additions & 0 deletions dbmail/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,9 @@ def get_settings(key, default):
(9, _("Deferred")),
))
CELERY_QUEUE = get_settings('DB_MAILER_CELERY_QUEUE', 'default')
PUSH_QUEUE = get_settings('DB_MAILER_PUSH_QUEUE', 'default')
SMS_QUEUE = get_settings('DB_MAILER_SMS_QUEUE', 'default')
TTS_QUEUE = get_settings('DB_MAILER_TTS_QUEUE', 'default')
SIGNALS_QUEUE = get_settings('DB_MAILER_SIGNALS_QUEUE', 'default')
SIGNALS_MAIL_QUEUE = get_settings('DB_MAILER_SIGNALS_MAIL_QUEUE', 'default')
ENABLE_CELERY = get_settings('DB_MAILER_ENABLE_CELERY', True)
Expand Down
Empty file.
70 changes: 70 additions & 0 deletions dbmail/providers/apple/apns.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
# -*- encoding: utf-8 -*-

from binascii import a2b_hex
from json import dumps
from socket import socket, AF_INET, SOCK_STREAM
from struct import pack
from time import time

try:
from ssl import wrap_socket
except ImportError:
from socket import ssl as wrap_socket

from django.conf import settings

from dbmail.providers.apple.errors import APNsError
from dbmail import PY3


def send(token_hex, message, **kwargs):
"""
Site: https://apple.com
API: https://developer.apple.com
Desc: iOS notifications
"""
is_enhanced = kwargs.pop('is_enhanced', False)
identifier = kwargs.pop('identifier', 0)
expiry = kwargs.pop('expiry', 0)
data = {
"aps": {
'alert': message,
'content-available': kwargs.pop('content_available', 0) and 1
}
}
data['aps'].update(kwargs)
payload = dumps(
data, separators=(',', ':'), ensure_ascii=False).encode('utf-8')

token = a2b_hex(token_hex)
if is_enhanced is True:
fmt = '!BIIH32sH%ds' % len(payload)
expiry = expiry and time() + expiry
notification = pack(
fmt, 1, identifier, expiry,
32, token, len(payload), payload)
else:
token_length_bin = pack('>H', len(token))
payload_length_bin = pack('>H', len(payload))
zero_byte = bytes('\0', 'utf-8') if PY3 is True else '\0'
notification = (
zero_byte + token_length_bin + token +
payload_length_bin + payload)

sock = socket(AF_INET, SOCK_STREAM)
sock.settimeout(3)
sock.connect((settings.APNS_GW_HOST, settings.APNS_GW_PORT))
ssl = wrap_socket(
sock, settings.APNS_KEY_FILE,
settings.APNS_CERT_FILE,
do_handshake_on_connect=False)

result = ssl.write(notification)

sock.close()
ssl.close()

if not result:
raise APNsError

return True
48 changes: 48 additions & 0 deletions dbmail/providers/apple/apns2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from json import dumps

from django.conf import settings
from hyper import HTTP20Connection
from hyper.tls import init_context

from dbmail.providers.apple.errors import APNsError


def send(token_hex, message, **kwargs):
"""
Site: https://apple.com
API: https://developer.apple.com
Desc: iOS notifications
Installation and usage:
pip install hyper
"""

priority = kwargs.pop('priority', 10)
topic = kwargs.pop('topic', None)
data = {
"aps": {
'alert': message,
'content-available': kwargs.pop('content_available', 0) and 1
}
}
data['aps'].update(kwargs)
payload = dumps(
data, separators=(',', ':'), ensure_ascii=False).encode('utf-8')

headers = {
'apns-priority': priority
}
if topic is not None:
headers['apns-topic'] = topic

ssl_context = init_context()
ssl_context.load_cert_chain(settings.APNS_CERT_FILE)
connection = HTTP20Connection(
settings.APNS_GW_HOST, settings.APNS_GW_PORT, ssl_context=ssl_context)

stream_id = connection.request(
'POST', '/3/device/{}'.format(token_hex), payload, headers)
response = connection.get_response(stream_id)
if response.status != 200:
raise APNsError(response.read())
return True
2 changes: 2 additions & 0 deletions dbmail/providers/apple/errors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class APNsError(Exception):
pass
Empty file.
37 changes: 37 additions & 0 deletions dbmail/providers/centrifugo/push.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- encoding: utf-8 -*-

import hmac
from hashlib import sha256
from json import dumps
from urllib import urlencode
from urllib2 import urlopen, Request

from django.conf import settings


class CentrifugoError(Exception):
pass


def send(channel, data, **kwargs):
if type(data) not in (set, dict, list, tuple):
kwargs['message'] = data

data = dumps([{
"method": "publish",
"params": {
"channel": channel,
"data": kwargs,
},
}])
sign = hmac.new(settings.CENTRIFUGO_TOKEN, data, sha256).hexdigest()
payload = urlencode({'data': data, 'sign': sign})

response = urlopen(
Request(settings.CENTRIFUGO_API),
payload,
timeout=10
)
if response.code != 200:
raise CentrifugoError(response.read())
return True
Empty file.
51 changes: 51 additions & 0 deletions dbmail/providers/google/android.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- encoding: utf-8 -*-

from httplib import HTTPSConnection
from json import dumps, loads
from urlparse import urlparse

from django.conf import settings


class GCMError(Exception):
pass


def send(user, message, **kwargs):
"""
Site: https://developers.google.com
API: https://developers.google.com/cloud-messaging/
Desc: Android notifications
"""

headers = {
"Content-type": "application/json",
"Authorization": "key=" + kwargs.pop("gcm_key", settings.GCM_KEY)
}

hook_url = 'https://android.googleapis.com/gcm/send'

data = {
"registration_ids": [user],
"data": {
"title": kwargs.pop("event"),
'message': message,
}
}
data['data'].update(kwargs)

up = urlparse(hook_url)
http = HTTPSConnection(up.netloc)
http.request(
"POST", up.path,
headers=headers,
body=dumps(data))
response = http.getresponse()

if response.status != 200:
raise GCMError(response.reason)

body = response.read()
if loads(body).get("failure") > 0:
raise GCMError(repr(body))
return True
Empty file.
40 changes: 40 additions & 0 deletions dbmail/providers/http/push.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# -*- encoding: utf-8 -*-

from httplib import HTTPConnection, HTTPSConnection
from urlparse import urlparse
from json import dumps

from django.conf import settings


class HTTPError(Exception):
pass


def send(hook_url, message, **kwargs):
headers = {
"Content-type": "application/json"
}

http_key = kwargs.pop("http_key", None)
if not http_key and hasattr(settings, 'HTTP_KEY'):
http_key = settings.HTTP_KEY
headers["Authorization"] = "key={}".format(http_key)

kwargs["msg"] = message

up = urlparse(hook_url)
if up.scheme == 'https':
http = HTTPSConnection(up.netloc)
else:
http = HTTPConnection(up.netloc)

http.request(
"POST", up.path,
headers=headers,
body=dumps(kwargs))
response = http.getresponse()

if response.status != 200:
raise HTTPError(response.reason)
return True
Empty file.
Loading

0 comments on commit fe302f7

Please sign in to comment.