The code and data behind xeiaso.net
5
fork

Configure Feed

Select the types of activity you want to include in your feed.

start protofeed implementation, add swagger UI

Signed-off-by: Xe Iaso <me@xeiaso.net>

Xe Iaso 1e2d398e b27ea6b8

+1467 -46
+2
cmd/xesite/main.go
··· 13 13 "github.com/donatj/hmacsig" 14 14 "github.com/facebookgo/flagenv" 15 15 _ "github.com/joho/godotenv/autoload" 16 + "github.com/esceer/todo/swagger-ui" 16 17 "github.com/twitchtv/twirp" 17 18 "xeiaso.net/v4/internal" 18 19 "xeiaso.net/v4/internal/lume" ··· 81 82 mux := http.NewServeMux() 82 83 mux.Handle("/", http.FileServer(http.FS(fs))) 83 84 mux.Handle("/api/defs/", http.StripPrefix("/api/defs/", http.FileServer(http.FS(pb.Proto)))) 85 + mux.Handle("/api/ui/", http.StripPrefix("/api/ui", swaggerui.Handler(pb.APISpec))) 84 86 85 87 ms := pb.NewMetaServer(&MetaServer{fs}, twirp.WithServerPathPrefix("/api")) 86 88 mux.Handle(ms.PathPrefix(), ms)
+7
lume/_config.ts
··· 81 81 "Image": Figure, 82 82 "LoadingSpinner": LoadingSpinner, 83 83 "TecharoDisclaimer": TecharoDisclaimer, 84 + "Conv": XeblogConv, 84 85 "XeblogConv": XeblogConv, 85 86 "XesiteConv": XeblogConv, 87 + "Hero": XeblogHero, 86 88 "XeblogHero": XeblogHero, 89 + "Picture": XeblogPicture, 87 90 "XeblogPicture": XeblogPicture, 91 + "Slide": XeblogSlide, 88 92 "XeblogSlide": XeblogSlide, 93 + "Sticker": XeblogSticker, 89 94 "XeblogSticker": XeblogSticker, 95 + "Toot": XeblogToot, 90 96 "XeblogToot": XeblogToot, 97 + "Video": XeblogVideo, 91 98 "XeblogVideo": XeblogVideo, 92 99 }, 93 100 rehypePlugins: [
+3
pb/external/generate.go
··· 1 + package protofeed 2 + 3 + //go:generate protoc --proto_path=. --proto_path=.. --go_out=./protofeed --go_opt=paths=source_relative ./protofeed.proto
+53
pb/external/protofeed.proto
··· 1 + syntax = "proto3"; 2 + package xeiaso.net.protofeed; 3 + option go_package = "xeiaso.net/v4/pb/external/protofeed"; 4 + 5 + import "google/protobuf/timestamp.proto"; 6 + 7 + message Feed { 8 + string version = 1; // (required, string) is the URL of the version of the format the feed uses. This should appear at the very top, though we recognize that not all JSON generators allow for ordering. 9 + string title = 2; // (required, string) is the name of the feed, which will often correspond to the name of the website (blog, for instance), though not necessarily. 10 + string home_page_url = 3; // (optional but strongly recommended, string) is the URL of the resource that the feed describes. This resource may or may not actually be a “home” page, but it should be an HTML page. If a feed is published on the public web, this should be considered as required. But it may not make sense in the case of a file created on a desktop computer, when that file is not shared or is shared only privately. 11 + string feed_url = 4; // (optional but strongly recommended, string) is the URL of the feed, and serves as the unique identifier for the feed. As with home_page_url, this should be considered required for feeds on the public web. 12 + string description = 5; // (optional, string) provides more detail, beyond the title, on what the feed is about. A feed reader may display this text. 13 + string user_comment = 6; // (optional, string) is a description of the purpose of the feed. This is for the use of people looking at the raw JSON, and should be ignored by feed readers. 14 + string next_url = 7; // (optional, string) is the URL of a feed that provides the next n items, where n is determined by the publisher. This allows for pagination, but with the expectation that reader software is not required to use it and probably won’t use it very often. next_url must not be the same as feed_url, and it must not be the same as a previous next_url. 15 + string icon = 8; // (optional, string) is the URL of an image for the feed suitable to be used in a source list. It should be square and relatively large — such as 512 x 512 — so that it can be scaled down and so that it can look good on retina displays. It should use transparency where appropriate, since it may be rendered on a non-white background. 16 + string favicon = 9; // (optional, string) is the URL of an image for the feed suitable to be used in a source list. It should be square and relatively small, but not smaller than 64 x 64. 17 + repeated Author authors = 10; // (optional, array of objects) specifies the feed authors. 18 + string language = 11; // (optional, string) is the primary language for the feed. 19 + bool expired = 12; // (optional, boolean) says whether or not the feed is finished — that is, whether or not it will ever update again. A feed for a temporary event, such as an instance of the Olympics, could expire. If the value is true, then it’s expired. Any other value, or the absence of expired, means the feed may continue to update. 20 + repeated Item items = 13; // (required, array of objects) contains the items in the feed. This is the most important element of the feed after the version field. Each item is a story, blog post, article, photograph, video, or other thing. For example, if a feed contains a long article, a podcast episode, and a photo, those three items would be included in items. 21 + } 22 + 23 + message Author { 24 + string name = 1; // (optional, string) is the author’s name. 25 + string url = 2; // (optional, string) is the URL of a site owned by the author. It could be a blog, micro-blog, Twitter account, and so on. Ideally the linked-to page provides a way to contact the author, but that’s not required. The URL could be a mailto: link, though we suspect that will be rare. 26 + string avatar = 3; // (optional, string) is the URL for an image for the author. As with icon, it should be square and relatively large — such as 512 x 512 pixels — and should use transparency where appropriate, since it may be rendered on a non-white background. 27 + } 28 + 29 + message Item { 30 + string id = 1; // (required, string) is unique for that item for that feed over time. If an item is ever updated, the id should be unchanged. New items should never use a previously-used id. If an id is presented as a number or other type, a JSON Feed reader must coerce it to a string. Ideally, the id is the full URL of the resource described by the item, since URLs make great unique identifiers. 31 + string url = 2; // (optional, string) is the URL of the resource described by the item. It’s the permalink. This may be the same as the id — but should be present regardless. 32 + string external_url = 3; // (optional, string) is the URL of a page elsewhere. This is especially useful for linkblogs. If url links to where you’re talking about a thing, then external_url links to the thing you’re talking about. 33 + string title = 4; // (optional, string) is plain text. Microblog items in particular may omit titles. 34 + string content_text = 5; // (optional, string) is the body of the item. It can be plain text, HTML, or a snippet of Markdown. (It should not be the entire Markdown document; just a snippet.) This is complete enough that it can be displayed alone in a reader. 35 + string content_html = 6; // (optional, string) is the body of the item. It can be plain text, HTML, or a snippet of Markdown. (It should not be the entire Markdown document; just a snippet.) This is complete enough that it can be displayed alone in a reader. 36 + string summary = 7; // (optional, string) is a plain text sentence or two describing the item. This might be presented in a timeline, for instance, where a detail view would display all of content_html or content_text. 37 + string image = 8; // (optional, string) is the URL of the main image for the item. This image may also appear in the content_html — if so, it’s a hint to the feed reader that this is the main, featured image. Even if it’s not, it will appear in the detail view. Images should be square, with a 4:3 aspect ratio. (We will be flexible on this in the future.) 38 + string banner_image = 9; // (optional, string) is the URL of an image to use as a banner. Some blogging systems (such as Medium) display a different banner image in the list view from the detail view. In those systems, this image should be used in the list view, and image in the detail view. 39 + google.protobuf.Timestamp date_published = 10; // (optional, string) specifies the date in RFC 3339 format. (Example: 2010-02-07T14:04:00-05:00.) 40 + google.protobuf.Timestamp date_modified = 11; // (optional, string) specifies the modification date in RFC 3339 format. 41 + repeated Author authors = 12; // (optional, array of objects) has the same structure as the top-level authors. If not specified in an item, then the top-level authors, if present, are the authors of the item. 42 + repeated string tags = 13; // (optional, array of strings) can have any plain text values you want. Tags tend to be just one word, but they may be anything. Note: they are not the equivalent of Twitter hashtags. Some blogging systems and other feed formats call these categories. 43 + string language = 14; // (optional, string) is the language for this item, using the same format as the top-level language field. The value can be different than the primary language for the feed when a specific item is written in a different language than other items in the feed. 44 + repeated Attachement attachments = 15; // (optional, array of objects) specifies the attachments associated with the item. Attachments are files that are associated with an item. The value of the attachments field is an array of objects, each of which has a url field, and other fields as specified in the attachment object definition. 45 + } 46 + 47 + message Attachement { 48 + string url = 1; // (required, string) specifies the location of the attachment. 49 + string mime_type = 2; // (required, string) specifies the type of the attachment, such as “audio/mpeg.” 50 + string title = 3; // (optional, string) specifies the title of the attachment. 51 + int32 size_in_bytes = 4; // (optional, number) specifies how large the file is. 52 + int32 duration_in_seconds = 5; // (optional, number) specifies how long it takes to listen to or watch, when played at normal speed. 53 + }
+664
pb/external/protofeed/protofeed.pb.go
··· 1 + // Code generated by protoc-gen-go. DO NOT EDIT. 2 + // versions: 3 + // protoc-gen-go v1.32.0 4 + // protoc v4.24.4 5 + // source: protofeed.proto 6 + 7 + package protofeed 8 + 9 + import ( 10 + protoreflect "google.golang.org/protobuf/reflect/protoreflect" 11 + protoimpl "google.golang.org/protobuf/runtime/protoimpl" 12 + timestamppb "google.golang.org/protobuf/types/known/timestamppb" 13 + reflect "reflect" 14 + sync "sync" 15 + ) 16 + 17 + const ( 18 + // Verify that this generated code is sufficiently up-to-date. 19 + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) 20 + // Verify that runtime/protoimpl is sufficiently up-to-date. 21 + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) 22 + ) 23 + 24 + type Feed struct { 25 + state protoimpl.MessageState 26 + sizeCache protoimpl.SizeCache 27 + unknownFields protoimpl.UnknownFields 28 + 29 + Version string `protobuf:"bytes,1,opt,name=version,proto3" json:"version,omitempty"` // (required, string) is the URL of the version of the format the feed uses. This should appear at the very top, though we recognize that not all JSON generators allow for ordering. 30 + Title string `protobuf:"bytes,2,opt,name=title,proto3" json:"title,omitempty"` // (required, string) is the name of the feed, which will often correspond to the name of the website (blog, for instance), though not necessarily. 31 + HomePageUrl string `protobuf:"bytes,3,opt,name=home_page_url,json=homePageUrl,proto3" json:"home_page_url,omitempty"` // (optional but strongly recommended, string) is the URL of the resource that the feed describes. This resource may or may not actually be a “home” page, but it should be an HTML page. If a feed is published on the public web, this should be considered as required. But it may not make sense in the case of a file created on a desktop computer, when that file is not shared or is shared only privately. 32 + FeedUrl string `protobuf:"bytes,4,opt,name=feed_url,json=feedUrl,proto3" json:"feed_url,omitempty"` // (optional but strongly recommended, string) is the URL of the feed, and serves as the unique identifier for the feed. As with home_page_url, this should be considered required for feeds on the public web. 33 + Description string `protobuf:"bytes,5,opt,name=description,proto3" json:"description,omitempty"` // (optional, string) provides more detail, beyond the title, on what the feed is about. A feed reader may display this text. 34 + UserComment string `protobuf:"bytes,6,opt,name=user_comment,json=userComment,proto3" json:"user_comment,omitempty"` // (optional, string) is a description of the purpose of the feed. This is for the use of people looking at the raw JSON, and should be ignored by feed readers. 35 + NextUrl string `protobuf:"bytes,7,opt,name=next_url,json=nextUrl,proto3" json:"next_url,omitempty"` // (optional, string) is the URL of a feed that provides the next n items, where n is determined by the publisher. This allows for pagination, but with the expectation that reader software is not required to use it and probably won’t use it very often. next_url must not be the same as feed_url, and it must not be the same as a previous next_url. 36 + Icon string `protobuf:"bytes,8,opt,name=icon,proto3" json:"icon,omitempty"` // (optional, string) is the URL of an image for the feed suitable to be used in a source list. It should be square and relatively large — such as 512 x 512 — so that it can be scaled down and so that it can look good on retina displays. It should use transparency where appropriate, since it may be rendered on a non-white background. 37 + Favicon string `protobuf:"bytes,9,opt,name=favicon,proto3" json:"favicon,omitempty"` // (optional, string) is the URL of an image for the feed suitable to be used in a source list. It should be square and relatively small, but not smaller than 64 x 64. 38 + Authors []*Author `protobuf:"bytes,10,rep,name=authors,proto3" json:"authors,omitempty"` // (optional, array of objects) specifies the feed authors. 39 + Language string `protobuf:"bytes,11,opt,name=language,proto3" json:"language,omitempty"` // (optional, string) is the primary language for the feed. 40 + Expired bool `protobuf:"varint,12,opt,name=expired,proto3" json:"expired,omitempty"` // (optional, boolean) says whether or not the feed is finished — that is, whether or not it will ever update again. A feed for a temporary event, such as an instance of the Olympics, could expire. If the value is true, then it’s expired. Any other value, or the absence of expired, means the feed may continue to update. 41 + Items []*Item `protobuf:"bytes,13,rep,name=items,proto3" json:"items,omitempty"` // (required, array of objects) contains the items in the feed. This is the most important element of the feed after the version field. Each item is a story, blog post, article, photograph, video, or other thing. For example, if a feed contains a long article, a podcast episode, and a photo, those three items would be included in items. 42 + } 43 + 44 + func (x *Feed) Reset() { 45 + *x = Feed{} 46 + if protoimpl.UnsafeEnabled { 47 + mi := &file_protofeed_proto_msgTypes[0] 48 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 49 + ms.StoreMessageInfo(mi) 50 + } 51 + } 52 + 53 + func (x *Feed) String() string { 54 + return protoimpl.X.MessageStringOf(x) 55 + } 56 + 57 + func (*Feed) ProtoMessage() {} 58 + 59 + func (x *Feed) ProtoReflect() protoreflect.Message { 60 + mi := &file_protofeed_proto_msgTypes[0] 61 + if protoimpl.UnsafeEnabled && x != nil { 62 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 63 + if ms.LoadMessageInfo() == nil { 64 + ms.StoreMessageInfo(mi) 65 + } 66 + return ms 67 + } 68 + return mi.MessageOf(x) 69 + } 70 + 71 + // Deprecated: Use Feed.ProtoReflect.Descriptor instead. 72 + func (*Feed) Descriptor() ([]byte, []int) { 73 + return file_protofeed_proto_rawDescGZIP(), []int{0} 74 + } 75 + 76 + func (x *Feed) GetVersion() string { 77 + if x != nil { 78 + return x.Version 79 + } 80 + return "" 81 + } 82 + 83 + func (x *Feed) GetTitle() string { 84 + if x != nil { 85 + return x.Title 86 + } 87 + return "" 88 + } 89 + 90 + func (x *Feed) GetHomePageUrl() string { 91 + if x != nil { 92 + return x.HomePageUrl 93 + } 94 + return "" 95 + } 96 + 97 + func (x *Feed) GetFeedUrl() string { 98 + if x != nil { 99 + return x.FeedUrl 100 + } 101 + return "" 102 + } 103 + 104 + func (x *Feed) GetDescription() string { 105 + if x != nil { 106 + return x.Description 107 + } 108 + return "" 109 + } 110 + 111 + func (x *Feed) GetUserComment() string { 112 + if x != nil { 113 + return x.UserComment 114 + } 115 + return "" 116 + } 117 + 118 + func (x *Feed) GetNextUrl() string { 119 + if x != nil { 120 + return x.NextUrl 121 + } 122 + return "" 123 + } 124 + 125 + func (x *Feed) GetIcon() string { 126 + if x != nil { 127 + return x.Icon 128 + } 129 + return "" 130 + } 131 + 132 + func (x *Feed) GetFavicon() string { 133 + if x != nil { 134 + return x.Favicon 135 + } 136 + return "" 137 + } 138 + 139 + func (x *Feed) GetAuthors() []*Author { 140 + if x != nil { 141 + return x.Authors 142 + } 143 + return nil 144 + } 145 + 146 + func (x *Feed) GetLanguage() string { 147 + if x != nil { 148 + return x.Language 149 + } 150 + return "" 151 + } 152 + 153 + func (x *Feed) GetExpired() bool { 154 + if x != nil { 155 + return x.Expired 156 + } 157 + return false 158 + } 159 + 160 + func (x *Feed) GetItems() []*Item { 161 + if x != nil { 162 + return x.Items 163 + } 164 + return nil 165 + } 166 + 167 + type Author struct { 168 + state protoimpl.MessageState 169 + sizeCache protoimpl.SizeCache 170 + unknownFields protoimpl.UnknownFields 171 + 172 + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // (optional, string) is the author’s name. 173 + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` // (optional, string) is the URL of a site owned by the author. It could be a blog, micro-blog, Twitter account, and so on. Ideally the linked-to page provides a way to contact the author, but that’s not required. The URL could be a mailto: link, though we suspect that will be rare. 174 + Avatar string `protobuf:"bytes,3,opt,name=avatar,proto3" json:"avatar,omitempty"` // (optional, string) is the URL for an image for the author. As with icon, it should be square and relatively large — such as 512 x 512 pixels — and should use transparency where appropriate, since it may be rendered on a non-white background. 175 + } 176 + 177 + func (x *Author) Reset() { 178 + *x = Author{} 179 + if protoimpl.UnsafeEnabled { 180 + mi := &file_protofeed_proto_msgTypes[1] 181 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 182 + ms.StoreMessageInfo(mi) 183 + } 184 + } 185 + 186 + func (x *Author) String() string { 187 + return protoimpl.X.MessageStringOf(x) 188 + } 189 + 190 + func (*Author) ProtoMessage() {} 191 + 192 + func (x *Author) ProtoReflect() protoreflect.Message { 193 + mi := &file_protofeed_proto_msgTypes[1] 194 + if protoimpl.UnsafeEnabled && x != nil { 195 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 196 + if ms.LoadMessageInfo() == nil { 197 + ms.StoreMessageInfo(mi) 198 + } 199 + return ms 200 + } 201 + return mi.MessageOf(x) 202 + } 203 + 204 + // Deprecated: Use Author.ProtoReflect.Descriptor instead. 205 + func (*Author) Descriptor() ([]byte, []int) { 206 + return file_protofeed_proto_rawDescGZIP(), []int{1} 207 + } 208 + 209 + func (x *Author) GetName() string { 210 + if x != nil { 211 + return x.Name 212 + } 213 + return "" 214 + } 215 + 216 + func (x *Author) GetUrl() string { 217 + if x != nil { 218 + return x.Url 219 + } 220 + return "" 221 + } 222 + 223 + func (x *Author) GetAvatar() string { 224 + if x != nil { 225 + return x.Avatar 226 + } 227 + return "" 228 + } 229 + 230 + type Item struct { 231 + state protoimpl.MessageState 232 + sizeCache protoimpl.SizeCache 233 + unknownFields protoimpl.UnknownFields 234 + 235 + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // (required, string) is unique for that item for that feed over time. If an item is ever updated, the id should be unchanged. New items should never use a previously-used id. If an id is presented as a number or other type, a JSON Feed reader must coerce it to a string. Ideally, the id is the full URL of the resource described by the item, since URLs make great unique identifiers. 236 + Url string `protobuf:"bytes,2,opt,name=url,proto3" json:"url,omitempty"` // (optional, string) is the URL of the resource described by the item. It’s the permalink. This may be the same as the id — but should be present regardless. 237 + ExternalUrl string `protobuf:"bytes,3,opt,name=external_url,json=externalUrl,proto3" json:"external_url,omitempty"` // (optional, string) is the URL of a page elsewhere. This is especially useful for linkblogs. If url links to where you’re talking about a thing, then external_url links to the thing you’re talking about. 238 + Title string `protobuf:"bytes,4,opt,name=title,proto3" json:"title,omitempty"` // (optional, string) is plain text. Microblog items in particular may omit titles. 239 + ContentText string `protobuf:"bytes,5,opt,name=content_text,json=contentText,proto3" json:"content_text,omitempty"` // (optional, string) is the body of the item. It can be plain text, HTML, or a snippet of Markdown. (It should not be the entire Markdown document; just a snippet.) This is complete enough that it can be displayed alone in a reader. 240 + ContentHtml string `protobuf:"bytes,6,opt,name=content_html,json=contentHtml,proto3" json:"content_html,omitempty"` // (optional, string) is the body of the item. It can be plain text, HTML, or a snippet of Markdown. (It should not be the entire Markdown document; just a snippet.) This is complete enough that it can be displayed alone in a reader. 241 + Summary string `protobuf:"bytes,7,opt,name=summary,proto3" json:"summary,omitempty"` // (optional, string) is a plain text sentence or two describing the item. This might be presented in a timeline, for instance, where a detail view would display all of content_html or content_text. 242 + Image string `protobuf:"bytes,8,opt,name=image,proto3" json:"image,omitempty"` // (optional, string) is the URL of the main image for the item. This image may also appear in the content_html — if so, it’s a hint to the feed reader that this is the main, featured image. Even if it’s not, it will appear in the detail view. Images should be square, with a 4:3 aspect ratio. (We will be flexible on this in the future.) 243 + BannerImage string `protobuf:"bytes,9,opt,name=banner_image,json=bannerImage,proto3" json:"banner_image,omitempty"` // (optional, string) is the URL of an image to use as a banner. Some blogging systems (such as Medium) display a different banner image in the list view from the detail view. In those systems, this image should be used in the list view, and image in the detail view. 244 + DatePublished *timestamppb.Timestamp `protobuf:"bytes,10,opt,name=date_published,json=datePublished,proto3" json:"date_published,omitempty"` // (optional, string) specifies the date in RFC 3339 format. (Example: 2010-02-07T14:04:00-05:00.) 245 + DateModified *timestamppb.Timestamp `protobuf:"bytes,11,opt,name=date_modified,json=dateModified,proto3" json:"date_modified,omitempty"` // (optional, string) specifies the modification date in RFC 3339 format. 246 + Authors []*Author `protobuf:"bytes,12,rep,name=authors,proto3" json:"authors,omitempty"` // (optional, array of objects) has the same structure as the top-level authors. If not specified in an item, then the top-level authors, if present, are the authors of the item. 247 + Tags []string `protobuf:"bytes,13,rep,name=tags,proto3" json:"tags,omitempty"` // (optional, array of strings) can have any plain text values you want. Tags tend to be just one word, but they may be anything. Note: they are not the equivalent of Twitter hashtags. Some blogging systems and other feed formats call these categories. 248 + Language string `protobuf:"bytes,14,opt,name=language,proto3" json:"language,omitempty"` // (optional, string) is the language for this item, using the same format as the top-level language field. The value can be different than the primary language for the feed when a specific item is written in a different language than other items in the feed. 249 + Attachments []*Attachement `protobuf:"bytes,15,rep,name=attachments,proto3" json:"attachments,omitempty"` // (optional, array of objects) specifies the attachments associated with the item. Attachments are files that are associated with an item. The value of the attachments field is an array of objects, each of which has a url field, and other fields as specified in the attachment object definition. 250 + } 251 + 252 + func (x *Item) Reset() { 253 + *x = Item{} 254 + if protoimpl.UnsafeEnabled { 255 + mi := &file_protofeed_proto_msgTypes[2] 256 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 257 + ms.StoreMessageInfo(mi) 258 + } 259 + } 260 + 261 + func (x *Item) String() string { 262 + return protoimpl.X.MessageStringOf(x) 263 + } 264 + 265 + func (*Item) ProtoMessage() {} 266 + 267 + func (x *Item) ProtoReflect() protoreflect.Message { 268 + mi := &file_protofeed_proto_msgTypes[2] 269 + if protoimpl.UnsafeEnabled && x != nil { 270 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 271 + if ms.LoadMessageInfo() == nil { 272 + ms.StoreMessageInfo(mi) 273 + } 274 + return ms 275 + } 276 + return mi.MessageOf(x) 277 + } 278 + 279 + // Deprecated: Use Item.ProtoReflect.Descriptor instead. 280 + func (*Item) Descriptor() ([]byte, []int) { 281 + return file_protofeed_proto_rawDescGZIP(), []int{2} 282 + } 283 + 284 + func (x *Item) GetId() string { 285 + if x != nil { 286 + return x.Id 287 + } 288 + return "" 289 + } 290 + 291 + func (x *Item) GetUrl() string { 292 + if x != nil { 293 + return x.Url 294 + } 295 + return "" 296 + } 297 + 298 + func (x *Item) GetExternalUrl() string { 299 + if x != nil { 300 + return x.ExternalUrl 301 + } 302 + return "" 303 + } 304 + 305 + func (x *Item) GetTitle() string { 306 + if x != nil { 307 + return x.Title 308 + } 309 + return "" 310 + } 311 + 312 + func (x *Item) GetContentText() string { 313 + if x != nil { 314 + return x.ContentText 315 + } 316 + return "" 317 + } 318 + 319 + func (x *Item) GetContentHtml() string { 320 + if x != nil { 321 + return x.ContentHtml 322 + } 323 + return "" 324 + } 325 + 326 + func (x *Item) GetSummary() string { 327 + if x != nil { 328 + return x.Summary 329 + } 330 + return "" 331 + } 332 + 333 + func (x *Item) GetImage() string { 334 + if x != nil { 335 + return x.Image 336 + } 337 + return "" 338 + } 339 + 340 + func (x *Item) GetBannerImage() string { 341 + if x != nil { 342 + return x.BannerImage 343 + } 344 + return "" 345 + } 346 + 347 + func (x *Item) GetDatePublished() *timestamppb.Timestamp { 348 + if x != nil { 349 + return x.DatePublished 350 + } 351 + return nil 352 + } 353 + 354 + func (x *Item) GetDateModified() *timestamppb.Timestamp { 355 + if x != nil { 356 + return x.DateModified 357 + } 358 + return nil 359 + } 360 + 361 + func (x *Item) GetAuthors() []*Author { 362 + if x != nil { 363 + return x.Authors 364 + } 365 + return nil 366 + } 367 + 368 + func (x *Item) GetTags() []string { 369 + if x != nil { 370 + return x.Tags 371 + } 372 + return nil 373 + } 374 + 375 + func (x *Item) GetLanguage() string { 376 + if x != nil { 377 + return x.Language 378 + } 379 + return "" 380 + } 381 + 382 + func (x *Item) GetAttachments() []*Attachement { 383 + if x != nil { 384 + return x.Attachments 385 + } 386 + return nil 387 + } 388 + 389 + type Attachement struct { 390 + state protoimpl.MessageState 391 + sizeCache protoimpl.SizeCache 392 + unknownFields protoimpl.UnknownFields 393 + 394 + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` // (required, string) specifies the location of the attachment. 395 + MimeType string `protobuf:"bytes,2,opt,name=mime_type,json=mimeType,proto3" json:"mime_type,omitempty"` // (required, string) specifies the type of the attachment, such as “audio/mpeg.” 396 + Title string `protobuf:"bytes,3,opt,name=title,proto3" json:"title,omitempty"` // (optional, string) specifies the title of the attachment. 397 + SizeInBytes int32 `protobuf:"varint,4,opt,name=size_in_bytes,json=sizeInBytes,proto3" json:"size_in_bytes,omitempty"` // (optional, number) specifies how large the file is. 398 + DurationInSeconds int32 `protobuf:"varint,5,opt,name=duration_in_seconds,json=durationInSeconds,proto3" json:"duration_in_seconds,omitempty"` // (optional, number) specifies how long it takes to listen to or watch, when played at normal speed. 399 + } 400 + 401 + func (x *Attachement) Reset() { 402 + *x = Attachement{} 403 + if protoimpl.UnsafeEnabled { 404 + mi := &file_protofeed_proto_msgTypes[3] 405 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 406 + ms.StoreMessageInfo(mi) 407 + } 408 + } 409 + 410 + func (x *Attachement) String() string { 411 + return protoimpl.X.MessageStringOf(x) 412 + } 413 + 414 + func (*Attachement) ProtoMessage() {} 415 + 416 + func (x *Attachement) ProtoReflect() protoreflect.Message { 417 + mi := &file_protofeed_proto_msgTypes[3] 418 + if protoimpl.UnsafeEnabled && x != nil { 419 + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) 420 + if ms.LoadMessageInfo() == nil { 421 + ms.StoreMessageInfo(mi) 422 + } 423 + return ms 424 + } 425 + return mi.MessageOf(x) 426 + } 427 + 428 + // Deprecated: Use Attachement.ProtoReflect.Descriptor instead. 429 + func (*Attachement) Descriptor() ([]byte, []int) { 430 + return file_protofeed_proto_rawDescGZIP(), []int{3} 431 + } 432 + 433 + func (x *Attachement) GetUrl() string { 434 + if x != nil { 435 + return x.Url 436 + } 437 + return "" 438 + } 439 + 440 + func (x *Attachement) GetMimeType() string { 441 + if x != nil { 442 + return x.MimeType 443 + } 444 + return "" 445 + } 446 + 447 + func (x *Attachement) GetTitle() string { 448 + if x != nil { 449 + return x.Title 450 + } 451 + return "" 452 + } 453 + 454 + func (x *Attachement) GetSizeInBytes() int32 { 455 + if x != nil { 456 + return x.SizeInBytes 457 + } 458 + return 0 459 + } 460 + 461 + func (x *Attachement) GetDurationInSeconds() int32 { 462 + if x != nil { 463 + return x.DurationInSeconds 464 + } 465 + return 0 466 + } 467 + 468 + var File_protofeed_proto protoreflect.FileDescriptor 469 + 470 + var file_protofeed_proto_rawDesc = []byte{ 471 + 0x0a, 0x0f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x65, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 0x74, 472 + 0x6f, 0x12, 0x14, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 473 + 0x6f, 0x74, 0x6f, 0x66, 0x65, 0x65, 0x64, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 474 + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 475 + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xa3, 0x03, 0x0a, 0x04, 0x46, 0x65, 0x65, 476 + 0x64, 0x12, 0x18, 0x0a, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 477 + 0x28, 0x09, 0x52, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x14, 0x0a, 0x05, 0x74, 478 + 0x69, 0x74, 0x6c, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 479 + 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x68, 0x6f, 0x6d, 0x65, 0x5f, 0x70, 0x61, 0x67, 0x65, 0x5f, 0x75, 480 + 0x72, 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x68, 0x6f, 0x6d, 0x65, 0x50, 0x61, 481 + 0x67, 0x65, 0x55, 0x72, 0x6c, 0x12, 0x19, 0x0a, 0x08, 0x66, 0x65, 0x65, 0x64, 0x5f, 0x75, 0x72, 482 + 0x6c, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x66, 0x65, 0x65, 0x64, 0x55, 0x72, 0x6c, 483 + 0x12, 0x20, 0x0a, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6f, 0x6e, 0x18, 484 + 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 485 + 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x75, 0x73, 0x65, 0x72, 0x5f, 0x63, 0x6f, 0x6d, 0x6d, 0x65, 486 + 0x6e, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x75, 0x73, 0x65, 0x72, 0x43, 0x6f, 487 + 0x6d, 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x19, 0x0a, 0x08, 0x6e, 0x65, 0x78, 0x74, 0x5f, 0x75, 0x72, 488 + 0x6c, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6e, 0x65, 0x78, 0x74, 0x55, 0x72, 0x6c, 489 + 0x12, 0x12, 0x0a, 0x04, 0x69, 0x63, 0x6f, 0x6e, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 490 + 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x18, 491 + 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x66, 0x61, 0x76, 0x69, 0x63, 0x6f, 0x6e, 0x12, 0x36, 492 + 0x0a, 0x07, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x18, 0x0a, 0x20, 0x03, 0x28, 0x0b, 0x32, 493 + 0x1c, 0x2e, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 494 + 0x74, 0x6f, 0x66, 0x65, 0x65, 0x64, 0x2e, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x52, 0x07, 0x61, 495 + 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 496 + 0x67, 0x65, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 497 + 0x67, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x18, 0x0c, 0x20, 498 + 0x01, 0x28, 0x08, 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x64, 0x12, 0x30, 0x0a, 0x05, 499 + 0x69, 0x74, 0x65, 0x6d, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x78, 0x65, 500 + 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x65, 501 + 0x65, 0x64, 0x2e, 0x49, 0x74, 0x65, 0x6d, 0x52, 0x05, 0x69, 0x74, 0x65, 0x6d, 0x73, 0x22, 0x46, 502 + 0x0a, 0x06, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 503 + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x10, 0x0a, 0x03, 504 + 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x16, 505 + 0x0a, 0x06, 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 506 + 0x61, 0x76, 0x61, 0x74, 0x61, 0x72, 0x22, 0xab, 0x04, 0x0a, 0x04, 0x49, 0x74, 0x65, 0x6d, 0x12, 507 + 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 508 + 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 509 + 0x6c, 0x12, 0x21, 0x0a, 0x0c, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x5f, 0x75, 0x72, 510 + 0x6c, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 511 + 0x6c, 0x55, 0x72, 0x6c, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x04, 0x20, 512 + 0x01, 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x21, 0x0a, 0x0c, 0x63, 0x6f, 513 + 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x74, 0x65, 0x78, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 514 + 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x54, 0x65, 0x78, 0x74, 0x12, 0x21, 0x0a, 515 + 0x0c, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x5f, 0x68, 0x74, 0x6d, 0x6c, 0x18, 0x06, 0x20, 516 + 0x01, 0x28, 0x09, 0x52, 0x0b, 0x63, 0x6f, 0x6e, 0x74, 0x65, 0x6e, 0x74, 0x48, 0x74, 0x6d, 0x6c, 517 + 0x12, 0x18, 0x0a, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x18, 0x07, 0x20, 0x01, 0x28, 518 + 0x09, 0x52, 0x07, 0x73, 0x75, 0x6d, 0x6d, 0x61, 0x72, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x69, 0x6d, 519 + 0x61, 0x67, 0x65, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x69, 0x6d, 0x61, 0x67, 0x65, 520 + 0x12, 0x21, 0x0a, 0x0c, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x5f, 0x69, 0x6d, 0x61, 0x67, 0x65, 521 + 0x18, 0x09, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x62, 0x61, 0x6e, 0x6e, 0x65, 0x72, 0x49, 0x6d, 522 + 0x61, 0x67, 0x65, 0x12, 0x41, 0x0a, 0x0e, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x70, 0x75, 0x62, 0x6c, 523 + 0x69, 0x73, 0x68, 0x65, 0x64, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 524 + 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 525 + 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0d, 0x64, 0x61, 0x74, 0x65, 0x50, 0x75, 0x62, 526 + 0x6c, 0x69, 0x73, 0x68, 0x65, 0x64, 0x12, 0x3f, 0x0a, 0x0d, 0x64, 0x61, 0x74, 0x65, 0x5f, 0x6d, 527 + 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 528 + 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 529 + 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x0c, 0x64, 0x61, 0x74, 0x65, 0x4d, 530 + 0x6f, 0x64, 0x69, 0x66, 0x69, 0x65, 0x64, 0x12, 0x36, 0x0a, 0x07, 0x61, 0x75, 0x74, 0x68, 0x6f, 531 + 0x72, 0x73, 0x18, 0x0c, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1c, 0x2e, 0x78, 0x65, 0x69, 0x61, 0x73, 532 + 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x65, 0x65, 0x64, 0x2e, 533 + 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x52, 0x07, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x73, 0x12, 534 + 0x12, 0x0a, 0x04, 0x74, 0x61, 0x67, 0x73, 0x18, 0x0d, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x74, 535 + 0x61, 0x67, 0x73, 0x12, 0x1a, 0x0a, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x18, 536 + 0x0e, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6c, 0x61, 0x6e, 0x67, 0x75, 0x61, 0x67, 0x65, 0x12, 537 + 0x43, 0x0a, 0x0b, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 0x65, 0x6e, 0x74, 0x73, 0x18, 0x0f, 538 + 0x20, 0x03, 0x28, 0x0b, 0x32, 0x21, 0x2e, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 539 + 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x65, 0x65, 0x64, 0x2e, 0x41, 0x74, 0x74, 0x61, 540 + 0x63, 0x68, 0x65, 0x6d, 0x65, 0x6e, 0x74, 0x52, 0x0b, 0x61, 0x74, 0x74, 0x61, 0x63, 0x68, 0x6d, 541 + 0x65, 0x6e, 0x74, 0x73, 0x22, 0xa6, 0x01, 0x0a, 0x0b, 0x41, 0x74, 0x74, 0x61, 0x63, 0x68, 0x65, 542 + 0x6d, 0x65, 0x6e, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, 543 + 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x6d, 0x69, 0x6d, 0x65, 0x5f, 0x74, 544 + 0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x6d, 0x69, 0x6d, 0x65, 0x54, 545 + 0x79, 0x70, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x18, 0x03, 0x20, 0x01, 546 + 0x28, 0x09, 0x52, 0x05, 0x74, 0x69, 0x74, 0x6c, 0x65, 0x12, 0x22, 0x0a, 0x0d, 0x73, 0x69, 0x7a, 547 + 0x65, 0x5f, 0x69, 0x6e, 0x5f, 0x62, 0x79, 0x74, 0x65, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x05, 548 + 0x52, 0x0b, 0x73, 0x69, 0x7a, 0x65, 0x49, 0x6e, 0x42, 0x79, 0x74, 0x65, 0x73, 0x12, 0x2e, 0x0a, 549 + 0x13, 0x64, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x69, 0x6e, 0x5f, 0x73, 0x65, 0x63, 550 + 0x6f, 0x6e, 0x64, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x05, 0x52, 0x11, 0x64, 0x75, 0x72, 0x61, 551 + 0x74, 0x69, 0x6f, 0x6e, 0x49, 0x6e, 0x53, 0x65, 0x63, 0x6f, 0x6e, 0x64, 0x73, 0x42, 0x25, 0x5a, 552 + 0x23, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x34, 0x2f, 0x70, 553 + 0x62, 0x2f, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 554 + 0x66, 0x65, 0x65, 0x64, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 555 + } 556 + 557 + var ( 558 + file_protofeed_proto_rawDescOnce sync.Once 559 + file_protofeed_proto_rawDescData = file_protofeed_proto_rawDesc 560 + ) 561 + 562 + func file_protofeed_proto_rawDescGZIP() []byte { 563 + file_protofeed_proto_rawDescOnce.Do(func() { 564 + file_protofeed_proto_rawDescData = protoimpl.X.CompressGZIP(file_protofeed_proto_rawDescData) 565 + }) 566 + return file_protofeed_proto_rawDescData 567 + } 568 + 569 + var file_protofeed_proto_msgTypes = make([]protoimpl.MessageInfo, 4) 570 + var file_protofeed_proto_goTypes = []interface{}{ 571 + (*Feed)(nil), // 0: xeiaso.net.protofeed.Feed 572 + (*Author)(nil), // 1: xeiaso.net.protofeed.Author 573 + (*Item)(nil), // 2: xeiaso.net.protofeed.Item 574 + (*Attachement)(nil), // 3: xeiaso.net.protofeed.Attachement 575 + (*timestamppb.Timestamp)(nil), // 4: google.protobuf.Timestamp 576 + } 577 + var file_protofeed_proto_depIdxs = []int32{ 578 + 1, // 0: xeiaso.net.protofeed.Feed.authors:type_name -> xeiaso.net.protofeed.Author 579 + 2, // 1: xeiaso.net.protofeed.Feed.items:type_name -> xeiaso.net.protofeed.Item 580 + 4, // 2: xeiaso.net.protofeed.Item.date_published:type_name -> google.protobuf.Timestamp 581 + 4, // 3: xeiaso.net.protofeed.Item.date_modified:type_name -> google.protobuf.Timestamp 582 + 1, // 4: xeiaso.net.protofeed.Item.authors:type_name -> xeiaso.net.protofeed.Author 583 + 3, // 5: xeiaso.net.protofeed.Item.attachments:type_name -> xeiaso.net.protofeed.Attachement 584 + 6, // [6:6] is the sub-list for method output_type 585 + 6, // [6:6] is the sub-list for method input_type 586 + 6, // [6:6] is the sub-list for extension type_name 587 + 6, // [6:6] is the sub-list for extension extendee 588 + 0, // [0:6] is the sub-list for field type_name 589 + } 590 + 591 + func init() { file_protofeed_proto_init() } 592 + func file_protofeed_proto_init() { 593 + if File_protofeed_proto != nil { 594 + return 595 + } 596 + if !protoimpl.UnsafeEnabled { 597 + file_protofeed_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { 598 + switch v := v.(*Feed); i { 599 + case 0: 600 + return &v.state 601 + case 1: 602 + return &v.sizeCache 603 + case 2: 604 + return &v.unknownFields 605 + default: 606 + return nil 607 + } 608 + } 609 + file_protofeed_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { 610 + switch v := v.(*Author); i { 611 + case 0: 612 + return &v.state 613 + case 1: 614 + return &v.sizeCache 615 + case 2: 616 + return &v.unknownFields 617 + default: 618 + return nil 619 + } 620 + } 621 + file_protofeed_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { 622 + switch v := v.(*Item); i { 623 + case 0: 624 + return &v.state 625 + case 1: 626 + return &v.sizeCache 627 + case 2: 628 + return &v.unknownFields 629 + default: 630 + return nil 631 + } 632 + } 633 + file_protofeed_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { 634 + switch v := v.(*Attachement); i { 635 + case 0: 636 + return &v.state 637 + case 1: 638 + return &v.sizeCache 639 + case 2: 640 + return &v.unknownFields 641 + default: 642 + return nil 643 + } 644 + } 645 + } 646 + type x struct{} 647 + out := protoimpl.TypeBuilder{ 648 + File: protoimpl.DescBuilder{ 649 + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), 650 + RawDescriptor: file_protofeed_proto_rawDesc, 651 + NumEnums: 0, 652 + NumMessages: 4, 653 + NumExtensions: 0, 654 + NumServices: 0, 655 + }, 656 + GoTypes: file_protofeed_proto_goTypes, 657 + DependencyIndexes: file_protofeed_proto_depIdxs, 658 + MessageInfos: file_protofeed_proto_msgTypes, 659 + }.Build() 660 + File_protofeed_proto = out.File 661 + file_protofeed_proto_rawDesc = nil 662 + file_protofeed_proto_goTypes = nil 663 + file_protofeed_proto_depIdxs = nil 664 + }
+5 -2
pb/generate.go
··· 7 7 func init() {} 8 8 9 9 //go:generate protoc --proto_path=. --go_out=. --go_opt=paths=source_relative --twirp_out=. --twirp_opt=paths=source_relative xesite.proto 10 - //go:generate go run github.com/blockthrough/twirp-openapi-gen/cmd/twirp-openapi-gen --in=xesite.proto --path-prefix=/api --servers=https://xeiaso.net --title="xeiaso.net API" --out openapi.json 10 + //go:generate go run ../cmd/twirp-openapi-gen --in=xesite.proto --path-prefix=/api --servers=https://xeiaso.net --title=xeiaso.net --out=openapi.json 11 11 12 - //go:embed xesite.proto openapi.json 12 + //go:embed xesite.proto openapi.json external/*.proto 13 13 var Proto embed.FS 14 + 15 + //go:embed openapi.json 16 + var APISpec []byte
+172 -1
pb/openapi.json
··· 21 21 } 22 22 }, 23 23 "type": "object" 24 + }, 25 + "xeiaso.net.protofeed.Attachement": { 26 + "properties": { 27 + "duration_in_seconds": { 28 + "format": "int32", 29 + "type": "integer" 30 + }, 31 + "mime_type": { 32 + "type": "string" 33 + }, 34 + "size_in_bytes": { 35 + "format": "int32", 36 + "type": "integer" 37 + }, 38 + "title": { 39 + "type": "string" 40 + }, 41 + "url": { 42 + "type": "string" 43 + } 44 + }, 45 + "type": "object" 46 + }, 47 + "xeiaso.net.protofeed.Author": { 48 + "properties": { 49 + "avatar": { 50 + "type": "string" 51 + }, 52 + "name": { 53 + "type": "string" 54 + }, 55 + "url": { 56 + "type": "string" 57 + } 58 + }, 59 + "type": "object" 60 + }, 61 + "xeiaso.net.protofeed.Feed": { 62 + "properties": { 63 + "authors": { 64 + "items": { 65 + "$ref": "#/components/schemas/xeiaso.net.protofeed.Author" 66 + }, 67 + "type": "array" 68 + }, 69 + "description": { 70 + "type": "string" 71 + }, 72 + "expired": { 73 + "type": "boolean" 74 + }, 75 + "favicon": { 76 + "type": "string" 77 + }, 78 + "feed_url": { 79 + "type": "string" 80 + }, 81 + "home_page_url": { 82 + "type": "string" 83 + }, 84 + "icon": { 85 + "type": "string" 86 + }, 87 + "items": { 88 + "items": { 89 + "$ref": "#/components/schemas/xeiaso.net.protofeed.Item" 90 + }, 91 + "type": "array" 92 + }, 93 + "language": { 94 + "type": "string" 95 + }, 96 + "next_url": { 97 + "type": "string" 98 + }, 99 + "title": { 100 + "type": "string" 101 + }, 102 + "user_comment": { 103 + "type": "string" 104 + }, 105 + "version": { 106 + "type": "string" 107 + } 108 + }, 109 + "type": "object" 110 + }, 111 + "xeiaso.net.protofeed.Item": { 112 + "properties": { 113 + "attachments": { 114 + "items": { 115 + "$ref": "#/components/schemas/xeiaso.net.protofeed.Attachement" 116 + }, 117 + "type": "array" 118 + }, 119 + "authors": { 120 + "items": { 121 + "$ref": "#/components/schemas/xeiaso.net.protofeed.Author" 122 + }, 123 + "type": "array" 124 + }, 125 + "banner_image": { 126 + "type": "string" 127 + }, 128 + "content_html": { 129 + "type": "string" 130 + }, 131 + "content_text": { 132 + "type": "string" 133 + }, 134 + "date_modified": { 135 + "format": "date-time", 136 + "type": "string" 137 + }, 138 + "date_published": { 139 + "format": "date-time", 140 + "type": "string" 141 + }, 142 + "external_url": { 143 + "type": "string" 144 + }, 145 + "id": { 146 + "type": "string" 147 + }, 148 + "image": { 149 + "type": "string" 150 + }, 151 + "language": { 152 + "type": "string" 153 + }, 154 + "summary": { 155 + "type": "string" 156 + }, 157 + "tags": { 158 + "items": { 159 + "type": "string" 160 + }, 161 + "type": "array" 162 + }, 163 + "title": { 164 + "type": "string" 165 + }, 166 + "url": { 167 + "type": "string" 168 + } 169 + }, 170 + "type": "object" 24 171 } 25 172 } 26 173 }, 27 174 "info": { 28 - "title": "xeiaso.net API", 175 + "title": "xeiaso.net", 29 176 "version": "0.1" 30 177 }, 31 178 "openapi": "3.0.0", 32 179 "paths": { 180 + "/api/xeiaso.net.Feed/Get": { 181 + "post": { 182 + "description": "\nGet fetches the current feed of posts", 183 + "requestBody": { 184 + "content": { 185 + "application/json": {} 186 + } 187 + }, 188 + "responses": { 189 + "200": { 190 + "content": { 191 + "application/json": { 192 + "schema": { 193 + "$ref": "#/components/schemas/xeiaso.net.protofeed.Feed" 194 + } 195 + } 196 + }, 197 + "description": "Success" 198 + } 199 + }, 200 + "summary": "Get" 201 + } 202 + }, 33 203 "/api/xeiaso.net.Meta/Metadata": { 34 204 "post": { 205 + "description": "\nMetadata fetches the build metadata of the version of xesite that is currently running", 35 206 "requestBody": { 36 207 "content": { 37 208 "application/json": {}
+33 -24
pb/xesite.pb.go
··· 13 13 timestamppb "google.golang.org/protobuf/types/known/timestamppb" 14 14 reflect "reflect" 15 15 sync "sync" 16 + protofeed "xeiaso.net/v4/pb/external/protofeed" 16 17 ) 17 18 18 19 const ( ··· 109 110 0x6c, 0x65, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x65, 0x6d, 0x70, 0x74, 110 111 0x79, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x1f, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2f, 111 112 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2f, 0x74, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 112 - 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0xc7, 0x01, 0x0a, 0x09, 0x42, 0x75, 0x69, 113 - 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 114 - 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x39, 115 - 0x0a, 0x0a, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 116 - 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 117 - 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 118 - 0x62, 0x75, 0x69, 0x6c, 0x64, 0x54, 0x69, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x6f, 0x5f, 119 - 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 120 - 0x6f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x6e, 0x6f, 121 - 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 122 - 0x64, 0x65, 0x6e, 0x6f, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x78, 123 - 0x65, 0x73, 0x69, 0x74, 0x65, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 124 - 0x01, 0x28, 0x09, 0x52, 0x0d, 0x78, 0x65, 0x73, 0x69, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 125 - 0x6f, 0x6e, 0x32, 0x41, 0x0a, 0x04, 0x4d, 0x65, 0x74, 0x61, 0x12, 0x39, 0x0a, 0x08, 0x4d, 0x65, 126 - 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 127 - 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 128 - 0x2e, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x42, 0x75, 0x69, 0x6c, 129 - 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x42, 0x12, 0x5a, 0x10, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 130 - 0x6e, 0x65, 0x74, 0x2f, 0x76, 0x34, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 131 - 0x33, 113 + 0x6d, 0x70, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x1a, 0x18, 0x65, 0x78, 0x74, 0x65, 0x72, 0x6e, 114 + 0x61, 0x6c, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x65, 0x65, 0x64, 0x2e, 0x70, 0x72, 0x6f, 115 + 0x74, 0x6f, 0x22, 0xc7, 0x01, 0x0a, 0x09, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 116 + 0x12, 0x16, 0x0a, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 117 + 0x52, 0x06, 0x63, 0x6f, 0x6d, 0x6d, 0x69, 0x74, 0x12, 0x39, 0x0a, 0x0a, 0x62, 0x75, 0x69, 0x6c, 118 + 0x64, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x1a, 0x2e, 0x67, 119 + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x54, 120 + 0x69, 0x6d, 0x65, 0x73, 0x74, 0x61, 0x6d, 0x70, 0x52, 0x09, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x54, 121 + 0x69, 0x6d, 0x65, 0x12, 0x1d, 0x0a, 0x0a, 0x67, 0x6f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 122 + 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x67, 0x6f, 0x56, 0x65, 0x72, 0x73, 0x69, 123 + 0x6f, 0x6e, 0x12, 0x21, 0x0a, 0x0c, 0x64, 0x65, 0x6e, 0x6f, 0x5f, 0x76, 0x65, 0x72, 0x73, 0x69, 124 + 0x6f, 0x6e, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0b, 0x64, 0x65, 0x6e, 0x6f, 0x56, 0x65, 125 + 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x12, 0x25, 0x0a, 0x0e, 0x78, 0x65, 0x73, 0x69, 0x74, 0x65, 0x5f, 126 + 0x76, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x78, 127 + 0x65, 0x73, 0x69, 0x74, 0x65, 0x56, 0x65, 0x72, 0x73, 0x69, 0x6f, 0x6e, 0x32, 0x41, 0x0a, 0x04, 128 + 0x4d, 0x65, 0x74, 0x61, 0x12, 0x39, 0x0a, 0x08, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 129 + 0x12, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 130 + 0x75, 0x66, 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x15, 0x2e, 0x78, 0x65, 0x69, 0x61, 0x73, 131 + 0x6f, 0x2e, 0x6e, 0x65, 0x74, 0x2e, 0x42, 0x75, 0x69, 0x6c, 0x64, 0x49, 0x6e, 0x66, 0x6f, 0x32, 132 + 0x41, 0x0a, 0x04, 0x46, 0x65, 0x65, 0x64, 0x12, 0x39, 0x0a, 0x03, 0x47, 0x65, 0x74, 0x12, 0x16, 133 + 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 134 + 0x2e, 0x45, 0x6d, 0x70, 0x74, 0x79, 0x1a, 0x1a, 0x2e, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 135 + 0x6e, 0x65, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x66, 0x65, 0x65, 0x64, 0x2e, 0x46, 0x65, 136 + 0x65, 0x64, 0x42, 0x12, 0x5a, 0x10, 0x78, 0x65, 0x69, 0x61, 0x73, 0x6f, 0x2e, 0x6e, 0x65, 0x74, 137 + 0x2f, 0x76, 0x34, 0x2f, 0x70, 0x62, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, 132 138 } 133 139 134 140 var ( ··· 148 154 (*BuildInfo)(nil), // 0: xeiaso.net.BuildInfo 149 155 (*timestamppb.Timestamp)(nil), // 1: google.protobuf.Timestamp 150 156 (*emptypb.Empty)(nil), // 2: google.protobuf.Empty 157 + (*protofeed.Feed)(nil), // 3: xeiaso.net.protofeed.Feed 151 158 } 152 159 var file_xesite_proto_depIdxs = []int32{ 153 160 1, // 0: xeiaso.net.BuildInfo.build_time:type_name -> google.protobuf.Timestamp 154 161 2, // 1: xeiaso.net.Meta.Metadata:input_type -> google.protobuf.Empty 155 - 0, // 2: xeiaso.net.Meta.Metadata:output_type -> xeiaso.net.BuildInfo 156 - 2, // [2:3] is the sub-list for method output_type 157 - 1, // [1:2] is the sub-list for method input_type 162 + 2, // 2: xeiaso.net.Feed.Get:input_type -> google.protobuf.Empty 163 + 0, // 3: xeiaso.net.Meta.Metadata:output_type -> xeiaso.net.BuildInfo 164 + 3, // 4: xeiaso.net.Feed.Get:output_type -> xeiaso.net.protofeed.Feed 165 + 3, // [3:5] is the sub-list for method output_type 166 + 1, // [1:3] is the sub-list for method input_type 158 167 1, // [1:1] is the sub-list for extension type_name 159 168 1, // [1:1] is the sub-list for extension extendee 160 169 0, // [0:1] is the sub-list for field type_name ··· 187 196 NumEnums: 0, 188 197 NumMessages: 1, 189 198 NumExtensions: 0, 190 - NumServices: 1, 199 + NumServices: 2, 191 200 }, 192 201 GoTypes: file_xesite_proto_goTypes, 193 202 DependencyIndexes: file_xesite_proto_depIdxs,
+11 -1
pb/xesite.proto
··· 5 5 import "google/protobuf/empty.proto"; 6 6 import "google/protobuf/timestamp.proto"; 7 7 8 - service Meta { rpc Metadata(google.protobuf.Empty) returns (BuildInfo); } 8 + import "external/protofeed.proto"; 9 + 10 + service Meta { 11 + // Metadata fetches the build metadata of the version of xesite that is currently running 12 + rpc Metadata(google.protobuf.Empty) returns (BuildInfo); 13 + } 9 14 10 15 message BuildInfo { 11 16 string commit = 1; ··· 13 18 string go_version = 3; 14 19 string deno_version = 4; 15 20 string xesite_version = 5; 21 + } 22 + 23 + service Feed { 24 + // Get fetches the current feed of posts 25 + rpc Get(google.protobuf.Empty) returns (xeiaso.net.protofeed.Feed); 16 26 }
+517 -18
pb/xesite.twirp.go
··· 17 17 import ctxsetters "github.com/twitchtv/twirp/ctxsetters" 18 18 19 19 import google_protobuf "google.golang.org/protobuf/types/known/emptypb" 20 + import xeiaso_net_protofeed "xeiaso.net/v4/pb/external/protofeed" 20 21 21 22 import bytes "bytes" 22 23 import errors "errors" ··· 34 35 // ============== 35 36 36 37 type Meta interface { 38 + // Metadata fetches the build metadata of the version of xesite that is currently running 37 39 Metadata(context.Context, *google_protobuf.Empty) (*BuildInfo, error) 38 40 } 39 41 ··· 523 525 return baseServicePath(s.pathPrefix, "xeiaso.net", "Meta") 524 526 } 525 527 528 + // ============== 529 + // Feed Interface 530 + // ============== 531 + 532 + type Feed interface { 533 + // Get fetches the current feed of posts 534 + Get(context.Context, *google_protobuf.Empty) (*xeiaso_net_protofeed.Feed, error) 535 + } 536 + 537 + // ==================== 538 + // Feed Protobuf Client 539 + // ==================== 540 + 541 + type feedProtobufClient struct { 542 + client HTTPClient 543 + urls [1]string 544 + interceptor twirp.Interceptor 545 + opts twirp.ClientOptions 546 + } 547 + 548 + // NewFeedProtobufClient creates a Protobuf client that implements the Feed interface. 549 + // It communicates using Protobuf and can be configured with a custom HTTPClient. 550 + func NewFeedProtobufClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Feed { 551 + if c, ok := client.(*http.Client); ok { 552 + client = withoutRedirects(c) 553 + } 554 + 555 + clientOpts := twirp.ClientOptions{} 556 + for _, o := range opts { 557 + o(&clientOpts) 558 + } 559 + 560 + // Using ReadOpt allows backwards and forwards compatibility with new options in the future 561 + literalURLs := false 562 + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) 563 + var pathPrefix string 564 + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { 565 + pathPrefix = "/twirp" // default prefix 566 + } 567 + 568 + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> 569 + serviceURL := sanitizeBaseURL(baseURL) 570 + serviceURL += baseServicePath(pathPrefix, "xeiaso.net", "Feed") 571 + urls := [1]string{ 572 + serviceURL + "Get", 573 + } 574 + 575 + return &feedProtobufClient{ 576 + client: client, 577 + urls: urls, 578 + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), 579 + opts: clientOpts, 580 + } 581 + } 582 + 583 + func (c *feedProtobufClient) Get(ctx context.Context, in *google_protobuf.Empty) (*xeiaso_net_protofeed.Feed, error) { 584 + ctx = ctxsetters.WithPackageName(ctx, "xeiaso.net") 585 + ctx = ctxsetters.WithServiceName(ctx, "Feed") 586 + ctx = ctxsetters.WithMethodName(ctx, "Get") 587 + caller := c.callGet 588 + if c.interceptor != nil { 589 + caller = func(ctx context.Context, req *google_protobuf.Empty) (*xeiaso_net_protofeed.Feed, error) { 590 + resp, err := c.interceptor( 591 + func(ctx context.Context, req interface{}) (interface{}, error) { 592 + typedReq, ok := req.(*google_protobuf.Empty) 593 + if !ok { 594 + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") 595 + } 596 + return c.callGet(ctx, typedReq) 597 + }, 598 + )(ctx, req) 599 + if resp != nil { 600 + typedResp, ok := resp.(*xeiaso_net_protofeed.Feed) 601 + if !ok { 602 + return nil, twirp.InternalError("failed type assertion resp.(*xeiaso_net_protofeed.Feed) when calling interceptor") 603 + } 604 + return typedResp, err 605 + } 606 + return nil, err 607 + } 608 + } 609 + return caller(ctx, in) 610 + } 611 + 612 + func (c *feedProtobufClient) callGet(ctx context.Context, in *google_protobuf.Empty) (*xeiaso_net_protofeed.Feed, error) { 613 + out := new(xeiaso_net_protofeed.Feed) 614 + ctx, err := doProtobufRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) 615 + if err != nil { 616 + twerr, ok := err.(twirp.Error) 617 + if !ok { 618 + twerr = twirp.InternalErrorWith(err) 619 + } 620 + callClientError(ctx, c.opts.Hooks, twerr) 621 + return nil, err 622 + } 623 + 624 + callClientResponseReceived(ctx, c.opts.Hooks) 625 + 626 + return out, nil 627 + } 628 + 629 + // ================ 630 + // Feed JSON Client 631 + // ================ 632 + 633 + type feedJSONClient struct { 634 + client HTTPClient 635 + urls [1]string 636 + interceptor twirp.Interceptor 637 + opts twirp.ClientOptions 638 + } 639 + 640 + // NewFeedJSONClient creates a JSON client that implements the Feed interface. 641 + // It communicates using JSON and can be configured with a custom HTTPClient. 642 + func NewFeedJSONClient(baseURL string, client HTTPClient, opts ...twirp.ClientOption) Feed { 643 + if c, ok := client.(*http.Client); ok { 644 + client = withoutRedirects(c) 645 + } 646 + 647 + clientOpts := twirp.ClientOptions{} 648 + for _, o := range opts { 649 + o(&clientOpts) 650 + } 651 + 652 + // Using ReadOpt allows backwards and forwards compatibility with new options in the future 653 + literalURLs := false 654 + _ = clientOpts.ReadOpt("literalURLs", &literalURLs) 655 + var pathPrefix string 656 + if ok := clientOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { 657 + pathPrefix = "/twirp" // default prefix 658 + } 659 + 660 + // Build method URLs: <baseURL>[<prefix>]/<package>.<Service>/<Method> 661 + serviceURL := sanitizeBaseURL(baseURL) 662 + serviceURL += baseServicePath(pathPrefix, "xeiaso.net", "Feed") 663 + urls := [1]string{ 664 + serviceURL + "Get", 665 + } 666 + 667 + return &feedJSONClient{ 668 + client: client, 669 + urls: urls, 670 + interceptor: twirp.ChainInterceptors(clientOpts.Interceptors...), 671 + opts: clientOpts, 672 + } 673 + } 674 + 675 + func (c *feedJSONClient) Get(ctx context.Context, in *google_protobuf.Empty) (*xeiaso_net_protofeed.Feed, error) { 676 + ctx = ctxsetters.WithPackageName(ctx, "xeiaso.net") 677 + ctx = ctxsetters.WithServiceName(ctx, "Feed") 678 + ctx = ctxsetters.WithMethodName(ctx, "Get") 679 + caller := c.callGet 680 + if c.interceptor != nil { 681 + caller = func(ctx context.Context, req *google_protobuf.Empty) (*xeiaso_net_protofeed.Feed, error) { 682 + resp, err := c.interceptor( 683 + func(ctx context.Context, req interface{}) (interface{}, error) { 684 + typedReq, ok := req.(*google_protobuf.Empty) 685 + if !ok { 686 + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") 687 + } 688 + return c.callGet(ctx, typedReq) 689 + }, 690 + )(ctx, req) 691 + if resp != nil { 692 + typedResp, ok := resp.(*xeiaso_net_protofeed.Feed) 693 + if !ok { 694 + return nil, twirp.InternalError("failed type assertion resp.(*xeiaso_net_protofeed.Feed) when calling interceptor") 695 + } 696 + return typedResp, err 697 + } 698 + return nil, err 699 + } 700 + } 701 + return caller(ctx, in) 702 + } 703 + 704 + func (c *feedJSONClient) callGet(ctx context.Context, in *google_protobuf.Empty) (*xeiaso_net_protofeed.Feed, error) { 705 + out := new(xeiaso_net_protofeed.Feed) 706 + ctx, err := doJSONRequest(ctx, c.client, c.opts.Hooks, c.urls[0], in, out) 707 + if err != nil { 708 + twerr, ok := err.(twirp.Error) 709 + if !ok { 710 + twerr = twirp.InternalErrorWith(err) 711 + } 712 + callClientError(ctx, c.opts.Hooks, twerr) 713 + return nil, err 714 + } 715 + 716 + callClientResponseReceived(ctx, c.opts.Hooks) 717 + 718 + return out, nil 719 + } 720 + 721 + // =================== 722 + // Feed Server Handler 723 + // =================== 724 + 725 + type feedServer struct { 726 + Feed 727 + interceptor twirp.Interceptor 728 + hooks *twirp.ServerHooks 729 + pathPrefix string // prefix for routing 730 + jsonSkipDefaults bool // do not include unpopulated fields (default values) in the response 731 + jsonCamelCase bool // JSON fields are serialized as lowerCamelCase rather than keeping the original proto names 732 + } 733 + 734 + // NewFeedServer builds a TwirpServer that can be used as an http.Handler to handle 735 + // HTTP requests that are routed to the right method in the provided svc implementation. 736 + // The opts are twirp.ServerOption modifiers, for example twirp.WithServerHooks(hooks). 737 + func NewFeedServer(svc Feed, opts ...interface{}) TwirpServer { 738 + serverOpts := newServerOpts(opts) 739 + 740 + // Using ReadOpt allows backwards and forwards compatibility with new options in the future 741 + jsonSkipDefaults := false 742 + _ = serverOpts.ReadOpt("jsonSkipDefaults", &jsonSkipDefaults) 743 + jsonCamelCase := false 744 + _ = serverOpts.ReadOpt("jsonCamelCase", &jsonCamelCase) 745 + var pathPrefix string 746 + if ok := serverOpts.ReadOpt("pathPrefix", &pathPrefix); !ok { 747 + pathPrefix = "/twirp" // default prefix 748 + } 749 + 750 + return &feedServer{ 751 + Feed: svc, 752 + hooks: serverOpts.Hooks, 753 + interceptor: twirp.ChainInterceptors(serverOpts.Interceptors...), 754 + pathPrefix: pathPrefix, 755 + jsonSkipDefaults: jsonSkipDefaults, 756 + jsonCamelCase: jsonCamelCase, 757 + } 758 + } 759 + 760 + // writeError writes an HTTP response with a valid Twirp error format, and triggers hooks. 761 + // If err is not a twirp.Error, it will get wrapped with twirp.InternalErrorWith(err) 762 + func (s *feedServer) writeError(ctx context.Context, resp http.ResponseWriter, err error) { 763 + writeError(ctx, resp, err, s.hooks) 764 + } 765 + 766 + // handleRequestBodyError is used to handle error when the twirp server cannot read request 767 + func (s *feedServer) handleRequestBodyError(ctx context.Context, resp http.ResponseWriter, msg string, err error) { 768 + if context.Canceled == ctx.Err() { 769 + s.writeError(ctx, resp, twirp.NewError(twirp.Canceled, "failed to read request: context canceled")) 770 + return 771 + } 772 + if context.DeadlineExceeded == ctx.Err() { 773 + s.writeError(ctx, resp, twirp.NewError(twirp.DeadlineExceeded, "failed to read request: deadline exceeded")) 774 + return 775 + } 776 + s.writeError(ctx, resp, twirp.WrapError(malformedRequestError(msg), err)) 777 + } 778 + 779 + // FeedPathPrefix is a convenience constant that may identify URL paths. 780 + // Should be used with caution, it only matches routes generated by Twirp Go clients, 781 + // with the default "/twirp" prefix and default CamelCase service and method names. 782 + // More info: https://twitchtv.github.io/twirp/docs/routing.html 783 + const FeedPathPrefix = "/twirp/xeiaso.net.Feed/" 784 + 785 + func (s *feedServer) ServeHTTP(resp http.ResponseWriter, req *http.Request) { 786 + ctx := req.Context() 787 + ctx = ctxsetters.WithPackageName(ctx, "xeiaso.net") 788 + ctx = ctxsetters.WithServiceName(ctx, "Feed") 789 + ctx = ctxsetters.WithResponseWriter(ctx, resp) 790 + 791 + var err error 792 + ctx, err = callRequestReceived(ctx, s.hooks) 793 + if err != nil { 794 + s.writeError(ctx, resp, err) 795 + return 796 + } 797 + 798 + if req.Method != "POST" { 799 + msg := fmt.Sprintf("unsupported method %q (only POST is allowed)", req.Method) 800 + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 801 + return 802 + } 803 + 804 + // Verify path format: [<prefix>]/<package>.<Service>/<Method> 805 + prefix, pkgService, method := parseTwirpPath(req.URL.Path) 806 + if pkgService != "xeiaso.net.Feed" { 807 + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) 808 + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 809 + return 810 + } 811 + if prefix != s.pathPrefix { 812 + msg := fmt.Sprintf("invalid path prefix %q, expected %q, on path %q", prefix, s.pathPrefix, req.URL.Path) 813 + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 814 + return 815 + } 816 + 817 + switch method { 818 + case "Get": 819 + s.serveGet(ctx, resp, req) 820 + return 821 + default: 822 + msg := fmt.Sprintf("no handler for path %q", req.URL.Path) 823 + s.writeError(ctx, resp, badRouteError(msg, req.Method, req.URL.Path)) 824 + return 825 + } 826 + } 827 + 828 + func (s *feedServer) serveGet(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 829 + header := req.Header.Get("Content-Type") 830 + i := strings.Index(header, ";") 831 + if i == -1 { 832 + i = len(header) 833 + } 834 + switch strings.TrimSpace(strings.ToLower(header[:i])) { 835 + case "application/json": 836 + s.serveGetJSON(ctx, resp, req) 837 + case "application/protobuf": 838 + s.serveGetProtobuf(ctx, resp, req) 839 + default: 840 + msg := fmt.Sprintf("unexpected Content-Type: %q", req.Header.Get("Content-Type")) 841 + twerr := badRouteError(msg, req.Method, req.URL.Path) 842 + s.writeError(ctx, resp, twerr) 843 + } 844 + } 845 + 846 + func (s *feedServer) serveGetJSON(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 847 + var err error 848 + ctx = ctxsetters.WithMethodName(ctx, "Get") 849 + ctx, err = callRequestRouted(ctx, s.hooks) 850 + if err != nil { 851 + s.writeError(ctx, resp, err) 852 + return 853 + } 854 + 855 + d := json.NewDecoder(req.Body) 856 + rawReqBody := json.RawMessage{} 857 + if err := d.Decode(&rawReqBody); err != nil { 858 + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 859 + return 860 + } 861 + reqContent := new(google_protobuf.Empty) 862 + unmarshaler := protojson.UnmarshalOptions{DiscardUnknown: true} 863 + if err = unmarshaler.Unmarshal(rawReqBody, reqContent); err != nil { 864 + s.handleRequestBodyError(ctx, resp, "the json request could not be decoded", err) 865 + return 866 + } 867 + 868 + handler := s.Feed.Get 869 + if s.interceptor != nil { 870 + handler = func(ctx context.Context, req *google_protobuf.Empty) (*xeiaso_net_protofeed.Feed, error) { 871 + resp, err := s.interceptor( 872 + func(ctx context.Context, req interface{}) (interface{}, error) { 873 + typedReq, ok := req.(*google_protobuf.Empty) 874 + if !ok { 875 + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") 876 + } 877 + return s.Feed.Get(ctx, typedReq) 878 + }, 879 + )(ctx, req) 880 + if resp != nil { 881 + typedResp, ok := resp.(*xeiaso_net_protofeed.Feed) 882 + if !ok { 883 + return nil, twirp.InternalError("failed type assertion resp.(*xeiaso_net_protofeed.Feed) when calling interceptor") 884 + } 885 + return typedResp, err 886 + } 887 + return nil, err 888 + } 889 + } 890 + 891 + // Call service method 892 + var respContent *xeiaso_net_protofeed.Feed 893 + func() { 894 + defer ensurePanicResponses(ctx, resp, s.hooks) 895 + respContent, err = handler(ctx, reqContent) 896 + }() 897 + 898 + if err != nil { 899 + s.writeError(ctx, resp, err) 900 + return 901 + } 902 + if respContent == nil { 903 + s.writeError(ctx, resp, twirp.InternalError("received a nil *xeiaso_net_protofeed.Feed and nil error while calling Get. nil responses are not supported")) 904 + return 905 + } 906 + 907 + ctx = callResponsePrepared(ctx, s.hooks) 908 + 909 + marshaler := &protojson.MarshalOptions{UseProtoNames: !s.jsonCamelCase, EmitUnpopulated: !s.jsonSkipDefaults} 910 + respBytes, err := marshaler.Marshal(respContent) 911 + if err != nil { 912 + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal json response")) 913 + return 914 + } 915 + 916 + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 917 + resp.Header().Set("Content-Type", "application/json") 918 + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 919 + resp.WriteHeader(http.StatusOK) 920 + 921 + if n, err := resp.Write(respBytes); err != nil { 922 + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 923 + twerr := twirp.NewError(twirp.Unknown, msg) 924 + ctx = callError(ctx, s.hooks, twerr) 925 + } 926 + callResponseSent(ctx, s.hooks) 927 + } 928 + 929 + func (s *feedServer) serveGetProtobuf(ctx context.Context, resp http.ResponseWriter, req *http.Request) { 930 + var err error 931 + ctx = ctxsetters.WithMethodName(ctx, "Get") 932 + ctx, err = callRequestRouted(ctx, s.hooks) 933 + if err != nil { 934 + s.writeError(ctx, resp, err) 935 + return 936 + } 937 + 938 + buf, err := io.ReadAll(req.Body) 939 + if err != nil { 940 + s.handleRequestBodyError(ctx, resp, "failed to read request body", err) 941 + return 942 + } 943 + reqContent := new(google_protobuf.Empty) 944 + if err = proto.Unmarshal(buf, reqContent); err != nil { 945 + s.writeError(ctx, resp, malformedRequestError("the protobuf request could not be decoded")) 946 + return 947 + } 948 + 949 + handler := s.Feed.Get 950 + if s.interceptor != nil { 951 + handler = func(ctx context.Context, req *google_protobuf.Empty) (*xeiaso_net_protofeed.Feed, error) { 952 + resp, err := s.interceptor( 953 + func(ctx context.Context, req interface{}) (interface{}, error) { 954 + typedReq, ok := req.(*google_protobuf.Empty) 955 + if !ok { 956 + return nil, twirp.InternalError("failed type assertion req.(*google_protobuf.Empty) when calling interceptor") 957 + } 958 + return s.Feed.Get(ctx, typedReq) 959 + }, 960 + )(ctx, req) 961 + if resp != nil { 962 + typedResp, ok := resp.(*xeiaso_net_protofeed.Feed) 963 + if !ok { 964 + return nil, twirp.InternalError("failed type assertion resp.(*xeiaso_net_protofeed.Feed) when calling interceptor") 965 + } 966 + return typedResp, err 967 + } 968 + return nil, err 969 + } 970 + } 971 + 972 + // Call service method 973 + var respContent *xeiaso_net_protofeed.Feed 974 + func() { 975 + defer ensurePanicResponses(ctx, resp, s.hooks) 976 + respContent, err = handler(ctx, reqContent) 977 + }() 978 + 979 + if err != nil { 980 + s.writeError(ctx, resp, err) 981 + return 982 + } 983 + if respContent == nil { 984 + s.writeError(ctx, resp, twirp.InternalError("received a nil *xeiaso_net_protofeed.Feed and nil error while calling Get. nil responses are not supported")) 985 + return 986 + } 987 + 988 + ctx = callResponsePrepared(ctx, s.hooks) 989 + 990 + respBytes, err := proto.Marshal(respContent) 991 + if err != nil { 992 + s.writeError(ctx, resp, wrapInternal(err, "failed to marshal proto response")) 993 + return 994 + } 995 + 996 + ctx = ctxsetters.WithStatusCode(ctx, http.StatusOK) 997 + resp.Header().Set("Content-Type", "application/protobuf") 998 + resp.Header().Set("Content-Length", strconv.Itoa(len(respBytes))) 999 + resp.WriteHeader(http.StatusOK) 1000 + if n, err := resp.Write(respBytes); err != nil { 1001 + msg := fmt.Sprintf("failed to write response, %d of %d bytes written: %s", n, len(respBytes), err.Error()) 1002 + twerr := twirp.NewError(twirp.Unknown, msg) 1003 + ctx = callError(ctx, s.hooks, twerr) 1004 + } 1005 + callResponseSent(ctx, s.hooks) 1006 + } 1007 + 1008 + func (s *feedServer) ServiceDescriptor() ([]byte, int) { 1009 + return twirpFileDescriptor0, 1 1010 + } 1011 + 1012 + func (s *feedServer) ProtocGenTwirpVersion() string { 1013 + return "v8.1.3" 1014 + } 1015 + 1016 + // PathPrefix returns the base service path, in the form: "/<prefix>/<package>.<Service>/" 1017 + // that is everything in a Twirp route except for the <Method>. This can be used for routing, 1018 + // for example to identify the requests that are targeted to this service in a mux. 1019 + func (s *feedServer) PathPrefix() string { 1020 + return baseServicePath(s.pathPrefix, "xeiaso.net", "Feed") 1021 + } 1022 + 526 1023 // ===== 527 1024 // Utils 528 1025 // ===== ··· 1089 1586 } 1090 1587 1091 1588 var twirpFileDescriptor0 = []byte{ 1092 - // 258 bytes of a gzipped FileDescriptorProto 1093 - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x64, 0x90, 0xc1, 0x4a, 0xc3, 0x40, 1094 - 0x10, 0x86, 0x89, 0xd6, 0x62, 0xa6, 0x55, 0x64, 0xc1, 0x12, 0x22, 0x62, 0x15, 0x84, 0x9e, 0x36, 1095 - 0x50, 0xbd, 0xf4, 0x68, 0xc1, 0x83, 0x07, 0x2f, 0x45, 0x3c, 0x78, 0x29, 0x1b, 0x33, 0x0d, 0x0b, 1096 - 0xdd, 0x4c, 0xe8, 0x4e, 0x4b, 0x7d, 0x42, 0x5f, 0x4b, 0x76, 0xb7, 0x49, 0xc0, 0x9e, 0x96, 0xf9, 1097 - 0xe7, 0x9b, 0x8f, 0x9d, 0x81, 0xe1, 0x1e, 0xad, 0x66, 0x94, 0xf5, 0x86, 0x98, 0x04, 0xec, 0x51, 1098 - 0x2b, 0x4b, 0xb2, 0x42, 0x4e, 0x6f, 0x4a, 0xa2, 0x72, 0x8d, 0x99, 0xef, 0xe4, 0xdb, 0x55, 0x86, 1099 - 0xa6, 0xe6, 0x9f, 0x00, 0xa6, 0x77, 0xff, 0x9b, 0xac, 0x0d, 0x5a, 0x56, 0xa6, 0x0e, 0xc0, 0xc3, 1100 - 0x6f, 0x04, 0xf1, 0x7c, 0xab, 0xd7, 0xc5, 0x5b, 0xb5, 0x22, 0x31, 0x82, 0xfe, 0x37, 0x19, 0xa3, 1101 - 0x39, 0x89, 0xc6, 0xd1, 0x24, 0x5e, 0x1c, 0x2a, 0x31, 0x03, 0xc8, 0x1d, 0xb4, 0x74, 0xe3, 0xc9, 1102 - 0xc9, 0x38, 0x9a, 0x0c, 0xa6, 0xa9, 0x0c, 0x6e, 0xd9, 0xb8, 0xe5, 0x47, 0xe3, 0x5e, 0xc4, 0x9e, 1103 - 0x76, 0xb5, 0xb8, 0x05, 0x28, 0x69, 0xb9, 0xc3, 0x8d, 0xd5, 0x54, 0x25, 0xa7, 0x5e, 0x1b, 0x97, 1104 - 0xf4, 0x19, 0x02, 0x71, 0x0f, 0xc3, 0x02, 0xab, 0x0e, 0xe8, 0x79, 0x60, 0xe0, 0xb2, 0x06, 0x79, 1105 - 0x84, 0xcb, 0xb0, 0x7c, 0x0b, 0x9d, 0x79, 0xe8, 0x22, 0xa4, 0x07, 0x6c, 0xfa, 0x02, 0xbd, 0x77, 1106 - 0x64, 0x25, 0x66, 0x70, 0xee, 0xde, 0x42, 0xb1, 0x12, 0xa3, 0xa3, 0x3f, 0xbe, 0xba, 0xe3, 0xa4, 1107 - 0xd7, 0xb2, 0x3b, 0xa0, 0x6c, 0xd7, 0x9f, 0x8b, 0xaf, 0xab, 0x2e, 0xcf, 0x76, 0xcf, 0x59, 0x9d, 1108 - 0xe7, 0x7d, 0x3f, 0xfa, 0xf4, 0x17, 0x00, 0x00, 0xff, 0xff, 0xdc, 0x61, 0x9c, 0x09, 0x81, 0x01, 1109 - 0x00, 0x00, 1589 + // 289 bytes of a gzipped FileDescriptorProto 1590 + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x90, 0x41, 0x4b, 0xc3, 0x40, 1591 + 0x10, 0x85, 0x89, 0xad, 0xc5, 0x4c, 0xab, 0xc8, 0x82, 0x25, 0x44, 0xc4, 0x2a, 0x08, 0x3d, 0x6d, 1592 + 0xa0, 0x7a, 0xe9, 0xd1, 0x82, 0x8a, 0x07, 0x2f, 0x45, 0x3c, 0x78, 0x29, 0x1b, 0x33, 0x09, 0x0b, 1593 + 0xc9, 0x6e, 0x48, 0xa6, 0x25, 0xfe, 0x42, 0xff, 0x96, 0xec, 0x6e, 0xd2, 0x88, 0xe2, 0x29, 0xcc, 1594 + 0xbc, 0x6f, 0xde, 0xcb, 0x3e, 0x98, 0x34, 0x58, 0x4b, 0x42, 0x5e, 0x56, 0x9a, 0x34, 0x83, 0x06, 1595 + 0xa5, 0xa8, 0x35, 0x57, 0x48, 0xe1, 0x79, 0xa6, 0x75, 0x96, 0x63, 0x64, 0x95, 0x78, 0x9b, 0x46, 1596 + 0x58, 0x94, 0xf4, 0xe9, 0xc0, 0xf0, 0xf2, 0xb7, 0x48, 0xb2, 0xc0, 0x9a, 0x44, 0x51, 0xb6, 0x40, 1597 + 0x80, 0x0d, 0x61, 0xa5, 0x44, 0xee, 0x90, 0x14, 0x31, 0x71, 0xca, 0xf5, 0x97, 0x07, 0xfe, 0x6a, 1598 + 0x2b, 0xf3, 0xe4, 0x59, 0xa5, 0x9a, 0x4d, 0x61, 0xf4, 0xa1, 0x8b, 0x42, 0x52, 0xe0, 0xcd, 0xbc, 1599 + 0xb9, 0xbf, 0x6e, 0x27, 0xb6, 0x04, 0x88, 0x0d, 0xb4, 0x31, 0xc6, 0xc1, 0xc1, 0xcc, 0x9b, 0x8f, 1600 + 0x17, 0x21, 0x77, 0xa9, 0xbc, 0x4b, 0xe5, 0xaf, 0x5d, 0xea, 0xda, 0xb7, 0xb4, 0x99, 0xd9, 0x05, 1601 + 0x40, 0xa6, 0x37, 0x3b, 0xac, 0x6a, 0xa9, 0x55, 0x30, 0xb0, 0xb6, 0x7e, 0xa6, 0xdf, 0xdc, 0x82, 1602 + 0x5d, 0xc1, 0x24, 0x41, 0xd5, 0x03, 0x43, 0x0b, 0x8c, 0xcd, 0xae, 0x43, 0x6e, 0xe0, 0xc4, 0xd5, 1603 + 0xb2, 0x87, 0x0e, 0x2d, 0x74, 0xec, 0xb6, 0x2d, 0xb6, 0xb8, 0x87, 0xe1, 0x0b, 0x92, 0x60, 0x4b, 1604 + 0x38, 0x32, 0xdf, 0x44, 0x90, 0x60, 0xd3, 0x3f, 0xff, 0xf8, 0x60, 0x6a, 0x0b, 0xcf, 0x78, 0x5f, 1605 + 0x2d, 0xdf, 0x3f, 0xdf, 0x58, 0x3c, 0x22, 0x26, 0x6c, 0x09, 0x83, 0x27, 0xa4, 0x7f, 0xaf, 0xc3, 1606 + 0x9f, 0xd7, 0x7d, 0xa1, 0xe6, 0x74, 0xc5, 0xde, 0x4f, 0x7b, 0x31, 0xda, 0xdd, 0x45, 0x65, 0x1c, 1607 + 0x8f, 0x2c, 0x73, 0xfb, 0x1d, 0x00, 0x00, 0xff, 0xff, 0x72, 0xb8, 0xce, 0x88, 0xde, 0x01, 0x00, 1608 + 0x00, 1110 1609 }