#include "websocketclient.h" #include #include #include #include #include #include WebSocketClient::WebSocketClient(QObject *parent) : QObject(parent), m_webSocket(nullptr), m_isConnected(false) { m_pingTimer = new QTimer(this); m_pingTimer->setInterval(60000); // Ping каждые 30 секунд m_channelJoined = false; // Используем новый синтаксис подключения сигналов для Qt5 connect(m_pingTimer, &QTimer::timeout, this, &WebSocketClient::onPingTimeout); } WebSocketClient::~WebSocketClient() { disconnectFromServer(); delete m_webSocket; } bool WebSocketClient::connectToServer(const QString &url) { if (m_webSocket) { disconnectFromServer(); delete m_webSocket; m_webSocket = nullptr; } m_webSocket = new QWebSocket(); // Подключаем сигналы с новым синтаксисом Qt5 connect(m_webSocket, &QWebSocket::connected, this, &WebSocketClient::onConnectedInternal); connect(m_webSocket, &QWebSocket::disconnected, this, &WebSocketClient::onDisconnectedInternal); connect(m_webSocket, &QWebSocket::textMessageReceived, this, &WebSocketClient::onTextMessageReceived); connect(m_webSocket, QOverload::of(&QWebSocket::error), this, &WebSocketClient::onErrorInternal); connect(m_webSocket, &QWebSocket::sslErrors, this, &WebSocketClient::onSslErrors); m_webSocket->open(QUrl(url)); return true; } void WebSocketClient::connectToTwitchChat(const QString &oauthToken, const QString &nickname, const QString &channel) { m_nickname = nickname; m_currentChannel = channel; m_oauthToken = oauthToken; // URL для подключения к Twitch IRC через WebSocket QString url = "wss://irc-ws.chat.twitch.tv:443"; connectToServer(url); } void WebSocketClient::onConnectedInternal() { m_isConnected = true; m_pingTimer->start(); // Аутентификация в Twitch authenticate(m_oauthToken, m_nickname); emit onConnected(); } void WebSocketClient::onDisconnectedInternal() { m_isConnected = false; m_pingTimer->stop(); m_channelJoined = false; emit onDisconnected(); } void WebSocketClient::send(const QString &data) { if (m_webSocket && m_isConnected) { m_webSocket->sendTextMessage(data); } else { } } void WebSocketClient::sendMessage(const QString &message) { send(message); } void WebSocketClient::sendIrcCommand(const QString &command, const QString ¶meters) { QString message = command; if (!parameters.isEmpty()) { message += " " + parameters; } message += "\r\n"; send(message); } void WebSocketClient::authenticate(const QString &oauthToken, const QString &nickname) { // PASS команда для аутентификации if (!oauthToken.startsWith("oauth:")) { sendIrcCommand("PASS", "oauth:" + oauthToken); } else { sendIrcCommand("PASS", oauthToken); } Sleep(1000); // NICK команда sendIrcCommand("NICK", nickname); // CAP REQ для получения дополнительных возможностей sendIrcCommand("CAP REQ", ":twitch.tv/membership twitch.tv/tags twitch.tv/commands"); } void WebSocketClient::joinChannel(const QString &channel) { if (m_isConnected) { m_channelJoined = true; sendIrcCommand("JOIN", "#" + channel.toLower()); } } void WebSocketClient::sendChatMessage(const QString &channel, const QString &message) { if (m_isConnected) { sendIrcCommand("PRIVMSG", "#" + channel.toLower() + " :" + message); } } void WebSocketClient::pingServer() { if (m_isConnected) { send("PING :tmi.twitch.tv\r\n"); } } void WebSocketClient::onPingTimeout() { pingServer(); } void WebSocketClient::onTextMessageReceived(const QString &message) { // Обработка PING от сервера if (message.startsWith("PING")) { send("PONG :tmi.twitch.tv\r\n"); return; } // Обработка успешной аутентификации if (message.contains("001") && !m_currentChannel.isEmpty()) { joinChannel(m_currentChannel); return; // Добавить return после первого успешного подключения } // Обработка сообщений чата if (message.contains("PRIVMSG")) { // Извлекаем информацию из сообщения // Формат: :nick!nick@nick.tmi.twitch.tv PRIVMSG #channel :message QString cleanMessage = message; emit onNewMessage(cleanMessage); // Удаляем служебную информацию для чистого отображения int msgIndex = cleanMessage.indexOf("PRIVMSG"); if (msgIndex != -1) { int channelIndex = cleanMessage.indexOf("#", msgIndex); int messageIndex = cleanMessage.indexOf(" :", channelIndex); if (messageIndex != -1) { QString chatMessage = cleanMessage.mid(messageIndex + 2).trimmed(); QString sender = cleanMessage.mid(1, cleanMessage.indexOf("!") - 1); QString channel = cleanMessage.mid(channelIndex + 1, messageIndex - channelIndex - 1); } } } } void WebSocketClient::onErrorInternal(QAbstractSocket::SocketError error) { QString errorMsg = QString("WebSocket error: %1 - %2") .arg(error) .arg(m_webSocket ? m_webSocket->errorString() : "Unknown error"); emit onError(errorMsg); } void WebSocketClient::onSslErrors(const QList &errors) { QStringList errorMessages; foreach (const QSslError &error, errors) { errorMessages.append(error.errorString()); } // В разработке можно игнорировать SSL ошибки, но в продакшене это опасно if (m_webSocket) { m_webSocket->ignoreSslErrors(); } } void WebSocketClient::disconnectFromServer() { if (m_webSocket && m_isConnected) { // Отправляем команду выхода из канала для Twitch if (!m_currentChannel.isEmpty()) { sendIrcCommand("PART", "#" + m_currentChannel.toLower()); } m_webSocket->close(); m_isConnected = false; m_pingTimer->stop(); } } bool WebSocketClient::isConnected() const { return m_isConnected; }