From 84b01d9ae1edbb0e1ef87d5d44a58e6c47c2db6d Mon Sep 17 00:00:00 2001 From: Syping Date: Fri, 10 Apr 2026 01:34:39 +0200 Subject: [PATCH] dtranslatebot: add GTK gui and port messages to log callback system --- CMakeLists.txt | 57 ++++++-- src/cli/main.cpp | 73 +++------- src/core/discord_bot.cpp | 91 +++++++++++++ src/core/discord_bot.h | 56 ++++++++ src/core/http_request.h | 2 +- src/core/log.h | 31 +++++ src/core/message_queue.cpp | 11 +- src/core/message_queue.h | 11 ++ src/core/regex.h | 28 ++-- src/core/settings.cpp | 95 ++++++------- src/core/settings.h | 10 +- src/core/submit_queue.cpp | 11 +- src/core/submit_queue.h | 14 ++ src/core/webhook_push.cpp | 8 +- src/database/file/file.cpp | 30 ++--- src/database/file/file.h | 4 +- src/gui/main.cpp | 31 +++++ src/gui/user_interface.cpp | 127 ++++++++++++++++++ src/gui/user_interface.h | 48 +++++++ src/translator/deepl/deepl.cpp | 9 +- src/translator/deepl/deepl.h | 4 +- .../libretranslate/libretranslate.cpp | 9 +- .../libretranslate/libretranslate.h | 6 +- .../lingvatranslate/lingvatranslate.cpp | 9 +- .../lingvatranslate/lingvatranslate.h | 6 +- src/translator/mozhi/mozhi.cpp | 9 +- src/translator/mozhi/mozhi.h | 6 +- 27 files changed, 620 insertions(+), 176 deletions(-) create mode 100644 src/core/discord_bot.cpp create mode 100644 src/core/discord_bot.h create mode 100644 src/core/log.h create mode 100644 src/gui/main.cpp create mode 100644 src/gui/user_interface.cpp create mode 100644 src/gui/user_interface.h diff --git a/CMakeLists.txt b/CMakeLists.txt index e9449c8..5758805 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,9 +25,11 @@ include(GNUInstallDirs) set(DTRANSLATEBOT_HEADERS src/core/curl_exception.h src/core/database.h + src/core/discord_bot.h src/core/http_headers.h src/core/http_request.h src/core/http_response.h + src/core/log.h src/core/message_queue.h src/core/regex.h src/core/settings.h @@ -44,9 +46,9 @@ set(DTRANSLATEBOT_HEADERS src/translator/stub/stub.h ) set(DTRANSLATEBOT_SOURCES - src/cli/main.cpp src/core/curl_exception.cpp src/core/database.cpp + src/core/discord_bot.cpp src/core/http_headers.cpp src/core/http_request.cpp src/core/message_queue.cpp @@ -69,13 +71,11 @@ list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") # Boost C++ Libraries option(WITH_BOOST "Build with Boost C++ Libraries" OFF) if (WITH_BOOST) - find_package(Boost COMPONENTS regex) - if (Boost_regex_FOUND) - list(APPEND DTRANSLATEBOT_LIBRARIES - Boost::regex - ) - set(DTRANSLATEBOT_USE_BOOST_REGEX TRUE) - endif() + find_package(Boost COMPONENTS regex REQUIRED) + list(APPEND DTRANSLATEBOT_LIBRARIES + Boost::regex + ) + set(DTRANSLATEBOT_USE_BOOST_REGEX TRUE) endif() # curl Library @@ -89,6 +89,23 @@ else() find_package(DPP REQUIRED) endif() +option(WITH_GUI "Build with dtranslatebot GUI" OFF) +if (WITH_GUI) + find_package(PkgConfig REQUIRED) + pkg_check_modules(GTKMM REQUIRED gtkmm-4.0) + list(APPEND DTRANSLATEBOT_HEADERS + src/gui/user_interface.h + ) + list(APPEND DTRANSLATEBOT_SOURCES + src/gui/main.cpp + src/gui/user_interface.cpp + ) +else() + list(APPEND DTRANSLATEBOT_SOURCES + src/cli/main.cpp + ) +endif() + # pthread Support set(THREADS_PREFER_PTHREAD_FLAG ON) find_package(Threads REQUIRED) @@ -129,13 +146,27 @@ if (WITH_DPP_STATIC_BUNDLE) endif() target_compile_definitions(dtranslatebot PRIVATE ${DPP_DEFINITIONS} + $<$:DTRANSLATEBOT_GUI> $<$:DTRANSLATEBOT_USE_BOOST_REGEX> ) -if (MSVC AND MSVC_VERSION GREATER_EQUAL 1914) - target_compile_options(dtranslatebot PRIVATE $<$:/Zc:__cplusplus>) -endif() -target_link_libraries(dtranslatebot PRIVATE ${DTRANSLATEBOT_LIBRARIES} ${DPP_LIBRARIES} CURL::libcurl Threads::Threads) -target_include_directories(dtranslatebot PRIVATE ${DPP_INCLUDE_DIR}) +target_compile_options(dtranslatebot PRIVATE + $<$,$,19.14>,$>:/Zc:__cplusplus> + $<$:${GTKMM_CFLAGS}> +) +target_link_libraries(dtranslatebot PRIVATE + ${DTRANSLATEBOT_LIBRARIES} + ${DPP_LIBRARIES} + CURL::libcurl + $<$:${GTKMM_LIBRARIES}> + Threads::Threads +) +target_link_directories(dtranslatebot PRIVATE + $<$:${GTKMM_LIBRARY_DIRS}> +) +target_include_directories(dtranslatebot PRIVATE + ${DPP_INCLUDE_DIR} + $<$:${GTKMM_INCLUDE_DIRS}> +) set_target_properties(dtranslatebot PROPERTIES CXX_STANDARD 17 CXX_STANDARD_REQUIRED ON diff --git a/src/cli/main.cpp b/src/cli/main.cpp index 9e3cb51..6288326 100644 --- a/src/cli/main.cpp +++ b/src/cli/main.cpp @@ -17,15 +17,16 @@ *****************************************************************************/ #include -#include -#include #include -#include +#include #include -#include "../core/message_queue.h" +#include "../core/discord_bot.h" #include "../core/settings.h" -#include "../core/slashcommands.h" -using namespace std::chrono_literals; + +void output_log(const std::string &message, const std::string &type, bool is_error) { + auto &output = !is_error ? std::cout : std::cerr; + output << "[" << type << "] " << message << std::endl; +} int main(int argc, char* argv[]) { bool flag_wait_for_translator = false; @@ -41,60 +42,26 @@ int main(int argc, char* argv[]) { return 0; } - std::cout << "[Launch] Processing configuration..." << std::endl; - bot::settings::settings settings; - if (!settings.parse_file(args.at(0))) - return 1; - CURLcode result = curl_global_init(CURL_GLOBAL_DEFAULT); if (result != CURLE_OK) { std::cerr << "[Error] Failed to initialise curl" << std::endl; return 1; } - for (;;) { - std::cout << "[Launch] Requesting supported languages..." << std::endl; - if (!settings.get_translator()->get_languages().empty()) { - break; - } - else if (flag_wait_for_translator) { - std::this_thread::sleep_for(5000ms); - } - else { - std::cerr << "[Error] Failed to initialise translateable languages" << std::endl; - return 1; - } + std::cout << "[Launch] Processing configuration..." << std::endl; + auto settings = std::make_shared(); + if (!settings->parse_file(args.at(0), &output_log)) + return 1; + + bot::discord_bot bot; + bot.log_callback_add(&output_log); + try { + bot.run(settings, false, flag_wait_for_translator); + } + catch (const std::exception &exception) { + std::cerr << "[Exception] " << exception.what() << std::endl; + std::cerr << "[Error] Exception while starting bot" << std::endl; } - - dpp::cluster bot(settings.token(), dpp::i_default_intents | dpp::i_direct_messages | dpp::i_message_content); - bot.on_log([&bot](const dpp::log_t &event) { - std::cerr << "[Log] " << event.message << std::endl; - }); - - bot::submit_queue submit_queue; - std::thread submit_queue_loop(&bot::submit_queue::run, &submit_queue, &bot); - - bot::message_queue message_queue; - std::thread message_queue_loop(&bot::message_queue::run, &message_queue, &settings, &submit_queue); - - bot.on_message_context_menu(std::bind(&bot::slashcommands::process_message_menu_event, &message_queue, &bot, &settings, std::placeholders::_1)); - bot.on_message_create(std::bind(&bot::message_queue::process_guild_message_event, &message_queue, &bot, &settings, std::placeholders::_1)); - bot.on_slashcommand(std::bind(&bot::slashcommands::process_command_event, &bot, &settings, std::placeholders::_1)); - bot.on_ready([&bot, &settings]([[maybe_unused]] const dpp::ready_t &event) { - if (dpp::run_once()) { - bot::slashcommands::register_commands(&bot, &settings); - } - }); - - std::cout << "[Launch] Starting bot..." << std::endl; - bot.start(dpp::st_wait); - - // It's unneccessary, but we choose to exit clean anyway - message_queue.terminate(); - message_queue_loop.join(); - - submit_queue.terminate(); - submit_queue_loop.join(); curl_global_cleanup(); diff --git a/src/core/discord_bot.cpp b/src/core/discord_bot.cpp new file mode 100644 index 0000000..4a213d1 --- /dev/null +++ b/src/core/discord_bot.cpp @@ -0,0 +1,91 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include "discord_bot.h" +#include "slashcommands.h" +using namespace bot; +using namespace std::chrono_literals; + +void discord_bot::run(std::shared_ptr settings, bool background, bool wait_for_translator) { + if (m_running) + terminate(); + for (;;) { + for (const auto &log_callback : m_log_callbacks) + log_callback("Requesting supported languages...", "Launch", false); + if (!settings->get_translator()->get_languages().empty()) + break; + else if (wait_for_translator) + std::this_thread::sleep_for(5000ms); + else + throw std::runtime_error("Failed to initialise translateable languages"); + } + m_settings = settings; + m_bot = std::make_unique(m_settings->token(), dpp::i_default_intents | dpp::i_direct_messages | dpp::i_message_content); + m_bot->on_log([=](const dpp::log_t &event) { + for (const auto &log_callback : m_log_callbacks) + log_callback(event.message, "Log", false); + }); + m_submit_queue_loop = std::make_unique(&bot::submit_queue::run, &m_submit_queue, m_bot.get()); + m_message_queue_loop = std::make_unique(&bot::message_queue::run, &m_message_queue, m_settings.get(), &m_submit_queue); + m_bot->on_message_context_menu(std::bind(&bot::slashcommands::process_message_menu_event, &m_message_queue, m_bot.get(), m_settings.get(), std::placeholders::_1)); + m_bot->on_message_create(std::bind(&bot::message_queue::process_guild_message_event, &m_message_queue, m_bot.get(), m_settings.get(), std::placeholders::_1)); + m_bot->on_slashcommand(std::bind(&bot::slashcommands::process_command_event, m_bot.get(), m_settings.get(), std::placeholders::_1)); + m_bot->on_ready([=]([[maybe_unused]] const dpp::ready_t &event) { + if (dpp::run_once()) + bot::slashcommands::register_commands(m_bot.get(), m_settings.get()); + }); + for (const auto &log_callback : m_log_callbacks) + log_callback("Starting bot...", "Launch", false); + m_bot->start(background ? dpp::st_return : dpp::st_wait); + m_running = true; +} + +void discord_bot::terminate() { + if (!m_running) + return; + if (std::thread *message_queue_loop = m_message_queue_loop.get()) { + m_message_queue.terminate(); + message_queue_loop->join(); + m_message_queue_loop.reset(); + } + if (std::thread *submit_queue_loop = m_submit_queue_loop.get()) { + m_submit_queue.terminate(); + submit_queue_loop->join(); + m_submit_queue_loop.reset(); + } + m_bot.reset(); + m_settings.reset(); + m_running = false; +} + +bool discord_bot::is_running() { + return m_running; +} + +void discord_bot::log_callback_add(const bot::log::log_message_callback &log_callback) { + m_log_callbacks.push_back(log_callback); +} + +message_queue* discord_bot::get_message_queue() { + return &m_message_queue; +} + +submit_queue* discord_bot::get_submit_queue() { + return &m_submit_queue; +} diff --git a/src/core/discord_bot.h b/src/core/discord_bot.h new file mode 100644 index 0000000..7c63575 --- /dev/null +++ b/src/core/discord_bot.h @@ -0,0 +1,56 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#ifndef DISCORD_BOT_H +#define DISCORD_BOT_H + +#include +#include +#include "../core/log.h" +#include "../core/message_queue.h" +#include "../core/settings.h" +#include "../core/submit_queue.h" + +namespace bot { + class discord_bot { + public: + void run(std::shared_ptr settings, bool background, bool wait_for_translator = false); + void terminate(); + void log_callback_add(const bot::log::log_message_callback &log_callback); + bool is_running(); + message_queue* get_message_queue(); + submit_queue* get_submit_queue(); + + /* prevent copies */ + explicit discord_bot() = default; + discord_bot(const discord_bot&) = delete; + discord_bot& operator=(const discord_bot&) = delete; + + private: + bool m_running; + bot::message_queue m_message_queue; + bot::submit_queue m_submit_queue; + std::shared_ptr m_settings; + std::unique_ptr m_bot; + std::unique_ptr m_message_queue_loop; + std::unique_ptr m_submit_queue_loop; + std::vector m_log_callbacks; + }; +} + +#endif // DISCORD_BOT_H diff --git a/src/core/http_request.h b/src/core/http_request.h index 07582e4..044b7fa 100644 --- a/src/core/http_request.h +++ b/src/core/http_request.h @@ -27,7 +27,7 @@ namespace bot { namespace http { class http_request { public: - http_request(); + explicit http_request(); http_request(const http_request&) = delete; http_request& operator=(const http_request&) = delete; ~http_request(); diff --git a/src/core/log.h b/src/core/log.h new file mode 100644 index 0000000..8c05ed7 --- /dev/null +++ b/src/core/log.h @@ -0,0 +1,31 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2023-2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#ifndef LOG_H +#define LOG_H + +#include +#include + +namespace bot { + namespace log { + typedef std::function log_message_callback; + } +} + +#endif // LOG_H diff --git a/src/core/message_queue.cpp b/src/core/message_queue.cpp index e9f38cb..7f7630f 100644 --- a/src/core/message_queue.cpp +++ b/src/core/message_queue.cpp @@ -27,16 +27,20 @@ void message_queue::add(const message &message) { const std::lock_guard guard(m_mutex); m_queue.push(message); +#ifdef DTRANSLATEBOT_GUI for (const message_queue_size_callback &callback : m_callbacks) callback(m_queue.size()); +#endif } void message_queue::add(message &&message) { const std::lock_guard guard(m_mutex); m_queue.push(message); +#ifdef DTRANSLATEBOT_GUI for (const message_queue_size_callback &callback : m_callbacks) callback(m_queue.size()); +#endif } void message_queue::process_direct_message_event(dpp::cluster *bot, bot::settings::settings *settings, const dpp::message_context_menu_t &event) @@ -105,8 +109,10 @@ void message_queue::run(bot::settings::settings *settings, submit_queue *submit_ if (!m_queue.empty()) { const message message = m_queue.front(); m_queue.pop(); +#ifdef DTRANSLATEBOT_GUI for (const message_queue_size_callback &callback : m_callbacks) callback(m_queue.size()); +#endif m_mutex.unlock(); auto translator = settings->get_translator(); @@ -138,24 +144,25 @@ void message_queue::run(bot::settings::settings *settings, submit_queue *submit_ std::this_thread::sleep_for(100ms); } } + std::queue().swap(m_queue); } +#ifdef DTRANSLATEBOT_GUI size_t message_queue::size() { const std::lock_guard guard(m_mutex); return m_queue.size(); } void message_queue::size_callback_add(const message_queue_size_callback &callback) { - const std::lock_guard guard(m_mutex); m_callbacks.push_back(callback); } /* void message_queue::size_callback_remove(const message_queue_size_callback &callback) { - const std::lock_guard guard(m_mutex); m_callbacks.erase(std::remove(m_callbacks.begin(), m_callbacks.end(), callback)); } */ +#endif void message_queue::terminate() { diff --git a/src/core/message_queue.h b/src/core/message_queue.h index 12bbecb..1b4705e 100644 --- a/src/core/message_queue.h +++ b/src/core/message_queue.h @@ -19,7 +19,9 @@ #ifndef MESSAGE_QUEUE_H #define MESSAGE_QUEUE_H #include +#ifdef DTRANSLATEBOT_GUI #include +#endif #include #include #include @@ -43,25 +45,34 @@ namespace bot { std::vector targets; }; +#ifdef DTRANSLATEBOT_GUI typedef std::function message_queue_size_callback; +#endif typedef std::variant message; class message_queue { public: + message_queue() = default; + message_queue(const message_queue&) = delete; + message_queue& operator=(const message_queue&) = delete; void add(const message &message); void add(message &&message); void process_direct_message_event(dpp::cluster *bot, bot::settings::settings *settings, const dpp::message_context_menu_t &event); void process_guild_message_event(dpp::cluster *bot, bot::settings::settings *settings, const dpp::message_create_t &event); void run(bot::settings::settings *settings, submit_queue *submit_queue); +#ifdef DTRANSLATEBOT_GUI size_t size(); void size_callback_add(const message_queue_size_callback &callback); +#endif void terminate(); private: bool m_running; std::mutex m_mutex; std::queue m_queue; +#ifdef DTRANSLATEBOT_GUI std::vector m_callbacks; +#endif }; } diff --git a/src/core/regex.h b/src/core/regex.h index 3f2e3db..a7039ab 100644 --- a/src/core/regex.h +++ b/src/core/regex.h @@ -1,6 +1,6 @@ /***************************************************************************** * dtranslatebot Discord Translate Bot -* Copyright (C) 2023-2024 Syping +* Copyright (C) 2023-2026 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -24,20 +24,30 @@ #else #include #endif +#include #include +#define DTRANSLATEBOT_DISCORD_BOT_TOKEN_REGEX "^([\\w-]{24,})\\.([\\w-]{6,})\\.([\\w-]{27,})$" + namespace bot { + namespace regex { #ifdef DTRANSLATEBOT_USE_BOOST_REGEX - using boost::regex; - using boost::regex_match; - using boost::match_results; - typedef boost::match_results svmatch; + using boost::regex; + using boost::regex_match; + using boost::match_results; + typedef boost::match_results smatch; + typedef boost::match_results svmatch; #else - using std::regex; - using std::regex_match; - using std::match_results; - typedef std::match_results svmatch; + using std::regex; + using std::regex_match; + using std::match_results; + typedef std::match_results smatch; + typedef std::match_results svmatch; #endif + inline bool verify_discord_bot_token(const std::string &token) { + return regex_match(token, regex(DTRANSLATEBOT_DISCORD_BOT_TOKEN_REGEX)); + } + } } #endif // REGEX_H diff --git a/src/core/settings.cpp b/src/core/settings.cpp index 5ad86b6..439bfdd 100644 --- a/src/core/settings.cpp +++ b/src/core/settings.cpp @@ -68,7 +68,6 @@ void process_database_channels(std::shared_ptr database void process_database(std::shared_ptr database, std::vector &guilds, std::vector &users, std::vector &webhookIds) { - std::cout << "[Launch] Loading database..." << std::endl; const std::vector db_guilds = database->get_guilds(); for (auto db_guild_id = db_guilds.begin(); db_guild_id != db_guilds.end(); db_guild_id++) { bool guild_found = false; @@ -162,11 +161,11 @@ void process_guild_settings(const dpp::json &json, std::vector &guilds, s } } -void process_preflang_settings(const dpp::json &json, std::vector *preferred_langs) +void process_preflang_settings(const dpp::json &json, std::vector *preferred_langs, const bot::log::log_message_callback &log_callback) { for (auto json_preferred_lang = json.begin(); json_preferred_lang != json.end(); json_preferred_lang++) { if (std::distance(json.begin(), json_preferred_lang) >= 25) { - std::cerr << "[Error] Value preferred_lang is limited to 25 languages" << std::endl; + log_callback("Value preferred_lang is limited to 25 languages", "Error", true); break; } preferred_langs->push_back(*json_preferred_lang); @@ -217,7 +216,7 @@ void process_server_url(const std::string &url, translator &translator) } } -bool process_server(const dpp::json &json, translator &translator) +bool process_server(const dpp::json &json, translator &translator, const bot::log::log_message_callback &log_callback) { auto json_hostname = json.find("hostname"); if (json_hostname != json.end()) @@ -237,7 +236,7 @@ bool process_server(const dpp::json &json, translator &translator) auto json_url = json.find("url"); if (json_url == json.end()) { - std::cerr << "[Error] Value url not found in translator object" << std::endl; + log_callback("Value url not found in translator object", "Error", true); return false; } if (translator.hostname.empty()) @@ -264,10 +263,10 @@ void process_user_settings(const dpp::json &json, uint16_t &avatar_size) } } -bool process_translator_settings(const dpp::json &json, std::shared_ptr &translator_instance) +bool process_translator_settings(const dpp::json &json, std::shared_ptr &translator_instance, const bot::log::log_message_callback &log_callback) { if (!json.is_object()) { - std::cerr << "[Error] Value translator needs to be a object" << std::endl; + log_callback("Value translator needs to be a object", "Error", true); return false; } @@ -290,15 +289,15 @@ bool process_translator_settings(const dpp::json &json, std::shared_ptr(translator.hostname, translator.apiKey); + translator_instance = std::make_shared(translator.hostname, translator.apiKey, log_callback); } else if (translator.type == "mozhi") { - if (!process_server(json, translator)) + if (!process_server(json, translator, log_callback)) return false; std::string mozhi_engine; @@ -308,25 +307,25 @@ bool process_translator_settings(const dpp::json &json, std::shared_ptr(translator.hostname, translator.port, translator.url, translator.tls, mozhi_engine); + translator_instance = std::make_shared(translator.hostname, translator.port, translator.url, translator.tls, mozhi_engine, log_callback); } else if (translator.type == "libretranslate") { - if (!process_server(json, translator)) + if (!process_server(json, translator, log_callback)) return false; - translator_instance = std::make_shared(translator.hostname, translator.port, translator.url, translator.tls, translator.apiKey); + translator_instance = std::make_shared(translator.hostname, translator.port, translator.url, translator.tls, translator.apiKey, log_callback); } else if (translator.type == "lingvatranslate") { - if (!process_server(json, translator)) + if (!process_server(json, translator, log_callback)) return false; - translator_instance = std::make_shared(translator.hostname, translator.port, translator.url, translator.tls); + translator_instance = std::make_shared(translator.hostname, translator.port, translator.url, translator.tls, log_callback); } else if (translator.type == "stub") { translator_instance = std::make_shared(); } else { - std::cerr << "[Error] Translator " << translator.type << " is unknown" << std::endl; + log_callback("Translator " + translator.type + " is unknown", "Error", true); return false; } @@ -559,19 +558,36 @@ void settings::lock() m_externallyLockedCount++; } -bool settings::parse(const std::string &data, bool initialize) +bool settings::parse(const std::string &data, const bot::log::log_message_callback &log_callback, bool initialize) { + dpp::json json; try { - dpp::json json; - try { - json = dpp::json::parse(data, nullptr, true, true); - } - catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; - std::cerr << "[Error] Exception while parsing JSON" << std::endl; - return false; - } + json = dpp::json::parse(data, nullptr, true, true); + } + catch (const std::exception &exception) { + log_callback(exception.what(), "Exception", true); + log_callback("Exception while parsing JSON", "Error", true); + return false; + } + return process(json, log_callback, initialize); +} +bool settings::parse_file(const std::string &filename, const bot::log::log_message_callback &log_callback, bool initialize) +{ + std::ifstream ifs(filename, std::ios::in | std::ios::binary); + if (!ifs.is_open()) { + log_callback("Failed to open JSON configuration file: " + filename, "Error", true); + return false; + } + + std::string sdata(std::istreambuf_iterator{ifs}, {}); + ifs.close(); + + return parse(sdata, log_callback, initialize); +} + +bool settings::process(const dpp::json &json, const bot::log::log_message_callback &log_callback, bool initialize) { + try { const std::lock_guard guard(m_mutex); auto json_token = json.find("token"); if (json_token != json.end()) @@ -580,7 +596,7 @@ bool settings::parse(const std::string &data, bool initialize) m_token = token; if (m_token.empty()) { - std::cerr << "[Error] Discord Bot Token is not configured" << std::endl; + log_callback("Discord Bot Token is not configured", "Error", true); return false; } @@ -594,14 +610,14 @@ bool settings::parse(const std::string &data, bool initialize) if (storage_path.empty()) storage_path = std::filesystem::current_path(); - m_database = std::make_shared(storage_path); + m_database = std::make_shared(storage_path, log_callback); auto json_translator = json.find("translator"); if (json_translator == json.end()) { - std::cerr << "[Error] Value translator not found" << std::endl; + log_callback("Value translator not found", "Error", true); return false; } - if (!process_translator_settings(*json_translator, m_translator)) + if (!process_translator_settings(*json_translator, m_translator, log_callback)) return false; auto json_guilds = json.find("guilds"); @@ -610,36 +626,23 @@ bool settings::parse(const std::string &data, bool initialize) auto json_preflangs = json.find("preferred_lang"); if (json_preflangs != json.end() && json_preflangs->is_array()) - process_preflang_settings(*json_preflangs, &m_prefLangs); + process_preflang_settings(*json_preflangs, &m_prefLangs, log_callback); auto json_user = json.find("user"); if (json_user != json.end() && json_user->is_object()) process_user_settings(*json_user, m_avatarSize); + log_callback("Loading database...", "Launch", false); process_database(m_database, m_guilds, m_users, m_webhookIds); return true; } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + log_callback(exception.what(), "Exception", true); } return false; } -bool settings::parse_file(const std::string &filename, bool initialize) -{ - std::ifstream ifs(filename, std::ios::in | std::ios::binary); - if (!ifs.is_open()) { - std::cerr << "[Error] Failed to open JSON configuration file located at " << filename << std::endl; - return false; - } - - std::string sdata(std::istreambuf_iterator{ifs}, {}); - ifs.close(); - - return parse(sdata, initialize); -} - void settings::unlock() { m_mutex.unlock(); diff --git a/src/core/settings.h b/src/core/settings.h index 80dc3ad..5ad9fcb 100644 --- a/src/core/settings.h +++ b/src/core/settings.h @@ -1,6 +1,6 @@ /***************************************************************************** * dtranslatebot Discord Translate Bot -* Copyright (C) 2023-2024 Syping +* Copyright (C) 2023-2026 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -20,6 +20,7 @@ #define SETTINGS_H #include #include "database.h" +#include "log.h" #include "settings_types.h" #include "translator.h" @@ -62,8 +63,11 @@ namespace bot { void unlock(); /* parse functions */ - bool parse(const std::string &data, bool initialize = true); - bool parse_file(const std::string &filename, bool initialize = true); + bool parse(const std::string &data, const bot::log::log_message_callback &log_callback, bool initialize = true); + bool parse_file(const std::string &filename, const bot::log::log_message_callback &log_callback, bool initialize = true); + + /* process functions */ + bool process(const dpp::json &json, const bot::log::log_message_callback &log_callback, bool initialize = true); /* prevent copies */ settings() = default; diff --git a/src/core/submit_queue.cpp b/src/core/submit_queue.cpp index 3fe5057..1020dea 100644 --- a/src/core/submit_queue.cpp +++ b/src/core/submit_queue.cpp @@ -26,16 +26,20 @@ void submit_queue::add(const translated_message &message) { const std::lock_guard guard(m_mutex); m_queue.push(message); +#ifdef DTRANSLATEBOT_GUI for (const submit_queue_size_callback &callback : m_callbacks) callback(m_queue.size()); +#endif } void submit_queue::add(translated_message &&message) { const std::lock_guard guard(m_mutex); m_queue.push(message); +#ifdef DTRANSLATEBOT_GUI for (const submit_queue_size_callback &callback : m_callbacks) callback(m_queue.size()); +#endif } void submit_queue::run(dpp::cluster *bot) @@ -46,8 +50,10 @@ void submit_queue::run(dpp::cluster *bot) if (!m_queue.empty()) { const translated_message message = m_queue.front(); m_queue.pop(); +#ifdef DTRANSLATEBOT_GUI for (const submit_queue_size_callback &callback : m_callbacks) callback(m_queue.size()); +#endif m_mutex.unlock(); if (const auto *direct_message = std::get_if(&message)) { @@ -64,24 +70,25 @@ void submit_queue::run(dpp::cluster *bot) std::this_thread::sleep_for(100ms); } } + std::queue().swap(m_queue); } +#ifdef DTRANSLATEBOT_GUI size_t submit_queue::size() { const std::lock_guard guard(m_mutex); return m_queue.size(); } void submit_queue::size_callback_add(const submit_queue_size_callback &callback) { - const std::lock_guard guard(m_mutex); m_callbacks.push_back(callback); } /* void submit_queue::size_callback_remove(const submit_queue_size_callback &callback) { - const std::lock_guard guard(m_mutex); m_callbacks.erase(std::remove(m_callbacks.begin(), m_callbacks.end(), callback)); } */ +#endif void submit_queue::terminate() { diff --git a/src/core/submit_queue.h b/src/core/submit_queue.h index 3a1b156..b42b447 100644 --- a/src/core/submit_queue.h +++ b/src/core/submit_queue.h @@ -20,10 +20,15 @@ #define SUBMIT_QUEUE_H #include #include +#ifdef DTRANSLATEBOT_GUI +#include +#endif #include #include #include +#ifdef DTRANSLATEBOT_GUI #include +#endif namespace bot { struct translated_direct_message { @@ -38,23 +43,32 @@ namespace bot { dpp::webhook webhook; }; +#ifdef DTRANSLATEBOT_GUI typedef std::function submit_queue_size_callback; +#endif typedef std::variant translated_message; class submit_queue { public: + submit_queue() = default; + submit_queue(const submit_queue&) = delete; + submit_queue& operator=(const submit_queue&) = delete; void add(const translated_message &message); void add(translated_message &&message); void run(dpp::cluster *bot); +#ifdef DTRANSLATEBOT_GUI size_t size(); void size_callback_add(const submit_queue_size_callback &callback); +#endif void terminate(); private: bool m_running; std::mutex m_mutex; std::queue m_queue; +#ifdef DTRANSLATEBOT_GUI std::vector m_callbacks; +#endif }; } diff --git a/src/core/webhook_push.cpp b/src/core/webhook_push.cpp index 3f80881..269b139 100644 --- a/src/core/webhook_push.cpp +++ b/src/core/webhook_push.cpp @@ -42,16 +42,16 @@ void bot::webhook_push::run(const bot::translated_guild_message &message, dpp::c message_v = message_v.substr(1333 + pos); } else { - bot::svmatch match; - if (bot::regex_match(message_eov.begin(), message_eov.end(), match, bot::regex("^.*(\\.|\\?|\\!|\\。)\\s.*$"))) { + bot::regex::svmatch match; + if (bot::regex::regex_match(message_eov.begin(), message_eov.end(), match, bot::regex::regex("^.*(\\.|\\?|\\!|\\。)\\s.*$"))) { json_body["content"] = message_v.substr(0, 1334 + match.position(1)); message_v = message_v.substr(1334 + match.position(1)); } - else if (bot::regex_match(message_eov.begin(), message_eov.end(), match, bot::regex("^.*(\\,)\\s.*$"))) { + else if (bot::regex::regex_match(message_eov.begin(), message_eov.end(), match, bot::regex::regex("^.*(\\,)\\s.*$"))) { json_body["content"] = message_v.substr(0, 1334 + match.position(1)); message_v = message_v.substr(1334 + match.position(1)); } - else if (bot::regex_match(message_eov.begin(), message_eov.end(), match, bot::regex("^.*()\\s.*$"))) { + else if (bot::regex::regex_match(message_eov.begin(), message_eov.end(), match, bot::regex::regex("^.*()\\s.*$"))) { json_body["content"] = message_v.substr(0, 1334 + match.position(1)); message_v = message_v.substr(1334 + match.position(1)); } diff --git a/src/database/file/file.cpp b/src/database/file/file.cpp index 5097426..bc012a5 100644 --- a/src/database/file/file.cpp +++ b/src/database/file/file.cpp @@ -31,11 +31,11 @@ using namespace bot::database; using namespace std::string_literals; -file::file(const std::filesystem::path &storage_path) : m_storagePath(storage_path) +file::file(const std::filesystem::path &storage_path, const log::log_message_callback &log_callback) : m_storagePath(storage_path), m_logCallback(log_callback) { - std::cout << "[Launch] Checking storage directory..." << std::endl; + m_logCallback("Checking storage directory...", "Launch", false); if (!std::filesystem::is_directory(storage_path)) { - std::cerr << "[Error] Storage directory " << storage_path << " can not be found" << std::endl; + m_logCallback("[Error] Storage directory " + storage_path.string() + " can not be found", "Error", true); throw std::runtime_error("Storage directory can not be found"); } @@ -50,19 +50,19 @@ file::file(const std::filesystem::path &storage_path) : m_storagePath(storage_pa const std::filesystem::path lock_file = storage_path / ".lock"; fd = open(lock_file.c_str(), O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd == -1) { - std::cerr << "[Error] Storage directory " << storage_path << " can not be locked" << std::endl; + m_logCallback("[Error] Storage directory " + storage_path.string() + " can not be locked", "Error", true); throw std::system_error(errno, std::system_category()); } if (fcntl(fd, F_SETLK, &lock) == -1) { close(fd); - std::cerr << "[Error] Storage directory " << storage_path << " can not be locked" << std::endl; + m_logCallback("[Error] Storage directory " + storage_path.string() + " can not be locked", "Error", true); throw std::system_error(errno, std::system_category()); } #elif defined(_WIN32) const std::filesystem::path lock_file = storage_path / ".lock"; fh = CreateFileW(lock_file.c_str(), GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_HIDDEN, NULL); if (fh == INVALID_HANDLE_VALUE) { - std::cerr << "[Error] Storage directory " << storage_path << " can not be locked" << std::endl; + m_logCallback("[Error] Storage directory " + storage_path.string() + " can not be found", "Error", true); throw std::system_error(GetLastError(), std::system_category()); } #endif @@ -408,7 +408,7 @@ bool file::sync() return true; } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } return false; @@ -475,7 +475,7 @@ void file::cache_get_channel(dpp::snowflake channel_id, settings::channel &chann } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } } @@ -504,7 +504,7 @@ void file::cache_get_user(dpp::snowflake user_id, settings::user &user) } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } } @@ -534,7 +534,7 @@ void file::cache_guild(dpp::snowflake guild_id, std::vector &cha } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } } @@ -555,7 +555,7 @@ void file::list_guilds(std::vector &guilds) guilds.push_back(guild_id); } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } } } @@ -580,7 +580,7 @@ void file::list_users(std::vector &users) users.push_back(user_id); } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } } } @@ -627,7 +627,7 @@ void file::sync_cache() } } else { - std::cerr << "[Error] Storage channel directory can not be created" << std::endl; + m_logCallback("Storage channel directory can not be created", "Error", true); } const std::filesystem::path guild_dir = m_storagePath / "guild"; @@ -657,7 +657,7 @@ void file::sync_cache() } } else { - std::cerr << "[Error] Storage guild directory can not be created" << std::endl; + m_logCallback("Storage guild directory can not be created", "Error", true); } const std::filesystem::path user_dir = m_storagePath / "user"; @@ -687,6 +687,6 @@ void file::sync_cache() } } else { - std::cerr << "[Error] Storage user directory can not be created" << std::endl; + m_logCallback("Storage user directory can not be created", "Error", true); } } diff --git a/src/database/file/file.h b/src/database/file/file.h index daceefd..48e3a62 100644 --- a/src/database/file/file.h +++ b/src/database/file/file.h @@ -27,12 +27,13 @@ #include #endif #include "../../core/database.h" +#include "../../core/log.h" namespace bot { namespace database { class file : public database { public: - explicit file(const std::filesystem::path &storage_path); + explicit file(const std::filesystem::path &storage_path, const bot::log::log_message_callback &log_callback); ~file(); void add_channel_target(dpp::snowflake guild_id, dpp::snowflake channel_id, const bot::settings::target &target) override; void delete_channel(dpp::snowflake guild_id, dpp::snowflake channel_id) override; @@ -72,6 +73,7 @@ namespace bot { std::vector m_userCache; std::mutex m_mutex; std::filesystem::path m_storagePath; + bot::log::log_message_callback m_logCallback; }; } } diff --git a/src/gui/main.cpp b/src/gui/main.cpp new file mode 100644 index 0000000..aba8907 --- /dev/null +++ b/src/gui/main.cpp @@ -0,0 +1,31 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include +#include "user_interface.h" +using namespace bot::gui; + +int main(int argc, char *argv[]) +{ + CURLcode result = curl_global_init(CURL_GLOBAL_DEFAULT); + if (result != CURLE_OK) + return -1; + auto app = Gtk::Application::create("de.syping.dtranslatebot"); + return app->make_window_and_run(argc, argv); +} diff --git a/src/gui/user_interface.cpp b/src/gui/user_interface.cpp new file mode 100644 index 0000000..644502e --- /dev/null +++ b/src/gui/user_interface.cpp @@ -0,0 +1,127 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include "../core/regex.h" +#include "user_interface.h" +using namespace bot::gui; + +user_interface::user_interface() +{ + set_title("dtranslatebot"); + set_default_size(500, 0); + set_resizable(false); + + auto vertical_box = Gtk::make_managed(Gtk::Orientation::VERTICAL); + vertical_box->set_margin(6); + vertical_box->set_spacing(6); + + auto token_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); + token_box->set_spacing(6); + vertical_box->append(*token_box); + + auto token_label = Gtk::make_managed("Discord Bot Token:"); + token_box->append(*token_label); + + m_token_entry = Gtk::make_managed(); + m_token_entry->set_show_peek_icon(true); + m_token_entry->set_hexpand(true); + token_box->append(*m_token_entry); + + auto log_textview = Gtk::make_managed(); + log_textview->set_size_request(-1, 300); + log_textview->set_editable(false); + log_textview->set_monospace(true); + log_textview->set_wrap_mode(Gtk::WrapMode::WORD_CHAR); + m_log = log_textview->get_buffer(); + vertical_box->append(*log_textview); + + auto button_box = Gtk::make_managed(Gtk::Orientation::HORIZONTAL); + button_box->set_spacing(6); + button_box->set_halign(Gtk::Align::CENTER); + vertical_box->append(*button_box); + + m_start_button = Gtk::make_managed("Start"); + m_start_button->set_size_request(80, -1); + m_start_button->set_sensitive(false); + m_start_button->signal_clicked().connect(sigc::mem_fun(*this, &user_interface::run)); + button_box->append(*m_start_button); + + m_stop_button = Gtk::make_managed("Stop"); + m_stop_button->set_size_request(80, -1); + m_stop_button->set_sensitive(false); + m_stop_button->signal_clicked().connect(sigc::mem_fun(*this, &user_interface::terminate)); + button_box->append(*m_stop_button); + + m_token_entry->signal_changed().connect([=]{ + bool token_valid = bot::regex::verify_discord_bot_token(m_token_entry->get_text()); + m_start_button->set_sensitive(!m_bot.is_running() ? token_valid : false); + }); + + set_child(*vertical_box); + + m_log_callback = std::bind(&user_interface::log_append, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3); + m_bot.log_callback_add(m_log_callback); + m_log_dispatcher.connect([=](){ + const std::lock_guard guard(m_log_buffer_mutex); + m_log->insert(m_log->end(), m_log->begin() != m_log->end() ? m_log_buffer : m_log_buffer.substr(1)); + m_log_buffer.clear(); + }); +} + +void user_interface::log_append(const std::string &message, const std::string &type, bool is_error) { + const std::lock_guard guard(m_log_buffer_mutex); + m_log_buffer.append("\n[" + type + "] " + message); + m_log_dispatcher.emit(); +} + +void user_interface::run() { + auto settings = std::make_shared(); + if (settings->process({{"token", m_token_entry->get_text()}, {"translator", {{"type", "stub"}}}}, m_log_callback)) { + try { + m_bot.run(settings, true, false); + m_start_button->set_sensitive(false); + m_stop_button->set_sensitive(true); + } + catch (const std::exception &exception) { + auto alert_dialog = Gtk::AlertDialog::create(exception.what()); + alert_dialog->set_modal(true); + alert_dialog->show(*this); + } + } + else { + auto alert_dialog = Gtk::AlertDialog::create("Failed to process settings"); + alert_dialog->set_modal(true); + alert_dialog->show(*this); + } +} + +void user_interface::terminate() { + log_append("Stopping bot...", "Launch"); + m_bot.terminate(); + bool token_valid = bot::regex::verify_discord_bot_token(m_token_entry->get_text()); + m_start_button->set_sensitive(token_valid); + m_stop_button->set_sensitive(false); +} diff --git a/src/gui/user_interface.h b/src/gui/user_interface.h new file mode 100644 index 0000000..4858997 --- /dev/null +++ b/src/gui/user_interface.h @@ -0,0 +1,48 @@ +/***************************************************************************** +* dtranslatebot Discord Translate Bot +* Copyright (C) 2026 Syping +* +* Redistribution and use in source and binary forms, with or without modification, +* are permitted provided that the following conditions are met: +* +* 1. Redistributions of source code must retain the above copyright notice, +* this list of conditions and the following disclaimer. +* +* 2. Redistributions in binary form must reproduce the above copyright notice, +* this list of conditions and the following disclaimer in the documentation +* and/or other materials provided with the distribution. +* +* This software is provided as-is, no warranties are given to you, we are not +* responsible for anything with use of the software, you are self responsible. +*****************************************************************************/ + +#include +#include +#include +#include +#include +#include +#include "../core/discord_bot.h" + +namespace bot { + namespace gui { + class user_interface : public Gtk::Window { + public: + explicit user_interface(); + void log_append(const std::string &message, const std::string &type = "Log", bool is_error = false); + void run(); + void terminate(); + + private: + bot::discord_bot m_bot; + Glib::RefPtr m_log; + Gtk::Button* m_start_button; + Gtk::Button* m_stop_button; + Gtk::PasswordEntry* m_token_entry; + std::string m_log_buffer; + Glib::Dispatcher m_log_dispatcher; + std::mutex m_log_buffer_mutex; + bot::log::log_message_callback m_log_callback; + }; + } +} diff --git a/src/translator/deepl/deepl.cpp b/src/translator/deepl/deepl.cpp index a8b8047..e1670f7 100644 --- a/src/translator/deepl/deepl.cpp +++ b/src/translator/deepl/deepl.cpp @@ -17,14 +17,13 @@ *****************************************************************************/ #include -#include #include "deepl.h" using namespace bot::http; using namespace bot::translator; using namespace std::chrono_literals; -deepl::deepl(const std::string &hostname, const std::string apiKey) : - m_hostname(hostname), m_apiKey(apiKey) +deepl::deepl(const std::string &hostname, const std::string &api_key, const bot::log::log_message_callback &log_callback) : + m_hostname(hostname), m_apiKey(api_key), m_logCallback(log_callback) { } @@ -97,7 +96,7 @@ const std::vector deepl::get_languages() } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } return m_languages.languages; @@ -130,7 +129,7 @@ const std::string deepl::translate(const std::string &text, const std::string &s } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } return text; diff --git a/src/translator/deepl/deepl.h b/src/translator/deepl/deepl.h index 0e07aa6..5189398 100644 --- a/src/translator/deepl/deepl.h +++ b/src/translator/deepl/deepl.h @@ -20,13 +20,14 @@ #define TRANSLATOR_DEEPL_H #include "../../core/http_request.h" +#include "../../core/log.h" #include "../../core/translator.h" namespace bot { namespace translator { class deepl : public translator { public: - explicit deepl(const std::string &hostname, const std::string apiKey = {}); + explicit deepl(const std::string &hostname, const std::string &api_key, const bot::log::log_message_callback &log_callback); ~deepl() override; const std::vector get_languages() override; const std::string translate(const std::string &text, const std::string &source, const std::string &target) override; @@ -36,6 +37,7 @@ namespace bot { std::string m_hostname; bot::http::http_request m_http; supported_languages m_languages; + bot::log::log_message_callback m_logCallback; }; } } diff --git a/src/translator/libretranslate/libretranslate.cpp b/src/translator/libretranslate/libretranslate.cpp index 8cc6926..309af95 100644 --- a/src/translator/libretranslate/libretranslate.cpp +++ b/src/translator/libretranslate/libretranslate.cpp @@ -17,14 +17,13 @@ *****************************************************************************/ #include -#include #include "libretranslate.h" using namespace bot::http; using namespace bot::translator; using namespace std::chrono_literals; -libretranslate::libretranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string apiKey) : - m_hostname(hostname), m_port(port), m_url(url), m_tls(tls), m_apiKey(apiKey) +libretranslate::libretranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string &api_key, const bot::log::log_message_callback &log_callback) : + m_hostname(hostname), m_port(port), m_url(url), m_tls(tls), m_apiKey(api_key), m_logCallback(log_callback) { } @@ -68,7 +67,7 @@ const std::vector libretranslate::get_languages() } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } return m_languages.languages; @@ -99,7 +98,7 @@ const std::string libretranslate::translate(const std::string &text, const std:: } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } return text; diff --git a/src/translator/libretranslate/libretranslate.h b/src/translator/libretranslate/libretranslate.h index d0237ec..b694f3f 100644 --- a/src/translator/libretranslate/libretranslate.h +++ b/src/translator/libretranslate/libretranslate.h @@ -1,6 +1,6 @@ /***************************************************************************** * dtranslatebot Discord Translate Bot -* Copyright (C) 2023-2024 Syping +* Copyright (C) 2023-2026 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -21,6 +21,7 @@ #include #include "../../core/http_request.h" +#include "../../core/log.h" #include "../../core/translator.h" namespace bot { @@ -28,7 +29,7 @@ namespace bot { class libretranslate : public translator { public: - explicit libretranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string apiKey = {}); + explicit libretranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string &api_key, const bot::log::log_message_callback &log_callback); ~libretranslate() override; const std::vector get_languages() override; const std::string translate(const std::string &text, const std::string &source, const std::string &target) override; @@ -41,6 +42,7 @@ namespace bot { uint16_t m_port; std::string m_url; bool m_tls; + bot::log::log_message_callback m_logCallback; }; } } diff --git a/src/translator/lingvatranslate/lingvatranslate.cpp b/src/translator/lingvatranslate/lingvatranslate.cpp index e9eaad1..e0bbf72 100644 --- a/src/translator/lingvatranslate/lingvatranslate.cpp +++ b/src/translator/lingvatranslate/lingvatranslate.cpp @@ -18,14 +18,13 @@ #include #include -#include #include "lingvatranslate.h" using namespace bot::http; using namespace bot::translator; using namespace std::chrono_literals; -lingvatranslate::lingvatranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls) : - m_hostname(hostname), m_port(port), m_url(url), m_tls(tls) +lingvatranslate::lingvatranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const bot::log::log_message_callback &log_callback) : + m_hostname(hostname), m_port(port), m_url(url), m_tls(tls), m_logCallback(log_callback) { } @@ -72,7 +71,7 @@ const std::vector lingvatranslate::get_languages() } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } return m_languages.languages; @@ -92,7 +91,7 @@ const std::string lingvatranslate::translate(const std::string &text, const std: } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } return text; diff --git a/src/translator/lingvatranslate/lingvatranslate.h b/src/translator/lingvatranslate/lingvatranslate.h index c18b67b..02742da 100644 --- a/src/translator/lingvatranslate/lingvatranslate.h +++ b/src/translator/lingvatranslate/lingvatranslate.h @@ -1,6 +1,6 @@ /***************************************************************************** * dtranslatebot Discord Translate Bot -* Copyright (C) 2024 Syping +* Copyright (C) 2024-2026 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -21,6 +21,7 @@ #include #include "../../core/http_request.h" +#include "../../core/log.h" #include "../../core/translator.h" namespace bot { @@ -28,7 +29,7 @@ namespace bot { class lingvatranslate : public translator { public: - explicit lingvatranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls); + explicit lingvatranslate(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const bot::log::log_message_callback &log_callback); ~lingvatranslate() override; const std::vector get_languages() override; const std::string translate(const std::string &text, const std::string &source, const std::string &target) override; @@ -40,6 +41,7 @@ namespace bot { uint16_t m_port; std::string m_url; bool m_tls; + bot::log::log_message_callback m_logCallback; }; } } diff --git a/src/translator/mozhi/mozhi.cpp b/src/translator/mozhi/mozhi.cpp index 5231113..74edb09 100644 --- a/src/translator/mozhi/mozhi.cpp +++ b/src/translator/mozhi/mozhi.cpp @@ -18,14 +18,13 @@ #include #include -#include #include "mozhi.h" using namespace bot::http; using namespace bot::translator; using namespace std::chrono_literals; -mozhi::mozhi(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string &engine) : - m_hostname(hostname), m_port(port), m_url(url), m_tls(tls), m_engine(engine) +mozhi::mozhi(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string &engine, const bot::log::log_message_callback &log_callback) : + m_hostname(hostname), m_port(port), m_url(url), m_tls(tls), m_engine(engine), m_logCallback(log_callback) { } @@ -72,7 +71,7 @@ const std::vector mozhi::get_languages() } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } return m_languages.languages; @@ -98,7 +97,7 @@ const std::string mozhi::translate(const std::string &text, const std::string &s } } catch (const std::exception &exception) { - std::cerr << "[Exception] " << exception.what() << std::endl; + m_logCallback(exception.what(), "Exception", true); } return text; diff --git a/src/translator/mozhi/mozhi.h b/src/translator/mozhi/mozhi.h index fb40aa4..9171e28 100644 --- a/src/translator/mozhi/mozhi.h +++ b/src/translator/mozhi/mozhi.h @@ -1,6 +1,6 @@ /***************************************************************************** * dtranslatebot Discord Translate Bot -* Copyright (C) 2024 Syping +* Copyright (C) 2024-2026 Syping * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: @@ -21,6 +21,7 @@ #include #include "../../core/http_request.h" +#include "../../core/log.h" #include "../../core/translator.h" namespace bot { @@ -28,7 +29,7 @@ namespace bot { class mozhi : public translator { public: - explicit mozhi(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string &engine); + explicit mozhi(const std::string &hostname, uint16_t port, const std::string &url, bool tls, const std::string &engine, const bot::log::log_message_callback &log_callback); ~mozhi() override; const std::vector get_languages() override; const std::string translate(const std::string &text, const std::string &source, const std::string &target) override; @@ -41,6 +42,7 @@ namespace bot { uint16_t m_port; std::string m_url; bool m_tls; + bot::log::log_message_callback m_logCallback; }; } }