220 lines
5.3 KiB
Go
220 lines
5.3 KiB
Go
package features
|
|
|
|
import (
|
|
"fmt"
|
|
"log/slog"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/disgoorg/disgo/bot"
|
|
"github.com/disgoorg/disgo/discord"
|
|
"github.com/disgoorg/disgo/events"
|
|
"github.com/disgoorg/snowflake/v2"
|
|
|
|
"github.com/elisiei/unbot/db"
|
|
)
|
|
|
|
type XDTracker struct {
|
|
db *db.DB
|
|
config XDTrackerConfig
|
|
client *bot.Client
|
|
guildID snowflake.ID
|
|
}
|
|
|
|
type XDTrackerConfig struct {
|
|
Enabled bool
|
|
}
|
|
|
|
func NewXDTracker(database *db.DB, client *bot.Client, guildID snowflake.ID, enabled bool) *XDTracker {
|
|
return &XDTracker{
|
|
db: database,
|
|
client: client,
|
|
guildID: guildID,
|
|
config: XDTrackerConfig{Enabled: enabled},
|
|
}
|
|
}
|
|
|
|
func (x *XDTracker) HandleMessage(e *events.MessageCreate) {
|
|
if e.Message.Author.Bot {
|
|
return
|
|
}
|
|
|
|
msgID := e.Message.ID.String()
|
|
processed, err := x.db.IsXDMessageProcessed(msgID)
|
|
if err != nil || processed {
|
|
return
|
|
}
|
|
|
|
content := strings.ToLower(e.Message.Content)
|
|
count := strings.Count(content, "xd")
|
|
if count == 0 {
|
|
return
|
|
}
|
|
|
|
if err := x.db.IncrementXD(e.Message.Author.ID.String(), count); err != nil {
|
|
slog.Error("failed to increment xd count", slog.Any("err", err))
|
|
return
|
|
}
|
|
|
|
if err := x.db.MarkXDMessageProcessed(msgID); err != nil {
|
|
slog.Error("failed to mark message as processed", slog.Any("err", err))
|
|
}
|
|
}
|
|
|
|
func (x *XDTracker) Command() discord.SlashCommandCreate {
|
|
return discord.SlashCommandCreate{
|
|
Name: "xd",
|
|
Description: "XD tracker commands",
|
|
Options: []discord.ApplicationCommandOption{
|
|
discord.ApplicationCommandOptionSubCommand{
|
|
Name: "leaderboard",
|
|
Description: "Show XD leaderboard",
|
|
},
|
|
discord.ApplicationCommandOptionSubCommand{
|
|
Name: "stats",
|
|
Description: "Show your XD stats",
|
|
},
|
|
},
|
|
}
|
|
}
|
|
|
|
func (x *XDTracker) HandleLeaderboard(e *events.ApplicationCommandInteractionCreate) {
|
|
entries, err := x.db.GetXDLeaderboard(10)
|
|
if err != nil {
|
|
e.CreateMessage(discord.NewMessageCreate().WithContent("Failed to get leaderboard.").WithEphemeral(true))
|
|
return
|
|
}
|
|
|
|
if len(entries) == 0 {
|
|
e.CreateMessage(discord.NewMessageCreate().WithContent("No XD data yet.").WithEphemeral(true))
|
|
return
|
|
}
|
|
|
|
total, err := x.db.GetTotalXDCount()
|
|
if err != nil {
|
|
total = 0
|
|
}
|
|
|
|
userCount, err := x.db.GetXDUserCount()
|
|
if err != nil {
|
|
userCount = 0
|
|
}
|
|
|
|
var sb strings.Builder
|
|
for i, entry := range entries {
|
|
sb.WriteString(fmt.Sprintf("%d. <@%s> - **%d** xd\n", i+1, entry.UserID, entry.Count))
|
|
}
|
|
|
|
sb.WriteString(fmt.Sprintf("\nTotal: **%d** xd across **%d** users", total, userCount))
|
|
|
|
e.CreateMessage(discord.NewMessageCreate().WithEmbeds(discord.NewEmbed().WithTitle("xd leaderboard").WithDescription(sb.String())))
|
|
}
|
|
|
|
func (x *XDTracker) HandleStats(e *events.ApplicationCommandInteractionCreate) {
|
|
userID := e.User().ID.String()
|
|
count, err := x.db.GetXDCount(userID)
|
|
if err != nil {
|
|
e.CreateMessage(discord.NewMessageCreate().WithContent("Failed to get stats.").WithEphemeral(true))
|
|
return
|
|
}
|
|
|
|
total, err := x.db.GetTotalXDCount()
|
|
if err != nil {
|
|
total = 0
|
|
}
|
|
|
|
userCount, err := x.db.GetXDUserCount()
|
|
if err != nil {
|
|
userCount = 0
|
|
}
|
|
|
|
rank, err := x.db.GetUserRank(userID)
|
|
if err != nil {
|
|
rank = 0
|
|
}
|
|
|
|
var pct string
|
|
if total > 0 {
|
|
pct = fmt.Sprintf(" (%.1f%%)", float64(count)/float64(total)*100)
|
|
}
|
|
|
|
var sb strings.Builder
|
|
sb.WriteString(fmt.Sprintf("**<@%s>'s XD Stats**\n", userID))
|
|
sb.WriteString(fmt.Sprintf("Rank: **#%d** of **%d**\n", rank, userCount))
|
|
sb.WriteString(fmt.Sprintf("Messages: **%d** xd%s\n", count, pct))
|
|
sb.WriteString(fmt.Sprintf("Server total: **%d** xd", total))
|
|
|
|
e.CreateMessage(discord.NewMessageCreate().WithContent(sb.String()))
|
|
}
|
|
|
|
func (x *XDTracker) BackfillHistory() {
|
|
if !x.config.Enabled {
|
|
return
|
|
}
|
|
|
|
slog.Info("starting XD history backfill")
|
|
|
|
channels, err := x.client.Rest.GetGuildChannels(x.guildID)
|
|
if err != nil {
|
|
slog.Error("failed to get guild channels for backfill", slog.Any("err", err))
|
|
return
|
|
}
|
|
|
|
for _, ch := range channels {
|
|
switch ch.Type() {
|
|
case discord.ChannelTypeGuildText, discord.ChannelTypeGuildNews:
|
|
go x.backfillChannel(ch.ID())
|
|
}
|
|
}
|
|
|
|
slog.Info("XD history backfill complete")
|
|
}
|
|
|
|
func (x *XDTracker) backfillChannel(channelID snowflake.ID) {
|
|
var lastID snowflake.ID
|
|
total := 0
|
|
|
|
for {
|
|
messages, err := x.client.Rest.GetMessages(channelID, 0, lastID, 0, 100)
|
|
if err != nil {
|
|
slog.Error("failed to get messages for backfill", slog.String("channel_id", channelID.String()), slog.Any("err", err))
|
|
return
|
|
}
|
|
if len(messages) == 0 {
|
|
break
|
|
}
|
|
|
|
for _, msg := range messages {
|
|
if msg.Author.Bot {
|
|
continue
|
|
}
|
|
|
|
msgID := msg.ID.String()
|
|
processed, err := x.db.IsXDMessageProcessed(msgID)
|
|
if err != nil || processed {
|
|
continue
|
|
}
|
|
|
|
content := strings.ToLower(msg.Content)
|
|
if count := strings.Count(content, "xd"); count > 0 {
|
|
if err := x.db.IncrementXD(msg.Author.ID.String(), count); err != nil {
|
|
slog.Error("failed to increment xd count during backfill", slog.Any("err", err))
|
|
continue
|
|
}
|
|
}
|
|
|
|
if err := x.db.MarkXDMessageProcessed(msgID); err != nil {
|
|
slog.Error("failed to mark message as processed", slog.Any("err", err))
|
|
}
|
|
}
|
|
|
|
total += len(messages)
|
|
lastID = messages[len(messages)-1].ID
|
|
|
|
time.Sleep(200 * time.Millisecond)
|
|
}
|
|
|
|
if total > 0 {
|
|
slog.Info("backfilled channel", slog.String("channel_id", channelID.String()), slog.Int("messages", total))
|
|
}
|
|
}
|