diff --git a/pkg/btcrate/fetch_rate.go b/pkg/btcrate/fetch_rate.go deleted file mode 100644 index b04f958..0000000 --- a/pkg/btcrate/fetch_rate.go +++ /dev/null @@ -1,54 +0,0 @@ -package btcrate - -import ( - "encoding/json" - "fmt" - "io" - "log" - "net/http" - - "github.com/araujo88/bitcoin-price-bot-nostr/pkg/config" - "github.com/araujo88/bitcoin-price-bot-nostr/pkg/message" -) - -const BASE_URL = "https://rest.coinapi.io/v1/exchangerate/BTC/" - -var API_KEY = config.GetDotEnvVariable("API_KEY") - -func FetchRate(currency string) float64 { - - client := &http.Client{} - req, err := http.NewRequest(http.MethodGet, BASE_URL+currency, nil) - var response []byte - - req.Header.Set("X-CoinAPI-Key", API_KEY) - - if err != nil { - log.Fatal(err) - } - - res, err := client.Do(req) - if err != nil { - log.Fatal(err) - } - - if res.StatusCode == http.StatusOK { - response, err = io.ReadAll(res.Body) - if err != nil { - log.Fatal(err) - } - - } else { - fmt.Printf("Error: status code %d", res.StatusCode) - } - - message := message.Message{} - - err = json.Unmarshal(response, &message) - - if err != nil { - log.Fatal(err) - } - - return message.Rate -} diff --git a/pkg/coinapi/coinapi.go b/pkg/coinapi/coinapi.go new file mode 100644 index 0000000..96858a1 --- /dev/null +++ b/pkg/coinapi/coinapi.go @@ -0,0 +1,80 @@ +package coinapi + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + + "github.com/araujo88/bitcoin-price-bot-nostr/pkg/config" + "github.com/araujo88/bitcoin-price-bot-nostr/pkg/responses" +) + +const BASE_URL = "https://rest.coinapi.io/v1/" + +var API_KEY = config.GetDotEnvVariable("API_KEY") + +// makeRequest handles the common operations of making an HTTP request to the CoinAPI +func makeRequest(url string) ([]byte, error) { + client := &http.Client{} + req, err := http.NewRequest(http.MethodGet, url, nil) + if err != nil { + return nil, fmt.Errorf("creating request: %w", err) + } + + req.Header.Set("X-CoinAPI-Key", API_KEY) + resp, err := client.Do(req) + if err != nil { + return nil, fmt.Errorf("executing request: %w", err) + } + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, fmt.Errorf("unexpected status code: %d", resp.StatusCode) + } + + response, err := io.ReadAll(resp.Body) + if err != nil { + return nil, fmt.Errorf("reading response: %w", err) + } + + return response, nil +} + +// FetchRate retrieves the current rate of Bitcoin in the specified currency +func FetchRate(currency string) (float64, error) { + url := BASE_URL + "exchangerate/BTC/" + currency + response, err := makeRequest(url) + if err != nil { + return 0, err + } + + var message responses.CurrencyRate + if err := json.Unmarshal(response, &message); err != nil { + return 0, fmt.Errorf("unmarshal response: %w", err) + } + + return message.Rate, nil +} + +// FetchDailyVariation retrieves the daily variation in percentage of the Bitcoin price for a specified currency +func FetchDailyVariation(currency string) (float64, error) { + url := fmt.Sprintf("%sohlcv/BTC/%s/latest?period_id=1DAY&limit=1", BASE_URL, currency) + response, err := makeRequest(url) + if err != nil { + return 0, err + } + + var data []responses.OHLCVData + if err := json.Unmarshal(response, &data); err != nil { + return 0, fmt.Errorf("unmarshal OHLCV data: %w", err) + } + + if len(data) > 0 { + ohlcv := data[0] + variation := ((ohlcv.PriceClose - ohlcv.PriceOpen) / ohlcv.PriceOpen) * 100 + return variation, nil + } + + return 0, fmt.Errorf("no data found") +} diff --git a/pkg/message/message.go b/pkg/message/message.go deleted file mode 100644 index f4238b0..0000000 --- a/pkg/message/message.go +++ /dev/null @@ -1,8 +0,0 @@ -package message - -type Message struct { - Time string `json:"time"` - AssetIdBase string `json:"asset_id_base"` - AssetIdQuote string `json:"asset_id_quote"` - Rate float64 `json:"rate"` -} diff --git a/pkg/post/post.go b/pkg/post/post.go index 7d4066c..f7491a6 100644 --- a/pkg/post/post.go +++ b/pkg/post/post.go @@ -8,7 +8,7 @@ import ( "sync/atomic" "time" - "github.com/araujo88/bitcoin-price-bot-nostr/pkg/btcrate" + "github.com/araujo88/bitcoin-price-bot-nostr/pkg/coinapi" "github.com/araujo88/bitcoin-price-bot-nostr/pkg/config" "github.com/araujo88/bitcoin-price-bot-nostr/pkg/relay" "github.com/nbd-wtf/go-nostr" @@ -40,14 +40,42 @@ func Post() error { return err } - rate_usd := 1 / btcrate.FetchRate("USD") / 0.00000001 - rate_eur := 1 / btcrate.FetchRate("EUR") / 0.00000001 - rate_jpy := 1 / btcrate.FetchRate("JPY") / 0.00000001 - rate_gbp := 1 / btcrate.FetchRate("GBP") / 0.00000001 - rate_brl := 1 / btcrate.FetchRate("BRL") / 0.00000001 + rate_usd, err := coinapi.FetchRate("USD") + if err != nil { + return errors.New("error fetching rate for USD") + } + rate_eur, err := coinapi.FetchRate("EUR") + if err != nil { + return errors.New("error fetching rate for EUR") + } + rate_brl, err := coinapi.FetchRate("BRL") + if err != nil { + return errors.New("error fetching rate for BRL") + } + + daily_variation_usd, err := coinapi.FetchDailyVariation("USD") + if err != nil { + return errors.New("error fetching daily vartion for USD") + } + + daily_variation_eur, err := coinapi.FetchDailyVariation("EUR") + if err != nil { + return errors.New("error fetching daily vartion for EUR") + } + + daily_variation_brl, err := coinapi.FetchDailyVariation("BRL") + if err != nil { + return errors.New("error fetching daily vartion for BRL") + } + + content := fmt.Sprintf(`1 BTC = %.0f USD (%.2f %%)\n + 1 BTC = %0.f EUR (%.2f %%)\n + 1 BTC = %0.f BRL (%.2f %%)\n`, + rate_usd, daily_variation_usd, + rate_eur, daily_variation_eur, + rate_brl, daily_variation_brl) - price_string := fmt.Sprintf("1 USD = %.0f sats\n1 EUR = %0.f sats\n1 JPY = %0.f sats\n1 GBP = %0.f sats\n1 BRL = %0.f sats", rate_usd, rate_eur, rate_jpy, rate_gbp, rate_brl) - ev.Content = price_string + ev.Content = content ev.CreatedAt = time.Now() ev.Kind = nostr.KindTextNote diff --git a/pkg/responses/resposes.go b/pkg/responses/resposes.go new file mode 100644 index 0000000..1364b93 --- /dev/null +++ b/pkg/responses/resposes.go @@ -0,0 +1,21 @@ +package responses + +type CurrencyRate struct { + Time string `json:"time"` + AssetIdBase string `json:"asset_id_base"` + AssetIdQuote string `json:"asset_id_quote"` + Rate float64 `json:"rate"` +} + +type OHLCVData struct { + TimePeriodStart string `json:"time_period_start"` + TimePeriodEnd string `json:"time_period_end"` + TimeOpen string `json:"time_open"` + TimeClose string `json:"time_close"` + PriceOpen float64 `json:"price_open"` + PriceHigh float64 `json:"price_high"` + PriceLow float64 `json:"price_low"` + PriceClose float64 `json:"price_close"` + VolumeTraded float64 `json:"volume_traded"` + TradesCount int `json:"trades_count"` +}