Files
TTW_Bot/ttw_api.cpp
T
2026-02-07 08:28:56 +03:00

594 lines
17 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "ttw_api.h"
#include "qeventloop.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QDateTime>
#include <QDebug>
TTwAPI::TTwAPI(QObject *parent)
: QObject(parent)
, m_networkManager(new QNetworkAccessManager(this))
{
// Настройка таймаутов
//m_networkManager->Timeout(30000); // 30 секунд
}
TTwAPI::~TTwAPI()
{
m_networkManager->deleteLater();
}
void TTwAPI::init(const QString &clientId,
const QString &token,
const QString &streamerToken,
const QString &channel,
const QString &botName)
{
m_clientId = clientId;
m_tokenApi = token;
m_tokenApiStreamer = streamerToken;
m_channelName = channel;
m_botName = botName;
toLog(1, "TTwAPI::init", "API инициализирован для канала: " + channel);
}
QString TTwAPI::sendRequest(const QString &url,
const QString &method,
const QByteArray &data,
const QString &token)
{
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
request.setRawHeader("Client-ID", m_clientId.toUtf8());
QString authHeader = token.startsWith("Bearer") ? token : "Bearer " + token;
request.setRawHeader("Authorization", authHeader.toUtf8());
QNetworkReply *reply = nullptr;
if (method.toUpper() == "GET") {
reply = m_networkManager->get(request);
} else if (method.toUpper() == "POST") {
reply = m_networkManager->post(request, data);
} else if (method.toUpper() == "DELETE") {
reply = m_networkManager->sendCustomRequest(request, "DELETE", data);
} else if (method.toUpper() == "PATCH") {
reply = m_networkManager->sendCustomRequest(request, "PATCH", data);
} else if (method.toUpper() == "PUT") {
reply = m_networkManager->sendCustomRequest(request, "PUT", data);
}
if (!reply) {
toLog(2, "TTwAPI::sendRequest", "Не удалось создать запрос: " + url);
return QString();
}
// Ожидание завершения запроса (синхронно)
QEventLoop loop;
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
loop.exec();
// Проверка ошибок
if (reply->error() != QNetworkReply::NoError) {
QString errorMsg = QString("Ошибка %1: %2").arg(reply->error()).arg(reply->errorString());
toLog(2, "TTwAPI::sendRequest", errorMsg);
if (reply->error() == QNetworkReply::AuthenticationRequiredError) {
emit tokenExpired(errorMsg);
}
reply->deleteLater();
return QString();
}
QString response = reply->readAll();
reply->deleteLater();
return response;
}
QString TTwAPI::getTTW(const QString &method,
const QString &clientId,
bool isStreamer)
{
Q_UNUSED(clientId);
QString baseUrl = "https://api.twitch.tv/helix/";
QString token = isStreamer ? m_tokenApiStreamer : m_tokenApi;
return sendRequest(baseUrl + method, "GET", QByteArray(), token);
}
QString TTwAPI::deleteTTW(const QString &method,
const QString &clientId,
bool isStreamer)
{
Q_UNUSED(clientId);
QString baseUrl = "https://api.twitch.tv/helix/";
QString token = isStreamer ? m_tokenApiStreamer : m_tokenApi;
return sendRequest(baseUrl + method, "DELETE", QByteArray(), token);
}
QString TTwAPI::postTTW(const QString &method,
const QString &clientId,
const QByteArray &data,
bool isStreamer)
{
Q_UNUSED(clientId);
QString baseUrl = "https://api.twitch.tv/helix/";
QString token = isStreamer ? m_tokenApiStreamer : m_tokenApi;
return sendRequest(baseUrl + method, "POST", data, token);
}
QString TTwAPI::patchTTW(const QString &method,
const QString &clientId,
const QByteArray &data,
bool isStreamer)
{
Q_UNUSED(clientId);
QString baseUrl = "https://api.twitch.tv/helix/";
QString token = isStreamer ? m_tokenApiStreamer : m_tokenApi;
return sendRequest(baseUrl + method, "PATCH", data, token);
}
void TTwAPI::getTTWStat(const QString &channel,
int &avgViewers,
int &maxViewers,
int &hoursWatched,
int &followers,
int &followersTotal)
{
Q_UNUSED(channel);
QString response = getTTW("channels?broadcaster_id=" + getRoomId(), m_clientId);
if (response.isEmpty()) {
toLog(2, "TTwAPI::getTTWStat", "Пустой ответ от API");
return;
}
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (!doc.isObject()) {
toLog(2, "TTwAPI::getTTWStat", "Неверный JSON формат");
return;
}
QJsonObject root = doc.object();
QJsonArray data = root["data"].toArray();
if (data.isEmpty()) {
toLog(2, "TTwAPI::getTTWStat", "Нет данных в ответе");
return;
}
QJsonObject channelData = data[0].toObject();
// Парсинг данных (заглушка - реальные поля могут отличаться)
avgViewers = channelData["average_viewers"].toInt();
maxViewers = channelData["peak_viewers"].toInt();
hoursWatched = channelData["hours_watched"].toInt();
followers = channelData["followers_gained"].toInt();
followersTotal = channelData["followers_total"].toInt();
}
QDate TTwAPI::getFollow(const QString &id)
{
// Правильный endpoint для проверки, является ли пользователь фоловером
QString response = getTTW(
QString("channels/followers?user_id=%1&broadcaster_id=%2")
.arg(id)
.arg(getRoomId()),
m_clientId,
true
);
if (response.isEmpty()) {
return QDate();
}
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (!doc.isObject()) {
return QDate();
}
QJsonObject root = doc.object();
QJsonArray data = root["data"].toArray();
if (data.isEmpty()) {
// Пользователь не фоловер
return QDate();
}
QJsonObject followData = data[0].toObject();
QString followedAt = followData["followed_at"].toString();
return QDate::fromString(followedAt, Qt::ISODate);
}
User TTwAPI::getUserByLogin(const QString &login)
{
User user;
user.login = login.toLower();
QString response = getTTW("users?login=" + login, m_clientId);
if (response.isEmpty()) {
toLog(2, "TTwAPI::getUserByLogin", "Не удалось получить данные пользователя: " + login);
return user;
}
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (!doc.isObject()) {
return user;
}
QJsonObject root = doc.object();
QJsonArray data = root["data"].toArray();
if (data.isEmpty()) {
return user;
}
QJsonObject userData = data[0].toObject();
user.id = userData["id"].toString();
user.displayName = userData["display_name"].toString();
user.createdAt = QDate::fromString(userData["created_at"].toString(), Qt::ISODate);
// Получаем информацию о подписке
user.followAt = getFollow(user.id);
return user;
}
void TTwAPI::setModerator(const QString &id)
{
QJsonDocument doc((QJsonObject()));
postTTW("moderation/moderators?broadcaster_id=" + getRoomId() +
"&user_id=" + id,
m_clientId,
doc.toJson(),
true);
}
void TTwAPI::delModerator(const QString &id)
{
deleteTTW("moderation/moderators?broadcaster_id=" + getRoomId() +
"&user_id=" + id, m_clientId, true);
}
void TTwAPI::setVIP(const QString &id)
{
QJsonDocument doc((QJsonObject()));
postTTW("channels/vips?broadcaster_id=" + getRoomId() +
"&user_id=" + id,
m_clientId,
doc.toJson(),
true);
}
void TTwAPI::delVIP(const QString &id)
{
deleteTTW("channels/vips?broadcaster_id=" + getRoomId() +
"&user_id=" + id, m_clientId, true);
}
void TTwAPI::banUser(const QString &id)
{
QString roomId = getRoomId();
if (roomId.isEmpty()) {
toLog(2, "TTwAPI::banUser", "Не удалось получить roomId");
return;
}
QString botId = getBotId(); // Нужно получить ID бота
if (botId.isEmpty()) {
toLog(2, "TTwAPI::banUser", "Не удалось получить botId");
return;
}
QJsonObject innerData;
innerData["user_id"] = id;
innerData["reason"] = "Нарушение правил чата";
QJsonObject dataObj;
dataObj["data"] = innerData;
QJsonDocument doc(dataObj);
postTTW("moderation/bans?broadcaster_id=" + roomId +
"&moderator_id=" + botId,
m_clientId,
doc.toJson(),
false);
}
void TTwAPI::banUserTime(const QString &id, int timeMinutes)
{
QString roomId = getRoomId();
if (roomId.isEmpty()) {
toLog(2, "TTwAPI::banUserTime", "Не удалось получить roomId");
return;
}
QString botId = getBotId();
if (botId.isEmpty()) {
toLog(2, "TTwAPI::banUserTime", "Не удалось получить botId");
return;
}
toLog(2, "TTwAPI::баним", id);
QJsonObject innerData;
innerData["user_id"] = id;
innerData["duration"] = timeMinutes * 60;
innerData["reason"] = "Таймаут";
QJsonObject dataObj;
dataObj["data"] = innerData;
QJsonDocument doc(dataObj);
postTTW("moderation/bans?broadcaster_id=" + roomId +
"&moderator_id=" + botId,
m_clientId,
doc.toJson(),
false);
}
void TTwAPI::warnUser(const QString &id)
{
QString roomId = getRoomId();
if (roomId.isEmpty()) {
toLog(2, "TTwAPI::warnUser", "Не удалось получить roomId");
return;
}
QString botId = getBotId();
if (botId.isEmpty()) {
toLog(2, "TTwAPI::warnUser", "Не удалось получить botId");
return;
}
QJsonObject innerData;
innerData["user_id"] = id;
QJsonObject dataObj;
dataObj["data"] = innerData;
QJsonDocument doc(dataObj);
postTTW("moderation/warnings?broadcaster_id=" + roomId +
"&moderator_id=" + botId,
m_clientId,
doc.toJson(),
false);
}
void TTwAPI::unbanUser(const QString &id)
{
QString roomId = getRoomId();
if (roomId.isEmpty()) {
toLog(2, "TTwAPI::unbanUser", "Не удалось получить roomId");
return;
}
QString botId = getBotId();
if (botId.isEmpty()) {
toLog(2, "TTwAPI::unbanUser", "Не удалось получить botId");
return;
}
deleteTTW("moderation/bans?broadcaster_id=" + roomId +
"&moderator_id=" + botId +
"&user_id=" + id,
m_clientId,
false);
}
QString TTwAPI::getBotId()
{
static QString cachedBotId;
if (!cachedBotId.isEmpty()) {
return cachedBotId;
}
if (m_botName.isEmpty()) {
toLog(2, "TTwAPI::getBotId", "Имя бота не установлено");
return QString();
}
QString response = getTTW("users?login=" + m_botName, m_clientId);
if (response.isEmpty()) {
toLog(2, "TTwAPI::getBotId", "Не удалось получить данные бота");
return QString();
}
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (!doc.isObject()) {
toLog(2, "TTwAPI::getBotId", "Неверный JSON формат");
return QString();
}
QJsonObject root = doc.object();
QJsonArray data = root["data"].toArray();
if (data.isEmpty()) {
toLog(2, "TTwAPI::getBotId", "Нет данных о боте");
return QString();
}
cachedBotId = data[0].toObject()["id"].toString();
return cachedBotId;
}
QString TTwAPI::getFollowedAtFromJson(const QString &jsonString)
{
QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8());
if (!doc.isObject()) {
return QString();
}
QJsonObject root = doc.object();
QJsonArray data = root["data"].toArray();
if (data.isEmpty()) {
return QString();
}
QJsonObject followData = data[0].toObject();
return followData["followed_at"].toString();
}
QString TTwAPI::getRoomId()
{
// Кэширование ID комнаты
static QString cachedRoomId;
if (!cachedRoomId.isEmpty()) {
return cachedRoomId;
}
QString response = getTTW("users?login=" + m_channelName, m_clientId);
if (response.isEmpty()) {
return QString();
}
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
if (!doc.isObject()) {
return QString();
}
QJsonObject root = doc.object();
QJsonArray data = root["data"].toArray();
if (data.isEmpty()) {
return QString();
}
cachedRoomId = data[0].toObject()["id"].toString();
return cachedRoomId;
}
QString TTwAPI::getRoomAndBot()
{
QString roomId = getRoomId();
QString botId;
QString response = getTTW("users?login=" + m_botName, m_clientId);
if (!response.isEmpty()) {
QJsonDocument doc = QJsonDocument::fromJson(response.toUtf8());
QJsonObject root = doc.object();
QJsonArray data = root["data"].toArray();
if (!data.isEmpty()) {
botId = data[0].toObject()["id"].toString();
}
}
return QString("RoomID: %1, BotID: %2").arg(roomId).arg(botId);
}
void TTwAPI::toLog(int level, const QString &method, const QString &message)
{
Q_UNUSED(level);
Q_UNUSED(method);
Q_UNUSED(message);
qDebug() << message;
// uGeneral.toLog("ttw_api", method, message, level);
}
void TTwAPI::sendAnnouncement(const QString &message) {
QString roomId = getRoomId();
if (roomId.isEmpty()) {
toLog(2, "TTwAPI::sendAnnouncement", "Не удалось получить roomId");
return;
}
QString botId = getBotId();
if (botId.isEmpty()) {
toLog(2, "TTwAPI::sendAnnouncement", "Не удалось получить botId");
return;
}
QJsonObject json;
json["message"] = message;
json["color"] = "primary"; // Можно сделать настраиваемым: "primary", "blue", "green", "orange", "purple"
QJsonDocument doc(json);
QString otv = postTTW("chat/announcements?broadcaster_id=" + roomId +
"&moderator_id=" + botId,
m_clientId,
doc.toJson(),
false);
}
void TTwAPI::sendAnnouncementToChat(const QString &message) {
// Этот метод может использоваться для обычных сообщений в чат
// Но для Twitch API это тот же endpoint
sendAnnouncement(message);
}
void TTwAPI::getGlobalChatBadges(QVector<ChatBadge> &badges)
{
QString response = getTTW("chat/badges/global", m_clientId, false);
parseBadgesFromApi(response, badges);
}
void TTwAPI::getCustomChatBadges(QVector<ChatBadge> &badges)
{
QString roomId = getRoomId();
if (roomId.isEmpty()) {
toLog(2, "TTwAPI::getCustomChatBadges", "Не удалось получить roomId");
return;
}
QString response = getTTW("chat/badges?broadcaster_id=" + roomId, m_clientId, false);
parseBadgesFromApi(response, badges);
}
void TTwAPI::parseBadgesFromApi(const QString &jsonString, QVector<ChatBadge> &badges)
{
if (jsonString.isEmpty()) {
return;
}
QJsonDocument doc = QJsonDocument::fromJson(jsonString.toUtf8());
if (!doc.isObject()) {
toLog(2, "TTwAPI::parseBadgesFromApi", "Неверный JSON формат");
return;
}
QJsonObject root = doc.object();
QJsonArray data = root["data"].toArray();
for (const QJsonValue &value : data) {
QJsonObject badgeObj = value.toObject();
ChatBadge badge;
badge.setId = badgeObj["set_id"].toString();
QJsonArray versions = badgeObj["versions"].toArray();
for (const QJsonValue &versionValue : versions) {
QJsonObject versionObj = versionValue.toObject();
BadgeVersion version;
version.id = versionObj["id"].toString();
version.imageUrl1x = versionObj["image_url_1x"].toString();
version.imageUrl2x = versionObj["image_url_2x"].toString();
version.imageUrl4x = versionObj["image_url_4x"].toString();
version.title = versionObj["title"].toString();
version.description = versionObj["description"].toString();
badge.versions.append(version);
}
badges.append(badge);
}
}