Files
sniper/include/SniperEngine.hpp
2026-08-09 16:51:01 -05:00

51 lines
1.5 KiB
C++

#pragma once
#include <thread>
#include <atomic>
#include <mutex>
#include <vector>
#include <string>
#include <optional>
#include "Config.hpp"
#include "State.hpp"
#include "Scraper.hpp"
#include "DiscordNotifier.hpp"
// Owns the background watch loop (equivalent to python's `bot_loop`) plus
// all the mutating operations the Flask `/api/*` routes used to expose.
// The Discord bot's slash-command handlers call straight into this class.
class SniperEngine {
public:
SniperEngine(AppConfig& config, SniperState& state, DiscordNotifier& notifier);
~SniperEngine();
bool start(std::string* error_out = nullptr);
void stop();
bool isRunning() const;
// Fire-and-forget manual rescan of a single URL (spawns a detached thread,
// matching python's api_scan_now()).
void scanNow(const std::string& url);
bool addLink(LinkConfig link);
bool removeLink(const std::string& url);
bool togglePause(const std::string& url, bool* now_paused_out = nullptr);
std::vector<LinkConfig> links();
int intervalSeconds() const;
void setIntervalSeconds(int seconds);
private:
AppConfig& config_;
SniperState& state_;
DiscordNotifier& notifier_;
Scraper scraper_;
std::mutex links_mtx_;
std::thread worker_;
std::atomic<bool> stop_requested_{false};
void loop();
void runOneScan(const LinkConfig& cfg, bool isManual);
static std::string getChangeText(size_t n, const std::optional<std::vector<std::string>>& old);
};