PHP

Продвинутая отправка уведомлений

В этом уроке мы научимся отсылать уведомления пользователям и получать "отчеты о доставке", которые будем регистрировать в БД.

Для начала нам понадобится таблица delivery, в которой будут содержаться uids пользователей, а также параметр undelivered для каждого uid, который будет показывать, сколько из отосланных этому пользователю сообщений не было доставлено.
CREATE TABLE delivery
(uid INT UNSIGNED NOT NULL DEFAULT 0, -- uid пользователя в VK
undelivered INT UNSIGNED NOT NULL DEFAULT 0, -- сколько раз недоставлено сообщение
PRIMARY KEY(uid))
ENGINE MyISAM CHARACTER SET utf8;

Когда эта таблица будет заполнена uid пользователей, мы сможем отправлять им сообщения с фильтрацией по параметру undelivered.
Если параметр undelivered отличен от нуля - это значит, что сообщения пользователю не доставляются: какому числу равен undelivered, столько сообщений и не было доставлено. Если undelivered растет, то скорее всего пользователь уже удалил приложение или запретил присылать ему уведомления. Таким образом, нет смысла высылать уведомления пользователям с высоким показателем undelivered.

Введем еще один параметр - threshold, который будет определять пользователей для доставки уведомлений.
Если undelivered<threshold, то уведомления будут высылаться, в противном случае пользователь считается неактивным.

Затронем еще один вопрос - какое число пользователей будет запрашиваться из БД за один раз. Если оно будет мало, то это обернется большим числом запросов к БД, если слишком велико - может привести к проблемам с выделением памяти. Введем для этого еще один параметр - n_per_page (списки пользователей как бы делятся на "страницы" по n_per_page штук на каждой).

С учетом всего выше сказанного получаем следующий скрипт:
<?php
 
// Настройки БД
$db_name = 'sample'; // Название базы данных
$db_user = 'sample'; // Имя пользователя для MySQL
$db_pass = 'sample'; // ...и пароль
$db_host = 'localhost'; // с вероятностью 99% вам не нужно менять это значение
$delivery_table = 'delivery'; // Название таблицы
 
// Настройки приложения
$api_id = 111;
$api_secret = 'sample';
 
// Сообщение и настройки отсылки
$message = 'test message';
$threshold = 10;
$n_per_page = 10000;
 
 
// *****************************************************************************
 
// Подключаем API-класс ( http://vkontakte.ru/source/APIServerPHPClass.zip )
include 'vkapi.class.php';
 
$total_delivered_count = 0; // сколько всего доставлено сообщений
 
// Определяем общее кол-во получателей из базы
$dbquery = "SELECT COUNT(*) FROM $delivery_table WHERE undelivered<$threshold";
$dbresult = mysql_query($dbquery) or die(mysql_error());
if ( $dbresult ) {
$total_users = intval(mysql_result($dbresult,0));
} else {
$total_users = 0;
die();
}
 
// Делим на "страницы" (для постепенного вывода из БД)
$total_pages = ceil($total_users / $n_per_page);
 
echo "Количество получателей: $total_users ($total_pages страниц по $n_per_page получателей) <br/>";
 
// Выдергиваем получателей постранично...
for ($i = 0; $i < $total_pages; $i++) {
 
echo "Страница: ".($i+1)." из $total_pages. <br/>";
 
$dbquery = "SELECT * FROM $delivery_table WHERE undelivered<$threshold ORDER BY uid LIMIT ".($i*$n_per_page).", $n_per_page";
$dbresult = mysql_query($dbquery) or die(mysql_error());
$recipient_array = array();
 
// Получатели с текущей страницы
while ($row = mysql_fetch_assoc($dbresult)) {
$recipient_array[] = $row['uid'];
}
echo count($recipient_array).' получателей. <br/>';
 
// Отсылка n_per_page (recipient_array count) сообщений
$delivered_count = 0;
$undelivered_count = 0;
$uids_array = array_chunk($recipient_array, 100); // делим страницу на куски по 100 получателей
foreach ($uids_array as $uids_item) {
 
set_time_limit(60);
 
// Отсылка одной партии (100 получателей)
$uids_string = implode(',', $uids_item);
$VK = new vkapi($api_id, $api_secret);
$answe = $VK->api('secure.sendNotification', array('uids'=>$uids_string,'message'=>$message));
$answer = "$answe";
// Парсим ответ
$pattern = '/<response>([\d\,]*)<\/response>/i';
if ( preg_match($pattern, $answer, $matches) > 0 ) {
$delivered_string = $matches[1];
$delivered_array = explode(',', $matches[1]);
$delivered_count += count($delivered_array);
} else {
$delivered_string = '';
$delivered_array = array();
}
 
// Вычисляем недоставленные сообщения
$undelivered_array = array_diff($uids_item,$delivered_array);
$undelivered_string = implode(',', $undelivered_array);
$undelivered_count += count($undelivered_array);
 
// фиксируем недоставленные в базе
if ( count($undelivered_array) > 0 ) {
$dbquery = "UPDATE $delivery_table SET undelivered=undelivered+1 WHERE uid IN ($undelivered_string)";
$dbresult = mysql_query($dbquery) or die(mysql_error());
}
 
// фиксируем доставленные в базе
if ( count($delivered_array) > 0 ) {
$dbquery = "UPDATE $delivery_table SET undelivered=undelivered-1 WHERE undelivered>0 AND uid IN ($delivered_string)";
$dbresult = mysql_query($dbquery) or die(mysql_error());
}
 
}
 
echo "Доставлено $delivered_count сообщений. Не доставлено - $undelivered_count. <br/>";
 
$total_delivered_count += $delivered_count;
}
 
echo "Всего доставлено $total_delivered_count сообщений из $total_users.";
 
 
?>

ВАЖНО!
Скрипт использует файл vkapixml.class.php - это обычный vkapi.class.php ( http://vkontakte.ru/source/APIServerPHPClass.zip ), переделанный так, чтобы выдавать ответы в XML, а не в объекте JSON.
Вот мой вариант (vkapixml.class.php):
<?php
 
/**
* VKAPI class for vk.com social network
*
* @package server API methods
* @link http://vk.com/developers.php
* @autor Oleg Illarionov
* @version 1.0
*/
 
class vkapi {
var $api_secret;
var $app_id;
var $api_url;
 
function vkapi($app_id, $api_secret, $api_url = 'api.vk.com/api.php') {
$this->app_id = $app_id;
$this->api_secret = $api_secret;
if (!strstr($api_url, 'http://')) $api_url = 'http://'.$api_url;
$this->api_url = $api_url;
}
 
function api($method,$params=false) {
if (!$params) $params = array();
$params['api_id'] = $this->app_id;
$params['v'] = '3.0';
$params['method'] = $method;
$params['timestamp'] = time();
$params['format'] = 'xml';
// $params['format'] = 'json';
$params['random'] = rand(0,10000);
ksort($params);
$sig = '';
foreach($params as $k=>$v) {
$sig .= $k.'='.$v;
}
$sig .= $this->api_secret;
$params['sig'] = md5($sig);
$query = $this->api_url.'?'.$this->params($params);
$res = file_get_contents($query);
return $res;
// return json_decode($res, true);
}
 
function params($params) {
$pice = array();
foreach($params as $k=>$v) {
$pice[] = $k.'='.urlencode($v);
}
return implode('&',$pice);
}
}
?>

Хинт для продвинутых
Чтобы обновлять данные о параметре undelivered, можно не уменьшать его на единицу при успешной доставке (строки 96-100 ), а обнулять. Но лично я обнуляю его только в том случае, когда пользователь заходит в приложение.
Добавлено: 16 Августа 2013 09:59:40 Добавил: Андрей Ковальчук Нравится 0
Добавить
Комментарии:
Нету комментариев для вывода...