7even / vkontakte_api

Ruby-адаптер для ВКонтакте API
http://7even.github.com/vkontakte_api
MIT License
282 stars 67 forks source link

vkontakte_api Build Status Gem Version Dependency Status Code Climate Gitter

vkontakte_api — ruby-адаптер для ВКонтакте API. Он позволяет вызывать методы API, загружать файлы на сервера ВКонтакте, а также поддерживает все 3 доступных способа авторизации (при этом позволяя использовать стороннее решение).

English version

For English version please go here.

Установка

# Gemfile
gem 'vkontakte_api', '~> 1.4'

или просто

$ gem install vkontakte_api

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

Вызов методов

# создаем клиент
@vk = VkontakteApi::Client.new
# и вызываем методы API
@vk.users.get(user_ids: 1)

# в ruby принято использовать snake_case в названиях методов,
# поэтому likes.getList становится likes.get_list
@vk.likes.get_list
# также названия методов, которые возвращают '1' или '0',
# заканчиваются на '?', а возвращаемые значения приводятся
# к true или false
@vk.is_app_user? # => false

# если ВКонтакте ожидает получить параметр в виде списка,
# разделенного запятыми, то его можно передать массивом
users = @vk.users.get(user_ids: [1, 2, 3])

# большинство методов возвращает структуры Hashie::Mash
# и массивы из них
users.first.uid        # => 1
users.first.first_name # => "Павел"
users.first.last_name  # => "Дуров"

# если метод, возвращающий массив, вызывается с блоком,
# то блок будет выполнен для каждого элемента,
# и метод вернет обработанный массив
fields = [:first_name, :last_name, :screen_name]
@vk.friends.get(user_id: 2, fields: fields) do |friend|
  "#{friend.first_name} '#{friend.screen_name}' #{friend.last_name}"
end
# => ["Павел 'durov' Дуров"]

Загрузка файлов

Загрузка файлов на сервера ВКонтакте осуществляется в несколько этапов: сначала вызывается метод API, возвращающий URL для загрузки, затем происходит сама загрузка файлов, и после этого в некоторых случаях нужно вызвать другой метод API, передав в параметрах данные, возвращенные сервером после предыдущего запроса. Вызываемые методы API зависят от типа загружаемых файлов и описаны в соответствующем разделе документации.

Файлы передаются в формате хэша, где ключом является название параметра в запросе (указано в документации, например для загрузки фото на стену это будет photo), а значением — массив из 2 строк: полный путь к файлу и его MIME-тип:

url = 'http://cs303110.vkontakte.ru/upload.php?act=do_add'
VkontakteApi.upload(url: url, photo: ['/path/to/file.jpg', 'image/jpeg'])

Если загружаемый файл доступен как открытый IO-объект, его можно передать альтернативным синтаксисом — IO-объект, MIME-тип и путь к файлу:

url = 'http://cs303110.vkontakte.ru/upload.php?act=do_add'
VkontakteApi.upload(url: url, photo: [file_io, 'image/jpeg', '/path/to/file.jpg'])

Метод вернет ответ сервера ВКонтакте, преобразованный в Hashie::Mash; его можно использовать при вызове метода API на последнем этапе процесса загрузки.

Авторизация

Для вызова большинства методов требуется токен доступа (access token). Чтобы получить его, можно использовать авторизацию, встроенную в vkontakte_api, либо положиться на какой-то другой механизм (например, OmniAuth). В последнем случае в результате авторизации будет получен токен, который нужно будет передать в VkontakteApi::Client.new.

Для работы с ВКонтакте API предусмотрено 3 типа авторизации: для сайтов, для клиентских приложений (мобильных либо десктопных, имеющих доступ к управлению браузером) и специальный тип авторизации серверов приложений для вызова административных методов без авторизации самого пользователя. Более подробно они описаны тут; рассмотрим, как работать с ними средствами vkontakte_api.

Для авторизации необходимо задать параметры app_id (ID приложения), app_secret (защищенный ключ) и redirect_uri (адрес, куда пользователь будет направлен после предоставления прав приложению) в настройках VkontakteApi.configure. Более подробно о конфигурировании vkontakte_api см. далее в соответствующем разделе.

Сайт

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

В соответствии с рекомендациями в протоколе OAuth2 для защиты от CSRF, нужно передать параметр state, содержащий случайное значение.

session[:state] = Digest::MD5.hexdigest(rand.to_s)
redirect_to VkontakteApi.authorization_url(scope: [:notify, :friends, :photos], state: session[:state])

После подтверждения пользователь перенаправляется на указанный в настройках redirect_uri, причем в параметрах будет передан код, по которому можно получить токен доступа, а также переданный ранее state. Если state не совпадает с тем, который был использован при отправлении пользователя на ВКонтакте, то скорее всего это попытка CSRF-атаки — стоит отправить пользователя на повторную авторизацию.

redirect_to login_url, alert: 'Ошибка авторизации' if params[:state] != session[:state]

vkontakte_api предоставляет метод VkontakteApi.authorize, который делает запрос к ВКонтакте, получает токен и создает клиент; нужно лишь передать ему код:

@vk = VkontakteApi.authorize(code: params[:code])
# и теперь можно вызывать методы API на объекте @vk
@vk.is_app_user?

Клиент будет содержать id пользователя, авторизовавшего приложение; его можно получить с помощью метода VkontakteApi::Client#user_id:

@vk.user_id # => 123456

Также в этот момент полезно сохранить полученный токен (и, при необходимости, id пользователя) в БД либо в сессии, чтобы использовать их повторно:

current_user.token = @vk.token
current_user.vk_id = @vk.user_id
current_user.save
# позже
@vk = VkontakteApi::Client.new(current_user.token)
Клиентское приложение

Авторизация клиентского приложения несколько проще — не нужно получать токен отдельным запросом, он выдается сразу после редиректа пользователя.

# пользователь направляется на следующий урл
VkontakteApi.authorization_url(type: :client, scope: [:friends, :photos])

Необходимо принимать во внимание, что redirect_uri нужно выставлять на http://api.vkontakte.ru/blank.html, иначе не получится вызывать методы, доступные клиентским приложениям.

Когда пользователь подтвердит права приложения, он будет перенаправлен на redirect_uri, при этом в параметре access_token будет токен, который нужно передать в VkontakteApi::Client.new.

Сервер приложения

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

@vk = VkontakteApi.authorize(type: :app_server)

Прочее

Если клиент API (объект класса VkontakteApi::Client) был создан с помощью метода VkontakteApi.authorize, он будет содержать информацию об id текущего пользователя (user_id) и о времени истечения токена (expires_at). Получить их можно с помощью соответствующих методов:

vk = VkontakteApi.authorize(code: 'c1493e81f69fce1b43')
# => #<VkontakteApi::Client:0x007fa578f00ad0>
vk.user_id    # => 628985
vk.expires_at # => 2012-12-18 23:22:55 +0400
# можно проверить, истекло ли время жизни токена
vk.expired?   # => false

Также можно получить список прав доступа, которые дает данный токен, в виде, аналогичном формату параметра scope в авторизации:

vk.scope # => [:friends, :groups]

Это работает на основе метода getUserSettings, причем результат запоминается после первого обращения.

Чтобы создать короткий синоним VK для модуля VkontakteApi, достаточно вызвать метод VkontakteApi.register_alias:

VkontakteApi.register_alias
VK::Client.new # => #<VkontakteApi::Client:0x007fa578d6d948>

При необходимости можно удалить синоним методом VkontakteApi.unregister_alias:

VK.unregister_alias
VK # => NameError: uninitialized constant VK

Обработка ошибок

Если ВКонтакте API возвращает ошибку, выбрасывается исключение класса VkontakteApi::Error.

vk = VK::Client.new
vk.friends.get(user_id: 1, fields: [:first_name, :last_name, :photo])
# VkontakteApi::Error: VKontakte returned an error 7: 'Permission to perform this action is denied' after calling method 'friends.get' with parameters {"user_id"=>"1", "fields"=>"first_name,last_name,photo"}.

Особый случай ошибки — 14: необходимо ввести код с captcha. В этом случае можно получить параметры капчи методами VkontakteApi::Error#captcha_sid и VkontakteApi::Error#captcha_img — например, так.

Логгирование

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

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

ключ настройки по умолчанию уровень логгирования
URL запроса log_requests true debug
JSON ответа при ошибке log_errors true warn
JSON удачного ответа log_responses false debug

Таким образом, в rails-приложении с настройками по умолчанию в production записываются только ответы сервера при ошибках; в development также логгируются URL-ы запросов.

Пример использования

Пример использования vkontakte_api совместно с eventmachine можно посмотреть здесь.

Также был написан пример использования с rails, но он больше не работает из-за отсутствия прав на вызов метода newsfeed.get.

Настройка

Глобальные параметры vkontakte_api задаются в блоке VkontakteApi.configure следующим образом:

VkontakteApi.configure do |config|
  # параметры, необходимые для авторизации средствами vkontakte_api
  # (не нужны при использовании сторонней авторизации)
  config.app_id       = '123'
  config.app_secret   = 'AbCdE654'
  config.redirect_uri = 'http://example.com/oauth/callback'

  # faraday-адаптер для сетевых запросов
  config.adapter = :net_http
  # HTTP-метод для вызова методов API (:get или :post)
  config.http_verb = :post
  # параметры для faraday-соединения
  config.faraday_options = {
    ssl: {
      ca_path:  '/usr/lib/ssl/certs'
    },
    proxy: {
      uri:      'http://proxy.example.com',
      user:     'foo',
      password: 'bar'
    }
  }
  # максимальное количество повторов запроса при ошибках
  # работает только если переключить http_verb в :get
  config.max_retries = 2

  # логгер
  config.logger        = Rails.logger
  config.log_requests  = true  # URL-ы запросов
  config.log_errors    = true  # ошибки
  config.log_responses = false # удачные ответы

  # используемая версия API
  config.api_version = '5.21'
end

По умолчанию для HTTP-запросов используется Net::HTTP; можно выбрать любой другой адаптер, поддерживаемый faraday.

ВКонтакте позволяет использовать как GET-, так и POST-запросы при вызове методов API. По умолчанию vkontakte_api использует POST, но в настройке http_verb можно указать :get, чтобы совершать GET-запросы.

При необходимости можно указать параметры для faraday-соединения — например, параметры прокси-сервера или путь к SSL-сертификатам.

Чтобы при каждом вызове API-метода передавалась определенная версия API, можно указать ее в конфигурации в api_version. По умолчанию версия не указана.

Чтобы сгенерировать файл с настройками по умолчанию в rails-приложении, можно воспользоваться генератором vkontakte_api:install:

$ cd /path/to/app
$ rails generate vkontakte_api:install

JSON-парсер

vkontakte_api использует парсер Oj — это единственный парсер, который не показал ошибок при парсинге JSON, генерируемого ВКонтакте.

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

Roadmap

Участие в разработке

Если вы хотите поучаствовать в разработке проекта, форкните репозиторий, положите свои изменения в отдельную ветку, покройте их спеками и отправьте мне pull request.

vkontakte_api тестируется под MRI 2.1, 2.2, 2.3 и 2.4, а также JRuby 9.x. Если в одной из этих сред что-то работает неправильно, либо вообще не работает, то это следует считать багом, и написать об этом в issues на Github.