From ee5ade56d7a246c97a07bcc0f877f2b857b4134a Mon Sep 17 00:00:00 2001 From: ZavaruKitsu Date: Mon, 10 Jul 2023 14:13:20 +0000 Subject: [PATCH] feat: progress AyuSync implementation --- Telegram/SourceFiles/ayu/libs/pipe.hpp | 4 +- .../ayu/sync/ayu_sync_controller.cpp | 82 +++++++++++++++-- .../ayu/sync/ayu_sync_controller.h | 8 +- Telegram/SourceFiles/ayu/sync/models.h | 28 ++++-- .../ayu/sync/utils/ayu_pipe_wrapper.cpp | 35 ++++---- .../ayu/sync/utils/ayu_pipe_wrapper.h | 9 +- .../ayu/sync/utils/process_utils.hpp | 88 ++++++++++--------- .../ayu/sync/utils/telegram_helpers.cpp | 18 ++++ .../ayu/sync/utils/telegram_helpers.h | 1 + .../ayu/ui/settings/settings_ayu.cpp | 2 +- Telegram/SourceFiles/data/data_histories.cpp | 4 + 11 files changed, 199 insertions(+), 80 deletions(-) diff --git a/Telegram/SourceFiles/ayu/libs/pipe.hpp b/Telegram/SourceFiles/ayu/libs/pipe.hpp index 4f4a0b981..f2e4e4b90 100644 --- a/Telegram/SourceFiles/ayu/libs/pipe.hpp +++ b/Telegram/SourceFiles/ayu/libs/pipe.hpp @@ -85,7 +85,7 @@ public: using off_type = typename Traits::off_type; public: - static constexpr std::size_t buf_size{1024}; + static constexpr std::size_t buf_size{4096}; public: basic_pipe_streambuf() = default; @@ -138,7 +138,7 @@ public: { native_mode = mode & std::ios_base::in ? PIPE_ACCESS_INBOUND : PIPE_ACCESS_OUTBOUND; - handle = CreateNamedPipeW(std::data(native_name), native_mode, PIPE_READMODE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, buf_size, buf_size, 0, nullptr); + handle = CreateNamedPipeW(std::data(native_name), native_mode, PIPE_TYPE_BYTE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, buf_size, buf_size, 0, nullptr); if(handle == INVALID_HANDLE_VALUE) return false; diff --git a/Telegram/SourceFiles/ayu/sync/ayu_sync_controller.cpp b/Telegram/SourceFiles/ayu/sync/ayu_sync_controller.cpp index 930233547..082c2f8f9 100644 --- a/Telegram/SourceFiles/ayu/sync/ayu_sync_controller.cpp +++ b/Telegram/SourceFiles/ayu/sync/ayu_sync_controller.cpp @@ -17,7 +17,10 @@ #include #include -#include "core/sandbox.h" +#include "data/data_histories.h" + +#include "history/history_item.h" +#include "history/view/history_view_element.h" namespace AyuSync { @@ -30,7 +33,7 @@ namespace AyuSync bool isAgentRunning() { - return is_process_running(AgentFilename); + return isProcessRunning(AgentFilename); } void initialize() @@ -43,7 +46,7 @@ namespace AyuSync controller = ayu_sync_controller(); } - ayu_sync_controller& getControllerInstance() + ayu_sync_controller& getInstance() { initialize(); return controller.value(); @@ -56,20 +59,44 @@ namespace AyuSync return; } - if (!isAgentRunning()) + if (isAgentRunning()) { - auto configPath = std::filesystem::absolute("./tdata/sync_preferences.json"); - auto process = nes::process{AgentPath, {configPath.string(), ""}, nes::process_options::none}; - process.detach(); + killProcess(AgentFilename); } + auto configPath = std::filesystem::absolute("./tdata/sync_preferences.json"); + auto process = nes::process{AgentPath, {configPath.string(), ""}, nes::process_options::none}; + process.detach(); + std::thread receiverThread(&ayu_sync_controller::receiver, this); receiverThread.detach(); + + initialized = true; + } + + void ayu_sync_controller::syncRead(not_null history, MsgId untilId) + { + if (!initialized) + { + return; + } + + SyncRead ev; + ev.userId = history->owner().session().userId().bare; + + ev.args.dialogId = getDialogIdFromPeer(history->peer); + ev.args.untilId = untilId.bare; + ev.args.unread = history->unreadCount(); + + pipe->send(ev); } void ayu_sync_controller::receiver() { pipe = std::make_unique(); + pipe->connect(); + + LOG(("Pipe created")); while (true) { @@ -123,6 +150,47 @@ namespace AyuSync void ayu_sync_controller::onSyncForce(SyncForce ev) { + auto session = getSession(ev.userId); + auto histories = session->data().chatsList(); + + SyncBatch readsBatchEvent; + readsBatchEvent.userId = ev.userId; + + for (const auto& row : histories->indexed()->all()) + { + if (const auto history = row->history()) + { + auto dialogId = getDialogIdFromPeer(history->peer); + + SyncRead readEv; + readEv.userId = ev.userId; + + history->calculateFirstUnreadMessage(); + auto unreadElement = history->firstUnreadMessage(); + + if (!unreadElement && history->unreadCount()) + { + LOG(("No unread can be calculated for %1").arg(dialogId)); + continue; + } + + auto untilId = unreadElement ? unreadElement->data()->id.bare : history->lastMessage()->id.bare; + + readEv.args.dialogId = dialogId; + readEv.args.untilId = untilId; + readEv.args.unread = history->unreadCount(); + + readsBatchEvent.args.events.emplace_back(readEv); + } + } + + pipe->send(readsBatchEvent); + + // send finish event + SyncForceFinish newEv; + newEv.userId = ev.userId; + + pipe->send(newEv); } void ayu_sync_controller::onSyncBatch(json ev) diff --git a/Telegram/SourceFiles/ayu/sync/ayu_sync_controller.h b/Telegram/SourceFiles/ayu/sync/ayu_sync_controller.h index 7e010d40d..47eab89b3 100644 --- a/Telegram/SourceFiles/ayu/sync/ayu_sync_controller.h +++ b/Telegram/SourceFiles/ayu/sync/ayu_sync_controller.h @@ -9,6 +9,9 @@ #include "models.h" #include "ayu/libs/json.hpp" + +#include "history/history.h" + #include "utils/ayu_pipe_wrapper.h" using json = nlohmann::json; @@ -29,6 +32,8 @@ namespace AyuSync public: void initializeAgent(); + void syncRead(not_null history, MsgId untilId); + void onSyncForce(SyncForce ev); void onSyncBatch(json ev); void onSyncRead(SyncRead ev); @@ -39,9 +44,10 @@ namespace AyuSync void receiver(); std::unique_ptr pipe; + bool initialized; }; - ayu_sync_controller& getControllerInstance(); + ayu_sync_controller& getInstance(); bool isAgentDownloaded(); bool isAgentRunning(); diff --git a/Telegram/SourceFiles/ayu/sync/models.h b/Telegram/SourceFiles/ayu/sync/models.h index dd368da5f..b5d9153fa 100644 --- a/Telegram/SourceFiles/ayu/sync/models.h +++ b/Telegram/SourceFiles/ayu/sync/models.h @@ -7,6 +7,8 @@ #define ID long long +using json = nlohmann::json; + class SyncEvent { public: @@ -17,13 +19,15 @@ public: class SyncBatch : public SyncEvent { public: - std::string type = "sync_batch"; - ID userId; + explicit SyncBatch() + { + type = "sync_batch"; + } class SyncBatchArgs { public: - std::vector events; + std::vector events; }; SyncBatchArgs args; @@ -32,8 +36,10 @@ public: class SyncRead : public SyncEvent { public: - std::string type = "sync_read"; - ID userId; + explicit SyncRead() + { + type = "sync_read"; + } class SyncReadArgs { @@ -49,8 +55,10 @@ public: class SyncForce : public SyncEvent { public: - std::string type = "sync_force"; - ID userId; + explicit SyncForce() + { + type = "sync_force"; + } class SyncForceArgs { @@ -64,8 +72,10 @@ public: class SyncForceFinish : public SyncEvent { public: - std::string type = "sync_force_finish"; - ID userId; + explicit SyncForceFinish() + { + type = "sync_force_finish"; + } class SyncForceFinishArgs { diff --git a/Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.cpp b/Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.cpp index f23dedf2d..732f9a24e 100644 --- a/Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.cpp +++ b/Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.cpp @@ -11,29 +11,34 @@ using stringbuf = std::basic_stringbuf, std::allocator>; -template -void ayu_pipe_wrapper::send(T obj) +void ayu_pipe_wrapper::connect() { - // auto s = json(obj).dump(); - // auto length = s.length(); - // char lengthBuff[4]; - // bit_converter::i32_to_bytes(length, false, lengthBuff); - // - // os.write(lengthBuff, 4); - // os.write(s.c_str(), length); - // os.flush(); - throw std::logic_error("not implemented"); + is = std::make_unique("AyuSync"); + receive(); + os = std::make_unique("AyuSync1338"); +} + +void ayu_pipe_wrapper::send(json p) +{ + auto s = p.dump(); + auto length = s.length(); + unsigned char lengthBuff[4]; + bit_converter::i32_to_bytes(length, false, lengthBuff); + + os->write(lengthBuff, 4); + os->write(reinterpret_cast(s.c_str()), length); + os->flush(); } std::optional ayu_pipe_wrapper::receive() { - if (!is.is_open()) + if (!is->is_open()) { return std::nullopt; } unsigned char lengthBuff[4]; - is.read(lengthBuff, 4); + is->read(lengthBuff, 4); auto length = bit_converter::bytes_to_i32(lengthBuff, false); @@ -50,9 +55,9 @@ std::optional ayu_pipe_wrapper::receive() while (length > 0) { auto readSize = std::min(length, static_cast(sizeof(buff))); - is.read(buff, readSize); + is->read(buff, readSize); - auto reallyRead = is.gcount(); + auto reallyRead = is->gcount(); sb.sputn(buff, reallyRead); length -= reallyRead; diff --git a/Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.h b/Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.h index 70aba4dd1..0f9b31ed0 100644 --- a/Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.h +++ b/Telegram/SourceFiles/ayu/sync/utils/ayu_pipe_wrapper.h @@ -12,15 +12,18 @@ #include "ayu/sync/models.h" using json = nlohmann::json; +using pipein = nes::basic_pipe_istream; +using pipeout = nes::basic_pipe_ostream; class ayu_pipe_wrapper { public: - template - void send(T obj); + void connect(); + void send(json p); std::optional receive(); private: - nes::basic_pipe_istream is{"AyuSync"}; + std::unique_ptr is; + std::unique_ptr os; }; diff --git a/Telegram/SourceFiles/ayu/sync/utils/process_utils.hpp b/Telegram/SourceFiles/ayu/sync/utils/process_utils.hpp index 73859d302..8fe6aa146 100644 --- a/Telegram/SourceFiles/ayu/sync/utils/process_utils.hpp +++ b/Telegram/SourceFiles/ayu/sync/utils/process_utils.hpp @@ -1,23 +1,18 @@ -#include #include #include #include #include -#include #ifdef _WIN32 #include #include -#else -#include -#include #endif // A function to check if a process is running by its name // Bing AI generated -bool is_process_running(const std::string& name) +inline bool isProcessRunning(const std::string& name) { #ifdef _WIN32 // Create a snapshot of all processes @@ -52,41 +47,50 @@ bool is_process_running(const std::string& name) CloseHandle(snapshot); return false; #else - // Open the /proc directory - DIR* dir = opendir("/proc"); - if (dir == nullptr) { - std::cerr << "Failed to open /proc\n"; - return false; - } - // Read the subdirectories - struct dirent* entry; - while ((entry = readdir(dir)) != nullptr) { - // Check if the subdirectory is a number (pid) - std::string pid = entry->d_name; - if (std::all_of(pid.begin(), pid.end(), isdigit)) { - // Read the /proc/pid/cmdline file - std::string cmdline_file = "/proc/" + pid + "/cmdline"; - std::ifstream file(cmdline_file); - if (file.is_open()) { - // Get the first word of the file (process name) - std::string process_name; - std::getline(file, process_name, '\0'); - // Remove the path if present - size_t pos = process_name.rfind('/'); - if (pos != std::string::npos) { - process_name = process_name.substr(pos + 1); - } - // Compare the names - if (name == process_name) { - // Found a match - closedir(dir); - return true; - } - } - } - } - // No match found - closedir(dir); - return false; + throw std::logic_error("not implemented"); +#endif +} + +// Copilot generated +void killProcess(const std::string& name) +{ +#ifdef _WIN32 + HANDLE snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); + if (snapshot == INVALID_HANDLE_VALUE) + { + std::cerr << "Failed to create snapshot\n"; + return; + } + // Iterate over the processes and compare the names + PROCESSENTRY32 entry; + entry.dwSize = sizeof(entry); + if (!Process32First(snapshot, &entry)) + { + std::cerr << "Failed to get first process\n"; + CloseHandle(snapshot); + return; + } + do + { + std::wstring_convert> converter; + std::string entry_name = converter.to_bytes(entry.szExeFile); + if (name == entry_name) + { + // Found a match + HANDLE hProcess = OpenProcess(PROCESS_TERMINATE, FALSE, entry.th32ProcessID); + if (hProcess != nullptr) + { + TerminateProcess(hProcess, 9); + CloseHandle(hProcess); + } + CloseHandle(snapshot); + return; + } + } + while (Process32Next(snapshot, &entry)); + // No match found + CloseHandle(snapshot); +#else + throw std::logic_error("not implemented"); #endif } diff --git a/Telegram/SourceFiles/ayu/sync/utils/telegram_helpers.cpp b/Telegram/SourceFiles/ayu/sync/utils/telegram_helpers.cpp index 1aec54062..374b86296 100644 --- a/Telegram/SourceFiles/ayu/sync/utils/telegram_helpers.cpp +++ b/Telegram/SourceFiles/ayu/sync/utils/telegram_helpers.cpp @@ -65,3 +65,21 @@ not_null getHistoryFromDialogId(ID dialogId, Main::Session* session) return session->data().history(peerFromChat(abs(dialogId))); } + +ID getDialogIdFromPeer(not_null peer) +{ + auto peerId = peerIsUser(peer->id) + ? peerToUser(peer->id).bare + : peerIsChat(peer->id) + ? peerToChat(peer->id).bare + : peerIsChannel(peer->id) + ? peerToChannel(peer->id).bare + : peer->id.value; + + if (peer->isChannel() || peer->isChat()) + { + peerId = -peerId; + } + + return peerId; +} diff --git a/Telegram/SourceFiles/ayu/sync/utils/telegram_helpers.h b/Telegram/SourceFiles/ayu/sync/utils/telegram_helpers.h index 7d0782e28..e1b508bfd 100644 --- a/Telegram/SourceFiles/ayu/sync/utils/telegram_helpers.h +++ b/Telegram/SourceFiles/ayu/sync/utils/telegram_helpers.h @@ -18,3 +18,4 @@ Main::Session* getSession(ID userId); bool accountExists(ID userId); void dispatchToMainThread(std::function callback); not_null getHistoryFromDialogId(ID dialogId, Main::Session* session); +ID getDialogIdFromPeer(not_null peer); diff --git a/Telegram/SourceFiles/ayu/ui/settings/settings_ayu.cpp b/Telegram/SourceFiles/ayu/ui/settings/settings_ayu.cpp index 445039b71..b309edf41 100644 --- a/Telegram/SourceFiles/ayu/ui/settings/settings_ayu.cpp +++ b/Telegram/SourceFiles/ayu/ui/settings/settings_ayu.cpp @@ -376,7 +376,7 @@ namespace Settings st::settingsButtonNoIcon )->addClickHandler([=] { - auto controller = &AyuSync::getControllerInstance(); + auto controller = &AyuSync::getInstance(); controller->initializeAgent(); }); } diff --git a/Telegram/SourceFiles/data/data_histories.cpp b/Telegram/SourceFiles/data/data_histories.cpp index 1dc4acabe..438c1a124 100644 --- a/Telegram/SourceFiles/data/data_histories.cpp +++ b/Telegram/SourceFiles/data/data_histories.cpp @@ -24,8 +24,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL #include "core/application.h" #include "apiwrap.h" +// AyuGram includes #include "ayu/ayu_settings.h" #include "ayu/ayu_state.h" +#include "ayu/sync/ayu_sync_controller.h" namespace Data { namespace { @@ -176,6 +178,8 @@ void Histories::readInboxTill( Core::App().notifications().clearIncomingFromHistory(history); + AyuSync::getInstance().syncRead(history, tillId); + // AyuGram sendReadPackets const auto settings = &AyuSettings::getInstance(); auto allow = settings->sendReadPackets;