eintopf.info@v0.13.16/service/rss/rss.go (about) 1 // Copyright (C) 2022 The Eintopf authors 2 // 3 // This program is free software: you can redistribute it and/or modify 4 // it under the terms of the GNU Affero General Public License as 5 // published by the Free Software Foundation, either version 3 of the 6 // License, or (at your option) any later version. 7 // 8 // This program is distributed in the hope that it will be useful, 9 // but WITHOUT ANY WARRANTY; without even the implied warranty of 10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 11 // GNU Affero General Public License for more details. 12 // 13 // You should have received a copy of the GNU Affero General Public License 14 // along with this program. If not, see <https://www.gnu.org/licenses/>. 15 16 package rss 17 18 import ( 19 "context" 20 "encoding/xml" 21 "fmt" 22 "time" 23 24 "eintopf.info/service/event" 25 "eintopf.info/service/eventsearch" 26 "eintopf.info/service/group" 27 "eintopf.info/service/place" 28 ) 29 30 type Service interface { 31 Feed(ctx context.Context, opts eventsearch.Options, channel *Channel) (*Feed, error) 32 GroupFeed(ctx context.Context, groupID string) (*Feed, error) 33 PlaceFeed(ctx context.Context, placeID string) (*Feed, error) 34 } 35 36 type Feed struct { 37 XMLName xml.Name `xml:"rss"` 38 Version string `xml:"version,attr"` 39 Channel *Channel `xml:"channel"` 40 } 41 42 type Channel struct { 43 XMLName xml.Name `xml:"channel"` 44 Title string `xml:"title"` 45 Link string `xml:"link"` 46 Description string `xml:"description"` 47 Items []Item `xml:"item"` 48 } 49 50 type Item struct { 51 XMLName xml.Name `xml:"item"` 52 Title string `xml:"title"` 53 Link string `xml:"link"` 54 Description string `xml:"description"` 55 Guid string `xml:"guid,omitempty"` 56 } 57 58 type ServiceImpl struct { 59 eventsearchService eventsearch.Service 60 groupService group.Service 61 placeService place.Service 62 63 baseURL string 64 } 65 66 func NewService(eventsearchService eventsearch.Service, groupService group.Service, placeService place.Service, baseURL string) *ServiceImpl { 67 return &ServiceImpl{ 68 eventsearchService: eventsearchService, 69 groupService: groupService, 70 placeService: placeService, 71 72 baseURL: baseURL, 73 } 74 } 75 76 func (s *ServiceImpl) Feed(ctx context.Context, opts eventsearch.Options, channel *Channel) (*Feed, error) { 77 if opts.Sort == "" { 78 opts.Sort = "start" 79 } 80 if opts.Filters == nil { 81 opts.Filters = []eventsearch.Filter{eventsearch.ListedFilter{Listed: true}} 82 } else { 83 foundListedFilter := false 84 for _, f := range opts.Filters { 85 if _, ok := f.(eventsearch.ListedFilter); ok { 86 foundListedFilter = true 87 break 88 } 89 } 90 if !foundListedFilter { 91 opts.Filters = append(opts.Filters, eventsearch.ListedFilter{Listed: true}) 92 } 93 } 94 opts.Filters = append(opts.Filters, eventsearch.DateRangeFilter{DateMin: time.Now()}) 95 result, err := s.eventsearchService.Search(ctx, opts) 96 if err != nil { 97 return nil, err 98 } 99 return s.feedFromEvents(result.Events, channel) 100 } 101 102 func (s *ServiceImpl) feedFromEvents(events []*eventsearch.EventDocument, channel *Channel) (*Feed, error) { 103 feed := &Feed{ 104 Version: "2.0", 105 Channel: channel, 106 } 107 108 for _, e := range events { 109 eventURL, err := event.URLFromID(s.baseURL, e.ID) 110 if err != nil { 111 return nil, fmt.Errorf("invalid event url: %s", err) 112 } 113 feed.Channel.Items = append(feed.Channel.Items, Item{ 114 Title: e.Name, 115 Link: eventURL, 116 Description: e.Description, 117 Guid: e.ID, 118 }) 119 } 120 121 return feed, nil 122 } 123 124 func (s *ServiceImpl) GroupFeed(ctx context.Context, groupID string) (*Feed, error) { 125 groupURL, err := group.URLFromID(s.baseURL, groupID) 126 if err != nil { 127 return nil, fmt.Errorf("invalid group url: %s", err) 128 } 129 g, err := s.groupService.FindByID(ctx, groupID) 130 if err != nil { 131 return nil, err 132 } 133 return s.Feed( 134 ctx, 135 eventsearch.Options{Filters: []eventsearch.Filter{eventsearch.GroupIDFilter{GroupID: groupID}}}, 136 &Channel{Title: g.Name, Link: groupURL + "/rss", Description: g.Description}, 137 ) 138 } 139 140 func (s *ServiceImpl) PlaceFeed(ctx context.Context, placeID string) (*Feed, error) { 141 placeURL, err := place.URLFromID(s.baseURL, placeID) 142 if err != nil { 143 return nil, fmt.Errorf("invalid group url: %s", err) 144 } 145 p, err := s.placeService.FindByID(ctx, placeID) 146 if err != nil { 147 return nil, err 148 } 149 return s.Feed( 150 ctx, 151 eventsearch.Options{Filters: []eventsearch.Filter{eventsearch.PlaceIDFilter{PlaceID: placeID}}}, 152 &Channel{Title: p.Name, Link: placeURL + "/rss", Description: p.Description}, 153 ) 154 }