package marketman import ( "encoding/json" "fmt" "io" "math" "net/http" "os" "path/filepath" "time" ) const cacheFilePath = "data/marketman.json" const cacheDuration = 6 * time.Hour type walletData struct { Current struct { TotalValueCad float64 `json:"total_value_cad"` } `json:"current"` } type Provider struct { serverURL string ynabAccount string } func (p *Provider) Name() string { return "marketman" } func (p *Provider) Configure() error { p.serverURL = os.Getenv("marketman_server") if p.serverURL == "" { return fmt.Errorf("marketman_server environment variable is not set") } p.ynabAccount = os.Getenv("marketman_ynab_account") if p.ynabAccount == "" { return fmt.Errorf("marketman_ynab_account environment variable is not set") } return nil } func (p *Provider) GetBalances() ([]int, []string, error) { var data walletData // Check if cache file exists and is recent enough if stat, err := os.Stat(cacheFilePath); err == nil { if time.Since(stat.ModTime()) < cacheDuration { fileData, err := os.ReadFile(cacheFilePath) if err == nil { if err := json.Unmarshal(fileData, &data); err == nil { cents := int(math.Round(data.Current.TotalValueCad * 100)) return []int{cents}, []string{p.ynabAccount}, nil } } } } // Fetch fresh data resp, err := http.Get(fmt.Sprintf("%s/api/wallet/data", p.serverURL)) if err != nil { return nil, nil, fmt.Errorf("failed to fetch wallet data: %v", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) } bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return nil, nil, fmt.Errorf("failed to read response body: %v", err) } if err := json.Unmarshal(bodyBytes, &data); err != nil { return nil, nil, fmt.Errorf("failed to decode wallet data: %v", err) } // Save raw response to cache file os.MkdirAll(filepath.Dir(cacheFilePath), 0755) _ = os.WriteFile(cacheFilePath, bodyBytes, 0644) cents := int(math.Round(data.Current.TotalValueCad * 100)) return []int{cents}, []string{p.ynabAccount}, nil }