twitch-bot/internal/apimodules/raffle/pick.go

65 lines
1.2 KiB
Go
Raw Normal View History

2023-07-14 14:15:58 +00:00
package raffle
import (
"crypto/rand"
"encoding/binary"
mathRand "math/rand"
"github.com/pkg/errors"
)
type (
cryptRandSrc struct{}
)
var errNoCandidatesLeft = errors.New("no candidates left")
func pickWinnerFromRaffle(r raffle) (winner raffleEntry, err error) {
var maxScore float64
for _, re := range r.Entries {
if re.WasPicked {
// We skip previously picked winners and pretend they
// don't exist
continue
}
maxScore += re.Multiplier
}
if maxScore == 0 {
return winner, errNoCandidatesLeft
}
winnerPoint := mathRand.New(cryptRandSrc{}).Float64() * maxScore //#nosec:G404 - RNG is using a secure source
for i := range r.Entries {
re := r.Entries[i]
if re.WasPicked {
// We skip previously picked winners and pretend they
// don't exist
continue
}
winnerPoint -= re.Multiplier
if winnerPoint < 0 {
winner = re
break
}
}
return winner, nil
}
func (cryptRandSrc) Int63() int64 {
var b [8]byte
if _, err := rand.Read(b[:]); err != nil {
return -1
}
2023-07-14 14:15:58 +00:00
// mask off sign bit to ensure positive number
return int64(binary.LittleEndian.Uint64(b[:]) & (1<<63 - 1)) //#nosec:G115 - Masking ensures conversion is fine
2023-07-14 14:15:58 +00:00
}
// We're using a non-seedable source
func (cryptRandSrc) Seed(int64) {}