• XSS.stack #1 – первый литературный журнал от юзеров форума

Статья [СОФТ] Урок. Работа со Steam WEB Api на C++ Qt

DSTR!

floppy-диск
Пользователь
Регистрация
16.12.2018
Сообщения
5
Реакции
3
Копирайт © DSTR!

Доброго времени суток!
Хотел бы немного рассказать (и показать) о взаимодействии со Steam WEB Api средствами Qt и C++.
Пишите, если хотите продолжение: статью о работе с инвентарем Steam: получение цены предметов и т.п


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

Ниже пример окончательной программы:
_4T8ZV_0rDc.jpg


Исходный код, а также скомпилированный проект доступен по ссылке: 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 (скрин)

60uIWHzROXY.jpg

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

4)
Реализуем наш класс.
В файле SteamUserProfileLink.h создаем переменные и прототипы функций:
C++:
#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:
C++:
#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();

    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:
C++:
#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:
C++:
#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();
    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();
    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:
C++:
#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
C++:
#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();
    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
C++:
#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:
C++:
#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;
}

Готово! Теперь компилируем и любуемся результатом:
A9iEIM8KpSs.jpg
 

Вложения

  • _4T8ZV_0rDc.jpg
    _4T8ZV_0rDc.jpg
    138.4 КБ · Просмотры: 43
Зарегистрировался для конкурса. Мой копирайт с другого борда.
Так вроде статья должна быть эксклюзивно для xss чтобы участвовать.
А так, радует что не все еще на js пишут что попало и где попало :)
 
Так вроде статья должна быть эксклюзивно для xss чтобы участвовать.
А так, радует что не все еще на js пишут что попало и где попало :)

Если что, напишу вторую по взаимодействию с инвентарем, там всё сложнее будет, не так интересно
 
Пожалуйста, обратите внимание, что пользователь заблокирован
а qt сейчас как, платный? или как он вообще распространяется? как-то давно писал на нем, удобная вещь.
 
а qt сейчас как, платный? или как он вообще распространяется? как-то давно писал на нем, удобная вещь.
Платный. Но есть Community версия с некоторым урезанным функционалом.
 


Напишите ответ...
  • Вставить:
Прикрепить файлы
Верх