mirror of
https://github.com/UltraCoderRU/court_monitor.git
synced 2026-01-28 02:15:12 +00:00
Рефакторинг.
This commit is contained in:
parent
0de99a97e8
commit
a4a53a6f19
9 changed files with 199 additions and 130 deletions
11
Asio.cpp
11
Asio.cpp
|
|
@ -1,11 +0,0 @@
|
|||
#include "Asio.h"
|
||||
|
||||
#include <boost/certify/https_verification.hpp>
|
||||
|
||||
void initSSL()
|
||||
{
|
||||
sslContext.set_verify_mode(boost::asio::ssl::verify_peer |
|
||||
boost::asio::ssl::verify_fail_if_no_peer_cert);
|
||||
sslContext.set_default_verify_paths();
|
||||
boost::certify::enable_native_https_server_verification(sslContext);
|
||||
}
|
||||
13
Asio.h
13
Asio.h
|
|
@ -1,13 +0,0 @@
|
|||
#ifndef COURT_MONITOR_ASIO_H
|
||||
#define COURT_MONITOR_ASIO_H
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
||||
static boost::asio::io_context asioContext;
|
||||
|
||||
static boost::asio::ssl::context sslContext(boost::asio::ssl::context::tls_client);
|
||||
|
||||
void initSSL();
|
||||
|
||||
#endif // COURT_MONITOR_ASIO_H
|
||||
104
Bot.cpp
Normal file
104
Bot.cpp
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
#include "Bot.h"
|
||||
|
||||
#include <banana/api.hpp>
|
||||
#include <fmt/format.h>
|
||||
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
|
||||
namespace {
|
||||
boost::asio::ssl::context sslContext(boost::asio::ssl::context::tlsv13_client);
|
||||
}
|
||||
|
||||
Bot::Bot(boost::asio::io_context& asioContext, LocalStorage& storage, bool& terminationFlag)
|
||||
: storage_(storage),
|
||||
terminationFlag_(terminationFlag),
|
||||
agent_(storage.token, asioContext, sslContext)
|
||||
{
|
||||
getUpdates();
|
||||
}
|
||||
|
||||
void Bot::setupCommands()
|
||||
{
|
||||
banana::api::set_my_commands_args_t args;
|
||||
args.commands.push_back(banana::api::bot_command_t{
|
||||
"/subscribe_case", "Подписаться на обновления дела по его номеру"});
|
||||
args.commands.push_back(banana::api::bot_command_t{"/find_case", "Найти дело по параметрам"});
|
||||
args.scope = banana::api::bot_command_scope_all_private_chats_t{"all_private_chats"};
|
||||
banana::api::set_my_commands(agent_, std::move(args),
|
||||
[](banana::expected<banana::boolean_t> result)
|
||||
{
|
||||
if (result)
|
||||
fmt::print("commands set up successfully\n");
|
||||
else
|
||||
fmt::print(stderr, "failed to set up commands\n");
|
||||
});
|
||||
}
|
||||
|
||||
void Bot::notifyUser(int userId,
|
||||
const std::string& caseNumber,
|
||||
std::string caseUrl,
|
||||
const CaseHistoryItem& item)
|
||||
{
|
||||
caseUrl = fmt::format("https://mirsud.spb.ru{}", caseUrl);
|
||||
std::string message = fmt::format(
|
||||
"Новое событие по делу [№{}]({}):\n"
|
||||
"{}\n"
|
||||
"Дата: {} {}\n",
|
||||
caseNumber, caseUrl, item.status, item.date, item.time);
|
||||
banana::api::send_message(agent_, {.chat_id = userId, .text = message, .parse_mode = "markdown"},
|
||||
[](const auto&) {});
|
||||
}
|
||||
|
||||
void Bot::getUpdates()
|
||||
{
|
||||
auto handler = [this](banana::expected<banana::array_t<banana::api::update_t>> updates)
|
||||
{
|
||||
if (terminationFlag_)
|
||||
return;
|
||||
|
||||
if (updates)
|
||||
{
|
||||
for (const auto& update : *updates)
|
||||
{
|
||||
processUpdate(update);
|
||||
updatesOffset_ = update.update_id + 1;
|
||||
}
|
||||
getUpdates();
|
||||
}
|
||||
else
|
||||
fmt::print(stderr, "failed to get updates: {}\n", updates.error());
|
||||
};
|
||||
|
||||
banana::api::get_updates(agent_, {.offset = updatesOffset_, .timeout = 50}, std::move(handler));
|
||||
}
|
||||
|
||||
void Bot::processUpdate(const banana::api::update_t& update)
|
||||
{
|
||||
if (update.message)
|
||||
{
|
||||
if (update.message->text)
|
||||
{
|
||||
fmt::print("rx: {}\n", *update.message->text);
|
||||
if (*update.message->text == "/start")
|
||||
processStartCommand(*update.message);
|
||||
else if (*update.message->text == "/subscribe_case")
|
||||
processSubscribeCaseCommand(*update.message);
|
||||
else
|
||||
{
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
else
|
||||
fmt::print("skip message without text"); // TODO ответить
|
||||
}
|
||||
else
|
||||
fmt::print("skip unknown update type");
|
||||
}
|
||||
|
||||
void Bot::processStartCommand(const banana::api::message_t& message)
|
||||
{
|
||||
}
|
||||
|
||||
void Bot::processSubscribeCaseCommand(const banana::api::message_t& message)
|
||||
{
|
||||
}
|
||||
35
Bot.h
Normal file
35
Bot.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
#ifndef COURT_MONITOR_BOT_H
|
||||
#define COURT_MONITOR_BOT_H
|
||||
|
||||
#include "CourtApi.h"
|
||||
#include "Storage.h"
|
||||
|
||||
#include <banana/agent/beast.hpp>
|
||||
#include <banana/types_fwd.hpp>
|
||||
|
||||
class Bot
|
||||
{
|
||||
public:
|
||||
explicit Bot(boost::asio::io_context& asioContext, LocalStorage& storage, bool& terminationFlag);
|
||||
|
||||
void setupCommands();
|
||||
|
||||
void notifyUser(int userId,
|
||||
const std::string& caseNumber,
|
||||
std::string caseUrl,
|
||||
const CaseHistoryItem& item);
|
||||
|
||||
private:
|
||||
void getUpdates();
|
||||
void processUpdate(const banana::api::update_t& update);
|
||||
|
||||
void processStartCommand(const banana::api::message_t& message);
|
||||
void processSubscribeCaseCommand(const banana::api::message_t& message);
|
||||
|
||||
LocalStorage& storage_;
|
||||
bool& terminationFlag_;
|
||||
banana::agent::beast_callback agent_;
|
||||
std::int64_t updatesOffset_ = 0;
|
||||
};
|
||||
|
||||
#endif // COURT_MONITOR_BOT_H
|
||||
|
|
@ -11,7 +11,7 @@ find_package(Boost COMPONENTS system REQUIRED)
|
|||
add_subdirectory(external)
|
||||
|
||||
add_executable(court_monitor
|
||||
Asio.cpp
|
||||
Bot.cpp
|
||||
CourtApi.cpp
|
||||
Storage.cpp
|
||||
main.cpp
|
||||
|
|
|
|||
35
CourtApi.cpp
35
CourtApi.cpp
|
|
@ -1,10 +1,9 @@
|
|||
#include "CourtApi.h"
|
||||
|
||||
#include "Asio.h"
|
||||
|
||||
#include <fmt/format.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <boost/asio/ssl/context.hpp>
|
||||
#include <boost/asio/ssl/stream.hpp>
|
||||
#include <boost/beast.hpp>
|
||||
#include <boost/certify/extensions.hpp>
|
||||
|
|
@ -15,10 +14,17 @@
|
|||
|
||||
const char* serverDomain = "mirsud.spb.ru";
|
||||
|
||||
namespace {
|
||||
boost::asio::ssl::context sslContext(boost::asio::ssl::context::tlsv13_client);
|
||||
using ssl_stream = boost::asio::ssl::stream<boost::beast::tcp_stream>;
|
||||
} // namespace
|
||||
|
||||
ssl_stream connect(const std::string& hostname)
|
||||
ssl_stream connect(boost::asio::io_context& asioContext, const std::string& hostname)
|
||||
{
|
||||
sslContext.set_verify_mode(boost::asio::ssl::verify_peer |
|
||||
boost::asio::ssl::verify_fail_if_no_peer_cert);
|
||||
sslContext.set_default_verify_paths();
|
||||
boost::certify::enable_native_https_server_verification(sslContext);
|
||||
ssl_stream stream(asioContext, sslContext);
|
||||
|
||||
static boost::asio::ip::tcp::resolver resolver(asioContext);
|
||||
|
|
@ -77,9 +83,11 @@ nlohmann::json getResults(ssl_stream& stream, const std::string_view& uuid)
|
|||
fmt::format("failed to retrieve JSON (server returned code {})", status));
|
||||
}
|
||||
|
||||
nlohmann::json getCaseDetails(int courtId, const std::string_view& caseNumber)
|
||||
nlohmann::json getCaseDetails(boost::asio::io_context& asioContext,
|
||||
int courtId,
|
||||
const std::string_view& caseNumber)
|
||||
{
|
||||
ssl_stream stream = connect(serverDomain);
|
||||
ssl_stream stream = connect(asioContext, serverDomain);
|
||||
|
||||
int status;
|
||||
std::string result;
|
||||
|
|
@ -105,3 +113,20 @@ nlohmann::json getCaseDetails(int courtId, const std::string_view& caseNumber)
|
|||
throw std::runtime_error(
|
||||
fmt::format("failed to retrieve JSON (server returned code {})", status));
|
||||
}
|
||||
|
||||
std::vector<CaseHistoryItem> parseHistory(const nlohmann::json& details)
|
||||
{
|
||||
std::vector<CaseHistoryItem> items;
|
||||
const auto& history = details.at("history");
|
||||
for (const auto& obj : history)
|
||||
{
|
||||
CaseHistoryItem item;
|
||||
item.date = obj.at("date").get<std::string>();
|
||||
item.time = obj.at("time").get<std::string>();
|
||||
item.status = obj.at("status").get<std::string>();
|
||||
item.publishDate = obj.at("publish_date").get<std::string>();
|
||||
item.publishTime = obj.at("publish_time").get<std::string>();
|
||||
items.push_back(std::move(item));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
|
|
|||
21
CourtApi.h
21
CourtApi.h
|
|
@ -3,10 +3,27 @@
|
|||
|
||||
#include <nlohmann/json_fwd.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <vector>
|
||||
|
||||
nlohmann::json findCases(const std::string_view& name);
|
||||
struct CaseHistoryItem
|
||||
{
|
||||
std::string date;
|
||||
std::string time;
|
||||
std::string status;
|
||||
std::string publishDate;
|
||||
std::string publishTime;
|
||||
};
|
||||
|
||||
nlohmann::json getCaseDetails(int courtId, const std::string_view& caseNumber);
|
||||
nlohmann::json findCases(boost::asio::io_context& asioContext, const std::string_view& name);
|
||||
|
||||
nlohmann::json getCaseDetails(boost::asio::io_context& asioContext,
|
||||
int courtId,
|
||||
const std::string_view& caseNumber);
|
||||
|
||||
std::vector<CaseHistoryItem> parseHistory(const nlohmann::json& details);
|
||||
|
||||
#endif // COURT_MONITOR_COURT_API_H
|
||||
|
|
|
|||
2
external/banana
vendored
2
external/banana
vendored
|
|
@ -1 +1 @@
|
|||
Subproject commit 075ac78bba0950341c9c477ca38a523730857fee
|
||||
Subproject commit 787d34ea6f616d3a37a09c7262c07d0ff1f0bef4
|
||||
106
main.cpp
106
main.cpp
|
|
@ -1,12 +1,13 @@
|
|||
#include "Asio.h"
|
||||
#include "Bot.h"
|
||||
#include "CourtApi.h"
|
||||
#include "Storage.h"
|
||||
|
||||
#include <banana/agent/beast.hpp>
|
||||
#include <banana/api.hpp>
|
||||
#include <fmt/format.h>
|
||||
#include <fmt/core.h>
|
||||
#include <nlohmann/json.hpp>
|
||||
|
||||
#include <boost/asio/io_context.hpp>
|
||||
#include <boost/asio/system_timer.hpp>
|
||||
|
||||
#include <chrono>
|
||||
|
|
@ -14,49 +15,9 @@
|
|||
#include <ctime>
|
||||
#include <string>
|
||||
|
||||
struct CaseHistoryItem
|
||||
{
|
||||
std::string date;
|
||||
std::string time;
|
||||
std::string status;
|
||||
std::string publishDate;
|
||||
std::string publishTime;
|
||||
};
|
||||
boost::asio::io_context asioContext;
|
||||
|
||||
std::vector<CaseHistoryItem> parseHistory(const nlohmann::json& details)
|
||||
{
|
||||
std::vector<CaseHistoryItem> items;
|
||||
const auto& history = details.at("history");
|
||||
for (const auto& obj : history)
|
||||
{
|
||||
CaseHistoryItem item;
|
||||
item.date = obj.at("date").get<std::string>();
|
||||
item.time = obj.at("time").get<std::string>();
|
||||
item.status = obj.at("status").get<std::string>();
|
||||
item.publishDate = obj.at("publish_date").get<std::string>();
|
||||
item.publishTime = obj.at("publish_time").get<std::string>();
|
||||
items.push_back(std::move(item));
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
void notifyUser(banana::agent::beast_callback& bot,
|
||||
int userId,
|
||||
const std::string& caseNumber,
|
||||
std::string caseUrl,
|
||||
const CaseHistoryItem& item)
|
||||
{
|
||||
caseUrl = fmt::format("https://mirsud.spb.ru{}", caseUrl);
|
||||
std::string message = fmt::format(
|
||||
"Новое событие по делу [№{}]({}):\n"
|
||||
"{}\n"
|
||||
"Дата: {} {}\n",
|
||||
caseNumber, caseUrl, item.status, item.date, item.time);
|
||||
banana::api::send_message(bot, {.chat_id = userId, .text = message, .parse_mode = "markdown"},
|
||||
[](const auto&) {});
|
||||
}
|
||||
|
||||
void processAllSubscriptions(LocalStorage& storage, banana::agent::beast_callback& bot)
|
||||
void processAllSubscriptions(LocalStorage& storage, Bot& bot)
|
||||
{
|
||||
for (auto& subscription : storage.subscriptions)
|
||||
{
|
||||
|
|
@ -66,13 +27,13 @@ void processAllSubscriptions(LocalStorage& storage, banana::agent::beast_callbac
|
|||
for (auto& counter : subscription.counters)
|
||||
{
|
||||
fmt::print("** Processing case {}\n", counter.caseNumber);
|
||||
auto details = getCaseDetails(counter.courtId, counter.caseNumber);
|
||||
auto details = getCaseDetails(asioContext, counter.courtId, counter.caseNumber);
|
||||
fmt::print("{}\n", details.dump());
|
||||
auto url = details["url"].get<std::string>();
|
||||
|
||||
auto history = parseHistory(details);
|
||||
for (std::size_t i = counter.value; i < history.size(); i++)
|
||||
notifyUser(bot, subscription.userId, counter.caseNumber, url, history[i]);
|
||||
bot.notifyUser(subscription.userId, counter.caseNumber, url, history[i]);
|
||||
counter.value = history.size();
|
||||
}
|
||||
}
|
||||
|
|
@ -84,17 +45,6 @@ void processAllSubscriptions(LocalStorage& storage, banana::agent::beast_callbac
|
|||
}
|
||||
}
|
||||
|
||||
void processUpdate(const banana::api::update_t& update)
|
||||
{
|
||||
if (update.message)
|
||||
{
|
||||
if (update.message->text)
|
||||
{
|
||||
fmt::print("rx: {}\n", *update.message->text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::chrono::system_clock::time_point getNextCheckTime(std::uint32_t checkTime)
|
||||
{
|
||||
std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
|
||||
|
|
@ -116,39 +66,6 @@ void handleSignal(int)
|
|||
asioContext.stop();
|
||||
}
|
||||
|
||||
int64_t offset = 0;
|
||||
|
||||
void getUpdates(banana::agent::beast_callback& bot);
|
||||
|
||||
void processUpdates(banana::agent::beast_callback bot,
|
||||
banana::expected<banana::array_t<banana::api::update_t>> updates)
|
||||
{
|
||||
if (terminate)
|
||||
{
|
||||
fmt::print("exit\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (updates)
|
||||
{
|
||||
for (const auto& update : *updates)
|
||||
{
|
||||
processUpdate(update);
|
||||
offset = update.update_id + 1;
|
||||
}
|
||||
}
|
||||
else
|
||||
fmt::print(stderr, "failed to get updates: {}\n", updates.error());
|
||||
|
||||
getUpdates(bot);
|
||||
}
|
||||
|
||||
void getUpdates(banana::agent::beast_callback& bot)
|
||||
{
|
||||
banana::api::get_updates(bot, {.offset = offset, .timeout = 50},
|
||||
[bot](auto updates) { processUpdates(bot, std::move(updates)); });
|
||||
}
|
||||
|
||||
int main()
|
||||
{
|
||||
std::signal(SIGTERM, handleSignal);
|
||||
|
|
@ -160,11 +77,9 @@ int main()
|
|||
loadStorage(storage);
|
||||
fmt::print("Storage loaded\n");
|
||||
|
||||
// Инициализировать SSL
|
||||
initSSL();
|
||||
|
||||
// Создать бота
|
||||
banana::agent::beast_callback bot(storage.token, asioContext, sslContext);
|
||||
Bot bot(asioContext, storage, terminate);
|
||||
bot.setupCommands();
|
||||
|
||||
// Создать таймер ежедневной проверки
|
||||
boost::asio::system_timer checkTimer(asioContext);
|
||||
|
|
@ -181,9 +96,6 @@ int main()
|
|||
checkTimer.expires_at(getNextCheckTime(storage.checkTime));
|
||||
checkTimer.async_wait(onTimer);
|
||||
|
||||
// Запустить асинхронное получение обновлений
|
||||
getUpdates(bot);
|
||||
|
||||
// Запустить цикл обработки событий
|
||||
asioContext.run();
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue