package imgur import ( "bytes" "encoding/json" "fmt" "io" "mime/multipart" "net/http" "os" "time" ) const uploadURL = "https://api.imgur.com/3/image" // Client uploads images to Imgur anonymously with a Client-ID. type Client struct { clientID string httpClient *http.Client } // New creates an Imgur client. clientID may be empty (uploads will fail clearly). func New(clientID string) *Client { return &Client{ clientID: clientID, httpClient: &http.Client{ Timeout: 60 * time.Second, }, } } // Enabled reports whether a Client-ID is configured. func (c *Client) Enabled() bool { return c != nil && c.clientID != "" } type uploadResponse struct { Success bool `json:"success"` Data struct { Link string `json:"link"` ID string `json:"id"` } `json:"data"` Status int `json:"status"` } // UploadFile uploads a local image file and returns the public Imgur URL. func (c *Client) UploadFile(path string) (string, error) { if !c.Enabled() { return "", fmt.Errorf("imgur is not configured (set IMGUR_CLIENT_ID)") } file, err := os.Open(path) if err != nil { return "", fmt.Errorf("open screenshot: %w", err) } defer file.Close() var body bytes.Buffer writer := multipart.NewWriter(&body) part, err := writer.CreateFormFile("image", "screenshot.png") if err != nil { return "", err } if _, err := io.Copy(part, file); err != nil { return "", err } _ = writer.WriteField("type", "file") if err := writer.Close(); err != nil { return "", err } req, err := http.NewRequest(http.MethodPost, uploadURL, &body) if err != nil { return "", err } req.Header.Set("Authorization", "Client-ID "+c.clientID) req.Header.Set("Content-Type", writer.FormDataContentType()) resp, err := c.httpClient.Do(req) if err != nil { return "", fmt.Errorf("imgur request: %w", err) } defer resp.Body.Close() raw, err := io.ReadAll(resp.Body) if err != nil { return "", err } var parsed uploadResponse if err := json.Unmarshal(raw, &parsed); err != nil { return "", fmt.Errorf("decode imgur response: %w (body=%s)", err, truncate(string(raw), 200)) } if !parsed.Success || parsed.Data.Link == "" { return "", fmt.Errorf("imgur upload failed (status=%d): %s", resp.StatusCode, truncate(string(raw), 300)) } return parsed.Data.Link, nil } func truncate(s string, n int) string { if len(s) <= n { return s } return s[:n] + "..." }