From d25cb0b579122362a6759e981d1cf9f6458e9959 Mon Sep 17 00:00:00 2001 From: Kevin Chabowski Date: Sun, 2 Mar 2014 15:48:54 +0100 Subject: Initial commit --- .gitignore | 1 + LICENSE | 5 +++ README | 1 + earthporn.go | 74 +++++++++++++++++++++++++++++++++++++ links.go | 34 +++++++++++++++++ main.go | 115 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ template.html | 62 +++++++++++++++++++++++++++++++ yr_no.go | 81 +++++++++++++++++++++++++++++++++++++++++ 8 files changed, 373 insertions(+) create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README create mode 100644 earthporn.go create mode 100644 links.go create mode 100644 main.go create mode 100644 template.html create mode 100644 yr_no.go diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..e55425f --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +startpage diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..d15d509 --- /dev/null +++ b/LICENSE @@ -0,0 +1,5 @@ + DO WHATEVER THE FUCK YOU WANT, PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. You just DO WHATEVER THE FUCK YOU WANT. + diff --git a/README b/README new file mode 100644 index 0000000..9a5bbc1 --- /dev/null +++ b/README @@ -0,0 +1 @@ +A simple start page with an Background image from /r/EarthPorn, weather from yr.no and customizable links. \ No newline at end of file diff --git a/earthporn.go b/earthporn.go new file mode 100644 index 0000000..ad1864a --- /dev/null +++ b/earthporn.go @@ -0,0 +1,74 @@ +package main + +import ( + "encoding/json" + "errors" + "mime" + "net/http" + "strings" +) + +type redditList struct { + Data struct { + Children []struct { + Data EarthPorn `json:"data"` + } `json:"children"` + } `json:"data"` +} + +type EarthPorn struct { + Title string `json:"title"` + URL string `json:"url,omitempty"` + Permalink string `json:"permalink"` +} + +const earthPornURL = "http://www.reddit.com/r/EarthPorn.json" + +func GetEarthPorn() (EarthPorn, error) { + resp, err := http.Get(earthPornURL) + if err != nil { + return EarthPorn{}, err + } + defer resp.Body.Close() + + var list redditList + dec := json.NewDecoder(resp.Body) + if err := dec.Decode(&list); err != nil { + return EarthPorn{}, err + } + + for _, el := range list.Data.Children { + p := el.Data + if p.URL == "" { + continue + } + + if (&p).getImageURL() { + return p, nil + } + } + + return EarthPorn{}, errors.New("Could not get EarthPorn: No image could be extracted") +} + +func (p *EarthPorn) getImageURL() bool { + // TODO: We can do further processing here (e.g. if we get a link to flickr, extract the image). + // For now, we will simply test, if the URL points to an image. + + resp, err := http.Head(p.URL) + if err != nil { + return false + } + defer resp.Body.Close() + + t := resp.Header.Get("Content-Type") + if t == "" { + return false + } + mt, _, err := mime.ParseMediaType(t) + if err != nil { + return false + } + + return (strings.Split(mt, "/")[0] == "image") +} diff --git a/links.go b/links.go new file mode 100644 index 0000000..f7bda76 --- /dev/null +++ b/links.go @@ -0,0 +1,34 @@ +package main + +import ( + "bufio" + "html/template" + "log" + "os" + "strings" +) + +type Link struct { + Title string + URL template.URL +} + +func GetLinks() (links []Link) { + fh, err := os.Open(os.ExpandEnv("$HOME/.startpage-urls")) + if err != nil { + log.Printf("Couldn't read links: %s", err) + return + } + defer fh.Close() + + scanner := bufio.NewScanner(fh) + for scanner.Scan() { + parts := strings.SplitN(scanner.Text(), "->", 2) + links = append(links, Link{ + strings.TrimSpace(parts[0]), + template.URL(strings.TrimSpace(parts[1])), + }) + } + + return +} diff --git a/main.go b/main.go new file mode 100644 index 0000000..952802a --- /dev/null +++ b/main.go @@ -0,0 +1,115 @@ +package main + +import ( + "flag" + "html/template" + "log" + "net/http" + "time" +) + +var porn EarthPorn +var weather Weather +var sun Sun + +func trylater(ch chan<- bool) { + log.Println("Will try again later...") + time.Sleep(1 * time.Minute) + ch <- true +} + +func earthPornUpdater(ch chan bool) { + for _ = range ch { + newporn, err := GetEarthPorn() + if err != nil { + log.Printf("Failed getting fap material: %s", err) + go trylater(ch) + } + + porn = newporn + log.Println("New fap material!") + } +} + +func weatherUpdater(ch chan bool) { + for _ = range ch { + newW, newS, err := CurrentWeather() + if err != nil { + log.Printf("Failed getting latest weather data: %s", err) + go trylater(ch) + } + + weather = newW + sun = newS + log.Println("New weather data") + } +} + +func intervalUpdates(d time.Duration, stopch <-chan bool, chans ...chan<- bool) { + send := func(chans ...chan<- bool) { + for _, ch := range chans { + go func(ch chan<- bool) { + ch <- true + }(ch) + } + } + + send(chans...) + + tick := time.NewTicker(d) + for { + select { + case <-tick.C: + send(chans...) + case <-stopch: + tick.Stop() + for _, ch := range chans { + close(ch) + } + return + } + } +} + +func main() { + laddr := flag.String("laddr", ":25145", "Listen on this port") + flag.Parse() + + pornch := make(chan bool) + weatherch := make(chan bool) + stopch := make(chan bool) + + go intervalUpdates(30*time.Minute, stopch, pornch, weatherch) + go weatherUpdater(weatherch) + go earthPornUpdater(pornch) + + defer func(stopch chan<- bool) { + stopch <- true + }(stopch) + + http.HandleFunc("/", startpage) + log.Fatal(http.ListenAndServe(*laddr, nil)) +} + +var tpl = template.Must(template.ParseFiles("template.html")) + +type TplData struct { + Porn *EarthPorn + Weather *Weather + Links []Link + LCols int +} + +func startpage(rw http.ResponseWriter, req *http.Request) { + links := GetLinks() + lcols := len(links) / 3 + if lcols < 1 { + lcols = 1 + } else if lcols > 4 { + lcols = 4 + } + + if err := tpl.Execute(rw, &TplData{&porn, &weather, links, lcols}); err != nil { + log.Printf("Failed executing template: %s\n", err) + } +} diff --git a/template.html b/template.html new file mode 100644 index 0000000..288c933 --- /dev/null +++ b/template.html @@ -0,0 +1,62 @@ + + + + Startpage + + + +
+ + {{ .Weather.Temp.Value }}° +
+ +
+ {{ .Porn.Title }} +
+ + \ No newline at end of file diff --git a/yr_no.go b/yr_no.go new file mode 100644 index 0000000..269ff62 --- /dev/null +++ b/yr_no.go @@ -0,0 +1,81 @@ +package main + +import ( + "encoding/xml" + "fmt" + "net/http" + "time" +) + +func toTime(s string) time.Time { + t, _ := time.Parse("2006-01-02T15:04:05", s) + return t +} + +type Weather struct { + Temp Temperature `xml:"temperature"` + Symbol struct { + Number int `xml:"number,attr"` + } `xml:"symbol"` + From string `xml:"from,attr"` + URL string + Icon string +} + +func (w *Weather) prepIcon(sun Sun) { + rise := toTime(sun.Rise) + set := toTime(sun.Set) + t := toTime(w.From) + + night := t.Before(rise) || t.After(set) + format := "http://symbol.yr.no/grafikk/sym/b100/%02d" + switch w.Symbol.Number { + case 1, 2, 3, 5, 6, 7, 8, 20, 21: + if night { + format += "n.50" + } else { + format += "d" + } + } + format += ".png" + + w.Icon = fmt.Sprintf(format, w.Symbol.Number) +} + +type Temperature struct { + Value int `xml:"value,attr"` + Unit string `xml:"unit,attr"` +} + +type Sun struct { + Rise string `xml:"rise,attr"` + Set string `xml:"set,attr"` +} + +type weatherdata struct { + Sun Sun `xml:"sun"` + Forecast []*Weather `xml:"forecast>tabular>time"` +} + +const place = "Germany/Schleswig-Holstein/Lübeck" + +func CurrentWeather() (Weather, Sun, error) { + url := "http://www.yr.no/place/" + place + "/forecast_hour_by_hour.xml" + resp, err := http.Get(url) + if err != nil { + return Weather{}, Sun{}, err + } + defer resp.Body.Close() + + var wd weatherdata + dec := xml.NewDecoder(resp.Body) + if err := dec.Decode(&wd); err != nil { + return Weather{}, Sun{}, err + } + + w := wd.Forecast[0] + w.URL = "http://www.yr.no/place/" + place + w.prepIcon(wd.Sun) + + return *w, wd.Sun, nil +} -- cgit v1.2.3-70-g09d2