Сервис push-уведомлений Pushover для Android и iOS в связке с PHP


    Вкратце, push-уведомления — это небольшие по объему важные сообщения от программы или сервиса, отображаемые операционной системой тогда, когда вы непосредственно не работаете с указанным приложением или сервисом. Преимущество таких уведомлений в отсутствии необходимости держать программу вечно в памяти, тратя на нее процессорные мощности и память.
    Не буду здесь расписывать всю технологию доставки удаленного уведомления, ибо это уже сделано до меня. Выглядит примерно так: периодически демон опрашивает сервер и в случае появления сообщения, показывает его нам.
    Для iOS придумали APNS, для Android-а — C2DM-GCM, я же хочу рассказать про кроссплатформенный (громко) сервис Pushover и связке его с php-сайтом.

    Пример задачи


    Предположим, что раз в день мы хотим знать что-либо о количестве заказов на сайте за день и их стоимости.
    Сайт крутится на некоторой CMS на PHP и mySQL, принимающая сторона имеет стильные iPhone и Android-телефоны.
    Срочность доставки сообщения не относится к жизненно-важным показателям.
    Надо найти условно безгеморройное решение.

    Pushover


    Pushover — это скромный сервис уведомлений, а также приложения для iOS и Android, планируются поделки и для BlackBerry и OS X Mountain Lion. Сервис имеет свой API, позволяет отправлять бесплатно до 7.5 тысяч сообщений в месяц.


    Сообщение, помимо основного текста сообщения длиной 512 символов, может содержать крупный заголовок, URL (тогда длина сообщения увеличивается до 500) и его тайтл (все отображается отдельными сформированными блоками, потому такое разграничение). Сообщение можно доставить под неким выбранным указанным приоритетом. Пользователь может указать «тихие» часы, когда его не стоить будить уведомления, а также подключать и отключать устройства, на которые будут приходить уведомления.
    Уведомление может быть доставлено пользователям, предоставившим свой код, всем устройствам этого пользователя или по выбору. Для приема сообщения пользователю необходимо быть зарегистрированным в сервисе и обладать хотя бы одним рабочим устройством.

    Добавление пользователя


    После прохождения регистрации, каждый пользователь попадает в свой кабинет, где он сразу видит свой хэш-токен. Это уникальный идентификатор пользователя, на который в последствии и отправляются уведомления.
    Пользователю, желающему принимать сообщения, необходимо поставить на свой телефон/планшет/абы что приложение из соответствующего магазина.

    Добавление сервиса

    Добавление сервиса ничуть не сложнее. Из личного кабинета надо перейти на страницу создания приложения, где предлагается описать продукт:

    После заполнения соответствующих полей, нам становится известен токен приложения. В принципе, это все, что необходимо для отправки сообщения.
    На странице приложения в последующем будет красивый график успешно отправленных сообщений.

    Связывание приложений и получателей

    … не выполняется никак. Любое приложение может отправить любому пользователю уведомление, если знает его токен. Прием токенов от населения остается на совести приложения. Также как и отписка от рассылок.

    API

    Небольшое, емкое и понятное. Для отправки сообщения в POST-запросе к api.pushover.net/1/messages.json или api.pushover.net/1/messages.xml минимально необходимо указать:
    • token — хэш-токен вашего приложения или сервиса.
    • user — хэш-токен пользователя, которому вы отправляете уведомление.
    • message — текстовая часть сообщения.

    Дополнительно к этому можно добавить:
    • device — идентификатор устройства пользователя, дабы не отсылать уведомления сразу на все его устройства
    • title — заголовок сообщения, если не указан, будет показано название сервиса
    • url — ссылка на web-страницу, если в этом есть необходимость
    • url_title — заголовок к ссылке
    • priority — приоритет сообщения, ставится в 1 для высокого приоритета, обходящего все «тихие» часы и -1 для тихого уведомления.
    • timestamp — UNIX метка времени, когда это уведомлениебыло создано. Расписания доставки сообщений сервисом не предусмотрено.


    Ответ сервера

    Ответ сервера будет представлен в json или xml формате (в зависимости от расширения вызываемого скрипта).
    Если все прошло удачно будет отдан объект содержанием поля status, равном 1.
    Иначе, поле status будет содержать нечто иное, а поле errors — массив описания ошибок. Вот примеры ответов удачной и неудачной отправок в формате XML:
    <?xml version="1.0" encoding="UTF-8"?>
    <hash>
      <status type="integer">1</status>
    </hash>
    
    и
    <?xml version="1.0" encoding="UTF-8"?>
    <hash>
      <token>invalid</token>
      <errors type="array">
        <error>application token is invalid</error>
      </errors>
      <status type="integer">0</status>
    </hash>
    


    PHP


    На главной странице и в факе в разделах «смотрите, как легко!» приводятся коды простейшего скрипта на различных языках для отправки и есть ссылка на 3rd-party php-класс от Chris Schalenborgh.
    Везде используется сURL, что впрочем, понятно.

    Зафигарим свой класс

    Куда ж нынче без велосипедов?
    На самом деле, мне не слишком понравилось, что успех отправки уведомления определяется либо как true/false, либо выводится сразу вся простыня ответа сервера. Да вообще обработки ошибок нет. Считаю, что если посетителю сайта не обязательно, то разработчику надо знать, почему не отправлено то или иное сообщение.
    В общем, существенно меняем все, классы уехали на GitHub.
    class PushoverException
    class PushoverException extends Exception
    {
    	/**
    	 * Messages array
    	 * @var array
    	 */
    	private $fMessages;
    
    	/**
    	 * Exception constructor
    	 * @param array $aMessages An array of messages
    	 */
    	public function __construct(array $aMessages)
    	{
    		parent::__construct('PushoverException exception');
    		$this->fMessages = $aMessages;
    	}
    
    	/**
    	 * Get messages array
    	 * @return array
    	 */
    	public function getMessages()
    	{
    		return empty($this->fMessages) ? array() : $this->fMessages;
    	}
    }
    

    class Pushover
    class Pushover
    {
    	/*
    	 * Pushover json api service url
    	 */
    	const C_API_URL = 'https://api.pushover.net/1/messages.json';
    
    	/**
    	 * Properties storage array
    	 * @var array
    	 */
    	private $fProperties;
    
    	/**
    	 * cURL instance
    	 */
    	private $fCurl;
    
    	//--------------------------------------------------------------------------//
    
    	/**
    	 * Properties getter
    	 * @param string $aPropertyName Property name
    	 * @return mixed
    	 */
    	public function __get($aPropertyName)
    	{
    		if(array_key_exists($aPropertyName, $this->fProperties))
    			return $this->fProperties[$aPropertyName];
    		return null;
    	}
    
    	/**
    	 * Properties setter
    	 * @param string $aPropertyName Property name
    	 * @param mixed $aValue Property value
    	 */
    	public function __set($aPropertyName, $aValue)
    	{
    		$this->fProperties[$aPropertyName] = $aValue;
    	}
    
    	//--------------------------------------------------------------------------//
    
    	/**
    	 * Class constructor
    	 * @param string $aApplicationToken Application token
    	 */
    	public function __construct($aApplicationToken = null)
    	{
    		if(!empty($aApplicationToken))
    			$this->applicationToken = $aApplicationToken;
    
    		$this->fCurl = curl_init();
    		curl_setopt($this->fCurl, CURLOPT_URL,            self::C_API_URL);
    		curl_setopt($this->fCurl, CURLOPT_HEADER,         false);
    		curl_setopt($this->fCurl, CURLOPT_RETURNTRANSFER, true);
    		curl_setopt($this->fCurl, CURLOPT_SSL_VERIFYPEER, false);		
    	}
    
    	/**
    	 * Class destructor
    	 */
    	public function __destruct()
    	{
    		curl_close($this->fCurl);
    	}
    
    	//--------------------------------------------------------------------------//
    
    	/**
    	 * Throws an exceprion with single message
    	 * @param mixed $aMessage
    	 * @throws PushoverException
    	 */
    	public function throwMessage($aMessage)
    	{
    		throw new PushoverException(array($aMessage));
    	}
    
    	/**
    	 * Throws an exceprion with an array of messages
    	 * @param array $aMessages
    	 * @throws PushoverException
    	 */
    	public function throwMessages(array $aMessages)
    	{
    		throw new PushoverException($aMessages);
    	}
    
    	//--------------------------------------------------------------------------//
    
    	/**
    	 * Send pushover notification
    	 */
    	public function send()
    	{
    		if(!strlen($this->applicationToken))
    			$this->throwMessage('Application token is empty');
    		if(!strlen($this->userToken))
    			$this->throwMessage('User token is empty');
    		if(!strlen($this->notificationMessage))
    			$this->throwMessage('Notification message is empty');
    
    		if(intval($this->notificationTimestamp) <= 0)
    			$this->notificationTimestamp = time();
    
    		$lSendParams = array(
    			'token'     => $this->applicationToken,
    			'user'      => $this->userToken,
    			'device'    => $this->userDevice,
    			'title'     => $this->notificationTitle,
    			'message'   => $this->notificationMessage,
    			'priority'  => $this->notificationPriority,
    			'timestamp' => $this->notificationTimestamp,
    			'url'       => $this->notificationUrl,
    			'url_title' => $this->notificationUrlTitle
    		);
    
    		foreach($lSendParams as $lKey => $lParam)
    			if(empty($lParam))
    				unset($lSendParams[$lKey]);
    
    		curl_setopt($this->fCurl, CURLOPT_POSTFIELDS, $lSendParams);
    		$lResponseJson = curl_exec($this->fCurl);
    
    		if($lResponseJson === false)
    			$this->throwMessage('API request error');
    
    		$lResponse = json_decode($lResponseJson, true);
    
    		if(empty($lResponse) || !is_array($lResponse))
    			$this->throwMessage('Bad API response');
    
    		if(!empty($lResponse['errors']))
    			$this->throwMessages($lResponse['errors']);
    		if(empty($lResponse['status']) || intval($lResponse['status']) != 1)
    			$this->throwMessage('Unknown notification send error');
    		
    	}
    }
    



    Минимальное сообщение теперь довольно просто отправить, ошибки можно разбирать:
    $lPushover = new Pushover('Токен приложения');
    $lPushover->userToken = 'Токен пользователя';
    $lPushover->notificationMessage = 'Текст сообщения';
    
    try
    {
    	$lPushover->send();
    	echo '<font color="green">Message sent</font>', PHP_EOL;
    }
    catch (PushoverException $aException)
    {
    	echo '<font color="red">Error sending messages</font><br>', PHP_EOL;
    	echo '<ul>', PHP_EOL;
    	foreach($aException->getMessages() as $lMessage)
    		echo '<li>', $lMessage, '</li>', PHP_EOL;
    	echo '</ul>', PHP_EOL;
    }
    

    Пользователь уже принял сообщение.

    Итого


    Знаем об удобном сервисе удаленных уведомлений, одинаково успешно передающий сообщения пользователям Android и iOS.
    Имеем рабочий механизм отправки уведомлений с сайта на PHP.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 26

      0
      Спасибо. Полезно. Чуть позже попробую прикрутить к сайту
        0
        Зря они приложения для платформ сделали платными :(
          0
          Да, 130 рублей — многовато, конечно. Но тут по принципу — хочешь, чтобы тебе приходили сообщения с разных сайтов — заплати денюжку.
            0
            Принцип-то ясен, но всё же — многовато. Было бы очень прикольно, если бы они открыли API для создания своих приложений. Или сделали бы либы для платформ. И продавали бы эти либы по доллару за лицензию.
              0
              Мне кажется, если поснифать траффик, можно вполне восстановить их API
              0
              Ну можно поискать и бесплатные СМС нотификации для себя, если хорошо поискать ) И даже без костылей, типа Google Calendar
                0
                Там вроде ограничение в день? только вот неизвестно сколько — вроде читал о 20 где то.
                  0
                  sms.ru (не реклама, я там не работаю), дает 60 сообщений на собственный номер в день бесплатно. Работает очень просто через API. Пользуюсь для всяких уведомлялок с сервера, очень удобно и костылей никаких не надо )
            0
            Более года назад рассматривал Prowl для iOS уведомлений от мониторинга, App тоже платное. Но без поддержки Android, так и не решил использовать. Спасибо за наводку. 3.99 USD у меня отобьются очень быстро, после первых 200 уведомлений вместо SMS. А если учесть размер уведомлений, который часто больше 160 символов и того раньше.
              0
              Учтите, что бесплатный месячный лимит — 7500 сообщений. Дальше сервис будет отвечать 409 кодом, если Вы не заплатите им.
              У меня информации о их ценовой политике нет.
                0
                7500 на каждое приложение. В данном случае я влажу в лимит. Если нужно будет немного больше, проблем разделить по пользователем не составит труда. А там возможно появится официальный прайс, который выйдет дешевле SMS.
                От SMS целиком отказываться не буду. Для критических уведомлений точно оставлю.
                  0
                  Точняк, еще можно сделать резервное второе приложение =)
                0
                Urban Airship дает миллион сообщений в месяц, для Basic Plan-a бесплатно. Дальше уже за деньги, но приемлимые деньги.
                  0
                  Urban Airship совсем другой размах. У меня меньше 10 пользователей, так что самый минимальный план уже перебор. А вот Basic план интерестно посмотреть.
                0
                Занятно, спасибо, для меня это как альтернатива sms нотификации. Попробую :)
                  0
                  Здорово, но не хватает одной фишки — возможности сегментированной рассылки по группам пользователей. При регистрации идентификатора устройства в системе — передавать следом его метрики — часовой пояс, регион, язык. И далее уже подстраивать отправляемую информацию с учетом этих метрик.
                    0
                    Сервис занимается только рассылкой сообщений. Когда Ваше приложение будет брать у пользователя его токен, оно точто также может и записать его метрики. Дальше — простейшая очередь сообщений.
                    Конечно, хотелось бы развития функционала сервера и на такие мелочи.
                      0
                      Ок. Приложение записало метрики. Я вижу подписанное на push устройство. Как мне узнать из админки какие там у него метрики? Кто будет фильтровать сообщения? Логично чтобы это происходило на стороне сервиса, а возможности такой нету.
                        0
                        Сервис занимается доставкой сообщений. Как Ваше приложение возьмет из Вашей базы данные пользователя, решает не сервис. Тут такое развитие событий.
                          0
                          Я понял. Значит это только часть готового решения. Или «отправлялка с api», другими словами.
                            0
                            Это смотря какие требования к сервису Вы ставите. В большинстве случаев, думаю, это решение вполне пойдет за целое и всеобъемлющее.
                        0
                        Я раньше пользовался отправкой сообщений от Urban Airship, но отсутствие таргетинга у них меня сдерживало — массовой рассылкой заниматься не хотелось. Сейчас глянул их сайт — дописали они таргетинг и даже разжевали как оно работает. Может у них понятней получилось.
                      0
                      Совсем недавно думал о подобном сервисе, только вот идея была больше похожа на RSS.
                      То есть тыкаем на сайтах подписаться, сайт по API получает токен, а юзер на каждом девайсе может выбрать о чем его уведомлять, когда и т.д. можно также отсеивать что то по фильтрам, допонительно можно было бы кнопки делать разные (в андроиде ведь есть поддержка кнопок в уведомлениях)
                      Правда как я понимаю тут почти тоже самое. Только судя по комментариям это все платно. да и я думал не только о мобилках, можно еще к этому и Email прикрутить — получать например раз в час все оповещения от сервисов, или даже если кто найдется, можно и SMS отправлять. Да даже в браузере сделать плагином.
                        0
                        Но зачем использовать платный сервис, если GCM и APNS бесплатны? Зачем платить за обёртку, если есть готовые либы, а обёртку можно сделать самому?
                          0
                          Чтобы не делать двойную работу. А разработчик ничего и не платит.
                            0
                            Там работы-то на десять минут.

                        Only users with full accounts can post comments. Log in, please.