mirror of
https://github.com/0xrsydn/idx-cli.git
synced 2026-08-07 09:43:53 +00:00
feat(rubick): rename project and add production release bundle
This commit is contained in:
commit
fab6cf26eb
33 changed files with 11554 additions and 0 deletions
129
internal/cli/brave.go
Normal file
129
internal/cli/brave.go
Normal file
|
|
@ -0,0 +1,129 @@
|
|||
package cli
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/url"
|
||||
"os"
|
||||
"time"
|
||||
|
||||
"github.com/enetx/g"
|
||||
"github.com/enetx/surf"
|
||||
)
|
||||
|
||||
// BraveResult represents a single news result from Brave Search API
|
||||
type BraveResult struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
Description string `json:"description"`
|
||||
PageAge string `json:"page_age"`
|
||||
}
|
||||
|
||||
// BraveNewsResponse represents the Brave News Search API response
|
||||
type BraveNewsResponse struct {
|
||||
Results []struct {
|
||||
Title string `json:"title"`
|
||||
URL string `json:"url"`
|
||||
Description string `json:"description"`
|
||||
Age string `json:"age"`
|
||||
} `json:"results"`
|
||||
}
|
||||
|
||||
// SearchConfig holds search parameters
|
||||
type SearchConfig struct {
|
||||
Query string
|
||||
From time.Time
|
||||
To time.Time
|
||||
Count int
|
||||
}
|
||||
|
||||
// SearchBrave queries the Brave News Search API
|
||||
// Uses dedicated News endpoint: GET /res/v1/news/search
|
||||
func SearchBrave(client *surf.Client, config SearchConfig) ([]BraveResult, error) {
|
||||
apiKey := os.Getenv("BRAVE_API_KEY")
|
||||
if apiKey == "" {
|
||||
return nil, fmt.Errorf("BRAVE_API_KEY environment variable not set")
|
||||
}
|
||||
|
||||
// Build query parameters
|
||||
params := url.Values{}
|
||||
params.Set("q", config.Query)
|
||||
params.Set("count", fmt.Sprintf("%d", config.Count))
|
||||
params.Set("freshness", fmt.Sprintf("%sto%s",
|
||||
config.From.Format("2006-01-02"),
|
||||
config.To.Format("2006-01-02"),
|
||||
))
|
||||
|
||||
apiURL := fmt.Sprintf("https://api.search.brave.com/res/v1/news/search?%s", params.Encode())
|
||||
|
||||
// Use plain client for API calls (no Chrome impersonation which overrides headers)
|
||||
apiClient := surf.NewClient()
|
||||
defer apiClient.CloseIdleConnections()
|
||||
|
||||
resp := apiClient.Get(g.String(apiURL)).
|
||||
SetHeaders("Accept", "application/json").
|
||||
SetHeaders("X-Subscription-Token", apiKey).
|
||||
// Jakarta, Indonesia location headers
|
||||
SetHeaders("X-Loc-Lat", "-6.2088").
|
||||
SetHeaders("X-Loc-Long", "106.8456").
|
||||
SetHeaders("X-Loc-Timezone", "Asia/Jakarta").
|
||||
SetHeaders("X-Loc-Country", "ID").
|
||||
Do()
|
||||
|
||||
if resp.IsErr() {
|
||||
return nil, fmt.Errorf("brave API request failed: %w", resp.Err())
|
||||
}
|
||||
|
||||
r := resp.Ok()
|
||||
if r.StatusCode != 200 {
|
||||
body := r.Body.String().Ok().Std()
|
||||
return nil, fmt.Errorf("brave API returned status %d: %s", r.StatusCode, body)
|
||||
}
|
||||
|
||||
body := r.Body.String().Ok().Std()
|
||||
|
||||
var braveResp BraveNewsResponse
|
||||
if err := json.Unmarshal([]byte(body), &braveResp); err != nil {
|
||||
return nil, fmt.Errorf("failed to parse brave API response: %w", err)
|
||||
}
|
||||
|
||||
if len(braveResp.Results) == 0 {
|
||||
return []BraveResult{}, nil
|
||||
}
|
||||
|
||||
results := make([]BraveResult, len(braveResp.Results))
|
||||
for i, r := range braveResp.Results {
|
||||
results[i] = BraveResult{
|
||||
Title: r.Title,
|
||||
URL: r.URL,
|
||||
Description: r.Description,
|
||||
PageAge: r.Age,
|
||||
}
|
||||
}
|
||||
|
||||
return results, nil
|
||||
}
|
||||
|
||||
// BuildStockQuery creates a boolean query for Indonesian stock news
|
||||
// Example: BuildStockQuery("MINA", "MINA Tbk") returns:
|
||||
// ("MINA" OR "MINA Tbk") AND (saham OR emiten OR "Bursa Efek Indonesia" OR BEI OR IDX)
|
||||
func BuildStockQuery(stockTerms ...string) string {
|
||||
if len(stockTerms) == 0 {
|
||||
return ""
|
||||
}
|
||||
|
||||
// Build stock terms part
|
||||
stockPart := "("
|
||||
for i, term := range stockTerms {
|
||||
if i > 0 {
|
||||
stockPart += " OR "
|
||||
}
|
||||
stockPart += fmt.Sprintf(`"%s"`, term)
|
||||
}
|
||||
stockPart += ")"
|
||||
|
||||
// Indonesian stock market keywords
|
||||
marketKeywords := `(saham OR emiten OR "Bursa Efek Indonesia" OR BEI OR IDX)`
|
||||
|
||||
return stockPart + " AND " + marketKeywords
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue