соединение с сервером событий

- получение информации о фоллов, подписках, рейдах, покупках наград за баллы
This commit is contained in:
2026-02-22 14:44:30 +03:00
parent 5094834ea1
commit 1fc22ec606
8 changed files with 663 additions and 3 deletions
+2
View File
@@ -43,6 +43,7 @@ SOURCES += \
soundmanager.cpp \ soundmanager.cpp \
tauth.cpp \ tauth.cpp \
ttw_api.cpp \ ttw_api.cpp \
twitcheventsub.cpp \
twitchmessage.cpp \ twitchmessage.cpp \
udatabase.cpp \ udatabase.cpp \
ugeneral.cpp \ ugeneral.cpp \
@@ -81,6 +82,7 @@ HEADERS += \
timerinfo.h \ timerinfo.h \
ttw_api.h \ ttw_api.h \
ttw_types.h \ ttw_types.h \
twitcheventsub.h \
twitchmessage.h \ twitchmessage.h \
udatabase.h \ udatabase.h \
ugeneral.h \ ugeneral.h \
+2
View File
@@ -20,6 +20,7 @@ debug/randomresponses.o
debug/soundmanager.o debug/soundmanager.o
debug/tauth.o debug/tauth.o
debug/ttw_api.o debug/ttw_api.o
debug/twitcheventsub.o
debug/twitchmessage.o debug/twitchmessage.o
debug/udatabase.o debug/udatabase.o
debug/ugeneral.o debug/ugeneral.o
@@ -49,6 +50,7 @@ debug/moc_randomresponses.o
debug/moc_soundmanager.o debug/moc_soundmanager.o
debug/moc_tauth.o debug/moc_tauth.o
debug/moc_ttw_api.o debug/moc_ttw_api.o
debug/moc_twitcheventsub.o
debug/moc_udatabase.o debug/moc_udatabase.o
debug/moc_ugeneral.o debug/moc_ugeneral.o
debug/moc_ulink.o debug/moc_ulink.o
+2
View File
@@ -20,6 +20,7 @@ release/randomresponses.o
release/soundmanager.o release/soundmanager.o
release/tauth.o release/tauth.o
release/ttw_api.o release/ttw_api.o
release/twitcheventsub.o
release/twitchmessage.o release/twitchmessage.o
release/udatabase.o release/udatabase.o
release/ugeneral.o release/ugeneral.o
@@ -49,6 +50,7 @@ release/moc_randomresponses.o
release/moc_soundmanager.o release/moc_soundmanager.o
release/moc_tauth.o release/moc_tauth.o
release/moc_ttw_api.o release/moc_ttw_api.o
release/moc_twitcheventsub.o
release/moc_udatabase.o release/moc_udatabase.o
release/moc_ugeneral.o release/moc_ugeneral.o
release/moc_ulink.o release/moc_ulink.o
+352
View File
@@ -0,0 +1,352 @@
#include "twitcheventsub.h"
#include <QJsonDocument>
#include <QJsonObject>
#include <QJsonArray>
#include <QNetworkReply>
#include <QUrlQuery>
#include <QThread>
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<QAbstractSocket::SocketError>::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;
}
+218
View File
@@ -0,0 +1,218 @@
#ifndef TWITCHEVENTSUB_H
#define TWITCHEVENTSUB_H
#include <QObject>
#include <QWebSocket>
#include <QNetworkAccessManager>
#include <QTimer>
#include <QJsonObject>
// Структуры данных событий (аналог 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
+84 -1
View File
@@ -675,6 +675,8 @@ void uGeneral::setupTwitchComponents()
connect(twitchAPI, &TTwAPI::rateLimitExceeded, this, &uGeneral::onRateLimit); connect(twitchAPI, &TTwAPI::rateLimitExceeded, this, &uGeneral::onRateLimit);
initTwitchAPI(); initTwitchAPI();
m_twitchEventSub = nullptr;
} }
void uGeneral::setupUserWidget() void uGeneral::setupUserWidget()
@@ -832,6 +834,7 @@ uGeneral::~uGeneral()
delete m_neuralTemplateManager; delete m_neuralTemplateManager;
m_neuralTemplateManager = nullptr; m_neuralTemplateManager = nullptr;
} }
delete m_twitchEventSub;
delete m_counterManager; delete m_counterManager;
delete m_createNotifyDialog; delete m_createNotifyDialog;
delete m_createChatDialog; delete m_createChatDialog;
@@ -1497,7 +1500,12 @@ void uGeneral::on_btnGetTokenStreamer_clicked()
return; 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:subscriptions+"
"channel:read:redemptions+" "channel:read:redemptions+"
"user:read:email"; "user:read:email";
@@ -2089,12 +2097,87 @@ void uGeneral::connectToTwitch()
ui->lbBotDays->setText(QString::number(botTokenDays)); ui->lbBotDays->setText(QString::number(botTokenDays));
ui->lbStreamerDays->setText(QString::number(streamerTokenDays)); ui->lbStreamerDays->setText(QString::number(streamerTokenDays));
setTwitchConnected(true); 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<LogLevel>(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() void uGeneral::disconnectFromTwitch()
{ {
m_twitchClient->disconnectFromServer(); m_twitchClient->disconnectFromServer();
if (m_twitchEventSub) {
m_twitchEventSub->disconnectFromTwitch();
}
m_userManager->clear(); m_userManager->clear();
ui->pushButton_2->setText("Подключиться"); ui->pushButton_2->setText("Подключиться");
setTwitchConnected(false); setTwitchConnected(false);
+2 -1
View File
@@ -17,6 +17,7 @@
#include "logmanager.h" #include "logmanager.h"
#include "neuralnetworkmanager.h" #include "neuralnetworkmanager.h"
#include "ttw_api.h" #include "ttw_api.h"
#include "twitcheventsub.h"
#include "user_manager.h" #include "user_manager.h"
#include "webserverchat.h" #include "webserverchat.h"
#include "webservernotify.h" #include "webservernotify.h"
@@ -420,7 +421,7 @@ private:
MediaFileManager *m_TextFiles; MediaFileManager *m_TextFiles;
NeuralTemplateManager *m_neuralTemplateManager; NeuralTemplateManager *m_neuralTemplateManager;
DonationManager *m_donationManager; DonationManager *m_donationManager;
TwitchEventSub *m_twitchEventSub = nullptr;
QList<TimerInfo> m_timers; // Список таймеров QList<TimerInfo> m_timers; // Список таймеров
int m_nextTimerId = 1; // Следующий ID таймера int m_nextTimerId = 1; // Следующий ID таймера
bool m_isTwitchConnected = false; // Статус подключения к Twitch bool m_isTwitchConnected = false; // Статус подключения к Twitch
+1 -1
View File
@@ -42,7 +42,7 @@
<enum>Qt::LeftToRight</enum> <enum>Qt::LeftToRight</enum>
</property> </property>
<property name="currentIndex"> <property name="currentIndex">
<number>3</number> <number>0</number>
</property> </property>
<property name="tabsClosable"> <property name="tabsClosable">
<bool>false</bool> <bool>false</bool>