summaryrefslogtreecommitdiff
path: root/bot/src
diff options
context:
space:
mode:
Diffstat (limited to 'bot/src')
-rw-r--r--bot/src/emotes.cpp195
-rw-r--r--bot/src/emotes.hpp28
-rw-r--r--bot/src/main.cpp40
-rw-r--r--bot/src/schemas/stream.cpp12
-rw-r--r--bot/src/schemas/stream.hpp13
5 files changed, 287 insertions, 1 deletions
diff --git a/bot/src/emotes.cpp b/bot/src/emotes.cpp
new file mode 100644
index 0000000..c19c65b
--- /dev/null
+++ b/bot/src/emotes.cpp
@@ -0,0 +1,195 @@
+#include "emotes.hpp"
+
+#include <algorithm>
+#include <chrono>
+#include <exception>
+#include <optional>
+#include <pqxx/pqxx>
+#include <string>
+#include <thread>
+#include <vector>
+
+#include "config.hpp"
+#include "logger.hpp"
+#include "schemas/stream.hpp"
+#include "utils/string.hpp"
+
+namespace bot::emotes {
+ void handle_emote_event(const EmoteEventBundle &bundle,
+ const schemas::EventType &event_type,
+ const std::string &channel_name,
+ const std::optional<std::string> &author_id,
+ const emotespp::Emote &emote) {
+ std::string c_name = "", author_name = "-", prefix = "";
+
+ if (author_id.has_value()) {
+ std::optional<emotespp::User> user =
+ bundle.stv_api_client.get_user(author_id.value());
+
+ if (user.has_value()) {
+ author_name = user->username;
+ }
+ }
+
+ bool is_stv = event_type >= schemas::EventType::STV_EMOTE_CREATE &&
+ schemas::EventType::STV_EMOTE_UPDATE >= event_type;
+
+ if (is_stv) {
+ std::optional<emotespp::EmoteSet> emote_set =
+ bundle.stv_api_client.get_emote_set(channel_name);
+ if (!emote_set.has_value()) {
+ return;
+ }
+
+ c_name = emote_set->owner.alias_id;
+ prefix = "(7TV)";
+ }
+
+ if (c_name.empty()) {
+ return;
+ }
+
+ pqxx::connection conn(GET_DATABASE_CONNECTION_URL(bundle.configuration));
+ pqxx::work work(conn);
+
+ pqxx::result events = work.exec_params(
+ "SELECT e.id, e.message, array_to_json(e.flags) AS "
+ "flags, c.alias_name AS channel_aname, c.alias_id AS channel_aid FROM "
+ "events e "
+ "INNER JOIN channels c ON c.id = e.channel_id "
+ "WHERE e.event_type = $1 AND e.name = $2",
+ pqxx::params{static_cast<int>(event_type), c_name});
+
+ for (const auto &event : events) {
+ std::vector<std::string> names;
+
+ bool massping_enabled = false;
+ if (!event[2].is_null()) {
+ nlohmann::json j = nlohmann::json::parse(event[2].as<std::string>());
+ massping_enabled = std::any_of(j.begin(), j.end(), [](const auto &x) {
+ return static_cast<int>(x) == static_cast<int>(schemas::MASSPING);
+ });
+ }
+
+ if (massping_enabled) {
+ auto chatters = bundle.helix_client.get_chatters(
+ event[4].as<int>(), bundle.irc_client.get_bot_id());
+
+ std::for_each(chatters.begin(), chatters.end(),
+ [&names](const auto &x) { names.push_back(x.login); });
+ } else {
+ pqxx::result subs = work.exec_params(
+ "SELECT u.alias_name FROM users u "
+ "INNER JOIN events e ON e.id = $1 "
+ "INNER JOIN event_subscriptions es ON es.event_id = e.id "
+ "WHERE u.id = es.user_id",
+ pqxx::params{event[0].as<int>()});
+
+ std::for_each(subs.begin(), subs.end(), [&names](const pqxx::row &x) {
+ names.push_back(x[0].as<std::string>());
+ });
+ }
+
+ std::string base = prefix + " " + event[1].as<std::string>();
+ if (!names.empty()) {
+ base.append(" ยท ");
+ }
+
+ int pos = base.find("{author}");
+ if (pos != std::string::npos) {
+ base.replace(pos, 8, author_name);
+ }
+
+ pos = base.find("{emote}");
+ if (pos != std::string::npos) {
+ base.replace(pos, 7, emote.code);
+ }
+
+ pos = base.find("{old_emote}");
+ if (pos != std::string::npos) {
+ base.replace(pos, 11, emote.original_code.value_or("-"));
+ }
+
+ std::vector<std::string> msgs =
+ utils::string::separate_by_length(base, names, "@", " ", 500);
+
+ for (const auto &msg : msgs) {
+ bundle.irc_client.say(event[3].as<std::string>(), base + msg);
+ }
+ }
+
+ work.commit();
+ conn.close();
+ }
+
+ void create_emote_thread(const EmoteEventBundle *bundle) {
+ log::info("emotes/thread", "Started emote thread.");
+
+ while (true) {
+ pqxx::connection conn(GET_DATABASE_CONNECTION_URL(bundle->configuration));
+ pqxx::work work(conn);
+
+ try {
+ pqxx::result events = work.exec(
+ "SELECT name FROM events WHERE event_type >= 10 AND event_type <= "
+ "12 GROUP BY name");
+
+ auto &ids = bundle->stv_ws_client.get_ids();
+
+ std::vector<std::string> names;
+ std::for_each(events.begin(), events.end(),
+ [&names](const pqxx::row &r) {
+ names.push_back(r[0].as<std::string>());
+ });
+
+ // adding new emote sets
+ for (const std::string &name : names) {
+ std::optional<emotespp::User> stv_user =
+ bundle->stv_api_client.get_user_by_twitch_id(std::stoi(name));
+
+ if (!stv_user.has_value()) {
+ continue;
+ }
+
+ if (!std::any_of(ids.begin(), ids.end(),
+ [&stv_user](const std::string &id) {
+ return id == stv_user->emote_set_id;
+ })) {
+ bundle->stv_ws_client.subscribe_emote_set(stv_user->emote_set_id);
+ log::info(
+ "emotes/thread",
+ "Subscribing to " + stv_user->emote_set_id + " (" + name + ")");
+ }
+ }
+
+ // removing old emote sets
+ std::for_each(
+ ids.begin(), ids.end(), [&names, &bundle](const std::string &id) {
+ std::optional<emotespp::EmoteSet> stv_set =
+ bundle->stv_api_client.get_emote_set(id);
+
+ if (!stv_set.has_value()) {
+ return;
+ }
+
+ if (!std::any_of(names.begin(), names.end(),
+ [&stv_set](const std::string &id) {
+ return id == stv_set->owner.alias_id;
+ })) {
+ bundle->stv_ws_client.unsubscribe_emote_set(id);
+ log::info("emotes/thread", "Unsubscribing from " + id + " (" +
+ stv_set->owner.alias_id + ")");
+ }
+ });
+ } catch (std::exception ex) {
+ log::error("emotes/thread",
+ "Error occurred in emote thread: " + std::string(ex.what()));
+ }
+
+ work.commit();
+ conn.close();
+
+ std::this_thread::sleep_for(std::chrono::seconds(30));
+ }
+ }
+} \ No newline at end of file
diff --git a/bot/src/emotes.hpp b/bot/src/emotes.hpp
new file mode 100644
index 0000000..7493edf
--- /dev/null
+++ b/bot/src/emotes.hpp
@@ -0,0 +1,28 @@
+#pragma once
+
+#include <emotespp/seventv.hpp>
+#include <optional>
+#include <string>
+
+#include "api/twitch/helix_client.hpp"
+#include "config.hpp"
+#include "irc/client.hpp"
+#include "schemas/stream.hpp"
+
+namespace bot::emotes {
+ struct EmoteEventBundle {
+ irc::Client &irc_client;
+ const api::twitch::HelixClient &helix_client;
+ emotespp::SevenTVWebsocketClient &stv_ws_client;
+ const emotespp::SevenTVAPIClient &stv_api_client;
+ const Configuration &configuration;
+ };
+
+ void handle_emote_event(const EmoteEventBundle &bundle,
+ const schemas::EventType &event_type,
+ const std::string &channel_name,
+ const std::optional<std::string> &author_id,
+ const emotespp::Emote &emote);
+
+ void create_emote_thread(const EmoteEventBundle *bundle);
+} \ No newline at end of file
diff --git a/bot/src/main.cpp b/bot/src/main.cpp
index c48d54a..733c0eb 100644
--- a/bot/src/main.cpp
+++ b/bot/src/main.cpp
@@ -1,3 +1,4 @@
+#include <emotespp/seventv.hpp>
#include <memory>
#include <optional>
#include <pqxx/pqxx>
@@ -12,12 +13,14 @@
#include "commands/lua.hpp"
#include "commands/response.hpp"
#include "config.hpp"
+#include "emotes.hpp"
#include "github.hpp"
#include "handlers.hpp"
#include "irc/client.hpp"
#include "irc/message.hpp"
#include "localization/localization.hpp"
#include "logger.hpp"
+#include "schemas/stream.hpp"
#include "stream.hpp"
#include "timer.hpp"
@@ -59,6 +62,9 @@ int main(int argc, char *argv[]) {
bot::api::twitch::HelixClient helix_client(cfg.twitch_credentials.token,
cfg.twitch_credentials.client_id);
+ emotespp::SevenTVWebsocketClient seventv_emote_listener;
+ emotespp::SevenTVAPIClient seventv_api_client;
+
client.join(client.get_bot_username());
pqxx::connection conn(GET_DATABASE_CONNECTION_URL(cfg));
@@ -109,6 +115,38 @@ int main(int argc, char *argv[]) {
bot::GithubListener github_listener(cfg, client, helix_client);
+ bot::emotes::EmoteEventBundle emote_bundle{
+ client, helix_client, seventv_emote_listener, seventv_api_client, cfg};
+
+ seventv_emote_listener.on_emote_create(
+ [&emote_bundle](const std::string &channel_name,
+ const std::optional<std::string> &author_id,
+ const emotespp::Emote &emote) {
+ bot::emotes::handle_emote_event(emote_bundle,
+ bot::schemas::STV_EMOTE_CREATE,
+ channel_name, author_id, emote);
+ });
+
+ seventv_emote_listener.on_emote_delete(
+ [&emote_bundle](const std::string &channel_name,
+ const std::optional<std::string> &author_id,
+ const emotespp::Emote &emote) {
+ bot::emotes::handle_emote_event(emote_bundle,
+ bot::schemas::STV_EMOTE_DELETE,
+ channel_name, author_id, emote);
+ });
+
+ seventv_emote_listener.on_emote_update(
+ [&emote_bundle](const std::string &channel_name,
+ const std::optional<std::string> &author_id,
+ const emotespp::Emote &emote) {
+ bot::emotes::handle_emote_event(emote_bundle,
+ bot::schemas::STV_EMOTE_UPDATE,
+ channel_name, author_id, emote);
+ });
+
+ seventv_emote_listener.start();
+
client.on<bot::irc::MessageType::Privmsg>(
[&client, &command_loader, &localization, &cfg, &helix_client](
const bot::irc::Message<bot::irc::MessageType::Privmsg> &message) {
@@ -130,6 +168,8 @@ int main(int argc, char *argv[]) {
threads.push_back(std::thread(&bot::stream::StreamListenerClient::run,
&stream_listener_client));
threads.push_back(std::thread(&bot::GithubListener::run, &github_listener));
+ threads.push_back(
+ std::thread(bot::emotes::create_emote_thread, &emote_bundle));
for (auto &thread : threads) {
thread.join();
diff --git a/bot/src/schemas/stream.cpp b/bot/src/schemas/stream.cpp
index 7671de5..d4f769b 100644
--- a/bot/src/schemas/stream.cpp
+++ b/bot/src/schemas/stream.cpp
@@ -12,6 +12,12 @@ namespace bot::schemas {
return EventType::GAME;
} else if (type == "github") {
return EventType::GITHUB;
+ } else if (type == "7tv_emote_add") {
+ return EventType::STV_EMOTE_CREATE;
+ } else if (type == "7tv_emote_delete") {
+ return EventType::STV_EMOTE_DELETE;
+ } else if (type == "7tv_emote_update") {
+ return EventType::STV_EMOTE_UPDATE;
} else {
return EventType::CUSTOM;
}
@@ -28,6 +34,12 @@ namespace bot::schemas {
return "game";
} else if (type == GITHUB) {
return "github";
+ } else if (type == STV_EMOTE_CREATE) {
+ return "7tv_emote_add";
+ } else if (type == STV_EMOTE_DELETE) {
+ return "7tv_emote_delete";
+ } else if (type == STV_EMOTE_UPDATE) {
+ return "7tv_emote_update";
} else {
return "custom";
}
diff --git a/bot/src/schemas/stream.hpp b/bot/src/schemas/stream.hpp
index acc6af1..357d476 100644
--- a/bot/src/schemas/stream.hpp
+++ b/bot/src/schemas/stream.hpp
@@ -4,7 +4,18 @@
#include <string>
namespace bot::schemas {
- enum EventType { LIVE, OFFLINE, TITLE, GAME, GITHUB = 10, CUSTOM = 99 };
+ enum EventType {
+ LIVE,
+ OFFLINE,
+ TITLE,
+ GAME,
+ STV_EMOTE_CREATE = 10,
+ STV_EMOTE_DELETE = 11,
+ STV_EMOTE_UPDATE = 12,
+ GITHUB = 40,
+ CUSTOM = 99
+ };
+
EventType string_to_event_type(const std::string &type);
std::string event_type_to_string(const int &type);