-
Notifications
You must be signed in to change notification settings - Fork 2
/
convert.go
125 lines (108 loc) · 3.17 KB
/
convert.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
// Package p contains an HTTP Cloud Function.
package p
import (
"fmt"
"log/slog"
"net/http"
"net/url"
"os"
"strconv"
"strings"
"time"
ical "github.com/arran4/golang-ical"
"github.com/mmcdole/gofeed"
)
var logger *slog.Logger
func init() {
logger = slog.New(slog.NewJSONHandler(os.Stdout, nil))
}
// HandleRequest Fetches RSS feed of events located at request param "rssUrl" and
// converts to iCal format with the specified "eventDuration," defaulting to 60
// minutes
func HandleRequest(w http.ResponseWriter, r *http.Request) {
// validate URL param
rssUrl := r.URL.Query().Get("rssUrl")
if _, err := url.ParseRequestURI(rssUrl); err != nil {
logger.Warn("rssUrl parse err: " + err.Error())
http.Error(w, "Invalid URL: "+err.Error(), http.StatusBadRequest)
return
}
// default to event duration of 60min if invalid or unspecified
eventDuration, err := strconv.Atoi(r.URL.Query().Get("eventDuration"))
if err != nil {
logger.Debug("atoi err: " + err.Error())
eventDuration = 60
}
logger.Info("handling request params from remote address",
"rssUrl", rssUrl,
"eventDuration", eventDuration,
"ip", GetIpAddress(r),
)
cal, err := doConvert(rssUrl, eventDuration)
if err != nil {
logger.Warn("err parsing feed: " + err.Error())
http.Error(w, "Error parsing feed: "+err.Error(), http.StatusInternalServerError)
return
}
// write to response
w.Header().Add("Content-Type", "text/calendar")
_, err = w.Write([]byte(cal.Serialize()))
if err != nil {
logger.Error("err writing response: " + err.Error())
}
}
func doConvert(rssUrl string, eventDuration int) (*ical.Calendar, error) {
// fetch RSS feed
fp := gofeed.NewParser()
feed, err := fp.ParseURL(rssUrl)
if err != nil {
logger.Warn("error parsing feed: " + err.Error())
return nil, err
}
logger.Info("fetched feed: " + feed.Title)
// convert to ical
cal := ical.NewCalendar()
// top level properties
productId := "-//" + feed.Title + "//mooseburgr/rss-to-ical"
cal.SetProductId(productId)
cal.SetVersion("2.0")
cal.SetCalscale("GREGORIAN")
cal.SetMethod(ical.MethodPublish)
cal.SetName(feed.Title)
cal.SetXWRCalName(feed.Title)
cal.SetXWRCalDesc(feed.Description)
cal.SetXWRCalID(productId)
cal.SetLastModified(*feed.UpdatedParsed)
// copy events
for _, item := range feed.Items {
event := cal.AddEvent(item.GUID)
event.SetStartAt(*item.PublishedParsed)
event.SetEndAt(item.PublishedParsed.Add(time.Minute * time.Duration(eventDuration)))
event.SetSummary(item.Title)
event.SetDescription(item.Description)
event.SetLocation(item.Link)
event.SetURL(item.Link)
event.SetOrganizer(authorsToOrganizer(item.Authors))
}
return cal, nil
}
func authorsToOrganizer(authors []*gofeed.Person) string {
var result []string
for _, author := range authors {
if strings.TrimSpace(author.Email) == "" {
result = append(result, strings.TrimSpace(author.Name))
} else {
result = append(result, fmt.Sprintf("%s (%s)",
strings.TrimSpace(author.Name), strings.TrimSpace(author.Email)))
}
}
return strings.Join(result, ", ")
}
func GetIpAddress(r *http.Request) string {
xffs := r.Header["X-Forwarded-For"]
if len(xffs) > 0 {
return xffs[0]
} else {
return r.RemoteAddr
}
}