aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt1
-rw-r--r--include/nostr.hpp33
-rw-r--r--src/filters.cpp74
3 files changed, 108 insertions, 0 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3d472c1..4e46b70 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -22,6 +22,7 @@ set(SOURCE_DIR ./src)
set(CLIENT_SOURCE_DIR ./src/client)
set(SOURCES
${SOURCE_DIR}/event.cpp
+ ${SOURCE_DIR}/filters.cpp
${SOURCE_DIR}/nostr_service.cpp
${CLIENT_SOURCE_DIR}/websocketpp_client.cpp
)
diff --git a/include/nostr.hpp b/include/nostr.hpp
index ec8d1a8..ce25446 100644
--- a/include/nostr.hpp
+++ b/include/nostr.hpp
@@ -15,6 +15,7 @@
namespace nostr
{
typedef std::vector<std::string> RelayList;
+typedef std::unordered_map<std::string, std::vector<std::string>> TagMap;
// TODO: Add null checking to seralization and deserialization methods.
/**
@@ -55,6 +56,38 @@ private:
void validate();
};
+/**
+ * @brief A set of filters for querying Nostr relays.
+ * @remark The `limit` field should always be included to keep the response size reasonable. The
+ * `since` field is not required, and the `until` field will default to the present. At least one
+ * of the other fields must be set for a valid filter.
+ */
+struct Filters
+{
+ std::vector<std::string> ids; ///< Event IDs.
+ std::vector<std::string> authors; ///< Event author npubs.
+ std::vector<int> kinds; ///< Kind numbers.
+ TagMap tags; ///< Tag names mapped to lists of tag values.
+ std::time_t since; ///< Unix timestamp. Matching events must be newer than this.
+ std::time_t until; ///< Unix timestamp. Matching events must be older than this.
+ int limit; ///< The maximum number of events the relay should return on the initial query.
+
+ /**
+ * @brief Serializes the filters to a JSON object.
+ * @returns A stringified JSON object representing the filters.
+ * @throws `std::invalid_argument` if the filter object is invalid.
+ */
+ std::string serialize();
+
+private:
+ /**
+ * @brief Validates the filters.
+ * @throws `std::invalid_argument` if the filter object is invalid.
+ * @remark The `until` field defaults to the present if it is not already set.
+ */
+ void validate();
+};
+
class NostrService
{
public:
diff --git a/src/filters.cpp b/src/filters.cpp
new file mode 100644
index 0000000..78f3ce4
--- /dev/null
+++ b/src/filters.cpp
@@ -0,0 +1,74 @@
+#include <ctime>
+#include <sstream>
+#include <string>
+#include <nlohmann/json.hpp>
+
+#include "nostr.hpp"
+
+using nlohmann::json;
+using std::invalid_argument;
+using std::stringstream;
+using std::string;
+using std::time;
+
+namespace nostr
+{
+string Filters::serialize()
+{
+ try
+ {
+ this->validate();
+ }
+ catch (const invalid_argument& e)
+ {
+ throw e;
+ }
+
+ json j = {
+ {"ids", this->ids},
+ {"authors", this->authors},
+ {"kinds", this->kinds},
+ {"since", this->since},
+ {"until", this->until},
+ {"limit", this->limit}
+ };
+
+ for (auto& tag : this->tags)
+ {
+ stringstream jss;
+ jss << "#" << tag.first;
+ string js = jss.str();
+
+ j[js] = tag.second;
+ }
+
+ return j.dump();
+};
+
+void Filters::validate()
+{
+ bool hasLimit = this->limit > 0;
+ if (!hasLimit)
+ {
+ throw invalid_argument("Filters::validate: The limit must be greater than 0.");
+ }
+
+ bool hasUntil = this->until > 0;
+ if (!hasUntil)
+ {
+ this->until = time(nullptr);
+ }
+
+ bool hasIds = this->ids.size() > 0;
+ bool hasAuthors = this->authors.size() > 0;
+ bool hasKinds = this->kinds.size() > 0;
+ bool hasTags = this->tags.size() > 0;
+
+ bool hasFilter = hasIds || hasAuthors || hasKinds || hasTags;
+
+ if (!hasFilter)
+ {
+ throw invalid_argument("Filters::validate: At least one filter must be set.");
+ }
+};
+} // namespace nostr