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: `

Bet on it

Vote for what you think will happen

If you lose the bet, you lose your points

If you win the bet, you get double your points back

`, 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!") } }) `, 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 }