Как стать автором
Обновить

Vreen — простая и удобная библиотека для работы с vk.api

Время на прочтение7 мин
Количество просмотров39K

Представляю вам новую Qt библиотеку для работы с vk api, которая может пригодиться вам при создании любых настольных и мобильных приложений, взаимодействующих с vk. Проект родился из vkontakte плагина для qutIM'а и перерос в отдельную независимую библиотеку, которой теперь может пользоваться каждый.


Краткий обзор



Vreen это обёртка над vk.com api, написанная на C++11/Qt, предназначенная для быстрого и простого создания мобильных и настольных приложений, а также встраивания взаимодействия с vk.com api в уже существующие приложения. Библиотека поддерживает несколько способов авторизации и позволяет при необходимости добавлять свои. Также в vreen существует полноценная привязка к qml, что позволяет создавать приложения, не написав ни единой строчки на C++. Для уменьшения количества обязательных зависимостей все способы авторизации вынесены в отдельные статические библиотеки.

Основные возможности:

  1. Поддержка любых видов диалогов в том числе и групповых чатов.
  2. Поддержка просмотра новостных лент, стен.
  3. Работа с комментариями, в том числе добавления, лайки и репосты.
  4. Поддержка работы с вложениями.
  5. Поддержка ростера в том числе отслеживание статуса собеседников.
  6. Поддержка аудиозаписей.
  7. Возможность напрямую работать с API из qml в привычном для многих XHTTPRequest стиле.
  8. Расширяемость.
  9. Свободная LGPL лицензия, позволяющая использовать библиотеку с проприетарными приложениям.


Основы:

Базовые классы для работы с API:
  • Connection — класс, основанный на QNetworkAccessManager'е, выполняющий авторизацию и непосредственно реализующий запросы к API.
  • Reply — класс отслеживает выполнение запроса.
  • Longpoll — класс, выполняющий опрос сервера на предмет событий.
  • Roster — класс, управляющий списком друзей.
  • Client — базовый класс, который сшивает всё в единое целое.

Выполнение запросов к API:
Простой запрос:
    QVariantMap args;
    args.insert("uids", join(ids));
    args.insert("fields", fields.join(","));
    auto reply = d->client->request("users.get", args);
    reply->connect(reply, SIGNAL(resultReady(const QVariant&)),
                   this, SLOT(_q_friends_received(const QVariant&)));

Сигнал resultReady кидается в случае завершения запроса и содержит в response сконвертированный в QVariant json ответ от сервера или ничего в случае ошибки, код ошибки в этом случае содержится в reply->error()
Помимо этого можно скрыть обработку результата от получателя, добавив в reply функцию-обработчик результата, в этом случае результат будет можно получить при помощи вызова метода reply->result(), результат будет возращен в виде QVariant'а, поэтому его нужно будет еще дополнительно преобразовать при помощи QVariant::fromValue.
     QVariantMap args;
    //TODO add chat messages support and contact check
    args.insert("uid", message.toId());
    args.insert("message", message.body());
    args.insert("title", message.subject());
	return request ("messages.send", args, [](const QVariant &response) -> QVariant { return response.toInt(); });

Для того, чтобы четко обозначить тип возращаемого значения можно применить следующую шаблонную магию:
     typedef ReplyBase<int> SendMessageReply;
     QVariantMap args;
    //TODO add chat messages support and contact check
    args.insert("uid", message.toId());
    args.insert("message", message.body());
    args.insert("title", message.subject());
	return request<SendMessageReply> ("messages.send", args, [](const QVariant &response) -> QVariant { return response.toInt(); });

В результате в слоте SendMessageReply::result() будет возращать int вместо QVariant'а.
	auto reply = static_cast<Vreen::SendMessageReply*>(sender());
	m_unreachedMessagesCount--;
	int mid = reply->result();

В целом, все взаимодействие с API получается простым и прозрачным.

Получение и сборка


Vreen использует субмодули и поэтому самым оптимальным способом получения исходников является.
$ git clone git://github.com/gorthauer/vreen.git
$ cd vreen
$ git submodule update --init

Для сборки необходимы cmake 2.8, Qt 4.7.4, и любой компилятор с базовой поддержкой С++11 (gcc 4.4+, clang 3.0+, msvc2010+).
Сама сборка достаточно тривиальна:
$ mkdir build
$ cd build
$ cmake .. -DCMAKE_INSTALL_PREFIX=/usr
$ make
$ make install (sudo)

Если будут возникать вопросы относительно сборки релизной или дебажной версии, то лучше просто обратитесь к документации по cmake. В качестве экспериментального варианта поддерживается сборка при помощи qbs. Существует возможность собрать библиотеку при помощи qmake', но данный способ не является официально поддерживаемым.

Использование в C++ проекте


Подключение

Для подключения к другим проектам в Vreen'е используется pkg-config, поэтому для подключения vreen'а со стандартым методом авторизации через браузер достаточно добавить в pro файл строчки.
CONFIG += link_pkgconfig
PKGCONFIG += vreen \
        vreenoauth 

В случае систем, которые не поддерживают pkgconfig, можно или вручную добавить LIBS и INCLUDEPATH или написать find скрипт для cmake или qbs.
Желающие могут помочь мне написать prf файл для Qt.

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

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

Для начала просто инициализируем клиента, указав ему способ подключения, идентификатор приложения, а также попросим его самостоятельно создавать окошко с разрешением для доступа из приложения к api.
    auto auth = new Vreen::OAuthConnection(3220807, this);  //в качестве clientId используйте собственный 
    auth->setConnectionOption(Vreen::Connection::ShowAuthDialog, true); //заставляет клиент самостоятельно создавать окошко авторизации.
    auth->setConnectionOption(Vreen::Connection::KeepAuthData, true); //заставляет хранить токен между сессиями
    setConnection(auth);

    connect(this, SIGNAL(onlineStateChanged(bool)), SLOT(onOnlineChanged(bool))); 
    connect(roster(), SIGNAL(syncFinished(bool)), SLOT(onSynced(bool)));

После того, как клиент успешно подключится к api, будет вызван слот onOnlineChanged в котором мы запросим у ростера список друзей. При запросе можно выбирать поля, которые хочется получить. Существует несколько макросов с наиболее распространёнными вариантами полей VK_COMMON_FIELDS, VK_EXTENDED_FIELDS и VK_ALL_FIELDS для всех известных полей.
Описание полей и получаемых свойств можно прочитать здесьu.
В данном случае нас интересует всего три поля и нет необходимости напрягать сервер более тяжелыми запросами.
        roster()->sync(QStringList() << QLatin1String("first_name")
                       << QLatin1String("last_name")
                       << QLatin1String("contacts")); 

После успешного завершения синхронизации ростера просто выведем на экран результат:
        qDebug() << tr("-- %1 contacts recieved").arg(roster()->buddies().count());
        foreach (auto buddy, roster()->buddies()) {
            QString homeNumber = buddy->property("_q_home_phone").toString();
            QString mobileNumber = buddy->property("_q_mobile_phone").toString();
            qDebug() << tr("name: %1, home: %2, mobile: %3").arg(buddy->name())
                        .arg(homeNumber.isEmpty() ? tr("unknown") : homeNumber)
                        .arg(mobileNumber.isEmpty() ? tr("unknown") : mobileNumber);
        }

Все свойства, которые еще не реализованы штатно для контактов будут иметь префикс _q_, остальные же можно получить через getter'ы или через имена, указанные в Q_PROPERTY.

В качестве второго примера попробуем воспользоваться qml api и получим список друзей.
Импортируем в qml поддержку Vreen'а:
import com.vk.api 1.0

И создадим клиента:
    Client {
        id: client
        connection: conn //используем в качестве подключения и авторизации известный по C++ примеру метод
    }

    OAuthConnection {
        id: conn

        Component.onCompleted: { //Не забудем установить для него те же самые свойства, что и в случае с photoview'ером
            setConnectionOption(Connection.ShowAuthDialog, true); 
            setConnectionOption(Connection.KeepAuthData, true);
        }

        clientId: 3220807
        displayType: OAuthConnection.Popup //из всех окошек авторизации это оказалось наиболее удобным
    }

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

Для отображения списка диалогов можно воспользоваться готовой моделью из com.vk.api 1.0:
    DialogsModel {
        id: dialogsModel
        client: client
    }

Для получения списка последних диалогов достаточно просто после подключения запросить список:
    Connections {
        target: client

        onOnlineChanged: {
            if (client.online) {
                client.roster.sync();
                dialogsModel.getDialogs(0, //смещение относительно последнего элемента  
                                15, //число элементов
                                160 //максимальная длина сообщения
                );
            }
        }
    }

Доступные для делегата свойства модели можно подсмотреть в коде её реализации:
    roles[SubjectRole] = "subject"; 
    roles[BodyRole] = "body";
    roles[FromRole] = "from";
    roles[ToRole] = "to";
    roles[ReadStateRole] = "unread"; 
    roles[DirectionRole] = "incoming";  
    roles[DateRole] = "date";
    roles[IdRole] = "mid";
    roles[AttachmentRole] = "attachments";
    roles[ChatIdRole] = "chatId";

С помощью использования этих полей можно получить вполне опрятный на вид список диалогов:


Аналогичным образом создадим фотоальбом:

Причем хочется заметить, что в случае создания фотоальбома мы работаем через qml напрямую с vk.api
    function getAll(ownerId, count, offset) {
        if (!offset)
            offset = 0;
        if (!count)
            count = 200;

        var args = {
            "owner_id"  : ownerId,
            "offset"    : offset,
            "count"     : count,
            "extended"  : 0
        }
        var reply = client.request("photos.getAll", args)
        reply.resultReady.connect(function(response) {
            var count = response.shift()
            for (var index in response) {
                var photo = response[index]
                model.append(photo)
            }
        })
    }

Получается очень кратко и лаконично.

Заключение


Текущая версия vreen'а — 0.9.5, то есть фактически это уже почти релиз, а это значит, что API по большей части уже не будет меняться и будет поддерживаться бинарная совместимость между версиями, а значит можно уже смело присоединяться к использованию и тестированию в реальных условиях.
На данный момент поддержка API еще очень неполная, но само написание оберток над ним максимально упрощено, так, что буду рад всем патчам с реализацией различных возможностей.
По остальным вопросам готов ответить в личку или в jabber'е, который можно найти в профиле.
Fork me with github.
Теги:
Хабы:
+63
Комментарии46

Публикации

Истории

Работа

QT разработчик
7 вакансий

Ближайшие события