#pragma once #include #include #include #include #include #include #include #include #include "data/data.hpp" #include "client/web_socket_client.hpp" namespace nostr { namespace service { class INostrServiceBase { public: virtual ~INostrServiceBase() = default; /** * @brief Opens connections to the default Nostr relays of the instance, as specified in * the constructor. * @return A list of the relay URLs to which connections were successfully opened. */ virtual std::vector openRelayConnections() = 0; /** * @brief Opens connections to the specified Nostr relays. * @returns A list of the relay URLs to which connections were successfully opened. */ virtual std::vector openRelayConnections(std::vector relays) = 0; /** * @brief Closes all open relay connections. */ virtual void closeRelayConnections() = 0; /** * @brief Closes any open connections to the specified Nostr relays. */ virtual void closeRelayConnections(std::vector relays) = 0; /** * @brief Publishes a Nostr event to all open relay connections. * @returns A tuple of `std::vector` objects, of the form ``, indicating * to which relays the event was published successfully, and to which relays the event failed * to publish. */ virtual std::tuple, std::vector> publishEvent( std::shared_ptr event) = 0; /** * @brief Queries all open relay connections for events matching the given set of filters, and * returns all stored matching events returned by the relays. * @param filters The filters to use for the query. * @returns A vector of all events matching the filters from all open relay connections. * @remark This method runs until the relays send an EOSE message, indicating they have no more * stored events matching the given filters. When the EOSE message is received, the method * will close the subscription for each relay and return the received events. * @remark Use this method to fetch a batch of events from the relays. A `limit` value must be * set on the filters in the range 1-64, inclusive. If no valid limit is given, it will be * defaulted to 16. */ virtual std::vector> queryRelays( std::shared_ptr filters) = 0; /** * @brief Queries all open relay connections for events matching the given set of filters. * @param filters The filters to use for the query. * @param eventHandler A callable object that will be invoked each time the client receives * an event matching the filters. * @param eoseHandler A callable object that will be invoked when the relay sends an EOSE * message. * @param closeHandler A callable object that will be invoked when the relay sends a CLOSE * message. * @returns The ID of the subscription created for the query. * @remark By providing a response handler, the caller assumes responsibility for handling all * events returned from the relay for the given filters. The service will not store the * events, and they will not be accessible via `getNewEvents`. */ virtual std::string queryRelays( std::shared_ptr filters, std::function)> eventHandler, std::function eoseHandler, std::function closeHandler) = 0; /** * @brief Closes the subscription with the given ID on all open relay connections. * @returns A tuple of `std::vector` objects, of the form ``, indicating * to which relays the message was sent successfully, and which relays failed to receive the * message. */ virtual std::tuple, std::vector> closeSubscription( std::string subscriptionId) = 0; /** * @brief Closes the subscription with the given ID on the given relay. * @returns True if the relay received the CLOSE message, false otherwise. * @remark If the subscription does not exist on the given relay, or if the relay is not * connected, the method will do nothing and return false. */ virtual bool closeSubscription(std::string subscriptionId, std::string relay) = 0; /** * @brief Closes all open subscriptions on all open relay connections. * @returns A list of any subscription IDs that failed to close. */ virtual std::vector closeSubscriptions() = 0; }; class NostrServiceBase : public INostrServiceBase { public: NostrServiceBase( std::shared_ptr appender, std::shared_ptr client); NostrServiceBase( std::shared_ptr appender, std::shared_ptr client, std::vector relays); ~NostrServiceBase() override; std::vector defaultRelays() const; std::vector activeRelays() const; std::unordered_map> subscriptions() const; std::vector openRelayConnections() override; std::vector openRelayConnections(std::vector relays) override; void closeRelayConnections() override; void closeRelayConnections(std::vector relays) override; // TODO: Make this method return a promise. std::tuple, std::vector> publishEvent( std::shared_ptr event) override; // TODO: Make this method return a promise. // TODO: Add a timeout to this method to prevent hanging while waiting for the relay. std::vector> queryRelays( std::shared_ptr filters) override; std::string queryRelays( std::shared_ptr filters, std::function)> eventHandler, std::function eoseHandler, std::function closeHandler) override; std::tuple, std::vector> closeSubscription( std::string subscriptionId) override; bool closeSubscription(std::string subscriptionId, std::string relay) override; std::vector closeSubscriptions() override; private: ///< The maximum number of events the service will store for each subscription. const int MAX_EVENTS_PER_SUBSCRIPTION = 128; ///< The WebSocket client used to communicate with relays. std::shared_ptr _client; ///< A mutex to protect the instance properties. std::mutex _propertyMutex; ///< The default set of Nostr relays to which the service will attempt to connect. std::vector _defaultRelays; ///< The set of Nostr relays to which the service is currently connected. std::vector _activeRelays; ///< A map from subscription IDs to the relays on which each subscription is open. std::unordered_map> _subscriptions; std::vector _getConnectedRelays(std::vector relays); std::vector _getUnconnectedRelays(std::vector relays); bool _isConnected(std::string relay); void _eraseActiveRelay(std::string relay); void _connect(std::string relay); void _disconnect(std::string relay); std::string _generateSubscriptionId(); std::string _generateCloseRequest(std::string subscriptionId); bool _hasSubscription(std::string subscriptionId); bool _hasSubscription(std::string subscriptionId, std::string relay); void _onSubscriptionMessage( std::string message, std::function)> eventHandler, std::function eoseHandler, std::function closeHandler); void _onAcceptance(std::string message, std::function acceptanceHandler); }; } // namespace service } // namespace nostr