Nostr client relay
Nostr_client_relay is a Nostr C++ engine that allows to build Nostr applications for command line, desktop or web.
It is available on GitHub
JSON
Nostr uses JSON as data format. JSON is a data-interchange format easy for humans to read and write. JSON is built on two structures:
a collection of name/value pairs, called an object, and an ordered list of values, called an array (or vector). Object vales can be strings, numbers and a boolean value for true or false.
API
Nostr_client_relay allows an easy integration between C++ objects like strings and vectors and Nostr JSON entities like events and filters,
defined in NIP-01 , using the
JSON for modern C++ library.
Data structures
A data structure is a storage that is used to store and organize data. Nostr uses the JSON data structures: arrays and objects with string and integer values.
An array is a collection of like variables that share a single name. Strings are a sequence of characters, used to store human-readable text, like words.
A Nostr event (NIP-01) is defined as
```cpp
class event_t
{
std::string id;
std::string pubkey;
std::time_t created_at;
int kind;
std::vector<std::vector<std::string>> tags;
std::string content;
std::string sig;
};
```
A filter is defined as
```cpp
class filter_t
{
std::vector<std::string> ids;
std::vector<std::string> authors;
std::vector<int> kinds;
std::vector<std::string> e;
std::vector<std::string> p;
std::time_t since;
std::time_t until;
size_t limit;
};
```
Functions
Make request
```cpp
std::string make_request(const std::string& subscription_id, const filter_t& filter);
```
The function make_request generates the JSON REQ to transmit to a relay. It accepts as input parameters a string with a subscription
identifier and a filter. The output (return value of the function) is the JSON as a string.
```cpp
const std::string pubkey("4ea843d54a8fdab39aa45f61f19f3ff79cc19385370f6a272dda81fade0a052b");
std::string subscription_id = "my_id";
nostr::filter_t filter;
filter.authors.push_back(pubkey);
filter.kinds.push_back(1);
filter.limit = 1;
std::string json = nostr::make_request(subscription_id, filter);
```
The following JSON is generated, where the pubkey was inserted as an item in the filter's authors array.
```json
[
"REQ",
"my_id",
{
"authors": [
"4ea843d54a8fdab39aa45f61f19f3ff79cc19385370f6a272dda81fade0a052b"
],
"kinds": [
1
],
"limit": 1
}
]
```
Note: the utility functions json_to_file and from_file can be used to save formatted JSON to a text file and to read it from file.
```cpp
std::string json;
comm::json_to_file("request.json", json);
comm::from_file("request.json", json);
```
Make event
```cpp
std::string make_event(nostr::event_t& ev, const std::optional<std::string>& seckey);
```
The function make_event generates the JSON EVENT to transmit to a relay. It accepts as input parameters an event data structure
and an optional secret key to sign the event. If no key is supplied, a random key is generated. This function generates:
- the public key
- the event ID
- the signature
```cpp
std::optional seckey;
nostr::event_t ev;
ev.content = "hello world";
ev.kind = 1;
std::string json = nostr::make_event(ev, seckey);
```
The following JSON is generated
```json
[
"EVENT",
{
"content": "hello world",
"created_at": 1688543634,
"id": "d4675a05eb2720b44bee08bd7c1131786f2d17ef7c1f35ee69005d5ca3377242",
"kind": 1,
"pubkey": "e7328fe0f6b936457b0d3fdc0e1a264e8ac80e0416f239009345750609fdc0d8",
"sig": "472acc460529a2cf56a4ff45f6726d5aa84ff556635fc56855911ee20f055689c508f05d3c64067e919d4335076e9014f47614cd2e7d5b66ba31d8c19973b21c",
"tags": []
}
]
```
Get message type
```cpp
Type get_message_type(const std::string& json);
```
The function get_message_type returns an identifier for the kind of Nostr message:
```cpp
enum class Type
{
EVENT,
REQ,
CLOSE,
EOSE,
NOTICE,
UNKNOWN
};
```
All Nostr messages are JSON arrays, so the identifier (string "EVENT" at array position zero, the first position) is obtained by inspecting array index positions with
```cpp
nlohmann::json js_message = nlohmann::json::parse(json);
std::string type = js_message.at(0);
if (type.compare("EVENT") == 0)
{
return nostr::Type::EVENT;
}
```
Parse request
```cpp
int parse_request(const std::string& json, std::string& request_id, nostr::filter_t& filter);
```
The function parse_request is used to parse a request. It has as input parameters the JSON REQ as a string,
and a request identifier. The output is a filter on the parameter list with the parsed JSON objects as C++
objects. To read the JSON used above in the function make_request
```cpp
std::string json = R"([
"REQ",
"34E8C71B-C0FB-4D6D-9CBB-694A091D6A2D",
{
"authors": [
"4ea843d54a8fdab39aa45f61f19f3ff79cc19385370f6a272dda81fade0a052b"
],
"kinds": [
1
],
"limit": 1
}
])";
std::string request_id;
nostr::filter_t filter;
nostr::parse_request(json, request_id, filter);
std::cout << request_id << std::endl;
std::cout << filter.authors.at(0) << std::endl;
```
Note: The functions returns an integer value for success (0) or failure (-1). The function success can be tested with
```cpp
if (nostr::parse_request(json, request_id, filter) < 0)
{
std::cout << "something went wrong";
}
```
Parse relay event
```cpp
int parse_relay_event(const std::string& json, std::string& event_id, nostr::event_t& ev);
```
The function parse_relay_event is used to parse an event sent by a relay.
```cpp
std::string json = R"([
"EVENT",
"34E8C71B-C0FB-4D6D-9CBB-694A091D6A2D",
{
"content": "https://github.com/pedro-vicente/nostr_client_relay",
"created_at": 1686887953,
"id": "24d7deac8173f5e7ead51282106728ae39d44aa558ff3c5b3b236bece71684ef",
"kind": 1,
"pubkey": "4ea843d54a8fdab39aa45f61f19f3ff79cc19385370f6a272dda81fade0a052b",
"sig": "14d1b7ea0cd8f4275221c297d0d30f5afe2a87a13b743211ed23bcb5dac095498b2b34f645544e3c956577d6554d812fbf908ccb7857b2e920875046579a105c",
"tags": []
}
])";
std::string event_id;
nostr::event_t ev;
nostr::parse_relay_event(json, event_id, ev);
std::cout << event_id << std::endl;
std::cout << ev.content << std::endl;
```
Relay to
```cpp
relay_to(const std::string& uri, const std::string& json, std::vector<std::string>&& store);
```
The function relay_to is used to send a Nostr message (REQ or EVENT) to a relay and return the responses in a array of strings.
```cpp
std::string json = R"([
"REQ",
"34E8C71B-C0FB-4D6D-9CBB-694A091D6A2D",
{
"authors": [
"4ea843d54a8fdab39aa45f61f19f3ff79cc19385370f6a272dda81fade0a052b"
],
"kinds": [
1
],
"limit": 1
}
])";
std::string uri = "nos.lol";
std::vector<std::string>& response;
nostr::relay_to(uri, json, response);
comm::to_file("response.txt", response);
for (int idx = 0; idx < response.size(); idx++)
{
std::string message = response.at(idx);
std::cout << message << std::endl;
}
```
The above call returned these 2 messages
```json
["EVENT","34E8C71B-C0FB-4D6D-9CBB-694A091D6A2D",{"content":"API version 1 released. Nostr_client_relay is a Nostr C++ engine that allows to build Nostr applications for command line, desktop or web. https://pedro-vicente.net/nostro.html","created_at":1688194430,"id":"9d05a7d271e63dd47dcda1f7c7058f1ce4c903fd24dfe6fdfd72034a040a9923","kind":1,"pubkey":"4ea843d54a8fdab39aa45f61f19f3ff79cc19385370f6a272dda81fade0a052b","sig":"c0aeca949da8b444f80009c81120b2d0059516c0c324b0d6cf523dfd1a20f78bdceec0324d0d3f9940d74b146d7e2d45b105dddd38658c4b07ec2b37ec89ff12","tags":[]}]
["EOSE","34E8C71B-C0FB-4D6D-9CBB-694A091D6A2D"]
```
Get follows
```cpp
int get_follows(const std::string& uri, const std::string& pubkey, std::vector<std::string>& response);
```
The function get_follows is a more elaborated higher level function that returns a list of Nostr accounts followed
from a public key input.
```cpp
std::vector<std::string>& response;
std::string uri = "nos.lol";
const std::string pubkey("4ea843d54a8fdab39aa45f61f19f3ff79cc19385370f6a272dda81fade0a052b");
nostr::get_follows(uri, pubkey, response);
comm::to_file("response.txt", response);
for (int idx = 0; idx < response.size(); idx++)
{
std::string message = response.at(idx);
std::cout << message << std::endl;
}
```
The above call returns messages in the form of EVENTS and EOSE
```json
["EVENT","731E4775-80AF-4819-81D6-F21BF3A1B296",{"content":"that needs to be listed on nostr.watch and then it will appear in NADAR.","created_at":1680047175,"id":"e730f566d2d992af7e8efbc1f48d15d316b5b07feb5274d5a2cb75014c538435","kind":1,"pubkey":"c708943ea349519dcf56b2a5c138fd9ed064ad65ddecae6394eabd87a62f1770","sig":"9704b9087879222c492226ab417de9cce436bb18d3022298f0ee1f076b07817022597a051b6a72cfdf68b52be9dcd2a7654b48d2b2b705d1a820444ad114324a","tags":[["e","f6fd01e5d1ba4fc343b27524e4d602f487a800b9c32cd4a0c2521cfdd348eca8","","root"],["e","404e611879f22c07b80c9e387e4c2c777d75af80d73cef15af98cfe52fed736a","","reply"],["p","d030bd233a1347e510c372b1878e00204b228072814361451623707896435da9"],["p","f4f377146e5f784dd0da0814bd95fa42d0ed7ce000d229299a7e8f95c640966f"]]}]
["EOSE","731E4775-80AF-4819-81D6-F21BF3A1B296"]
```