hswaw/site: add checkinator integration

Change-Id: I19a72da67410332d6d82d49e3a54f1dc0f81ff65
diff --git a/hswaw/site/at.go b/hswaw/site/at.go
new file mode 100644
index 0000000..70df8c7
--- /dev/null
+++ b/hswaw/site/at.go
@@ -0,0 +1,48 @@
+package main
+
+import (
+	"context"
+	"encoding/json"
+	"fmt"
+	"net/http"
+)
+
+const (
+	atURL = "https://at.hackerspace.pl/api"
+)
+
+// atStatus is the result of queruing checkinator/at (Hackerspace presence
+// service).
+type atStatus struct {
+	// Users is the list of present and publicly visible users.
+	Users []atUser `json:"users"`
+	// ESPs is the number of ESP{8266,32} devices.
+	ESPs int `json:"esps"`
+	// Kektops is the number of nettop “Kektop” devices.
+	Kektops int `json:"kektops"`
+	// Unknown is the number of unknown devices in the network.
+	Unknown int `json:"unknown"`
+}
+
+type atUser struct {
+	Login string `json:"login"`
+}
+
+func getAt(ctx context.Context) (*atStatus, error) {
+	r, err := http.NewRequestWithContext(ctx, "GET", atURL, nil)
+	if err != nil {
+		return nil, fmt.Errorf("NewRequest(%q): %w", atURL, err)
+	}
+	res, err := http.DefaultClient.Do(r)
+	if err != nil {
+		return nil, fmt.Errorf("GET: %w", err)
+	}
+	defer res.Body.Close()
+
+	var status atStatus
+	if err := json.NewDecoder(res.Body).Decode(&status); err != nil {
+		return nil, fmt.Errorf("when decoding JSON: %w", err)
+	}
+
+	return &status, nil
+}