Аутентификация через ВКонтакте

С каждым днём влияние социальных сетей и сервисов только крепчает. Это означает, что нам, как веб разработчикам нужно это учитывать. Сегодня я расскажу и покажу, как создать аутентификацию ваших пользователей через социальную сеть ВКонтакте. Для этого мы не будем пользоваться какими-то сторонними библиотеками, а реализуем всё с нуля, собственными руками. Думаю, многие ждали подобного урока, так что томить не буду. Начнём!

Заметка. Пример, созданный в данном уроке, предназначен для работы на локальном сервере.

Шаг 1. Регистрация нового приложения
Для начала нам необходимо создать новое приложение на сайте социальной сети ВКонтакте

В открывшейся форме введите название приложения; выберите тип “Веб-сайт”; В качестве адреса сайта введите путь к папке с проектом на вашем локальном сервере. В моём случае, это http://localhost/vk-auth. Базовый домен: localhost.

После нажатия на кнопку “Подключить сайт”, вам наверняка придётся ввести проверочный код, который придёт по смс. Если вы пройдёте проверку, то вам должна открыться следующая форма с настройками приложения.

Сразу же хочу предупредить, что настоящие данные, относящиеся к моему приложению, я заменил на фиктивные, т.к. публикация таких значений как “Защищённый ключ” карается удалением вашего приложения или учётной записи в целом.

Из данной формы нам понадобятся такие данные, как `ID приложения`, `Защищённый ключ`, `Адрес сайта`. Запишем их в специальные переменные в файле index.php:

<?php
 
$client_id = '3485070'; // ID приложения
$client_secret = 'lYjfUZwZmlJJlFIqQFAj'; // Защищённый ключ
$redirect_uri = 'http://localhost/vk-auth'; // Адрес сайта
Шаг 2. Генерация ссылки для аутентификации
Для генерации ссылки нам потребуется адрес аутентификации и специальные параметры:

$url = 'http://oauth.vk.com/authorize';
 
$params = array(
    'client_id'     => $client_id,
    'redirect_uri'  => $redirect_uri,
    'response_type' => 'code'
);

С помощью функции http_build_query, передав туда массив параметров, получим чередование ключей и значений, как в url адресе. Итак, генерируем ссылку и выводим на экран:
echo $link = '<p><a href="' . $url . '?' . urldecode(http_build_query($params)) . '">Аутентификация через ВКонтакте</a></p>';

Также тут я воспользовался функцией urldecode. Если этого не сделать, то в сгенерированной ссылке могут появиться закодированные символы слешей, знаков двоеточия и так далее. Что-то вроде этого:
http://oauth.vk.com/authorize?client_id=3485070&redirect_uri=http%3A%2F%2Flocalhost%2Fvk-auth&response_type=code

Если же мы пропустим данную строку через функцию urldecode, то получим:
http://oauth.vk.com/authorize?client_id=3485070&redirect_uri=http://localhost/vk-auth&response_type=code

Итак, ссылка для аутентификации у нас готова. Если мы сформировали все параметры правильным образом и получили верный url, то пройдя по ссылке, будем перенаправлены по адресу, указанному в настройках приложения ('http://localhost/vk-auth'). Только теперь к этому адресу будет прикреплён специальный параметр code:
// Пример. В вашем случае код будет другой
http://localhost/vk-auth/?code=f30621b146115b3bad

Шаг 3. Получение токена
Начинать процедуру аутентификации мы можем тогда и только тогда, когда к нам пришёл параметр code. Он нам нужен для того, чтобы получить специальный токен доступа, с помощью которого, в дальнейшем, мы достанем информацию о пользователе.

В первую очередь, снова сформируем нужные нам параметры для этого запроса:
if (isset($_GET['code'])) {
    $params = array(
        'client_id' => $>clientId,
        'client_secret' => $this->clientSecret,
        'code' => $_GET['code'],
        'redirect_uri' => $this->redirectUri
    );
}

Далее нам нужно отправить GET запрос на адрес https://oauth.vk.com/access_token, передав перечисленные параметры. В PHP выполнить GET запрос по какому-то адресу можно несколькими способами. Для данного урока я воспользуюсь функцией file_get_contents.
if (isset($_GET['code'])) {
    $params = array(
        'client_id' => $client_id,
        'client_secret' => $client_secret,
        'code' => $_GET['code'],
        'redirect_uri' => $redirect_uri
    );
 
    $token = json_decode(file_get_contents('https://oauth.vk.com/access_token' . '?' . urldecode(http_build_query($params))), true);
}

В результате, при успешном выполнении запроса в переменную $token будет записан ответ от ВКонтакте в JSON формате. Данная строка содержит 3 параметра: access_token, который мы будем использовать в следующих запросах для извлечения информации о пользователе, expires_in - время жизни токена, user_id - id пользователя, который прошёл аутентификацию.
{"access_token":"2c6276b767b5e2f35f908e89d61416beea17b6d1ebcd3d14e20ac910281d306bb506ec78e75518ed614e9","expires_in":86399,"user_id":14966712}

Для того чтобы мы далее могли работать с данными параметрами, декодируем JSON строку с помощью функции json_decode и помещаем данные в массив, передав в качестве второго аргумента true.

Шаг 4. Получение информации о пользователе
Итак, теперь когда у нас есть параметры access_token и user_id, мы можем сделать запрос к ВКонтакте API и получить информацию о пользователе. Для начала снова подготовим массив с параметрами, которые в последствии превратим в фрагмент url строки.
if (isset($_GET['code'])) {
    $params = array(
        'client_id' => $client_id,
        'client_secret' => $client_secret,
        'code' => $_GET['code'],
        'redirect_uri' => $redirect_uri
    );
 
    $token = json_decode(file_get_contents('https://oauth.vk.com/access_token' . '?' . urldecode(http_build_query($params))), true);
 
    if (isset($token['access_token'])) {
        $params = array(
            'uids'         => $token['user_id'],
            'fields'       => 'uid,first_name,last_name,screen_name,sex,bdate,photo_big',
            'access_token' => $token['access_token']
        );
    }
}

В параметр uids записываем id пользователя; в fields перечисляем через запятую поля, которые хотим извлечь (uid - id пользователя, first_name - имя, last_name - фамилию, screen_name - имя отображаемое на страницах VK, sex - пол, bdate - дату рождения, photo_big - фотографию). Для доступа к большему количеству полей обратитесь к ВКонтакте API users.get. В качестве последнего параметра передаём 'access_token'.

Для получения информации о пользователе сфомированные параметры нам нужно отправить GET запросом по адресу https://api.vk.com/method/users.get.
if (isset($_GET['code'])) {
    $params = array(
        'client_id' => $client_id,
        'client_secret' => $client_secret,
        'code' => $_GET['code'],
        'redirect_uri' => $redirect_uri
    );
 
    $token = json_decode(file_get_contents('https://oauth.vk.com/access_token' . '?' . urldecode(http_build_query($params))), true);
 
    if (isset($token['access_token'])) {
        $params = array(
            'uids'         => $token['user_id'],
            'fields'       => 'uid,first_name,last_name,screen_name,sex,bdate,photo_big',
            'access_token' => $token['access_token']
        );
 
        $userInfo = json_decode(file_get_contents('https://api.vk.com/method/users.get' . '?' . urldecode(http_build_query($params))), true);
    }
}

В результате, если всё было сделано правильно, то получим JSON ответ следующего вида:
{"response":[{"uid":14966712,"first_name":"Стас","last_name":"Протасевич","screen_name":"stanislav.protasevich","sex":2,"bdate":"3.7.1988","photo_big":"http:\/\/cs307601.vk.me\/u14966712\/a_8234a279.jpg"}]}

Снова преобразуем JSON ответ в массив и обратимся к нулевому элементу, хранящемуся в массиве, доступному по ключу response:
if (isset($_GET['code'])) {
    $result = false;
    $params = array(
        'client_id' => $client_id,
        'client_secret' => $client_secret,
        'code' => $_GET['code'],
        'redirect_uri' => $redirect_uri
    );
 
    $token = json_decode(file_get_contents('https://oauth.vk.com/access_token' . '?' . urldecode(http_build_query($params))), true);
 
    if (isset($token['access_token'])) {
        $params = array(
            'uids'         => $token['user_id'],
            'fields'       => 'uid,first_name,last_name,screen_name,sex,bdate,photo_big',
            'access_token' => $token['access_token']
        );
 
        $userInfo = json_decode(file_get_contents('https://api.vk.com/method/users.get' . '?' . urldecode(http_build_query($params))), true);
        if (isset($userInfo['response'][0]['uid'])) {
            $userInfo = $userInfo['response'][0];
            $result = true;
        }
    }
}

Прошу обратить внимание, что в данном фрагменте, я добавил специальную переменную $result, равную изначально false сразу же после проверки наличия GET параметра code. Если нам удалось извлечь информацию о пользователе, то мы меняем значение этой переменной на true.

Полный код:
<!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="ru">
<head>
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8"/>
    <title></title>
</head>
<body>
    <?php
 
    $client_id = '3485070'; // ID приложения
    $client_secret = 'lYjfUZwZmlJJlFIqQFAj'; // Защищённый ключ
    $redirect_uri = 'http://localhost/vk-auth'; // Адрес сайта
 
    $url = 'http://oauth.vk.com/authorize';
 
    $params = array(
        'client_id'     => $client_id,
        'redirect_uri'  => $redirect_uri,
        'response_type' => 'code'
    );
 
    echo $link = '<p><a href="' . $url . '?' . urldecode(http_build_query($params)) . '">Аутентификация через ВКонтакте</a></p>';
 
if (isset($_GET['code'])) {
    $result = false;
    $params = array(
        'client_id' => $client_id,
        'client_secret' => $client_secret,
        'code' => $_GET['code'],
        'redirect_uri' => $redirect_uri
    );
 
    $token = json_decode(file_get_contents('https://oauth.vk.com/access_token' . '?' . urldecode(http_build_query($params))), true);
 
    if (isset($token['access_token'])) {
        $params = array(
            'uids'         => $token['user_id'],
            'fields'       => 'uid,first_name,last_name,screen_name,sex,bdate,photo_big',
            'access_token' => $token['access_token']
        );
 
        $userInfo = json_decode(file_get_contents('https://api.vk.com/method/users.get' . '?' . urldecode(http_build_query($params))), true);
        if (isset($userInfo['response'][0]['uid'])) {
            $userInfo = $userInfo['response'][0];
            $result = true;
        }
    }
 
    if ($result) {
        echo "Социальный ID пользователя: " . $userInfo['uid'] . '<br />';
        echo "Имя пользователя: " . $userInfo['first_name'] . '<br />';
        echo "Ссылка на профиль пользователя: " . $userInfo['screen_name'] . '<br />';
        echo "Пол пользователя: " . $userInfo['sex'] . '<br />';
        echo "День Рождения: " . $userInfo['bdate'] . '<br />';
        echo '<img src="' . $userInfo['photo_big'] . '" />'; echo "<br />";
    }
}
?>
</body>
</html>

Шаг 5. Извлечение информации о пользователе
Теперь извлекать информацию о пользователе мы можем из массива, хранящегося в переменной $userInfo по ключам uid, first_name, last_name, screen_name, sex, bdate, photo_big.
if ($result) {
    echo "Социальный ID пользователя: " . $userInfo['uid'] . '<br />';
    echo "Имя пользователя: " . $userInfo['first_name'] . '<br />';
    echo "Ссылка на профиль пользователя: ". 'http://vk.com/' . $userInfo['screen_name'] . '<br />';
    echo "Пол пользователя: " . $userInfo['sex'] . '<br />';
    echo "День Рождения: " . $userInfo['bdate'] . '<br />';
    echo '<img src="' . $userInfo['photo_big'] . '" />'; echo "<br />";
}

Шаг 6. Дело за вами
Теперь, когда у нас есть такая информация, как ID пользователя, в первую очередь, нам необходимо проверить его наличие в нашей базе данных. Если пользователя с таким ID не существует, то значит он авторизовался с нашего сайта впервые, и мы внесём его в базу. Если пользователь уже есть, можем проверить не изменились ли какие-то данные о нём, например, имя или ещё что-то. Если да, обновим запись.

После этого, всё что нам осталось сделать, так это создать сессию и поместить в неё информацию о нашем пользователе.
$_SESSION['user'] = $userInfo;

На странице выхода из системы просто удаляем сессию с помощью функции unset.

Итог
Вот мы и подошли к концу данного урока, и теперь в ваших руках есть решение, которого вы, возможно, долго ждали. Если вас интересует тема аутентификации через социальные сети, то пишите об этом в комментариях, и я с радостью расскажу и покажу, как работать с Одноклассниками, Google-ом, Facebook-ом и другими сервисами.

Добавлено: 02 Мая 2018 14:28:25 Добавил: Андрей Ковальчук

Система диалогов на php, как в контакте

В этой статье я расскажу про систему диалогов на php, которую я создал на одном из сервисов компании, где я работаю.

Изначально, хотелось получить систему, которая бы повторяла функционал диалоговой системы контакта. Требования были следующие: скрипт не должен требовать установки на сервер дополнительных средств (поддержка long-pool запросов, phpdemon, поддержки websocket и т.п), позволять создавать диалог неограниченного числа пользователей и работать на обычном ajax-post сообщении с сайтом.

Приступим

Для начала создадим структуру БД

Вот дамп

Когда один пользователь хочет написать сообщение другому, скрипт ищет подходящий диалог. Диалог, является подходящим, если в нем участвую те же лица. Т.е. если мы раньше писали этому человеку, и никого больше не подключали к этому диалогу, то он подходит. Если же подходящего диалога нет, то создаем новый. Диалог и пользователь связаны таблицей user_to_dialog. Когда пользователь посылает сообщение, оно записывается в табличку message. А информация о том, кому оно предназначено лежит в табличке message_to_user. По сути, эта таблица избыточна, так как у нас уже есть информация кому показывать сообщение исходя из данных user_to_dialog. Но мне было так удобно. Можете это изменить в своем форке.

Перейдем к коду.

Структура класса dialog

<?php
class dialog{
  public $utime = 0; // время по часовому поясу пользователя в UNIXTIME
  public $userid = 0;// id пользователя
  public $err = '';
  public $hash = ''; // hash диалога
  public $id = '';
  private $user_id_field = 'IDClient'; // название поля с id пользователя, необходимо для подключения
  //к Вашему скрипту с уже созданной структурой пользователей
  private function create(){} // создание нового диалога
  public function find_suit_dialog($userlist = array()){} // поиск подходящего диалога
  function get_new_messages_cnt(){} // количество новых сообщений для пользователя
  function get_users_from_dialog(){} // список пользователей принадлежащих диалогу
  function get_user_dialogs( $start=0,$cnt = 10 ){} // вывод диалогов пользователя
  function get_messages_from_dialog($new=false,$reset_status = true){} // вывод сообщений из диалога
  function remove_users_from_dialog( $userlist = array() ){}
  function add_users_to_dialog( $userlist = array() ){} // добавить пользователя в диалог
  function send($msg,$intro = false){} // посылка сообщения в диалог
  function send_many_users( $msg,$userlist,$intro = false ){} // посылка сообщения нескольким пользователям
  public function delete_message( $messageid ){} // удаление сообщения
}

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

Полная реализация всех методов

Как использовать

Создаем экземпляр класса dialog
<?php
$dialog = new dialog($db,time(),$userid,isset($_REQUEST['hash'])?$_REQUEST['hash']:'');

где $db инициализированный и подключенный экземпляр класса db, а $userid это id текущего пользователя.

Получить все диалоги пользователя

$dialogs = $xddialog->get_user_dialogs();
$out = '';
foreach($dialogs as $dg)
  $out.='
  <div class="dialog '.(!$dg['msg_status']?'newmsg':'').'">
    <div class="float_left">
      <span class="nikname"><a href="#" id="user_'.$dg['senderid'].'">'.$dg['sender_name'].'</a></span>
      <span class="message_time">'.date('H:i:s d/m/Y',$dg['public']).'</span>
      <div>'.$dg['message'].'</div>
    </div>
    <div class="float_right">
      <input  class="btn gotodialog" id="dialog_'.$dg['hash'].'" value="ПЕРЕЙТИ К ДИАЛОГУ"/>
    </div>
    <div class="clearex"></div>
  </div>';

получить все сообщения из текущего дилога
$cnt = $xddialog->get_new_messages_cnt();
$messages = (!$cnt)?array():$xddialog->get_messages_from_dialog();
$out = '';
foreach($messages as $msg)
  $out.='
  <div>
    <div class="float_left">
      <span class="nikname"><a href="#" id="user_'.$msg['senderid'].'">'.$msg['sender_name'].'</a></span>
      <span class="message_time">'.date('H:i:s d/m/Y',$msg['public']).'</span>
      <div>'.$msg['message'].'</div>
    </div>
    <div class="clearex"></div>
  </div>';

получить только новые сообщения
$cnt = $xddialog->get_new_messages_cnt();
$messages = (!$cnt)?array():$xddialog->get_messages_from_dialog(true);

Отправка сообщения в диалог
$xddialog->send($_POST['message']);

Поиск подходящего диалога и добавление туда всех пользователей
$xddialog->find_suit_dialog(array($userid1,$userid2,$userid3,));
$xddialog->add_users_to_dialog(array($userid,$userid1,$userid2,$userid3,));

где $userid это id текущего пользователя, а $userid,$userid1,$userid2,$userid3, id пользователей с которыми будет вестись диалог

Добавлено: 03 Апреля 2018 22:22:45 Добавил: Андрей Ковальчук

Алгоритм работы с VK.COM из вашего приложения



Для любого серьёзного сайта с серьёзной посещаемостью разрабатывают API-функции. Сайт, с посещаемостью, которая оценивается миллионами человек, конечно не будет в стороне. Поговорим о работе с сайтом vk.com - социальной сети, объединившей людей как с России и СНГ, так и с зарубежья.


Теперь, что же такое API - функция? Это обычная функция (как в pascal или Делфи), которая что-то вернёт (на то она и функция). Например, собираемся мы узнать имя человека, а знаем только его ID. Что мы будем делать? Ну лично я бы полез в исходный код, спарсил бы оттуда значения, если они открыты. А если скрыты? Тогда надо думать и искать пути, как же нам узнать имя человека. Вот что бы такого не было, и существуют функции, которые может использовать разработчик для комфортной работы с сайтом. Вместо 20-ти строчек кода мы можем выполнить 1 запрос с функцией, получив при этом в ответе все необходимые параметры.

Чем это (использование API) выгодно сайту? Распространением приложений для сайта, соответственно и увеличением аудитории, да и просто комфортной работой пользователей с сайтом (различные аудиопрееры, аудиосейверы, мессендж-клиенты). Чем это выгодно разработчику приложения? Зависит как от приложения, так и от разработчика. Можно зарабатывать как на приложении (голоса), так и на рекламе, данной на сайте этого приложения.

Пример общего алгоритма работы с сайтом я буду рассматривать на языке программирования Делфи. Сам алгоритм, в последующем, вы можете использовать в любом языке и любом приложении, так как я рассмотрю все (исключая платёжные и рекламые) формы запросов для различных API.

Для начала скажу, что, так как мы делаем всё с помощью API, я подразумеваю, что вы зарегистрировали заранее своё приложения.

Все методы (говори API-функции) для работы с vk представлены на данной странице - vk.com/dev/main. Разделить их можно на 2 типа - открытые методы и закрытые, которые требуют ключ приложения (как это что такое ключ? вы точно прочли предыдущую статью?:) ). Открытые методы представляют собой те API-функции, которые не требуют ключа, поэтому мы, фактически, можем обращаться к ним и без зарегистрированного приложения. Например, метод users.get является открытым, о чём можно догадаться вот по этой сноске на странице описания метода:



Данный метод возвратит данные со стены пользователя.

Отправляются открытые методы обычным GET-запросом в данном общем виде:

https://api.vk.com/method/ИМЯ_МЕТОДА?ПАРАМЕТР1=ЗНАЧЕНИЕ1&ПАРАМЕТР2=ЗНАЧЕНИЕ2.


Внимание стоит обратить на то, что параметров может быть неограниченное количество, в любом запросе должны присутствовать ОБЯЗАТЕЛЬНЫЕ параметры (о них предупреждает сноска возле параметра). Второе, на что стоит обратить внимание - это httpS протокол, т.е. использование ssl-защиты. Это стоит учитывать в своей программе. В Delphi работать с ssl легче всего через Synapse. Но сегодня рассмотрим indy - компонент idHTTP, т.к. он есть в стандартном наборе компонентов и его гораздо чаще применяют.

Так как выше я привёл в пример метод users.get, то сейчас и рассмотрим его вызов на практике. На форму кидаем 6 компонентов: 2 Button, memo и ещё 1 memo, IdSSLIOHandlerSocketOpenSSL, idhttp.
IdSSLIOHandlerSocketOpenSSL - страшное название, не правда ли? Зато этот компонент, идущий в XE-версиях в стандартном наборе, позволяет с лёгкостью работать с протоколом ssl. Настраивается и привязывается он очень легко: в компоненте idhttp щёлкните по свойству IOHandler, там в выпадающем списке выберете IdSSLIOHandlerSocketOpenSSL(1/2/n).

Пример формы:



Теперь на событии onclick кнопки "Отправить запрос" напишем следующее:

begin
		data:= TStringList.Create;
		data.Text:= form1.IdHTTP1.Get('https://api.vk.com/method/users.get?user_ids=1&fields=sex,bdate,city');
		form1.sMemo1.Text:= data.Text;
		end;


Сам запрос - https://api.vk.com/method/users.get?user_ids=1&fields=sex,bdate,city

У метода 3 параметра - user_ids - ID юзера, fields - то, что хотим получить (что можно - смотрите тут) и name_case - падеж склонения (не обязателен).

Нажмём кнопку "Отправить запрос" и получим в Memo1 вот что:

{"response":[{"uid":1,"first_name":"Павел","last_name":"Дуров","sex":2,"bdate":"10.10.1984","city":"2"}]}


Имя и фамилия, sex (пол), дата рождения и код города. Всё, что запросили.
Именно так происходят запросы ко всем (или почти всем) открытым методам.

ЗАЩИЩЁННЫЕ МЕТОДЫ.

В закрытом методе структура та же, что и в открытом, но добавляется лишь 1 параметр - access_token. Что это и как его получить - смотрим в статье, ссылка на которую была сверху. Продемонстрирую на практике. Возьмём для этого метод photos.createAlbum (первое, на что взгляд упал:) ). Я надеюсь, что token вы умеете получать, как уже было сказано выше. Код с запросом выглядит следующим образом:

token:= virvytoken(WebBrowser1.LocationURL);
		data:= TStringList.Create;
		ShowMEssage(token);
		data.Text:= form1.IdHTTP1.Get('https://api.vk.com/method/photos.createAlbum?title=НАЗВАНИЕ_АЛЬБОМА&group_id=ID_ГРУППЫ&description=ПОДПИСЬ_АЛЬБОМА&access_token='+token);
		form5.sMemo1.Text:= data.Text;


Собственно virvytoken - моя функция, которую приводил в прошлой статье. Парсит токен из URL браузера.
Видим, что в конце добавлен параметр - access_token=token. В переменной токен лежит сам секретный ключ. В результате функция вернёт следующее:

{"response":{"aid":11111111,"thumb_id":-1,"owner_id":-123123,"title":"TestAlbum","description":"Test","created":123123,"updated":123123,"privacy":0,"comment_privacy":0,"size":0,"can_upload":1}}


Где:


id — идентификатор созданного альбома;
thumb_id — идентификатор фотографии, которая является обложкой альбома;
owner_id идентификатор пользователя или сообщества, которому принадлежит альбом;
title — название альбома;
description — описание альбома;
created — дата создания альбома в формате unixtime;
updated — дата обновления альбома в формате unixtime;
size — количество фотографий в альбоме;
privacy — настройки приватности для просмотра альбома;
comment_privacy — настройки приватности для комментирования альбома;
can_upload — может ли текущий пользователь добавлять фотографии в альбом.

Ну и подтверждение тому, что всё создано:



Давайте подведём итоги.
Алгоритм сложился, и выглядит он так:

1) Смотрим, требует метод, который нам нужен, token, или же нет.
2) Если нет, то отправляем запрос вида https://api.vk.com/method/ИМЯ_МЕТОДА?ПАРАМЕТР1=ЗНАЧЕНИЕ1&ПАРАМЕТР2=ЗНАЧЕНИЕ2
3) Если требует, то авторизируем приложение, получаем токен, и отправляем запрос вида
https://api.vk.com/method/ИМЯ_МЕТОДА?ПАРАМЕТР1=ЗНАЧЕНИЕ1&ПАРАМЕТР2=ЗНАЧЕНИЕ2&&access_token=token.
4) Не забываем, что запросы вида GET.

Статья прежде всего рассчитана на новичков, кто не работал с vk-api ранее. Показывает алгоритм, актуальный на 2014 год.

Спасибо за внимание.

Добавлено: 25 Февраля 2015 07:55:29 Добавил: Андрей Ковальчук

Пишем плеер для vk.com. Работа с API [0]

-----Предисловие-----

В общем не так давно (тем 3-5) назад поднимался вопрос авторизации на сайте vk.com. Я выступал с тем, что авторизироваться нужно по-правилам, что бы получить token (секретный ключ, выдающийся приложению для работы с API сайта vk), а мой аппонент (vershik) говорил, что можно залогинниться и простым POST-запросом. В принципе тут как посмотреть и какие цели преследовать. Если брутить аккаунты, то можно и post'ы слать, а если делать приложение для работы с сайтом - то нужно всё сделать по уму. Чем мы сегодня и займёмся.

На vb.net, на c#, да и на всём, на чём только что-то пишется, есть статьи по работе с vk-api. Delphi далеко не уходил, и статей по нему тоже в избытке. Разница лишь в том, что все они (или почти все), написаны в 2010-2011 годах, и многое в API vk.com с тех пор изменилось: начиная от авторизации, заканчивая простыми API'шками, показывающими информацию о пользователе.

-----/Предисловие-----

Сегодня (1-ая часть как никак), рассмотрим процесс авторизации на сайте, затронем многие API-функции и сделаем по-итогу небольшое приложение для сайта.

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



Далее называем своё детище, и указываем его тип: IFrame/Flash приложение. После чего можно указать описание, поставить галочку напротив пункта "Приложение", прочитать правила размещения приложений, перейти к загрузке:



После чего на телефон, привязанный к аккаунту, будет отправлен код подтверждения. Вводим его и нажимаем "Отправить код". Всё, часть работы уже сделана, приложение создано. Осталось всего ничего, создать его клиентскую платформу =).

Заходим в Delphi, создаём проект. Я покажу лишь авторизацию и принцип работы с API.

Для авторизации можно сделать отдельную форму, так как с ней нам придётся подать пользовтаелю WebBrowser. Зачем? Потому что при авторизации пользователь должен подтвердить , что он согласен с тем, что приложение будет иметь доступ к его аккаунту (не, ну а что? мы делаем обычную программу=) ).

В общем создали форму, кинули туда WebBrowser, idhttp, IdSSLIOHandlerSocketOpenSSL1, Button. У меня что-то вроде этого:

Соответственно, при нажатии на кнопку с названием "Auth" (ваш КЭП - это название метода), будет производиться авторизация. Теперь по коду и по принципу действия. У меня всё свелось к следующему:

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

В глобальных переменных у меня лежат:

var
  token: String; // Сам ключ
  data: TStringList; //Переменная типа TStringList, для отчётов
  http: TidHTTP; //переменная типа TIdHTTP, что бы не прописывать имя компонента


И так же имеются 2 публичных функции:

function TForm1.virvytoken(s: string): string;
begin
delete(s,pos('&expires_in',s), length(s));
Result:= copy(s, pos('access_token=',s)+13, length(s));
end;


и

procedure TForm1.krinthereg(s: string);
var reg: TRegistry;
begin
reg:= TRegistry.Create;
reg.RootKey:= HKEY_CURRENT_USER;
reg.LazyWrite:= true;
try
reg.OpenKey('Software\VKbot', true); //путь до ключа
reg.WriteString('key',s);
reg.CloseKey;
except
MessageBox(handle,PChar('Не удалось записать ключ в реестр'), PChar('Error'), MB_ICONSTOP);
end;
end;


Первая, как понятно из названия =), парсит token из ссылки, куда нас кинул браузер после авторизации.

Вторая же записывает ключ в реестр. Соответственно потом мы будем его оттуда извлекать и работать с ним дальше.

Теперь возможно будут возгласы, что у ключа есть время действия. Это так, но если передать в качестве параметра "offline", то ключ будет без времени ограничения.

В функциях ничего особенного нет, а всё что может вызвать затруднения - выделено комментариями.

Мы пришли к самому главному - отправки запроса с помощью браузера. Делается это просто:

form1.WebBrowser1.Navigate('http://api.vkontakte.ru/oauth/authorize?client_id=id_юзера&scope=friends,video,offline,notify,photos&redirect_uri=http://oauth.vk.com/blank.html&display=page&response_type=token');


Метод Navigate перебрасывает нас на указанную страницу.

http://api.vkontakte.ru - тут, думаю, понятно.
oauth - протокол, по которому производится авторизация на сайте. Если кому-то это интересно, то в тут о нём подробно расписано.
authorize - метод, сообщающий vk, что мы пытаемся авторизироваться
client_id - идентификатор Вашего приложения
scope - Права доступа. Передаём тут те параметры, с которыми будем работать. Все права тут.
redirect_uri=http://oauth.vk.com/blank.html - редирект, куда мы будем перенаправлены
display - параметр, отвечающий за вид окна авторизации (page, popup, touch и wap)
response_type=token - мы требуем токен


redirect_uri=https://oauth.vk.com/blank.html
Это обязательное условие для работы с методами, в описании которых указано, что они доступны только для Desktop-приложений.


И ещё одна цитата:

После успешной авторизации приложения браузер пользователя будет перенаправлен по адресу REDIRECT_URI, указанному при открытии диалога авторизации. При этом ключ доступа к API access_token и другие параметры будут переданы в URL-фрагменте ссылки:


http://REDIRECT_URI#access_token= 533bacf01e11f55b536a565b57531ad114461ae8736d6506a3&expires_in=86400&user_id=8492


Вместе с ключом access_token также будет указано время его жизни expires_in, заданное в секундах.

Идём дальше.

На событие onclick кнопки пишем следующее:

token:= virvytoken(form1.WebBrowser1.LocationURL);  //Получение token'а
krinthereg(token); //запись token'а в реестр


На этом процесс авторизации завершён. Токен скопирован в реестр (про безопасность и какое-либо шифрование в первой части я умышленно забыл).

Пробуем авторизироваться и получаем по-итогу следующий текст:

Пожалуйста, не копируйте данные из адресной строки для сторонних сайтов. Таким образом Вы можете потерять доступ к Вашему аккаунту.


Так. Теперь разбираемся с API vk.com. Первым делом, как и планировалось - вывод элементов профиля. Сделать это не сложно, можно даже одним API-методом - users.get. Подробней про него можно прочитать тут - http://vk.com/dev/users.get

Скажу, что у него 2 обязательных параметра - user_ids (идентификатор пользователя) и fields(то, что хотим от него узнать). В качестве fields можно запросить имя, пол, фотографию и так далее.

data.Text:= http.Get('https://api.vk.com/method/users.get?user_ids=ваш_id&fields=sex,city,country,photo_50,photo_100,online');


Рассмотрим такой GET запрос. Параметры: SEX - пол, city - город, country - страна, photo_50 - фотография со сторонами 50x50 px, online - статус, в сети ли юзер.
Про остальные параметры читаем там же, где и про функцию. В data нам вернётся следующее:

{"response":[{"uid":11111111,"first_name":"Иван","last_name":"Иванов","sex":2,"bdate":"01.01","city":"100","country":"1","photo_50":"http:\/\/vk.com\/images\/camera_c.gif","photo_100":"http:\/\/vk.com\/images\/camera_b.gif","online":1}]}


Ну а что вы хотели? Тут надо парсить .

Некоторые параметры передаются ключами. Например "sex":2 - значит, что пол мужской. Если бы было "sex":1 - был бы женский.

Примечание: это открытый метод, и token'а, который мы спарсили выше он не требует. Если метод требует токен - то просто добавляем к запросу данный параметр.

На этом первую часть работы с API сайта vk.com я завершаю. Во второй рассмотрим как сделать уже рабочее приложение, а что оно должно делать - можете писать в комментарии.

Добавлено: 24 Февраля 2015 20:41:08 Добавил: Андрей Ковальчук

Пишем плеер для vk.com. Работа с API и BASS.DLL

Продолжаем работать с API сайта vk.com. Сегодня сделаем несколько рутинных задач, вроде вывода друзей в красивый список, вывода личного профиля и самое главное - начнём реализовывать проигрыватель музыки (ничего лучше я не придумал).

Часть 1. Парсинг личной информации.

Первым делом выведем личный профиль в более-менее красивом виде, что-то вроде этого:



Для вывода фото была написана отдельная процедура - getusersphoto(image: TImage), которая выводит фото юзера в компонент Image. Код процедуры:

		Procedure getusersphoto (simage: TImage);
		 
		uses JPEG;
		
		var
		  buf, s1: string;
		  i: integer;
		  image: tjpegimage;
		  ms:   TMemoryStream;
		
		begin
		  data.Text:= http.Get('https://api.vk.com/method/users.get?user_ids=USER_ID&fields=photo_max');
		  buf:= data.Text;
		  delete (buf, pos('.jpg', buf)+4, length(buf));
		  s1:= copy (buf, pos('"photo_max":"http:',buf)+13, length(buf));
		
		for i := 1 to length(s1) do
		begin
		  if s1[i] = '\' then delete(s1,pos(s1[i],s1),1);
		end;
		
		image:= tjpegimage.Create;
		ms:= TMemoryStream.Create;
		http.Get(s1, ms);
		ms.Position := 0;
		simage.AutoSize:= true;
		image.LoadFromStream(ms);
		simage.Picture.Graphic := image;
		
		end;


В коде, думаю, всё понятно. Что может вызвать затруднение - помечено комментариями.
Для вывода имени, ID, статуса и прочего говнаих вещей, тупой парсинг одним словом, так же создана процедурка - procedure parsinfo;

Для работы процедуры нужна переменная типа info. Info - запись, содержащая следующие параметры:

		type
		  info = record
		    name: string;
		    family: string;
		    pol: string;
		    city_index: string;
		    city: string;
		    sp: string;
		    id: string;
		  end;


Может кому-нибудь пригодиться код ниже, т.к. я тут парсю основные данные юзера, а процесс этот утомительный и скучный, так что пользуйтесь : )

var
	tekinfo: info;
	 
	procedure TForm1.parsinfo;
	var
	  buf, temp: String;
	begin
	
	data.Text:= http.Get('https://api.vk.com/method/users.get?user_ids='+userid.Text+'&fields=sex,city,has_mobile,relation');
	buf:= Data.Text; //Хранитель времени и 1-ого запроса - переменная BUF
	temp:= buf;  //Переменная temp будет содержать копию 1-ого запроса, для парсинга из неё локальных данных
	
	//id
	temp:= buf;
	delete (temp, pos(',"first_name"',temp), length(temp));
	tekinfo.id:= copy(temp, pos('"uid":',temp)+6, length(temp));
	
	//name
	temp:= buf;
	delete (temp, pos(',"last_name"',temp)-1, length(temp));
	tekinfo.name:= copy (temp, pos('"first_name":"',temp)+14, length(temp));
	
	//family
	temp:= buf;
	delete (temp, pos('","sex"',temp), length(temp));
	tekinfo.family:= copy(temp, pos('"last_name":"',temp)+13, length(temp));
	
	//city
	temp:= buf;
	delete (temp, pos('","has_mobile"',temp), length(temp));
	tekinfo.city_index:= copy (temp, pos('"city":"',temp)+8, length(temp));
	data.Text:= http.get('https://api.vk.com/method/getCities?api_id=ID_ПРИЛОЖЕНИЯ&'+md5(tekinfo.id+'api_id=ID_ПРИЛОЖЕНИЯ'+'cids='+tekinfo.city_index+'v=2.0')+'&v=2.0&cids='+tekinfo.city_index);
	temp:= data.Text;
	delete (temp, pos('"}]',temp), length(temp));
	tekinfo.city:= copy (temp, pos('"name":"',temp)+8, length(temp));
	
	//pol
	temp:= buf;
	delete (temp, pos(',"city"',temp), length(temp));
	tekinfo.pol:= copy(temp, pos('"sex":',temp)+6, length(temp));
	if tekinfo.pol = '1' then tekinfo.pol:= 'Женский' else tekinfo.pol:= 'Мужской';
	
	//SP
	temp:= buf;
	delete (temp, (pos('","relation_partner"',temp)or pos('"}]}',temp)), length(temp));
	tekinfo.sp:= copy(temp, pos('"relation":"',temp)+12, length(temp));
	case strtoint(tekinfo.sp) of
	1: tekinfo.sp:= 'не женат/не замужем';
	2: tekinfo.sp:= 'есть друг/есть подруга';
	3: tekinfo.sp:= 'помолвлен/помолвлена';
	4: tekinfo.sp:= 'женат/замужем';
	5: tekinfo.sp:= 'всё сложно';
	6: tekinfo.sp:= 'в активном поиске';
	7: tekinfo.sp:= 'влюблён/влюблена';
	end;
	
	form1.lbname.Caption:= tekinfo.name+' '+tekinfo.family;
	form1.lbpol.Caption:= tekinfo.pol;
	form1.lbgorod.Caption:= tekinfo.city;
	form1.lbSP.Caption:= tekinfo.sp;
	end;


Ничего сложно тут нет, обычный парсинг обычными строковыми функциями. Кто-то скажет: "Почему ты не использовал регулярки?" А потому что не люблю я их юзать, когда не так уж много нужно парсить. + лишние модули - не есть хорошо. Так что обошлось и без regExpr.

По коду - самое большое затруднение (да-да, даже в парсинге появились трудности) вызвал у меня парсинг города. Т.к. в основном запросе я получил лишь код города, то подумал - "Заюзаю-ка я ещё 1-у функцию, которая вернёт по коду города его название". Заюзал - функция getCities . Но и с ней не всё гладко - ей, в параметрах запроса нужно передать, цитирую:


sig
Подпись запроса по стандартной схеме.
Ну ладно, идём на эту самую "стандартную схему". Цитирую оттуда:


Как создавать подпись запроса?
Параметр sig равен md5 от контактенации следующих строк:

viewer_id – id текущего пользователя, переданный приложению посредством flashVars/GET запроса. (При запросе с сервера viewer_id указывать не нужно)
пар "parameter_name=parameter_value", расположенных в порядке возрастания имени параметра (по алфавиту).
секрета secret полученного через flashVars / GET запрос, или, если метод вызывается с сервера - секрета приложения api_secret (секрет Вы можете менять при редактировании страницы приложения).

Ну и всё это дело, конечно же, с MD5. Как же не зашифровать город алгоритмом, ведь город - это почти логин с паролем! MD5 модуль можно будет скачать где-то в конце статьи, либо прикреплённым, либо выложенным на файлообменник.

Вопрос - зачем так ебаться заморачиваться ИМЕННО с городом - остаётся открытым. Ну а запрос, по феншую, я составил - его можете наблюдать в коде выше.

В общем, данная процедурка выводит основные данные юзера в заданные на форме элементы. Так что с первой частью (а ей был парсинг) задания я справился. Осталось "всего ничего" - научиться воспроизводить звуковые файлы с vk.com.

Часть 2. BASS.DLL + vk.

Итак, как и было задумано - будем сейчас пилить что-то вроде плеера. Для воспроизведения звука я юзаю библиотеку bass.dll. Как её устанавливать - есть в сети, это я описывать не буду.

Кидаем в uses bass и инициаллизируем либу - в OnCreate формы пишем:

if (HIWORD(BASS_GetVersion) <> BASSVERSION) then //Проверка версии библиотеки
		    begin
		        MessageBox(0,'An incorrect version of BASS.DLL was loaded',nil,MB_IConerror);
		        Halt;
		    end;
		    if not BASS_Init(-1, 44100, 0, Handle, nil) then   // Initialize audio - default device, 44100hz, stereo, 16 bits
		        ShowMessage('Error initializing audio!');  


BASS_Init(-1, 44100, 0, Handle, nil) - собственно вот в этой строчке и есть основная настройка библиотеки. Функция инициализирует звуковой поток.
В первом параметре сидит девайс, значения могут быть 0 - первое устройство, -1 - по умолчанию, -2 - без звука. Далее частота - по-умолчанию лучше указывать 44100. А далее различные флаги.
Итак, проинициаллизировали, что дальше? А дальше немного ознакомимся с тем, как работает либа. Сначала можно провести небольшой эксперимент, и просто научиться воспроизводить звук с пк.

Напишем небольшую процедуру:

var Channel: HStream;
	 
	procedure BasicPlay(FileName: String);
	var
	  FFileName: PChar;
	begin
	  if not FileExists(FileName) then Exit;
	  FFileName:= PChar(FileName);
	
	  if Channel <> 0 then
	  begin
	    bass_ChannelStop(Channel);
	    bass_StreamFree(Channel);
	    Channel:= 0;
	  end;
	
	  Channel:= Bass_StreamCreateFile(False, FFileName, 0, 0, 0 {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});
	  if Channel =0 then begin
	    MessageBox(0,'Ошибка загрузки файла',0, Mb_ok);
	  end;
	
	  if not Bass_ChannelPlay(Channel, False) then
	  MessageBox(0, 'Ошибка воспроизведения файла',0, Mb_ok);
	
	end;


Ну и вызываем её так:

if form1.sOpenDialog1.Execute then begin
		BasicPlay(sopendialog1.FileName);


Собственно основная строчка - вот:

Channel:= Bass_StreamCreateFile(False, FFileName, 0, 0, 0 {$IFDEF UNICODE} or BASS_UNICODE {$ENDIF});

function BASS_StreamCreateFile(mem: BOOL; f: Pointer; offset, length, flags: DWORD): HSTREAM; 


Параметры:

mem - если TRUE, то файл в оперативке. Если FALSE - то на диске. f - имя файла (если он на диске).

offset - смещение, с которого надо начинать. Обычно я начинаю с начала, но если у тебя другое мнение - сообщи его этому аргументу.

length - необходимое количество данных. Если ты хочешь использовать все до конца файла, то просто ставь 0.

flags - ставь 0.

Функция возвращает переменную типа HSTREAM, которая и есть хэндл новорожденного потока.

Потоку HStream передаём файл, после чего функция его создаёт, а затем с помощью процедуры Bass_ChannelPlay воспроизводим. Всё просто. Попробуй всё это проделать, и воспроизвести файл.
Теперь попробуем воспроизвести файл из сети, ссылкой. Собственно выражаю благодарность разработчикам bass.dll за то, что позаботились об такой фиче, как воспроизведение трека по ссылке.
Настройки оставляем те же (инициализация и прочее), но допишем лишь 1-у функцию :

		var channel: HStream;
		  
		
		
		procedure BasicPlay(FileName: AnsiString);
		begin
		  Channel:= BASS_StreamCreateURL(PAnsiChar(FileName),0,0,nil,0); //Создаём файл, функция аналогична предыдущей, так что без комментариев.
		  if not Bass_ChannelPlay(Channel, False) then //Воспроизводим
		  MessageBox(0, 'Ошибка воспроизведения файла',0, Mb_ok);
		end;


Ну что? Вот и всё, в принципе, готово. Осталось самая малость - спарсить треки и ссылки к ним. Я этого не хотел, но пришлось подключить регулярки. Так же нужно создать 3 массива : в 1-ом у нас артист, во втором название трека, в третьем - линк на песню.
Наверное назрел вопрос, откуда парсить? Применим для этого api-функцию audio.get - ей в качестве параметров нужно передать:
owner_id - id юзера или группы (если группа, то перед id ставим минус (-) )
need_user - может быть 0 и 1. Если 1 - вернёт информацию о пользователе, загрузившим песню, если 1 - нет. Нам эта инфа нахер не сдалась, так что ставим 0.
v - версия протокола api-функций. Сейчас актуальная 5.5
И access_token - токен. Наконец-то он пригодился :)

Видим в каких тегах название, в каких название, а в каких линк. Всё, как я уже сказал, будем парсить в массивы. Ладно, хватит разглагольствовать, привожу код процедурки:

procedure TForm1.getaudio;
			  var
			    i,j: integer;
			    linktek: string;
			begin
			i:=0;
			data.Text:= http.Get('https://api.vk.com/method/audio.get?owner_id='+tekinfo.id+'&need_user=0&v=5.5&access_token='+token); //Отправляем api-запрос
			
			reg:= TRegExpr.Create; //Инициализируем переменную типа TregExpr (регулярные выражения. перед этим добавить нужно в uses модуль регулярок - RegExpr).
			reg.InputString:= data.Text; //Загружаем текст для регулярки
			reg.Expression:= '"artist":"(.*?)","title":"(.*?)","duration":(.*?),"url":"(.*?)"'; //Сама регулярка
			
			if reg.Exec(data.Text) then //Если тебе не понятен алгоритм работы с регулярными - пиши в комментах или ищи в google.com
			repeat
			  inc(i);
			  artist[i]:=reg.Match[1];
			  nazv[i]:= reg.Match[2];
			  linktek:= reg.Match[4];
			
			    for j := 1 to length(linktek) do //Ссылка все видели в каком формате. Там лишний символ - '\'. Удаляем его.
			      begin
			        if linktek[j] = '\' then
			          delete (linktek, pos(linktek[j],linktek), 1);
			      end;
			  link[i]:= linktek;
			until not reg.ExecNext;
			reg.Free;
			
			end;


На этом, с гордостью заверяю, парсинг завершён. Осталось вывести ссылки в ListBox:

var i: integer;
				begin
				  for i := 0 to High(artist)-1 do
				    form1.ListBox1.Items[i]:= artist[i]+' - '+nazv[i];
				end;


И по нажатию на ссылку запускать песню:

В событии onmousedown ListBox'а:

procedure TForm1.ListBox1MouseDown(Sender: TObject; Button: TMouseButton;
				  Shift: TShiftState; X, Y: Integer);
				  var i: integer;
				begin
				if ListBox1.ItemIndex>=0 then
				begin
				i:= ListBox1.ItemIndex;
				BasicPlay(link[i]);
				end;
				
				end;


Ну тут, думаю, всё понятно. Не комментирую. Итак, подведём итог 2-х статей.

Наша программа умеет:
1) Авторизироваться легальными методами, которые vk одобряет
2) Парсить профиль такими же методами
3) Выводить и запускать музыку юзера

На этом вторая статья окончена, всем спасибо, все свободны.

Мне осталось лишь допилить дизайн, обработать все ошибки в коде и оптимизировать сей код. Готовый исходник этой статьи я выложу уже в следующей :) По-поводу следующей, если есть идеи - пишите, буду рад)

Спасибо за внимание!

Добавлено: 24 Февраля 2015 20:34:21 Добавил: Андрей Ковальчук

synch vk #1. Добавление фотографий в vk перемещением в папку

Сегодня сделаю такую фишку, как автоматическая отправка фотографий в группу, простым закидыванием фотки в папку. Что-то вроде Mail Cloud'а или dropbox клиента, только у нас синхронизация будет не с серверами mail или dropbox, а с группой vk. А что? Тоже весьма надёжное место для хранения фотографий (в нашем случае не хранения, а обычной публикации.) :).

Если подумать, то можно догадаться, что при заливании новой фотографии в папку, размер сей папки меняется. (Загорелась лампочка, осенившая: а ведь так можно отлавливать, когда закинута новая фотка!). Так же можно догадаться, что у каждой фотографии есть своё имя. При появлении новой в группе, её имя будет проверяться с массивом имён уже закинутых фотографий, и если такой нет в массиве, то отправляем её в альбом, а имя ложим в массив. Идея простая, а приложение - полезное :)

Начали реализовывать. Пока я не буду смешивать код этого проекта с кодом предыдущего и сделаю всё на новой форме. Собственно, вот и моя форма:

Итак, сначала забиндим процедуру, которая даст нам выбор папки для фотографий, и запишет в ini-файл её размер, а в текстовик имена файлов:

В var:

  ini: TIniFile;
  dir: String;
  path_ini: string;
  file_settings: File;
  file_name: TextFile;
  namefiles: TStringList;


procedure searchsize;
  var
  mnf: array [1..1000] of string;
  size: Int64;
  List: TStringList;
  I: Integer;
begin
AssignFile(file_settings, ExtractFilePath(Application.ExeName)+'init.ini');
Rewrite(file_settings);
CloseFile(file_settings);
 
AssignFile(file_name, ExtractFilePath(Application.ExeName)+'list.txt');
if FileExists((Application.ExeName)+'list.txt') = false then Rewrite(file_name);
CloseFile(file_name);
 
List:= TStringList.Create;
if ini.ValueExists('file','directory_root') = false  then
begin
  SelectDirectory ('Выберите папку для сохранения фотографий', 'C:\', dir);
  ini.WriteString('file','directory_root',dir); // INI : пишем имя дирректории для изображений
  List:= GetAllFiles(dir);
end;
size:=0;
GetDirSize(dir, size);
ini.WriteInteger('file', 'size_one', size); // INI: пишем первоначальный размер файла
 
{записываем имена файлов в текстовик}
 
Append(file_name);
for I := 0 to list.Count-1 do
begin
ShowMessage(list.Strings[i]);
writeln(file_name, list.Strings[i]);
end;
CloseFile(file_name);
end;


Для определения размера дирректории служит процедура GetDirSize (путь до дирректории, переменная (в которую ляжет размер));

GetDirSize:

Функция, собирающая в стринглист имена всех файлов:

function GetAllFiles( Path: string): TStringList;
var
sRec: TSearchRec;
isFound: boolean;
Lb: TStringList;
begin
Lb:= TStringList.Create;
isFound := FindFirst( Path + '\*.*', faAnyFile, sRec ) = 0;
while isFound do
begin
if ( sRec.Name <> '.' ) and ( sRec.Name <> '..' ) then
begin
if ( sRec.Attr and faDirectory ) = faDirectory then
GetAllFiles( Path + '\' + sRec.Name);
Lb.Add(sRec.Name);
end;
Application.ProcessMessages;
isFound := FindNext( sRec ) = 0;
end;
FindClose( sRec );
Result:= lb;
end;


Запуск, проверка:

Файлы создаются, данные заносятся. Дальше напишем код, который будет определять были ли изменения в папке. В данном случае будем ловить только новые фото, удалённые нас пока, в этой версии программы, не интересуют.

var value_one: integer;
  value_new: int64;
  i, size: Integer;
  path: string;
  namefiles_new, file_old: TStringList;
begin
//привязка переменных к файлам 
AssignFile(file_name, ExtractFilePath(Application.ExeName)+'list.txt');
//Инициализация переменных
namefiles_new:= TStringList.Create;
file_old:= TStringList.Create;
 
value_one:= ini.ReadInteger('file', 'size_one', 0);
path:= ini.ReadString('file', 'directory_root', 'nil');
 
GetDirSize(path, value_new);      //получаем новый размер
if value_new <> value_one then
begin
  namefiles_new:= GetAllFiles(path);     //Получаем список новых имён
  Reset(file_name);
  file_old.LoadFromFile(ExtractFilePath(Application.ExeName)+'list.txt');    //Загружаем список старых имён в стринглист
  //Проверяем, кто новенький:
  for i := 0 to namefiles_new.Count-1 do
  begin
    if pos(namefiles_new.Strings[i], file_old.Text) = 0 then ShowMessage('Новый - '+ namefiles_new.Strings[i]);
  end;
end;
end;


В событии создания формы (OnCreate) у меня прописана инициализация ini-переменной:

ini:= TIniFile.Create(ExtractFilePath(Application.ExeName)+'init.ini');


Кидаем в папку с изображениями какое-либо новое, назовём его к примеру "фото-12". Запускаем, проверяем:

Отловили, отлично.

Теперь на моменте отлова нужно прилепить api-функции, которые загрузят этот самый новый файл на сервера vk, а затем сохранят его в альбом. Напомню, что что бы понять нижеизложенный код, его логику и принцип работы, прочитайте ранее описанные статьи, описанные ранее. Было уже достаточно статей по работе с vk api, как для определённых api-шек, так и описание общего алгоритма работы с api vk.

var
  path, url_no_pars, buf_2, url, server, photos_list, hash, temp : string;
  namefiles_new, file_old: TStringList;
  data: Tidmultipartformdatastream;
  dir: string;

//добавляем в основной блок begin..end:
data:=Tidmultipartformdatastream.Create;
dir:= ExtractFileDir(path);

 if pos(namefiles_new.Strings[i], file_old.Text) = 0 then  // если имя нового файла не найдено в массиве старых, то:
      begin
 
{
ШАГ 1
}
        //Отправляем запрос с методом getUploadServer , который вернёт нам URL для загрузки изображений.
        url_no_pars:= http.Get('https://api.vk.com/method/photos.getUploadServer?album_id=ID_альбома&group_id=ID_группы&access_token='+key+'&v=5.8');
 
        data.AddFile('file1',dir+'\фото\'+namefiles_new.Strings[i], 'image/jpeg');
 
        {парсинг URL}
        for j := 1 to length(url_no_pars) do
          begin
            if url_no_pars[j] = '\' then
            begin
             delete(url_no_pars, pos(url_no_pars[j], url_no_pars), 1);
            end;
 
          end;
          if pos (',"album_id":', url_no_pars) <> 0 then delete(url_no_pars, pos(',"album_id":', url_no_pars)-1, length(url_no_pars));
          url:= copy(url_no_pars, pos('http://',url_no_pars), length(url_no_pars));
         {/парсинг URL}
 
{
ШАГ 2
}
        buf_2:= http.Post(url, data); //передаём изображения (data) на URL, полученный в предыдущем шаге
 
        //Pars HASH
        temp:= buf_2;
        delete(temp, pos('","gid"', temp), length(temp));
        hash:= copy (temp, pos('"hash":"', temp)+8, length(temp));
        //Pars HASH
 
        //Pars server
        delete(temp, pos(',"photos_list', temp), length(temp));
        server:= copy (temp, pos('{"server":', temp)+10, length(temp));
        //Pars server
 
        //Pars photos_list
        temp:= buf_2;
        delete(temp, pos('","aid":', temp), length(temp));
        photos_list:= copy (temp, pos('photos_list":"', temp)+14, length(temp));
        for j := 1 to length(photos_list) do
          begin
            if photos_list[j] = '\'  then delete (photos_list, pos('\', photos_list), 1);
          end;
          ShowMessage(photos_list);
        //Pars photos_list
 
{
ШАГ 3
}
        //Конечная загрузка изображений на сервер
        http.Get('https://api.vk.com/method/photos.save?album_id=ID_альбома&group_id=ID_группы&server='+server+'&photos_list='+photos_list+'&hash='+hash+'&caption=Описание&description=Текст_описания_альбома&access_token='+key+'&v=5.8');
      end;
  end;


Всё таки прокомментирую. Работа с изображениями в vk, а в частности загрузкой и пуликацией, происходит в 3 эпата:

шаг 1) Получение строки с запросом, происходит с помощью отправки запроса с методом getUploadServer. Он вернёт нам URL для загрузки файлов.

шаг 2) Затем на этот адрес отсылаем POST-запрос, где передаём файлы в потоке типа Tidmultipartformdatastream. В итоге получаем в ответ адрес сервера, массив адресов на фото и hash.

шаг 3) Отсылаем финальный запрос на сервер с помощью метода photos.save, передав параметры полученные в шаге 2. Открываем альбом и смотрим:

Это именно то, что я закинул в папку, т.е. что является новым. Осталось добавить лишь добавление имён этих фоток в текстовик, а размера дирректории в ini-файл и кинуть код в таймер. Но с этим, я думаю, вы и сами справитесь :)

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

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

Возможностей усовершенствовать клиента - огромное количество, так что дерзайте!

Добавлено: 24 Февраля 2015 19:28:59 Добавил: Андрей Ковальчук

Работа с методами ВКонтакте API на стороне клиента и сервера

Часто начинающие пытаются использовать все методы ВКонтакте API с сервера (PHP).
В этой статье я опишу почему при создании Flash/IFrame-приложений не все методы можно вызывать с сервера и как определить можно ли вызвать конкретный метод API с сервера.



Существует 2 способа взаимодействия с ВКонтакте API:

На стороне клиента (JavaScript)
На стороне сервера (PHP и др. серверные языки)
Некоторые методы можно вызывать только на стороне клиента, а некоторые только на стороне сервера. Большинство методов ВКонтакте API можно использовать только на стороне клиента.

Почему не все методы можно вызвать на стороне клиента

Со стороны клиента нельзя вызывать secure.* методы. Эти методы нужно вызывать только с сервера, потому что вызывать их со стороны клиента не безопасно. Злоумышленник сможет получить доступ к балансу приложения, возможность отправлять уведомление пользователям и другие секретные данные.

Почему не все методы можно вызвать с сервера

При авторизации пользователя ВКонтакте, в браузере пользователя храниться информация, по которой сайт ВКонтакте идентифицирует пользователя.
Сервер (хостинг) - это не компьютер пользователя. Это отдельный компьютер. На нем не хранится информация пользователя. Вызывая методы API с сервера, сайт ВКонтакте не может идентифицировать пользователя.
Если метод работает с какими-то данными конкретного пользователя, то API должно знать с данными какого пользователя нужно работать. Если в методе есть параметр для передачи ID пользователя, то можно попробовать вызвать метод с сервера.

Доступен ли метод с сервера можно узнать, прочитав документацию или попробовав его вызвать. Но если в документации не описана возможность вызывать метод с серверной стороны, то со временем он может перестать работать с сервера.
Если при вызове метода с сервера вы получаете ответ:

[error_code] => 3
[error_msg] => Unknown method passed
то метод не работает с сервера.

Методы, вызываемые со стороны клиента

Все методы ВКонтакте API, кроме secure.* методов

Методы, вызываемые с серверной стороны

Официально (описано в документации):

secure.*
friends.get
Неофициально:

isAppUserusers.get
getUserSettings
likes.getList
groups.getById
groups.isMember
photos.getAlbums
photos.get
wall.get
wall.getById
newsfeed.search
storage.get
storage.set
getServerTime

Добавлено: 24 Февраля 2015 06:41:36 Добавил: Андрей Ковальчук

Отправка сообщения на стену пользователя с внешнего сайта (wall.post)

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



Приложения ВКонтакте (Flash/IFrame)

Интерактивные приложения используют API ВКонтакте для глубокой интеграции с сайтом.

Очевидно, что Flash/IFrame приложения - это не то что нужно в данном случае.
Однако из приложения внутри ВКонтакте, метод wall.post работает из Flash и IFrame приложений.

Standalone/Mobile приложения
Запускаются в виде обыкновенных программ на устройстве пользователя — компьютере или смартфоне.

Это тоже не то что нужно.
Такое приложение должно иметь доступ к управлению Web-браузером. Управлять браузером с внешнего сайта не получится.

Кто-то игнорирует понимание что такое Standalone-приложение и пытается использовать этот вид авторизации для внешнего сайта - [ur=http://vk.com/developers.php?id=-1_37230422&s=1l]Авторизация клиентских приложений[/url].

Сразу же в описании видим:
Для доступа к API ВКонтакте из любого Standalone-приложения предусмотрен механизм клиентской авторизации на базе протокола OAuth 2.0. В качестве клиента может выступать любое Desktop/мобильное приложение, имеющее доступ к управлению Web-браузером (например, компонент UIWebView при создании приложения для iOS).

Логично, что с внешнего сайта мы не сможем управлять браузером.
Если всё-таки не верить документации и попытаться использовать эту авторизацию на сайте и думать логически, то после переадресации на страницу http://oauth.vk.com/blank.html# - браузер находится уже не на сервере со скриптом разработчика, поэтому считать этот url не получится.
Если же подобный url открывается в отдельном приложении со своим объектом браузера, то этот url можно считать программно, обратившись к свойствам окна браузера. На PHP такое обращение не выполнить.

Вывод: такой вариант не подходит для отправки сообщений на стену пользователя с внешнего сайта.

Виджеты и сторонние сайты

Набор виджетов ВКонтакте для сторонних сайтов позволит моментально добавить к Вашему проекту социальную составляющую. Также возможна более глубокая интеграция через Open API или OAuth 2.0.

Мы хотим использовать методы на стороннем сайте, значит теоретически это то что нам нужно.
Если не требуется какой-то особой отправки сообщений на стены пользователей, то вполне подойдут виджеты «Мне нравится» или Share.

Если по каким-то причинам эти виджеты не подходят, то вероятно нужно использовать Open API или OAuth 2.0.

Open API

Авторизация проходит. Некоторые методы API успешно вызываются.

Попробуем вызвать метод wall.post. Для начала почитаем о нём в документации http://vk.com/developers.php?oid=-1&p=wall.post
Для вызова этого метода из Desktop приложений - приложение должно иметь права с битовой маской, содержащей 8192. (Подробнее о получении прав)
Данный метод доступен также Flash и IFrame приложениям, использующим функцию VK.api в Javascript API.


Про сайты ничего не сказано. Посмотрим какие права должно иметь приложение для использования этого метода.
+8192 Доступ к обычным и расширенным методам работы со стеной.
Внимание, данное право доступа недоступно для сайтов (игнорируется при попытке авторизации).


http://vk.com/developers.php?oid=-1&p=% ... 0%B8%D0%B9

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

При запросе прав доступа в списке запрашиваемых прав есть доступ к стене. Было ли оно проигнорировано?



Мое приложение называется «Flapps.ru Open API»

Попробуем вызвать метод wall.post.
Появилось окно



Добавление записи на стену
Приложение Flapps.ru Open API предлагает разместить у Вас на стене и в новостях у Ваших друзей следующую запись.
Hello!

Нажимаем "Разместить запись".

Сообщение опубликовано на стене! У сообщения есть подпись с названием приложения "через Flapps.ru Open API".



Open API - используется для внешнего сайта? Тогда почему публикация на стену произошла, несмотря на описание в документации о том что данное право доступа недоступно для сайтов?

Вывод: при использовании авторизации Open API можно публиковать сообщения на стену пользователя.
Может ли случится так, что метод перестанет работать через Open API? Этого я сказать не могу. Сейчас метод работает.

OAuth 2.0

Авторизация проходит без проблем, многие методы API работают.

Но при вызове метода wall.post сервер API возвращает ошибку:
Permission to perform this action is denied for non-standalone applications

Если перевести на русский, то можно понять, что это действие запрещено использовать с сервера для не standalone-приложений.

Вывод: OAuth 2.0 не подходит для отправки сообщений на стену пользователя с внешнего сайта.

Дополнительная информация

Из официальной группы разработчиков приложений для ВКонтакте
Метод wall.post, позволяющий Desktop-приложениям размещать записи на стенах пользователей, теперь доступен для Flash- и IFrame-приложений, использующих Flash-посредник и IFrame API соответственно.

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

Обратите внимание, что метод wall.post может быть вызван только при помощи функции VK.api, встроенной в библиотеку Flash-посредника или IFrame API. Такой подход позволяет нам выводить окно с предварительным просмотром публикуемой записи, всегда давая пользователю возможность подтвердить или отклонить это действие./div>

http://vkontakte.ru/topic-1_24039123

Почему возникают такие сложности с отправкой записей на стену с внешнего сайта

ВКонтакте API при публикации требует от пользователя подтверждения размещения записи на стене.
На мой взгляд, так сделано для безопасности. Чтобы не использовали это для рассылки спама.
Если отправка происходит из приложения внутри ВКонтакте - пользователь видит окно подтверждения.
Если пытаются отправить с внешнего сайта, то такое окно не выводится. Это не реализовано ВКонтакте при публикации с внешнего сайта.
Если отправка происходит из Standalone приложения, то она происходит без подтверждения, т.к. можно предположить, что авторизация произошла через http://oauth.vk.com/blank.html# приложением, которое пользователь сознательно установил.

Другие варианты публикации записей на стене пользователя с внешнего сайта

- Использовать виджеты «Мне нравится» или Share.

- Создать отдельное Iframe/Flash приложение внутри ВКонтакте для публикации на стене пользователя.
С внешнего сайта открывать это приложение на новой странице. Пользователь установит приложение и подтвердит публикацию.

- Авторизовать пользователя по логину и паролю на своем сайте. Не использовать API, а использовать curl и т.п.
Это плохой вариант из-за запроса пароля у пользователей и из-за того что такой способ может перестать работать из-за изменений скриптов сайта Вконтакте. Этот способ нарушает правила пользования сайтом ВКонтакте.


Если вам есть что дополнить о публикации на стене пользователя с внешнего сайта, то пишите в этой теме.

Добавлено: 23 Февраля 2015 21:18:37 Добавил: Андрей Ковальчук

IFrame-приложение ВКонтакте. Вывод информации о пользователе

Приложение получает информацию о пользователе и выводит её.

Для работы приложения нужен хостинг.

1. Создаём html файл.

Можно использовать специальную программу или любой текстовый редактор, например, блокнот.
Расширение файла должно быть html.

2. Пишем в созданый html файл код:

<html>
<head>
<!-- подключаем xd_connection.js -->
<script src="http://vkontakte.ru/js/api/xd_connection.js?2" type="text/javascript"></script>
<script type="text/javascript" charset="cp1251" >
window.onload = (function() {   // когда загрузится вся страница
    VK.init(function() {    // инициализируем Vk API
    // узнаём flashVars, переданные приложению GET запросом. Сохраняем их в переменную flashVars
    var parts=document.location.search.substr(1).split("&");
    var flashVars={}, curr;
    for (i=0; i<parts.length; i++) {
        curr = parts[i].split('=');
        // записываем в массив flashVars значения. Например: flashVars['viewer_id'] = 1;
        flashVars[curr[0]] = curr[1];
    }
 
    // получаем viewer_id из полученных переменных
    var viewer_id = flashVars['viewer_id'];    // выполняем запрос получения профиля
    VK.api("getProfiles", {uids:viewer_id,fields:"photo_big"}, function(data) {         // обрабатываем полученные данные
        // выводим имя и фамилию в блок user_info
        document.getElementById('user_info').innerHTML = data.response[0].first_name + ' ' + data.response[0].last_name + '<br />';        // создаем img, для отображения аватарки
        var image=document.createElement('img');
        // из полученных данных берем ссылку на фото
        image.src=data.response[0].photo_big;
        // добавляем img в блок user_info
        user_info.appendChild(image);
    });
    });
});
</script>
</head>
<body>
<div id="user_info"></div>
</body>
</html>


В методах ВКонтакте API произошли изменения, используйте вместо метода getProfiles метод users.get
xd_connection.js позволяет делать запросы к API ВКонтакте при помощи функции VK.api(String method, Object params, Function callback), при этом не нужно заботиться о подписи, версии Api, и анализе полученных данных, данные будут приходить в виде javascript объекта.

VK.api принимает 3 параметра:
Название метода api.
Объект с параметрами запроса.
Функция для обработки результата.
3. Загружаем файл на свой сервер.

4. Настраиваем приложение на сайте вконтакте.
Выбираем:
Состояние: Приложение включено и видно всем
Тип приложения: IFrame
Адрес IFrame: Ссылка на созданный html файл на вашем сервере. Например http://site/vk.html

5. Всё готово.

Добавлено: 19 Февраля 2015 20:11:59 Добавил: Андрей Ковальчук

Вывод видеозаписей (video.get)

В этом уроке показано как вывести видеозаписи группы или пользователя в IFrame-приложении.
Описание метода video.get - http://vkontakte.ru/developers.php?o=-1&p=video.get
В настройках приложения должен быть разрешен доступ к видеозаписям.

<html>
<head>
<script src="http://vkontakte.ru/js/api/xd_connection.js?2" type="text/javascript"></script>
<script src="http://code.jquery.com/jquery.min.js" type="text/javascript"></script>
<style>
/* немного оформления */
body {
    padding:0;
    margin:0;
    font-size: 11px;
    font-family: tahoma, tahoma, verdana, arial, sans-serif;
}
h2 {
    font-size: 12px;
    color: #45688E;
    font-weight: bold;
    padding-bottom: 2px;
}
p {
    padding:0;
    margin;0;
}
</style>
 
<script type="text/javascript" charset="cp1251" >
$(document).ready(function() {
    VK.init(function() {
        // метод video.get, gid - id группы, width - ширина видео, count - количество видео
        // чтобы получить видео пользователя, а не группы, меняем "gid" на "uid", 17157755 - на id пользвоателя.
        VK.api("video.get", {gid:17157755,width:320,count:3}, function(data) {
            if (data.response) {
                // количество полученных видео (+1 - объект хранит количество видео "всего" в группе)
                var videoCount = data.response.length;
                $('#video_count').html('Всего видео: ' + data.response[0]);
                // создаём переменную, в которую будем сохранять код для вывода инфомрации
                var video_html = '';
                // начинаем с i=1, а не 0, потому что data.response[0] - количество видео "всего"
                for (var i=1; i<videoCount; i++) {
                    video_html += '<h2>' + data.response[i].title + '</h2>' +
                                    '<iframe src="' + data.response[i].player  + '" width="607" height="360" frameborder="0"></iframe>' +
                                    '<p>' + data.response[i].description + '</p><br /><br />';
                }
                // выводим в блок #result
                $('#result').html(video_html);
                // изменяем размер окна
                VK.callMethod("resizeWindow", 607, $('#result').height()+50);
            } else {
                $('#result').html('Ошибка!');
            }
        });
    });
});
</script>
</head>
 
<body>
<p id="video_count"></p>
<div id="result"></div>
</body>
</html>

Добавлено: 19 Февраля 2015 20:09:26 Добавил: Андрей Ковальчук

Блок "приглашение друга"

пример можно посмотреть тут - http://vk.com/app2134400_23898673 основан на нескольких уроках, которые мне просто лень указывать, блок представляет собой вывод случайно выбранного друга и возможностью запостить на его стену сообщение, блок обновляется без перезагрузки страницы через заданное вами время

Код скрипта:

<script type="text/javascript">
VK.init(function(){
    getFriend();
});
 
$(document).ready(function(){
getFriend();
});
 
function getFriend(){
    $('#inviteFriend').fadeOut();
    VK.api('friends.get',{fields: 'uid,first_name,last_name,photo_rec,sex'},function(data){
        friend_n    = Math.floor(Math.random()*$(data.response).size());
        friend_id    = data.response[friend_n].uid;
        friend_name    = data.response[friend_n].first_name;
        //alert(friend_id);
        invHtml    = '<div class="infoBlock clearFix" >';
        invHtml+= '<div style="height: 60px; overflow: hidden; float: left; margin-right: 6px;">';
        invHtml+= '<IMG src="'+data.response[friend_n].photo_rec+'" height="50" alt="" style="margin-bottom: -2px;">';
        invHtml+= '</div>';
        invHtml+= '<div style="float: left; width: 106px;">';
        invHtml+= '<a href="#">'+data.response[friend_n].first_name+'</a></br>';
        invHtml+= 'еще не ';
        invHtml+= '<span>'+(data.response[friend_n].sex == 1 ? 'была':'был')+'</span> в Кинозале?</br></br>';
        invHtml+= '<div style="float: right; margin-top: 4px;">';
        invHtml+= '<input value=Пригласить type=button onclick=sendwallwindow() >';
        invHtml+= '</div>';
        invHtml+= '</div>';
        invHtml+= '</div>';
        $('#inviteFriend').html(invHtml).fadeIn();
    });
}
 
setInterval('getFriend()', 10000);
function sendwallwindow(user_id){
VK.api("wall.post", { owner_id:friend_id, message:"Заходи, тут очень много фильмов! Есть ЧАТ!", attachment:'photo23898673_268709136,photo23898673_268709137,photo23898673_268709138,photo23898673_268709139,photo23898673_268709140,photo23898673_268709141,photo23898673_268709142,photo23898673_268709143,photo23898673_268709144,http://vkontakte.ru/app2134400_23898673' }, function(data) {
alert(data.response);
if (data.response) alert("Сообщение успешно отправлено.");
});
}
</script>

Код блока (его вставить в нужном месте):
<div id="Friend"style="background: #0851ab; margin: 10px; width:200px; height:110px;">
<div class="inviteFriend">
<div id="inviteFriend"style="background: #0851ab; padding: 4px;width:180px; height:100px;"></div>
</div>
</div>

Вид поста выглядит так (в моем случае) -

Добавлено: 19 Февраля 2015 20:04:32 Добавил: Андрей Ковальчук

ВКонтакте без API: посты с картинками и ссылками

В недавнем времени появилась задача постить на стены пользователей и групп вконтакте посты с вложением картинок и ссылок.

Были опробованы различные готовые решения, но полноценных рабочих вариантов так и не было найдено.

Среди таких решений стоит выделить разработку пользователя xbreaker — класс vk.wallpost. В этой реализации работал постинг с вложением ссылок, но с картинками были проблемы — они не закачивались. Попытавшись доработать класс vk.wallpost, желаемого результата я так и не получил. Запросы к ВК в этом модуле делаются через небезызвестный PHP модуль CURL. Именно в нём, как мне показалось, и была проблема при закачке картинок, возможно просто не удалось настроить его должным образом. Помимо этого, проблемой было и то что в PHP нет хорошего модуля для программного server-side «браузера», такого например как WWW::Mechanize в Perl. Оценив все «за» и «против» я взялся написать свой модуль для управления аккаунтом в ВКонтакте, но уже на языке Perl.

Итак, перейдём непосредственно к разработке VK.pm.

Для наглядности приведу пример использования. В самом кратком варианте это выглядит так:
use VK;

my $vk = VK->new('vkaccount@email.com', 'mypassword', undef, 1234);
print $vk->wallPost(
    message         => "Hello World!",
    link            => "http://code.google.com/p/vkontakte-non-api-manager",
    photo           => "sample.jpg"
)?':-)':':-(';



Далее более подробный и полный пример с комментариями:

use VK;

# последние 4 цифры вашего телефона привязанного к аккаунту вК
my $security_code = 1234;

# если хотим работать со стеной группы указываем вместо undef адрес стенки (напр. "/mygroupaddress")
my $walluri = undef;

# логинимся (email/телефон, пароль)
my $vk = VK->new('vkaccount@email.com', 'mypassword', $walluri, $security_code);

print $vk->wallPost(
    message         => "Hello World!", # наш пост собственно
    #to_id           => 1234456, # id стенки/юзера куда хотим постить, если пусто то себе или в группу

    link            => "http://code.google.com/p/vkontakte-non-api-manager", # ссылка
    link_title      => "This is the title of the link popup", # заголовок во всплывающем окне ссылки
    link_desc       => "This is the content of link popup", # контент для всплывающего окна ссылки
    
    signed          => '', # 1/0 - подписывать или нет посты

    photo           => "sample.jpg", # фотка, имя файла для поста
    album           => "This is the new album", # название альбома куда качать фоту, создаст альбом если его нет
    album_desc      => "This is description of a new album", # описание альбома
    album_view      => 0, # виден альбом: 0-всем, 1-друзьям, 2-друзьям друзей, 3-себе
    album_comments  => 0, # каменты к альбому: 0-всем, 1-друзьям, 2-друзьям друзей, 3-себе
)?':-)':':-(';



Как видно, код ничего сложного из себя не представляет, можно сразу использовать. Отмечу лишь что $security_code нужен для случаев когда ВК предполагает что мы «робот» и просит ввести последние 4 цифры номера телефона. Возможно иногда ВК требует и капчу, пока не попадалась.

Сам модуль VK.pm реализован через упомянутый выше модуль WWW::Mechanize и его расширение WWW::Mechanize::GZip для обработки «сжатых» страниц. Никакого API вконтакта мы не используем, соответственно никаких ключей нам получать не нужно. Через скриптовый браузер мы эмулируем действия реального пользователя зашедшего в свой аккаунт.

Дополнительно имеется ещё несколько функций которые могут оказаться полезными:

# создание альбома
$vk->createAlbum("Название альбома", "Описание альбома");


# закачиваем фото в альбом
$vk->addPhoto("photo.jpg", "Название альбома куда положить фото", "Описание альбома", 0, 0);


# если вдруг нужно залогиниться в другой акк
$vk->login('vkaccount@email.com', 'mypassword', $walluri, $security_code);



Если не указывать название альбома при закачке картинок, то модуль создаст его сам с названием "#shared".

В будущем предполагается дополнить модуль дополнительными функциями управления аккаунтом.

Пока это всё.

Исходный код с примерами можно найти здесь:
http://search.cpan.org/~leonmedia/VK/lib/VK.pm

или здесь:
http://code.google.com/p/vkontakte-non-api-manager

Спасибо за внимание. Буду рад если модуль окажется полезен.

Добавлено: 19 Февраля 2015 08:39:40 Добавил: Андрей Ковальчук

Автоматическое оповещение читателей о новостях с помощью ВКонтакте. Часть 4

Для этого есть официальный API от вконтакте.

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

Начнем с регистрации приложения

На странице для разработчиков выбираем «Подключить сайт».

Тип приложения — Standalone. С ним у нас будет доступно больше методов, которые не доступны если подключать просто веб-сайт.



Дальше мы видим страницу управления приложением:



Вы можете подробнее прочитать о авторизации приложений в документации.

После регистрации для работы нам необходимо получить access token.

Для этого, и остальной работы с API будем использовать написанный мною класс.[/B]

В классе нужно прописать client_id:



Выполняем метод авторизации:

VkApi::auth(array('offline', 'wall'));


В метод auth() можно указать массив прав приложения. Весь список можно посмотреть в [URL=http://vk.com/developers.php?oid=-1&p=%D0%9F%D1%80%D0%B0%D0%B2%D0%B0_%D0%B4%D0%BE%D1%81%D1%82%D1%83%D0%BF%D0%B0_%D0%BF%D1%80%D0%B8%D0%BB%D0%BE%D0%B6%D0%B5%D0%BD%D0%B8%D0%B9]документации
.
Хочу заметить, что полученный в итоге access token мы будем прописывать в тело класса. А так как обычная сессия живет не долго — нам придется постоянно обновлять токен. Есть выход — добавить в список необходимых прав offline-право. Тогда мы получим долгоживущую сессию, которая собъется только если будет сменен пароль текущего пользователя.

Запускаем скрипт в браузере и видим страницу авторизации:



Дальше запрос доступа приложения:



После разрешения на выходе нам дают все необходимые параметры:
access_token=%access_token%&expires_in=0&user_id=%userid%

Прописываем access token внутрь класса:



Всё! Готово, теперь мы можем использовать методы API.
В документации есть список и описание методов.

Например, отправляем сообщение на стену. Если отправляем на страницу или группу, в owner_id нужно добавить минус перед номером страницы. Чтобы писать от имени страницы указывается from_group.

VkApi::invoke('wall.post', array(
    'owner_id' => '%id%',
    'message' => '%message%',
    'from_group' => 1
));


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

UPD Обновил класс и примеры.

Добавлено: 19 Февраля 2015 08:35:25 Добавил: Андрей Ковальчук

Автоматическое оповещение читателей о новостях с помощью ВКонтакте. Часть 3

Скрипт замечательно работает на локальном компьютере. Но когда его закидываешь на хостинг он перестает отсылать сообщения.

Поиски проблемы

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

Первое я отбросил, т.к. был уверен, что скрипт не содержит никаких мудреных конструкций, которые не могут работать на нормально настроенном хостинге. Мне показалось, что «вконтакте» блокирует айпи адреса некоторых хостеров, но это было бы полным извращением для администраторов «вконтакте».

Причина оказалось совсем банальной. После нескольких в пустую потраченных часов я решил вывести, что же выдает в ответ сервис «вконтакте»:



Теперь стало очевидно, что действительно это блокировка. Теперь осталось ее обойти.

Решение проблемы

Первое, что нам понадобиться – это последние четыре цифры номера телефона, как написано в сообщении. Чтобы посмотреть заголовки ответа при отправке кода, я воспользовался анонимайзером и инспектором браузера:



Видно, что нам необходимы для отправки данного кода дополнительно два параметра: «hash» и «to», которые можно получить на странице с запросом этого кода (т.к. эти данные отправляются через AJAX, то они написаны в JS коде).

Выражения для парсинга я составил следующие:

preg_match_all('/hash: \'(.+?)\'/i', $r, $f4);
preg_match_all('/[, ]to: \'(.+?)\'/i', $r, $f5);


И добавил их в функцию getParams():

private function getParams() 
{
$c=$this->getCurl(); 
curl_setopt($c, CURLOPT_REFERER, 'http://vkontakte.ru/settings.php');
curl_setopt($c, CURLOPT_URL, $this->wallURL); //запрос стены для поиска на ней информации
$r = curl_exec($c); 
preg_match_all('/"post_hash":"(\w+)"/i', $r, $f1);
preg_match_all('/"user_id":(\d+),/i', $r, $f2);
preg_match_all('/handlePageParams\(\{"id":(\d+),/i', $r, $f3);
preg_match_all('/hash: \'(.+?)\'/i', $r, $f4);
preg_match_all('/[, ]to: \'(.+?)\'/i', $r, $f5);
$f = array(
'post_hash' => @$f1[1][0], //необходим для успешного поста 
'user_id' => @$f2[1][0], 
'my_id' => @$f3[1][0], //id залогиненного пользователя
'hash' => @$f4[1][0],
'to' => @$f5[1][0]);
if ($this->wallId=="") 
$this->wallId=$f["my_id"];
return $f;
}


Т.е. теперь мы получаем со страницы кроме айдишников еще и эти два параметра.

Теперь можно сделать функцию, для ввода этого кода:

private function check($params) {
$c = curl_init(); 
$params = 'code='. $this->code. '&act=security_check&to='. $params['to']. '&hash=' .$params['hash']. '&al_page=';
curl_setopt($c, CURLOPT_URL,'http://vkontakte.ru/login.php'); 
curl_setopt($c, CURLOPT_RETURNTRANSFER, 1); 
@curl_setopt($c, CURLOPT_FOLLOWLOCATION, 1); 
curl_setopt($c, CURLOPT_COOKIEJAR, $this->_cookies); 
curl_setopt($c, CURLOPT_COOKIEFILE, $this->_cookies); 
curl_setopt($c, CURLOPT_POST, 1); 
curl_setopt($c, CURLOPT_USERAGENT, $this->userAgent);
curl_setopt($c, CURLOPT_POSTFIELDS, $params);

$this->execCurl($c, 'check');

return true;
}


В ней используется свойство класса $this->code, которое я объявил выше.

Это свойство устанавливается в конструкторе, поэтому теперь создание класса происходит следующим образом:

$vk = new vk_wallpost($login, $pass, $code, $wallURL, $wallId="");


Использование

Теперь скрипт работает и на хостинге. Но все же в нем осталась одна проблема: если параметры авторизации сервиса «вконтакте» изменятся, скрипт перестанет работать.

Добавлено: 19 Февраля 2015 08:27:11 Добавил: Андрей Ковальчук

Автоматическое оповещение читателей о новостях с помощью ВКонтакте. Часть 2

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

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

Официальная страница

Вы должны входить в состав администраторов страницы для возможности публикации новостей. Рассмотрим нужны нам параметры, которые передаются al_wall.php при публикации простого сообщения:

act => 'post', 'delete'
al => 1
facebook_export => ''
status_export => ''
friends_only => ''
hash => ''
message => ''
note_title => ''
official => ''
to_id   => ''
type => 'all'
media   => ''
media_type => 'photo'


- В параметре act мы передаем необходимо действие — либо 'post' для публикации сообщения, либо 'delete' для его удаления. Причем, при удалении необходимо так же передать параметр 'post', который содержит id страницы и порядковый номер сообщения на стенке, например, 11111_32;
- Параметры facebook_export и status_export служат для экспорта в сервисы Facebook и Twitter, если таковые прилинкованы к странице;
- Параметр hash нам уже знаком, его можно найти при открытии страницы или группы в блоке с параметрами под названием post_hash;
Стоит заметить, что параметр note_title опускается, то есть заметки создано не будет, вместо этого вашу запись обрежут, если она превысит допустимый размер;
- В to_id стоит передавать ваш id Официальной страницы или группы со знаком минус впереди, например, '-11111';
- Осталось рассмотреть media и media_type, в первом следует передавать внутреннюю ссылку на нужный объект, а во втором его тип. Например, media => '1111_1213232213', media_type => 'photo'. В данном случае к записи будет прикреплена указанная фотография. Тип так же может быть 'audio', 'video'. Если передать тип 'share', а в 'media' указатель на фото, то в сообщении-ссылке данная фотка будет в подсказке. Только нужно не забыть добавить еще три известных нам параметра для ссылки — url, title и description

Увы, но пока я не нашел способа публиковать сообщение-фото совместно с сообщением-ссылкой как это сделано на страничке Хабрахабр ВКонтакте.

Загрузка фотографии

На самом деле при загрузке картинки главное не забывать о прокси, а дальше все достаточно просто. Итак, чтобы загрузить картинку мы воспользуемся файлом share.php, который предназначен для публикации внешних ссылок. Вот пример POST параметров, которые необходимо на него передать:

act => a_photo
url =>
image =>
extra =>


В параметре url передаете вашу ссылку, в параметре image — ссылку на нужную картинку, а парамерт extra можно проигнорировать. Отмечу, что если вы используете CURL, как я в прошлой части, то не забудьте следующее:

curl_setopt($ch, CURLOPT_REFERER, 'http://vkontakte.ru/share.php');
curl_setopt($ch, CURLOPT_FOLLOWLOCATION, 1);


Второй параметр нам нужен, так как после отправки POST запроса, share.php перенаправит запрос (путем возврата 302 ошибки и передачи параметра Location) на upload.php с нужным сервером и необходимыми параметрами, рассматривать которые, я думаю, не имеет смысла. В свою очередь upload.php перенаправит запрос на complete.php, который, в зависимости от успеха, выдаст либо ошибку, либо нужный нам результат.

Вариант с ошибкой:

<script type="text/javascript">
document.domain = location.host.toString().match(/[a-zA-Z]*\.[a-zA-Z]*$/)[0];
parent.onUploadFail(0, 'Неизвестная ошибка');
</script>


Это значит, что скорее всего ваша ссылка на картинку неверна или невалидна. Вариант, который нам нужен выглядит так:

<script type="text/javascript">
document.domain = location.host.toString().match(/[a-zA-Z]*\.[a-zA-Z]*$/)[0];
parent.onUploadDone(0, {"user_id":1234567,"photo_id":235889241});
</script>


Вот он, тот самый photo_id, который нужно передавать при размещении фотографии на стенке, в данном случае полный составной id: 1234567_235889241. Просто, неправда ли?

Чтобы получить прямой путь к загруженной фотке необходимо послать запрос к al_photo.php, с работой которого я пока до конца не разобрался, в частности пока не ясно какой именно hash он требует для проверки запроса. Если кто-то разберется — сообщите:).

Стоит заметить, что можно разместить ссылку с картинкой в подсказке не только через al_wall.php как я описал выше, но и через share.php, для этого нужно снова послать запрос к share.php с большим количеством параметров. Поставлю коментарии, там где могут возникнуть трудности:

act: 'a_submit'     // метод
hash: shareHash     // параметр функции onDomReady, который можно найти на странице share.php при открытии, полное название window.shareHash
title:              // наименование заметки
url:                // ссылка
share_title:        // название статуса
share_text:         // описание статуса
share_comment:      // ваш комментарий
image_url:          // ссылка на картинку, если загрузка картинки вернула onUploadFail
photo_owner_id:     // id загрузившего фото
photo_id:           // id, полученный нами выше
privacy_note: 0     // можно просто поставить 0
privacy_notecomm: 0 // можно просто поставить 0
to_status: 1        // 1, если публикуем статус
status_export:      // экспорт статуса в твиттер
to_note:            // пусто, если публикация в статус и 1, если в заметку


Вообще через share.php можно публиковать видео и аудио, загружать их на сервер через параметры extra и extra_data, однако я не вижу в этом острой необходимости.

Думаю, что пока этого более чем достаточно, когда найду способ совмещения в одном сообщении ссылки и фото, то напишу каким образом это можно сделать. Или буду надеяться, что они откроют такую функциональность для Групп и Официальных страничек. Замечу только, что если загружать фотку через интерфейс ВКонтакте на Официальной странице, то photo_id будет присвоен вида '[-id страницы]_[номер_фото]', а при просмотре фотки Автором будет ваша страница или группа.

Такого эффекта можно добиться и через скрипт, если передавать параметры не на share.php, а напрямую на upload.php, но в этом случае нам нужно знать еще кучу параметров, в том числе два разных hash-параметра, поэтому этот способ я рассматривать не стал.

Отдельная просьба задавать вопросы по теме в комментариях, а не через личную почту, потому что большинство вопросов одинаковы и приходится объяснять одно и то же каждый раз :).


UPD: По просьбам в комментариях набросал простенький класс и пример использования.
Брать тут: github.com/xbreaker/vk.wallpost

Добавлено: 19 Февраля 2015 08:23:07 Добавил: Андрей Ковальчук