253 lines
5.9 KiB
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
|
||
|
}
|