Серверная реализация IOS Push уведомлений (python)
Содержание:
Я уже писал статью про реализацию Push уведомлений с использованием сервиса Urbanairship. У этого способа есть ряд преимуществ и недостатков. Преимущества я описал в вышеприведенной статье, недостаки сервиса в его лимитах на количество бесплатных сообщений и периодических отключениях на техническое обслуживание.
Сегодня мы рассмотрим реализацию IOS Push уведомлений своими руками для Python программ.
Note
Исходные коды для данной статьи можно увидеть по адресу: https://github.com/klen/klen.github.com/tree/master/_code/ios-push
Введение
IOS Push уведомления бывают двух видов: локальные и удаленные. Локальные инициируются приложением на стороне клиента и в данной статье не рассматриваются. Удаленные уведомления отсылаются со стороны сервера на клиентское приложение при помощи службы APNS (Apple Push Notification Service). Процесс выглядит следующим образом:
Получение и конвертация сертификата
Push сертификаты для приложений можно получить на iOS Provisioning Portal.
Выберите свое приложение и зайдите в раздел настроек: Configure.
Скачайте production и development сертификаты (*.cer). Возможно вам потребуется предварительно включить Push нотификации для своего приложения и создать их.
Дальнейшие действия лучше провести на компьютере с OSX. Импортируйте полученные сертификаты и экспортируйте их в формате p12. Вероятно вам потребуется ключ разработчика с которым были созданы CSR запросы.
Полученный файл в формате p12 необходимо переконвертировать в pem. Для этой задачи я написал простенький shell скрипт.
https://github.com/klen/klen.github.com/blob/master/_code/ios-push/convert12.sh
Сконвертируем с его помощью полученный от Apple Dev сертификат в нужный нам формат:
# Получим и подготовим скрипт конвертации
wget https://raw.github.com/klen/klen.github.com/master/_code/ios-push/convert12.sh && chmod +x convert12.sh
# Сконвертируем полученные от Apple сертификаты
./convert12 convert pushprod.p12
./convert12 convert pushdev.p12
В процессе у вас будет запрошен пароль на сертификаты, если они создавались без пароля, просто нажмите Enter. Результатом этой операции будут pem файлы с теми же названиями.
Этим же скриптом их можно протестировать, что в дальнейшем избавит от множества проблем при отладке:
# В результате должно быть установлено соединение с Apple
./convert12 test pushprod.pem prod
./convert12 test pushdev.pem dev
Если соединение сразу сбрасывается или выводятся SSL ошибки, вы что-то сделали неправильно.
Реализация IOS Push в python-приложениях
В своих проектах я использую PyAPNs библиотеку. Простой файл обвязка вокруг нее реализует все основные задачи: push.py
import os.path as op
from apns import APNs, Payload
KEYS_FILE = op.abspath(op.join(op.dirname(__file__), 'fakekey.pem'))
assert op.exists(KEYS_FILE)
def get_server(use_sandbox=False, keys_file=KEYS_FILE):
" Create and return production or develop server. "
return APNs(use_sandbox=use_sandbox, cert_file=keys_file, key_file=keys_file)
def send_notify(token, server=None, **payloads):
" Send simple device notify. "
server = server or get_server()
return server.gateway_server.send_notification(token, Payload(**payloads))
def get_feedbacks(server):
" Get inactive tokens. "
return list(server.feedback_server.items()) # Fix rst**
Пример использования:
from push import send_notify, get_server
token_hex = 'b5bb9d8014a0f9b1d61e21e796d78dccdf1352f23cd32812f4850b87'
result = send_notify(token_hex, alert="Hello world!", sound="default")
Вполне возможно, что токен от приложения будет приходить к вам в формате Base64, тогда перед сохранением необходимо проделать следующие преобразования.
import binascii
from base64 import urlsafe_b64decode
def fix_token(token):
token = token.strip()
token = token.encode('utf-8') if isinstance(token, unicode) else token
token = urlsafe_b64decode(token + '=' * (4 - len(token) % 4))
token = binascii.hexlify(token)
Интеграция с Django
При интеграции с Django проектом необходимо подумать о переключении между developer и production и об асинхронности запросов в APNS.
Асинхронность достигается использованием Celery.
from celery.decorators import task
from ios import send_notify
@task(ignore_result=True, max_retries=2, default_retry_delay=10, priority=1)
def _async_ios_push(token, payloads, **kwargs):
return send_notify(token, **payloads)
def send_async_ios_notify(message, token=None):
" Send IOS push notification. "
if not token:
return False
if isinstance(message, basestring):
message = dict(alert=message)
data = dict(item for item in message.iteritems() if not item[0] in ['alert', 'sound'])
return _async_ios_push.apply_async(args=(token, dict(
sound=message.get('sound', 'default'),
alert=message.get('alert'),
custom=dict(data=data),
))) # Fix rst**
Стоит подумать и об отключении неактивных устройств: tasks.py:
from abstract_app.models import Client
from celery.decorators import periodic_task
from .ios import get_feedbacks
from .settings import IOS_CLEAN_INTERVAL
@periodic_task(ignore_result=True, run_every=IOS_CLEAN_INTERVAL)
def parse_async_feedbacks():
feedbacks = get_feedbacks()
if feedbacks:
tokens, _ = zip(*feedbacks)
Client.objects.filter(token__in=tokens).update(active=False)
return True # Fix rst*
Полностью пример модуля для Django вы можете увидеть по ссылке: https://github.com/klen/klen.github.com/tree/master/_code/ios-push/django/push
Написать функцию для асинхронной broadcast рассылки сообщений множеству клиентов, предлагается для самостоятельного написания в качестве домашнего задания.