diff options
Diffstat (limited to 'src/irc')
| -rw-r--r-- | src/irc/client.cpp | 155 | ||||
| -rw-r--r-- | src/irc/client.hpp | 58 | ||||
| -rw-r--r-- | src/irc/message.cpp | 30 | ||||
| -rw-r--r-- | src/irc/message.hpp | 130 |
4 files changed, 0 insertions, 373 deletions
diff --git a/src/irc/client.cpp b/src/irc/client.cpp deleted file mode 100644 index 018736e..0000000 --- a/src/irc/client.cpp +++ /dev/null @@ -1,155 +0,0 @@ -#include "client.hpp" - -#include <ixwebsocket/IXWebSocketMessage.h> -#include <ixwebsocket/IXWebSocketMessageType.h> - -#include <algorithm> -#include <optional> -#include <string> -#include <vector> - -#include "../logger.hpp" -#include "cpr/api.h" -#include "cpr/cprtypes.h" -#include "cpr/response.h" -#include "message.hpp" -#include "nlohmann/json.hpp" - -using namespace bot::irc; - -Client::Client(std::string client_id, std::string token) { - this->client_id = client_id; - this->token = token; - - this->host = "wss://irc-ws.chat.twitch.tv"; - this->port = "443"; - - this->websocket.setUrl(this->host + ":" + this->port); - - // getting token owner - cpr::Response response = cpr::Get( - cpr::Url{"https://api.twitch.tv/helix/users"}, cpr::Bearer{this->token}, - cpr::Header{{"Client-Id", this->client_id}}); - - if (response.status_code != 200) { - log::warn("IRC", "Failed to get bot username from Twitch API: " + - std::to_string(response.status_code) + " " + - response.status_line); - } else { - nlohmann::json j = nlohmann::json::parse(response.text); - - auto d = j["data"][0]; - this->id = std::stoi(d["id"].get<std::string>()); - this->username = d["login"]; - } -} - -void Client::run() { - this->websocket.setOnMessageCallback( - [this](const ix::WebSocketMessagePtr &msg) { - switch (msg->type) { - case ix::WebSocketMessageType::Message: { - log::debug("IRC", "Received message: " + msg->str); - - std::vector<std::string> lines = - utils::string::split_text(msg->str, '\n'); - - for (std::string &line : lines) { - line.erase(std::remove_if(line.begin(), line.end(), - [](char c) { - return c == '\n' || c == '\r' || - c == '\t'; - }), - line.end()); - - std::optional<MessageType> type = define_message_type(line); - - if (!type.has_value()) { - break; - } - - MessageType m_type = type.value(); - - if (m_type == MessageType::Privmsg) { - std::optional<Message<MessageType::Privmsg>> message = - parse_message<MessageType::Privmsg>(line); - - if (message.has_value()) { - this->onPrivmsg(message.value()); - } - } - } - - break; - } - case ix::WebSocketMessageType::Open: { - log::info("IRC", "Connected to Twitch IRC"); - this->is_connected = true; - this->authorize(); - for (const auto &msg : this->pool) { - this->websocket.send(msg); - } - this->pool.clear(); - break; - } - case ix::WebSocketMessageType::Close: { - log::info("IRC", "Twitch IRC connection closed"); - this->is_connected = false; - - for (const auto &x : this->joined_channels) { - this->raw("JOIN #" + x); - } - - break; - } - default: { - break; - } - } - }); - - this->websocket.start(); -} - -void Client::say(const std::string &channel_login, const std::string &message) { - this->raw("PRIVMSG #" + channel_login + " :" + message); - log::debug("IRC", "Sent '" + message + "' in #" + channel_login); -} - -bool Client::join(const std::string &channel_login) { - auto already_joined = - std::any_of(this->joined_channels.begin(), this->joined_channels.end(), - [&](const auto &x) { return x == channel_login; }); - - if (!already_joined) { - this->raw("JOIN #" + channel_login); - this->joined_channels.push_back(channel_login); - log::info("IRC", "Joined #" + channel_login); - } - - return !already_joined; -} - -void Client::raw(const std::string &raw_message) { - std::string msg = raw_message + "\r\n"; - if (this->is_connected) { - this->websocket.send(msg); - } else { - this->pool.push_back(msg); - } -} - -void Client::authorize() { - if (this->username.empty() || this->token.empty()) { - log::error("IRC", "Bot username and token must be set for authorization!"); - return; - } - - log::info("IRC", "Authorizing on Twitch IRC servers..."); - - this->raw("PASS oauth:" + this->token); - this->raw("NICK " + this->username); - this->raw("CAP REQ :twitch.tv/membership"); - this->raw("CAP REQ :twitch.tv/commands"); - this->raw("CAP REQ :twitch.tv/tags"); -} diff --git a/src/irc/client.hpp b/src/irc/client.hpp deleted file mode 100644 index cff867f..0000000 --- a/src/irc/client.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include <ixwebsocket/IXWebSocket.h> - -#include <string> -#include <vector> - -#include "message.hpp" - -namespace bot { - namespace irc { - class Client { - public: - Client(std::string client_id, std::string token); - ~Client() = default; - - void run(); - - void say(const std::string &channel_login, const std::string &message); - bool join(const std::string &channel_login); - void raw(const std::string &raw_message); - - template <MessageType T> - void on(typename MessageHandler<T>::fn function) { - switch (T) { - case Privmsg: - this->onPrivmsg = function; - break; - default: - break; - } - } - - const std::string &get_bot_username() const { return this->username; }; - const int &get_bot_id() const { return this->id; } - - private: - void authorize(); - - std::string client_id, token, username; - - std::string host; - std::string port; - - int id; - - ix::WebSocket websocket; - - bool is_connected = false; - std::vector<std::string> pool; - - std::vector<std::string> joined_channels; - - // Message handlers - typename MessageHandler<MessageType::Privmsg>::fn onPrivmsg; - }; - } -} diff --git a/src/irc/message.cpp b/src/irc/message.cpp deleted file mode 100644 index 569e691..0000000 --- a/src/irc/message.cpp +++ /dev/null @@ -1,30 +0,0 @@ -#include "message.hpp" - -#include <optional> -#include <string> -#include <vector> - -namespace bot { - namespace irc { - std::optional<MessageType> define_message_type(const std::string &msg) { - std::vector<std::string> parts = utils::string::split_text(msg, ' '); - int i; - - if (msg[0] == '@') { - i = 2; - } else if (msg[0] == ':') { - i = 1; - } else { - return std::nullopt; - } - - if (parts[i] == "NOTICE") { - return MessageType::Notice; - } else if (parts[i] == "PRIVMSG") { - return MessageType::Privmsg; - } - - return std::nullopt; - } - } -} diff --git a/src/irc/message.hpp b/src/irc/message.hpp deleted file mode 100644 index 164d7ca..0000000 --- a/src/irc/message.hpp +++ /dev/null @@ -1,130 +0,0 @@ -#pragma once - -#include <functional> -#include <map> -#include <optional> -#include <sstream> -#include <string> -#include <vector> - -#include "../utils/string.hpp" - -namespace bot { - namespace irc { - enum MessageType { Privmsg, Notice }; - std::optional<MessageType> define_message_type(const std::string &msg); - - struct MessageSender { - std::string login; - std::string display_name; - int id; - - std::map<std::string, int> badges; - - // More fields will be here - }; - - struct MessageSource { - std::string login; - int id; - }; - - template <MessageType T> - struct Message; - - template <> - struct Message<MessageType::Privmsg> { - MessageSender sender; - MessageSource source; - std::string message; - }; - - template <MessageType T> - std::optional<Message<T>> parse_message(const std::string &msg) { - std::vector<std::string> parts = utils::string::split_text(msg, ' '); - - if (T == MessageType::Privmsg) { - MessageSender sender; - MessageSource source; - - Message<MessageType::Privmsg> message; - - std::string tags = parts[0]; - tags = tags.substr(1, tags.length()); - parts.erase(parts.begin()); - - std::string user = parts[0]; - user = user.substr(1, user.length()); - - std::vector<std::string> user_parts = - utils::string::split_text(user, '!'); - - sender.login = user_parts[0]; - - parts.erase(parts.begin(), parts.begin() + 2); - - std::string channel_login = parts[0]; - source.login = channel_login.substr(1, channel_login.length()); - - parts.erase(parts.begin()); - - std::string chat_message = utils::string::join_vector(parts, ' '); - message.message = chat_message.substr(1, chat_message.length()); - - std::vector<std::string> tags_parts = - utils::string::split_text(tags, ';'); - - for (const std::string &tag : tags_parts) { - std::istringstream iss(tag); - std::string key; - std::string value; - - std::getline(iss, key, '='); - std::getline(iss, value); - - if (key == "display-name") { - sender.display_name = value; - } else if (key == "room-id") { - source.id = std::stoi(value); - } else if (key == "user-id") { - sender.id = std::stoi(value); - } else if (key == "badges") { - std::vector<std::string> badges = - utils::string::split_text(value, ','); - - std::map<std::string, int> map; - - for (const auto &badge : badges) { - std::istringstream iss2(badge); - std::string name; - std::string value; - - std::getline(iss2, name, '/'); - std::getline(iss2, value); - - map.insert({name, std::stoi(value)}); - } - - sender.badges = map; - } - } - - message.sender = sender; - message.source = source; - - return message; - } - - return std::nullopt; - } - - template <MessageType T> - struct MessageHandler; - - template <> - struct MessageHandler<MessageType::Privmsg> { - using fn = std::function<void(Message<Privmsg> message)>; - }; - - } -} |
