diff --git a/assets/common.css b/assets/common.css index 0b11e8379..cbd64ddba 100644 --- a/assets/common.css +++ b/assets/common.css @@ -176,6 +176,8 @@ thead th { border-bottom-width: 2px; } +.bold { font-weight: bold } + .btn { border: solid 1px var(--color); color: var(--color); diff --git a/go.mod b/go.mod index 273f8f5fe..16bcde18d 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ require ( github.com/SeerUK/assert v1.0.0 github.com/buildkite/terminal v3.2.0+incompatible github.com/cv-library/negroni-brotli v0.1.0 + github.com/gorilla/feeds v1.1.1 github.com/julienschmidt/httprouter v1.3.0 github.com/lib/pq v1.3.0 github.com/pmezard/go-difflib v1.0.0 diff --git a/go.sum b/go.sum index 2ef6ec20a..f1883367b 100644 --- a/go.sum +++ b/go.sum @@ -4,6 +4,8 @@ github.com/buildkite/terminal v3.2.0+incompatible h1:08p6611HADinUwK0oyxCaAsnFXV github.com/buildkite/terminal v3.2.0+incompatible/go.mod h1:iQavkS6X0wlozOmO2rxHYt/9mE5Ij2XTk6yGcclx6hk= github.com/cv-library/negroni-brotli v0.1.0 h1:XHp5az3a/+S/E0y9QgzvAQ6iKHwcaKfvMO83VKmH6o0= github.com/cv-library/negroni-brotli v0.1.0/go.mod h1:trJIsbpAH+Rex8NJCyemf2jEVucd7lcS8LTjvS2k6wY= +github.com/gorilla/feeds v1.1.1 h1:HwKXxqzcRNg9to+BbvJog4+f3s/xzvtZXICcQGutYfY= +github.com/gorilla/feeds v1.1.1/go.mod h1:Nk0jZrvPFZX1OBe5NPiddPw7CfwF6Q9eqzaBbaightA= github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/lib/pq v1.3.0 h1:/qkRGz8zljWiDcFvgpwUpwIAPu3r07TDvs3Rws+o/pU= diff --git a/routes/feeds.go b/routes/feeds.go new file mode 100644 index 000000000..b864c57b3 --- /dev/null +++ b/routes/feeds.go @@ -0,0 +1,168 @@ +package routes + +import ( + "net/http" + "net/url" + "strings" + "time" + + "github.com/gorilla/feeds" + "github.com/julienschmidt/httprouter" +) + +var atomFeed, jsonFeed, rssFeed []byte + +// TODO Trophies +// f761931 Add "Happy Birthday, Code Golf" trophy +// b8c8fc2 Add "Slowcoach" trophy +// dbe1a24 Add "Inception" trophy +// 412861e Add "It’s Over 9000!" trophy +// b9b1f50 Add "The Watering Hole" trophy +// eee3b47 Add "My God, It’s Full of Stars" trophy +// 82c3f33 Add "Polyglot" trophy +// 39b205c Add "ElePHPant in the Room" trophy +// c1503f7 initial +func init() { + feed := feeds.Feed{ + Link: &feeds.Link{Href: "https://code-golf.io/"}, + Title: "Code Golf", + } + + for _, i := range []struct { + sha, created, name string + hole bool + }{ + {"a9bbba9", "2020-01-03 19:05:17", "Rust", false}, + {"fb227ed", "2019-11-17 17:32:14", "Abundant Numbers", true}, + {"4e38900", "2019-07-11 16:34:13", "Ordinal Numbers", true}, + {"de9369a", "2019-06-17 16:33:28", "Rock-paper-scissors-Spock-lizard", true}, + {"5348743", "2019-06-12 22:10:04", "Ten-pin Bowling", true}, + {"14290db", "2019-05-25 10:22:43", "√2", true}, + {"269ff68", "2019-05-19 20:13:21", "Nim", false}, + {"3cbc7cf", "2019-04-07 14:33:47", "Brainfuck", false}, + {"c960e70", "2019-02-28 21:27:25", "C", false}, + {"c689b8a", "2019-01-13 17:09:36", "Cubes", true}, + {"2f87dea", "2018-12-03 18:34:16", "Leap Years", true}, + {"1178818", "2018-11-15 19:16:17", "Sudoku", true}, + {"447121b", "2018-09-02 16:27:32", "Julia", false}, + {"00660df", "2018-08-08 23:11:21", "J", false}, + {"edd2828", "2018-07-25 19:50:07", "Poker", true}, + {"646df41", "2018-07-06 21:49:47", "Rule 110", true}, + {"2080b94", "2018-06-07 07:22:01", "λ", true}, + {"834750b", "2018-06-06 20:54:22", "Diamonds", true}, + {"bd8e789", "2018-05-20 18:09:59", "Haskell", false}, + {"7b72ebc", "2018-05-03 17:30:42", "Niven Numbers", true}, + {"827599e", "2018-03-22 16:56:44", "Lisp", false}, + {"5790715", "2018-02-18 21:01:24", "Morse Decoder", true}, + {"5790715", "2018-02-18 21:01:24", "Morse Encoder", true}, + {"05e21ff", "2018-01-27 23:39:06", "Brainfuck", true}, + {"922fb91", "2018-01-10 20:36:47", "Divisors", true}, + {"d83fcf9", "2018-01-07 12:52:41", "Lua", false}, + {"079513e", "2017-12-08 17:12:30", "12 Days of Christmas", true}, + {"30fc7c2", "2017-12-05 15:27:34", "Christmas Trees", true}, + {"2dfbcfe", "2017-11-30 19:23:00", "Pangram Grep", true}, + {"31d18c8", "2017-11-28 20:31:07", "τ", true}, + {"a3ef71c", "2017-11-12 03:11:36", "Bash", false}, + {"0219147", "2017-11-11 00:02:40", "φ", true}, + {"63510fc", "2017-11-10 23:21:35", "Roman to Arabic", true}, + {"ee1742f", "2017-11-07 21:56:03", "Quine", true}, + {"ce2d6b9", "2017-10-31 04:39:43", "Happy Numbers", true}, + {"63bad53", "2017-10-18 19:13:56", "Pernicious Numbers", true}, + {"e71743f", "2017-10-18 18:24:51", "Evil Numbers", true}, + {"e71743f", "2017-10-18 18:24:51", "Odious Numbers", true}, + {"35da66a", "2017-10-08 02:11:33", "Sierpiński Triangle", true}, + {"aa9e81e", "2017-10-08 01:39:39", "Emirp Numbers", true}, + {"ac9b179", "2017-10-04 13:00:28", "Prime Numbers", true}, + {"39ce198", "2017-09-29 20:41:30", "Spelling Numbers", true}, + {"bb1a117", "2017-09-20 17:18:23", "𝑒", true}, + {"7475f08", "2017-09-16 13:57:43", "Fibonacci", true}, + {"7d34727", "2017-08-27 23:41:32", "Seven Segment", true}, + {"b1d91b8", "2017-07-22 22:22:25", "Arabic to Roman", true}, + {"07aa3ed", "2017-07-16 00:18:21", "JavaScript", false}, + {"b1d083d", "2017-07-15 21:24:09", "Python", false}, + {"26cf869", "2017-07-15 20:28:52", "Pascal’s Triangle", true}, + {"15bc065", "2017-07-06 21:37:43", "99 Bottles of Beer", true}, + {"9cc775e", "2017-07-02 17:00:43", "π", true}, + {"dc1b9a8", "2017-06-14 22:03:21", "PHP", false}, + {"c5468f0", "2017-06-12 23:34:47", "Raku", false}, + {"8029a96", "2017-05-08 23:06:22", "Ruby", false}, + } { + link := "https://code-golf.io/" + + if !i.hole { + link += "scores/all-holes/" + } + + link += url.PathEscape(strings.ReplaceAll(strings.ToLower(i.name), " ", "-")) + + item := feeds.Item{ + Description: "Added the “" + i.name + "” ", + Id: link, + Link: &feeds.Link{Href: link}, + Title: "Added “" + i.name + "” ", + } + + if i.hole { + item.Title += "Hole" + item.Description += "hole" + } else { + item.Title += "Language" + item.Description += "language" + } + + item.Description += " via " + i.sha + "." + + var err error + if item.Created, err = time.Parse("2006-01-02 15:04:05", i.created); err != nil { + panic(err) + } + + feed.Items = append(feed.Items, &item) + + if feed.Created.IsZero() { + feed.Created = item.Created + } + } + + feed.Title = "Code Golf (Atom Feed)" + + if data, err := feed.ToAtom(); err != nil { + panic(err) + } else { + atomFeed = []byte(data) + } + + feed.Title = "Code Golf (JSON Feed)" + + if data, err := feed.ToJSON(); err != nil { + panic(err) + } else { + jsonFeed = []byte(data) + } + + feed.Title = "Code Golf (RSS Feed)" + + if data, err := feed.ToRss(); err != nil { + panic(err) + } else { + rssFeed = []byte(data) + } +} + +// Feeds serves /feeds/:feed +func Feeds(w http.ResponseWriter, r *http.Request, ps httprouter.Params) { + switch ps.ByName("feed") { + case "atom": + w.Header().Set("Content-Type", "application/atom+xml; charset=utf-8") + w.Write(atomFeed) + case "json": + w.Header().Set("Content-Type", "application/json") + w.Write(jsonFeed) + case "rss": + w.Header().Set("Content-Type", "application/rss+xml; charset=utf-8") + w.Write(rssFeed) + default: + Render(w, r, http.StatusNotFound, "404", "", nil) + } +} diff --git a/routes/routes.go b/routes/routes.go index fa9ebc938..8ef3b7d1e 100644 --- a/routes/routes.go +++ b/routes/routes.go @@ -25,6 +25,7 @@ func init() { Router.GET("/assets/:asset", asset) Router.GET("/callback", callback) Router.GET("/favicon.ico", asset) + Router.GET("/feeds/:feed", Feeds) Router.GET("/ideas", ideas) Router.GET("/log-out", logOut) Router.GET("/random", random) diff --git a/views/header.html b/views/header.html index 8ae773f1f..32473e7d7 100644 --- a/views/header.html +++ b/views/header.html @@ -3,6 +3,9 @@ + + + {{/* FIXME This won't change for dark themes :-( */}} diff --git a/views/index.html b/views/index.html index 6bd9819d2..9876ef187 100644 --- a/views/index.html +++ b/views/index.html @@ -47,4 +47,11 @@