Загрузка...

[SOFT] Lesson. Working with Steam WEB API in C++ Qt

Thread in C/C++ created by DSTR2 Dec 1, 2018. 3787 views

  1. DSTR2
    DSTR2 Topic starter Dec 1, 2018 ? 49 May 19, 2017
    Доброго времени суток!
    Хотел бы немного рассказать (и показать) о взаимодействии со Steam WEB Api средствами Qt и C++.
    Пишите, если хотите продолжение: статью о работе с инвентарем Steam: получение цены предметов и т.п
    Буду очень благодарен за префикс "Авторская статья".



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

    Ниже пример окончательной программы:
    [IMG]

    Исходный код, а также скомпилированный проект доступен по ссылке: https://goo.gl/TCivNm
    ВЕРСИИ:
    — SteamUserInfoFull — скомпилированная полная версия этой программы (как на скриншоте выше).
    — SteamUserInfoLite — скомпилированная версия из статьи.
    — SteamUserInfoSource — исходный код из статьи.

    В этой статье описана самая малая часть, так как всего очень много и за один раз всё не покажешь.
    Мы не будем рассматривать то, как устроен формат ответа Json-файла от Steam.
    Всё это можете посмотреть самостоятельно, вставив запрос в браузер.

    Я использую компилятор MinGW32.
    У вас должна быть поддержка SSL (если нет - скачайте эти два файла и добавьте в папку с компилируемым проектом): https://yadi.sk/d/lZZrpzoH3afXY8

    Итак, мы рассмотрим:
    — получение оригинальной ссылки на профиль (SteamID64);
    — получение аватарки профиля;
    — получение даты регистрации аккаунта;
    — получение даты последней активности;
    — получение статуса VAC, количество игровых блокировок, дней с момента последней блокировки и статус профиля в сообществе Steam (в простонародье — КТ).


    Приступим.
    1)
    Для начала работы нам понадобится наш уникальный API ключ для доступа к сервисам Steam.
    Чтобы его получить нужен БЕЗЛИМИТНЫЙ аккаунт Steam.
    Переходим по ссылке https://steamcommunity.com/dev/apikey и вводим рандомный адрес (к примеру, qwertyabc@abcd.com), нажимаем на кнопочку регистрации и сохраняем наш ключ.

    2)
    Теперь запускаем Qt, создаем проект Qt Widgets Application -> указываем название как у меня, чтобы не было разногласий: SteamUserInfo и дальше указываете свой путь.
    Проект создался.
    В файле SteamUserInfo.pro прописываете: QT += network (скрин)
    [IMG]

    3)
    Теперь создаем первый класс. Кликаем на название проекта -> Add new... -> слева выбираем C++, а справа C++ Class.
    Назовем его SteamUserProfileLink
    [IMG]

    4)
    Реализуем наш класс.
    В файле SteamUserProfileLink.h создаем переменные и прототипы функций:
    Code

    #include <QtNetwork/QtNetwork>
    #include <QString>

    class SteamUserProfileLink
    {
    QString apiKey;
    QString steamID64;
    public:
    SteamUserProfileLink();

    void setApiKey(QString key);

    bool setConnection(const QString &steamUrl);

    QString getSteamID64() const;
    };


    Реализуем эти функции.
    Файл SteamUserProfileLink.cpp:

    Code

    #include "SteamUserProfileLink.h"

    SteamUserProfileLink::SteamUserProfileLink() { }

    void SteamUserProfileLink::setApiKey(QString key)
    {
    apiKey = key;
    }

    //true - соединение успешно: вся информация загружена.
    //false - не удалось получить ответ от сервера.
    bool SteamUserProfileLink::setConnection(const QString &steamUrl)
    {
    QNetworkAccessManager manager;
    QNetworkReply *reply = manager.get(
    //Отправляем запрос.
    QNetworkRequest(QString
    ("http://api.steampowered.com/ISteamUser/ResolveVanityURL/v0001/?key=%1&vanityurl=%2").arg(apiKey).arg(steamUrl)));

    QEventLoop loop; //Ждем ответ от сервера.
    QObject::connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    loop.exec();
    reply->deleteLater();

    const QJsonDocument document = QJsonDocument::fromJson(reply.readAll()); //Загружаем Json документ.
    //Если ответ равен 1, значит SteamID64 успешно получен.
    if (document.object().value("response").toObject().value("success").toDouble() == 1.0) {
    steamID64 = document.object().value("response").toObject().value("steamid").toString(); //Запоминаем.
    return true; //Возвращаем true: успешно.
    }
    //Иначе считаем, что такого steamUrl не существует и возвращаем false.
    return false;
    }

    QString SteamUserProfileLink::getSteamID64() const
    {
    return steamID64;
    }


    5)
    Теперь по той же схеме создаем следующий класс SteamPlayerSummaries
    Файл SteamPlayerSummaries.h:
    Code

    #include <QtNetwork/QtNetwork>
    #include <QString>
    #include <QPixmap>

    class SteamPlayerSummaries
    {
    QString apiKey;
    uint timeCreated;//Дата создания аккаунта в формате UTC.
    uint lastLogOff; //Дата последней активности в формате UTC.
    QPixmap avatar; //Аватарка профиля.
    void loadAvatar(const QString &avatarUrl);
    public:
    SteamPlayerSummaries();

    void setApiKey(QString key);

    bool setConnection(const QString &steamID64);

    QString getLastLofOff() const; //Дата последней активности в формате дата-месяц-год.
    QString getTimeCreated() const; //Дата создания профиля в формате дата-месяц-год.
    QPixmap getAvatar() const;
    };

    Файл SteamPlayerSummaries.cpp:

    Code

    #include "SteamPlayerSummaries.h"

    SteamPlayerSummaries::SteamPlayerSummaries() { }
    void SteamPlayerSummaries::setApiKey(QString key)
    {
    apiKey = key;
    }

    //true - соединение успешно: вся информация загружена.
    //false - не удалось получить ответ от сервера.
    bool SteamPlayerSummaries::setConnection(const QString &steamID64)
    {
    QNetworkAccessManager manager;
    QNetworkReply *reply = manager.get(
    //Отправляем запрос.
    QNetworkRequest(QString
    ("http://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002/?key=%1&steamids=%2").arg(apiKey).arg(steamID64)));

    QEventLoop loop; //Ждем ответ от сервера.
    QObject::connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    loop.exec();
    reply->deleteLater();
    const QJsonDocument document = QJsonDocument::fromJson(reply.readAll());

    if (document.isEmpty()) //ApiKey неправильный.
    return false;
    //Конвертируем массив в object для обращения к элементам по имени, а не индексах.
    const QJsonObject object = document.object().value("response").toObject().value("players").toArray()[0].toObject();
    if (object.isEmpty()) //SteamID не найден.
    return false;

    loadAvatar(object["avatarfull"].toString()); //Загружаем аватарку.

    timeCreated = static_cast<unsigned int>(object["timecreated"].toInt()); //Получаем дату регистрации аккаунта.

    lastLogOff = static_cast<unsigned int>(object["lastlogoff"].toInt()); //Последняя активность.

    return true; //Возвращаем true: успешно загружено.
    }

    void SteamPlayerSummaries::loadAvatar(const QString &avatarUrl) //Загрузка аватарки.
    {
    QNetworkAccessManager manager;
    QNetworkReply *reply = manager.get(QNetworkRequest(avatarUrl));
    QEventLoop loop; //Ждем ответ от сервера.
    QObject::connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    loop.exec();
    reply->deleteLater();
    const QByteArray header = reply.readAll();
    avatar.loadFromData(header,"jpg");
    }

    QString SteamPlayerSummaries::getLastLofOff() const
    {
    //Конвертируем UTC в обычное представление времени.
    static const QString c = QDateTime::fromTime_t(lastLogOff).toString("dd-MM-yyyy");
    return c;
    }

    QString SteamPlayerSummaries::getTimeCreated() const
    {
    //Конвертируем UTC в обычное представление времени.
    static const QString c = QDateTime::fromTime_t(timeCreated).toString("dd-MM-yyyy");
    return c;
    }

    QPixmap SteamPlayerSummaries::getAvatar() const
    {
    return avatar;
    }



    6)
    Теперь создадим файлик для получения информации о блокировках аккаунта.
    Создадим класс SteamPlayerBans:
    Файл SteamPlayerBans.h:
    Code

    #include <QtNetwork/QtNetwork>

    class SteamPlayerBans
    {
    QString apiKey;
    bool communityBanned; //Блокировка в сообществе (КТ).
    bool VACBanned; //Блокировка VAC.
    QString economyBanned;//Блокировка обмена.
    int numberOfGameBans;//Количество игровых блокировок.
    int numberOfVACBans; //Количество VAC блокировок.
    int daysSinceLastBan;//Дней с момента последней блокировки.
    public:
    SteamPlayerBans();

    void setApiKey(QString key);

    bool setConnection(const QString &steamID64);

    bool isCommunityBanned() const;
    bool isVACBanned() const;
    QString getEconomyBan() const;
    int getNumberOfGameBans() const;
    int getNumberOfVACBans() const;
    int getDaysSinceLastBan() const;
    };

    Реализация SteamPlayerBans.cpp

    Code

    #include "SteamPlayerBans.h"

    SteamPlayerBans::SteamPlayerBans() { }

    void SteamPlayerBans::setApiKey(QString key)
    {
    apiKey = key;
    }

    //true - соединение успешно: вся информация загружена.
    //false - не удалось получить ответ от сервера.
    bool SteamPlayerBans::setConnection(const QString &steamID64)
    {
    QNetworkAccessManager manager;
    QNetworkReply *reply = manager.get(
    //Отправляем запрос.
    QNetworkRequest(QString
    ("http://api.steampowered.com/ISteamUser/GetPlayerBans/v1/?key=%1&steamids=%2&format=json").arg(apiKey).arg(steamID64)));

    QEventLoop loop; //Ждем ответ от сервера.
    QObject::connect(&manager, &QNetworkAccessManager::finished, &loop, &QEventLoop::quit);
    loop.exec();
    reply->deleteLater();
    QJsonDocument document = QJsonDocument::fromJson(reply.readAll());

    if (document.isEmpty()) //ApiKey неправильный.
    return false;

    const QJsonObject object = document.object().value("players")[0].toObject();

    if (object.isEmpty()) //SteamID не найден.
    return false;

    communityBanned = object["CommunityBanned"].toBool();
    VACBanned = object["VACBanned"].toBool();
    economyBanned = object["EconomyBan"].toBool();
    numberOfGameBans = object["NumberOfGameBans"].toInt();
    numberOfVACBans = object["NumberOfVACBans"].toInt();
    daysSinceLastBan = object["DaysSinceLastBan"].toInt();
    return true;
    }

    bool SteamPlayerBans::isCommunityBanned() const
    {
    return communityBanned;
    }

    bool SteamPlayerBans::isVACBanned() const
    {
    return VACBanned;
    }

    QString SteamPlayerBans::getEconomyBan() const
    {
    return economyBanned;
    }

    int SteamPlayerBans::getNumberOfGameBans() const
    {
    return numberOfGameBans;
    }

    int SteamPlayerBans::getNumberOfVACBans() const
    {
    return numberOfVACBans;
    }

    int SteamPlayerBans::getDaysSinceLastBan() const
    {
    return daysSinceLastBan;
    }

    Теперь осталось самое простое — создать графический интерфейс для взаимодействия.

    Файл MainWindow.h

    Code

    #include <QMainWindow>
    #include <QLineEdit>
    #include <QWidget>
    #include <QPushButton>
    #include <QLabel>
    #include <QLayout>
    #include <QGroupBox>
    #include <QMessageBox>

    #include "SteamUserProfileLink.h"
    #include "SteamPlayerSummaries.h"
    #include "SteamPlayerBans.h"

    namespace Ui {
    class MainWindow;
    }

    class MainWindow : public QMainWindow
    {
    Q_OBJECT

    public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

    //Функция обработки.
    void setup();

    //Парс ссылки: получаем только url: steamcommunity.com/id/abcd -> abcd
    QString getUrl(QString steamProfileUrl);
    private:
    Ui::MainWindow *ui;
    };

    Файл MainWindow.cpp:
    Code

    #include "MainWindow.h"
    #include "ui_MainWindow.h"

    MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
    {
    ui->setupUi(this);

    QFont font;
    setWindowTitle("SteamUserInfo");
    font.setPointSize(10);
    setFont(font);

    QPalette palette;
    palette.setBrush(QPalette::Background, QColor(0xFCE7FC));
    setPalette(palette);

    setFixedSize(500, 600);
    setup();
    }

    MainWindow::~MainWindow()
    {
    delete ui;
    }

    void MainWindow::setup()
    {
    QWidget *mainWidget = new QWidget(this);
    QVBoxLayout *mainLayout = new QVBoxLayout(mainWidget);
    mainLayout->setSizeConstraint(QLayout::SetFixedSize);

    QGroupBox *boxSettings = new QGroupBox("Конфигурация:");
    QGridLayout *boxLayout = new QGridLayout(boxSettings);

    boxLayout->addWidget(new QLabel("Вставьте ApiKey:"), 0, 0);

    QLineEdit *lineApiKey = new QLineEdit();
    boxLayout->addWidget(lineApiKey, 0, 1);

    boxLayout->addWidget(new QLabel("Вставьте ссылку на аккаунт:"), 1, 0);

    QLineEdit *lineSteamProfileLink = new QLineEdit();
    boxLayout->addWidget(lineSteamProfileLink, 1, 1);

    QPushButton *btnFind = new QPushButton("Найти профиль");
    boxLayout->addWidget(btnFind);

    mainLayout->addWidget(boxSettings);

    QGroupBox *boxAccountInfo = new QGroupBox("Информация про аккаунт");
    QGridLayout *layoutAccount = new QGridLayout(boxAccountInfo);

    layoutAccount->addWidget(new QLabel("Аватарка:"), 0, 0);

    QLabel *lblAvatar = new QLabel();
    lblAvatar->setFixedSize(184, 184);
    layoutAccount->addWidget(lblAvatar, 0, 1);

    layoutAccount->addWidget(new QLabel("Оригинальная ссылка (SteamID64):"), 1, 0);

    QLineEdit *lineOriginalSteamLink = new QLineEdit();
    lineOriginalSteamLink->setReadOnly(true);
    layoutAccount->addWidget(lineOriginalSteamLink, 1, 1);

    layoutAccount->addWidget(new QLabel("Дата регистрации:"), 2, 0);

    QLineEdit *lineTimeCreated = new QLineEdit();
    lineTimeCreated->setReadOnly(true);
    layoutAccount->addWidget(lineTimeCreated, 2, 1);

    layoutAccount->addWidget(new QLabel("Последняя активность:"), 3, 0);

    QLineEdit *lineLastLogOff = new QLineEdit();
    lineLastLogOff->setReadOnly(true);
    layoutAccount->addWidget(lineLastLogOff, 3, 1);

    layoutAccount->addWidget(new QLabel("VAC блокировка:"), 4, 0);

    QLineEdit *lineVacBanned = new QLineEdit();
    lineVacBanned->setReadOnly(true);
    layoutAccount->addWidget(lineVacBanned, 4, 1);

    layoutAccount->addWidget(new QLabel("Кол-во игровых блокировок:"), 5, 0);

    QLineEdit *lineGamebans = new QLineEdit();
    lineGamebans->setReadOnly(true);
    layoutAccount->addWidget(lineGamebans, 5, 1);

    layoutAccount->addWidget(new QLabel("Дней с последней блокировки:"), 6, 0);

    QLineEdit *lineDaysSinceLastBan = new QLineEdit();
    lineDaysSinceLastBan->setReadOnly(true);
    layoutAccount->addWidget(lineDaysSinceLastBan, 6, 1);

    layoutAccount->addWidget(new QLabel("Блокировка в сообществе (КТ):"), 7, 0);

    QLineEdit *lineCommunityBan = new QLineEdit();
    lineCommunityBan ->setReadOnly(true);
    layoutAccount->addWidget(lineCommunityBan , 7, 1);

    mainLayout->addWidget(boxAccountInfo);

    connect(btnFind, &QPushButton::clicked, this, [=]() {
    btnFind->setEnabled(false);
    const QString apiKey = lineApiKey->text();
    const QString steamName = MainWindow::getUrl(lineSteamProfileLink->text());
    QString steamID64;
    if (lineSteamProfileLink->text().contains("id")) {
    SteamUserProfileLink steamLink;
    steamLink.setApiKey(apiKey);
    if (steamLink.setConnection(steamName)) {
    steamID64 = steamLink.getSteamID64();
    }
    else {
    QMessageBox::information(this, "Сообщение", "Ошибка. Пользователя найти не удалось.\nПроверье ApiKey и ссылку на профиль.");
    btnFind->setEnabled(true);
    return ;
    }
    }
    else
    steamID64 = steamName;

    SteamPlayerSummaries playerSummaries;
    playerSummaries.setApiKey(apiKey);
    if (!playerSummaries.setConnection(steamID64)) {
    QMessageBox::information(this, "Сообщение", "Ошибка. Пользователя найти не удалось.\nПроверье ApiKey и ссылку на профиль.");
    btnFind->setEnabled(true);
    return ;
    }

    lineOriginalSteamLink->setText("https://steamcommunity.com/profiles/" + steamID64);

    lblAvatar->setPixmap(playerSummaries.getAvatar());
    lineTimeCreated->setText(playerSummaries.getTimeCreated());
    lineLastLogOff->setText(playerSummaries.getLastLofOff());

    SteamPlayerBans bans;
    bans.setApiKey(apiKey);
    bans.setConnection(steamID64);
    lineVacBanned->setText(bans.isVACBanned()? "ДА | " + QString::number(bans.getNumberOfVACBans()): "НЕТ");
    lineGamebans->setText(QString::number(bans.getNumberOfGameBans()));
    lineDaysSinceLastBan->setText(QString::number(bans.getDaysSinceLastBan()));
    lineCommunityBan->setText(bans.isCommunityBanned()? "ДА" : "НЕТ");
    btnFind->setEnabled(true);
    });
    }

    QString MainWindow::getUrl(QString steamProfileUrl)
    {
    if (steamProfileUrl.back() == '/')
    steamProfileUrl = steamProfileUrl.left(steamProfileUrl.length() - 1);

    steamProfileUrl = steamProfileUrl.right(steamProfileUrl.length() - steamProfileUrl.lastIndexOf('/') - 1);
    return steamProfileUrl;
    }



















    Готово! Теперь компилируем и любуемся результатом:
    [IMG]

    Как оцените эту статью? Заслуживает ли статья продолжения, где можно будет рассмотреть получение списка игр, стоимость инвентаря и много других вещей?

    Спасибо за уделенное время! Давайте кодить вместе.
     
  2. GITEM
    Очень круто,но я не шарю в кодинге,поэтому просто прочитал для интереса. Я считаю,что если это не паста,то заслуживает авторской
     
  3. DSTR2
    DSTR2 Topic starter Dec 1, 2018 ? 49 May 19, 2017
    Спасибо!
    Всё писал сам, конечно же.
     
  4. renameduser_649137
    renameduser_649137 Dec 1, 2018 Banned 14 Nov 5, 2018
    Интересно - но Qt ,хоть C++ Builder был=)
     
  5. WA1Jl
    WA1Jl Dec 1, 2018 不要相信別人 226 Sep 8, 2018
    Нечего не понял, но выглядит достойно!
     
  6. MorSalve
    MorSalve Dec 1, 2018 Banned 16 Apr 16, 2018
    Выглядит годно, может кому то понадобица. Авторку этому человеку, жыво!
     
  7. I_Know_inactive61395
    I_Know_inactive61395 Dec 1, 2018 Banned 204 Nov 25, 2016
    Годно, авторка тут была бы не лишней!
     
  8. 0_0
    0_0 Dec 1, 2018 XX YEARS | Путь к гаранту! 44 Nov 7, 2018
    От души)
     
  9. NDqODXha
    NDqODXha Dec 5, 2018 0 Feb 9, 2018
    Очень годно, однозначно авторку и продолжение)
     
  10. Maksum1
    Maksum1 Dec 5, 2018 Услуги кодера С/С++ 21 Jan 17, 2018
    Авторку ему ) Годнота API , топ что сказать )
     
  11. GRAFON11
    GRAFON11 Dec 9, 2018 68 Jun 7, 2017
    +Годнота, авторку в студию
     
  12. Alishka471
    Alishka471 Dec 9, 2018 Живи для себя,но не забывай что есть добро. 36 Sep 17, 2018
    Да хз я не разбираюсь толком и комменты здесь с такими же симпами как я,так что верить я тут не буду ни кому
     
  13. Alishka471
    Alishka471 Dec 9, 2018 Живи для себя,но не забывай что есть добро. 36 Sep 17, 2018
    Достоиных комментов нет которым можно было прислушатся и проверить инфу,только и пишут класс,круто и всякую херню
     
  14. Shellar
    На самом деле годно :roflanBuldiga:
     
  15. Proxylist
    Proxylist Jan 3, 2019 Banned 324 Jan 11, 2018
    Интересно, для ознакомления очень даже отлично!
     
  16. Maikez
    Maikez Jan 3, 2019 Сливаю лохов на Низине Арати 435 Mar 4, 2018
    Неплохо, хороший урок!
     
  17. Metafroud
    Metafroud Jan 3, 2019 Разработка - lolz.live/threads/3346631/ 227 Feb 28, 2017
    Блять,вот нихуя не понял,но было очень интересно.
    #авторку
     
  18. Metafroud
    Metafroud Feb 8, 2019 Разработка - lolz.live/threads/3346631/ 227 Feb 28, 2017
    там скомпиленная версия есть,проверь
     
  19. Maksum1
    Maksum1 Feb 14, 2019 Услуги кодера С/С++ 21 Jan 17, 2018
    Спасибо автору за даное чудо , смотивировал меня на создание market.dota2.net Helper , жду пока проверят)
     
  20. Lancaster
    Lancaster Mar 7, 2019 Быстро накачаться? - lolz.guru/threads/2538866/ 300 Nov 1, 2018
    авторку)
     
Loading...
Top