diff --git a/db.go b/db/db.go similarity index 93% rename from db.go rename to db/db.go index da7c5d0..3dc7482 100644 --- a/db.go +++ b/db/db.go @@ -1,4 +1,4 @@ -package main +package db import ( "database/sql" @@ -20,3 +20,7 @@ func init() { db.Exec("CREATE TABLE IF NOT EXISTS prestige(id STRING NOT NULL UNIQUE, multiplier INTEGER NOT NULL)") db.Exec("CREATE TABLE IF NOT EXISTS starred(id STRING NOT NULL UNIQUE)") } + +func Close() { + db.Close() +} diff --git a/db/prestige.go b/db/prestige.go new file mode 100644 index 0000000..3085b1d --- /dev/null +++ b/db/prestige.go @@ -0,0 +1,60 @@ +package db + +import ( + "fmt" + "sync" +) + +var ( + prestigeCache = make(map[string]int) + prestigeMutex sync.Mutex +) + +func SetPrestigeMultiplier(discordID string, newMultiplier int) error { + prestigeMutex.Lock() + defer prestigeMutex.Unlock() + + tx, err := db.Begin() + if err != nil { + return fmt.Errorf("db: SetPrestige: underlying SQL error on 'begin': %w", err) + } + _, err = tx.Exec("REPLACE INTO prestige(id, multiplier) VALUES(?,?)", discordID, newMultiplier) + if err != nil { + tx.Rollback() + return fmt.Errorf("db: SetPrestige: underlying SQL error on 'replace/prestige': %w", err) + } + _, err = tx.Exec("REPLACE INTO xp(id, xp) VALUES(?,0)", discordID) + if err != nil { + tx.Rollback() + return fmt.Errorf("db: SetPrestige: underlying SQL error on 'replace/xp': %w", err) + } + err = tx.Commit() + if err != nil { + return fmt.Errorf("db: SetPrestige: underlying SQL error on 'commit': %w", err) + } + prestigeCache[discordID] = newMultiplier + return nil +} + +func GetPrestigeMultiplier(discordID string) (int, error) { + prestigeMutex.Lock() + defer prestigeMutex.Unlock() + + if prestigeCache[discordID] > 0 { + return prestigeCache[discordID], nil + } + + rows, err := db.Query("SELECT multiplier FROM prestige WHERE id=?", discordID) + if err != nil { + return 1, fmt.Errorf("db: GetPrestigeMultiplier: underlying SQL error on 'select' (%s): %w", discordID, err) + } + defer rows.Close() + if rows.Next() { + var multiplier int + rows.Scan(&multiplier) + prestigeCache[discordID] = multiplier + return multiplier, nil + } + prestigeCache[discordID] = 1 + return 1, nil +} diff --git a/db/shard.go b/db/shard.go new file mode 100644 index 0000000..a03b093 --- /dev/null +++ b/db/shard.go @@ -0,0 +1,25 @@ +package db + +import "fmt" + +func RewardShard(discordID string, count int) error { + _, err := db.Exec("INSERT INTO shard(id, shard) VALUES(?, 1) ON CONFLICT(id) DO UPDATE SET shard = shard+1", discordID) + if err != nil { + return fmt.Errorf("db: RewardShard: underlying SQL error on 'insert' (%s, %d): %w", discordID, count, err) + } + return nil +} + +func GetShard(discordID string) (int, error) { + rows, err := db.Query("SELECT shard FROM shard WHERE id=?", discordID) + if err != nil { + return 0, fmt.Errorf("db: GetShard: underlying SQL error on 'select': %w", err) + } + defer rows.Close() + if rows.Next() { + var shard int + rows.Scan(&shard) + return shard, nil + } + return 0, nil +} diff --git a/db/starboard.go b/db/starboard.go new file mode 100644 index 0000000..2c6336d --- /dev/null +++ b/db/starboard.go @@ -0,0 +1,20 @@ +package db + +import "fmt" + +func HasStarredBefore(discordID string) (bool, error) { + rows, err := db.Query("SELECT id FROM starred WHERE id=?", discordID) + if err != nil { + return true, fmt.Errorf("db: HasStarredBefore: underlying SQL error on 'select' (%s): %w", discordID, err) + } + defer rows.Close() + return rows.Next(), nil +} + +func SetStarred(discordID string) error { + _, err := db.Exec("INSERT INTO starred(id) VALUES(?)", discordID) + if err != nil { + return fmt.Errorf("db: SetStarred: underlying SQL error on 'insert' (%s): %w", discordID, err) + } + return nil +} diff --git a/db/xp.go b/db/xp.go new file mode 100644 index 0000000..5a3a553 --- /dev/null +++ b/db/xp.go @@ -0,0 +1,28 @@ +package db + +import "fmt" + +func IncrementXP(discordID string, amount int) error { + _, err := db.Exec( + "INSERT INTO xp(id, xp) VALUES(?, ?) ON CONFLICT(id) DO UPDATE SET xp = xp+?", + discordID, amount, amount, + ) + if err != nil { + return fmt.Errorf("db: IncrementXP: underlying SQL error on 'insert' (%s, %d): %w", discordID, amount, err) + } + return nil +} + +func GetXP(discordID string) (int, error) { + rows, err := db.Query("SELECT xp FROM xp WHERE id=?", discordID) + if err != nil { + return 0, fmt.Errorf("db: GetXP: underlying SQL error on 'select' (%s): %w", discordID, err) + } + defer rows.Close() + if rows.Next() { + var xp int + rows.Scan(&xp) + return xp, nil + } + return 0, nil +} diff --git a/gacha.go b/gacha.go index cc8c507..ef3d23b 100644 --- a/gacha.go +++ b/gacha.go @@ -5,6 +5,8 @@ import ( "fmt" "github.com/bwmarrin/discordgo" + + "PermissionGacha/db" ) const ( @@ -53,7 +55,7 @@ func rollGacha(dg *discordgo.Session, member *discordgo.Member) { } channel, err := dg.UserChannelCreate(member.User.ID) if found { - rewardShard(dg, member.User.ID) + db.RewardShard(member.User.ID, 1) if err == nil { dg.ChannelMessageSend( channel.ID, diff --git a/levels.go b/levels.go index 1230d19..ae0c17e 100644 --- a/levels.go +++ b/levels.go @@ -39,7 +39,11 @@ func listenToLevelUpdate(dg *discordgo.Session) { continue } levelQueue[k] = false - level, _ := getLevelAndXP(dg, k) + level, _, err := getLevelAndXP(k) + if err != nil { + logError(dg, err) + continue + } if assignedLevels[k] != level { assignedLevels[k] = level go checkForGachaEligibility(dg, k, rolesFromLevel[level-1]) diff --git a/main.go b/main.go index 8cb80ff..ff6f1d4 100644 --- a/main.go +++ b/main.go @@ -9,6 +9,8 @@ import ( "syscall" "github.com/bwmarrin/discordgo" + + "PermissionGacha/db" ) func main() { diff --git a/messageCreate.go b/messageCreate.go index c401856..19c5807 100644 --- a/messageCreate.go +++ b/messageCreate.go @@ -8,9 +8,10 @@ import ( "time" "github.com/bwmarrin/discordgo" + + "PermissionGacha/db" ) -const generalChannelID = "645550570001924098" const emojiCheck = "✅" const prestigeRequirement = 90000 @@ -20,7 +21,7 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { if m.Author.Bot { return } - go incrementXP(s, m.Author.ID, m.ChannelID == generalChannelID) + go incrementXP(s, m) if m.Content == "pgqlevel" { handleXPRequest(s, m) @@ -37,7 +38,11 @@ func messageCreate(s *discordgo.Session, m *discordgo.MessageCreate) { } func handleXPRequest(s *discordgo.Session, m *discordgo.MessageCreate) { - level, rawXP := getLevelAndXP(s, m.Author.ID) + level, rawXP, err := getLevelAndXP(m.Author.ID) + if err != nil { + logError(s, err) + return + } randomSource := make([]byte, 1) rand.Read(randomSource) xp := rawXP - (rawXP % 100) + ((int(randomSource[0]) + 128) % 100) @@ -55,7 +60,11 @@ func handleXPRequest(s *discordgo.Session, m *discordgo.MessageCreate) { } func handleShardRequest(s *discordgo.Session, m *discordgo.MessageCreate) { - shard := getShard(s, m.Author.ID) + shard, err := db.GetShard(m.Author.ID) + if err != nil { + logError(s, err) + return + } s.ChannelMessageSend( m.ChannelID, fmt.Sprintf( @@ -66,8 +75,16 @@ func handleShardRequest(s *discordgo.Session, m *discordgo.MessageCreate) { } func handlePrestigeRequest(s *discordgo.Session, m *discordgo.MessageCreate) { - currentPrestigeLevel := getPrestige(s, m.Author.ID) - xp := getXP(s, m.Author.ID) + currentPrestigeLevel, err := db.GetPrestigeMultiplier(m.Author.ID) + if err != nil { + logError(s, err) + return + } + xp, err := db.GetXP(m.Author.ID) + if err != nil { + logError(s, err) + return + } newPrestigeLevel := xp/prestigeRequirement + 1 if xp < levelUpRequirementCache[3] && currentPrestigeLevel == 1 { @@ -120,10 +137,18 @@ func checkPrestigeReact(s *discordgo.Session, m *discordgo.MessageReactionAdd) { if m.Emoji.Name != emojiCheck { return } - xp := getXP(s, m.UserID) + xp, err := db.GetXP(m.UserID) + if err != nil { + logError(s, err) + return + } newPrestigeLevel := xp/prestigeRequirement + 1 go cleanUpRole(s, m.UserID) - setPrestige(s, m.UserID, newPrestigeLevel) + err = db.SetPrestigeMultiplier(m.UserID, newPrestigeLevel) + if err != nil { + logError(s, err) + return + } s.ChannelMessageSend(m.ChannelID, fmt.Sprintf( "**PRESTIGE > ** <@%s>, you have prestiged!", m.UserID, diff --git a/prestige.go b/prestige.go index c4a24c5..f475701 100644 --- a/prestige.go +++ b/prestige.go @@ -1,68 +1,9 @@ package main import ( - "sync" - "github.com/bwmarrin/discordgo" ) -var prestigeCache = make(map[string]int) -var prestigeMutex = sync.Mutex{} - -func setPrestige(dg *discordgo.Session, discordID string, newMultiplier int) { - prestigeMutex.Lock() - defer prestigeMutex.Unlock() - - tx, err := db.Begin() - if err != nil { - logError(dg, "setPrestige", "begin", discordID, newMultiplier, err) - return - } - _, err = tx.Exec("REPLACE INTO prestige(id, multiplier) VALUES(?,?)", discordID, newMultiplier) - if err != nil { - logError(dg, "setPrestige", "insertMultiplier", discordID, newMultiplier, err) - tx.Rollback() - return - } - _, err = tx.Exec("REPLACE INTO xp(id, xp) VALUES(?,0)", discordID) - err = tx.Commit() - if err != nil { - logError(dg, "setPrestige", "insertXP", discordID, newMultiplier, err) - tx.Rollback() - return - } - if err != nil { - logError(dg, "setPrestige", "commit", discordID, newMultiplier, err) - return - } - - prestigeCache[discordID] = newMultiplier -} - -func getPrestige(dg *discordgo.Session, discordID string) int { - prestigeMutex.Lock() - defer prestigeMutex.Unlock() - - if prestigeCache[discordID] > 0 { - return prestigeCache[discordID] - } - - rows, err := db.Query("SELECT multiplier FROM prestige WHERE id=?", discordID) - if err != nil { - logError(dg, "getPrestige", "select", discordID, err) - return 0 - } - defer rows.Close() - if rows.Next() { - var multiplier int - rows.Scan(&multiplier) - prestigeCache[discordID] = multiplier - return multiplier - } - prestigeCache[discordID] = 1 - return 1 -} - func cleanUpRole(s *discordgo.Session, discordID string) { member, err := s.GuildMember(guildID, discordID) if err != nil { diff --git a/shard.go b/shard.go deleted file mode 100644 index aa182fc..0000000 --- a/shard.go +++ /dev/null @@ -1,39 +0,0 @@ -package main - -import ( - "github.com/bwmarrin/discordgo" -) - -func rewardShard(dg *discordgo.Session, discordID string) { - tx, err := db.Begin() - if err != nil { - logError(dg, "rewardShard", "begin", discordID, err) - return - } - _, err = tx.Exec("INSERT INTO shard(id, shard) VALUES(?, 1) ON CONFLICT(id) DO UPDATE SET shard = shard+1;", discordID) - if err != nil { - logError(dg, "rewardShard", "insert", discordID, err) - tx.Rollback() - return - } - err = tx.Commit() - if err != nil { - logError(dg, "rewardShard", "commit", discordID, err) - return - } -} - -func getShard(dg *discordgo.Session, discordID string) int { - rows, err := db.Query("SELECT shard FROM shard WHERE id=?", discordID) - if err != nil { - logError(dg, "getShard", "select", discordID, err) - return 0 - } - defer rows.Close() - if rows.Next() { - var shard int - rows.Scan(&shard) - return shard - } - return 0 -} diff --git a/starboard.go b/starboard.go index 8ebc8ac..af81dde 100644 --- a/starboard.go +++ b/starboard.go @@ -7,6 +7,8 @@ import ( "time" "github.com/bwmarrin/discordgo" + + "PermissionGacha/db" ) const starEmoji = "⭐" @@ -32,7 +34,12 @@ func listenToStarboardReact(s *discordgo.Session, m *discordgo.MessageReactionAd starboardMutex.Lock() defer starboardMutex.Unlock() - if checkStarredBefore(s, m.MessageID) { + starredBefore, err := db.HasStarredBefore(m.MessageID) + if err != nil { + logError(s, err) + return + } + if starredBefore { return } @@ -90,25 +97,13 @@ func listenToStarboardReact(s *discordgo.Session, m *discordgo.MessageReactionAd return } err = s.MessageReactionAdd(msg.ChannelID, msg.ID, starEmoji) - noteStarred(s, msg.ID) if err != nil { logError(s, "listenToStarboardReact", "messageReactionAdd", err) + return } -} - -func checkStarredBefore(s *discordgo.Session, discordID string) bool { - rows, err := db.Query("SELECT id FROM starred WHERE id=?", discordID) - if err != nil { - logError(s, "checkStarredBefore", "query", err) - return true - } - defer rows.Close() - return rows.Next() -} - -func noteStarred(s *discordgo.Session, discordID string) { - _, err := db.Exec("INSERT INTO starred(id) VALUES(?)", discordID) + err = db.SetStarred(msg.ID) if err != nil { - logError(s, "noteStarred", "exec", err) + logError(s, err) + return } } diff --git a/xp.go b/xp.go index bd8906f..3b6756c 100644 --- a/xp.go +++ b/xp.go @@ -1,15 +1,20 @@ package main import ( + "fmt" "math" "sync" "time" "github.com/bwmarrin/discordgo" + + "PermissionGacha/db" ) const notInGeneralNerf = 10 +const generalChannelID = "645550570001924098" + var lastMessage = make(map[string]time.Time) var incrementMutex = sync.Mutex{} var levelUpRequirementCache [30]int @@ -23,49 +28,24 @@ func init() { } } -func incrementXP(dg *discordgo.Session, discordID string, inGeneral bool) { - amountToIncrement := calculateIncrement(discordID) - if !inGeneral { +func incrementXP(s *discordgo.Session, m *discordgo.MessageCreate) { + amountToIncrement := calculateIncrement(m.Author.ID) + if m.ChannelID != generalChannelID { amountToIncrement /= notInGeneralNerf } - amountToIncrement *= getPrestige(dg, discordID) - - tx, err := db.Begin() - if err != nil { - logError(dg, "incrementXP", "begin", discordID, err) - return - } - _, err = tx.Exec("INSERT INTO xp(id, xp) VALUES(?, ?) ON CONFLICT(id) DO UPDATE SET xp = xp+?;", discordID, amountToIncrement, amountToIncrement) + multiplier, err := db.GetPrestigeMultiplier(m.Author.ID) if err != nil { - logError(dg, "incrementXP", "insert", discordID, err) - tx.Rollback() + logError(s, err) return } - err = tx.Commit() - if err != nil { - logError(dg, "incrementXP", "commit", discordID, err) - return - } - addToLevelQueue(discordID) -} + amountToIncrement *= multiplier -func getXP(dg *discordgo.Session, discordID string) int { - rows, err := db.Query("SELECT xp FROM xp WHERE id=?", discordID) + err = db.IncrementXP(m.Author.ID, amountToIncrement) if err != nil { - logError(dg, "getLevelAndXP", "select", discordID, err) - return 0 - } - defer rows.Close() - if rows.Next() { - var xp int - rows.Scan(&xp) - return xp + logError(s, err) + return } - return 0 -} - -func getLevelAndXP(dg *discordgo.Session, discordID string) (int, int) { - return convertXPIntoLevel(getXP(dg, discordID)) + addToLevelQueue(m.Author.ID) } func calculateIncrement(discordID string) int { @@ -96,3 +76,12 @@ func convertXPIntoLevel(xp int) (int, int) { } return 30, xp - levelUpRequirementCache[29] } + +func getLevelAndXP(discordID string) (int, int, error) { + rawXP, err := db.GetXP(discordID) + if err != nil { + return 1, 0, fmt.Errorf("xp: getLevelAndXP: error requesting XP: %w", err) + } + level, xp := convertXPIntoLevel(rawXP) + return level, xp, nil +}