💾 Archived View for tilde.team › ~gryffyn › posts › golang-pubsubhubbub captured on 2023-06-16 at 19:25:49. Gemini links have been rewritten to link to archived content

View Raw

More Information

⬅️ Previous capture (2021-12-03)

-=-=-=-=-=-=-

A Very Basic Pubsubhubbub Client in Go

The only way, as far as I can tell, to get push notifications from the youtube API is to use google's Pubsubhubbub hub. We're not even a sentence in and I'm already so done with calling it that, so I'm just going to call it PH from now on.

A basic client needs to do two things - implement a GET handler, and a POST handler. The GET handler is used for subscribing and unsubscribing, and the POST handler is for PH to push updates to.

The GET handler needs to return the PH challenge --

func parseGET(w http.ResponseWriter, r *http.Request) {
    queries, _ := url.ParseQuery(r.URL.RawQuery)
    _, err := w.Write([]byte(queries.Get("hub.challenge")))
    err = r.Body.Close()
    if err != nil { log.Fatal(err) }
}

The POST handler will accept XML containing the PH event data.

func parsePOST(w http.ResponseWriter, r *http.Request) {

    //... parse XML in r.Body

    w.WriteHeader(204) // PH uses 204 to indicate successful pushes.
    err = r.Body.Close()
    if err != nil { log.Fatal(err) }
}

Then we just need a router to handle the handlers --

func makeMuxRouter(loc string) http.Handler {
    r := mux.NewRouter().StrictSlash(true)
    r.HandleFunc(loc, parseGET).Methods("GET")
    r.HandleFunc(loc, parsePOST).Methods("POST")
    return r
}

All that's left is to create an HTTP server, assign the router, and run it.

handlr := makeMuxRouter("/location")
server := &http.Server{
        Handler:        handlr,

        //... rest of the Server{}
}

server.ListenAndServe()

This page

will let you subscribe to topics. Set your `https://your-domain.tld/location` as the callback URL.

---

as a little bonus: here's a struct that maps the XML from youtube's `videos.xml` feed.

type Feed struct {
    XMLName xml.Name `xml:"feed"`
    Text    string   `xml:",chardata"`
    Yt      string   `xml:"yt,attr"`
    Xmlns   string   `xml:"xmlns,attr"`
    Link    []struct {
        Text string `xml:",chardata"`
        Rel  string `xml:"rel,attr"`
        Href string `xml:"href,attr"`
    } `xml:"link"`
    Title   string `xml:"title"`
    Updated string `xml:"updated"`
    Entry   struct {
        Text      string `xml:",chardata"`
        ID        string `xml:"id"`
        VideoId   string `xml:"videoId"`
        ChannelId string `xml:"channelId"`
        Title     string `xml:"title"`
        Link      struct {
            Text string `xml:",chardata"`
            Rel  string `xml:"rel,attr"`
            Href string `xml:"href,attr"`
        } `xml:"link"`
        Author struct {
            Text string `xml:",chardata"`
            Name string `xml:"name"`
            URI  string `xml:"uri"`
        } `xml:"author"`
        Published string `xml:"published"`
        Updated   string `xml:"updated"`
    } `xml:"entry"`
}