forked from coracle/nostrlib
83 lines
2.3 KiB
Markdown
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.
|