@@ -3,9 +3,13 @@ package handlers
33import (
44 "encoding/json"
55 "fmt"
6+ "html"
67 "io"
78 "net/http"
89 "net/url"
10+ "time"
11+
12+ "github.com/gorilla/feeds"
913)
1014
1115type LumaEvent struct {
@@ -39,12 +43,10 @@ type EventOutput struct {
3943 Entries []EventItem `json:"entries"`
4044}
4145
42- func lumaFetch ( w http. ResponseWriter , r * http.Request , apiBase string , limit string ) {
46+ func lumaFetchItems ( r * http.Request , apiBase string , limit string ) ([] EventItem , error ) {
4347 cal := r .URL .Query ().Get ("calendar" )
4448 if cal == "" {
45- w .Write ([]byte ("missing calendar query parameter" ))
46-
47- panic ("missing calendar query parameter" )
49+ return nil , fmt .Errorf ("missing calendar query parameter" )
4850 }
4951
5052 u := apiBase + "?" + url.Values {
@@ -55,22 +57,21 @@ func lumaFetch(w http.ResponseWriter, r *http.Request, apiBase string, limit str
5557
5658 resp , err := http .Get (u )
5759 if err != nil {
58- panic ( err )
60+ return nil , err
5961 }
6062 defer resp .Body .Close ()
6163
6264 body , err := io .ReadAll (resp .Body )
6365 if err != nil {
64- panic ( err )
66+ return nil , err
6567 }
6668
6769 var lr LumaResponse
6870 if err := json .Unmarshal (body , & lr ); err != nil {
69- panic ( err )
71+ return nil , err
7072 }
7173
72- output := EventOutput {}
73-
74+ var items []EventItem
7475 for _ , entry := range lr .Entries {
7576 evt := entry .Event
7677
@@ -82,7 +83,7 @@ func lumaFetch(w http.ResponseWriter, r *http.Request, apiBase string, limit str
8283 }
8384 }
8485
85- output . Entries = append (output . Entries , EventItem {
86+ items = append (items , EventItem {
8687 Name : evt .Name ,
8788 StartAt : evt .StartAt ,
8889 URL : evt .URL ,
@@ -91,7 +92,18 @@ func lumaFetch(w http.ResponseWriter, r *http.Request, apiBase string, limit str
9192 })
9293 }
9394
94- j , err := json .Marshal (output )
95+ return items , nil
96+ }
97+
98+ func lumaJSON (w http.ResponseWriter , r * http.Request , apiBase string , limit string ) {
99+ items , err := lumaFetchItems (r , apiBase , limit )
100+ if err != nil {
101+ w .Write ([]byte (err .Error ()))
102+
103+ panic (err )
104+ }
105+
106+ j , err := json .Marshal (EventOutput {Entries : items })
95107 if err != nil {
96108 panic (err )
97109 }
@@ -100,9 +112,81 @@ func lumaFetch(w http.ResponseWriter, r *http.Request, apiBase string, limit str
100112}
101113
102114func NextEventHandler (w http.ResponseWriter , r * http.Request , apiBase string ) {
103- lumaFetch (w , r , apiBase , "1" )
115+ lumaJSON (w , r , apiBase , "1" )
104116}
105117
106118func EventsHandler (w http.ResponseWriter , r * http.Request , apiBase string ) {
107- lumaFetch (w , r , apiBase , "20" )
119+ lumaJSON (w , r , apiBase , "20" )
120+ }
121+
122+ func EventsFeedHandler (w http.ResponseWriter , r * http.Request , apiBase string , siteURL string ) {
123+ items , err := lumaFetchItems (r , apiBase , "20" )
124+ if err != nil {
125+ w .Write ([]byte (err .Error ()))
126+
127+ panic (err )
128+ }
129+
130+ now := time .Now ()
131+
132+ feed := & feeds.Feed {
133+ Title : "VanLUG Events" ,
134+ Link : & feeds.Link {Href : siteURL + "events/" },
135+ Description : "Upcoming events from the Vancouver Linux Users Group" ,
136+ Author : & feeds.Author {Name : "Vancouver Linux Users Group" },
137+ Updated : now ,
138+ Created : now ,
139+ Id : siteURL + "events/" ,
140+ }
141+
142+ for _ , item := range items {
143+ link := "https://luma.com/vanlug"
144+ if item .URL != "" {
145+ link = "https://luma.com/" + item .URL
146+ }
147+
148+ published := now
149+ if t , err := time .Parse (time .RFC3339 , item .StartAt ); err == nil {
150+ published = t
151+ }
152+
153+ description := item .StartAt
154+ if item .Location != "" {
155+ description = item .Location + " — " + description
156+ }
157+
158+ content := "<p>" + html .EscapeString (item .Name ) + "</p>"
159+ if item .Location != "" {
160+ content += "<p>" + html .EscapeString (item .Location ) + "</p>"
161+ }
162+ if item .CoverURL != "" {
163+ content += `<p><img src="` + html .EscapeString (item .CoverURL ) + `" alt="` + html .EscapeString (item .Name ) + `" /></p>`
164+ }
165+
166+ feedItem := & feeds.Item {
167+ Title : item .Name ,
168+ Link : & feeds.Link {Href : link },
169+ Description : description ,
170+ Id : link ,
171+ Created : published ,
172+ Updated : published ,
173+ Content : content ,
174+ }
175+
176+ if item .CoverURL != "" {
177+ feedItem .Enclosure = & feeds.Enclosure {
178+ Url : item .CoverURL ,
179+ Type : "image/jpeg" ,
180+ }
181+ }
182+
183+ feed .Add (feedItem )
184+ }
185+
186+ w .Header ().Set ("Content-Type" , "application/atom+xml; charset=utf-8" )
187+ w .Header ().Set ("Cache-Control" , "public, max-age=900" )
188+
189+ if err := feed .WriteAtom (w ); err != nil {
190+ panic (err )
191+ }
108192}
0 commit comments