shoGambler/plugins-src/bet/main.go

253 lines
5.9 KiB
Go

package main
import (
"errors"
"math/big"
"shoGambler/lib"
"time"
"github.com/gin-gonic/gin"
)
type bet struct {
amount *big.Int
answer string
}
var (
bets = make(map[string]bet)
possibleAnswers []string
bettingIsOpen = false
question string
addPoints func(string, *big.Int)
userIsModerator func(string) bool
timeToBet time.Time
)
func Metadata() (lib.PluginData, error) {
return lib.PluginData{
Name: "bet-on-it",
CanReturnPoints: false,
RecommendedPoints: big.NewInt(10),
CanAcceptArbitraryPointAmount: true,
PluginHTML: `
<h1>Bet on it</h1>
<p>Vote for what you think will happen</p>
<p>If you lose the bet, you lose your points</p>
<p>If you win the bet, you get double your points back</p>
<p id="question"></p>
<p id="possible"></p>
<p id="timer"></p>
<button id="bet" disabled>Bet</button>
`,
PluginScript: `
async function updateTimer(time) {
while (true) {
let timeLeft = time - Math.floor(Date.now() / 1000);
if (timeLeft <= 0) {
document.getElementById("timer").innerText = "Betting is now closed";
document.getElementById("bet").disabled = true;
} else {
document.getElementById("timer").innerText = "Betting closes in " + timeLeft + " seconds";
}
await new Promise(r => setTimeout(r, 1000));
}
}
let data
fetch("/api/extra/bet-on-it", {
method: "POST",
body: JSON.stringify({
Action: "getCurrentBet",
}),
headers: {
"Content-Type": "application/json",
},
})
.then(async (response) => {
if (response.status == 206) {
alert("There is not a running bet");
window.location.href = "/";
} else if (response.status != 200) {
alert("Error: " + response.statusText);
window.location.href = "/";
}
data = await response.json();
document.getElementById("question").innerText = data["question"];
document.getElementById("possible").innerText = data["possible"].join(", ");
updateTimer(data["timeToBet"]);
document.getElementById("bet").disabled = false;
})
document.getElementById("bet").addEventListener("click", async () => {
let points = BigInt(0);
try {
points = BigInt(prompt("How many points do you want to spend?"));
} catch (e) {
alert("Invalid number");
return;
}
let candidate = prompt(data["question"] + data["possible"].join(", ") + "(case sensitive)");
if (data["possible"].includes(candidate)) {
sendCost(points, candidate);
} else {
alert("That's not an option!")
}
})
</script>
`,
ApiCode: ApiCode,
HasExtraAPI: true,
ExtraAPICode: ExtraAPICode,
OnDataReturn: "alert('Bet placed. Good luck!')",
CanAddPoints: true,
CanCheckMod: true,
}, nil
}
func ApiCode(_ *gin.Context, input lib.ApiInput) (*big.Int, error) {
if time.Now().Before(timeToBet) {
// See which option the user bet on
candidate := input.OptionalData
// Add the user's bet to the map
validBet := false
for _, possibleAnswer := range possibleAnswers {
if candidate == possibleAnswer {
validBet = true
break
}
}
if !validBet {
return nil, errors.New("invalid bet")
} else {
bets[input.ChannelID] = bet{
amount: input.InputPoints,
answer: candidate,
}
return nil, nil
}
} else {
return nil, errors.New("betting is closed")
}
}
func ExtraAPICode(c *gin.Context) {
var data map[string]interface{}
err := c.BindJSON(&data)
if err != nil {
c.JSON(400, gin.H{
"error": "Invalid JSON",
})
return
}
action, ok := data["Action"].(string)
if !ok {
c.JSON(400, gin.H{
"error": "Invalid action",
})
return
}
switch action {
case "getCurrentBet":
if bettingIsOpen {
c.JSON(200, gin.H{
"question": question,
"possible": possibleAnswers,
"timeToBet": timeToBet.Unix(),
})
return
} else {
c.JSON(206, gin.H{
"error": "There is not a running bet",
})
return
}
case "startBet":
if bettingIsOpen {
c.JSON(206, gin.H{
"error": "There is already a running bet",
})
return
} else {
accessToken := c.GetHeader("Authorization")
if !userIsModerator(accessToken) || accessToken == "" {
c.JSON(403, gin.H{
"error": "You must be a moderator to start a bet",
})
return
}
question, ok = data["question"].(string)
if !ok {
c.JSON(400, gin.H{
"error": "Invalid question",
})
return
}
possibleAnswersJSON, ok := data["possible"].([]interface{})
if !ok {
c.JSON(400, gin.H{
"error": "Invalid possible answers",
})
return
}
possibleAnswers = make([]string, len(possibleAnswersJSON))
for i, possibleAnswer := range possibleAnswersJSON {
possibleAnswers[i], ok = possibleAnswer.(string)
if !ok {
c.JSON(400, gin.H{
"error": "Invalid possible answer",
})
return
}
}
timeToBet = time.Unix(int64(data["timeToBet"].(float64)), 0)
bettingIsOpen = true
c.JSON(200, gin.H{
"success": true,
})
return
}
case "endBet":
if bettingIsOpen {
accessToken := c.GetHeader("Authorization")
if !userIsModerator(accessToken) || accessToken == "" {
c.JSON(403, gin.H{
"error": "You must be a moderator to end a bet",
})
return
}
bettingIsOpen = false
// Give the winners double their points
for channelID, bet := range bets {
if bet.answer == data["answer"].(string) {
addPoints(channelID, new(big.Int).Mul(bet.amount, big.NewInt(2)))
}
}
// Clear the bets
bets = make(map[string]bet)
question = ""
possibleAnswers = nil
// Return success
c.JSON(200, gin.H{
"success": true,
})
return
}
default:
c.JSON(400, gin.H{
"error": "Invalid action",
})
return
}
}
func SetAddPointsFunc(addPointsFunc func(string, *big.Int)) {
addPoints = addPointsFunc
}
func SetCheckModFunc(userIsModeratorFunc func(string) bool) {
userIsModerator = userIsModeratorFunc
}