#pragma once #include #include "platform/counted_client.h" #include #include #include #include #include "event_bus.h" #include "irc_config.h" #include "irc_session.h" #include "reconnect_policy.h" #include "service.h" #include "services/clock_service.h" #include "services/storage_service.h" #include "services/wifi_service.h" namespace roro { // Keeps the IRC connection alive in the background once started (see IRC Service in CONTEXT.md): // connects over TLS while Wi-Fi is Connected, pauses while Monitoring, reconnects with backoff, // writes Logs and raises Notifications for Mentions. Networking runs on its own task; the // session is shared with the IRC App under a lock. class IrcService : public Service { public: enum class Status { Stopped, WaitingForWifi, Connecting, Registering, Online, Paused, Retrying }; IrcService(KeyValueStore& store, const std::string& defaultNick, WifiService& wifi, StorageService& storage, ClockService& clock, EventBus& bus); const char* name() const override { return "irc"; } void start() override; // Starts the session (the IRC App does this when opened, unless you stopped it). void connect(); // Stops it, from any state: says QUIT if connected, and stops retrying. /quit, `irc stop`. void disconnect(); bool running() const { return wanted_; } bool stoppedByUser() const { return stoppedByUser_; } Status status() const { return status_; } // Read or act on the session while holding its lock: withSession([](IrcSession& s) { ... }). template auto withSession(F f) { Lock l(lock_); return f(*session_); } int totalUnread(); // A copy of the server settings to edit; applyConfig() validates, saves and, if running, // reconnects with it (a fresh session). IrcConfig draftConfig(); std::string applyConfig(const IrcConfig& draft); private: struct Lock { explicit Lock(SemaphoreHandle_t m) : m_(m) { xSemaphoreTake(m_, portMAX_DELAY); } ~Lock() { xSemaphoreGive(m_); } SemaphoreHandle_t m_; }; static void taskEntry(void* self); void loop(); bool open(); void close(const std::string& reason); void readLines(); void flushEffects(); void scheduleRetry(const std::string& why); IrcConfig config_; WifiService& wifi_; StorageService& storage_; ClockService& clock_; EventBus& bus_; std::unique_ptr session_; SemaphoreHandle_t lock_ = nullptr; TaskHandle_t task_ = nullptr; Counted tlsClient_{net::User::Irc}; // counted for the System App (S1, Q121) Counted plainClient_{net::User::Irc}; NetworkClient* conn_ = &tlsClient_; // whichever the config asks for ReconnectPolicy backoff_; std::string partial_; volatile bool wanted_ = false; volatile bool stoppedByUser_ = false; // a manual stop: opening the App doesn't reconnect volatile bool stopRequested_ = false; bool quitSent_ = false; // /quit already queued its QUIT volatile bool restart_ = false; volatile Status status_ = Status::Stopped; bool open_ = false; uint32_t retryAtMs_ = 0; uint32_t lastRxMs_ = 0; bool pingSent_ = false; }; } // namespace roro