Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
build
run.sh
.vscode
.cache
24 changes: 6 additions & 18 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@ include(FetchContent)

file(GLOB_RECURSE SRC_FILES "src/*.cpp")

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
find_package(CURL)

set(SKIP_BUILD_TEST ON)
set(CPR_USE_SYSTEM_CURL ON)
Expand All @@ -22,33 +21,22 @@ target_include_directories(${PROJECT_NAME} PRIVATE include)
FetchContent_Declare(
cpr
GIT_REPOSITORY https://github.com/libcpr/cpr.git
GIT_TAG 1.11.2
)
FetchContent_Declare(
json
GIT_REPOSITORY https://github.com/nlohmann/json.git
GIT_TAG v3.11.3
GIT_TAG 1.12.0
)
FetchContent_Declare(
tgbot
GIT_REPOSITORY https://github.com/reo7sp/tgbot-cpp.git
GIT_TAG v1.9
)
FetchContent_Declare(
fmt
GIT_REPOSITORY https://github.com/fmtlib/fmt.git
GIT_TAG 11.1.4
GIT_TAG v1.9.1
)
FetchContent_Declare(
libpqxx
GIT_REPOSITORY https://github.com/jtv/libpqxx.git
GIT_TAG 7.10.0
GIT_TAG 7.10.1
)
FetchContent_Declare(
pugixml
GIT_REPOSITORY https://github.com/zeux/pugixml.git
GIT_TAG v1.15
)
FetchContent_MakeAvailable(cpr json tgbot fmt libpqxx pugixml)

target_link_libraries(${PROJECT_NAME} cpr nlohmann_json TgBot ${OPENSSL_LIBRARIES} fmt::fmt pugixml pqxx ${CURL_LIBRARIES})
FetchContent_MakeAvailable(cpr tgbot libpqxx pugixml)
target_link_libraries(${PROJECT_NAME} cpr TgBot pugixml pqxx)
23 changes: 8 additions & 15 deletions include/bot.hpp
Original file line number Diff line number Diff line change
@@ -1,27 +1,18 @@
#pragma once

#include <string>
#include <string_view>
#include <tuple>
#include <vector>
#include <array>
#include <filesystem>
#include <memory>

#include <tgbot/tgbot.h>

#include <log.hpp>
#include <db/db.hpp>
#include <utils.hpp>
#include <services/service.hpp>
#include <commandspool.hpp>
#include <threadspool.hpp>
#include <sync.hpp>

TgBot::InlineKeyboardMarkup buildInlineKeyboard(const std::vector<std::string>& buttons);

class Bot : public TgBot::Bot {
public:
Bot(const std::string& token, unsigned int workers = 15);
Bot(const std::string& token);
~Bot();

void start();
Expand All @@ -39,7 +30,8 @@ class Bot : public TgBot::Bot {

template<typename... Args>
void sendMessagef(int64_t id, std::string fmt, Args&&... args) {
const std::string text = fmt::vformat(fmt, fmt::vargs<Args...>{{args...}});
auto format_args = std::make_format_args(std::forward<Args>(args)...);
const std::string text = std::vformat(fmt, format_args);
sendMessage(id, text);
}

Expand Down Expand Up @@ -71,10 +63,11 @@ class Bot : public TgBot::Bot {

std::vector<std::shared_ptr<Service>> services;

std::shared_ptr<CommandsPool> commands_pool;
std::shared_ptr<ThreadsPool> thread_pool;
std::unique_ptr<CommandsPool> commands_pool;
std::unique_ptr<ThreadsPool> thread_pool;
std::unique_ptr<Sync> sync;

void sendContent(const send_t& send, std::int64_t user_id, std::shared_ptr<Service> service);
void sendContent(const postData& post, std::int64_t user_id, std::shared_ptr<Service> service);
void mainloop();
void update_services();
void command_handler(TgBot::Message::Ptr message);
Expand Down
6 changes: 3 additions & 3 deletions include/commands.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ struct BaseCommand : public AbstractBaseCommand {
std::string args_str = "";

for (const auto& arg : args)
args_str += fmt::format("<{}> ", arg);
args_str += std::format("<{}> ", arg);

help_str += fmt::format("\t/{} {}", name, args_str);
help_str += std::format("\t/{} {}", name, args_str);

if (help.size() > 0)
help_str += fmt::format(":\t{}", help);
help_str += std::format(":\t{}", help);

return help_str;
}
Expand Down
17 changes: 14 additions & 3 deletions include/db/connectionpool.hpp
Original file line number Diff line number Diff line change
@@ -1,17 +1,28 @@
#pragma once

#include <future>
#include <mutex>
#include <queue>
#include <condition_variable>
#include <pqxx/pqxx>
#include <log.hpp>
#include <settings.hpp>

class ConnectionPool {
public:
ConnectionPool(const std::string &url, int count) {
for (int i = 0; i < count; i++)
connections.push(std::make_unique<pqxx::connection>(url));
ConnectionPool(const std::string &url) {
std::vector<std::future<std::unique_ptr<pqxx::connection>>> futures;

for (int i = 0; i < CONNECT_COUNT; ++i) {
futures.push_back(std::async(std::launch::async, [url]() {
return std::make_unique<pqxx::connection>(url);
}));
}

for (auto& f : futures) {
auto conn = f.get();
connections.push(std::move(conn));
}
LOG_INFO("[DB] Connected to database");
}

Expand Down
37 changes: 18 additions & 19 deletions include/db/db.hpp
Original file line number Diff line number Diff line change
@@ -1,51 +1,50 @@
#pragma once

#include <pqxx/pqxx>
#include <string>
#include <string_view>
#include <vector>
#include <log.hpp>
#include <unordered_map>
#include <services/service.hpp>
#include <db/connectionpool.hpp>
#include <db/transaction.hpp>
#include <expected>

class DB {
public:
DB(const DB&) = delete;
DB& operator=(const DB&) = delete;

bool addTag(std::shared_ptr<Service> service, std::string_view tag, std::int64_t user_id);
std::expected<std::monostate, std::string> addTag(std::shared_ptr<Service> service, std::string_view tag, std::int64_t user_id);
void addAntiTag(std::shared_ptr<Service> service, std::string_view tag, std::int64_t user_id);
void rmTag(std::shared_ptr<Service> service, std::string_view tag, std::int64_t user_id);
void rmAntiTag(std::shared_ptr<Service> service, std::string_view tag, std::int64_t user_id);
void addHistory(std::shared_ptr<Service> service, int tag_id, const post_data_tv &newhistory, std::int64_t user_id);
void addHistory(const post_data_tv &newhistory, std::int64_t user_id);
bool isUserTableEmpty();
bool userExist(std::int64_t tg_id, bool is_admin = false);
std::unordered_map<int, std::vector<int64_t>> getUsersByTags(std::shared_ptr<Service> service);
std::vector<std::string> getHistory(std::int64_t user_id, std::shared_ptr<Service> service);
std::vector<std::string> getAntiTagForUserAndSite(std::int64_t user_id, std::shared_ptr<Service> service);
post_data_tv getNewPostForUser(std::shared_ptr<Service> service, std::int64_t tg_id);
std::vector<int> getUserForTags(std::shared_ptr<Service> service);
std::string getFormattedTagsAndAntiTags(std::int64_t user_id);
void addUser(std::int64_t tg_id, bool is_admin = false);
void rmUser(std::int64_t tg_id);
void scoreUpdate(std::shared_ptr<Service> service, std::int64_t user_id, std::int64_t score);
std::int64_t getScore(std::shared_ptr<Service> service, std::int64_t user_id);
bool userIsAdmin(std::int64_t tg_id);
void addSystemValue(std::string_view key, std::string_view value);
std::string getSystemValue(std::string_view key);
static DB& getInstance(const std::string& connStr = "") {
static DB instance(connStr);
return instance;
}

std::pair<int, std::optional<taginfo_t>> getCacheTagById(std::string_view site, int tag_id);
std::pair<int, std::optional<taginfo_t>> getCacheTagByName(std::string_view site, std::string_view name);
void addCacheTag(const taginfo_t& info);
std::optional<Taginfo> getCacheTagById(int service_id, int tag_id);
std::pair<int, std::optional<Taginfo>> getCacheTagByName(int service_id, std::string_view name);
void addCacheTag(const Taginfo& info);
std::optional<std::int64_t> getUserTagId(std::int64_t user_id, std::int64_t cache_tag_id);
std::optional<int> getUserTagIdByExternalTagId(int external_tag_id);
std::optional<taginfo_t> getTagFromCacheOrAddTag(std::shared_ptr<Service> service, int tag_id);
std::optional<Taginfo> getTagFromCacheOrAddTag(std::shared_ptr<Service> service, int tag_id);

std::optional<int> getIdServiceByName(std::string_view name);
std::string getServiceName(int service_id);

bool addPost(std::shared_ptr<Service> service, const post_data_tv& posts);
bool addTagOnPost(std::shared_ptr<Service> service, const std::vector<std::string>& tags, int post_id);
private:
DB(const std::string &url_db);
std::shared_ptr<ConnectionPool> connection_pool;
std::pair<int, std::optional<taginfo_t>> getAndCreate(std::shared_ptr<Service> service, std::string_view tag);

std::optional<Taginfo> tagInfoFromSite(std::shared_ptr<Service> service, const std::string& tag);
post_data_tv getPostsByTagId(std::shared_ptr<Service> service, int tag_id);
};
2 changes: 1 addition & 1 deletion include/db/transaction.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ class Transaction {
void commit() {
txn.commit();
}

pqxx::work& get() {
return txn;
}
Expand Down
14 changes: 4 additions & 10 deletions include/keyboard.hpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
#pragma once

#include "commands.hpp"
#include "fmt/format.h"
#include "tgbot/types/InlineKeyboardButton.h"
#include <format>
#include <log.hpp>
#include <memory>
#include <utility>
#include <vector>
#include <unordered_map>
#include <string>
#include <string_view>

#include <db/db.hpp>
#include <utils.hpp>
#include <tgbot/tgbot.h>
#include <bot.hpp>
#include <commands.hpp>

struct BaseAbstractButton : public TgBot::InlineKeyboardButton {
Expand All @@ -35,7 +29,7 @@ struct BaseButton : public BaseAbstractButton {

BaseButton(const std::string& prefix) {
text = Name;
callbackData = fmt::format("{} {}", prefix, std::string(Name));
callbackData = std::format("{} {}", prefix, std::string(Name));
LOG_INFO("Make button: {}, CallData: {}", text, callbackData);
}

Expand All @@ -57,7 +51,7 @@ struct ButtonRow {

template<std::size_t... Is>
static std::vector<TgBot::InlineKeyboardButton::Ptr> make_buttons(const std::string& prefix, std::index_sequence<Is...>) {
return {std::make_shared<Buttons>(fmt::format("{} {}", prefix, Is))...};
return {std::make_shared<Buttons>(std::format("{} {}", prefix, Is))...};
}

void dispatch(int index, CommandContext& ctx) {
Expand Down Expand Up @@ -125,7 +119,7 @@ struct BaseKeyboard : public AbstractBaseKeyboard {

template<std::size_t... Is>
static std::tuple<Rows...> initialize_rows(std::index_sequence<Is...>) {
return {Rows(fmt::format("{} {}", std::string(Name), Is))...};
return {Rows(std::format("{} {}", std::string(Name), Is))...};
}

// Recursive helper to dispatch based on the row index.
Expand Down
23 changes: 13 additions & 10 deletions include/log.hpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,25 @@
#pragma once

#include <fmt/format.h>
#include <fmt/std.h>
#include <iostream>
#include <thread>
#include <format>

#define RESET "\033[0m"
#define RED "\033[31m"
#define GREEN "\033[32m"
#define YELLOW "\033[33m"
#define BOLDRED "\033[1m\033[31m"
#define BLUE "\033[34m"
#define BLUE "\033[34m"

#define LOG_GENERIC(color, level, ...) fmt::format(color "[" #level "][{}]" RESET " {}\n", std::this_thread::get_id(), fmt::format(__VA_ARGS__))
#define LOG_GENERIC(color, level, ...) \
std::cout << color "[" #level "][" << std::this_thread::get_id() << "] " << std::format(__VA_ARGS__) << RESET << "\n"

#define LOG_INFO(...) std::cout << LOG_GENERIC(GREEN, INFO, __VA_ARGS__)
#define LOG_WARN(...) std::cout << LOG_GENERIC(YELLOW, WARN, __VA_ARGS__)
#define LOG_DEBUG(...) std::cout << LOG_GENERIC(BLUE, DEBUG, __VA_ARGS__)
#define LOG_ERROR(...) std::cerr << LOG_GENERIC(RED, ERR, __VA_ARGS__)
#define LOG_CRITICAL(...) std::cerr << LOG_GENERIC(BOLDRED, CRIT, __VA_ARGS__)
#define LOG_FATAL(...) std::cerr << LOG_GENERIC(BOLDRED, FATAL, __VA_ARGS__)
#define LOG_GENERIC2(color, level, ...) \
std::cerr << color "[" #level "][" << std::this_thread::get_id() << "] " << std::format(__VA_ARGS__) << RESET << "\n"

#define LOG_INFO(...) LOG_GENERIC(GREEN, INFO, __VA_ARGS__)
#define LOG_WARN(...) LOG_GENERIC(YELLOW, WARN, __VA_ARGS__)
#define LOG_DEBUG(...) LOG_GENERIC(BLUE, DEBUG, __VA_ARGS__)
#define LOG_ERROR(...) LOG_GENERIC2(BLUE, ERROR, __VA_ARGS__)
#define LOG_CRITICAL(...) LOG_GENERIC2(BLUE, CRIT, __VA_ARGS__)
#define LOG_FATAL(...) LOG_GENERIC2(BLUE, FATAL, __VA_ARGS__)
15 changes: 5 additions & 10 deletions include/services/gelbooru.hpp
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
#pragma once

#include <services/service.hpp>
#include <string_view>
#include <utils.hpp>
#include <cpr/cookies.h>
#include <pugixml.hpp>
#include <nlohmann/json.hpp>
#include <db/db.hpp>
using json = nlohmann::json;

class Gelbooru : public Service, public std::enable_shared_from_this<Gelbooru> {
public:
Gelbooru();
post_data_tv parse(int tag_id) override;

std::optional<taginfo_t> getTagInfo(const std::string& tag) override;
std::optional<taginfo_t> tagInfoById(int tag_id) override;
std::optional<Taginfo> getTagInfo(const std::string& tag) override;
std::optional<Taginfo> tagInfoById(int tag_id) override;

private:
std::optional<taginfo_t> parseTagInfoFromXml(std::string_view xml);
std::string apikey;
std::string userid;
std::optional<Taginfo> parseTagInfoFromXml(std::string_view xml);
};
12 changes: 3 additions & 9 deletions include/services/rule34.hpp
Original file line number Diff line number Diff line change
@@ -1,20 +1,14 @@
#pragma once

#include <optional>
#include <services/service.hpp>
#include <utils.hpp>
#include <pugixml.hpp>
#include <db/db.hpp>
#include <nlohmann/json.hpp>
using json = nlohmann::json;

class Rule34 : public Service, public std::enable_shared_from_this<Rule34> {
public:
Rule34();
post_data_tv parse(int tag_id) override;
std::optional<taginfo_t> getTagInfo(const std::string& tag) override;
std::optional<taginfo_t> tagInfoById(int tag_id) override;
std::optional<Taginfo> getTagInfo(const std::string& tag) override;
std::optional<Taginfo> tagInfoById(int tag_id) override;

private:
std::optional<taginfo_t> parseTagInfoFromXml(std::string_view xml);
std::optional<Taginfo> parseTagInfoFromXml(std::string_view xml);
};
Loading