mirror of
https://github.com/automuteus/automuteus.git
synced 2025-03-14 10:29:29 +00:00
Add functionality to download each table separately, bump utils, adjust redis keys
This commit is contained in:
@ -39,8 +39,8 @@ func UserSoftbanCountKey(userID string) string {
|
||||
return "automuteus:ratelimit:softban:count:user:" + userID
|
||||
}
|
||||
|
||||
func GuildDownloadCooldownKey(guildID string) string {
|
||||
return "automuteus:ratelimit:download:guild:" + guildID
|
||||
func GuildDownloadCategoryCooldownKey(guildID, category string) string {
|
||||
return "automuteus:ratelimit:download:guild:" + guildID + ":category:" + category
|
||||
}
|
||||
|
||||
func MarkUserRateLimit(client *redis.Client, userID, cmdType string, ttl time.Duration) {
|
||||
@ -120,15 +120,15 @@ func IsUserRateLimitedSpecific(client *redis.Client, userID string, cmdType stri
|
||||
return v == 1 // =1 means the user is present, and thus rate-limited
|
||||
}
|
||||
|
||||
func MarkGuildDownloadCooldown(client *redis.Client, guildID string) {
|
||||
err := client.Set(context.Background(), GuildDownloadCooldownKey(guildID), "", GuildDownloadCooldown).Err()
|
||||
func MarkDownloadCategoryCooldown(client *redis.Client, guildID, category string) {
|
||||
err := client.Set(context.Background(), GuildDownloadCategoryCooldownKey(guildID, category), "", GuildDownloadCooldown).Err()
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
}
|
||||
|
||||
func GetGuildDownloadCooldown(client *redis.Client, guildID string) (time.Duration, error) {
|
||||
v, err := client.TTL(context.Background(), GuildDownloadCooldownKey(guildID)).Result()
|
||||
func GetDownloadCategoryCooldown(client *redis.Client, guildID, category string) (time.Duration, error) {
|
||||
v, err := client.TTL(context.Background(), GuildDownloadCategoryCooldownKey(guildID, category)).Result()
|
||||
if err == redis.Nil {
|
||||
return 0, nil
|
||||
} else if err != nil {
|
||||
|
@ -20,19 +20,19 @@ const (
|
||||
// All is all slash commands for the bot, ordered to match the README
|
||||
var All = []*discordgo.ApplicationCommand{
|
||||
&Help,
|
||||
//&New,
|
||||
//&Refresh,
|
||||
//&Pause,
|
||||
//&End,
|
||||
//&Link,
|
||||
//&Unlink,
|
||||
//&Settings,
|
||||
//&Privacy,
|
||||
//&Info,
|
||||
//&Map,
|
||||
//&Stats,
|
||||
//&Premium,
|
||||
//&Debug,
|
||||
&New,
|
||||
&Refresh,
|
||||
&Pause,
|
||||
&End,
|
||||
&Link,
|
||||
&Unlink,
|
||||
&Settings,
|
||||
&Privacy,
|
||||
&Info,
|
||||
&Map,
|
||||
&Stats,
|
||||
&Premium,
|
||||
&Debug,
|
||||
&Download,
|
||||
}
|
||||
|
||||
|
@ -8,7 +8,11 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
Category = "category"
|
||||
Category = "category"
|
||||
Users = "users"
|
||||
UsersGames = "users_games"
|
||||
Games = "games"
|
||||
GameEvents = "game_events"
|
||||
)
|
||||
|
||||
var Download = discordgo.ApplicationCommand{
|
||||
@ -20,11 +24,26 @@ var Download = discordgo.ApplicationCommand{
|
||||
Description: "Data to download",
|
||||
Type: discordgo.ApplicationCommandOptionString,
|
||||
Choices: []*discordgo.ApplicationCommandOptionChoice{
|
||||
&discordgo.ApplicationCommandOptionChoice{
|
||||
{
|
||||
Name: Guild,
|
||||
Value: Guild,
|
||||
},
|
||||
// TODO add option to download individual user data?
|
||||
{
|
||||
Name: Users,
|
||||
Value: Users,
|
||||
},
|
||||
{
|
||||
Name: UsersGames,
|
||||
Value: UsersGames,
|
||||
},
|
||||
{
|
||||
Name: Games,
|
||||
Value: Games,
|
||||
},
|
||||
{
|
||||
Name: GameEvents,
|
||||
Value: GameEvents,
|
||||
},
|
||||
},
|
||||
Required: true,
|
||||
},
|
||||
@ -35,17 +54,21 @@ func GetDownloadParams(options []*discordgo.ApplicationCommandInteractionDataOpt
|
||||
return options[0].StringValue()
|
||||
}
|
||||
|
||||
func DownloadGuildOnCooldownResponse(sett *settings.GuildSettings, duration time.Duration) *discordgo.InteractionResponse {
|
||||
func DownloadCooldownResponse(sett *settings.GuildSettings, category string, duration time.Duration) *discordgo.InteractionResponse {
|
||||
// report with minute-level precision
|
||||
durationStr := duration.Truncate(time.Minute).String()
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: 1 << 6,
|
||||
Content: sett.LocalizeMessage(&i18n.Message{
|
||||
ID: "commands.download.guild.cooldown",
|
||||
Other: "Sorry, guild stats can only downloaded once every 24 hours!\n\n" +
|
||||
ID: "commands.download.cooldown",
|
||||
Other: "Sorry, `{{.Category}}` data can only downloaded once every 24 hours!\n\n" +
|
||||
"Please wait {{.Duration}} and then try again",
|
||||
}, map[string]interface{}{
|
||||
"Duration": duration.Truncate(time.Hour).String(),
|
||||
"Category": category,
|
||||
// strip the "0s" off the end
|
||||
"Duration": durationStr[:len(durationStr)-2],
|
||||
}),
|
||||
},
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package discord
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/automuteus/utils/pkg/storage"
|
||||
"log"
|
||||
"regexp"
|
||||
"strconv"
|
||||
@ -37,12 +38,16 @@ var VoicePermissions = []int64{
|
||||
}
|
||||
|
||||
const (
|
||||
resetUserConfirmedID = "reset-user-confirmed"
|
||||
resetUserCanceledID = "reset-user-canceled"
|
||||
resetGuildConfirmedID = "reset-guild-confirmed"
|
||||
resetGuildCanceledID = "reset-guild-canceled"
|
||||
downloadGuildConfirmedID = "download-guild-confirmed"
|
||||
downloadGuildCanceledID = "download-guild-canceled"
|
||||
resetUserConfirmedID = "reset-user-confirmed"
|
||||
resetUserCanceledID = "reset-user-canceled"
|
||||
resetGuildConfirmedID = "reset-guild-confirmed"
|
||||
resetGuildCanceledID = "reset-guild-canceled"
|
||||
downloadGuildConfirmedID = "download-guild-confirmed"
|
||||
downloadUsersConfirmedID = "download-users-confirmed"
|
||||
downloadUsersGamesConfirmedID = "download-users-games-confirmed"
|
||||
downloadGamesConfirmedID = "download-games-confirmed"
|
||||
downloadGameEventsConfirmedID = "download-game-events-confirmed"
|
||||
downloadCanceledID = "download-canceled"
|
||||
)
|
||||
|
||||
func (bot *Bot) handleInteractionCreate(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
@ -512,6 +517,9 @@ func (bot *Bot) slashCommandHandler(s *discordgo.Session, i *discordgo.Interacti
|
||||
return command.PrivateResponse(ThumbsUp)
|
||||
}
|
||||
case command.Download.Name:
|
||||
if !isAdmin {
|
||||
return command.InsufficientPermissionsResponse(sett)
|
||||
}
|
||||
// don't send the userid because downloading is restricted to Gold members
|
||||
premStatus, days, err := bot.PostgresInterface.GetGuildOrUserPremiumStatus(bot.official, bot.TopGGClient, i.GuildID, "")
|
||||
if err != nil {
|
||||
@ -527,31 +535,45 @@ func (bot *Bot) slashCommandHandler(s *discordgo.Session, i *discordgo.Interacti
|
||||
if missingPerms > 0 {
|
||||
return command.ReinviteMeResponse(missingPerms, i.ChannelID, sett)
|
||||
}
|
||||
|
||||
category := command.GetDownloadParams(i.ApplicationCommandData().Options)
|
||||
|
||||
d, err := redis_common.GetDownloadCategoryCooldown(bot.RedisInterface.client, i.GuildID, category)
|
||||
if err != nil {
|
||||
return command.PrivateErrorResponse("/download guild", err, sett)
|
||||
}
|
||||
if d > 0 {
|
||||
return command.DownloadCooldownResponse(sett, category, d)
|
||||
}
|
||||
var content string
|
||||
var components []discordgo.MessageComponent
|
||||
content = sett.LocalizeMessage(&i18n.Message{
|
||||
ID: "commands.download.guild.confirmation",
|
||||
Other: "⚠️**Are you sure?**⚠️\nIf you download the `{{.Category}}` data now, it will not be downloadable again for 24 hours!",
|
||||
}, map[string]interface{}{
|
||||
"Category": category,
|
||||
})
|
||||
var downloadConfirmedID string
|
||||
switch category {
|
||||
case command.Guild:
|
||||
d, err := redis_common.GetGuildDownloadCooldown(bot.RedisInterface.client, i.GuildID)
|
||||
if err != nil {
|
||||
return command.PrivateErrorResponse("/download guild", err, sett)
|
||||
}
|
||||
if d > 0 {
|
||||
return command.DownloadGuildOnCooldownResponse(sett, d)
|
||||
}
|
||||
var content string
|
||||
var components []discordgo.MessageComponent
|
||||
content = sett.LocalizeMessage(&i18n.Message{
|
||||
ID: "commands.download.guild.confirmation",
|
||||
Other: "⚠️**Are you sure?**⚠️\nIf you download the guild's stats now, they will not be downloadable again for 24 hours",
|
||||
})
|
||||
components = confirmationComponents(downloadGuildConfirmedID, downloadGuildCanceledID, sett)
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: 1 << 6, //private message
|
||||
Content: content,
|
||||
Components: components,
|
||||
},
|
||||
}
|
||||
downloadConfirmedID = downloadGuildConfirmedID
|
||||
case command.Users:
|
||||
downloadConfirmedID = downloadUsersConfirmedID
|
||||
case command.UsersGames:
|
||||
downloadConfirmedID = downloadUsersGamesConfirmedID
|
||||
case command.Games:
|
||||
downloadConfirmedID = downloadGamesConfirmedID
|
||||
case command.GameEvents:
|
||||
downloadConfirmedID = downloadGameEventsConfirmedID
|
||||
}
|
||||
components = confirmationComponents(downloadConfirmedID, downloadCanceledID, sett)
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: 1 << 6, //private message
|
||||
Content: content,
|
||||
Components: components,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
@ -562,6 +584,11 @@ func (bot *Bot) slashCommandHandler(s *discordgo.Session, i *discordgo.Interacti
|
||||
}
|
||||
redis_common.MarkUserRateLimit(bot.RedisInterface.client, i.Member.User.ID, i.MessageComponentData().CustomID, redis_common.GlobalUserRateLimitDuration)
|
||||
|
||||
gid, err := strconv.ParseUint(i.GuildID, 10, 64)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
// TODO report this properly
|
||||
}
|
||||
switch i.MessageComponentData().CustomID {
|
||||
case colorSelectID:
|
||||
if len(i.MessageComponentData().Values) > 0 {
|
||||
@ -662,46 +689,137 @@ func (bot *Bot) slashCommandHandler(s *discordgo.Session, i *discordgo.Interacti
|
||||
},
|
||||
}
|
||||
case downloadGuildConfirmedID:
|
||||
// TODO fetch the basic information about the guild (to populate the guild table)
|
||||
|
||||
gid, err := strconv.ParseUint(i.GuildID, 10, 64)
|
||||
guild, err := bot.PostgresInterface.GetGuildForDownload(gid)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
// TODO report this properly
|
||||
log.Println("Error downloading guild data:", err)
|
||||
return downloadErrorResponse(sett, err)
|
||||
} else {
|
||||
games, err := bot.PostgresInterface.GetGamesForGuild(gid)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
log.Println("Games played", len(games))
|
||||
events, err := bot.PostgresInterface.GetGamesEventsForGuild(gid)
|
||||
if err != nil {
|
||||
log.Println(err)
|
||||
}
|
||||
log.Println("Events", events)
|
||||
// TODO get the relevant users as well? To view opt info?
|
||||
|
||||
// TODO prepend the relevant headers to each csv
|
||||
|
||||
// redis_common.MarkGuildDownloadCooldown(bot.RedisInterface.client, i.GuildID)
|
||||
|
||||
redis_common.MarkDownloadCategoryCooldown(bot.RedisInterface.client, i.GuildID, command.Guild)
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: 1 << 6, //private message
|
||||
Content: "Here are your files!",
|
||||
Flags: 1 << 6, //private message
|
||||
Content: sett.LocalizeMessage(&i18n.Message{
|
||||
ID: "commands.download.file.success",
|
||||
Other: "Here's that file for you!",
|
||||
}),
|
||||
Components: []discordgo.MessageComponent{},
|
||||
Files: []*discordgo.File{
|
||||
&discordgo.File{
|
||||
Name: "test.csv",
|
||||
{
|
||||
Name: "guilds.csv",
|
||||
ContentType: "text/csv",
|
||||
Reader: strings.NewReader("testfile"),
|
||||
Reader: strings.NewReader(guild.ToCSV()),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
case downloadGuildCanceledID:
|
||||
|
||||
case downloadUsersConfirmedID:
|
||||
users, err := bot.PostgresInterface.GetUsersForGuild(gid)
|
||||
if err != nil {
|
||||
log.Println("Error downloading users data:", err)
|
||||
return downloadErrorResponse(sett, err)
|
||||
} else {
|
||||
redis_common.MarkDownloadCategoryCooldown(bot.RedisInterface.client, i.GuildID, command.Users)
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: 1 << 6, //private message
|
||||
Content: sett.LocalizeMessage(&i18n.Message{
|
||||
ID: "commands.download.file.success",
|
||||
Other: "Here's that file for you!",
|
||||
}),
|
||||
Components: []discordgo.MessageComponent{},
|
||||
Files: []*discordgo.File{
|
||||
{
|
||||
Name: "users.csv",
|
||||
ContentType: "text/csv",
|
||||
Reader: strings.NewReader(storage.UsersToCSV(users)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
case downloadUsersGamesConfirmedID:
|
||||
usersGames, err := bot.PostgresInterface.GetUsersGamesForGuild(gid)
|
||||
if err != nil {
|
||||
log.Println("Error downloading users_games data:", err)
|
||||
return downloadErrorResponse(sett, err)
|
||||
} else {
|
||||
redis_common.MarkDownloadCategoryCooldown(bot.RedisInterface.client, i.GuildID, command.UsersGames)
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: 1 << 6, //private message
|
||||
Content: sett.LocalizeMessage(&i18n.Message{
|
||||
ID: "commands.download.file.success",
|
||||
Other: "Here's that file for you!",
|
||||
}),
|
||||
Components: []discordgo.MessageComponent{},
|
||||
Files: []*discordgo.File{
|
||||
{
|
||||
Name: "users_games.csv",
|
||||
ContentType: "text/csv",
|
||||
Reader: strings.NewReader(storage.UsersGamesToCSV(usersGames)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
case downloadGamesConfirmedID:
|
||||
games, err := bot.PostgresInterface.GetGamesForGuild(gid)
|
||||
if err != nil {
|
||||
log.Println("Error downloading game data:", err)
|
||||
return downloadErrorResponse(sett, err)
|
||||
} else {
|
||||
redis_common.MarkDownloadCategoryCooldown(bot.RedisInterface.client, i.GuildID, command.Games)
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: 1 << 6, //private message
|
||||
Content: sett.LocalizeMessage(&i18n.Message{
|
||||
ID: "commands.download.file.success",
|
||||
Other: "Here's that file for you!",
|
||||
}),
|
||||
Components: []discordgo.MessageComponent{},
|
||||
Files: []*discordgo.File{
|
||||
{
|
||||
Name: "games.csv",
|
||||
ContentType: "text/csv",
|
||||
Reader: strings.NewReader(storage.GamesToCSV(games)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
case downloadGameEventsConfirmedID:
|
||||
events, err := bot.PostgresInterface.GetGamesEventsForGuild(gid)
|
||||
if err != nil {
|
||||
log.Println("Error downloading game events data:", err)
|
||||
return downloadErrorResponse(sett, err)
|
||||
} else {
|
||||
redis_common.MarkDownloadCategoryCooldown(bot.RedisInterface.client, i.GuildID, command.GameEvents)
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: 1 << 6, //private message
|
||||
Content: sett.LocalizeMessage(&i18n.Message{
|
||||
ID: "commands.download.file.success",
|
||||
Other: "Here's that file for you!",
|
||||
}),
|
||||
Components: []discordgo.MessageComponent{},
|
||||
Files: []*discordgo.File{
|
||||
{
|
||||
Name: "events.csv",
|
||||
ContentType: "text/csv",
|
||||
Reader: strings.NewReader(storage.EventsToCSV(events)),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
case downloadCanceledID:
|
||||
fallthrough
|
||||
case resetUserCanceledID:
|
||||
fallthrough
|
||||
@ -717,6 +835,21 @@ func (bot *Bot) slashCommandHandler(s *discordgo.Session, i *discordgo.Interacti
|
||||
return nil
|
||||
}
|
||||
|
||||
func downloadErrorResponse(sett *settings.GuildSettings, err error) *discordgo.InteractionResponse {
|
||||
return &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Flags: 1 << 6, //private message
|
||||
Content: sett.LocalizeMessage(&i18n.Message{
|
||||
ID: "commands.download.guild.error",
|
||||
Other: "I encountered an error fetching your stats for download: {{.Error}}",
|
||||
}, map[string]interface{}{
|
||||
"Error": err.Error(),
|
||||
}),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
func (bot *Bot) linkOrUnlinkAndRespond(dgs *GameState, userID, testValue string, sett *settings.GuildSettings) (*discordgo.InteractionResponse, bool) {
|
||||
if testValue != "" {
|
||||
// don't care if it's successful, just always unlink before linking
|
||||
|
6
go.mod
6
go.mod
@ -3,7 +3,7 @@ module github.com/automuteus/automuteus
|
||||
go 1.18
|
||||
|
||||
require (
|
||||
github.com/automuteus/utils v0.3.2
|
||||
github.com/automuteus/utils v0.4.1
|
||||
github.com/bsm/redislock v0.7.1
|
||||
github.com/bwmarrin/discordgo v0.24.0
|
||||
github.com/go-redis/redis/v8 v8.8.0
|
||||
@ -50,7 +50,3 @@ require (
|
||||
gopkg.in/yaml.v2 v2.4.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c // indirect
|
||||
)
|
||||
|
||||
replace (
|
||||
github.com/automuteus/utils v0.3.2 => ../utils
|
||||
)
|
||||
|
4
go.sum
4
go.sum
@ -23,8 +23,8 @@ github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e/go.mod h1:3U/XgcO3hC
|
||||
github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da/go.mod h1:Q73ZrmVTwzkszR9V5SSuryQ31EELlFMUz1kKyl939pY=
|
||||
github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310/go.mod h1:ufUuZ+zHj4x4TnLV4JWEpy2hxWSpsRywHrMgIH9cCH8=
|
||||
github.com/aryann/difflib v0.0.0-20170710044230-e206f873d14a/go.mod h1:DAHtR1m6lCRdSC2Tm3DSWRPvIPr6xNKyeHdqDQSQT+A=
|
||||
github.com/automuteus/utils v0.3.2 h1:+r598HkMEYXmz9RjDSRamVgObxVJE9EayhSf7XdBulc=
|
||||
github.com/automuteus/utils v0.3.2/go.mod h1:A8zWmP3FEG1Aanz/9msstVMswIfkx1nxY59gNfRztXw=
|
||||
github.com/automuteus/utils v0.4.1 h1:wrEqLaQa0xZ8Ka4J6eXGf0OIx84DgwD3XF4AkSnP6lo=
|
||||
github.com/automuteus/utils v0.4.1/go.mod h1:A8zWmP3FEG1Aanz/9msstVMswIfkx1nxY59gNfRztXw=
|
||||
github.com/aws/aws-lambda-go v1.13.3/go.mod h1:4UKl9IzQMoD+QF79YdCuzCwp8VbmG4VAQwij/eHl5CU=
|
||||
github.com/aws/aws-sdk-go v1.27.0/go.mod h1:KmX6BPdI08NWTb3/sm4ZGu5ShLoqVDhKgpiN924inxo=
|
||||
github.com/aws/aws-sdk-go-v2 v0.18.0/go.mod h1:JWVYvqSMppoMJC0x5wdwiImzgXTI9FuZwxzkQq9wy+g=
|
||||
|
Reference in New Issue
Block a user