Initial commit
This commit is contained in:
145
scripts/rsw/internal/cmd/price.go
Normal file
145
scripts/rsw/internal/cmd/price.go
Normal file
@@ -0,0 +1,145 @@
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/runescape-wiki/rsw/internal/prices"
|
||||
"github.com/runescape-wiki/rsw/internal/render"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
func newPriceCmd() *cobra.Command {
|
||||
return &cobra.Command{
|
||||
Use: "price <item name>",
|
||||
Short: "Look up Grand Exchange prices for an item",
|
||||
Long: `Fetches real-time GE price data for an item: current buy/sell prices,
|
||||
trade volume, and recent trend.
|
||||
|
||||
With --ironman, shows alch values and vendor prices instead of GE data.
|
||||
|
||||
Examples:
|
||||
rsw osrs price "abyssal whip"
|
||||
rsw rs3 price "blue partyhat"
|
||||
rsw osrs price "dragon bones" --ironman`,
|
||||
Args: cobra.MinimumNArgs(1),
|
||||
RunE: func(cmd *cobra.Command, args []string) error {
|
||||
name := args[0]
|
||||
priceClient := prices.NewClient(GamePriceBaseURL())
|
||||
mc := prices.NewMappingCache(priceClient)
|
||||
|
||||
if err := mc.Load(); err != nil {
|
||||
return fmt.Errorf("failed to load item mapping: %w", err)
|
||||
}
|
||||
|
||||
item := mc.LookupByName(name)
|
||||
if item == nil {
|
||||
matches := mc.SearchByName(name)
|
||||
if len(matches) == 0 {
|
||||
return fmt.Errorf("no item found matching %q", name)
|
||||
}
|
||||
if len(matches) > 1 {
|
||||
md := render.New()
|
||||
md.H2("Multiple items found")
|
||||
md.P("Did you mean one of these?")
|
||||
shown := 0
|
||||
for _, m := range matches {
|
||||
md.Bullet(m.Name)
|
||||
shown++
|
||||
if shown >= 15 {
|
||||
md.P(fmt.Sprintf("...and %d more", len(matches)-15))
|
||||
break
|
||||
}
|
||||
}
|
||||
fmt.Print(md.String())
|
||||
return nil
|
||||
}
|
||||
item = matches[0]
|
||||
}
|
||||
|
||||
md := render.New()
|
||||
md.H1(item.Name)
|
||||
md.KV("Examine", item.Examine)
|
||||
md.KV("Members", fmt.Sprintf("%v", item.Members))
|
||||
md.KV("Store value", render.FormatGP(item.Value))
|
||||
|
||||
if item.HighAlch != nil {
|
||||
md.KV("High Alch", render.FormatGP(*item.HighAlch))
|
||||
}
|
||||
if item.LowAlch != nil {
|
||||
md.KV("Low Alch", render.FormatGP(*item.LowAlch))
|
||||
}
|
||||
md.Newline()
|
||||
|
||||
if Ironman() {
|
||||
md.H2("Ironman Info")
|
||||
md.P("*GE trading unavailable — showing self-sufficient values.*")
|
||||
md.KV("Store value", render.FormatGP(item.Value))
|
||||
if item.HighAlch != nil {
|
||||
md.KV("High Alch value", render.FormatGP(*item.HighAlch))
|
||||
}
|
||||
md.P("Use `rsw item` for drop sources and shop locations.")
|
||||
} else {
|
||||
latest, err := priceClient.GetLatestForItem(item.ID)
|
||||
if err != nil {
|
||||
md.P("*Price data unavailable.*")
|
||||
} else {
|
||||
md.H2("Grand Exchange Price")
|
||||
if latest.High != nil {
|
||||
md.KV("Instant buy", render.FormatGP(*latest.High))
|
||||
if latest.HighTime != nil {
|
||||
md.KV("Last buy", timeAgo(*latest.HighTime))
|
||||
}
|
||||
}
|
||||
if latest.Low != nil {
|
||||
md.KV("Instant sell", render.FormatGP(*latest.Low))
|
||||
if latest.LowTime != nil {
|
||||
md.KV("Last sell", timeAgo(*latest.LowTime))
|
||||
}
|
||||
}
|
||||
if item.Limit != nil {
|
||||
md.KV("Buy limit", render.FormatNumber(*item.Limit))
|
||||
}
|
||||
md.Newline()
|
||||
}
|
||||
|
||||
hourly, err := priceClient.Get1Hour(item.ID)
|
||||
if err == nil && len(hourly.Data) > 0 {
|
||||
recent := hourly.Data[len(hourly.Data)-1]
|
||||
md.H2("Recent Activity (1h)")
|
||||
if recent.AvgHighPrice != nil {
|
||||
md.KV("Avg buy price", render.FormatGP(*recent.AvgHighPrice))
|
||||
}
|
||||
md.KV("Buy volume", render.FormatNumber(recent.HighVolume))
|
||||
if recent.AvgLowPrice != nil {
|
||||
md.KV("Avg sell price", render.FormatGP(*recent.AvgLowPrice))
|
||||
}
|
||||
md.KV("Sell volume", render.FormatNumber(recent.LowVolume))
|
||||
md.Newline()
|
||||
}
|
||||
}
|
||||
|
||||
fmt.Print(md.String())
|
||||
return nil
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func timeAgo(unixTime int64) string {
|
||||
t := time.Unix(unixTime, 0)
|
||||
d := time.Since(t)
|
||||
switch {
|
||||
case d < time.Minute:
|
||||
return "just now"
|
||||
case d < time.Hour:
|
||||
return fmt.Sprintf("%d min ago", int(d.Minutes()))
|
||||
case d < 24*time.Hour:
|
||||
return fmt.Sprintf("%d hours ago", int(d.Hours()))
|
||||
default:
|
||||
return fmt.Sprintf("%d days ago", int(d.Hours()/24))
|
||||
}
|
||||
}
|
||||
|
||||
func init() {
|
||||
RegisterCommand(newPriceCmd)
|
||||
}
|
||||
Reference in New Issue
Block a user