--- 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.QueryEvents = append(relay.QueryEvents, 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.