Added !poker and auth entry point
This commit is contained in:
@@ -4,9 +4,10 @@ import (
|
|||||||
"log"
|
"log"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
"streambot_twitch/internal/auth"
|
auth "streambot_twitch/internal/auth"
|
||||||
"streambot_twitch/internal/chat"
|
"streambot_twitch/internal/chat"
|
||||||
"streambot_twitch/internal/commands"
|
"streambot_twitch/internal/commands"
|
||||||
|
"streambot_twitch/internal/points"
|
||||||
twitchapi "streambot_twitch/internal/twitchapi"
|
twitchapi "streambot_twitch/internal/twitchapi"
|
||||||
tests "streambot_twitch/internal/tests"
|
tests "streambot_twitch/internal/tests"
|
||||||
twitch "github.com/gempir/go-twitch-irc/v4"
|
twitch "github.com/gempir/go-twitch-irc/v4"
|
||||||
@@ -14,7 +15,7 @@ import (
|
|||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
go func() {
|
go func() {
|
||||||
if err := oauth.StartOAuthServer(":8080"); err != nil {
|
if err := auth.StartOAuthServer(":8080"); err != nil {
|
||||||
log.Fatalf("OAuth server error: %v", err)
|
log.Fatalf("OAuth server error: %v", err)
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -22,9 +23,9 @@ func main() {
|
|||||||
log.Println("OAuth server started on port 8080")
|
log.Println("OAuth server started on port 8080")
|
||||||
|
|
||||||
botUsername := os.Getenv("nz_chatterbot")
|
botUsername := os.Getenv("nz_chatterbot")
|
||||||
botOAuth :+ os.Getenv("BOT_OAUTH")
|
botOAuth := os.Getenv("BOT_OAUTH")
|
||||||
channel := os Getenv("brycefromnz101")
|
channel := os Getenv("brycefromnz101")
|
||||||
clientID := os.Getenv("TWITCH_CLIENTID")
|
clientID := os.Getenv("TWITCH_CLIENT_ID")
|
||||||
clientSecret := os.Getenv("TWITCH_CLIENT_SECRET")
|
clientSecret := os.Getenv("TWITCH_CLIENT_SECRET")
|
||||||
botChannel := botUsername
|
botChannel := botUsername
|
||||||
|
|
||||||
@@ -52,6 +53,14 @@ log.Println("OAuth server started on port 8080")
|
|||||||
log.Println("All startup tests passed. Starting bot. . .")
|
log.Println("All startup tests passed. Starting bot. . .")
|
||||||
// END of TESTS
|
// END of TESTS
|
||||||
|
|
||||||
|
// load chat points
|
||||||
|
if err := points.LoadPoints(); err != nil {
|
||||||
|
log.Fatalf("Failed to load points: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// make sure points are saved on exit
|
||||||
|
defer points.SavePoints()
|
||||||
|
|
||||||
// load custom commands
|
// load custom commands
|
||||||
if err := commands.LoadCommands(); err != nil {
|
if err := commands.LoadCommands(); err != nil {
|
||||||
log.Fatalf("Failed to Load Custom Commands: %v", err)
|
log.Fatalf("Failed to Load Custom Commands: %v", err)
|
||||||
|
13
cmd/oauth/main.go
Normal file
13
cmd/oauth/main.go
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"log"
|
||||||
|
|
||||||
|
auth "streambot_twitch/internal/auth"
|
||||||
|
)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
if err := auth.StartOAuthServer(":8080"); err != nil {
|
||||||
|
log.Fatalf("OAuth server error: %v", err)
|
||||||
|
}
|
||||||
|
}
|
@@ -1,4 +1,4 @@
|
|||||||
package oauth
|
package auth
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
@@ -91,6 +91,12 @@ func HandleMessage(client *twitch.Client, apiClient interface{}, message twitch.
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// dispatch poker/table commands
|
||||||
|
if strings.HasPrefix(strings.ToLower(msg), "!poker") || strings.HasPrefix(strings.ToLower(msg), "!table") {
|
||||||
|
HandlePokerCommand(client, message, user, channel, msg)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// Dispatch custom/default commands
|
// Dispatch custom/default commands
|
||||||
HandleCustomCommands(client, message, user, channel, botUsername)
|
HandleCustomCommands(client, message, user, channel, botUsername)
|
||||||
}
|
}
|
||||||
|
154
internal/chat/poker.go
Normal file
154
internal/chat/poker.go
Normal file
@@ -0,0 +1,154 @@
|
|||||||
|
package chat
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"math/rand"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
twitch "github.com/gempir/go-twitch-irc/v4"
|
||||||
|
"streambot_twitch/internal/points"
|
||||||
|
)
|
||||||
|
|
||||||
|
// card
|
||||||
|
type Card struct {
|
||||||
|
Rank, Suit string
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
suits = []string{" of Hearts", " of Diamnods", " of Clubs", " of Spades"}
|
||||||
|
ranks = []string{"2", "3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A"}
|
||||||
|
baseDeck []Card
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
// build 2 decks (no Jokers) 104 cards
|
||||||
|
for d := 0; d < 2; d++ {
|
||||||
|
for _, s := range suits {
|
||||||
|
for _, r := range ranks {
|
||||||
|
baseDeck = append(baseDeck, Card{Rank: r, Suit: s})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
rand.Seed(time.Now().UnixNano())
|
||||||
|
}
|
||||||
|
|
||||||
|
// impliment !poker or !table <bet>
|
||||||
|
func HandlePokerCommand(
|
||||||
|
client *twitch.Client,
|
||||||
|
message twitch.PrivateMessage,
|
||||||
|
user twitch.User,
|
||||||
|
channel, msg string,
|
||||||
|
) {
|
||||||
|
parts := strings.Fields(msg)
|
||||||
|
if len(parts) < 2 {
|
||||||
|
client.Say(channel, fmt.Sprintf("@%s Usage: !poker <bet>", user.DisplayName))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
bet, err := strconv.Atoi(parts[1])
|
||||||
|
if err != nil || bet <= 0 {
|
||||||
|
client.Say(channel, fmt.Sprintf("@%s Invalid bet amount", user.DisplayName))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
login := strings.ToLower(user.Name)
|
||||||
|
// load or init user points
|
||||||
|
balance := points.GetPoints(login)
|
||||||
|
if bet > ballance {
|
||||||
|
client.Say(channel, fmt.Sprintf("@%s You have %d points, bet lower", user.DisplayName, balance))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// Deduct the bet
|
||||||
|
balance, err = points.AddPoints(login, -bet)
|
||||||
|
if err != nil {
|
||||||
|
client.Say(channel, fmt.Sprintf("@%s Error updating points: %v", user.DisplayName, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// shuffle deck
|
||||||
|
deck := make([]Card, len(baseDeck))
|
||||||
|
copy(deck, baseDeck)
|
||||||
|
rand.Shuffle(len(deck), func(i, j int) {deck[i], deck[j] = deck[j], deck[i]})
|
||||||
|
|
||||||
|
// deal 2 cards each
|
||||||
|
player := []Card{deck[0], deck[1]}
|
||||||
|
dealer := []Card{deck[2], deck[3]}
|
||||||
|
|
||||||
|
// compute hand values
|
||||||
|
pv := handValue(player)
|
||||||
|
dv := handValue(dealer)
|
||||||
|
|
||||||
|
// Determine outcome and payout multiplier
|
||||||
|
var net int
|
||||||
|
var outcome string
|
||||||
|
switch {
|
||||||
|
case pv > 21:
|
||||||
|
outcome = "FBPenalty Bust! You lose"
|
||||||
|
net = 0
|
||||||
|
case pv == 21:
|
||||||
|
outcome = "BLACKJACK! GoatEmotey"
|
||||||
|
net = bet * 2
|
||||||
|
case dv > 21:
|
||||||
|
outcome = "GoldPLZ Dealer bust! You win"
|
||||||
|
net = int(float64(bet) * 1.5)
|
||||||
|
case pv > dv:
|
||||||
|
outcome = "OhMyDog You Win"
|
||||||
|
net int(float64(bet) * 1.3)
|
||||||
|
case pv < dv:
|
||||||
|
outcome = "cmonBruh You lose"
|
||||||
|
net = 0
|
||||||
|
default:
|
||||||
|
outcome = "PunchTrees Push"
|
||||||
|
net = bet
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add winnings
|
||||||
|
balance, err = points.AddPoints(login, net)
|
||||||
|
if err != nil {
|
||||||
|
client.Say(channel, fmt.Sprintf("@%s Error updating Points: %v", user.DisplayName, err))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// format a hand for display
|
||||||
|
fmtHand := func(h []Card) string {
|
||||||
|
var parts []string
|
||||||
|
for _, c := range h {
|
||||||
|
parts = append(parts, c.Rank+c.Suit)
|
||||||
|
}
|
||||||
|
return strings.Join(parts, ",")
|
||||||
|
}
|
||||||
|
|
||||||
|
// post result
|
||||||
|
client.Say(channel, fmt.Sprintf("@%s You: [%s]=%d Dealer: [%s]=%d => %s | Bet: %d Net: %d Ballance: %d", user.DisplayName,
|
||||||
|
fmtHand(player), pv,
|
||||||
|
fmtHand(dealer), dv,
|
||||||
|
outcome,
|
||||||
|
bet, net, balance,
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// compute BlackJack style value (A = 11 or 1)
|
||||||
|
func handValue(hand []Card) int {
|
||||||
|
total := 0
|
||||||
|
aces := 0
|
||||||
|
for _, c := range hand {
|
||||||
|
switch c.Rank {
|
||||||
|
case "J", "Q", "K":
|
||||||
|
total += 10
|
||||||
|
case "A":
|
||||||
|
total += 11
|
||||||
|
aces++
|
||||||
|
default:
|
||||||
|
v, _ := strconv.Atoi(c.Rank)
|
||||||
|
total += v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert A from 11 to 1 while busting
|
||||||
|
for total > 21 && aces > 0 {
|
||||||
|
total -= 10
|
||||||
|
aces--
|
||||||
|
}
|
||||||
|
return total
|
||||||
|
}
|
84
internal/points/points.go
Normal file
84
internal/points/points.go
Normal file
@@ -0,0 +1,84 @@
|
|||||||
|
package points
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"io/ioutil"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
dataDir = "internal/storage"
|
||||||
|
pointsFile = "points.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
mu sync.Mutex
|
||||||
|
pts map[string]int
|
||||||
|
)
|
||||||
|
|
||||||
|
const DefaultPoints = 100000
|
||||||
|
|
||||||
|
// init or load points from disk
|
||||||
|
func LoadPoints() error {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
pts = make(map[string]int)
|
||||||
|
path := filepath.Join(dataDir, pointsFile)
|
||||||
|
|
||||||
|
data, err := ioutil.ReadFile(path)
|
||||||
|
if err != nil {
|
||||||
|
if os.IsNotExist(err) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return json.Unmarshal(data, &pts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// save balances to disk
|
||||||
|
func SavePoints() error {
|
||||||
|
mu.Lock()
|
||||||
|
data, err := json.MarshalIndent(pts, "", " ")
|
||||||
|
mu.Unlock()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if err := os.MkdirAll(dataDir, 0755); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return ioutil.WriteFile(filepath.Join(dataDir, pointsFile), data, 0644)
|
||||||
|
}
|
||||||
|
|
||||||
|
// return point balance for user, init if needed
|
||||||
|
func GetPoints(user string) int {
|
||||||
|
mu.Lock()
|
||||||
|
defer mu.Unlock()
|
||||||
|
key := strings.ToLower(user)
|
||||||
|
bal, ok := pts[key]
|
||||||
|
if !ok {
|
||||||
|
pts[key] = DefaultPoints
|
||||||
|
bal = DefaultPoints
|
||||||
|
}
|
||||||
|
return bal
|
||||||
|
}
|
||||||
|
|
||||||
|
// add points to user's bal, save & return new bal
|
||||||
|
func AddPoints(user string, delta int) (int error) {
|
||||||
|
mu.Lock()
|
||||||
|
key := strings.ToLower(user)
|
||||||
|
bal, ok := pts[key]
|
||||||
|
if !ok {
|
||||||
|
bal = DefaultPoints
|
||||||
|
}
|
||||||
|
bal += delta
|
||||||
|
pts[key] = bal
|
||||||
|
mu.Unlock()
|
||||||
|
|
||||||
|
if err := SavePoints(); err != nil {
|
||||||
|
return bal, err
|
||||||
|
}
|
||||||
|
return bal, nil
|
||||||
|
}
|
Reference in New Issue
Block a user