Для начала нам понадобится таблица 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;
Если параметр 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'; // Название таблицы // Соединяемся, выбираем базу данных$dblink = mysql_connect($db_host, $db_user, $db_pass) or die(mysql_error());$dbselected = mysql_select_db($db_name, $dblink) or die(mysql_error()); // Настройки приложения$api_id = 111;$api_secret = 'sample'; // Сообщение и настройки отсылки$message = 'test message';$threshold = 10;$n_per_page = 10000; // ***************************************************************************** // Подключаем API-класс ( * о нем будет написано ниже * )include 'vkapixml.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); $answer = $VK->api('secure.sendNotification', array('uids'=>$uids_string,'message'=>$message)); // Парсим ответ $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 ), а обнулять. Но лично я обнуляю его только в том случае, когда пользователь заходит в приложение.