Files
nostrlib/khatru/docs/cookbook/custom-stores.md
T
2025-05-03 11:56:38 -03:00

83 lines
2.3 KiB
Markdown

---
outline: deep
---
# Generating events on the fly from a non-Nostr data-source
Suppose you want to serve events with the weather data for periods in the past. All you have is a big CSV file with the data.
Then you get a query like `{"#g": ["d6nvp"], "since": 1664074800, "until": 1666666800, "kind": 10774}`, imagine for a while that kind `10774` means weather data.
First you do some geohashing calculation to discover that `d6nvp` corresponds to Willemstad, Curaçao, then you query your XML file for the Curaçao weather data for the given period -- from `2022-09-25` to `2022-10-25`, then you return the events corresponding to such query, signed on the fly:
```go
func main () {
// other stuff here
relay := khatru.NewRelay()
relay.QueryStored = handleWeatherQuery
// other stuff here
}
func handleWeatherQuery(ctx context.Context, filter nostr.Filter) iter.Seq[nostr.Event] {
if filter.Kind != nostr.Kind(10774) {
// this function only handles kind 10774, if the query is for something else we return
// a nil channel, which corresponds to no results
return nil
}
file, err := os.Open("weatherdata.xml")
if err != nil {
return nil
}
return func(yield func(nostr.Event) bool) {
defer file.Close()
// we're going to do this for each tag in the filter
gTags, _ := filter.Tags["g"]
for _, gTag := range gTags {
// translate geohash into city name
citName, err := geohashToCityName(gTag)
if err != nil {
continue
}
reader := csv.NewReader(file)
for {
record, err := reader.Read()
if err != nil {
return
}
// ensure we're only getting records for Willemstad
if cityName != record[0] {
continue
}
date, _ := time.Parse("2006-01-02", record[1])
ts := nostr.Timestamp(date.Unix())
if ts > filter.Since && ts < filter.Until {
// we found a record that matches the filter, so we make
// an event on the fly and return it
evt := nostr.Event{
CreatedAt: ts,
Kind: 10774,
Tags: nostr.Tags{
{"temperature", record[2]},
{"condition", record[3]},
}
}
evt.Sign(global.RelaySecretKey)
ch <- evt
}
}
}
}()
return ch, nil
}
}
```
Beware, the code above is inefficient and the entire approach is not very smart, it's meant just as an example.