diff --git a/TTW_Bot.pro b/TTW_Bot.pro index 15b3315..dd6f766 100644 --- a/TTW_Bot.pro +++ b/TTW_Bot.pro @@ -43,6 +43,7 @@ SOURCES += \ soundmanager.cpp \ tauth.cpp \ ttw_api.cpp \ + twitcheventsub.cpp \ twitchmessage.cpp \ udatabase.cpp \ ugeneral.cpp \ @@ -81,6 +82,7 @@ HEADERS += \ timerinfo.h \ ttw_api.h \ ttw_types.h \ + twitcheventsub.h \ twitchmessage.h \ udatabase.h \ ugeneral.h \ diff --git a/object_script.TTW_Bot.Debug b/object_script.TTW_Bot.Debug index de875f8..62a9c62 100644 --- a/object_script.TTW_Bot.Debug +++ b/object_script.TTW_Bot.Debug @@ -20,6 +20,7 @@ debug/randomresponses.o debug/soundmanager.o debug/tauth.o debug/ttw_api.o +debug/twitcheventsub.o debug/twitchmessage.o debug/udatabase.o debug/ugeneral.o @@ -49,6 +50,7 @@ debug/moc_randomresponses.o debug/moc_soundmanager.o debug/moc_tauth.o debug/moc_ttw_api.o +debug/moc_twitcheventsub.o debug/moc_udatabase.o debug/moc_ugeneral.o debug/moc_ulink.o diff --git a/object_script.TTW_Bot.Release b/object_script.TTW_Bot.Release index 1bf7052..8f10f3d 100644 --- a/object_script.TTW_Bot.Release +++ b/object_script.TTW_Bot.Release @@ -20,6 +20,7 @@ release/randomresponses.o release/soundmanager.o release/tauth.o release/ttw_api.o +release/twitcheventsub.o release/twitchmessage.o release/udatabase.o release/ugeneral.o @@ -49,6 +50,7 @@ release/moc_randomresponses.o release/moc_soundmanager.o release/moc_tauth.o release/moc_ttw_api.o +release/moc_twitcheventsub.o release/moc_udatabase.o release/moc_ugeneral.o release/moc_ulink.o diff --git a/twitcheventsub.cpp b/twitcheventsub.cpp new file mode 100644 index 0000000..56426d8 --- /dev/null +++ b/twitcheventsub.cpp @@ -0,0 +1,352 @@ +#include "twitcheventsub.h" +#include +#include +#include +#include +#include +#include + +TwitchEventSub::TwitchEventSub(QObject *parent) + : QObject(parent) + , m_connected(false) +{ + connect(&m_webSocket, &QWebSocket::connected, this, &TwitchEventSub::onWebSocketConnected); + connect(&m_webSocket, &QWebSocket::disconnected, this, &TwitchEventSub::onWebSocketDisconnected); + connect(&m_webSocket, &QWebSocket::textMessageReceived, this, &TwitchEventSub::onWebSocketTextMessageReceived); + connect(&m_webSocket, QOverload::of(&QWebSocket::error), this, &TwitchEventSub::onWebSocketError); + + m_pingTimer.setInterval(30000); // 30 секунд + connect(&m_pingTimer, &QTimer::timeout, this, &TwitchEventSub::onPingTimer); +} + +TwitchEventSub::~TwitchEventSub() +{ + disconnectFromTwitch(); +} + +void TwitchEventSub::init(const QString &accessToken, const QString &clientId, const QString &broadcasterId) +{ + m_accessToken = accessToken; + m_clientId = clientId; + m_broadcasterId = broadcasterId; +} + +void TwitchEventSub::connectToTwitch() +{ + if (m_webSocket.state() == QAbstractSocket::ConnectedState) + return; + + emit onLog(0, "connectToTwitch", "Connecting to EventSub WebSocket..."); + m_webSocket.open(QUrl("wss://eventsub.wss.twitch.tv/ws?keepalive_timeout_seconds=60")); +} + +void TwitchEventSub::disconnectFromTwitch() +{ + m_pingTimer.stop(); + m_webSocket.close(); +} + +void TwitchEventSub::onWebSocketConnected() +{ + emit onLog(0, "onWebSocketConnected", "WebSocket connected"); + m_pingTimer.start(); + m_connected = true; + emit onConnected(); +} + +void TwitchEventSub::onWebSocketDisconnected() +{ + m_pingTimer.stop(); + m_connected = false; + emit onLog(1, "onWebSocketDisconnected", "WebSocket disconnected"); + emit onDisconnected(); +} + +void TwitchEventSub::onWebSocketTextMessageReceived(const QString &message) +{ + emit onRawMessage(message); + parseMessage(message); +} + +void TwitchEventSub::onWebSocketError(QAbstractSocket::SocketError error) +{ + emit onLog(2, "onWebSocketError", m_webSocket.errorString()); + emit onError(m_webSocket.errorString()); +} + +void TwitchEventSub::onPingTimer() +{ + if (m_webSocket.state() == QAbstractSocket::ConnectedState) { + m_webSocket.ping(); // отправляем Ping-фрейм + emit onLog(3, "onPingTimer", "Ping sent"); + } +} + +void TwitchEventSub::parseMessage(const QString &message) +{ + QJsonDocument doc = QJsonDocument::fromJson(message.toUtf8()); + if (!doc.isObject()) return; + + QJsonObject root = doc.object(); + QJsonObject metadata = root.value("metadata").toObject(); + QString messageType = metadata.value("message_type").toString(); + QString subscriptionType = metadata.value("subscription_type").toString(); + + emit onLog(0, "parseMessage", QString("Message type: %1, Subscription: %2").arg(messageType, subscriptionType)); + + if (messageType == "session_welcome") { + QJsonObject payload = root.value("payload").toObject(); + QJsonObject session = payload.value("session").toObject(); + m_sessionId = session.value("id").toString(); + emit onLog(0, "parseMessage", "Received session_welcome, session_id: " + m_sessionId); + performSubscriptions(); + } + else if (messageType == "notification") { + QJsonObject payload = root.value("payload").toObject(); + if (subscriptionType == "channel.channel_points_custom_reward_redemption.add") { + emit onCustomReward(parseCustomReward(payload)); + } + else if (subscriptionType == "channel.follow") { + emit onFollow(parseFollow(payload)); + } + else if (subscriptionType == "channel.subscribe") { + emit onSubscribe(parseSubscribe(payload)); + } + else if (subscriptionType == "channel.subscription.gift") { + emit onGift(parseGift(payload)); + } + else if (subscriptionType == "channel.raid") { + emit onRaid(parseRaid(payload)); + } + } + else if (messageType == "session_keepalive") { + emit onLog(3, "parseMessage", "Received keepalive"); + } +} + +void TwitchEventSub::performSubscriptions() +{ + if (m_sessionId.isEmpty()) { + emit onLog(2, "performSubscriptions", "No session_id, cannot subscribe"); + return; + } + + // Формируем условие для каждого типа подписки + subscribeTo("channel.channel_points_custom_reward_redemption.add", "1", + QString("{\"broadcaster_user_id\":\"%1\"}").arg(m_broadcasterId)); + _sleep(500); + subscribeTo("channel.raid", "1", + QString("{\"to_broadcaster_user_id\":\"%1\"}").arg(m_broadcasterId)); + + _sleep(500); + subscribeTo("channel.follow", "2", + QString("{\"broadcaster_user_id\":\"%1\",\"moderator_user_id\":\"%1\"}").arg(m_broadcasterId)); + + _sleep(500); + subscribeTo("channel.subscribe", "1", + QString("{\"broadcaster_user_id\":\"%1\"}").arg(m_broadcasterId)); + + _sleep(500); + subscribeTo("channel.subscription.gift", "1", + QString("{\"broadcaster_user_id\":\"%1\"}").arg(m_broadcasterId)); + _sleep(500); +} + + + +// --- Парсеры событий --- +TCustomRewardEvent2 TwitchEventSub::parseCustomReward(const QJsonObject &payload) +{ + TCustomRewardEvent2 result; + QJsonObject subscription = payload.value("subscription").toObject(); + result.subscription.id = subscription.value("id").toString(); + result.subscription.type = subscription.value("type").toString(); + result.subscription.version = subscription.value("version").toString(); + result.subscription.status = subscription.value("status").toString(); + result.subscription.cost = subscription.value("cost").toInt(); + result.subscription.created_at = subscription.value("created_at").toString(); + + QJsonObject cond = subscription.value("condition").toObject(); + result.subscription.condition.broadcaster_user_id = cond.value("broadcaster_user_id").toString(); + result.subscription.condition.reward_id = cond.value("reward_id").toString(); + + QJsonObject transp = subscription.value("transport").toObject(); + result.subscription.transport.method = transp.value("method").toString(); + + QJsonObject event = payload.value("event").toObject(); + result.event.id = event.value("id").toString(); + result.event.broadcaster_user_id = event.value("broadcaster_user_id").toString(); + result.event.broadcaster_user_login = event.value("broadcaster_user_login").toString(); + result.event.broadcaster_user_name = event.value("broadcaster_user_name").toString(); + result.event.user_id = event.value("user_id").toString(); + result.event.user_login = event.value("user_login").toString(); + result.event.user_name = event.value("user_name").toString(); + result.event.user_input = event.value("user_input").toString(); + + QJsonObject reward = event.value("reward").toObject(); + result.event.reward.id = reward.value("id").toString(); + result.event.reward.title = reward.value("title").toString(); + result.event.reward.cost = reward.value("cost").toInt(); + result.event.reward.prompt = reward.value("prompt").toString(); + + return result; +} + +TFollowEvent TwitchEventSub::parseFollow(const QJsonObject &payload) +{ + TFollowEvent result; + QJsonObject subscription = payload.value("subscription").toObject(); + result.subscription.id = subscription.value("id").toString(); + result.subscription.type = subscription.value("type").toString(); + result.subscription.version = subscription.value("version").toString(); + result.subscription.status = subscription.value("status").toString(); + result.subscription.cost = subscription.value("cost").toInt(); + result.subscription.created_at = subscription.value("created_at").toString(); + + QJsonObject cond = subscription.value("condition").toObject(); + result.subscription.condition.broadcaster_user_id = cond.value("broadcaster_user_id").toString(); + + QJsonObject transp = subscription.value("transport").toObject(); + result.subscription.transport.method = transp.value("method").toString(); + + QJsonObject event = payload.value("event").toObject(); + result.event.broadcaster_user_id = event.value("broadcaster_user_id").toString(); + result.event.broadcaster_user_login = event.value("broadcaster_user_login").toString(); + result.event.broadcaster_user_name = event.value("broadcaster_user_name").toString(); + result.event.user_id = event.value("user_id").toString(); + result.event.user_login = event.value("user_login").toString(); + result.event.user_name = event.value("user_name").toString(); + result.event.followed_at = event.value("followed_at").toString(); + + return result; +} + +TSubEvent TwitchEventSub::parseSubscribe(const QJsonObject &payload) +{ + TSubEvent result; + QJsonObject subscription = payload.value("subscription").toObject(); + result.subscription.id = subscription.value("id").toString(); + result.subscription.type = subscription.value("type").toString(); + result.subscription.version = subscription.value("version").toString(); + result.subscription.status = subscription.value("status").toString(); + result.subscription.cost = subscription.value("cost").toInt(); + result.subscription.created_at = subscription.value("created_at").toString(); + + QJsonObject cond = subscription.value("condition").toObject(); + result.subscription.condition.broadcaster_user_id = cond.value("broadcaster_user_id").toString(); + + QJsonObject transp = subscription.value("transport").toObject(); + result.subscription.transport.method = transp.value("method").toString(); + + QJsonObject event = payload.value("event").toObject(); + result.event.broadcaster_user_id = event.value("broadcaster_user_id").toString(); + result.event.broadcaster_user_login = event.value("broadcaster_user_login").toString(); + result.event.broadcaster_user_name = event.value("broadcaster_user_name").toString(); + result.event.user_id = event.value("user_id").toString(); + result.event.user_login = event.value("user_login").toString(); + result.event.user_name = event.value("user_name").toString(); + result.event.tier = event.value("tier").toString(); + result.event.is_gift = event.value("is_gift").toBool(); + + return result; +} + +TGiftEvent TwitchEventSub::parseGift(const QJsonObject &payload) +{ + TGiftEvent result; + QJsonObject subscription = payload.value("subscription").toObject(); + result.subscription.id = subscription.value("id").toString(); + result.subscription.type = subscription.value("type").toString(); + result.subscription.version = subscription.value("version").toString(); + result.subscription.status = subscription.value("status").toString(); + result.subscription.cost = subscription.value("cost").toInt(); + result.subscription.created_at = subscription.value("created_at").toString(); + + QJsonObject cond = subscription.value("condition").toObject(); + result.subscription.condition.broadcaster_user_id = cond.value("broadcaster_user_id").toString(); + + QJsonObject transp = subscription.value("transport").toObject(); + result.subscription.transport.method = transp.value("method").toString(); + + QJsonObject event = payload.value("event").toObject(); + result.event.broadcaster_user_id = event.value("broadcaster_user_id").toString(); + result.event.broadcaster_user_login = event.value("broadcaster_user_login").toString(); + result.event.broadcaster_user_name = event.value("broadcaster_user_name").toString(); + result.event.user_id = event.value("user_id").toString(); + result.event.user_login = event.value("user_login").toString(); + result.event.user_name = event.value("user_name").toString(); + result.event.total = event.value("total").toInt(); + result.event.tier = event.value("tier").toString(); + result.event.cumulative_total = event.value("cumulative_total").toInt(); + result.event.is_anonymous = event.value("is_anonymous").toBool(); + + return result; +} + +TRaidEvent TwitchEventSub::parseRaid(const QJsonObject &payload) +{ + TRaidEvent result; + QJsonObject subscription = payload.value("subscription").toObject(); + result.subscription.id = subscription.value("id").toString(); + result.subscription.type = subscription.value("type").toString(); + result.subscription.version = subscription.value("version").toString(); + result.subscription.status = subscription.value("status").toString(); + result.subscription.cost = subscription.value("cost").toInt(); + result.subscription.created_at = subscription.value("created_at").toString(); + + QJsonObject cond = subscription.value("condition").toObject(); + result.subscription.condition.to_broadcaster_user_id = cond.value("to_broadcaster_user_id").toString(); + + QJsonObject transp = subscription.value("transport").toObject(); + result.subscription.transport.method = transp.value("method").toString(); + + QJsonObject event = payload.value("event").toObject(); + result.event.from_broadcaster_user_id = event.value("from_broadcaster_user_id").toString(); + result.event.from_broadcaster_user_login = event.value("from_broadcaster_user_login").toString(); + result.event.from_broadcaster_user_name = event.value("from_broadcaster_user_name").toString(); + result.event.to_broadcaster_user_id = event.value("to_broadcaster_user_id").toString(); + result.event.to_broadcaster_user_login = event.value("to_broadcaster_user_login").toString(); + result.event.to_broadcaster_user_name = event.value("to_broadcaster_user_name").toString(); + result.event.viewers = event.value("viewers").toInt(); + + return result; +} + + +bool TwitchEventSub::subscribeTo(const QString &eventType, const QString &version, const QString &condition) +{ + emit onLog(0, "subscribeTo", QString("Subscribing to %1").arg(eventType)); + + QJsonObject transport; + transport["method"] = "websocket"; + transport["session_id"] = m_sessionId; + + QJsonObject requestBody; + requestBody["type"] = eventType; + requestBody["version"] = version; + requestBody["condition"] = QJsonDocument::fromJson(condition.toUtf8()).object(); + requestBody["transport"] = transport; + + QNetworkRequest request(QUrl("https://api.twitch.tv/helix/eventsub/subscriptions")); + request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); + request.setRawHeader("Authorization", ("Bearer " + m_accessToken).toUtf8()); + request.setRawHeader("Client-Id", m_clientId.toUtf8()); + + QNetworkReply *reply = m_networkManager.post(request, QJsonDocument(requestBody).toJson()); + connect(reply, &QNetworkReply::finished, this, [this, reply, eventType]() { + if (reply->error() == QNetworkReply::NoError) { + QByteArray data = reply->readAll(); + emit onLog(3, "subscribeTo", QString("Response: %1").arg(QString(data))); + if (data.contains("\"status\":\"enabled\"")) { + emit onLog(0, "subscribeTo", QString("Subscription %1 successful").arg(eventType)); + } else { + emit onLog(1, "subscribeTo", QString("Subscription %1 failed: %2").arg(eventType, QString(data))); + } + } else { + emit onLog(2, "subscribeTo", QString("Network error: %1").arg(reply->errorString())); + } + reply->deleteLater(); // обязательно удаляем + }); + return true; +} + diff --git a/twitcheventsub.h b/twitcheventsub.h new file mode 100644 index 0000000..81a2c36 --- /dev/null +++ b/twitcheventsub.h @@ -0,0 +1,218 @@ +#ifndef TWITCHEVENTSUB_H +#define TWITCHEVENTSUB_H + +#include +#include +#include +#include +#include + +// Структуры данных событий (аналог Delphi-записей) +struct TCustomRewardEvent2 +{ + struct Subscription { + QString id; + QString type; + QString version; + QString status; + int cost; + QString created_at; + struct Condition { + QString broadcaster_user_id; + QString reward_id; + } condition; + struct Transport { + QString method; + } transport; + } subscription; + + struct Event { + QString id; + QString broadcaster_user_id; + QString broadcaster_user_login; + QString broadcaster_user_name; + QString user_id; + QString user_login; + QString user_name; + QString user_input; + struct Reward { + QString id; + QString title; + int cost; + QString prompt; + } reward; + } event; +}; + +struct TFollowEvent +{ + struct Subscription { + QString id; + QString type; + QString version; + QString status; + int cost; + QString created_at; + struct Condition { + QString broadcaster_user_id; + } condition; + struct Transport { + QString method; + } transport; + } subscription; + + struct Event { + QString broadcaster_user_id; + QString broadcaster_user_login; + QString broadcaster_user_name; + QString user_id; + QString user_login; + QString user_name; + QString followed_at; + } event; +}; + +struct TSubEvent +{ + struct Subscription { + QString id; + QString type; + QString version; + QString status; + int cost; + QString created_at; + struct Condition { + QString broadcaster_user_id; + } condition; + struct Transport { + QString method; + } transport; + } subscription; + + struct Event { + QString broadcaster_user_id; + QString broadcaster_user_login; + QString broadcaster_user_name; + QString user_id; + QString user_login; + QString user_name; + QString tier; + bool is_gift; + } event; +}; + +struct TGiftEvent +{ + struct Subscription { + QString id; + QString type; + QString version; + QString status; + int cost; + QString created_at; + struct Condition { + QString broadcaster_user_id; + } condition; + struct Transport { + QString method; + } transport; + } subscription; + + struct Event { + QString broadcaster_user_id; + QString broadcaster_user_login; + QString broadcaster_user_name; + QString user_id; + QString user_login; + QString user_name; + int total; + QString tier; + int cumulative_total; + bool is_anonymous; + } event; +}; + +struct TRaidEvent +{ + struct Subscription { + QString id; + QString type; + QString version; + QString status; + int cost; + QString created_at; + struct Condition { + QString to_broadcaster_user_id; + } condition; + struct Transport { + QString method; + } transport; + } subscription; + + struct Event { + QString from_broadcaster_user_id; + QString from_broadcaster_user_login; + QString from_broadcaster_user_name; + QString to_broadcaster_user_id; + QString to_broadcaster_user_login; + QString to_broadcaster_user_name; + int viewers; + } event; +}; + +class TwitchEventSub : public QObject +{ + Q_OBJECT +public: + explicit TwitchEventSub(QObject *parent = nullptr); + ~TwitchEventSub(); + + void init(const QString &accessToken, const QString &clientId, const QString &broadcasterId); + void connectToTwitch(); + void disconnectFromTwitch(); + +signals: + void onConnected(); + void onDisconnected(); + void onError(const QString &error); + void onLog(int level, const QString &method, const QString &message); + void onStatus(const QString &event, int code, const QString &description); + void onRawMessage(const QString &message); + + void onCustomReward(const TCustomRewardEvent2 &data); + void onFollow(const TFollowEvent &data); + void onSubscribe(const TSubEvent &data); + void onGift(const TGiftEvent &data); + void onRaid(const TRaidEvent &data); + +private slots: + void onWebSocketConnected(); + void onWebSocketDisconnected(); + void onWebSocketTextMessageReceived(const QString &message); + void onWebSocketError(QAbstractSocket::SocketError error); + void onPingTimer(); + +private: + bool subscribeTo(const QString &eventType, const QString &version, const QString &condition); + void performSubscriptions(); + void parseMessage(const QString &message); + + // Парсеры событий + TCustomRewardEvent2 parseCustomReward(const QJsonObject &payload); + TFollowEvent parseFollow(const QJsonObject &payload); + TSubEvent parseSubscribe(const QJsonObject &payload); + TGiftEvent parseGift(const QJsonObject &payload); + TRaidEvent parseRaid(const QJsonObject &payload); + + QString m_accessToken; + QString m_clientId; + QString m_broadcasterId; + QString m_sessionId; + + QWebSocket m_webSocket; + QNetworkAccessManager m_networkManager; + QTimer m_pingTimer; + bool m_connected; +}; + +#endif // TWITCHEVENTSUB_H diff --git a/ugeneral.cpp b/ugeneral.cpp index f853277..4b0f2aa 100644 --- a/ugeneral.cpp +++ b/ugeneral.cpp @@ -675,6 +675,8 @@ void uGeneral::setupTwitchComponents() connect(twitchAPI, &TTwAPI::rateLimitExceeded, this, &uGeneral::onRateLimit); initTwitchAPI(); + + m_twitchEventSub = nullptr; } void uGeneral::setupUserWidget() @@ -832,6 +834,7 @@ uGeneral::~uGeneral() delete m_neuralTemplateManager; m_neuralTemplateManager = nullptr; } + delete m_twitchEventSub; delete m_counterManager; delete m_createNotifyDialog; delete m_createChatDialog; @@ -1497,7 +1500,12 @@ void uGeneral::on_btnGetTokenStreamer_clicked() return; } - QString scope = "channel:manage:broadcast+" + QString scope = + "channel:manage:vips+" + "moderator:read:followers+" + "channel:manage:moderators+" + "channel:manage:redemptions+" + "channel:manage:broadcast+" "channel:read:subscriptions+" "channel:read:redemptions+" "user:read:email"; @@ -2089,12 +2097,87 @@ void uGeneral::connectToTwitch() ui->lbBotDays->setText(QString::number(botTokenDays)); ui->lbStreamerDays->setText(QString::number(streamerTokenDays)); setTwitchConnected(true); + + // === Инициализация EventSub === + if (!m_twitchEventSub) { + m_twitchEventSub = new TwitchEventSub(this); + + // Подключаем сигналы (например, для логирования) + connect(m_twitchEventSub, &TwitchEventSub::onLog, + [this](int level, const QString &method, const QString &msg) { + LogManager::instance()->log(static_cast(level), "TwitchEventSub", method, msg); + }); + + connect(m_twitchEventSub, &TwitchEventSub::onError, + [this](const QString &error) { + LogManager::instance()->error("TwitchEventSub", "onError", error); + }); + + // Здесь можно подключить обработчики конкретных событий + connect(m_twitchEventSub, &TwitchEventSub::onCustomReward, + [this](const TCustomRewardEvent2 &data) { + LogManager::instance()->info("TwitchEventSub", "onCustomReward", + QString("Reward: %1 by %2") + .arg(data.event.reward.title) + .arg(data.event.user_name)); + // TODO: вызвать нужный метод (например, уведомление, звук) + }); + + connect(m_twitchEventSub, &TwitchEventSub::onFollow, + [this](const TFollowEvent &data) { + LogManager::instance()->info("TwitchEventSub", "onFollow", + QString("%1 followed").arg(data.event.user_name)); + // playNotify(...); или другое + }); + + connect(m_twitchEventSub, &TwitchEventSub::onSubscribe, + [this](const TSubEvent &data) { + LogManager::instance()->info("TwitchEventSub", "onSubscribe", + QString("%1 subscribed (tier %2)") + .arg(data.event.user_name) + .arg(data.event.tier)); + }); + + connect(m_twitchEventSub, &TwitchEventSub::onGift, + [this](const TGiftEvent &data) { + LogManager::instance()->info("TwitchEventSub", "onGift", + QString("%1 gifted %2 subs") + .arg(data.event.user_name) + .arg(data.event.total)); + }); + + connect(m_twitchEventSub, &TwitchEventSub::onRaid, + [this](const TRaidEvent &data) { + LogManager::instance()->info("TwitchEventSub", "onRaid", + QString("Raid from %1 with %2 viewers") + .arg(data.event.from_broadcaster_user_name) + .arg(data.event.viewers)); + }); + } + + // Получаем broadcaster_id через Twitch API (предположим, есть метод) + QString broadcasterId = twitchAPI->getUserByLogin(ui->edtChannel->text()).id; + if (broadcasterId.isEmpty()) { + LogManager::instance()->warning("uGeneral", "connectToTwitch", + "Could not resolve broadcaster ID, EventSub may fail"); + } + + m_twitchEventSub->init(ui->edtBotTokenStreamer->text(), ui->edtBotClientID->text(), broadcasterId); + m_twitchEventSub->connectToTwitch(); + + setTwitchConnected(true); } } void uGeneral::disconnectFromTwitch() { + m_twitchClient->disconnectFromServer(); + + if (m_twitchEventSub) { + m_twitchEventSub->disconnectFromTwitch(); + } + m_userManager->clear(); ui->pushButton_2->setText("Подключиться"); setTwitchConnected(false); diff --git a/ugeneral.h b/ugeneral.h index 3c74c98..1f6ce67 100644 --- a/ugeneral.h +++ b/ugeneral.h @@ -17,6 +17,7 @@ #include "logmanager.h" #include "neuralnetworkmanager.h" #include "ttw_api.h" +#include "twitcheventsub.h" #include "user_manager.h" #include "webserverchat.h" #include "webservernotify.h" @@ -420,7 +421,7 @@ private: MediaFileManager *m_TextFiles; NeuralTemplateManager *m_neuralTemplateManager; DonationManager *m_donationManager; - + TwitchEventSub *m_twitchEventSub = nullptr; QList m_timers; // Список таймеров int m_nextTimerId = 1; // Следующий ID таймера bool m_isTwitchConnected = false; // Статус подключения к Twitch diff --git a/ugeneral.ui b/ugeneral.ui index 69bddb8..34091f6 100644 --- a/ugeneral.ui +++ b/ugeneral.ui @@ -42,7 +42,7 @@ Qt::LeftToRight - 3 + 0 false