diff --git a/accountProviders.go b/accountProviders.go index 825f040..56e30d7 100644 --- a/accountProviders.go +++ b/accountProviders.go @@ -3,6 +3,7 @@ package main import ( "code.stevenpolley.net/steven/ynab-portfolio-monitor/providers/bitcoin" "code.stevenpolley.net/steven/ynab-portfolio-monitor/providers/bitcoinElectrum" + "code.stevenpolley.net/steven/ynab-portfolio-monitor/providers/marketman" "code.stevenpolley.net/steven/ynab-portfolio-monitor/providers/questrade" "code.stevenpolley.net/steven/ynab-portfolio-monitor/providers/staticjsonFinnhub" "code.stevenpolley.net/steven/ynab-portfolio-monitor/providers/staticjsonYahooFinance" @@ -23,4 +24,5 @@ var allProviders []AccountProvider = []AccountProvider{ &staticjsonFinnhub.Provider{}, &staticjsonYahooFinance.Provider{}, &bitcoinElectrum.Provider{}, + &marketman.Provider{}, } diff --git a/providers/marketman/marketman.go b/providers/marketman/marketman.go new file mode 100644 index 0000000..44e4056 --- /dev/null +++ b/providers/marketman/marketman.go @@ -0,0 +1,89 @@ +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 +}