commands+db: Implement level recalculation

master
Luther Wen Xu 2020-05-12 20:57:42 +07:00
parent 24a3887288
commit 4f687f4d56
Signed by: chanbakjsd
GPG Key ID: B7D77E3E9D102B70
7 changed files with 226 additions and 23 deletions

@ -18,6 +18,7 @@ type command struct {
var commands = []command{
command{"autorole", "<message link> <emoji> <role to assign>", handleAutorole, 3, true},
command{"recalclevel", "", handleRecalculateLevel, 0, false},
}
func Event(dg *discordgo.Session, m *discordgo.MessageCreate) {

@ -0,0 +1,49 @@
package commands
import (
"fmt"
"math/rand"
"time"
"github.com/bwmarrin/discordgo"
"gitea.teamortix.com/chanbakjsd/Milen/level"
"gitea.teamortix.com/chanbakjsd/Milen/util"
)
func handleRecalculateLevel(dg *discordgo.Session, m *discordgo.MessageCreate, arguments []string) {
if len(arguments) == 0 {
util.SendFailEmbed(
dg, m.ChannelID, "Bot Owner Access Required",
"⚠️ This action might take a while and disable level calculation system.\nTo confirm, check console for launch code.",
)
fmt.Println("Level launch requested: " + calculationCode)
return
}
if arguments[0] != calculationCode {
util.SendFailEmbed(
dg, m.ChannelID, "Incorrect Launch Code", "You entered an incorrect code.",
)
}
util.SendSuccessEmbed(dg, m.ChannelID, "Level is being recalculated.")
level.RecalculateEverything(dg, m.GuildID)
rand.Seed(time.Now().UnixNano())
calculationCode = randomBase64(20)
}
const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+="
var calculationCode string
func init() {
rand.Seed(time.Now().UnixNano())
calculationCode = randomBase64(20)
}
func randomBase64(n int) string {
b := make([]byte, n)
for i := range b {
b[i] = letterBytes[rand.Int63()%int64(len(letterBytes))]
}
return string(b)
}

@ -5,12 +5,6 @@ import (
_ "github.com/jinzhu/gorm/dialects/sqlite"
)
type ReactRole struct {
MessageID string
EmojiID string
RoleID string
}
var db *gorm.DB
func init() {
@ -20,25 +14,9 @@ func init() {
panic(err)
}
db.AutoMigrate(&ReactRole{})
db.AutoMigrate(&PlayerLevel{})
}
func Close() {
db.Close()
}
func CreateReactRole(messageID, emojiID, roleID string) {
db.Create(&ReactRole{
MessageID: messageID,
EmojiID: emojiID,
RoleID: roleID,
})
}
func GetReactRole(messageID, emojiID string) string {
result := ReactRole{
MessageID: messageID,
EmojiID: emojiID,
}
db.Where(&result).First(&result)
return result.RoleID
}

@ -0,0 +1,40 @@
package db
import (
"fmt"
"time"
"github.com/jinzhu/gorm"
)
type PlayerLevel struct {
UserID string `gorm:"primary_key"`
LastActive time.Time
XP int64
}
func IncrementXP(userID string, value int64) {
db.Where(&PlayerLevel{UserID: userID}).
UpdateColumn("xp", gorm.Expr("xp + ?", value)).
UpdateColumn("last_active", time.Now())
}
func GetLastActive(userID string) time.Time {
result := PlayerLevel{
UserID: userID,
}
db.Where(&result).First(&result)
return result.LastActive
}
func DeleteAllLevel() {
db.Delete(&PlayerLevel{})
}
func SetXP(userID string, lastActive time.Time, xp int64) {
db.Create(&PlayerLevel{
UserID: userID,
LastActive: lastActive,
XP: xp,
})
}

@ -0,0 +1,24 @@
package db
type ReactRole struct {
MessageID string
EmojiID string
RoleID string
}
func CreateReactRole(messageID, emojiID, roleID string) {
db.Create(&ReactRole{
MessageID: messageID,
EmojiID: emojiID,
RoleID: roleID,
})
}
func GetReactRole(messageID, emojiID string) string {
result := ReactRole{
MessageID: messageID,
EmojiID: emojiID,
}
db.Where(&result).First(&result)
return result.RoleID
}

@ -0,0 +1,13 @@
package level
import (
"github.com/bwmarrin/discordgo"
)
var ShouldListen = true
func Event(dg *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.Bot || ShouldListen {
return
}
}

@ -0,0 +1,98 @@
package level
import (
"fmt"
"sort"
"time"
"github.com/bwmarrin/discordgo"
"gitea.teamortix.com/chanbakjsd/Milen/db"
"gitea.teamortix.com/chanbakjsd/Milen/util"
)
func RecalculateEverything(dg *discordgo.Session, guildID string) {
ShouldListen = false
defer func() {
ShouldListen = true
}()
startTime := time.Now()
guild, err := dg.Guild(guildID)
if err != nil {
util.ReportError(dg, err)
return
}
listOfMessages := make(map[string][]*discordgo.Message)
for _, v := range guild.Channels {
beforeID := ""
for {
messages, err := dg.ChannelMessages(v.ID, 100, beforeID, "", "")
if err != nil {
util.ReportError(dg, err)
return
}
for _, msg := range messages {
if list, ok := listOfMessages[msg.Author.ID]; ok {
listOfMessages[msg.Author.ID] = append(list, msg)
} else {
listOfMessages[msg.Author.ID] = []*discordgo.Message{msg}
}
}
if len(messages) < 100 {
break
}
beforeID = messages[99].ID
}
}
fetchedTime := time.Now()
totalXP := make(map[string]int64)
for k, v := range listOfMessages {
sort.Slice(v, func(i, j int) bool {
return v[i].ID < v[j].ID
})
prevTime, err := discordgo.SnowflakeTimestamp(v[0].ID)
if err != nil {
util.ReportError(dg, err)
return
}
for i := 1; i < len(v); i++ {
nextTime, err := discordgo.SnowflakeTimestamp(v[i].ID)
if err != nil {
util.ReportError(dg, err)
return
}
xp := int64(nextTime.Sub(prevTime).Seconds())
if xp > int64(len(v[i].Content)-3)/3 {
xp = int64(len(v[i].Content)-3) / 3
}
if xp > 10 {
xp = 10
}
if xp >= 0 {
totalXP[k] += xp
}
prevTime = nextTime
}
}
processTime := time.Now()
db.DeleteAllLevel()
for k, v := range totalXP {
userHistory := listOfMessages[k]
newestMessage := userHistory[len(userHistory)-1]
time, err := discordgo.SnowflakeTimestamp(newestMessage.ID)
if err != nil {
util.ReportError(dg, err)
return
}
db.SetXP(k, time, v)
}
finishTime := time.Now()
fmt.Printf("[RECALC] Fetch took %v.\n", fetchedTime.Sub(startTime))
fmt.Printf("[RECALC] XP calculation took %v.\n", processTime.Sub(fetchedTime))
fmt.Printf("[RECALC] DB write took %v.\n", finishTime.Sub(processTime))
fmt.Printf("[RECALC] Total took %v.\n", finishTime.Sub(startTime))
}