summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorilotterytea <iltsu@alright.party>2024-05-18 14:48:12 +0500
committerilotterytea <iltsu@alright.party>2024-05-18 14:48:12 +0500
commitd1793df1eda463b10107d41785ad1d7f055ed476 (patch)
treefd3e41c3b4a05924748ae4b762e1ae55a0bc815c /src
parentd7a2de17e9b7931f68b5b4079b1c36866a19d343 (diff)
upd: moved the bot part to a relative subfolder
Diffstat (limited to 'src')
-rw-r--r--src/api/twitch/helix_client.cpp144
-rw-r--r--src/api/twitch/helix_client.hpp31
-rw-r--r--src/api/twitch/schemas/stream.hpp35
-rw-r--r--src/api/twitch/schemas/user.hpp10
-rw-r--r--src/bundle.hpp15
-rw-r--r--src/commands/command.cpp104
-rw-r--r--src/commands/command.hpp55
-rw-r--r--src/commands/request.hpp25
-rw-r--r--src/commands/request_util.cpp205
-rw-r--r--src/commands/request_util.hpp13
-rw-r--r--src/commands/response_error.hpp222
-rw-r--r--src/config.cpp84
-rw-r--r--src/config.hpp54
-rw-r--r--src/constants.hpp13
-rw-r--r--src/handlers.cpp85
-rw-r--r--src/handlers.hpp14
-rw-r--r--src/irc/client.cpp155
-rw-r--r--src/irc/client.hpp58
-rw-r--r--src/irc/message.cpp30
-rw-r--r--src/irc/message.hpp130
-rw-r--r--src/localization/line_id.cpp100
-rw-r--r--src/localization/line_id.hpp56
-rw-r--r--src/localization/localization.cpp132
-rw-r--r--src/localization/localization.hpp37
-rw-r--r--src/logger.cpp96
-rw-r--r--src/logger.hpp16
-rw-r--r--src/main.cpp127
-rw-r--r--src/modules/custom_command.hpp96
-rw-r--r--src/modules/event.hpp145
-rw-r--r--src/modules/help.hpp31
-rw-r--r--src/modules/join.hpp91
-rw-r--r--src/modules/massping.hpp62
-rw-r--r--src/modules/notify.hpp131
-rw-r--r--src/modules/ping.hpp59
-rw-r--r--src/modules/timer.hpp112
-rw-r--r--src/schemas/channel.hpp76
-rw-r--r--src/schemas/stream.cpp17
-rw-r--r--src/schemas/stream.hpp11
-rw-r--r--src/schemas/user.hpp73
-rw-r--r--src/stream.cpp200
-rw-r--r--src/stream.hpp41
-rw-r--r--src/timer.cpp70
-rw-r--r--src/timer.hpp9
-rw-r--r--src/utils/chrono.cpp48
-rw-r--r--src/utils/chrono.hpp11
-rw-r--r--src/utils/string.cpp66
-rw-r--r--src/utils/string.hpp32
47 files changed, 0 insertions, 3427 deletions
diff --git a/src/api/twitch/helix_client.cpp b/src/api/twitch/helix_client.cpp
deleted file mode 100644
index 04d630b..0000000
--- a/src/api/twitch/helix_client.cpp
+++ /dev/null
@@ -1,144 +0,0 @@
-#include "helix_client.hpp"
-
-#include <nlohmann/json.hpp>
-#include <string>
-#include <vector>
-
-#include "cpr/api.h"
-#include "cpr/bearer.h"
-#include "cpr/cprtypes.h"
-#include "cpr/response.h"
-#include "schemas/stream.hpp"
-#include "schemas/user.hpp"
-
-namespace bot::api::twitch {
- HelixClient::HelixClient(const std::string &token,
- const std::string &client_id) {
- this->token = token;
- this->client_id = client_id;
- }
-
- std::vector<schemas::User> HelixClient::get_users(
- const std::vector<std::string> &logins) const {
- std::string s;
-
- for (auto i = logins.begin(); i != logins.end(); i++) {
- std::string start;
- if (i == logins.begin()) {
- start = "?";
- } else {
- start = "&";
- }
-
- s += start + "login=" + *i;
- }
-
- return this->get_users_by_query(s);
- }
-
- std::vector<schemas::User> HelixClient::get_users(
- const std::vector<int> &ids) const {
- std::string s;
-
- for (auto i = ids.begin(); i != ids.end(); i++) {
- std::string start;
- if (i == ids.begin()) {
- start = "?";
- } else {
- start = "&";
- }
-
- s += start + "id=" + std::to_string(*i);
- }
-
- return this->get_users_by_query(s);
- }
-
- std::vector<schemas::User> HelixClient::get_users_by_query(
- const std::string &query) const {
- cpr::Response response = cpr::Get(
- cpr::Url{this->base_url + "/users" + query}, cpr::Bearer{this->token},
- cpr::Header{{"Client-Id", this->client_id.c_str()}});
-
- if (response.status_code != 200) {
- return {};
- }
-
- std::vector<schemas::User> users;
-
- nlohmann::json j = nlohmann::json::parse(response.text);
-
- for (const auto &d : j["data"]) {
- schemas::User u{std::stoi(d["id"].get<std::string>()), d["login"]};
-
- users.push_back(u);
- }
-
- return users;
- }
-
- std::vector<schemas::User> HelixClient::get_chatters(
- const int &broadcaster_id, const int &moderator_id) const {
- cpr::Response response =
- cpr::Get(cpr::Url{this->base_url + "/chat/chatters?broadcaster_id=" +
- std::to_string(broadcaster_id) +
- "&moderator_id=" + std::to_string(moderator_id)},
- cpr::Bearer{this->token},
- cpr::Header{{"Client-Id", this->client_id.c_str()}});
-
- if (response.status_code != 200) {
- return {};
- }
-
- std::vector<schemas::User> users;
-
- nlohmann::json j = nlohmann::json::parse(response.text);
-
- for (const auto &d : j["data"]) {
- schemas::User u{std::stoi(d["user_id"].get<std::string>()),
- d["user_login"]};
-
- users.push_back(u);
- }
-
- return users;
- }
-
- std::vector<schemas::Stream> HelixClient::get_streams(
- const std::vector<int> &ids) const {
- std::string s;
-
- for (auto i = ids.begin(); i != ids.end(); i++) {
- std::string start;
- if (i == ids.begin()) {
- start = "?";
- } else {
- start = "&";
- }
-
- s += start + "user_id=" + std::to_string(*i);
- }
-
- cpr::Response response = cpr::Get(
- cpr::Url{this->base_url + "/streams" + s}, cpr::Bearer{this->token},
- cpr::Header{{"Client-Id", this->client_id.c_str()}});
-
- if (response.status_code != 200) {
- return {};
- }
-
- std::vector<schemas::Stream> streams;
-
- nlohmann::json j = nlohmann::json::parse(response.text);
-
- for (const auto &d : j["data"]) {
- schemas::Stream u{std::stoi(d["user_id"].get<std::string>()),
- d["user_login"], d["game_name"], d["title"],
- d["started_at"]};
-
- streams.push_back(u);
- }
-
- return streams;
- }
-}
diff --git a/src/api/twitch/helix_client.hpp b/src/api/twitch/helix_client.hpp
deleted file mode 100644
index 27a9fa3..0000000
--- a/src/api/twitch/helix_client.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include <string>
-#include <vector>
-
-#include "schemas/stream.hpp"
-#include "schemas/user.hpp"
-
-namespace bot::api::twitch {
- class HelixClient {
- public:
- HelixClient(const std::string &token, const std::string &client_id);
- ~HelixClient() = default;
-
- std::vector<schemas::User> get_users(
- const std::vector<std::string> &logins) const;
- std::vector<schemas::User> get_users(const std::vector<int> &ids) const;
-
- std::vector<schemas::User> get_chatters(const int &broadcaster_id,
- const int &moderator_id) const;
-
- std::vector<schemas::Stream> get_streams(
- const std::vector<int> &ids) const;
-
- private:
- std::vector<schemas::User> get_users_by_query(
- const std::string &query) const;
- std::string token, client_id;
- const std::string base_url = "https://api.twitch.tv/helix";
- };
-}
diff --git a/src/api/twitch/schemas/stream.hpp b/src/api/twitch/schemas/stream.hpp
deleted file mode 100644
index e3d485e..0000000
--- a/src/api/twitch/schemas/stream.hpp
+++ /dev/null
@@ -1,35 +0,0 @@
-#pragma once
-
-#include <chrono>
-#include <string>
-
-#include "../../../utils/chrono.hpp"
-
-namespace bot::api::twitch::schemas {
- class Stream {
- public:
- Stream(int user_id, std::string user_login, std::string game_name,
- std::string title, std::string started_at)
- : user_id(user_id),
- user_login(user_login),
- game_name(game_name),
- title(title),
- started_at(utils::chrono::string_to_time_point(
- started_at, "%Y-%m-%dT%H:%M:%SZ")) {}
-
- Stream(int user_id) : user_id(user_id) {}
-
- const int &get_user_id() const { return this->user_id; }
- const std::string &get_user_login() const { return this->user_login; }
- const std::string &get_game_name() const { return this->game_name; }
- const std::string &get_title() const { return this->title; }
- const std::chrono::system_clock::time_point &get_started_at() const {
- return this->started_at;
- }
-
- private:
- int user_id;
- std::string user_login, game_name, title;
- std::chrono::system_clock::time_point started_at;
- };
-}
diff --git a/src/api/twitch/schemas/user.hpp b/src/api/twitch/schemas/user.hpp
deleted file mode 100644
index 288ec72..0000000
--- a/src/api/twitch/schemas/user.hpp
+++ /dev/null
@@ -1,10 +0,0 @@
-#pragma once
-
-#include <string>
-
-namespace bot::api::twitch::schemas {
- struct User {
- int id;
- std::string login;
- };
-}
diff --git a/src/bundle.hpp b/src/bundle.hpp
deleted file mode 100644
index d30f5f8..0000000
--- a/src/bundle.hpp
+++ /dev/null
@@ -1,15 +0,0 @@
-#pragma once
-
-#include "api/twitch/helix_client.hpp"
-#include "config.hpp"
-#include "irc/client.hpp"
-#include "localization/localization.hpp"
-
-namespace bot {
- struct InstanceBundle {
- irc::Client &irc_client;
- const api::twitch::HelixClient &helix_client;
- const bot::loc::Localization &localization;
- const Configuration &configuration;
- };
-}
diff --git a/src/commands/command.cpp b/src/commands/command.cpp
deleted file mode 100644
index e3b45b1..0000000
--- a/src/commands/command.cpp
+++ /dev/null
@@ -1,104 +0,0 @@
-#include "command.hpp"
-
-#include <algorithm>
-#include <chrono>
-#include <ctime>
-#include <memory>
-#include <optional>
-#include <pqxx/pqxx>
-#include <string>
-
-#include "../bundle.hpp"
-#include "../modules/custom_command.hpp"
-#include "../modules/event.hpp"
-#include "../modules/help.hpp"
-#include "../modules/join.hpp"
-#include "../modules/massping.hpp"
-#include "../modules/notify.hpp"
-#include "../modules/ping.hpp"
-#include "../modules/timer.hpp"
-#include "../utils/chrono.hpp"
-#include "request.hpp"
-
-namespace bot {
- namespace command {
- CommandLoader::CommandLoader() {
- this->add_command(std::make_unique<mod::Ping>());
- this->add_command(std::make_unique<mod::Massping>());
- this->add_command(std::make_unique<mod::Event>());
- this->add_command(std::make_unique<mod::Notify>());
- this->add_command(std::make_unique<mod::Join>());
- this->add_command(std::make_unique<mod::CustomCommand>());
- this->add_command(std::make_unique<mod::Timer>());
- this->add_command(std::make_unique<mod::Help>());
- }
-
- void CommandLoader::add_command(std::unique_ptr<Command> command) {
- this->commands.push_back(std::move(command));
- }
-
- std::optional<std::variant<std::vector<std::string>, std::string>>
- CommandLoader::run(const InstanceBundle &bundle,
- const Request &request) const {
- auto command = std::find_if(
- this->commands.begin(), this->commands.end(),
- [&](const auto &x) { return x->get_name() == request.command_id; });
-
- if (command == this->commands.end()) {
- return std::nullopt;
- }
-
- if ((*command)->get_permission_level() >
- request.user_rights.get_level()) {
- return std::nullopt;
- }
-
- pqxx::work work(request.conn);
-
- pqxx::result action_query = work.exec(
- "SELECT sent_at FROM actions WHERE user_id = " +
- std::to_string(request.user.get_id()) +
- " AND channel_id = " + std::to_string(request.channel.get_id()) +
- " AND command = '" + request.command_id + "' ORDER BY sent_at DESC");
-
- if (!action_query.empty()) {
- auto last_sent_at = utils::chrono::string_to_time_point(
- action_query[0][0].as<std::string>());
-
- auto now = std::chrono::system_clock::now();
- auto now_time_it = std::chrono::system_clock::to_time_t(now);
- auto now_tm = std::gmtime(&now_time_it);
- now = std::chrono::system_clock::from_time_t(std::mktime(now_tm));
-
- auto difference = std::chrono::duration_cast<std::chrono::seconds>(
- now - last_sent_at);
-
- if (difference.count() < command->get()->get_delay_seconds()) {
- return std::nullopt;
- }
- }
-
- std::string arguments;
-
- if (request.subcommand_id.has_value()) {
- arguments += request.subcommand_id.value() + " ";
- }
-
- if (request.message.has_value()) {
- arguments += request.message.value();
- }
-
- work.exec(
- "INSERT INTO actions(user_id, channel_id, command, arguments, "
- "full_message) VALUES (" +
- std::to_string(request.user.get_id()) + ", " +
- std::to_string(request.channel.get_id()) + ", '" +
- request.command_id + "', '" + arguments + "', '" +
- request.irc_message.message + "')");
-
- work.commit();
-
- return (*command)->run(bundle, request);
- }
- }
-}
diff --git a/src/commands/command.hpp b/src/commands/command.hpp
deleted file mode 100644
index 40ec114..0000000
--- a/src/commands/command.hpp
+++ /dev/null
@@ -1,55 +0,0 @@
-#pragma once
-
-#include <memory>
-#include <optional>
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "request.hpp"
-
-namespace bot {
- namespace command {
- enum CommandArgument {
- SUBCOMMAND,
- MESSAGE,
- INTERVAL,
- NAME,
- TARGET,
- VALUE,
- AMOUNT,
- };
-
- class Command {
- public:
- virtual std::string get_name() const = 0;
- virtual std::variant<std::vector<std::string>, std::string> run(
- const InstanceBundle &bundle, const Request &request) const = 0;
- virtual schemas::PermissionLevel get_permission_level() const {
- return schemas::PermissionLevel::USER;
- }
- virtual int get_delay_seconds() const { return 5; }
- virtual std::vector<std::string> get_subcommand_ids() const {
- return {};
- }
- };
-
- class CommandLoader {
- public:
- CommandLoader();
- ~CommandLoader() = default;
-
- void add_command(std::unique_ptr<Command> cmd);
- std::optional<std::variant<std::vector<std::string>, std::string>> run(
- const InstanceBundle &bundle, const Request &msg) const;
-
- const std::vector<std::unique_ptr<Command>> &get_commands() const {
- return this->commands;
- };
-
- private:
- std::vector<std::unique_ptr<Command>> commands;
- };
- }
-}
diff --git a/src/commands/request.hpp b/src/commands/request.hpp
deleted file mode 100644
index e2685f1..0000000
--- a/src/commands/request.hpp
+++ /dev/null
@@ -1,25 +0,0 @@
-#pragma once
-
-#include <optional>
-#include <pqxx/pqxx>
-#include <string>
-
-#include "../irc/message.hpp"
-#include "../schemas/channel.hpp"
-#include "../schemas/user.hpp"
-
-namespace bot::command {
- struct Request {
- std::string command_id;
- std::optional<std::string> subcommand_id;
- std::optional<std::string> message;
- const irc::Message<irc::MessageType::Privmsg> &irc_message;
-
- schemas::Channel channel;
- schemas::ChannelPreferences channel_preferences;
- schemas::User user;
- schemas::UserRights user_rights;
-
- pqxx::connection &conn;
- };
-}
diff --git a/src/commands/request_util.cpp b/src/commands/request_util.cpp
deleted file mode 100644
index 90750e5..0000000
--- a/src/commands/request_util.cpp
+++ /dev/null
@@ -1,205 +0,0 @@
-#include "request_util.hpp"
-
-#include <algorithm>
-#include <optional>
-#include <pqxx/pqxx>
-#include <string>
-
-#include "../constants.hpp"
-#include "../irc/message.hpp"
-#include "../schemas/channel.hpp"
-#include "command.hpp"
-#include "request.hpp"
-
-namespace bot::command {
- std::optional<Request> generate_request(
- const command::CommandLoader &command_loader,
- const irc::Message<irc::MessageType::Privmsg> &irc_message,
- pqxx::connection &conn) {
- pqxx::work *work;
-
- work = new pqxx::work(conn);
-
- std::vector<std::string> parts =
- utils::string::split_text(irc_message.message, ' ');
-
- std::string command_id = parts[0];
-
- if (command_id.substr(0, DEFAULT_PREFIX.length()) != DEFAULT_PREFIX) {
- delete work;
- return std::nullopt;
- }
-
- command_id =
- command_id.substr(DEFAULT_PREFIX.length(), command_id.length());
-
- auto cmd = std::find_if(
- command_loader.get_commands().begin(),
- command_loader.get_commands().end(),
- [&](const auto &command) { return command->get_name() == command_id; });
-
- if (cmd == command_loader.get_commands().end()) {
- delete work;
- return std::nullopt;
- }
-
- parts.erase(parts.begin());
-
- pqxx::result query = work->exec("SELECT * FROM channels WHERE alias_id = " +
- std::to_string(irc_message.source.id));
-
- // Create new channel data in the database if it didn't exist b4
- if (query.empty()) {
- work->exec("INSERT INTO channels (alias_id, alias_name) VALUES (" +
- std::to_string(irc_message.source.id) + ", '" +
- irc_message.source.login + "')");
-
- work->commit();
-
- delete work;
- work = new pqxx::work(conn);
-
- query = work->exec("SELECT * FROM channels WHERE alias_id = " +
- std::to_string(irc_message.source.id));
- }
-
- schemas::Channel channel(query[0]);
-
- if (channel.get_opted_out_at().has_value()) {
- delete work;
- return std::nullopt;
- }
-
- query = work->exec("SELECT * FROM channel_preferences WHERE channel_id = " +
- std::to_string(channel.get_id()));
-
- // Create new channel preference data in the database if it didn't exist b4
- if (query.empty()) {
- work->exec(
- "INSERT INTO channel_preferences (channel_id, prefix, locale) VALUES "
- "(" +
- std::to_string(channel.get_id()) + ", '" + DEFAULT_PREFIX + "', '" +
- DEFAULT_LOCALE_ID + "')");
-
- work->commit();
-
- delete work;
- work = new pqxx::work(conn);
-
- query =
- work->exec("SELECT * FROM channel_preferences WHERE channel_id = " +
- std::to_string(channel.get_id()));
- }
-
- schemas::ChannelPreferences channel_preferences(query[0]);
-
- query = work->exec("SELECT * FROM users WHERE alias_id = " +
- std::to_string(irc_message.sender.id));
-
- // Create new user data in the database if it didn't exist before
- if (query.empty()) {
- work->exec("INSERT INTO users (alias_id, alias_name) VALUES (" +
- std::to_string(irc_message.sender.id) + ", '" +
- irc_message.sender.login + "')");
-
- work->commit();
-
- delete work;
- work = new pqxx::work(conn);
-
- query = work->exec("SELECT * FROM users WHERE alias_id = " +
- std::to_string(irc_message.sender.id));
- }
-
- schemas::User user(query[0]);
-
- if (user.get_alias_name() != irc_message.sender.login) {
- work->exec("UPDATE users SET alias_name = '" + irc_message.sender.login +
- "' WHERE id = " + std::to_string(user.get_id()));
- work->commit();
-
- delete work;
- work = new pqxx::work(conn);
-
- user.set_alias_name(irc_message.sender.login);
- }
-
- schemas::PermissionLevel level = schemas::PermissionLevel::USER;
- const auto &badges = irc_message.sender.badges;
-
- if (user.get_alias_id() == channel.get_alias_id()) {
- level = schemas::PermissionLevel::BROADCASTER;
- } else if (std::any_of(badges.begin(), badges.end(), [&](const auto &x) {
- return x.first == "moderator";
- })) {
- level = schemas::PermissionLevel::MODERATOR;
- } else if (std::any_of(badges.begin(), badges.end(),
- [&](const auto &x) { return x.first == "vip"; })) {
- level = schemas::PermissionLevel::VIP;
- }
-
- query = work->exec("SELECT * FROM user_rights WHERE user_id = " +
- std::to_string(user.get_id()) +
- " AND channel_id = " + std::to_string(channel.get_id()));
-
- if (query.empty()) {
- work->exec(
- "INSERT INTO user_rights (user_id, channel_id, level) VALUES (" +
- std::to_string(user.get_id()) + ", " +
- std::to_string(channel.get_id()) + ", " + std::to_string(level) +
- ")");
-
- work->commit();
-
- delete work;
- work = new pqxx::work(conn);
-
- query = work->exec("SELECT * FROM user_rights WHERE user_id = " +
- std::to_string(user.get_id()) + " AND channel_id = " +
- std::to_string(channel.get_id()));
- }
-
- schemas::UserRights user_rights(query[0]);
-
- if (user_rights.get_level() != level) {
- work->exec("UPDATE user_rights SET level = " + std::to_string(level) +
- " WHERE id = " + std::to_string(query[0][0].as<int>()));
-
- work->commit();
-
- user_rights.set_level(level);
- }
-
- delete work;
-
- if (parts.empty()) {
- Request req{command_id, std::nullopt, std::nullopt,
- irc_message, channel, channel_preferences,
- user, user_rights, conn};
-
- return req;
- }
-
- std::optional<std::string> subcommand_id = parts[0];
- auto subcommand_ids = (*cmd)->get_subcommand_ids();
-
- if (std::any_of(
- subcommand_ids.begin(), subcommand_ids.end(),
- [&](const auto &x) { return x == subcommand_id.value(); })) {
- parts.erase(parts.begin());
- } else {
- subcommand_id = std::nullopt;
- }
-
- std::optional<std::string> message = utils::string::join_vector(parts, ' ');
-
- if (message->empty()) {
- message = std::nullopt;
- }
-
- Request req{command_id, subcommand_id, message,
- irc_message, channel, channel_preferences,
- user, user_rights, conn};
- return req;
- }
-}
diff --git a/src/commands/request_util.hpp b/src/commands/request_util.hpp
deleted file mode 100644
index dea6e12..0000000
--- a/src/commands/request_util.hpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#include <optional>
-#include <pqxx/pqxx>
-
-#include "../irc/message.hpp"
-#include "command.hpp"
-#include "request.hpp"
-
-namespace bot::command {
- std::optional<Request> generate_request(
- const command::CommandLoader &command_loader,
- const irc::Message<irc::MessageType::Privmsg> &irc_message,
- pqxx::connection &conn);
-}
diff --git a/src/commands/response_error.hpp b/src/commands/response_error.hpp
deleted file mode 100644
index ae2c3ee..0000000
--- a/src/commands/response_error.hpp
+++ /dev/null
@@ -1,222 +0,0 @@
-#pragma once
-
-#include <exception>
-#include <optional>
-#include <string>
-#include <type_traits>
-#include <vector>
-
-#include "command.hpp"
-#include "request.hpp"
-
-namespace bot {
- enum ResponseError {
- NOT_ENOUGH_ARGUMENTS,
- INCORRECT_ARGUMENT,
-
- INCOMPATIBLE_NAME,
- NAMESAKE_CREATION,
- NOT_FOUND,
-
- SOMETHING_WENT_WRONG,
-
- EXTERNAL_API_ERROR,
- INSUFFICIENT_RIGHTS,
-
- ILLEGAL_COMMAND
- };
-
- template <ResponseError T, class Enable = void>
- class ResponseException;
-
- template <ResponseError T>
- class ResponseException<
- T, typename std::enable_if<
- T == INCORRECT_ARGUMENT || T == INCOMPATIBLE_NAME ||
- T == NAMESAKE_CREATION || T == NOT_FOUND>::type>
- : public std::exception {
- public:
- ResponseException(const command::Request &request,
- const loc::Localization &localizator,
- const std::string &message)
- : request(request),
- localizator(localizator),
- message(message),
- error(T) {
- loc::LineId line_id;
-
- switch (this->error) {
- case INCORRECT_ARGUMENT:
- line_id = loc::LineId::ErrorIncorrectArgument;
- break;
- case INCOMPATIBLE_NAME:
- line_id = loc::LineId::ErrorIncompatibleName;
- break;
- case NAMESAKE_CREATION:
- line_id = loc::LineId::ErrorNamesakeCreation;
- break;
- case NOT_FOUND:
- line_id = loc::LineId::ErrorNotFound;
- break;
- default:
- line_id = loc::LineId::ErrorSomethingWentWrong;
- break;
- };
-
- this->line =
- this->localizator
- .get_formatted_line(this->request, line_id, {this->message})
- .value();
- }
- ~ResponseException() = default;
-
- const char *what() const noexcept override { return this->line.c_str(); }
-
- private:
- const command::Request &request;
- const loc::Localization &localizator;
- std::string message, line;
- ResponseError error;
- };
-
- template <ResponseError T>
- class ResponseException<T,
- typename std::enable_if<T == SOMETHING_WENT_WRONG ||
- T == INSUFFICIENT_RIGHTS ||
- T == ILLEGAL_COMMAND>::type>
- : public std::exception {
- public:
- ResponseException(const command::Request &request,
- const loc::Localization &localizator)
- : request(request), localizator(localizator), error(T) {
- loc::LineId line_id;
-
- switch (this->error) {
- case INSUFFICIENT_RIGHTS:
- line_id = loc::LineId::ErrorInsufficientRights;
- break;
- case ILLEGAL_COMMAND:
- line_id = loc::LineId::ErrorIllegalCommand;
- break;
- default:
- line_id = loc::LineId::ErrorSomethingWentWrong;
- break;
- }
-
- this->line =
- this->localizator.get_formatted_line(this->request, line_id, {})
- .value();
- }
- ~ResponseException() = default;
-
- const char *what() const noexcept override { return this->line.c_str(); }
-
- private:
- const command::Request &request;
- const loc::Localization &localizator;
- std::string line;
- ResponseError error;
- };
-
- template <ResponseError T>
- class ResponseException<
- T, typename std::enable_if<T == EXTERNAL_API_ERROR>::type>
- : public std::exception {
- public:
- ResponseException(
- const command::Request &request, const loc::Localization &localizator,
- const int &code,
- const std::optional<std::string> &message = std::nullopt)
- : request(request),
- localizator(localizator),
- code(code),
- message(message),
- error(T) {
- loc::LineId line_id = loc::LineId::ErrorExternalAPIError;
- std::vector<std::string> args = {std::to_string(this->code)};
-
- if (this->message.has_value()) {
- args.push_back(" " + this->message.value());
- }
-
- this->line =
- this->localizator.get_formatted_line(this->request, line_id, args)
- .value();
- }
- ~ResponseException() = default;
-
- const char *what() const noexcept override { return this->line.c_str(); }
-
- private:
- const command::Request &request;
- const loc::Localization &localizator;
- int code;
- std::optional<std::string> message;
- std::string line;
- ResponseError error;
- };
-
- template <ResponseError T>
- class ResponseException<
- T, typename std::enable_if<T == NOT_ENOUGH_ARGUMENTS>::type>
- : public std::exception {
- public:
- ResponseException(const command::Request &request,
- const loc::Localization &localizator,
- command::CommandArgument argument)
- : request(request),
- localizator(localizator),
- argument(argument),
- error(T) {
- loc::LineId line_id = loc::LineId::ErrorNotEnoughArguments;
- loc::LineId arg_id;
-
- switch (this->argument) {
- case command::SUBCOMMAND:
- arg_id = loc::LineId::ArgumentSubcommand;
- break;
- case command::MESSAGE:
- arg_id = loc::LineId::ArgumentMessage;
- break;
- case command::INTERVAL:
- arg_id = loc::LineId::ArgumentInterval;
- break;
- case command::NAME:
- arg_id = loc::LineId::ArgumentName;
- break;
- case command::TARGET:
- arg_id = loc::LineId::ArgumentTarget;
- break;
- case command::VALUE:
- arg_id = loc::LineId::ArgumentValue;
- break;
- case command::AMOUNT:
- arg_id = loc::LineId::ArgumentAmount;
- break;
- default:
- break;
- }
-
- auto arg =
- this->localizator
- .get_localized_line(
- this->request.channel_preferences.get_locale(), arg_id)
- .value();
-
- this->line =
- this->localizator.get_formatted_line(this->request, line_id, {arg})
- .value();
- }
- ~ResponseException() = default;
-
- const char *what() const noexcept override { return this->line.c_str(); }
-
- private:
- const command::Request &request;
- const loc::Localization &localizator;
- command::CommandArgument argument;
- ResponseError error;
- std::string line;
- };
-
-}
diff --git a/src/config.cpp b/src/config.cpp
deleted file mode 100644
index ec55913..0000000
--- a/src/config.cpp
+++ /dev/null
@@ -1,84 +0,0 @@
-#include "config.hpp"
-
-#include <cctype>
-#include <fstream>
-#include <optional>
-#include <sstream>
-#include <string>
-
-#include "logger.hpp"
-
-namespace bot {
- std::optional<Configuration> parse_configuration_from_file(
- const std::string &file_path) {
- std::ifstream ifs(file_path);
-
- if (!ifs.is_open()) {
- log::error("Configuration", "Failed to open the file at " + file_path);
- return std::nullopt;
- }
-
- Configuration cfg;
- TwitchCredentialsConfiguration ttv_crd_cfg;
- DatabaseConfiguration db_cfg;
- CommandConfiguration cmd_cfg;
- OwnerConfiguration owner_cfg;
- UrlConfiguration url_cfg;
-
- std::string line;
- while (std::getline(ifs, line, '\n')) {
- std::istringstream iss(line);
- std::string key;
- std::string value;
-
- std::getline(iss, key, '=');
- std::getline(iss, value);
-
- for (char &c : key) {
- c = tolower(c);
- }
-
- if (key == "twitch_credentials.client_id") {
- ttv_crd_cfg.client_id = value;
- } else if (key == "twitch_credentials.token") {
- ttv_crd_cfg.token = value;
- } else if (key == "db_name") {
- db_cfg.name = value;
- } else if (key == "db_user") {
- db_cfg.user = value;
- } else if (key == "db_password") {
- db_cfg.password = value;
- } else if (key == "db_host") {
- db_cfg.host = value;
- } else if (key == "db_port") {
- db_cfg.port = value;
- }
-
- else if (key == "commands.join_allowed") {
- cmd_cfg.join_allowed = std::stoi(value);
- } else if (key == "commands.join_allow_from_other_chats") {
- cmd_cfg.join_allow_from_other_chats = std::stoi(value);
- }
-
- else if (key == "owner.name") {
- owner_cfg.name = value;
- } else if (key == "owner.id") {
- owner_cfg.id = std::stoi(value);
- }
-
- else if (key == "url.help") {
- url_cfg.help = value;
- }
- }
-
- cfg.url = url_cfg;
- cfg.owner = owner_cfg;
- cfg.commands = cmd_cfg;
- cfg.twitch_credentials = ttv_crd_cfg;
- cfg.database = db_cfg;
-
- log::info("Configuration",
- "Successfully loaded the file from '" + file_path + "'");
- return cfg;
- }
-}
diff --git a/src/config.hpp b/src/config.hpp
deleted file mode 100644
index 5c437d6..0000000
--- a/src/config.hpp
+++ /dev/null
@@ -1,54 +0,0 @@
-#pragma once
-
-#include <optional>
-#include <string>
-
-#define GET_DATABASE_CONNECTION_URL(c) \
- "dbname = " + c.database.name + " user = " + c.database.user + \
- " password = " + c.database.password + " host = " + c.database.host + \
- " port = " + c.database.port
-
-#define GET_DATABASE_CONNECTION_URL_POINTER(c) \
- "dbname = " + c->database.name + " user = " + c->database.user + \
- " password = " + c->database.password + " host = " + c->database.host + \
- " port = " + c->database.port
-
-namespace bot {
- struct DatabaseConfiguration {
- std::string name;
- std::string user;
- std::string password;
- std::string host;
- std::string port;
- };
-
- struct TwitchCredentialsConfiguration {
- std::string client_id;
- std::string token;
- };
-
- struct CommandConfiguration {
- bool join_allowed = true;
- bool join_allow_from_other_chats = false;
- };
-
- struct OwnerConfiguration {
- std::optional<std::string> name = std::nullopt;
- std::optional<int> id = std::nullopt;
- };
-
- struct UrlConfiguration {
- std::optional<std::string> help = std::nullopt;
- };
-
- struct Configuration {
- TwitchCredentialsConfiguration twitch_credentials;
- DatabaseConfiguration database;
- CommandConfiguration commands;
- OwnerConfiguration owner;
- UrlConfiguration url;
- };
-
- std::optional<Configuration> parse_configuration_from_file(
- const std::string &file_path);
-}
diff --git a/src/constants.hpp b/src/constants.hpp
deleted file mode 100644
index 3c3462b..0000000
--- a/src/constants.hpp
+++ /dev/null
@@ -1,13 +0,0 @@
-#pragma once
-
-#include <chrono>
-#include <string>
-
-#define DEFAULT_LOCALE_ID "english"
-
-#ifdef DEBUG_MODE
-const std::string DEFAULT_PREFIX = "~";
-#else
-const std::string DEFAULT_PREFIX = "!";
-#endif
-const auto START_TIME = std::chrono::steady_clock::now();
diff --git a/src/handlers.cpp b/src/handlers.cpp
deleted file mode 100644
index c7820b4..0000000
--- a/src/handlers.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-#include "handlers.hpp"
-
-#include <exception>
-#include <optional>
-#include <pqxx/pqxx>
-#include <string>
-#include <vector>
-
-#include "bundle.hpp"
-#include "commands/command.hpp"
-#include "commands/request.hpp"
-#include "commands/request_util.hpp"
-#include "irc/message.hpp"
-#include "localization/line_id.hpp"
-#include "logger.hpp"
-#include "utils/string.hpp"
-
-namespace bot::handlers {
- void handle_private_message(
- const InstanceBundle &bundle,
- const command::CommandLoader &command_loader,
- const irc::Message<irc::MessageType::Privmsg> &message,
- pqxx::connection &conn) {
- if (utils::string::string_contains_sql_injection(message.message)) {
- log::warn("PrivateMessageHandler",
- "Received the message in #" + message.source.login +
- " with SQL injection: " + message.message);
- return;
- }
-
- std::optional<command::Request> request =
- command::generate_request(command_loader, message, conn);
-
- if (request.has_value()) {
- try {
- auto response = command_loader.run(bundle, request.value());
-
- if (response.has_value()) {
- try {
- auto str = std::get<std::string>(*response);
- bundle.irc_client.say(message.source.login, str);
- } catch (const std::exception &e) {
- }
-
- try {
- auto strs = std::get<std::vector<std::string>>(*response);
- for (const std::string &str : strs) {
- bundle.irc_client.say(message.source.login, str);
- }
- } catch (const std::exception &e) {
- }
- }
- } catch (const std::exception &e) {
- std::string line =
- bundle.localization
- .get_formatted_line(request.value(), loc::LineId::ErrorTemplate,
- {e.what()})
- .value();
-
- bundle.irc_client.say(message.source.login, line);
- }
- }
-
- pqxx::work work(conn);
- pqxx::result channels =
- work.exec("SELECT id FROM channels WHERE alias_id = " +
- std::to_string(message.source.id));
-
- if (!channels.empty()) {
- int channel_id = channels[0][0].as<int>();
- pqxx::result cmds =
- work.exec("SELECT message FROM custom_commands WHERE name = '" +
- message.message + "' AND channel_id = '" +
- std::to_string(channel_id) + "'");
-
- if (!cmds.empty()) {
- std::string msg = cmds[0][0].as<std::string>();
-
- bundle.irc_client.say(message.source.login, msg);
- }
- }
-
- work.commit();
- }
-}
diff --git a/src/handlers.hpp b/src/handlers.hpp
deleted file mode 100644
index a143f76..0000000
--- a/src/handlers.hpp
+++ /dev/null
@@ -1,14 +0,0 @@
-#pragma once
-
-#include "bundle.hpp"
-#include "commands/command.hpp"
-#include "irc/message.hpp"
-#include "pqxx/pqxx"
-
-namespace bot::handlers {
- void handle_private_message(
- const InstanceBundle &bundle,
- const command::CommandLoader &command_loader,
- const irc::Message<irc::MessageType::Privmsg> &message,
- pqxx::connection &conn);
-}
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)>;
- };
-
- }
-}
diff --git a/src/localization/line_id.cpp b/src/localization/line_id.cpp
deleted file mode 100644
index 567a3ba..0000000
--- a/src/localization/line_id.cpp
+++ /dev/null
@@ -1,100 +0,0 @@
-#include "line_id.hpp"
-
-#include <optional>
-#include <string>
-
-namespace bot {
- namespace loc {
- std::optional<LineId> string_to_line_id(const std::string &str) {
- if (str == "ping.response") {
- return LineId::PingResponse;
- }
-
- else if (str == "msg.owner") {
- return LineId::MsgOwner;
- }
-
- else if (str == "argument.subcommand") {
- return LineId::ArgumentSubcommand;
- } else if (str == "argument.message") {
- return LineId::ArgumentMessage;
- } else if (str == "argument.interval") {
- return LineId::ArgumentInterval;
- } else if (str == "argument.name") {
- return LineId::ArgumentName;
- } else if (str == "argument.target") {
- return LineId::ArgumentTarget;
- } else if (str == "argument.value") {
- return LineId::ArgumentValue;
- } else if (str == "argument.amount") {
- return LineId::ArgumentAmount;
- }
-
- else if (str == "error.template") {
- return LineId::ErrorTemplate;
- } else if (str == "error.not_enough_arguments") {
- return LineId::ErrorNotEnoughArguments;
- } else if (str == "error.incorrect_argument") {
- return LineId::ErrorIncorrectArgument;
- } else if (str == "error.incompatible_name") {
- return LineId::ErrorIncompatibleName;
- } else if (str == "error.namesake_creation") {
- return LineId::ErrorNamesakeCreation;
- } else if (str == "error.not_found") {
- return LineId::ErrorNotFound;
- } else if (str == "error.something_went_wrong") {
- return LineId::ErrorSomethingWentWrong;
- } else if (str == "error.insufficient_rights") {
- return LineId::ErrorInsufficientRights;
- } else if (str == "error.illegal_command") {
- return LineId::ErrorIllegalCommand;
- }
-
- else if (str == "event.on") {
- return LineId::EventOn;
- } else if (str == "event.off") {
- return LineId::EventOff;
- }
-
- else if (str == "notify.sub") {
- return LineId::NotifySub;
- } else if (str == "notify.unsub") {
- return LineId::NotifyUnsub;
- }
-
- else if (str == "join.response") {
- return LineId::JoinResponse;
- } else if (str == "join.response_in_chat") {
- return LineId::JoinResponseInChat;
- } else if (str == "join.already_in") {
- return LineId::JoinAlreadyIn;
- } else if (str == "join.rejoined") {
- return LineId::JoinRejoined;
- } else if (str == "join.from_other_chat") {
- return LineId::JoinFromOtherChat;
- } else if (str == "join.not_allowed") {
- return LineId::JoinNotAllowed;
- }
-
- else if (str == "custom_command.new") {
- return LineId::CustomcommandNew;
- } else if (str == "custom_command.delete") {
- return LineId::CustomcommandDelete;
- }
-
- else if (str == "timer.new") {
- return LineId::TimerNew;
- } else if (str == "timer.delete") {
- return LineId::TimerDelete;
- }
-
- else if (str == "help.response") {
- return LineId::HelpResponse;
- }
-
- else {
- return std::nullopt;
- }
- }
- }
-}
diff --git a/src/localization/line_id.hpp b/src/localization/line_id.hpp
deleted file mode 100644
index 41ceec6..0000000
--- a/src/localization/line_id.hpp
+++ /dev/null
@@ -1,56 +0,0 @@
-#pragma once
-
-#include <optional>
-#include <string>
-
-namespace bot {
- namespace loc {
- enum LineId {
- MsgOwner,
-
- ArgumentSubcommand,
- ArgumentMessage,
- ArgumentInterval,
- ArgumentName,
- ArgumentTarget,
- ArgumentValue,
- ArgumentAmount,
-
- ErrorTemplate,
- ErrorNotEnoughArguments,
- ErrorIncorrectArgument,
- ErrorIncompatibleName,
- ErrorNamesakeCreation,
- ErrorNotFound,
- ErrorSomethingWentWrong,
- ErrorExternalAPIError,
- ErrorInsufficientRights,
- ErrorIllegalCommand,
-
- PingResponse,
-
- EventOn,
- EventOff,
-
- NotifySub,
- NotifyUnsub,
-
- JoinResponse,
- JoinResponseInChat,
- JoinAlreadyIn,
- JoinRejoined,
- JoinFromOtherChat,
- JoinNotAllowed,
-
- CustomcommandNew,
- CustomcommandDelete,
-
- TimerNew,
- TimerDelete,
-
- HelpResponse
- };
-
- std::optional<LineId> string_to_line_id(const std::string &str);
- }
-}
diff --git a/src/localization/localization.cpp b/src/localization/localization.cpp
deleted file mode 100644
index 2742602..0000000
--- a/src/localization/localization.cpp
+++ /dev/null
@@ -1,132 +0,0 @@
-#include "localization.hpp"
-
-#include <algorithm>
-#include <filesystem>
-#include <fstream>
-#include <map>
-#include <nlohmann/json.hpp>
-#include <optional>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "../utils/string.hpp"
-#include "line_id.hpp"
-
-namespace bot {
- namespace loc {
- Localization::Localization(const std::string &folder_path) {
- for (const auto &entry :
- std::filesystem::directory_iterator(folder_path)) {
- std::vector<std::string> file_name_parts =
- utils::string::split_text(entry.path(), '/');
- std::string file_name = file_name_parts[file_name_parts.size() - 1];
- file_name = file_name.substr(0, file_name.length() - 5);
-
- std::unordered_map<LineId, std::string> lines =
- this->load_from_file(entry.path());
-
- this->localizations[file_name] = lines;
- }
- }
-
- std::unordered_map<LineId, std::string> Localization::load_from_file(
- const std::string &file_path) {
- std::ifstream ifs(file_path);
-
- std::unordered_map<LineId, std::string> map;
-
- nlohmann::json json;
- ifs >> json;
-
- for (auto it = json.begin(); it != json.end(); ++it) {
- std::optional<LineId> line_id = string_to_line_id(it.key());
-
- if (line_id.has_value()) {
- map[line_id.value()] = it.value();
- }
- }
-
- ifs.close();
- return map;
- }
-
- std::optional<std::string> Localization::get_localized_line(
- const std::string &locale_id, const LineId &line_id) const {
- auto locale_it =
- std::find_if(this->localizations.begin(), this->localizations.end(),
- [&](const auto &x) { return x.first == locale_id; });
-
- if (locale_it == this->localizations.end()) {
- return std::nullopt;
- }
-
- auto line_it =
- std::find_if(locale_it->second.begin(), locale_it->second.end(),
- [&](const auto &x) { return x.first == line_id; });
-
- if (line_it == locale_it->second.end()) {
- return std::nullopt;
- }
-
- return line_it->second;
- }
-
- std::optional<std::string> Localization::get_formatted_line(
- const std::string &locale_id, const LineId &line_id,
- const std::vector<std::string> &args) const {
- std::optional<std::string> o_line =
- this->get_localized_line(locale_id, line_id);
-
- if (!o_line.has_value()) {
- return std::nullopt;
- }
-
- std::string line = o_line.value();
-
- int pos = 0;
- int index = 0;
-
- while ((pos = line.find("%s", pos)) != std::string::npos) {
- line.replace(pos, 2, args[index]);
- pos += args[index].size();
- ++index;
-
- if (index >= args.size()) {
- break;
- }
- }
-
- return line;
- }
-
- std::optional<std::string> Localization::get_formatted_line(
- const command::Request &request, const LineId &line_id,
- const std::vector<std::string> &args) const {
- std::optional<std::string> o_line = this->get_formatted_line(
- request.channel_preferences.get_locale(), line_id, args);
-
- if (!o_line.has_value()) {
- return std::nullopt;
- }
-
- std::string line = o_line.value();
-
- std::map<std::string, std::string> token_map = {
- {"{sender.alias_name}", request.user.get_alias_name()},
- {"{source.alias_name}", request.channel.get_alias_name()},
- {"{default.prefix}", DEFAULT_PREFIX}};
-
- for (const auto &pair : token_map) {
- int pos = line.find(pair.first);
-
- while (pos != std::string::npos) {
- line.replace(pos, pair.first.length(), pair.second);
- pos = line.find(pair.first, pos + pair.second.length());
- }
- }
-
- return line;
- }
- }
-}
diff --git a/src/localization/localization.hpp b/src/localization/localization.hpp
deleted file mode 100644
index 4626c68..0000000
--- a/src/localization/localization.hpp
+++ /dev/null
@@ -1,37 +0,0 @@
-#pragma once
-
-#include <optional>
-#include <string>
-#include <unordered_map>
-#include <vector>
-
-#include "../commands/request.hpp"
-#include "line_id.hpp"
-
-namespace bot {
- namespace loc {
- class Localization {
- public:
- Localization(const std::string &folder_path);
- ~Localization() = default;
-
- std::optional<std::string> get_localized_line(
- const std::string &locale_id, const LineId &line_id) const;
-
- std::optional<std::string> get_formatted_line(
- const std::string &locale_id, const LineId &line_id,
- const std::vector<std::string> &args) const;
-
- std::optional<std::string> get_formatted_line(
- const command::Request &request, const LineId &line_id,
- const std::vector<std::string> &args) const;
-
- private:
- std::unordered_map<LineId, std::string> load_from_file(
- const std::string &file_path);
- std::unordered_map<std::string, std::unordered_map<LineId, std::string>>
- localizations;
- };
- }
-
-}
diff --git a/src/logger.cpp b/src/logger.cpp
deleted file mode 100644
index 3d142a2..0000000
--- a/src/logger.cpp
+++ /dev/null
@@ -1,96 +0,0 @@
-#include "logger.hpp"
-
-#include <ctime>
-#include <filesystem>
-#include <fstream>
-#include <iomanip>
-#include <iostream>
-#include <sstream>
-#include <stdexcept>
-
-namespace bot::log {
- void log(const LogLevel &level, const std::string &source,
- const std::string &message) {
- std::string dir_name = "logs";
- if (!std::filesystem::exists(dir_name)) {
- std::filesystem::create_directory(dir_name);
- }
-
- if (std::filesystem::exists(dir_name) &&
- !std::filesystem::is_directory(dir_name)) {
- throw std::runtime_error("The path '" + dir_name +
- "' is not a directory!");
- return;
- }
-
- std::ostringstream line;
-
- // getting time
- std::time_t current_time = std::time(nullptr);
- std::tm *local_time = std::localtime(&current_time);
-
- line << "[" << std::put_time(local_time, "%H:%M:%S") << "] ";
-
- std::string level_str;
-
- switch (level) {
- case DEBUG:
- level_str = "DEBUG";
- break;
- case WARN:
- level_str = "WARN";
- break;
- case ERROR:
- level_str = "ERROR";
- break;
- default:
- level_str = "INFO";
- break;
- }
-
- line << level_str << " - ";
-
- line << source << ": " << message << "\n";
-
-#ifdef DEBUG_MODE
- std::cout << line.str();
-#else
- if (level != LogLevel::DEBUG) {
- std::cout << line.str();
- }
-#endif
-
- // saving into the log file
- std::ostringstream file_name_oss;
- file_name_oss << dir_name << "/";
- file_name_oss << "log_";
- file_name_oss << std::put_time(local_time, "%Y-%m-%d");
- file_name_oss << ".log";
-
- std::ofstream ofs;
- ofs.open(file_name_oss.str(), std::ios::app);
-
- if (ofs.is_open()) {
- ofs << line.str();
- ofs.close();
- } else {
- std::cerr << "Failed to write to the log file!\n";
- }
- }
-
- void info(const std::string &source, const std::string &message) {
- log(LogLevel::INFO, source, message);
- }
-
- void debug(const std::string &source, const std::string &message) {
- log(LogLevel::DEBUG, source, message);
- }
-
- void warn(const std::string &source, const std::string &message) {
- log(LogLevel::WARN, source, message);
- }
-
- void error(const std::string &source, const std::string &message) {
- log(LogLevel::ERROR, source, message);
- }
-}
diff --git a/src/logger.hpp b/src/logger.hpp
deleted file mode 100644
index 91b4757..0000000
--- a/src/logger.hpp
+++ /dev/null
@@ -1,16 +0,0 @@
-#pragma once
-
-#include <string>
-
-namespace bot::log {
- enum LogLevel { INFO, DEBUG, WARN, ERROR };
-
- void log(const LogLevel &level, const std::string &source,
- const std::string &message);
-
- // just shorthands
- void info(const std::string &source, const std::string &message);
- void debug(const std::string &source, const std::string &message);
- void warn(const std::string &source, const std::string &message);
- void error(const std::string &source, const std::string &message);
-}
diff --git a/src/main.cpp b/src/main.cpp
deleted file mode 100644
index 3c8f5e7..0000000
--- a/src/main.cpp
+++ /dev/null
@@ -1,127 +0,0 @@
-#include <optional>
-#include <pqxx/pqxx>
-#include <string>
-#include <vector>
-
-#include "api/twitch/helix_client.hpp"
-#include "bundle.hpp"
-#include "commands/command.hpp"
-#include "config.hpp"
-#include "handlers.hpp"
-#include "irc/client.hpp"
-#include "irc/message.hpp"
-#include "localization/localization.hpp"
-#include "logger.hpp"
-#include "stream.hpp"
-#include "timer.hpp"
-
-int main(int argc, char *argv[]) {
- bot::log::info("Main", "Starting up...");
-
- std::optional<bot::Configuration> o_cfg =
- bot::parse_configuration_from_file(".env");
-
- if (!o_cfg.has_value()) {
- return 1;
- }
-
- bot::Configuration cfg = o_cfg.value();
-
- if (cfg.twitch_credentials.client_id.empty() ||
- cfg.twitch_credentials.token.empty()) {
- bot::log::error("Main",
- "TWITCH_CREDENTIALS.CLIENT_ID and TWITCH_CREDENTIALS.TOKEN "
- "must be set in environmental file!");
- return 1;
- }
-
- if (cfg.database.name.empty() || cfg.database.user.empty() ||
- cfg.database.password.empty() || cfg.database.host.empty() ||
- cfg.database.port.empty()) {
- bot::log::error("Main",
- "DB_NAME, DB_USER, DB_PASSWORD, DB_HOST, DB_PORT "
- "must be set in environmental file!");
- return 1;
- }
-
- bot::irc::Client client(cfg.twitch_credentials.client_id,
- cfg.twitch_credentials.token);
- bot::command::CommandLoader command_loader;
- bot::loc::Localization localization("localization");
- bot::api::twitch::HelixClient helix_client(cfg.twitch_credentials.token,
- cfg.twitch_credentials.client_id);
-
- client.join(client.get_bot_username());
-
- pqxx::connection conn(GET_DATABASE_CONNECTION_URL(cfg));
- pqxx::work *work = new pqxx::work(conn);
-
- pqxx::result rows = work->exec(
- "SELECT alias_id FROM channels WHERE opted_out_at is null AND alias_id "
- "!= " +
- std::to_string(client.get_bot_id()));
-
- std::vector<int> ids;
-
- for (const auto &row : rows) {
- ids.push_back(row[0].as<int>());
- }
-
- auto helix_channels = helix_client.get_users(ids);
-
- // it could be optimized
- for (const auto &helix_channel : helix_channels) {
- auto channel =
- work->exec("SELECT id, alias_name FROM channels WHERE alias_id = " +
- std::to_string(helix_channel.id));
-
- if (!channel.empty()) {
- std::string name = channel[0][1].as<std::string>();
-
- if (name != helix_channel.login) {
- work->exec("UPDATE channels SET alias_name = '" + helix_channel.login +
- "' WHERE id = " + std::to_string(channel[0][0].as<int>()));
- work->commit();
-
- delete work;
- work = new pqxx::work(conn);
- }
-
- client.join(helix_channel.login);
- }
- }
-
- work->commit();
- delete work;
-
- conn.close();
-
- bot::stream::StreamListenerClient stream_listener_client(helix_client, client,
- cfg);
-
- client.on<bot::irc::MessageType::Privmsg>(
- [&client, &command_loader, &localization, &cfg, &helix_client](
- const bot::irc::Message<bot::irc::MessageType::Privmsg> &message) {
- bot::InstanceBundle bundle{client, helix_client, localization, cfg};
-
- pqxx::connection conn(GET_DATABASE_CONNECTION_URL(cfg));
-
- bot::handlers::handle_private_message(bundle, command_loader, message,
- conn);
-
- conn.close();
- });
-
- client.run();
-
- std::vector<std::thread> threads;
- threads.push_back(std::thread(bot::create_timer_thread, &client, &cfg));
- threads.push_back(std::thread(&bot::stream::StreamListenerClient::run,
- &stream_listener_client));
-
- for (auto &thread : threads) {
- thread.join();
- }
-
- return 0;
-}
diff --git a/src/modules/custom_command.hpp b/src/modules/custom_command.hpp
deleted file mode 100644
index 50b3692..0000000
--- a/src/modules/custom_command.hpp
+++ /dev/null
@@ -1,96 +0,0 @@
-#pragma once
-
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "../commands/command.hpp"
-#include "../commands/response_error.hpp"
-
-namespace bot {
- namespace mod {
- class CustomCommand : public command::Command {
- std::string get_name() const override { return "scmd"; }
-
- schemas::PermissionLevel get_permission_level() const override {
- return schemas::PermissionLevel::MODERATOR;
- }
-
- std::vector<std::string> get_subcommand_ids() const override {
- return {"new", "remove"};
- }
-
- std::variant<std::vector<std::string>, std::string> run(
- const InstanceBundle &bundle,
- const command::Request &request) const override {
- if (!request.subcommand_id.has_value()) {
- throw ResponseException<NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::SUBCOMMAND);
- }
-
- const std::string &subcommand_id = request.subcommand_id.value();
-
- if (!request.message.has_value()) {
- throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::CommandArgument::NAME);
- }
-
- const std::string &message = request.message.value();
- std::vector<std::string> s = utils::string::split_text(message, ' ');
-
- std::string name = s[0];
- s.erase(s.begin());
-
- pqxx::work work(request.conn);
- pqxx::result cmds = work.exec(
- "SELECT id FROM custom_commands WHERE name = '" + name +
- "' AND channel_id = " + std::to_string(request.channel.get_id()));
-
- if (subcommand_id == "new") {
- if (!cmds.empty()) {
- throw ResponseException<ResponseError::NAMESAKE_CREATION>(
- request, bundle.localization, name);
- }
-
- if (s.empty()) {
- throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization,
- command::CommandArgument::MESSAGE);
- }
-
- std::string m = utils::string::str(s.begin(), s.end(), ' ');
-
- work.exec(
- "INSERT INTO custom_commands(channel_id, name, message) VALUES "
- "(" +
- std::to_string(request.channel.get_id()) + ", '" + name +
- "', '" + m + "')");
- work.commit();
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::CustomcommandNew,
- {name})
- .value();
- } else if (subcommand_id == "remove") {
- if (cmds.empty()) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, name);
- }
-
- work.exec("DELETE FROM custom_commands WHERE id = " +
- std::to_string(cmds[0][0].as<int>()));
- work.commit();
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::CustomcommandDelete,
- {name})
- .value();
- }
-
- throw ResponseException<ResponseError::SOMETHING_WENT_WRONG>(
- request, bundle.localization);
- }
- };
- }
-}
diff --git a/src/modules/event.hpp b/src/modules/event.hpp
deleted file mode 100644
index 4242f07..0000000
--- a/src/modules/event.hpp
+++ /dev/null
@@ -1,145 +0,0 @@
-#pragma once
-
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "../commands/command.hpp"
-#include "../commands/response_error.hpp"
-#include "../schemas/stream.hpp"
-
-namespace bot {
- namespace mod {
- class Event : public command::Command {
- std::string get_name() const override { return "event"; }
-
- schemas::PermissionLevel get_permission_level() const override {
- return schemas::PermissionLevel::MODERATOR;
- }
-
- std::vector<std::string> get_subcommand_ids() const override {
- return {"on", "off"};
- }
-
- std::variant<std::vector<std::string>, std::string> run(
- const InstanceBundle &bundle,
- const command::Request &request) const override {
- if (!request.subcommand_id.has_value()) {
- throw ResponseException<NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::SUBCOMMAND);
- }
-
- const std::string &subcommand_id = request.subcommand_id.value();
-
- if (!request.message.has_value()) {
- throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::CommandArgument::TARGET);
- }
-
- const std::string &message = request.message.value();
- std::vector<std::string> s = utils::string::split_text(message, ' ');
-
- std::string target;
- schemas::EventType type;
-
- std::vector<std::string> target_and_type =
- utils::string::split_text(s[0], ':');
-
- if (target_and_type.size() != 2) {
- throw ResponseException<ResponseError::INCORRECT_ARGUMENT>(
- request, bundle.localization, s[0]);
- }
-
- s.erase(s.begin());
-
- target = target_and_type[0];
- type = schemas::string_to_event_type(target_and_type[1]);
-
- std::string t = target_and_type[0] + ":" + target_and_type[1];
-
- auto channels = bundle.helix_client.get_users({target});
- api::twitch::schemas::User channel;
-
- if (channels.empty() && type != schemas::EventType::CUSTOM) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, t);
- }
-
- pqxx::work work(request.conn);
- std::string query;
-
- if (type != schemas::CUSTOM) {
- channel = channels[0];
-
- query = "SELECT id FROM events WHERE channel_id = " +
- std::to_string(request.channel.get_id()) +
- " AND target_alias_id = " + std::to_string(channel.id) +
- " AND event_type = " + std::to_string(type);
- } else {
- query = "SELECT id FROM events WHERE channel_id = " +
- std::to_string(request.channel.get_id()) +
- " AND custom_alias_id = '" + target +
- "' AND event_type = " + std::to_string(type);
- }
-
- pqxx::result event = work.exec(query);
-
- if (subcommand_id == "on") {
- if (!event.empty()) {
- throw ResponseException<ResponseError::NAMESAKE_CREATION>(
- request, bundle.localization, t);
- }
-
- if (s.empty()) {
- throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization,
- command::CommandArgument::MESSAGE);
- }
-
- std::string m = utils::string::str(s.begin(), s.end(), ' ');
-
- if (type != schemas::CUSTOM) {
- query =
- "INSERT INTO events (channel_id, target_alias_id, "
- "event_type, "
- "message) VALUES (" +
- std::to_string(request.channel.get_id()) + ", " +
- std::to_string(channel.id) + ", " + std::to_string(type) +
- ", '" + m + "')";
- } else {
- query =
- "INSERT INTO events (channel_id, custom_alias_id, "
- "event_type, "
- "message) VALUES (" +
- std::to_string(request.channel.get_id()) + ", '" + target +
- "', " + std::to_string(type) + ", '" + m + "')";
- }
-
- work.exec(query);
- work.commit();
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::EventOn, {t})
- .value();
- } else if (subcommand_id == "off") {
- if (event.empty()) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, t);
- }
-
- work.exec("DELETE FROM events WHERE id = " +
- std::to_string(event[0][0].as<int>()));
- work.commit();
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::EventOff, {t})
- .value();
- }
-
- throw ResponseException<ResponseError::SOMETHING_WENT_WRONG>(
- request, bundle.localization);
- }
- };
- }
-}
diff --git a/src/modules/help.hpp b/src/modules/help.hpp
deleted file mode 100644
index 13af228..0000000
--- a/src/modules/help.hpp
+++ /dev/null
@@ -1,31 +0,0 @@
-#pragma once
-
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "../commands/command.hpp"
-#include "../commands/response_error.hpp"
-
-namespace bot {
- namespace mod {
- class Help : public command::Command {
- std::string get_name() const override { return "help"; }
-
- std::variant<std::vector<std::string>, std::string> run(
- const InstanceBundle &bundle,
- const command::Request &request) const override {
- if (!bundle.configuration.url.help.has_value()) {
- throw ResponseException<ResponseError::ILLEGAL_COMMAND>(
- request, bundle.localization);
- }
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::HelpResponse,
- {*bundle.configuration.url.help})
- .value();
- }
- };
- }
-}
diff --git a/src/modules/join.hpp b/src/modules/join.hpp
deleted file mode 100644
index 16e8b4a..0000000
--- a/src/modules/join.hpp
+++ /dev/null
@@ -1,91 +0,0 @@
-#pragma once
-
-#include <pqxx/pqxx>
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "../commands/command.hpp"
-#include "../schemas/channel.hpp"
-
-namespace bot {
- namespace mod {
- class Join : public command::Command {
- std::string get_name() const override { return "join"; }
-
- std::variant<std::vector<std::string>, std::string> run(
- const InstanceBundle &bundle,
- const command::Request &request) const override {
- if (!bundle.configuration.commands.join_allowed) {
- std::string owner = "";
-
- if (bundle.configuration.owner.name.has_value()) {
- owner = " " + bundle.localization
- .get_formatted_line(
- request, loc::LineId::MsgOwner,
- {*bundle.configuration.owner.name})
- .value();
- }
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::JoinNotAllowed,
- {owner})
- .value();
- }
-
- if (!bundle.configuration.commands.join_allow_from_other_chats &&
- request.channel.get_alias_name() !=
- bundle.irc_client.get_bot_username()) {
- return bundle.localization
- .get_formatted_line(request, loc::LineId::JoinFromOtherChat,
- {bundle.irc_client.get_bot_username()})
- .value();
- }
-
- pqxx::work work(request.conn);
-
- pqxx::result channels =
- work.exec("SELECT * FROM channels WHERE alias_id = " +
- std::to_string(request.user.get_alias_id()));
-
- if (!channels.empty()) {
- schemas::Channel channel(channels[0]);
-
- if (channel.get_opted_out_at().has_value()) {
- work.exec("UPDATE channels SET opted_out_at = null WHERE id = " +
- std::to_string(channel.get_id()));
- work.commit();
-
- bundle.irc_client.join(channel.get_alias_name());
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::JoinRejoined, {})
- .value();
- }
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::JoinAlreadyIn, {})
- .value();
- }
-
- work.exec("INSERT INTO channels(alias_id, alias_name) VALUES (" +
- std::to_string(request.user.get_alias_id()) + ", '" +
- request.user.get_alias_name() + "')");
- work.commit();
-
- bundle.irc_client.join(request.user.get_alias_name());
- bundle.irc_client.say(
- request.user.get_alias_name(),
- bundle.localization
- .get_formatted_line(request, loc::LineId::JoinResponseInChat,
- {})
- .value());
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::JoinResponse, {})
- .value();
- }
- };
- }
-}
diff --git a/src/modules/massping.hpp b/src/modules/massping.hpp
deleted file mode 100644
index 2957e34..0000000
--- a/src/modules/massping.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-#pragma once
-
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "../commands/command.hpp"
-
-namespace bot {
- namespace mod {
- class Massping : public command::Command {
- std::string get_name() const override { return "massping"; }
-
- schemas::PermissionLevel get_permission_level() const override {
- return schemas::PermissionLevel::MODERATOR;
- }
-
- int get_delay_seconds() const override { return 1; }
-
- std::variant<std::vector<std::string>, std::string> run(
- const InstanceBundle &bundle,
- const command::Request &request) const override {
- auto chatters = bundle.helix_client.get_chatters(
- request.channel.get_alias_id(), bundle.irc_client.get_bot_id());
-
- std::string m;
-
- if (request.message.has_value()) {
- m = request.message.value() + " ·";
- }
-
- std::string base = "📣 " + m + " ";
- std::vector<std::string> msgs = {""};
- int index = 0;
-
- for (const auto &chatter : chatters) {
- const std::string &current_msg = msgs.at(index);
- std::string x = "@" + chatter.login;
-
- if (base.length() + current_msg.length() + 1 + x.length() >= 500) {
- index += 1;
- }
-
- if (index > msgs.size() - 1) {
- msgs.push_back(x);
- } else {
- msgs[index] = current_msg + " " + x;
- }
- }
-
- std::vector<std::string> msgs2;
-
- for (const auto &m : msgs) {
- msgs2.push_back(base + m);
- }
-
- return msgs2;
- }
- };
- }
-}
diff --git a/src/modules/notify.hpp b/src/modules/notify.hpp
deleted file mode 100644
index 3587e73..0000000
--- a/src/modules/notify.hpp
+++ /dev/null
@@ -1,131 +0,0 @@
-#pragma once
-
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "../commands/command.hpp"
-#include "../commands/response_error.hpp"
-#include "../schemas/stream.hpp"
-
-namespace bot {
- namespace mod {
- class Notify : public command::Command {
- std::string get_name() const override { return "notify"; }
-
- std::vector<std::string> get_subcommand_ids() const override {
- return {"sub", "unsub"};
- }
-
- std::variant<std::vector<std::string>, std::string> run(
- const InstanceBundle &bundle,
- const command::Request &request) const override {
- if (!request.subcommand_id.has_value()) {
- throw ResponseException<NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::SUBCOMMAND);
- }
-
- const std::string &subcommand_id = request.subcommand_id.value();
-
- if (!request.message.has_value()) {
- throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::CommandArgument::TARGET);
- }
-
- const std::string &message = request.message.value();
- std::vector<std::string> s = utils::string::split_text(message, ' ');
-
- std::string target;
- schemas::EventType type;
-
- std::vector<std::string> target_and_type =
- utils::string::split_text(s[0], ':');
-
- if (target_and_type.size() != 2) {
- throw ResponseException<ResponseError::INCORRECT_ARGUMENT>(
- request, bundle.localization, s[0]);
- }
-
- s.erase(s.begin());
-
- target = target_and_type[0];
- type = schemas::string_to_event_type(target_and_type[1]);
-
- std::string t = target_and_type[0] + ":" + target_and_type[1];
-
- auto channels = bundle.helix_client.get_users({target});
- api::twitch::schemas::User channel;
-
- if (channels.empty() && type != schemas::EventType::CUSTOM) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, t);
- }
-
- pqxx::work work(request.conn);
- std::string query;
-
- if (type != schemas::CUSTOM) {
- channel = channels[0];
-
- query = "SELECT id FROM events WHERE channel_id = " +
- std::to_string(request.channel.get_id()) +
- " AND target_alias_id = " + std::to_string(channel.id) +
- " AND event_type = " + std::to_string(type);
- } else {
- query = "SELECT id FROM events WHERE channel_id = " +
- std::to_string(request.channel.get_id()) +
- " AND custom_alias_id = '" + target +
- "' AND event_type = " + std::to_string(type);
- }
-
- pqxx::result events = work.exec(query);
-
- if (events.empty()) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, t);
- }
-
- pqxx::row event = events[0];
-
- pqxx::result subs =
- work.exec("SELECT id FROM event_subscriptions WHERE event_id = " +
- std::to_string(event[0].as<int>()) + " AND user_id = " +
- std::to_string(request.user.get_id()));
-
- if (subcommand_id == "sub") {
- if (!subs.empty()) {
- throw ResponseException<ResponseError::NAMESAKE_CREATION>(
- request, bundle.localization, t);
- }
-
- work.exec(
- "INSERT INTO event_subscriptions(event_id, user_id) VALUES (" +
- std::to_string(event[0].as<int>()) + ", " +
- std::to_string(request.user.get_id()));
- work.commit();
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::NotifySub, {t})
- .value();
- } else if (subcommand_id == "unsub") {
- if (subs.empty()) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, t);
- }
-
- work.exec("DELETE FROM event_subscriptions WHERE id = " +
- std::to_string(subs[0][0].as<int>()));
- work.commit();
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::NotifyUnsub, {t})
- .value();
- }
-
- throw ResponseException<ResponseError::SOMETHING_WENT_WRONG>(
- request, bundle.localization);
- }
- };
- }
-}
diff --git a/src/modules/ping.hpp b/src/modules/ping.hpp
deleted file mode 100644
index 836917d..0000000
--- a/src/modules/ping.hpp
+++ /dev/null
@@ -1,59 +0,0 @@
-#pragma once
-
-#include <sys/resource.h>
-#include <sys/types.h>
-#include <unistd.h>
-
-#include <chrono>
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "../commands/command.hpp"
-#include "../utils/chrono.hpp"
-
-namespace bot {
- namespace mod {
- class Ping : public command::Command {
- std::string get_name() const override { return "ping"; }
-
- std::variant<std::vector<std::string>, std::string> run(
- const InstanceBundle &bundle,
- const command::Request &request) const override {
- auto now = std::chrono::steady_clock::now();
- auto duration = now - START_TIME;
- auto seconds =
- std::chrono::duration_cast<std::chrono::seconds>(duration);
- std::string uptime = utils::chrono::format_timestamp(seconds.count());
-
- struct rusage usage;
- getrusage(RUSAGE_SELF, &usage);
-
- int used_memory = usage.ru_maxrss / 1024;
-
- std::string cpp_info;
-
-#ifdef __cplusplus
- cpp_info.append("C++" + std::to_string(__cplusplus).substr(2, 2));
-#endif
-
-#ifdef __VERSION__
- cpp_info.append(" (gcc " +
- bot::utils::string::split_text(__VERSION__, ' ')[0] +
- ")");
-#endif
-
- if (!cpp_info.empty()) {
- cpp_info.append(" · ");
- }
-
- return bundle.localization
- .get_formatted_line(
- request, loc::LineId::PingResponse,
- {cpp_info, uptime, std::to_string(used_memory)})
- .value();
- }
- };
- }
-}
diff --git a/src/modules/timer.hpp b/src/modules/timer.hpp
deleted file mode 100644
index 36c3982..0000000
--- a/src/modules/timer.hpp
+++ /dev/null
@@ -1,112 +0,0 @@
-#pragma once
-
-#include <string>
-#include <variant>
-#include <vector>
-
-#include "../bundle.hpp"
-#include "../commands/command.hpp"
-#include "../commands/response_error.hpp"
-
-namespace bot {
- namespace mod {
- class Timer : public command::Command {
- std::string get_name() const override { return "timer"; }
-
- schemas::PermissionLevel get_permission_level() const override {
- return schemas::PermissionLevel::MODERATOR;
- }
-
- std::vector<std::string> get_subcommand_ids() const override {
- return {"new", "remove"};
- }
-
- std::variant<std::vector<std::string>, std::string> run(
- const InstanceBundle &bundle,
- const command::Request &request) const override {
- if (!request.subcommand_id.has_value()) {
- throw ResponseException<NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::SUBCOMMAND);
- }
-
- const std::string &subcommand_id = request.subcommand_id.value();
-
- if (!request.message.has_value()) {
- throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization, command::CommandArgument::NAME);
- }
-
- const std::string &message = request.message.value();
- std::vector<std::string> s = utils::string::split_text(message, ' ');
-
- std::string name = s[0];
- s.erase(s.begin());
-
- pqxx::work work(request.conn);
- pqxx::result timers = work.exec(
- "SELECT id FROM timers WHERE name = '" + name +
- "' AND channel_id = " + std::to_string(request.channel.get_id()));
-
- if (subcommand_id == "new") {
- if (!timers.empty()) {
- throw ResponseException<ResponseError::NAMESAKE_CREATION>(
- request, bundle.localization, name);
- }
-
- if (s.empty()) {
- throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization,
- command::CommandArgument::INTERVAL);
- }
-
- int interval_s;
-
- try {
- interval_s = std::stoi(s[0]);
- } catch (std::exception e) {
- throw ResponseException<ResponseError::INCORRECT_ARGUMENT>(
- request, bundle.localization, s[0]);
- }
-
- s.erase(s.begin());
-
- if (s.empty()) {
- throw ResponseException<ResponseError::NOT_ENOUGH_ARGUMENTS>(
- request, bundle.localization,
- command::CommandArgument::MESSAGE);
- }
-
- std::string m = utils::string::str(s.begin(), s.end(), ' ');
-
- work.exec(
- "INSERT INTO timers(channel_id, name, message, interval_sec) "
- "VALUES "
- "(" +
- std::to_string(request.channel.get_id()) + ", '" + name +
- "', '" + m + "', " + std::to_string(interval_s) + ")");
- work.commit();
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::TimerNew, {name})
- .value();
- } else if (subcommand_id == "remove") {
- if (timers.empty()) {
- throw ResponseException<ResponseError::NOT_FOUND>(
- request, bundle.localization, name);
- }
-
- work.exec("DELETE FROM timers WHERE id = " +
- std::to_string(timers[0][0].as<int>()));
- work.commit();
-
- return bundle.localization
- .get_formatted_line(request, loc::LineId::TimerDelete, {name})
- .value();
- }
-
- throw ResponseException<ResponseError::SOMETHING_WENT_WRONG>(
- request, bundle.localization);
- }
- };
- }
-}
diff --git a/src/schemas/channel.hpp b/src/schemas/channel.hpp
deleted file mode 100644
index 2560331..0000000
--- a/src/schemas/channel.hpp
+++ /dev/null
@@ -1,76 +0,0 @@
-#pragma once
-
-#include <chrono>
-#include <optional>
-#include <pqxx/pqxx>
-#include <string>
-
-#include "../constants.hpp"
-#include "../utils/chrono.hpp"
-
-namespace bot::schemas {
- class Channel {
- public:
- Channel(const pqxx::row &row) {
- this->id = row[0].as<int>();
- this->alias_id = row[1].as<int>();
- this->alias_name = row[2].as<std::string>();
-
- this->joined_at =
- utils::chrono::string_to_time_point(row[3].as<std::string>());
-
- if (!row[4].is_null()) {
- this->opted_out_at =
- utils::chrono::string_to_time_point(row[4].as<std::string>());
- }
- }
-
- ~Channel() = default;
-
- const int &get_id() const { return this->id; }
- const int &get_alias_id() const { return this->alias_id; }
- const std::string &get_alias_name() const { return this->alias_name; }
- const std::chrono::system_clock::time_point &get_joined_at() const {
- return this->joined_at;
- }
- const std::optional<std::chrono::system_clock::time_point> &
- get_opted_out_at() const {
- return this->opted_out_at;
- }
-
- private:
- int id, alias_id;
- std::string alias_name;
- std::chrono::system_clock::time_point joined_at;
- std::optional<std::chrono::system_clock::time_point> opted_out_at;
- };
-
- class ChannelPreferences {
- public:
- ChannelPreferences(const pqxx::row &row) {
- this->channel_id = row[0].as<int>();
-
- if (!row[2].is_null()) {
- this->prefix = row[1].as<std::string>();
- } else {
- this->prefix = DEFAULT_PREFIX;
- }
-
- if (!row[3].is_null()) {
- this->locale = row[2].as<std::string>();
- } else {
- this->locale = DEFAULT_LOCALE_ID;
- }
- }
-
- ~ChannelPreferences() = default;
-
- const int &get_channel_id() const { return this->channel_id; }
- const std::string &get_prefix() const { return this->prefix; }
- const std::string &get_locale() const { return this->locale; }
-
- private:
- int channel_id;
- std::string prefix, locale;
- };
-}
diff --git a/src/schemas/stream.cpp b/src/schemas/stream.cpp
deleted file mode 100644
index 6ef10dc..0000000
--- a/src/schemas/stream.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#include "stream.hpp"
-
-namespace bot::schemas {
- EventType string_to_event_type(const std::string &type) {
- if (type == "live") {
- return EventType::LIVE;
- } else if (type == "offline") {
- return EventType::OFFLINE;
- } else if (type == "title") {
- return EventType::TITLE;
- } else if (type == "game") {
- return EventType::GAME;
- } else {
- return EventType::CUSTOM;
- }
- }
-}
diff --git a/src/schemas/stream.hpp b/src/schemas/stream.hpp
deleted file mode 100644
index a636ea5..0000000
--- a/src/schemas/stream.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include <string>
-
-namespace bot::schemas {
- enum EventType { LIVE, OFFLINE, TITLE, GAME, CUSTOM = 99 };
- EventType string_to_event_type(const std::string &type);
-
- enum EventFlag { MASSPING };
-
-}
diff --git a/src/schemas/user.hpp b/src/schemas/user.hpp
deleted file mode 100644
index 0bd1368..0000000
--- a/src/schemas/user.hpp
+++ /dev/null
@@ -1,73 +0,0 @@
-#pragma once
-
-#include <chrono>
-#include <optional>
-#include <pqxx/pqxx>
-#include <string>
-
-#include "../utils/chrono.hpp"
-
-namespace bot::schemas {
- class User {
- public:
- User(const pqxx::row &row) {
- this->id = row[0].as<int>();
- this->alias_id = row[1].as<int>();
- this->alias_name = row[2].as<std::string>();
-
- this->joined_at =
- utils::chrono::string_to_time_point(row[3].as<std::string>());
-
- if (!row[4].is_null()) {
- this->opted_out_at =
- utils::chrono::string_to_time_point(row[4].as<std::string>());
- }
- }
-
- ~User() = default;
-
- const int &get_id() const { return this->id; }
- const int &get_alias_id() const { return this->alias_id; }
- const std::string &get_alias_name() const { return this->alias_name; }
- void set_alias_name(const std::string &alias_name) {
- this->alias_name = alias_name;
- }
- const std::chrono::system_clock::time_point &get_joined_at() const {
- return this->joined_at;
- }
- const std::optional<std::chrono::system_clock::time_point> &
- get_opted_out_at() const {
- return this->opted_out_at;
- }
-
- private:
- int id, alias_id;
- std::string alias_name;
- std::chrono::system_clock::time_point joined_at;
- std::optional<std::chrono::system_clock::time_point> opted_out_at;
- };
-
- enum PermissionLevel { SUSPENDED, USER, VIP, MODERATOR, BROADCASTER };
-
- class UserRights {
- public:
- UserRights(const pqxx::row &row) {
- this->id = row[0].as<int>();
- this->user_id = row[1].as<int>();
- this->channel_id = row[2].as<int>();
- this->level = static_cast<PermissionLevel>(row[3].as<int>());
- }
-
- ~UserRights() = default;
-
- const int &get_id() const { return this->id; }
- const int &get_user_id() const { return this->user_id; }
- const int &get_channel_id() const { return this->channel_id; }
- const PermissionLevel &get_level() const { return this->level; }
- void set_level(PermissionLevel level) { this->level = level; }
-
- private:
- int id, user_id, channel_id;
- PermissionLevel level;
- };
-}
diff --git a/src/stream.cpp b/src/stream.cpp
deleted file mode 100644
index 6e48fb8..0000000
--- a/src/stream.cpp
+++ /dev/null
@@ -1,200 +0,0 @@
-#include "stream.hpp"
-
-#include <algorithm>
-#include <chrono>
-#include <pqxx/pqxx>
-#include <set>
-#include <string>
-#include <thread>
-#include <utility>
-#include <vector>
-
-#include "api/twitch/schemas/stream.hpp"
-#include "config.hpp"
-#include "logger.hpp"
-#include "schemas/stream.hpp"
-#include "utils/string.hpp"
-
-namespace bot::stream {
- void StreamListenerClient::listen_channel(const int &id) {
- this->ids.push_back(id);
- }
- void StreamListenerClient::unlisten_channel(const int &id) {
- auto x = std::find_if(this->ids.begin(), this->ids.end(),
- [&](const auto &x) { return x == id; });
-
- if (x != this->ids.end()) {
- this->ids.erase(x);
- }
-
- auto y = std::find_if(this->online_ids.begin(), this->online_ids.end(),
- [&](const auto &x) { return x == id; });
-
- if (y != this->online_ids.end()) {
- this->online_ids.erase(y);
- }
- }
- void StreamListenerClient::run() {
- while (true) {
- this->update_channel_ids();
- this->check();
- std::this_thread::sleep_for(std::chrono::seconds(5));
- }
- }
- void StreamListenerClient::check() {
- auto streams = this->helix_client.get_streams(this->ids);
- auto now = std::chrono::system_clock::now();
- auto now_time_it = std::chrono::system_clock::to_time_t(now);
- auto now_tm = std::gmtime(&now_time_it);
- now = std::chrono::system_clock::from_time_t(std::mktime(now_tm));
-
- // adding new ids
- for (const auto &stream : streams) {
- bool is_already_live =
- std::any_of(this->online_ids.begin(), this->online_ids.end(),
- [&](const auto &x) { return x == stream.get_user_id(); });
-
- if (!is_already_live) {
- this->online_ids.insert(stream.get_user_id());
-
- auto difference = now - stream.get_started_at();
- auto difference_min =
- std::chrono::duration_cast<std::chrono::minutes>(difference);
-
- if (difference_min.count() < 1) {
- this->handler(schemas::EventType::LIVE, stream);
- }
- }
- }
-
- // removing old ids
- for (auto i = this->online_ids.begin(); i != this->online_ids.end();) {
- auto stream =
- std::find_if(streams.begin(), streams.end(),
- [&](const auto &x) { return x.get_user_id() == *i; });
-
- if (stream == streams.end()) {
- this->handler(schemas::EventType::OFFLINE,
- api::twitch::schemas::Stream{*i});
- i = this->online_ids.erase(i);
- } else {
- ++i;
- }
- }
- }
- void StreamListenerClient::handler(
- const schemas::EventType &type,
- const api::twitch::schemas::Stream &stream) {
- pqxx::connection conn(GET_DATABASE_CONNECTION_URL(this->configuration));
- pqxx::work work(conn);
-
- pqxx::result events = work.exec(
- "SELECT id, channel_id, message, flags FROM events WHERE event_type "
- "= " +
- std::to_string(type) +
- " AND target_alias_id = " + std::to_string(stream.get_user_id()));
-
- for (const auto &event : events) {
- pqxx::row channel = work.exec1(
- "SELECT alias_id, alias_name, opted_out_at FROM channels WHERE id "
- "= " +
- std::to_string(event[1].as<int>()));
-
- if (!channel[2].is_null()) {
- continue;
- }
-
- pqxx::result subs = work.exec(
- "SELECT user_id FROM event_subscriptions WHERE event_id = " +
- std::to_string(event[0].as<int>()));
-
- std::set<std::string> user_ids;
- if (!subs.empty()) {
- for (const auto &sub : subs) {
- user_ids.insert(std::to_string(sub[0].as<int>()));
- }
-
- pqxx::result users = work.exec(
- "SELECT alias_name FROM users WHERE id IN (" +
- utils::string::str(user_ids.begin(), user_ids.end(), ',') + ")");
-
- user_ids.clear();
-
- for (const auto &user : users) {
- user_ids.insert(user[0].as<std::string>());
- }
- }
-
- auto flags = event[3].as_array();
- std::pair<pqxx::array_parser::juncture, std::string> elem;
-
- do {
- elem = flags.get_next();
- if (elem.first == pqxx::array_parser::juncture::string_value) {
- if (std::stoi(elem.second) == schemas::EventFlag::MASSPING) {
- auto chatters = this->helix_client.get_chatters(
- channel[0].as<int>(), this->irc_client.get_bot_id());
-
- for (const auto &chatter : chatters) {
- user_ids.insert(chatter.login);
- }
- }
- }
- } while (elem.first != pqxx::array_parser::juncture::done);
-
- std::string base = "⚡ " + event[2].as<std::string>();
- std::vector<std::string> msgs = {""};
- int index = 0;
-
- if (!user_ids.empty()) {
- base.append(" · ");
- }
-
- for (const auto &user_id : user_ids) {
- const std::string &current_msg = msgs.at(index);
- std::string x = "@" + user_id;
-
- if (base.length() + current_msg.length() + 1 + x.length() >= 500) {
- index += 1;
- }
-
- if (index > msgs.size() - 1) {
- msgs.push_back(x);
- } else {
- msgs[index] = current_msg + " " + x;
- }
- }
-
- for (const auto &msg : msgs) {
- this->irc_client.say(channel[1].as<std::string>(), base + msg);
- }
- }
-
- work.commit();
- conn.close();
- }
- void StreamListenerClient::update_channel_ids() {
- pqxx::connection conn(GET_DATABASE_CONNECTION_URL(this->configuration));
- pqxx::work work(conn);
-
- pqxx::result ids =
- work.exec("SELECT target_alias_id FROM events WHERE event_type < 99");
-
- for (const auto &row : ids) {
- int id = row[0].as<int>();
-
- if (std::any_of(this->ids.begin(), this->ids.end(),
- [&](const auto &x) { return x == id; })) {
- continue;
- }
-
- log::info("TwitchStreamListener",
- "Listening stream events for ID " + std::to_string(id));
-
- this->ids.push_back(id);
- }
-
- work.commit();
- conn.close();
- }
-}
diff --git a/src/stream.hpp b/src/stream.hpp
deleted file mode 100644
index 73313ed..0000000
--- a/src/stream.hpp
+++ /dev/null
@@ -1,41 +0,0 @@
-#pragma once
-
-#include <set>
-#include <vector>
-
-#include "api/twitch/helix_client.hpp"
-#include "api/twitch/schemas/stream.hpp"
-#include "config.hpp"
-#include "irc/client.hpp"
-#include "schemas/stream.hpp"
-
-namespace bot::stream {
- class StreamListenerClient {
- public:
- StreamListenerClient(const api::twitch::HelixClient &helix_client,
- irc::Client &irc_client,
- const Configuration &configuration)
- : helix_client(helix_client),
- irc_client(irc_client),
- configuration(configuration){};
- ~StreamListenerClient() = default;
-
- void run();
- void listen_channel(const int &id);
- void unlisten_channel(const int &id);
-
- private:
- void check();
- void handler(const schemas::EventType &type,
- const api::twitch::schemas::Stream &stream);
- void update_channel_ids();
-
- const api::twitch::HelixClient &helix_client;
- irc::Client &irc_client;
- const Configuration &configuration;
-
- std::vector<int> ids;
-
- std::set<int> online_ids;
- };
-}
diff --git a/src/timer.cpp b/src/timer.cpp
deleted file mode 100644
index 055dde0..0000000
--- a/src/timer.cpp
+++ /dev/null
@@ -1,70 +0,0 @@
-#include "timer.hpp"
-
-#include <chrono>
-#include <pqxx/pqxx>
-#include <string>
-#include <thread>
-
-#include "config.hpp"
-#include "irc/client.hpp"
-#include "utils/chrono.hpp"
-
-namespace bot {
- void create_timer_thread(irc::Client *irc_client,
- Configuration *configuration) {
- while (true) {
- pqxx::connection conn(GET_DATABASE_CONNECTION_URL_POINTER(configuration));
- pqxx::work *work = new pqxx::work(conn);
-
- pqxx::result timers = work->exec(
- "SELECT id, interval_sec, message, channel_id, last_executed_at FROM "
- "timers");
-
- for (const auto &timer : timers) {
- int id = timer[0].as<int>();
- int interval_sec = timer[1].as<int>();
- std::string message = timer[2].as<std::string>();
- int channel_id = timer[3].as<int>();
-
- // it could be done in sql query
- std::chrono::system_clock::time_point last_executed_at =
- utils::chrono::string_to_time_point(timer[4].as<std::string>());
- auto now = std::chrono::system_clock::now();
- auto now_time_it = std::chrono::system_clock::to_time_t(now);
- auto now_tm = std::gmtime(&now_time_it);
- now = std::chrono::system_clock::from_time_t(std::mktime(now_tm));
-
- auto difference = std::chrono::duration_cast<std::chrono::seconds>(
- now - last_executed_at);
-
- if (difference.count() > interval_sec) {
- pqxx::result channels = work->exec(
- "SELECT alias_name, opted_out_at FROM channels WHERE id = " +
- std::to_string(channel_id));
-
- if (!channels.empty() && channels[0][1].is_null()) {
- std::string alias_name = channels[0][0].as<std::string>();
-
- irc_client->say(alias_name, message);
- }
-
- work->exec(
- "UPDATE timers SET last_executed_at = timezone('utc', now()) "
- "WHERE "
- "id = " +
- std::to_string(id));
-
- work->commit();
-
- delete work;
- work = new pqxx::work(conn);
- }
- }
-
- delete work;
- conn.close();
-
- std::this_thread::sleep_for(std::chrono::seconds(1));
- }
- }
-}
diff --git a/src/timer.hpp b/src/timer.hpp
deleted file mode 100644
index 40a52ee..0000000
--- a/src/timer.hpp
+++ /dev/null
@@ -1,9 +0,0 @@
-#pragma once
-
-#include "config.hpp"
-#include "irc/client.hpp"
-
-namespace bot {
- void create_timer_thread(irc::Client *irc_client,
- Configuration *configuration);
-}
diff --git a/src/utils/chrono.cpp b/src/utils/chrono.cpp
deleted file mode 100644
index 7a7f2c9..0000000
--- a/src/utils/chrono.cpp
+++ /dev/null
@@ -1,48 +0,0 @@
-#include "chrono.hpp"
-
-#include <chrono>
-#include <cmath>
-#include <ctime>
-#include <iomanip>
-#include <sstream>
-#include <string>
-
-namespace bot::utils::chrono {
- std::string format_timestamp(int seconds) {
- int d = round(seconds / (60 * 60 * 24));
- int h = round(seconds / (60 * 60) % 24);
- int m = round(seconds % (60 * 60) / 60);
- int s = round(seconds % 60);
-
- // Only seconds:
- if (d == 0 && h == 0 && m == 0) {
- return std::to_string(s) + "s";
- }
- // Minutes and seconds:
- else if (d == 0 && h == 0) {
- return std::to_string(m) + "m" + std::to_string(s) + "s";
- }
- // Hours and minutes:
- else if (d == 0) {
- return std::to_string(h) + "h" + std::to_string(m) + "m";
- }
- // Days and hours:
- else {
- return std::to_string(d) + "d" + std::to_string(h) + "h";
- }
- }
-
- std::chrono::system_clock::time_point string_to_time_point(
- const std::string &value, const std::string &format) {
- std::tm tm = {};
- std::stringstream ss(value);
-
- ss >> std::get_time(&tm, format.c_str());
-
- if (ss.fail()) {
- throw std::invalid_argument("Invalid time format");
- }
-
- return std::chrono::system_clock::from_time_t(std::mktime(&tm));
- }
-}
diff --git a/src/utils/chrono.hpp b/src/utils/chrono.hpp
deleted file mode 100644
index 7e85e70..0000000
--- a/src/utils/chrono.hpp
+++ /dev/null
@@ -1,11 +0,0 @@
-#pragma once
-
-#include <chrono>
-#include <string>
-
-namespace bot::utils::chrono {
- std::string format_timestamp(int seconds);
- std::chrono::system_clock::time_point string_to_time_point(
- const std::string &value,
- const std::string &format = "%Y-%m-%d %H:%M:%S");
-}
diff --git a/src/utils/string.cpp b/src/utils/string.cpp
deleted file mode 100644
index 71c06bf..0000000
--- a/src/utils/string.cpp
+++ /dev/null
@@ -1,66 +0,0 @@
-#include "string.hpp"
-
-#include <sstream>
-#include <string>
-#include <vector>
-
-namespace bot {
- namespace utils {
- namespace string {
- std::vector<std::string> split_text(const std::string &text,
- char delimiter) {
- std::vector<std::string> parts;
-
- std::istringstream iss(text);
- std::string part;
-
- while (std::getline(iss, part, delimiter)) {
- parts.push_back(part);
- }
-
- return parts;
- }
-
- std::string join_vector(const std::vector<std::string> &vec,
- char delimiter) {
- if (vec.empty()) {
- return "";
- }
-
- std::string str;
-
- for (auto i = vec.begin(); i != vec.end() - 1; i++) {
- str += *i + delimiter;
- }
-
- str += vec[vec.size() - 1];
-
- return str;
- }
-
- std::string join_vector(const std::vector<std::string> &vec) {
- std::string str;
-
- for (const auto &e : vec) {
- str += e;
- }
-
- return str;
- }
-
- bool string_contains_sql_injection(const std::string &input) {
- std::string forbidden_strings[] = {";", "--", "'", "\"",
- "/*", "*/", "xp_", "exec",
- "sp_", "insert", "select", "delete"};
-
- for (const auto &str : forbidden_strings) {
- if (input.find(str) != std::string::npos) {
- return true;
- }
- }
-
- return false;
- }
- }
- }
-}
diff --git a/src/utils/string.hpp b/src/utils/string.hpp
deleted file mode 100644
index c8385ad..0000000
--- a/src/utils/string.hpp
+++ /dev/null
@@ -1,32 +0,0 @@
-#pragma once
-
-#include <sstream>
-#include <string>
-#include <vector>
-
-namespace bot {
- namespace utils {
- namespace string {
- std::vector<std::string> split_text(const std::string &text,
- char delimiter);
- std::string join_vector(const std::vector<std::string> &vec,
- char delimiter);
- std::string join_vector(const std::vector<std::string> &vec);
-
- template <typename T>
- std::string str(T begin, T end, char delimiter) {
- std::stringstream ss;
- bool first = true;
-
- for (; begin != end; begin++) {
- if (!first) ss << delimiter;
- ss << *begin;
- first = false;
- }
- return ss.str();
- }
-
- bool string_contains_sql_injection(const std::string &input);
- }
- }
-}