diff --git a/.drone.yml b/.drone.yml index ae421e0..b19e5a5 100644 --- a/.drone.yml +++ b/.drone.yml @@ -5,6 +5,16 @@ name: default steps: - name: test + image: xena/go:1.13.7 + commands: + - go test ./... + environment: + PLURALKIT_TOKEN: + from_secret: PLURALKIT_TOKEN + PLURALKIT_SYSTEM: tlici + PLURALKIT_MEMBER: cddmp + +- name: docker build image: docker:dind volumes: - name: dockersock diff --git a/pluralkit/client.go b/pluralkit/client.go new file mode 100644 index 0000000..9e5be93 --- /dev/null +++ b/pluralkit/client.go @@ -0,0 +1,74 @@ +// Package pluralkit is a binding to the PluralKit API as documented here: https://pluralkit.me/api +package pluralkit + +import ( + "bytes" + "encoding/json" + "io" + "net/http" + + "within.website/x/web" +) + +// Client connects to the PluralKit API. +type Client struct { + baseURL string + token string + hc *http.Client +} + +// RoundTrip sends an authenticated request to the PluralKit API. +func (c Client) RoundTrip(req *http.Request) (*http.Response, error) { + req.Header.Set("Authorization", c.token) + return c.hc.Do(req) +} + +// Do is a high level wrapper for sending data to/from the PluralKit API. +func (c Client) Do(method, pathNoSlash string, want int, post interface{}, result interface{}) error { + var body io.Reader + if post != nil { + var buf bytes.Buffer + err := json.NewEncoder(&buf).Encode(post) + if err != nil { + return nil + } + body = &buf + } + req := c.MakeRequest(method, pathNoSlash, body) + resp, err := c.RoundTrip(req) + if err != nil { + return err + } + + if resp.StatusCode != want { + return web.NewError(want, resp) + } + + defer resp.Body.Close() + + err = json.NewDecoder(resp.Body).Decode(result) + if err != nil { + return err + } + + return nil +} + +// MakeRequest makes a HTTP request for the PluralKit API, catting pathNoSlash to the base PluralKit API address. +func (c Client) MakeRequest(method, pathNoSlash string, body io.Reader) *http.Request { + req, err := http.NewRequest(method, c.baseURL+pathNoSlash, body) + if err != nil /* programmer error */ { + panic(err) + } + + return req +} + +// New creates a new instance of the PluralKit API client. +func New(token string, hc *http.Client) Client { + return Client{ + baseURL: "https://api.pluralkit.me/v1/", + token: token, + hc: hc, + } +} diff --git a/pluralkit/members.go b/pluralkit/members.go new file mode 100644 index 0000000..60190d7 --- /dev/null +++ b/pluralkit/members.go @@ -0,0 +1,50 @@ +package pluralkit + +import ( + "net/http" + "time" +) + +// Member is an individual member of a plural system +type Member struct { + ID string `json:"id"` + Name *string `json:"name"` + DisplayName *string `json:"display_name"` + Description *string `json:"description"` + Color *string `json:"color"` + AvatarURL *string `json:"avatar_url"` + Birthday *string `json:"birthday"` + ProxyTags []ProxyTag `json:"proxy_tags"` + KeepProxy bool `json:"keep_proxy"` + Created time.Time `json:"created"` +} + +// ProxyTags are the message encoding used for proxying messages +type ProxyTag struct { + Prefix string `json:"prefix"` + Suffix string `json:"suffix"` +} + +// Member fetches a given member by ID. +func (c Client) Member(id string) (Member, error) { + var result Member + + err := c.Do(http.MethodGet, "m/"+id, http.StatusOK, nil, &result) + if err != nil { + return result, err + } + + return result, nil +} + +// SystemMembers returns the members for a given system. +func (c Client) SystemMembers(systemID string) ([]Member, error) { + var result []Member + + err := c.Do(http.MethodGet, "s/"+systemID+"/members", http.StatusOK, nil, &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/pluralkit/members_test.go b/pluralkit/members_test.go new file mode 100644 index 0000000..fc3a696 --- /dev/null +++ b/pluralkit/members_test.go @@ -0,0 +1,30 @@ +package pluralkit + +import ( + "os" + "testing" + + "github.com/kr/pretty" +) + +func TestMember(t *testing.T) { + c := mkClient(t) + + member, err := c.Member(os.Getenv("PLURALKIT_MEMBER")) + if err != nil { + t.Fatal(err) + } + + t.Log(pretty.Sprint(member)) +} + +func TestSystemMembers(t *testing.T) { + c := mkClient(t) + + members, err := c.SystemMembers(os.Getenv("PLURALKIT_SYSTEM")) + if err != nil { + t.Fatal(err) + } + + t.Log(pretty.Sprint(members)) +} diff --git a/pluralkit/switch.go b/pluralkit/switch.go new file mode 100644 index 0000000..bcf6669 --- /dev/null +++ b/pluralkit/switch.go @@ -0,0 +1,44 @@ +package pluralkit + +import ( + "net/http" + "time" +) + +type SwitchResponse struct { + Timestamp time.Time `json:"timestamp"` + Members []Member `json:"members"` +} + +// Switch tells the PluralKit API who is front. +func (c Client) Switch(memberIDs []string) error { + return c.Do(http.MethodPost, "s/switches", http.StatusNoContent, struct { + Members []string `json:"members"` + }{ + Members: memberIDs, + }, nil) +} + +// Switches gets a historical view of switches. For pagination see SwitchesBefore. +func (c Client) Switches(systemID string) ([]SwitchResponse, error) { + var result []SwitchResponse + + err := c.Do(http.MethodGet, "s/"+systemID+"/switches", http.StatusOK, nil, &result) + if err != nil { + return nil, err + } + + return result, nil +} + +// SwitchesBefore shows paginated views of switches. +func (c Client) SwitchesBefore(systemID string, before time.Time) ([]SwitchResponse, error) { + var result []SwitchResponse + + err := c.Do(http.MethodGet, "s/"+systemID+"/switches?before="+before.Format(time.RFC3339), http.StatusOK, nil, &result) + if err != nil { + return nil, err + } + + return result, nil +} diff --git a/pluralkit/system.go b/pluralkit/system.go new file mode 100644 index 0000000..5850646 --- /dev/null +++ b/pluralkit/system.go @@ -0,0 +1,41 @@ +package pluralkit + +import ( + "net/http" + "time" +) + +// System is a plural system. +type System struct { + ID string `json:"id"` + Name *string `json:"name"` + Description *string `json:"description"` + Tag *string `json:"tag"` + AvatarURL *string `json:"avatar_url"` + Tz *string `json:"tz"` + Created time.Time `json:"created"` +} + +// System gets the currently authenticated system. +func (c Client) System() (System, error) { + var result System + + err := c.Do(http.MethodGet, "s", http.StatusOK, nil, &result) + if err != nil { + return result, err + } + + return result, nil +} + +// SystemByID gets information on a system by its ID. +func (c Client) SystemByID(id string) (System, error) { + var result System + + err := c.Do(http.MethodGet, "s/"+id, http.StatusOK, nil, &result) + if err != nil { + return result, err + } + + return result, nil +} diff --git a/pluralkit/system_test.go b/pluralkit/system_test.go new file mode 100644 index 0000000..854789a --- /dev/null +++ b/pluralkit/system_test.go @@ -0,0 +1,37 @@ +package pluralkit + +import ( + "net/http" + "os" + "testing" + + "github.com/kr/pretty" +) + +func mkClient(t *testing.T) Client { + t.Helper() + + return New(os.Getenv("PLURALKIT_TOKEN"), http.DefaultClient) +} + +func TestPluralKitSystemInfo(t *testing.T) { + c := mkClient(t) + + system, err := c.System() + if err != nil { + t.Fatal(err) + } + + t.Log(pretty.Sprint(system)) +} + +func TestSystemByID(t *testing.T) { + c := mkClient(t) + + system, err := c.SystemByID(os.Getenv("PLURALKIT_SYSTEM")) + if err != nil { + t.Fatal(err) + } + + t.Log(pretty.Sprint(system)) +}