Replaced `currency.Amount` with `int` for jackpot values to simplify representation. Adjusted parsing logic accordingly. Updated Go version to 1.24.0 and refreshed dependencies in go.mod for compatibility.
214 lines
4.1 KiB
Go
214 lines
4.1 KiB
Go
package powerball
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"strconv"
|
|
"strings"
|
|
"time"
|
|
|
|
"gitea.stevedudenhoeffer.com/steve/go-extractor"
|
|
)
|
|
|
|
type Config struct {
|
|
}
|
|
|
|
var DefaultConfig = Config{}
|
|
|
|
func (c Config) validate() Config {
|
|
return c
|
|
}
|
|
|
|
type Drawing struct {
|
|
Date time.Time
|
|
Numbers [5]int
|
|
PowerBall int
|
|
PowerPlay int
|
|
}
|
|
|
|
type NextDrawing struct {
|
|
Date string
|
|
JackpotDollars int
|
|
}
|
|
|
|
func deferClose(cl io.Closer) {
|
|
if cl != nil {
|
|
_ = cl.Close()
|
|
}
|
|
}
|
|
|
|
func getDrawing(_ context.Context, doc extractor.Document) (*Drawing, error) {
|
|
var drawing Drawing
|
|
|
|
nums := doc.Select("div.game-ball-group div.white-balls")
|
|
|
|
if len(nums) != 5 {
|
|
return nil, fmt.Errorf("expected 5 white balls, got %d", len(nums))
|
|
}
|
|
|
|
for i, num := range nums {
|
|
txt, err := num.Text()
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get white ball %d: %w", i, err)
|
|
}
|
|
|
|
val, err := strconv.Atoi(txt)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to convert white ball %d to int: %w", i, err)
|
|
}
|
|
drawing.Numbers[i] = val
|
|
}
|
|
|
|
powerball := doc.Select("div.game-ball-group div.powerball")
|
|
|
|
if len(powerball) != 1 {
|
|
return nil, fmt.Errorf("expected 1 powerball, got %d", len(powerball))
|
|
}
|
|
|
|
txt, err := powerball[0].Text()
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get powerball: %w", err)
|
|
}
|
|
|
|
val, err := strconv.Atoi(txt)
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to convert powerball to int: %w", err)
|
|
}
|
|
|
|
drawing.PowerBall = val
|
|
|
|
powerplay := doc.Select("span.power-play span.multiplier")
|
|
|
|
if len(powerplay) != 1 {
|
|
return nil, fmt.Errorf("expected 1 powerplay, got %d", len(powerplay))
|
|
}
|
|
|
|
// powerplay is in the format of "2X" or "3X" etc.
|
|
|
|
txt, err = powerplay[0].Text()
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get powerplay: %w", err)
|
|
}
|
|
|
|
val, err = strconv.Atoi(strings.ReplaceAll(strings.ReplaceAll(txt, "X", ""), "x", ""))
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to convert powerplay to int: %w", err)
|
|
}
|
|
drawing.PowerPlay = val
|
|
|
|
return &drawing, nil
|
|
}
|
|
|
|
func getNextDrawing(_ context.Context, doc extractor.Document) (*NextDrawing, error) {
|
|
var nextDrawing NextDrawing
|
|
|
|
date := doc.Select("div.next-powerball h5.title-date")
|
|
|
|
if len(date) != 1 {
|
|
return nil, fmt.Errorf("expected 1 date, got %d", len(date))
|
|
}
|
|
|
|
var err error
|
|
nextDrawing.Date, err = date[0].Text()
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get date: %w", err)
|
|
}
|
|
|
|
jackpot := doc.Select("div.next-powerball div.game-detail-group span.game-jackpot-number")
|
|
|
|
if len(jackpot) != 1 {
|
|
return nil, fmt.Errorf("expected 1 jackpot, got %d", len(jackpot))
|
|
}
|
|
|
|
txt, err := jackpot[0].Text()
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("failed to get jackpot: %w", err)
|
|
}
|
|
|
|
// jackpot is in the format of "$1.5 billion", "$100 million", or "$200,000" etc
|
|
|
|
// make one filter to only get the numeric part of the jackpot
|
|
|
|
numericOnly := func(in string) float64 {
|
|
var out string
|
|
for _, r := range in {
|
|
if r >= '0' && r <= '9' {
|
|
out += string(r)
|
|
}
|
|
|
|
if r == '.' {
|
|
out += string(r)
|
|
}
|
|
}
|
|
|
|
val, err := strconv.ParseFloat(out, 64)
|
|
|
|
if err != nil {
|
|
return 0
|
|
}
|
|
|
|
return val
|
|
}
|
|
|
|
numeric := numericOnly(txt)
|
|
|
|
set := false
|
|
if strings.Contains(txt, "Billion") {
|
|
amt := numeric * 1000000000
|
|
nextDrawing.JackpotDollars = int(amt)
|
|
set = true
|
|
} else if strings.Contains(txt, "Million") {
|
|
amt := numeric * 1000000
|
|
nextDrawing.JackpotDollars = int(amt)
|
|
set = true
|
|
} else {
|
|
nextDrawing.JackpotDollars = int(numeric)
|
|
set = true
|
|
}
|
|
|
|
if !set {
|
|
return nil, fmt.Errorf("failed to convert jackpot to currency: %w", err)
|
|
}
|
|
|
|
return &nextDrawing, nil
|
|
}
|
|
|
|
func (c Config) GetCurrent(ctx context.Context, b extractor.Browser) (*Drawing, *NextDrawing, error) {
|
|
c = c.validate()
|
|
|
|
doc, err := b.Open(ctx, "https://www.powerball.com/", extractor.OpenPageOptions{})
|
|
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
defer deferClose(doc)
|
|
|
|
d, err := getDrawing(ctx, doc)
|
|
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
nd, err := getNextDrawing(ctx, doc)
|
|
|
|
if err != nil {
|
|
return nil, nil, err
|
|
}
|
|
|
|
return d, nd, nil
|
|
}
|
|
|
|
func GetCurrent(ctx context.Context, b extractor.Browser) (*Drawing, *NextDrawing, error) {
|
|
return DefaultConfig.GetCurrent(ctx, b)
|
|
}
|