mirror of
https://github.com/UltraCoderRU/court_monitor.git
synced 2026-01-28 10:25:13 +00:00
Наработки по машинам состояния.
This commit is contained in:
parent
4309860ddb
commit
193dabe805
13 changed files with 293 additions and 108 deletions
27
Bot.cpp
27
Bot.cpp
|
|
@ -94,31 +94,30 @@ void Bot::processUpdate(const banana::api::update_t& update)
|
||||||
else
|
else
|
||||||
LOG(bot, "incoming message: user={} (not text)", userId);
|
LOG(bot, "incoming message: user={} (not text)", userId);
|
||||||
|
|
||||||
auto sessionIt = sessions_.find(userId);
|
auto& session = getOrCreateSession(userId);
|
||||||
if (sessionIt == sessions_.end())
|
session.processMessage(*update.message);
|
||||||
{
|
|
||||||
bool ok;
|
|
||||||
std::tie(sessionIt, ok) =
|
|
||||||
sessions_.emplace(std::piecewise_construct, std::forward_as_tuple(userId),
|
|
||||||
std::forward_as_tuple(agent_, userId));
|
|
||||||
}
|
|
||||||
sessionIt->second.processMessage(*update.message);
|
|
||||||
}
|
}
|
||||||
else if (update.callback_query)
|
else if (update.callback_query)
|
||||||
{
|
{
|
||||||
banana::integer_t userId = update.callback_query->from.id;
|
banana::integer_t userId = update.callback_query->from.id;
|
||||||
LOG(bot, "incoming callback query: user={} data='{}'", userId, *update.callback_query->data);
|
LOG(bot, "incoming callback query: user={} data='{}'", userId, *update.callback_query->data);
|
||||||
|
|
||||||
|
auto& session = getOrCreateSession(userId);
|
||||||
|
session.processCallbackQuery(*update.callback_query);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LOG(bot, "skip unknown update type");
|
||||||
|
}
|
||||||
|
|
||||||
|
BotSession& Bot::getOrCreateSession(banana::integer_t userId)
|
||||||
|
{
|
||||||
auto sessionIt = sessions_.find(userId);
|
auto sessionIt = sessions_.find(userId);
|
||||||
if (sessionIt == sessions_.end())
|
if (sessionIt == sessions_.end())
|
||||||
{
|
{
|
||||||
bool ok;
|
bool ok;
|
||||||
std::tie(sessionIt, ok) =
|
std::tie(sessionIt, ok) =
|
||||||
sessions_.emplace(std::piecewise_construct, std::forward_as_tuple(userId),
|
sessions_.emplace(std::piecewise_construct, std::forward_as_tuple(userId),
|
||||||
std::forward_as_tuple(agent_, userId));
|
std::forward_as_tuple(agent_, userId, storage_));
|
||||||
}
|
}
|
||||||
sessionIt->second.processCallbackQuery(*update.callback_query);
|
return sessionIt->second;
|
||||||
}
|
|
||||||
else
|
|
||||||
LOG(bot, "skip unknown update type");
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
Bot.h
1
Bot.h
|
|
@ -25,6 +25,7 @@ private:
|
||||||
|
|
||||||
void getUpdates();
|
void getUpdates();
|
||||||
void processUpdate(const banana::api::update_t& update);
|
void processUpdate(const banana::api::update_t& update);
|
||||||
|
BotSession& getOrCreateSession(banana::integer_t userId);
|
||||||
|
|
||||||
LocalStorage& storage_;
|
LocalStorage& storage_;
|
||||||
bool& terminationFlag_;
|
bool& terminationFlag_;
|
||||||
|
|
|
||||||
|
|
@ -1,13 +1,16 @@
|
||||||
#include "BotSession.h"
|
#include "BotSession.h"
|
||||||
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "Storage.h"
|
||||||
#include "SubscribeCaseDialog.h"
|
#include "SubscribeCaseDialog.h"
|
||||||
|
|
||||||
#include <banana/api.hpp>
|
#include <banana/api.hpp>
|
||||||
#include <fmt/core.h>
|
#include <fmt/core.h>
|
||||||
|
|
||||||
BotSession::BotSession(banana::agent::beast_callback& agent, banana::integer_t userId)
|
BotSession::BotSession(banana::agent::beast_callback& agent,
|
||||||
: agent_(agent), userId_(userId)
|
banana::integer_t userId,
|
||||||
|
LocalStorage& storage)
|
||||||
|
: agent_(agent), userId_(userId), storage_(storage)
|
||||||
{
|
{
|
||||||
LOG(session, "new session created for user {}", userId_);
|
LOG(session, "new session created for user {}", userId_);
|
||||||
}
|
}
|
||||||
|
|
@ -20,9 +23,9 @@ void BotSession::processMessage(const banana::api::message_t& message)
|
||||||
{
|
{
|
||||||
auto text = *message.text;
|
auto text = *message.text;
|
||||||
if (text == "/start")
|
if (text == "/start")
|
||||||
processStartCommand(message);
|
processStartCommand();
|
||||||
else if (text == "/stop")
|
else if (text == "/stop")
|
||||||
processStopCommand(message);
|
processStopCommand();
|
||||||
else if (text == "/subscribe_case")
|
else if (text == "/subscribe_case")
|
||||||
activeDialog_ = makeDialog<SubscribeCaseDialog>();
|
activeDialog_ = makeDialog<SubscribeCaseDialog>();
|
||||||
else if (activeDialog_)
|
else if (activeDialog_)
|
||||||
|
|
@ -30,13 +33,13 @@ void BotSession::processMessage(const banana::api::message_t& message)
|
||||||
if (activeDialog_->processMessage(message))
|
if (activeDialog_->processMessage(message))
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
processStartCommand(message);
|
processStartCommand();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// TODO
|
// TODO
|
||||||
processStartCommand(message);
|
processStartCommand();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
|
@ -45,9 +48,21 @@ void BotSession::processMessage(const banana::api::message_t& message)
|
||||||
|
|
||||||
void BotSession::processCallbackQuery(const banana::api::callback_query_t& query)
|
void BotSession::processCallbackQuery(const banana::api::callback_query_t& query)
|
||||||
{
|
{
|
||||||
|
if (query.data)
|
||||||
|
{
|
||||||
|
if (activeDialog_)
|
||||||
|
{
|
||||||
|
if (activeDialog_->processCallbackQuery(query))
|
||||||
|
processStartCommand();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LOGE(session, "skip callback query, because there is no active dialog");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
LOGE(session, "skip callback query without data");
|
||||||
}
|
}
|
||||||
|
|
||||||
void BotSession::processStartCommand(const banana::api::message_t& message)
|
void BotSession::processStartCommand()
|
||||||
{
|
{
|
||||||
activeDialog_.reset();
|
activeDialog_.reset();
|
||||||
|
|
||||||
|
|
@ -59,7 +74,7 @@ void BotSession::processStartCommand(const banana::api::message_t& message)
|
||||||
[](const banana::expected<banana::api::message_t>& message) {});
|
[](const banana::expected<banana::api::message_t>& message) {});
|
||||||
}
|
}
|
||||||
|
|
||||||
void BotSession::processStopCommand(const banana::api::message_t& message)
|
void BotSession::processStopCommand()
|
||||||
{
|
{
|
||||||
activeDialog_.reset();
|
activeDialog_.reset();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
11
BotSession.h
11
BotSession.h
|
|
@ -1,6 +1,8 @@
|
||||||
#ifndef COURT_MONITOR_BOT_SESSION_H
|
#ifndef COURT_MONITOR_BOT_SESSION_H
|
||||||
#define COURT_MONITOR_BOT_SESSION_H
|
#define COURT_MONITOR_BOT_SESSION_H
|
||||||
|
|
||||||
|
#include "Storage.h"
|
||||||
|
|
||||||
#include <banana/agent/beast.hpp>
|
#include <banana/agent/beast.hpp>
|
||||||
#include <banana/types_fwd.hpp>
|
#include <banana/types_fwd.hpp>
|
||||||
#include <banana/utils/basic_types.hpp>
|
#include <banana/utils/basic_types.hpp>
|
||||||
|
|
@ -12,7 +14,7 @@ class Dialog;
|
||||||
class BotSession final
|
class BotSession final
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
BotSession(banana::agent::beast_callback& agent, banana::integer_t userId);
|
BotSession(banana::agent::beast_callback& agent, banana::integer_t userId, LocalStorage& storage);
|
||||||
~BotSession();
|
~BotSession();
|
||||||
|
|
||||||
void processMessage(const banana::api::message_t& message);
|
void processMessage(const banana::api::message_t& message);
|
||||||
|
|
@ -22,14 +24,15 @@ private:
|
||||||
template <class T>
|
template <class T>
|
||||||
std::unique_ptr<Dialog> makeDialog()
|
std::unique_ptr<Dialog> makeDialog()
|
||||||
{
|
{
|
||||||
return std::make_unique<T>(agent_, userId_);
|
return std::make_unique<T>(agent_, userId_, storage_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void processStartCommand(const banana::api::message_t& message);
|
void processStartCommand();
|
||||||
void processStopCommand(const banana::api::message_t& message);
|
void processStopCommand();
|
||||||
|
|
||||||
banana::agent::beast_callback& agent_;
|
banana::agent::beast_callback& agent_;
|
||||||
banana::integer_t userId_;
|
banana::integer_t userId_;
|
||||||
|
LocalStorage& storage_;
|
||||||
std::unique_ptr<Dialog> activeDialog_;
|
std::unique_ptr<Dialog> activeDialog_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
77
CourtApi.cpp
77
CourtApi.cpp
|
|
@ -1,5 +1,7 @@
|
||||||
#include "CourtApi.h"
|
#include "CourtApi.h"
|
||||||
|
|
||||||
|
#include "Logger.h"
|
||||||
|
|
||||||
#include <fmt/format.h>
|
#include <fmt/format.h>
|
||||||
#include <nlohmann/json.hpp>
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
|
|
@ -9,7 +11,6 @@
|
||||||
#include <boost/certify/extensions.hpp>
|
#include <boost/certify/extensions.hpp>
|
||||||
#include <boost/certify/https_verification.hpp>
|
#include <boost/certify/https_verification.hpp>
|
||||||
|
|
||||||
#include <iostream>
|
|
||||||
#include <thread>
|
#include <thread>
|
||||||
|
|
||||||
const char* serverDomain = "mirsud.spb.ru";
|
const char* serverDomain = "mirsud.spb.ru";
|
||||||
|
|
@ -24,15 +25,16 @@ ssl_stream connect(boost::asio::io_context& asioContext, const std::string& host
|
||||||
sslContext.set_verify_mode(boost::asio::ssl::verify_peer |
|
sslContext.set_verify_mode(boost::asio::ssl::verify_peer |
|
||||||
boost::asio::ssl::verify_fail_if_no_peer_cert);
|
boost::asio::ssl::verify_fail_if_no_peer_cert);
|
||||||
sslContext.set_default_verify_paths();
|
sslContext.set_default_verify_paths();
|
||||||
|
sslContext.load_verify_file("ISRG_X1.pem");
|
||||||
boost::certify::enable_native_https_server_verification(sslContext);
|
boost::certify::enable_native_https_server_verification(sslContext);
|
||||||
ssl_stream stream(asioContext, sslContext);
|
ssl_stream stream(asioContext, sslContext);
|
||||||
|
|
||||||
static boost::asio::ip::tcp::resolver resolver(asioContext);
|
static boost::asio::ip::tcp::resolver resolver(asioContext);
|
||||||
auto const results = resolver.resolve(hostname, "https");
|
auto const results = resolver.resolve(hostname, "https");
|
||||||
|
boost::beast::get_lowest_layer(stream).connect(results);
|
||||||
|
|
||||||
boost::certify::set_server_hostname(stream, hostname);
|
boost::certify::set_server_hostname(stream, hostname);
|
||||||
boost::certify::sni_hostname(stream, hostname);
|
boost::certify::sni_hostname(stream, hostname);
|
||||||
boost::beast::get_lowest_layer(stream).connect(results);
|
|
||||||
stream.handshake(boost::asio::ssl::stream_base::client);
|
stream.handshake(boost::asio::ssl::stream_base::client);
|
||||||
|
|
||||||
return stream;
|
return stream;
|
||||||
|
|
@ -56,14 +58,11 @@ std::pair<int, std::string> get(ssl_stream& stream,
|
||||||
request.body() = std::move(*payload);
|
request.body() = std::move(*payload);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::cout << "tx: " << request << std::endl;
|
|
||||||
|
|
||||||
boost::beast::http::write(stream, request);
|
boost::beast::http::write(stream, request);
|
||||||
|
|
||||||
boost::beast::http::response<boost::beast::http::string_body> response;
|
boost::beast::http::response<boost::beast::http::string_body> response;
|
||||||
boost::beast::flat_buffer buffer;
|
boost::beast::flat_buffer buffer;
|
||||||
boost::beast::http::read(stream, buffer, response);
|
boost::beast::http::read(stream, buffer, response);
|
||||||
std::cout << "rx: " << response << std::endl;
|
|
||||||
|
|
||||||
return {response.base().result_int(), response.body()};
|
return {response.base().result_int(), response.body()};
|
||||||
}
|
}
|
||||||
|
|
@ -83,27 +82,58 @@ nlohmann::json getResults(ssl_stream& stream, const std::string_view& uuid)
|
||||||
fmt::format("failed to retrieve JSON (server returned code {})", status));
|
fmt::format("failed to retrieve JSON (server returned code {})", status));
|
||||||
}
|
}
|
||||||
|
|
||||||
nlohmann::json getCaseDetails(boost::asio::io_context& asioContext,
|
CaseDetails getCaseDetails(boost::asio::io_context& asioContext, const std::string_view& caseNumber)
|
||||||
int courtId,
|
|
||||||
const std::string_view& caseNumber)
|
|
||||||
{
|
{
|
||||||
ssl_stream stream = connect(asioContext, serverDomain);
|
ssl_stream stream = connect(asioContext, serverDomain);
|
||||||
|
|
||||||
int status;
|
int status;
|
||||||
std::string result;
|
std::string result;
|
||||||
std::tie(status, result) =
|
std::tie(status, result) =
|
||||||
get(stream, serverDomain,
|
get(stream, serverDomain, fmt::format("/cases/api/detail/?id={}", caseNumber));
|
||||||
fmt::format("/cases/api/detail/?id={}&court_site_id={}", caseNumber, courtId));
|
|
||||||
if (status == 200)
|
if (status == 200)
|
||||||
{
|
{
|
||||||
auto uuid = nlohmann::json::parse(result).at("id").get<std::string>();
|
auto uuid = nlohmann::json::parse(result).at("id").get<std::string>();
|
||||||
|
|
||||||
for (int i = 0; i < 10; i++)
|
for (int i = 0; i < 5; i++)
|
||||||
{
|
{
|
||||||
auto results = getResults(stream, uuid);
|
auto response = getResults(stream, uuid);
|
||||||
bool finished = results.at("finished").get<bool>();
|
if (response.at("finished").get<bool>())
|
||||||
if (finished)
|
{
|
||||||
return results.at("result");
|
auto& results = response.at("result");
|
||||||
|
LOG(court, results.dump());
|
||||||
|
|
||||||
|
CaseDetails details;
|
||||||
|
details.id = results["id"].get<std::string>();
|
||||||
|
details.courtNumber = results["court_number"].get<std::string>();
|
||||||
|
details.name = results["name"].get<std::string>();
|
||||||
|
details.description = results["description"].get<std::string>();
|
||||||
|
details.url =
|
||||||
|
fmt::format("https://{}{}", serverDomain, results["url"].get<std::string>());
|
||||||
|
|
||||||
|
details.districtName = results["district_name"].get<std::string>();
|
||||||
|
details.judgeName = results["judge"].get<std::string>();
|
||||||
|
|
||||||
|
for (const auto& participant : results["participants"])
|
||||||
|
{
|
||||||
|
CaseParticipant p;
|
||||||
|
p.title = participant["title"].get<std::string>();
|
||||||
|
p.name = participant["name"].get<std::string>();
|
||||||
|
details.participants.push_back(std::move(p));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const auto& obj : results["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>();
|
||||||
|
details.history.push_back(std::move(item));
|
||||||
|
}
|
||||||
|
|
||||||
|
return details;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
std::this_thread::sleep_for(std::chrono::seconds(1));
|
std::this_thread::sleep_for(std::chrono::seconds(1));
|
||||||
}
|
}
|
||||||
|
|
@ -113,20 +143,3 @@ nlohmann::json getCaseDetails(boost::asio::io_context& asioContext,
|
||||||
throw std::runtime_error(
|
throw std::runtime_error(
|
||||||
fmt::format("failed to retrieve JSON (server returned code {})", status));
|
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;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
33
CourtApi.h
33
CourtApi.h
|
|
@ -9,6 +9,12 @@
|
||||||
#include <string_view>
|
#include <string_view>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
struct CaseParticipant
|
||||||
|
{
|
||||||
|
std::string title;
|
||||||
|
std::string name;
|
||||||
|
};
|
||||||
|
|
||||||
struct CaseHistoryItem
|
struct CaseHistoryItem
|
||||||
{
|
{
|
||||||
std::string date;
|
std::string date;
|
||||||
|
|
@ -18,12 +24,29 @@ struct CaseHistoryItem
|
||||||
std::string publishTime;
|
std::string publishTime;
|
||||||
};
|
};
|
||||||
|
|
||||||
nlohmann::json findCases(boost::asio::io_context& asioContext, const std::string_view& name);
|
struct CaseDetails
|
||||||
|
{
|
||||||
|
std::string id;
|
||||||
|
std::string courtNumber;
|
||||||
|
std::string name;
|
||||||
|
std::string description;
|
||||||
|
std::string url;
|
||||||
|
|
||||||
nlohmann::json getCaseDetails(boost::asio::io_context& asioContext,
|
std::string districtName;
|
||||||
int courtId,
|
std::string judgeName;
|
||||||
const std::string_view& caseNumber);
|
std::vector<CaseParticipant> participants;
|
||||||
|
|
||||||
std::vector<CaseHistoryItem> parseHistory(const nlohmann::json& details);
|
std::string status;
|
||||||
|
std::vector<CaseHistoryItem> history;
|
||||||
|
|
||||||
|
std::string createdDate;
|
||||||
|
std::string acceptedDate;
|
||||||
|
std::string judicialUid;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<CaseDetails> findCases(boost::asio::io_context& asioContext,
|
||||||
|
const std::string_view& name);
|
||||||
|
|
||||||
|
CaseDetails getCaseDetails(boost::asio::io_context& asioContext, const std::string_view& caseNumber);
|
||||||
|
|
||||||
#endif // COURT_MONITOR_COURT_API_H
|
#endif // COURT_MONITOR_COURT_API_H
|
||||||
|
|
|
||||||
13
Dialog.cpp
13
Dialog.cpp
|
|
@ -2,8 +2,7 @@
|
||||||
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
|
||||||
Dialog::Dialog(banana::agent::beast_callback& agent, banana::integer_t userId, const char* name)
|
Dialog::Dialog(banana::integer_t userId, const char* name) : userId_(userId), name_(name)
|
||||||
: agent_(agent), userId_(userId), name_(name)
|
|
||||||
{
|
{
|
||||||
LOG(dialog, "{} dialog created for user {}", name_, userId_);
|
LOG(dialog, "{} dialog created for user {}", name_, userId_);
|
||||||
}
|
}
|
||||||
|
|
@ -12,13 +11,3 @@ Dialog::~Dialog()
|
||||||
{
|
{
|
||||||
LOG(dialog, "{} dialog for user {} destroyed", name_, userId_);
|
LOG(dialog, "{} dialog for user {} destroyed", name_, userId_);
|
||||||
}
|
}
|
||||||
|
|
||||||
banana::agent::beast_callback& Dialog::getAgent() const
|
|
||||||
{
|
|
||||||
return agent_;
|
|
||||||
}
|
|
||||||
|
|
||||||
banana::integer_t Dialog::getUserId() const
|
|
||||||
{
|
|
||||||
return userId_;
|
|
||||||
}
|
|
||||||
|
|
|
||||||
7
Dialog.h
7
Dialog.h
|
|
@ -1,25 +1,20 @@
|
||||||
#ifndef COURT_MONITOR_DIALOG_H
|
#ifndef COURT_MONITOR_DIALOG_H
|
||||||
#define COURT_MONITOR_DIALOG_H
|
#define COURT_MONITOR_DIALOG_H
|
||||||
|
|
||||||
#include <banana/agent/beast.hpp>
|
|
||||||
#include <banana/types_fwd.hpp>
|
#include <banana/types_fwd.hpp>
|
||||||
#include <banana/utils/basic_types.hpp>
|
#include <banana/utils/basic_types.hpp>
|
||||||
|
|
||||||
class Dialog
|
class Dialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
Dialog(banana::agent::beast_callback& agent, banana::integer_t userId, const char* name);
|
Dialog(banana::integer_t userId, const char* name);
|
||||||
virtual ~Dialog();
|
virtual ~Dialog();
|
||||||
|
|
||||||
[[nodiscard]] banana::agent::beast_callback& getAgent() const;
|
|
||||||
[[nodiscard]] banana::integer_t getUserId() const;
|
|
||||||
|
|
||||||
// Возвращают true, если диалог завершен.
|
// Возвращают true, если диалог завершен.
|
||||||
virtual bool processMessage(const banana::api::message_t& message) = 0;
|
virtual bool processMessage(const banana::api::message_t& message) = 0;
|
||||||
virtual bool processCallbackQuery(const banana::api::callback_query_t& query) = 0;
|
virtual bool processCallbackQuery(const banana::api::callback_query_t& query) = 0;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
banana::agent::beast_callback& agent_;
|
|
||||||
banana::integer_t userId_;
|
banana::integer_t userId_;
|
||||||
const char* name_;
|
const char* name_;
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
#define COURT_MONITOR_DIALOG_HELPERS_H
|
#define COURT_MONITOR_DIALOG_HELPERS_H
|
||||||
|
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
#include "Storage.h"
|
||||||
|
|
||||||
#include <banana/types.hpp>
|
#include <banana/types.hpp>
|
||||||
|
|
||||||
|
|
@ -13,8 +14,13 @@ namespace statechart = boost::statechart;
|
||||||
template <class MostDerived, class InitialState>
|
template <class MostDerived, class InitialState>
|
||||||
struct StateMachine : public statechart::state_machine<MostDerived, InitialState>
|
struct StateMachine : public statechart::state_machine<MostDerived, InitialState>
|
||||||
{
|
{
|
||||||
explicit StateMachine(Dialog& dialog) : dialog(dialog) {}
|
explicit StateMachine(banana::agent::beast_callback& agent, banana::integer_t userId)
|
||||||
Dialog& dialog;
|
: agent(agent), userId(userId)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
banana::agent::beast_callback& agent;
|
||||||
|
banana::integer_t userId;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct BasicState
|
struct BasicState
|
||||||
|
|
|
||||||
31
ISRG_X1.pem
Normal file
31
ISRG_X1.pem
Normal file
|
|
@ -0,0 +1,31 @@
|
||||||
|
-----BEGIN CERTIFICATE-----
|
||||||
|
MIIFazCCA1OgAwIBAgIRAIIQz7DSQONZRGPgu2OCiwAwDQYJKoZIhvcNAQELBQAw
|
||||||
|
TzELMAkGA1UEBhMCVVMxKTAnBgNVBAoTIEludGVybmV0IFNlY3VyaXR5IFJlc2Vh
|
||||||
|
cmNoIEdyb3VwMRUwEwYDVQQDEwxJU1JHIFJvb3QgWDEwHhcNMTUwNjA0MTEwNDM4
|
||||||
|
WhcNMzUwNjA0MTEwNDM4WjBPMQswCQYDVQQGEwJVUzEpMCcGA1UEChMgSW50ZXJu
|
||||||
|
ZXQgU2VjdXJpdHkgUmVzZWFyY2ggR3JvdXAxFTATBgNVBAMTDElTUkcgUm9vdCBY
|
||||||
|
MTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAK3oJHP0FDfzm54rVygc
|
||||||
|
h77ct984kIxuPOZXoHj3dcKi/vVqbvYATyjb3miGbESTtrFj/RQSa78f0uoxmyF+
|
||||||
|
0TM8ukj13Xnfs7j/EvEhmkvBioZxaUpmZmyPfjxwv60pIgbz5MDmgK7iS4+3mX6U
|
||||||
|
A5/TR5d8mUgjU+g4rk8Kb4Mu0UlXjIB0ttov0DiNewNwIRt18jA8+o+u3dpjq+sW
|
||||||
|
T8KOEUt+zwvo/7V3LvSye0rgTBIlDHCNAymg4VMk7BPZ7hm/ELNKjD+Jo2FR3qyH
|
||||||
|
B5T0Y3HsLuJvW5iB4YlcNHlsdu87kGJ55tukmi8mxdAQ4Q7e2RCOFvu396j3x+UC
|
||||||
|
B5iPNgiV5+I3lg02dZ77DnKxHZu8A/lJBdiB3QW0KtZB6awBdpUKD9jf1b0SHzUv
|
||||||
|
KBds0pjBqAlkd25HN7rOrFleaJ1/ctaJxQZBKT5ZPt0m9STJEadao0xAH0ahmbWn
|
||||||
|
OlFuhjuefXKnEgV4We0+UXgVCwOPjdAvBbI+e0ocS3MFEvzG6uBQE3xDk3SzynTn
|
||||||
|
jh8BCNAw1FtxNrQHusEwMFxIt4I7mKZ9YIqioymCzLq9gwQbooMDQaHWBfEbwrbw
|
||||||
|
qHyGO0aoSCqI3Haadr8faqU9GY/rOPNk3sgrDQoo//fb4hVC1CLQJ13hef4Y53CI
|
||||||
|
rU7m2Ys6xt0nUW7/vGT1M0NPAgMBAAGjQjBAMA4GA1UdDwEB/wQEAwIBBjAPBgNV
|
||||||
|
HRMBAf8EBTADAQH/MB0GA1UdDgQWBBR5tFnme7bl5AFzgAiIyBpY9umbbjANBgkq
|
||||||
|
hkiG9w0BAQsFAAOCAgEAVR9YqbyyqFDQDLHYGmkgJykIrGF1XIpu+ILlaS/V9lZL
|
||||||
|
ubhzEFnTIZd+50xx+7LSYK05qAvqFyFWhfFQDlnrzuBZ6brJFe+GnY+EgPbk6ZGQ
|
||||||
|
3BebYhtF8GaV0nxvwuo77x/Py9auJ/GpsMiu/X1+mvoiBOv/2X/qkSsisRcOj/KK
|
||||||
|
NFtY2PwByVS5uCbMiogziUwthDyC3+6WVwW6LLv3xLfHTjuCvjHIInNzktHCgKQ5
|
||||||
|
ORAzI4JMPJ+GslWYHb4phowim57iaztXOoJwTdwJx4nLCgdNbOhdjsnvzqvHu7Ur
|
||||||
|
TkXWStAmzOVyyghqpZXjFaH3pO3JLF+l+/+sKAIuvtd7u+Nxe5AW0wdeRlN8NwdC
|
||||||
|
jNPElpzVmbUq4JUagEiuTDkHzsxHpFKVK7q4+63SM1N95R1NbdWhscdCb+ZAJzVc
|
||||||
|
oyi3B43njTOQ5yOf+1CceWxG1bQVs5ZufpsMljq4Ui0/1lvh+wjChP4kqKOJ2qxq
|
||||||
|
4RgqsahDYVvTH9w7jXbyLeiNdd8XM2w9U/t7y0Ff/9yi0GE44Za4rF2LN9d11TPA
|
||||||
|
mRGunUHBcnWEvgJBQl9nJEiU0Zsnvgc/ubhPgXRR4Xq37Z0j4r7g1SgEEzwxA57d
|
||||||
|
emyPxgcYxn/eR44/KJ4EBs+lVDR3veyJm+kXQ99b21/+jh5Xos1AnX5iItreGCc=
|
||||||
|
-----END CERTIFICATE-----
|
||||||
|
|
@ -5,10 +5,13 @@
|
||||||
#include "Logger.h"
|
#include "Logger.h"
|
||||||
|
|
||||||
#include <banana/api.hpp>
|
#include <banana/api.hpp>
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
|
||||||
#include <boost/statechart/custom_reaction.hpp>
|
#include <boost/statechart/custom_reaction.hpp>
|
||||||
#include <boost/statechart/event.hpp>
|
#include <boost/statechart/event.hpp>
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
// clang-format off
|
// clang-format off
|
||||||
|
|
@ -17,6 +20,7 @@ namespace {
|
||||||
struct WaitingForInput;
|
struct WaitingForInput;
|
||||||
struct GettingCaseDetails;
|
struct GettingCaseDetails;
|
||||||
struct WaitingForConfirmation;
|
struct WaitingForConfirmation;
|
||||||
|
struct Subscribed;
|
||||||
|
|
||||||
// События
|
// События
|
||||||
struct CaseDetailsFetched : statechart::event<CaseDetailsFetched> { };
|
struct CaseDetailsFetched : statechart::event<CaseDetailsFetched> { };
|
||||||
|
|
@ -28,7 +32,12 @@ struct SubscriptionConfirmed : statechart::event<SubscriptionConfirmed> { };
|
||||||
|
|
||||||
struct SubscribeCaseStateMachine : StateMachine<SubscribeCaseStateMachine, WaitingForInput>
|
struct SubscribeCaseStateMachine : StateMachine<SubscribeCaseStateMachine, WaitingForInput>
|
||||||
{
|
{
|
||||||
using StateMachine::StateMachine;
|
SubscribeCaseStateMachine(banana::agent::beast_callback& agent, long userId, LocalStorage& storage)
|
||||||
|
: StateMachine(agent, userId), storage(storage)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
LocalStorage& storage;
|
||||||
|
std::string caseNumber;
|
||||||
};
|
};
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
@ -39,24 +48,122 @@ struct WaitingForInput : State<WaitingForInput, SubscribeCaseStateMachine>
|
||||||
|
|
||||||
explicit WaitingForInput(const my_context& ctx) : State(ctx, "WaitingForInput")
|
explicit WaitingForInput(const my_context& ctx) : State(ctx, "WaitingForInput")
|
||||||
{
|
{
|
||||||
auto& dialog = context<SubscribeCaseStateMachine>().dialog;
|
auto& machine = context<SubscribeCaseStateMachine>();
|
||||||
std::string text = "Введите номер дела";
|
std::string text = "Введите номер дела...";
|
||||||
banana::api::send_message(dialog.getAgent(),
|
banana::api::send_message(machine.agent,
|
||||||
{.chat_id = dialog.getUserId(), .text = std::move(text)},
|
{.chat_id = machine.userId, .text = std::move(text)}, [](auto) {});
|
||||||
[](auto) {});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
statechart::result react(const NewMessageEvent& event) { return transit<GettingCaseDetails>(); }
|
statechart::result react(const NewMessageEvent& event)
|
||||||
|
{
|
||||||
|
auto& machine = context<SubscribeCaseStateMachine>();
|
||||||
|
const std::regex rex(R"(\d-(?:\d+)/(?:\d){4}-(?:\d+))");
|
||||||
|
std::smatch captures;
|
||||||
|
if (std::regex_match(*event.message.text, captures, rex))
|
||||||
|
{
|
||||||
|
machine.caseNumber = *event.message.text;
|
||||||
|
return transit<GettingCaseDetails>();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
std::string text =
|
||||||
|
"Некорректный формат номера дела!\n"
|
||||||
|
"Попробуйте еще раз.";
|
||||||
|
banana::api::send_message(
|
||||||
|
machine.agent, {.chat_id = machine.userId, .text = std::move(text)}, [](auto) {});
|
||||||
|
return discard_event();
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct GettingCaseDetails : State<GettingCaseDetails, SubscribeCaseStateMachine, true>
|
struct GettingCaseDetails : State<GettingCaseDetails, SubscribeCaseStateMachine>
|
||||||
{
|
{
|
||||||
explicit GettingCaseDetails(const my_context& ctx) : State(ctx, "GettingCaseDetails") {}
|
using reactions = statechart::custom_reaction<CaseDetailsFetched>;
|
||||||
|
|
||||||
|
explicit GettingCaseDetails(const my_context& ctx) : State(ctx, "GettingCaseDetails")
|
||||||
|
{
|
||||||
|
auto& machine = context<SubscribeCaseStateMachine>();
|
||||||
|
boost::asio::io_context ioContext;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
auto details = getCaseDetails(ioContext, machine.caseNumber);
|
||||||
|
std::string text;
|
||||||
|
fmt::format_to(std::back_inserter(text), "Проверьте информацию:\n{}\n", details.name);
|
||||||
|
for (const auto& participant : details.participants)
|
||||||
|
fmt::format_to(std::back_inserter(text), "{}: {}\n", participant.title,
|
||||||
|
participant.name);
|
||||||
|
fmt::format_to(std::back_inserter(text), "Судья: {}", details.judgeName);
|
||||||
|
|
||||||
|
banana::api::inline_keyboard_markup_t keyboard;
|
||||||
|
keyboard.inline_keyboard.resize(1);
|
||||||
|
keyboard.inline_keyboard[0].resize(2);
|
||||||
|
|
||||||
|
keyboard.inline_keyboard[0][0].text = "Верно";
|
||||||
|
keyboard.inline_keyboard[0][0].callback_data = "yes";
|
||||||
|
keyboard.inline_keyboard[0][1].text = "Отмена";
|
||||||
|
keyboard.inline_keyboard[0][1].callback_data = "no";
|
||||||
|
|
||||||
|
banana::api::send_message(
|
||||||
|
machine.agent,
|
||||||
|
{.chat_id = machine.userId, .text = std::move(text), .reply_markup = keyboard},
|
||||||
|
[](auto) {});
|
||||||
|
|
||||||
|
post_event(CaseDetailsFetched());
|
||||||
|
}
|
||||||
|
catch (const std::exception& e)
|
||||||
|
{
|
||||||
|
LOGE(dialog, e.what());
|
||||||
|
// TODO ???
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
statechart::result react(const CaseDetailsFetched& event)
|
||||||
|
{
|
||||||
|
return transit<WaitingForConfirmation>();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
struct WaitingForConfirmation : State<WaitingForConfirmation, SubscribeCaseStateMachine>
|
struct WaitingForConfirmation : State<WaitingForConfirmation, SubscribeCaseStateMachine>
|
||||||
{
|
{
|
||||||
|
using reactions = statechart::custom_reaction<NewCallbackQueryEvent>;
|
||||||
|
|
||||||
explicit WaitingForConfirmation(const my_context& ctx) : State(ctx, "WaitingForConfirmation") {}
|
explicit WaitingForConfirmation(const my_context& ctx) : State(ctx, "WaitingForConfirmation") {}
|
||||||
|
|
||||||
|
statechart::result react(const NewCallbackQueryEvent& event)
|
||||||
|
{
|
||||||
|
auto& machine = context<SubscribeCaseStateMachine>();
|
||||||
|
if (event.query.data)
|
||||||
|
{
|
||||||
|
if (event.query.message)
|
||||||
|
banana::api::edit_message_reply_markup(
|
||||||
|
machine.agent,
|
||||||
|
{.chat_id = event.query.message->chat.id,
|
||||||
|
.message_id = event.query.message->message_id},
|
||||||
|
[](banana::expected<banana::variant_t<banana::api::message_t, banana::boolean_t>> result)
|
||||||
|
{
|
||||||
|
if (!result)
|
||||||
|
LOGE(dialog, result.error());
|
||||||
|
});
|
||||||
|
|
||||||
|
if (*event.query.data == "yes")
|
||||||
|
{
|
||||||
|
// TODO
|
||||||
|
|
||||||
|
return transit<Subscribed>();
|
||||||
|
}
|
||||||
|
else if (*event.query.data == "no")
|
||||||
|
{
|
||||||
|
return transit<WaitingForInput>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return discard_event();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Subscribed : State<Subscribed, SubscribeCaseStateMachine, true>
|
||||||
|
{
|
||||||
|
explicit Subscribed(const my_context& ctx) : State(ctx, "Subscribed") {}
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
@ -64,9 +171,10 @@ struct WaitingForConfirmation : State<WaitingForConfirmation, SubscribeCaseState
|
||||||
/////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
SubscribeCaseDialog::SubscribeCaseDialog(banana::agent::beast_callback& agent,
|
SubscribeCaseDialog::SubscribeCaseDialog(banana::agent::beast_callback& agent,
|
||||||
banana::integer_t userId)
|
banana::integer_t userId,
|
||||||
: Dialog(agent, userId, "SubscribeCase"),
|
LocalStorage& storage)
|
||||||
machine_(std::make_unique<SubscribeCaseStateMachine>(*this))
|
: Dialog(userId, "SubscribeCase"),
|
||||||
|
machine_(std::make_unique<SubscribeCaseStateMachine>(agent, userId, storage))
|
||||||
{
|
{
|
||||||
machine_->initiate();
|
machine_->initiate();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,18 @@
|
||||||
#define COURT_MONITOR_SUBSCRIBE_CASE_DIALOG_H
|
#define COURT_MONITOR_SUBSCRIBE_CASE_DIALOG_H
|
||||||
|
|
||||||
#include "Dialog.h"
|
#include "Dialog.h"
|
||||||
|
#include "Storage.h"
|
||||||
|
|
||||||
|
#include <banana/agent/beast.hpp>
|
||||||
|
|
||||||
struct SubscribeCaseStateMachine;
|
struct SubscribeCaseStateMachine;
|
||||||
|
|
||||||
class SubscribeCaseDialog : public Dialog
|
class SubscribeCaseDialog : public Dialog
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
SubscribeCaseDialog(banana::agent::beast_callback& agent, banana::integer_t userId);
|
SubscribeCaseDialog(banana::agent::beast_callback& agent,
|
||||||
|
banana::integer_t userId,
|
||||||
|
LocalStorage& storage);
|
||||||
~SubscribeCaseDialog() override;
|
~SubscribeCaseDialog() override;
|
||||||
|
|
||||||
bool processMessage(const banana::api::message_t& message) override;
|
bool processMessage(const banana::api::message_t& message) override;
|
||||||
|
|
|
||||||
13
main.cpp
13
main.cpp
|
|
@ -27,14 +27,11 @@ void processAllSubscriptions(LocalStorage& storage, Bot& bot)
|
||||||
for (auto& counter : subscription.counters)
|
for (auto& counter : subscription.counters)
|
||||||
{
|
{
|
||||||
LOG(main, "** Processing case {}", counter.caseNumber);
|
LOG(main, "** Processing case {}", counter.caseNumber);
|
||||||
auto details = getCaseDetails(asioContext, counter.courtId, counter.caseNumber);
|
auto details = getCaseDetails(asioContext, counter.caseNumber);
|
||||||
LOG(main, details.dump());
|
for (std::size_t i = counter.value; i < details.history.size(); i++)
|
||||||
auto url = details["url"].get<std::string>();
|
bot.notifyUser(subscription.userId, counter.caseNumber, details.url,
|
||||||
|
details.history[i]);
|
||||||
auto history = parseHistory(details);
|
counter.value = details.history.size();
|
||||||
for (std::size_t i = counter.value; i < history.size(); i++)
|
|
||||||
bot.notifyUser(subscription.userId, counter.caseNumber, url, history[i]);
|
|
||||||
counter.value = history.size();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (const std::exception& e)
|
catch (const std::exception& e)
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue