ScuttleBot

scuttlebot / internal / ergo / api.go
Source Blame History 179 lines
c369cd5… lmata 1 package ergo
c369cd5… lmata 2
c369cd5… lmata 3 import (
c369cd5… lmata 4 "bytes"
c369cd5… lmata 5 "encoding/json"
c369cd5… lmata 6 "fmt"
c369cd5… lmata 7 "net/http"
c369cd5… lmata 8 "time"
c369cd5… lmata 9 )
c369cd5… lmata 10
c369cd5… lmata 11 // APIClient is an HTTP client for Ergo's management API.
c369cd5… lmata 12 type APIClient struct {
c369cd5… lmata 13 baseURL string
c369cd5… lmata 14 token string
c369cd5… lmata 15 http *http.Client
c369cd5… lmata 16 }
c369cd5… lmata 17
c369cd5… lmata 18 // NewAPIClient returns a new APIClient pointed at addr with the given bearer token.
c369cd5… lmata 19 func NewAPIClient(addr, token string) *APIClient {
c369cd5… lmata 20 return &APIClient{
c369cd5… lmata 21 baseURL: "http://" + addr,
c369cd5… lmata 22 token: token,
c369cd5… lmata 23 http: &http.Client{Timeout: 10 * time.Second},
c369cd5… lmata 24 }
c369cd5… lmata 25 }
c369cd5… lmata 26
c369cd5… lmata 27 // Status returns the Ergo server status.
c369cd5… lmata 28 func (c *APIClient) Status() (*StatusResponse, error) {
c369cd5… lmata 29 var resp StatusResponse
c369cd5… lmata 30 if err := c.post("/v1/status", nil, &resp); err != nil {
c369cd5… lmata 31 return nil, fmt.Errorf("ergo api: status: %w", err)
c369cd5… lmata 32 }
c369cd5… lmata 33 return &resp, nil
c369cd5… lmata 34 }
c369cd5… lmata 35
c369cd5… lmata 36 // Rehash reloads Ergo's configuration file.
c369cd5… lmata 37 func (c *APIClient) Rehash() error {
c369cd5… lmata 38 var resp successResponse
c369cd5… lmata 39 if err := c.post("/v1/rehash", nil, &resp); err != nil {
c369cd5… lmata 40 return fmt.Errorf("ergo api: rehash: %w", err)
c369cd5… lmata 41 }
c369cd5… lmata 42 if !resp.Success {
c369cd5… lmata 43 return fmt.Errorf("ergo api: rehash failed: %s", resp.Error)
c369cd5… lmata 44 }
c369cd5… lmata 45 return nil
c369cd5… lmata 46 }
c369cd5… lmata 47
c369cd5… lmata 48 // RegisterAccount creates a NickServ account via saregister.
c369cd5… lmata 49 func (c *APIClient) RegisterAccount(name, passphrase string) error {
c369cd5… lmata 50 var resp registerResponse
c369cd5… lmata 51 if err := c.post("/v1/ns/saregister", map[string]string{
c369cd5… lmata 52 "accountName": name,
c369cd5… lmata 53 "passphrase": passphrase,
c369cd5… lmata 54 }, &resp); err != nil {
c369cd5… lmata 55 return fmt.Errorf("ergo api: register account %q: %w", name, err)
c369cd5… lmata 56 }
c369cd5… lmata 57 if !resp.Success {
c369cd5… lmata 58 return fmt.Errorf("ergo api: register account %q: %s", name, resp.ErrorCode)
c369cd5… lmata 59 }
c369cd5… lmata 60 return nil
c369cd5… lmata 61 }
c369cd5… lmata 62
c369cd5… lmata 63 // ChangePassword updates the passphrase of an existing NickServ account.
c369cd5… lmata 64 func (c *APIClient) ChangePassword(name, passphrase string) error {
c369cd5… lmata 65 var resp passwdResponse
c369cd5… lmata 66 if err := c.post("/v1/ns/passwd", map[string]string{
c369cd5… lmata 67 "accountName": name,
c369cd5… lmata 68 "passphrase": passphrase,
c369cd5… lmata 69 }, &resp); err != nil {
c369cd5… lmata 70 return fmt.Errorf("ergo api: change password %q: %w", name, err)
c369cd5… lmata 71 }
c369cd5… lmata 72 if !resp.Success {
c369cd5… lmata 73 return fmt.Errorf("ergo api: change password %q: %s", name, resp.ErrorCode)
c369cd5… lmata 74 }
c369cd5… lmata 75 return nil
c369cd5… lmata 76 }
c369cd5… lmata 77
c369cd5… lmata 78 // AccountInfo fetches details about a NickServ account.
c369cd5… lmata 79 func (c *APIClient) AccountInfo(name string) (*AccountInfoResponse, error) {
c369cd5… lmata 80 var resp AccountInfoResponse
c369cd5… lmata 81 if err := c.post("/v1/ns/info", map[string]string{
c369cd5… lmata 82 "accountName": name,
c369cd5… lmata 83 }, &resp); err != nil {
c369cd5… lmata 84 return nil, fmt.Errorf("ergo api: account info %q: %w", name, err)
c369cd5… lmata 85 }
c369cd5… lmata 86 if !resp.Success {
c369cd5… lmata 87 return nil, fmt.Errorf("ergo api: account %q not found", name)
c369cd5… lmata 88 }
c369cd5… lmata 89 return &resp, nil
c369cd5… lmata 90 }
c369cd5… lmata 91
c369cd5… lmata 92 // ListChannels returns all channels currently active on the server.
c369cd5… lmata 93 func (c *APIClient) ListChannels() (*ListChannelsResponse, error) {
c369cd5… lmata 94 var resp ListChannelsResponse
c369cd5… lmata 95 if err := c.post("/v1/list", nil, &resp); err != nil {
c369cd5… lmata 96 return nil, fmt.Errorf("ergo api: list channels: %w", err)
c369cd5… lmata 97 }
c369cd5… lmata 98 return &resp, nil
c369cd5… lmata 99 }
c369cd5… lmata 100
c369cd5… lmata 101 func (c *APIClient) post(path string, body any, out any) error {
c369cd5… lmata 102 var reqBody bytes.Buffer
c369cd5… lmata 103 if body != nil {
c369cd5… lmata 104 if err := json.NewEncoder(&reqBody).Encode(body); err != nil {
c369cd5… lmata 105 return err
c369cd5… lmata 106 }
c369cd5… lmata 107 }
c369cd5… lmata 108
c369cd5… lmata 109 req, err := http.NewRequest(http.MethodPost, c.baseURL+path, &reqBody)
c369cd5… lmata 110 if err != nil {
c369cd5… lmata 111 return err
c369cd5… lmata 112 }
c369cd5… lmata 113 req.Header.Set("Authorization", "Bearer "+c.token)
c369cd5… lmata 114 req.Header.Set("Content-Type", "application/json")
c369cd5… lmata 115
c369cd5… lmata 116 resp, err := c.http.Do(req)
c369cd5… lmata 117 if err != nil {
c369cd5… lmata 118 return err
c369cd5… lmata 119 }
c369cd5… lmata 120 defer resp.Body.Close()
c369cd5… lmata 121
c369cd5… lmata 122 if resp.StatusCode != http.StatusOK {
c369cd5… lmata 123 return fmt.Errorf("HTTP %d", resp.StatusCode)
c369cd5… lmata 124 }
c369cd5… lmata 125 return json.NewDecoder(resp.Body).Decode(out)
c369cd5… lmata 126 }
c369cd5… lmata 127
c369cd5… lmata 128 // Response types.
c369cd5… lmata 129
c369cd5… lmata 130 type successResponse struct {
c369cd5… lmata 131 Success bool `json:"success"`
c369cd5… lmata 132 Error string `json:"error,omitempty"`
c369cd5… lmata 133 }
c369cd5… lmata 134
c369cd5… lmata 135 type registerResponse struct {
c369cd5… lmata 136 Success bool `json:"success"`
c369cd5… lmata 137 ErrorCode string `json:"errorCode,omitempty"`
c369cd5… lmata 138 Error string `json:"error,omitempty"`
c369cd5… lmata 139 }
c369cd5… lmata 140
c369cd5… lmata 141 type passwdResponse struct {
c369cd5… lmata 142 Success bool `json:"success"`
c369cd5… lmata 143 ErrorCode string `json:"errorCode,omitempty"`
c369cd5… lmata 144 }
c369cd5… lmata 145
c369cd5… lmata 146 // StatusResponse is the response from /v1/status.
c369cd5… lmata 147 type StatusResponse struct {
c369cd5… lmata 148 Success bool `json:"success"`
c369cd5… lmata 149 Version string `json:"version"`
c369cd5… lmata 150 StartTime string `json:"start_time"`
c369cd5… lmata 151 Users struct {
0e244d2… lmata 152 Total int `json:"total"`
0e244d2… lmata 153 Max int `json:"max"`
c369cd5… lmata 154 } `json:"users"`
c369cd5… lmata 155 Channels int `json:"channels"`
c369cd5… lmata 156 }
c369cd5… lmata 157
c369cd5… lmata 158 // AccountInfoResponse is the response from /v1/ns/info.
c369cd5… lmata 159 type AccountInfoResponse struct {
c369cd5… lmata 160 Success bool `json:"success"`
c369cd5… lmata 161 AccountName string `json:"accountName"`
c369cd5… lmata 162 Email string `json:"email"`
c369cd5… lmata 163 RegisteredAt string `json:"registeredAt"`
c369cd5… lmata 164 Channels []string `json:"channels"`
c369cd5… lmata 165 }
c369cd5… lmata 166
c369cd5… lmata 167 // ChannelInfo is a single channel entry from /v1/list.
c369cd5… lmata 168 type ChannelInfo struct {
c369cd5… lmata 169 Name string `json:"name"`
c369cd5… lmata 170 UserCount int `json:"userCount"`
c369cd5… lmata 171 Topic string `json:"topic"`
c369cd5… lmata 172 Registered bool `json:"registered"`
c369cd5… lmata 173 }
c369cd5… lmata 174
c369cd5… lmata 175 // ListChannelsResponse is the response from /v1/list.
c369cd5… lmata 176 type ListChannelsResponse struct {
c369cd5… lmata 177 Success bool `json:"success"`
c369cd5… lmata 178 Channels []ChannelInfo `json:"channels"`
c369cd5… lmata 179 }

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button