#include "application.hpp"
#include <cerrno>
#include <csignal>
#include <ctime>
#include <cstring>
#include <pthread.h>
#include <spdlog/spdlog.h>
#include "service_error.hpp"
namespace telegrammer
{
Application::Application(ApplicationConfig config)
: database_lock_(std::move(config.database_lock)),
listen_info_(config.listen_info), db_path_(std::move(config.db_path)),
keys_(db_path_),
subscriptions_(db_path_), deliveries_(db_path_),
telegram_(std::move(config.token)), dispatcher_(deliveries_),
poller_(telegram_, deliveries_, dispatcher_, state_, 0),
server_(config.listen_info, keys_, subscriptions_, deliveries_,
telegram_, state_)
{}
void Application::requestStop()
{
if(stopping_.exchange(true))
{
return;
}
server_.stop();
poller_.stop();
dispatcher_.stop();
}
void Application::waitForSignal(std::stop_token stop_token)
{
sigset_t signals;
sigemptyset(&signals);
sigaddset(&signals, SIGINT);
sigaddset(&signals, SIGTERM);
while(!stop_token.stop_requested())
{
timespec timeout{1, 0};
int signal = sigtimedwait(&signals, nullptr, &timeout);
if(signal == SIGINT || signal == SIGTERM)
{
requestStop();
return;
}
if(signal < 0 && errno != EAGAIN && errno != EINTR)
{
spdlog::error("Signal wait failed: {}", std::strerror(errno));
requestStop();
return;
}
}
}
int Application::run()
{
sigset_t signals;
sigemptyset(&signals);
sigaddset(&signals, SIGINT);
sigaddset(&signals, SIGTERM);
if(pthread_sigmask(SIG_BLOCK, &signals, nullptr) != 0)
{
spdlog::error("Unable to block termination signals");
return 1;
}
auto identity = telegram_.getMe();
if(!identity.has_value())
{
const ServiceError* error = asServiceError(identity.error());
if(error != nullptr)
{
spdlog::error("Unable to validate Telegram bot [{}]: {}",
error->code, error->msg);
}
else
{
spdlog::error("Unable to validate Telegram bot: {}",
mw::errorMsg(identity.error()));
}
return 1;
}
int64_t bot_id = (*identity)["result"]["id"].get<int64_t>();
auto bot_result = deliveries_.ensureBot(bot_id);
if(!bot_result.has_value())
{
spdlog::error("Unable to configure bot state: {}",
mw::errorMsg(bot_result.error()));
return 1;
}
auto reset_result = deliveries_.resetInFlight();
if(!reset_result.has_value())
{
spdlog::error("Unable to recover callback jobs: {}",
mw::errorMsg(reset_result.error()));
return 1;
}
poller_.setBotId(bot_id);
state_.polling_ready = true;
state_.degraded = false;
auto start_result = server_.start();
if(!start_result.has_value())
{
spdlog::error("Failed to start server: {}",
mw::errorMsg(start_result.error()));
return 1;
}
spdlog::info("Server listening on {}:{}", listen_info_.address,
listen_info_.port);
dispatcher_.start();
poller_.start();
signal_watcher_ = std::jthread([this](std::stop_token stop_token)
{
waitForSignal(stop_token);
});
server_.wait();
requestStop();
signal_watcher_.request_stop();
if(signal_watcher_.joinable())
{
signal_watcher_.join();
}
return 0;
}
} // namespace telegrammer