Интеграция с ВКонтакте на базе Open API

Социальная сеть ВКонтакте предоставляет широкие возможности для интеграции со сторонними сайтами. В основном эти возможности представлены уже готовыми виджетами. Однако, есть еще и Open API, которое позволяет не только авторизовать пользователя, но и выполнить любой метод, доступный приложениям ВКонтакте (ВКонтакте API).

Попробуем создать небольшое приложение-игру, используя возможности Open API. Тренироваться будем на игре «Память», суть которой заключается в том, что надо очистить игровое поле, открывая по 2 одинаковых картинки. В качестве картинок будем использовать фотографии друзей игрока.

Приступаем к работе


Прежде всего необходимо создать новое приложение, указав его название и тип.



После этого переходим к настройкам приложения и указываем адрес сайта и базовый домен.



Реализация


Создадим HTML страницу, которая будет содержать несколько блоков:
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/>       
        <title>VK Memory</title>
        <script type="text/javascript" src="./js/jquery.min.js"></script>
        <script type="text/javascript" src="./js/jquery-ui.min.js"></script>
        <script type="text/javascript" src="http://vkontakte.ru/js/api/openapi.js"></script>
        <script type="text/javascript" src="./js/jquery.pnotify.min.js"></script>
        <script type="text/javascript" src="./js/jquery.blockUI.js"></script>
        <script type="text/javascript" src="./js/general.js"></script>
        <link rel="stylesheet" href="./css/styles.css" type="text/css"/>
        <link rel="stylesheet" href="./css/ui-lightness/jquery-ui-1.8.9.custom.css" type="text/css"/>
        <link rel="stylesheet" href="./css/jquery.pnotify.default.css" type="text/css"/>
    </head>
    <body>
        <div id="content">
            <div id="header">
                VK Memory
            </div>
            <div class="flash-notice" style="display: none" id="login-bar">
                Для начала игры необходимо <a href="#" id="vk-login">авторизироваться</a>
            </div>
            <div class="flash-success" style="display: none" id="logout-bar">
                Вы можете <a href="./index.html" id="new-game">начать новую игру</a>, <a href="#" id="game-scores">посмотреть таблицу рекордов</a> или <a href="#" id="vk-logout">выйти</a>.
            </div>
            <div class="flash-error" style="display: none; text-align: center" id="stats"></div>
            <div id="game-container"></div>
        </div>
        <div id="scores-dialog" style="display: none"></div>
    </body>
</html>

Назначение блоков:
  • div#login-bar — содержит ссылку для авторизации пользователя
  • div#logout-bar — содержит ссылки для начала новой игры, просмотра статистики и выхода
  • div#game-container — контейнер для игрового поля

Важным моментом является подключение JavaScript файла, содержащего методы Open API:
<script type="text/javascript" src="http://vkontakte.ru/js/api/openapi.js"></script>

Основная функциональность нашего приложения будет располагаться в файле general.js, поэтому перейдем к его рассмотрению.

Инициализация Open API


Важной частью скрипта является инициализация API, которая заключается в вызове метода VK.init, в который передается объект, содержащий минимум одно поле — apiId — ID приложения (он отображается на странице настроек приложения):
VK.init({
	apiId: 2151186
});


Авторизация


Для авторизации пользователя добавим обработчик для ссылки a#vk-login, который будет вызывать метод VK.Auth.login. Первым параметром передается callback-функция, а вторым — необходимые пользовательские настройки приложения, которые являются перечнем необходимых уровней доступа. В нашем случае необходим доступ только к списку друзей, поэтому обработчик для ссылки будет выглядеть так:
$('a#vk-login').click(function(event){
	event.preventDefault();
	VK.Auth.login(null, VK.access.FRIENDS);
});


Завершение сеанса


Для того, чтобы пользователь мог завершить сессию, добавим обработчик для ссылки a#vk-logout, который будет вызывать метод VK.Auth.logout. Первым параметром передается callback-функция, которая в нашем случае будет обновлять страницу:
$('a#vk-logout').click(function(event){
	event.preventDefault();
	VK.Auth.logout(function(){
		refreshPage(false);
	});
});


Отслеживание событий


Для того, чтобы перейти к игре (в нашем случае это заключается в обновлении страницы) после того как пользователь прошел авторизацию, необходимо подписаться на событие auth.login. Также подпишемся на событие auth.sessionChange, оно будет генерироваться, когда изменяются данные, связанные с авторизацией пользователя. Это необходимо для того, чтобы перейти к игре в том случае, если пользователь уже авторизирован ранее и только подтверждает доступ к списку друзей. Для добавления обработчика события необходимо вызвать метод VK.Observer.subscribe, который принимает два параметра — событие, на которое необходимо подписаться, и callback-функцию — обработчик события:
VK.Observer.subscribe('auth.login', function(response){
	refreshPage(true);
});
VK.Observer.subscribe('auth.sessionChange', function(response){
	refreshPage(true);
});

Метод refreshPage обновляет страницу, если пользователь не был ранее успешно авторизован:
function refreshPage(checkLogged)
{
    if((checkLogged && !isLogged) || !checkLogged)
    {
        location.href = 'index.html';
    }
}


Сбор начальных данных


Для проверки текущего статуса пользователя существует метод VK.Auth.getLoginStatus. Задействуем его, чтобы определить, авторизирован ли пользователь, и скрыть/показать необходимые из описанных выше блоков. Также, если пользователь уже авторизирован, сразу перейдем к началу игру. В метод VK.Auth.getLoginStatus передается один параметр — callback-функция, в которую, в свою очередь, передается объект, содержащий сессионные данные пользователя:
VK.Auth.getLoginStatus(function(response){
	if(response.session)
	{
		// пользователь авторизирован
	}
	else
	{
		// пользователь не авторизирован
	}
});

Для вызова любого метода ВКонтакте API служит метод VK.Api.call, который принимает три параметра — название метода, объект, представляющий собой параметры, передаваемые в указанный метод, и callback-функцию, в которую будет передан результат вызова метода. Для получения списка друзей необходимо вызвать метод friends.get. Метод не имеет обязательных параметров и в случае вызова без параметров вернет список всех друзей. Поскольку нам нужны фотографии друзей пользователя, передадим в метод параметр fields со значением 'photo_rec', что соответствует квадратной фотографии со стороной 50 пикселей:
VK.Api.call('friends.get', {
	fields: 'photo_rec'
}, function(data){
	// обработка списка друзей
});

В случае возникновения ошибки, объект data будет содержать вложенный объект error, содержащий информацию об ошибке. Ошибка с кодом 7 говорит о том, что у приложения не хватает прав, то есть в нашем случае о том, что пользователь не разрешил доступ к списку друзей.
Если вызов метода завершен успешно, то объект data будет содержать поле response, которое является массивом объектов, каждый из которых представляет информацию об одном из друзей пользователя.
После того, как список друзей получен, отбираем из него тех, у кого задана фотография (то есть не является стандартной картинкой со знаком вопроса) и переходим к инициализации игры:
VK.Api.call('friends.get', {
	fields: 'photo_rec'
}, function(data){
	if(!data.error)
	{
		if(data.response.length > 0)
		{
			data.response.shuffle();
			for(i = 0; i < data.response.length; i++)
			{
				var friend = data.response[i];
				if(friend.photo_rec && friend.photo_rec.indexOf('images/question_c.gif') == -1)
				{
					photos.push({
						photo: friend.photo_rec,
						id: friend.uid
					});
				}
			}
		}
		initGameField();
	}
	else
	{
		if(data.error.error_code == 7)
		{
			isLogged = false;
			$('div#login-bar').show();
			$('div#logout-bar').hide();
			$.unblockUI();
		}
		else
		{
			showError(data.error.error_msg);
		}
	}
});


Подготовка игрового процесса


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

В случае успешного хода вызывается метод ВКонтакте API getProfiles, который возвращает информацию об указанных пользователях. Список пользователей, для которых необходимо получить информацию, передается в качестве параметра uids. Вызов этого метода используется для отображения имени и ссылки на профиль пользователя, который был успешно отгадан.
Если очищено все поле, сохраняем текущий результат (количество шагов, за которое было очищено поле) в таблицу рекордов. Для этого служит метод setUserScore, который принимает единственный параметр — рекорд пользователя:
VK.Api.call('setUserScore', {
	score: stepsCount
}, function(data){
	if(data.error)
	{
		 showError(data.error.error_msg, false);
	}
});

Необходимо учесть, что в нашей игре лучший результат — это меньший результат, поэтому в идеале описанный метод не совсем подходит для сохранения результатов, поскольку при достижении максимально возможного количества рекордов, лучшие результаты будут вытеснены худшими.

Список чемпионов


Для получения таблицы рекордов используется метод getHighScores, который не имеет параметров. Метод возвращает массив объектов, каждый из которых содержит информацию о пользователе и его рекорд.
Чтобы пользователь мог посмотреть таблицу рекордов, добавим обработчик ссылки a#game-scores, который будет вызывать указанный выше метод, формировать таблицу результатов и показывать ее в диалоговом окне:
$('a#game-scores').click(function(event){
	event.preventDefault();
	VK.Api.call('getHighScores', {}, function(data){
		if(!data.error)
		{
			var scoresTable = '';
			if(data.response && data.response.length > 0)
			{
				for(i = 0; i < data.response.length; i++)
				{
					scoresTable = '<tr><td><a href="http://vkontakte.ru/id' + data.response[i].user_id + '" target="_blank">' + data.response[i].user_name + '</a></td><td>' + data.response[i].score + '</td></tr>' + scoresTable;
				}
				scoresTable = '<table border="0" class="scores-table" align="center"><thead><tr><td>Пользователь</td><td>Результат</td></tr></thead><tbody>' + scoresTable + '</tbody></table>';
			}
			else
			{
				scoresTable = '<div style="text-align: center">Нет результатов</div>';
			}
			$('#scores-dialog:ui-dialog').dialog('destroy');
			$('#scores-dialog')
				.html(scoresTable)
				.dialog({
					title: 'Таблица рекордов',
					modal: true,
					draggable: false,
					resizable: false,
					buttons: {
						'Закрыть': function(){
							$(this).dialog('close');
						}
					}
				});
		}
		else
		{
			showError(data.error.error_msg, false);
		}
	});
})

Как видно из кода обработчика, каждая новая строка таблицы добавляется перед предыдущей, что связано с описанной выше особенностью результатов игры.

Результат


Итоговый файл general.js можно посмотреть здесь, сама игра доступна по ссылке VK Memory.

Для создания приложения были использованы плагины для jQuery Pines Notify и BlockUI. Всю информацию об API ВКонтакте можно получить на странице для разработчиков.
Share post
AdBlock has stolen the banner, but banners are not teeth — they will be back

More
Ads

Comments 21

    0
    После нажатия на «авторизоваться», в открывшемся окне постоянный релоад окна. Опера 11.00(1156).
      0
      Если релоад происходит в открывшемся окне, то это скорее всего проблемы API. Попробуй авторизироваться на этой странице: durov.at
      +4
      Закрыт доступ к домену vkontakte.ru. Сделайте возможность логиниться на vk.com.
        0
        Кстати, это было бы хорошее правило, поскольку тот же vkontakte.ru заблочен на рабочих местах многих компаний. Так зачем же запрещать нерадивым сотрудникам тратить время на свою программу? :) Пусть работает при любых условиях :)
          +4
          Я блочу ВК по IP. На данный момент (последний раз проверял пару месяцев назад) их всего 3, выделенных им блока. При таком раскладе домен не имеет значения )
          0
          Даже если подключать API с vk.com, то сами вызовы методов API будут идти на адрес api.vkontakte.ru и эту настройку нельзя изменить.
            +1
            «Вы можете производить запросы к API ВКонтакте путем создания HTTP-запроса (POST или GET) к адресу API-сервиса api.vkontakte.ru/api.php или api.vk.com/api.php в зависимости от того, откуда запускается приложение.»
              +1
              Это уже не будет использованием Open API.
                0
                Спорить не буду, не силен я в эти апи-шмапи. Но поясните пожалуйста более развернуто, мне правда интересна эта тема.
                  0
                  Похоже это будет Web API.
                    0
                    По этой ссылке описано обычное обращение к ВКонтакте API. Open API имеет метод VK.Api.call, который выполняет всю необходимую работу по вызову ВКонтакте API — вычисляет подпись, формирует нужный запрос, но при этом в нем жестко задан адрес, по которому происходит обращение.
            +5
            «У вас недостаточное количество друзей для начала игры» и я наигрался.
            Было бы полезным для меня что бы при нехватке друзей подставлялись какие нибудь картинки.
              +3
              В итоге получилась бы обычная игра, которая не использует ВКонтакте API. Это будет стимулом заводить новых друзей. :)
                0
                Безусловно, но я бы
                s/начать новую игру, //
            • UFO just landed and posted this here
                +6
                FireBug может пройти игрульку за минимально возможные 64 хода )

                Copy Source | Copy HTML
                1. function findPair(i, j){
                2.     var id=field[i][j].id, x, y;
                3.     for(x= 0; x<8; x++)
                4.         for(y= 0; y<8; y++)
                5.             if(field[x][y]!=null)
                6.                 if(!(i==x && j==y))
                7.                     if(field[x][y].id==id){
                8.                         return [x, y];
                9.                     }
                10. }
                11.  
                12. var links=Array();
                13. $('table#game-field td a').each(function(){links.push($(this))});
                14.  
                15. timer=setInterval(function(){
                16.     if(links.length<= 0)
                17.         clearInterval(timer);
                18.  
                19.     var el = links.pop();
                20.     var image = $('img', el);
                21.  
                22.     if(image.src!="./images/cleared.jpg"){
                23.         var parentCell = el.parent('td');
                24.         var parentRow = parentCell.parent('tr');
                25.         var i = $('table#game-field tr').index(parentRow);
                26.         var j = $('td', parentRow).index(parentCell);
                27.         var pair=findPair(i, j);
                28.  
                29.         el.click();
                30.         $('table#game-field tr').slice(pair[ 0], pair[ 0]+1).find('td').slice(pair[1], pair[1]+1).find('a').click();
                31.     }
                32. }, 1000);
                  0
                  Исправьте, пожалуйста, свои циклы for в примерах. Вот это неверно:

                  for(i = 0; i < data.response.length; i++)
                  
                    0
                    Можно поточнее?
                      +4
                      Да, конечно.

                      i тут — свободная переменная, она не является локальной для вашей анонимной функции, конкретное ее значение и поведение зависит от лексического окружения коллбека.

                      Если вы не оборачивали код в какие-то дополнительные функции (не приведенные в примерах), то i свяжется с «глобальной» window.i, соответственно при изменении i в цикле будет меняться не локальная переменная коллбека i, как, вероятно, подразумевалось, а window.i.

                      У вас таких циклов 2, и оба они будут менять эту глобальную переменную. Таким образом очень просто словить непонятные ошибки, когда переменная неведомым образом неожиданно меняет свое значение. Вы мусорите в глобальном контексте (что может повлиять на работу других скриптов).

                      Особенно было бы весело вызвать внутри этого цикла другую функцию с похожим образом написанным циклом. В коде сейчас этого нет, но это все равно потенциальная ошибка. Следите за такими вещами внимательнее и не забывайте расставлять var там, где это необходимо. Если непонятно, где необходимо — почитайте где-нибудь про замыкания в js.
                    0
                    Спасибо за примеры и статью. Скажите, насколько легко преобразовать ваше приложение в iframe-приложение? (с примером, если можно)
                      0
                      Сделать это не сложно, я собирался рассказать об этом в следующих публикациях. :)

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