diff options
Diffstat (limited to 'internal/client/github.go')
| -rw-r--r-- | internal/client/github.go | 123 |
1 files changed, 123 insertions, 0 deletions
diff --git a/internal/client/github.go b/internal/client/github.go new file mode 100644 index 0000000..f8b7097 --- /dev/null +++ b/internal/client/github.go @@ -0,0 +1,123 @@ +package client + +import ( + "bufio" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "strings" +) + +func (c *Client) SearchGitHubLeads(query string, callback func(result json.RawMessage) error) error { + path := "/github/leads" + + params := url.Values{} + params.Set("q", query) + + fullURL := c.config.BaseURL + path + "?" + params.Encode() + + req, err := http.NewRequest(http.MethodGet, fullURL, nil) + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("User-Agent", c.config.UserAgent) + req.Header.Set("Accept", "application/x-ndjson, application/json") + + resp, err := c.httpClient.Do(req) + if err != nil { + return fmt.Errorf("failed to execute request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) + } + + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + line := scanner.Bytes() + if len(line) == 0 { + continue + } + + if err := callback(json.RawMessage(line)); err != nil { + return err + } + } + + if err := scanner.Err(); err != nil { + if !strings.Contains(err.Error(), "context deadline exceeded") && !strings.Contains(err.Error(), "timeout") { + return fmt.Errorf("stream reading error: %w", err) + } + } + + return nil +} + +func (c *Client) SearchGitHubLeadsWithParams(query, sort, exclude string, callback func(result json.RawMessage) error) error { + path := "/github/leads" + + params := url.Values{} + params.Set("q", query) + if sort != "" { + params.Set("sort", sort) + } + if exclude != "" { + params.Set("exclude", exclude) + } + + fullURL := c.config.BaseURL + path + "?" + params.Encode() + + req, err := http.NewRequest(http.MethodGet, fullURL, nil) + if err != nil { + return fmt.Errorf("failed to create request: %w", err) + } + + req.Header.Set("User-Agent", c.config.UserAgent) + req.Header.Set("Accept", "application/x-ndjson, application/json") + req.Header.Set("Connection", "keep-alive") + req.ProtoMajor = 1 + req.ProtoMinor = 1 + + http1Client := &http.Client{ + Timeout: c.config.Timeout, + Transport: &http.Transport{ + ForceAttemptHTTP2: false, + }, + } + + resp, err := http1Client.Do(req) + if err != nil { + return fmt.Errorf("failed to execute request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + body, _ := io.ReadAll(resp.Body) + return fmt.Errorf("API request failed with status %d: %s", resp.StatusCode, string(body)) + } + + scanner := bufio.NewScanner(resp.Body) + for scanner.Scan() { + line := scanner.Bytes() + if len(line) == 0 { + continue + } + + if err := callback(json.RawMessage(line)); err != nil { + return err + } + } + + if err := scanner.Err(); err != nil { + if !strings.Contains(err.Error(), "context deadline exceeded") && !strings.Contains(err.Error(), "timeout") { + return fmt.Errorf("stream reading error: %w", err) + } + } + + return nil +} |
