Added autorefresh/game summary message settings, more stats queries

This commit is contained in:
denverquane
2020-11-27 18:39:32 -07:00
parent caf15dccc7
commit 2f9fab5c71
17 changed files with 612 additions and 190 deletions

View File

@ -10,5 +10,8 @@ Dockerfile
LICENSE
renovate.json
TODO.org
LOCALIZATION.md
PRIVACY.md
README.md
*.md

View File

@ -1,18 +0,0 @@
#builds:
# - goos:
# - windows
# - linux
# goarch:
# - "386"
# - arm
# ignore:
# - goos: windows
# goarch: arm
# mod_timestamp: "{{ .CommitTimestamp }}"
#archives:
# - format: binary
# replacements:
# "386": 32bit
# "arm": arm_raspberrypi
#checksum:
# disable: true

View File

@ -10,14 +10,14 @@ General Data Protection Regulation](https://eur-lex.europa.eu/legal-content/EN/T
3. Users can opt-out of data collection at any time if they don't wish for AutoMuteUs to gather this data. (`.au priv optout`)
# What Data does AutoMuteUs collect?
AutoMuteUs collects a very small amount of user information for statistics. Your Discord UserID is the only
Personally-Identifiable Information (PII) that the bot requires to gather statistics. All other data collected by AutoMuteUs
is non-identifiable in-game data, such as in-game player names, player color, etc. An example of a game data record recorded
AutoMuteUs collects a very small amount of user information for statistics. Your Discord UserID, and any in-game names you have used
are the only Personally-Identifiable Information (PII) that the bot requires to gather statistics. All other data collected by AutoMuteUs
is non-identifiable in-game data, such as player color, crewmate/imposter role, etc. An example of a game data record recorded
by AutoMuteUs is shown below:
```
{
"color": 11,
"name": "mouse",
"name": "Soup",
"isAlive": true
}
```
@ -26,4 +26,6 @@ AutoMuteUs uses a mapping of Discord UserIDs to arbitrary numerical IDs, which a
choose to delete the data that AutoMuteUs stores about you (with `.au privacy optout`), the mapping to your User ID is removed,
and the full history of your past games is deleted. Because of this, re-opting into data collection with AutoMuteUs (`.au privacy optin`) means
your past games and game events **are not recoverable**. Please carefully consider this before opting out, if you plan to
view your game statistics at any point in the future!
view your game statistics at any point in the future!
Questions and concerns about your Data Collection and Privacy can be addressed to gdpr@automute.us

View File

@ -1,4 +0,0 @@
TODO private channel for posting round transitions/debug info
TODO @the bot if you forget command prefix or the like
TODO add emoji reactions to the help message to also work as commands
TODO include docker compose file and sample env

View File

@ -192,14 +192,6 @@ func (bot *Bot) newGuild(emojiGuildID string) func(s *discordgo.Session, m *disc
log.Printf("Added to new Guild, id %s, name %s", m.Guild.ID, m.Guild.Name)
bot.RedisInterface.AddUniqueGuildCounter(m.Guild.ID, Version)
//f, err := os.Create(path.Join(bot.logPath, m.Guild.ID+"_log.txt"))
//w := io.MultiWriter(os.Stdout)
//if err != nil {
// log.Println("Couldn't create logger for " + m.Guild.ID + "; only using stdout for logging")
//} else {
// w = io.MultiWriter(f, os.Stdout)
//}
if emojiGuildID == "" {
log.Println("[This is not an error] No explicit guildID provided for emojis; using the current guild default")
emojiGuildID = m.Guild.ID

View File

@ -10,7 +10,6 @@ import (
"strings"
"github.com/bwmarrin/discordgo"
"github.com/denverquane/amongusdiscord/game"
"github.com/denverquane/amongusdiscord/storage"
"github.com/nicksnyder/go-i18n/v2/i18n"
)
@ -490,9 +489,6 @@ var AllCommands = []Command{
//TODO cache/preconstruct these (no reason to make them fresh everytime help is called, except for the prefix...)
func ConstructEmbedForCommand(prefix string, cmd Command, sett *storage.GuildSettings) *discordgo.MessageEmbed {
if cmd.cmdType == Settings {
return settingResponse(prefix, AllSettings, sett)
}
return &discordgo.MessageEmbed{
URL: "",
Type: "",
@ -718,30 +714,31 @@ func (bot *Bot) HandleCommand(isAdmin, isPermissioned bool, sett *storage.GuildS
bot.applyToAll(dgs, false, false)
break
case Force:
if len(args[1:]) == 0 {
embed := ConstructEmbedForCommand(prefix, cmd, sett)
s.ChannelMessageSendEmbed(m.ChannelID, embed)
} else {
phase := getPhaseFromString(args[1])
if phase == game.UNINITIALIZED {
s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
ID: "commands.HandleCommand.Force.UNINITIALIZED",
Other: "Sorry, I didn't understand the game phase you tried to force",
}))
} else {
//TODO fix
//dgs := bot.RedisInterface.GetReadOnlyDiscordGameState(gsr)
//if dgs.ConnectCode != "" {
// i := strconv.FormatInt(int64(phase), 10)
// bot.RedisInterface.PublishPhaseUpdate(dgs.ConnectCode, i)
//}
}
}
break
//case Force:
// if len(args[1:]) == 0 {
// embed := ConstructEmbedForCommand(prefix, cmd, sett)
// s.ChannelMessageSendEmbed(m.ChannelID, embed)
// } else {
// phase := getPhaseFromString(args[1])
// if phase == game.UNINITIALIZED {
// s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
// ID: "commands.HandleCommand.Force.UNINITIALIZED",
// Other: "Sorry, I didn't understand the game phase you tried to force",
// }))
// } else {
// //TODO fix
// //dgs := bot.RedisInterface.GetReadOnlyDiscordGameState(gsr)
// //if dgs.ConnectCode != "" {
// // i := strconv.FormatInt(int64(phase), 10)
// // bot.RedisInterface.PublishPhaseUpdate(dgs.ConnectCode, i)
// //}
// }
// }
// break
case Settings:
bot.HandleSettingsCommand(s, m, sett, args)
premStatus := bot.PostgresInterface.GetGuildPremiumStatus(m.GuildID)
bot.HandleSettingsCommand(s, m, sett, args, premStatus != 0)
break
case Log:

View File

@ -176,12 +176,17 @@ func (bot *Bot) SubscribeToGameByConnectCode(guildID, connectCode string, endGam
} else if err == nil {
bot.MetricsCollector.RecordDiscordRequests(bot.RedisInterface.client, metrics.MessageCreateDelete, 1)
}
}
go dumpGameToPostgres(*dgs, bot.PostgresInterface, gameOverResult)
dgs.MatchID = -1
dgs.MatchStartUnix = -1
bot.RedisInterface.SetDiscordGameState(dgs, lock)
bot.RefreshGameStateMessage(dgsRequest, sett)
//in this context, only refresh the game message automatically
if sett.AutoRefresh {
bot.RefreshGameStateMessage(dgsRequest, sett)
}
}
}
if job.JobType != broker.Connection {

View File

@ -75,7 +75,7 @@ func helpResponse(isAdmin, isPermissioned bool, CommandPrefix string, commands [
return embed
}
func settingResponse(CommandPrefix string, settings []Setting, sett *storage.GuildSettings) *discordgo.MessageEmbed {
func settingResponse(CommandPrefix string, settings []Setting, sett *storage.GuildSettings, prem bool) *discordgo.MessageEmbed {
embed := discordgo.MessageEmbed{
URL: "",
Type: "",
@ -99,12 +99,18 @@ func settingResponse(CommandPrefix string, settings []Setting, sett *storage.Gui
Author: nil,
}
fields := make([]*discordgo.MessageEmbedField, len(settings))
for i, v := range settings {
fields[i] = &discordgo.MessageEmbedField{
Name: v.name,
Value: sett.LocalizeMessage(v.shortDesc),
Inline: true,
fields := make([]*discordgo.MessageEmbedField, 0)
for _, v := range settings {
if !v.premium || v.premium == prem {
name := v.name
if v.premium {
name = "💎 " + name
}
fields = append(fields, &discordgo.MessageEmbedField{
Name: name,
Value: sett.LocalizeMessage(v.shortDesc),
Inline: true,
})
}
}
@ -316,11 +322,6 @@ func menuMessage(dgs *DiscordGameState, emojis AlivenessEmojis, sett *storage.Gu
}
func lobbyMessage(dgs *DiscordGameState, emojis AlivenessEmojis, sett *storage.GuildSettings) *discordgo.MessageEmbed {
//gameInfoFields[2] = &discordgo.MessageEmbedField{
// Name: "\u200B",
// Value: "\u200B",
// Inline: false,
//}
room, region := dgs.AmongUsData.GetRoomRegion()
gameInfoFields := lobbyMetaEmbedFields(room, region, dgs.AmongUsData.GetNumDetectedPlayers(), dgs.GetCountLinked(), sett)
@ -381,7 +382,6 @@ func gamePlayMessage(dgs *DiscordGameState, emojis AlivenessEmojis, sett *storag
desc := ""
//if game is over, dont append the player count or the other description fields
//TODO include some sort of game summary for gameover
if phase != game.GAMEOVER {
desc = dgs.makeDescription(sett)
gameInfoFields := lobbyMetaEmbedFields("", "", dgs.AmongUsData.GetNumDetectedPlayers(), dgs.GetCountLinked(), sett)
@ -458,18 +458,31 @@ func premiumEmbedResponse(tier storage.PremiumTier, sett *storage.GuildSettings)
if tier != storage.FreeTier {
desc = sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.PremiumDescription",
Other: "Looks like you have AutoMuteUs **{{.Tier}}**! Thanks for the support!\n\nBelow are commands you can invoke with `{{.CommandPrefix}} premium <command>`",
Other: "Looks like you have AutoMuteUs **{{.Tier}}**! Thanks for the support!\n\nBelow are some of the benefits you can customize with your Premium status!",
},
map[string]interface{}{
"Tier": storage.PremiumTierStrings[tier],
"CommandPrefix": sett.GetCommandPrefix(),
"Tier": storage.PremiumTierStrings[tier],
})
//TODO localize
fields = []*discordgo.MessageEmbedField{
{
Name: "invites",
Value: "View a list of Premium bots you can invite",
Name: "Bot Invites",
Value: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.Invites",
Other: "View a list of Premium bots you can invite with `{{.CommandPrefix}} premium invites`!",
}, map[string]interface{}{
"CommandPrefix": sett.GetCommandPrefix(),
}),
Inline: false,
},
{
Name: "Premium Settings",
Value: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.Settings",
Other: "Look for the settings marked with 💎 under `{{.CommandPrefix}} settings!`",
}, map[string]interface{}{
"CommandPrefix": sett.GetCommandPrefix(),
}),
Inline: false,
},
}
@ -481,23 +494,58 @@ func premiumEmbedResponse(tier storage.PremiumTier, sett *storage.GuildSettings)
//TODO localize
fields = []*discordgo.MessageEmbedField{
{
Name: "🙊 Fast Mute/Deafen",
Value: "Premium users get access to \"helper\" bots that make sure muting is fast!",
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.PriorityGameAccess",
Other: "👑 Priority Game Access",
}),
Value: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.PriorityGameAccessDesc",
Other: "If the Bot is under heavy load, Premium users will always be able to make new games!",
}),
Inline: false,
},
{
Name: "📊 Game Stats and Leaderboards",
Value: "Premium users have access to a full suite of player stats and leaderboards!",
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.FastMute",
Other: "🙊 Fast Mute/Deafen",
}),
Value: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.FastMuteDesc",
Other: "Premium users get access to \"helper\" bots that make sure muting is fast!",
}),
Inline: false,
},
{
Name: "👑 Priority Game Access",
Value: "If the Bot is under heavy load, Premium users will always be able to make new games!",
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.Stats",
Other: "📊 Game Stats and Leaderboards",
}),
Value: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.StatsDesc",
Other: "Premium users have access to a full suite of player stats and leaderboards!",
}),
Inline: false,
},
{
Name: "👂 Premium Support",
Value: "Premium users get access to private channels on our official Discord channel!",
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.Settings",
Other: "🛠 Special Settings",
}),
Value: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.SettingsDesc",
Other: "Premium users can specify additional settings, like displaying an end-game status message, or auto-refreshing the status message!",
}),
Inline: false,
},
{
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.Support",
Other: "👂 Premium Support",
}),
Value: sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumResponse.SupportDesc",
Other: "Premium users get access to private channels on our official Discord channel!",
}),
Inline: false,
},
}
@ -524,6 +572,15 @@ func premiumEmbedResponse(tier storage.PremiumTier, sett *storage.GuildSettings)
return &msg
}
func nonPremiumSettingResponse(sett *storage.GuildSettings) string {
return sett.LocalizeMessage(&i18n.Message{
ID: "responses.nonPremiumSetting.Desc",
Other: "Sorry, but that setting is reserved for AutoMuteUs Premium users! See `{{.CommandPrefix}} premium` for details",
}, map[string]interface{}{
"CommandPrefix": sett.GetCommandPrefix(),
})
}
func premiumInvitesEmbed(tier storage.PremiumTier, sett *storage.GuildSettings) *discordgo.MessageEmbed {
desc := ""
fields := []*discordgo.MessageEmbedField{}
@ -540,9 +597,10 @@ func premiumInvitesEmbed(tier storage.PremiumTier, sett *storage.GuildSettings)
count := 0
if tier == storage.SilverTier {
count = 1
} else if tier == storage.GoldTier {
} else if tier == storage.GoldTier || tier == storage.PlatTier {
count = 3
}
//TODO account for Platinum
desc = sett.LocalizeMessage(&i18n.Message{
ID: "responses.premiumInviteResponse.desc",
Other: "{{.Tier}} users have access to {{.Count}} Priority mute bots: invites provided below!",

View File

@ -26,6 +26,7 @@ const (
Delays
VoiceRules
MatchSummary
AutoRefresh
Show
Reset
NullSetting
@ -39,6 +40,7 @@ type Setting struct {
desc *i18n.Message
args *i18n.Message
aliases []string
premium bool
}
var AllSettings = []Setting{
@ -48,7 +50,7 @@ var AllSettings = []Setting{
example: "commandPrefix !",
shortDesc: &i18n.Message{
ID: "settings.AllSettings.Prefix.shortDesc",
Other: "Bot prefix",
Other: "Bot Prefix",
},
desc: &i18n.Message{
ID: "settings.AllSettings.Prefix.desc",
@ -59,6 +61,7 @@ var AllSettings = []Setting{
Other: "<prefix>",
},
aliases: []string{"prefix", "cp"},
premium: false,
},
{
settingType: Language,
@ -66,7 +69,7 @@ var AllSettings = []Setting{
example: "language ru",
shortDesc: &i18n.Message{
ID: "settings.AllSettings.Language.shortDesc",
Other: "Bot language",
Other: "Bot Language",
},
desc: &i18n.Message{
ID: "settings.AllSettings.Language.desc",
@ -77,6 +80,7 @@ var AllSettings = []Setting{
Other: "<language> or reload",
},
aliases: []string{"lang", "l"},
premium: false,
},
{
settingType: AdminUserIDs,
@ -95,6 +99,7 @@ var AllSettings = []Setting{
Other: "<User @ mentions>...",
},
aliases: []string{"admins", "admin", "auid", "aui", "a"},
premium: false,
},
{
settingType: RoleIDs,
@ -113,6 +118,7 @@ var AllSettings = []Setting{
Other: "<role @ mentions>...",
},
aliases: []string{"operators", "op", "oproles", "roles", "role"},
premium: false,
},
{
settingType: UnmuteDead,
@ -120,7 +126,7 @@ var AllSettings = []Setting{
example: "unmuteDeadDuringTasks false",
shortDesc: &i18n.Message{
ID: "settings.AllSettings.UnmuteDead.shortDesc",
Other: "Bot unmutes players on death",
Other: "Bot Unmutes Deaths",
},
desc: &i18n.Message{
ID: "settings.AllSettings.UnmuteDead.desc",
@ -131,6 +137,7 @@ var AllSettings = []Setting{
Other: "<true/false>",
},
aliases: []string{"unmute", "uddt"},
premium: false,
},
{
settingType: Delays,
@ -138,7 +145,7 @@ var AllSettings = []Setting{
example: "delays lobby tasks 5",
shortDesc: &i18n.Message{
ID: "settings.AllSettings.Delays.shortDesc",
Other: "Delays between stages",
Other: "Delays Between Stages",
},
desc: &i18n.Message{
ID: "settings.AllSettings.Delays.desc",
@ -149,6 +156,7 @@ var AllSettings = []Setting{
Other: "<start phase> <end phase> <delay>",
},
aliases: []string{"delays", "d"},
premium: false,
},
{
settingType: VoiceRules,
@ -156,7 +164,7 @@ var AllSettings = []Setting{
example: "voiceRules mute tasks dead true",
shortDesc: &i18n.Message{
ID: "settings.AllSettings.VoiceRules.shortDesc",
Other: "Mute/deafen rules",
Other: "Mute/deafen Rules",
},
desc: &i18n.Message{
ID: "settings.AllSettings.VoiceRules.desc",
@ -167,6 +175,7 @@ var AllSettings = []Setting{
Other: "<mute/deaf> <game phase> <dead/alive> <true/false>",
},
aliases: []string{"voice", "vr"},
premium: false,
},
{
settingType: MatchSummary,
@ -174,7 +183,7 @@ var AllSettings = []Setting{
example: "matchSummary 5",
shortDesc: &i18n.Message{
ID: "settings.AllSettings.MatchSummary.shortDesc",
Other: "Match summary deletion",
Other: "Match Summary Message",
},
desc: &i18n.Message{
ID: "settings.AllSettings.MatchSummary.desc",
@ -185,6 +194,26 @@ var AllSettings = []Setting{
Other: "<minutes>",
},
aliases: []string{"matchsum", "summary", "match"},
premium: true,
},
{
settingType: AutoRefresh,
name: "autoRefresh",
example: "autoRefresh true",
shortDesc: &i18n.Message{
ID: "settings.AllSettings.AutoRefresh.shortDesc",
Other: "Autorefresh Status Message",
},
desc: &i18n.Message{
ID: "settings.AllSettings.AutoRefresh.desc",
Other: "Specify if the bot should auto-refresh the status message after a match ends",
},
args: &i18n.Message{
ID: "settings.AllSettings.AutoRefresh.args",
Other: "<true/false>",
},
aliases: []string{"refresh", "auto"},
premium: true,
},
{
settingType: Show,
@ -203,6 +232,7 @@ var AllSettings = []Setting{
Other: "None",
},
aliases: []string{"sh", "s"},
premium: false,
},
{
settingType: Reset,
@ -221,10 +251,15 @@ var AllSettings = []Setting{
Other: "None",
},
aliases: []string{},
premium: false,
},
}
func ConstructEmbedForSetting(value string, setting Setting, sett *storage.GuildSettings) discordgo.MessageEmbed {
title := setting.name
if setting.premium {
title = "💎 " + title
}
return discordgo.MessageEmbed{
URL: "",
Type: "",
@ -289,9 +324,9 @@ func getSetting(arg string) SettingType {
return NullSetting
}
func (bot *Bot) HandleSettingsCommand(s *discordgo.Session, m *discordgo.MessageCreate, sett *storage.GuildSettings, args []string) {
func (bot *Bot) HandleSettingsCommand(s *discordgo.Session, m *discordgo.MessageCreate, sett *storage.GuildSettings, args []string, prem bool) {
if len(args) == 1 {
s.ChannelMessageSendEmbed(m.ChannelID, settingResponse(sett.CommandPrefix, AllSettings, sett))
s.ChannelMessageSendEmbed(m.ChannelID, settingResponse(sett.CommandPrefix, AllSettings, sett, prem))
return
}
// if command invalid, no need to reapply changes to json file
@ -321,8 +356,19 @@ func (bot *Bot) HandleSettingsCommand(s *discordgo.Session, m *discordgo.Message
isValid = SettingVoiceRules(s, m, sett, args)
break
case MatchSummary:
if !prem {
s.ChannelMessageSend(m.ChannelID, nonPremiumSettingResponse(sett))
break
}
isValid = SettingMatchSummary(s, m, sett, args)
break
case AutoRefresh:
if !prem {
s.ChannelMessageSend(m.ChannelID, nonPremiumSettingResponse(sett))
break
}
isValid = SettingAutoRefresh(s, m, sett, args)
break
case Show:
jBytes, err := json.MarshalIndent(sett, "", " ")
if err != nil {
@ -635,58 +681,6 @@ func SettingPermissionRoleIDs(s *discordgo.Session, m *discordgo.MessageCreate,
return true
}
//func SettingApplyNicknames(s *discordgo.Session, m *discordgo.MessageCreate, sett *storage.GuildSettings, args []string) bool {
// applyNicknames := sett.GetApplyNicknames()
// if len(args) == 2 {
// current := "false"
// if applyNicknames {
// current = "true"
// }
// embed := ConstructEmbedForSetting(current, AllSettings[Nicknames], sett)
// s.ChannelMessageSendEmbed(m.ChannelID, &embed)
// return false
// }
//
// if args[2] == "true" {
// if applyNicknames {
// s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
// ID: "settings.SettingApplyNicknames.true_applyNicknames",
// Other: "It's already true!",
// }))
// } else {
// s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
// ID: "settings.SettingApplyNicknames.true_noApplyNicknames",
// Other: "I will now rename the players in the voice chat.",
// }))
// sett.SetApplyNicknames(true)
// return true
// }
// } else if args[2] == "false" {
// if applyNicknames {
// s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
// ID: "settings.SettingApplyNicknames.false_applyNicknames",
// Other: "I will no longer rename the players in the voice chat.",
// }))
// sett.SetApplyNicknames(false)
// return true
// } else {
// s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
// ID: "settings.SettingApplyNicknames.false_noApplyNicknames",
// Other: "It's already false!",
// }))
// }
// } else {
// s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
// ID: "settings.SettingApplyNicknames.wrongArg",
// Other: "Sorry, `{{.Arg}}` is neither `true` nor `false`.",
// },
// map[string]interface{}{
// "Arg": args[2],
// }))
// }
// return false
//}
func SettingUnmuteDeadDuringTasks(s *discordgo.Session, m *discordgo.MessageCreate, sett *storage.GuildSettings, args []string) bool {
unmuteDead := sett.GetUnmuteDeadDuringTasks()
if len(args) == 2 {
@ -1030,3 +1024,40 @@ func SettingMatchSummary(s *discordgo.Session, m *discordgo.MessageCreate, sett
return true
}
func SettingAutoRefresh(s *discordgo.Session, m *discordgo.MessageCreate, sett *storage.GuildSettings, args []string) bool {
if len(args) == 2 {
embed := ConstructEmbedForSetting(fmt.Sprintf("%v", sett.GetAutoRefresh()), AllSettings[AutoRefresh], sett)
s.ChannelMessageSendEmbed(m.ChannelID, &embed)
return false
}
val := args[2]
if val != "t" && val != "true" && val != "f" && val != "false" {
s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
ID: "settings.SettingAutoRefresh.Unrecognized",
Other: "{{.Arg}} is not a true/false value. See `{{.CommandPrefix}} settings autorefresh` for usage",
},
map[string]interface{}{
"Arg": val,
"CommandPrefix": sett.CommandPrefix,
}))
return false
}
newSet := val == "t" || val == "true"
sett.SetAutoRefresh(newSet)
if newSet {
s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
ID: "settings.SettingAutoRefresh.True",
Other: "From now on, I'll AutoRefresh the game status message",
}))
} else {
s.ChannelMessageSend(m.ChannelID, sett.LocalizeMessage(&i18n.Message{
ID: "settings.SettingAutoRefresh.False",
Other: "From now on, I will not AutoRefresh the game status message",
}))
}
return true
}

View File

@ -8,8 +8,11 @@ import (
"github.com/denverquane/amongusdiscord/storage"
"github.com/nicksnyder/go-i18n/v2/i18n"
"log"
"strconv"
)
const UserLeaderboardCount = 3
func (bot *Bot) UserStatsEmbed(userID, guildID string, sett *storage.GuildSettings, premium storage.PremiumTier) *discordgo.MessageEmbed {
gamesPlayed := bot.PostgresInterface.NumGamesPlayedByUser(userID)
gamesPlayedServer := bot.PostgresInterface.NumGamesPlayedByUserOnServer(userID, guildID)
@ -23,24 +26,16 @@ func (bot *Bot) UserStatsEmbed(userID, guildID string, sett *storage.GuildSettin
avatarUrl = mem.User.AvatarURL("")
}
fields := make([]*discordgo.MessageEmbedField, 3)
fields := make([]*discordgo.MessageEmbedField, 2)
fields[0] = &discordgo.MessageEmbedField{
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.userStatsEmbed.GamesPlayed",
Other: "Total Games Played",
}),
Value: fmt.Sprintf("%d", gamesPlayed),
Inline: true,
}
fields[1] = &discordgo.MessageEmbedField{
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.userStatsEmbed.GamesPlayedOnServer",
Other: "On This Server",
Other: "Games Played",
}),
Value: fmt.Sprintf("%d", gamesPlayedServer),
Inline: true,
}
fields[2] = &discordgo.MessageEmbedField{
fields[1] = &discordgo.MessageEmbedField{
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.userStatsEmbed.TotalWins",
Other: "Total Wins",
@ -48,6 +43,11 @@ func (bot *Bot) UserStatsEmbed(userID, guildID string, sett *storage.GuildSettin
Value: fmt.Sprintf("%d", winsOnServer),
Inline: true,
}
fields = append(fields, &discordgo.MessageEmbedField{
Name: "\u200b",
Value: "\u200b",
Inline: true,
})
extraDesc := sett.LocalizeMessage(&i18n.Message{
ID: "responses.userStatsEmbed.NoPremium",
@ -64,11 +64,11 @@ func (bot *Bot) UserStatsEmbed(userID, guildID string, sett *storage.GuildSettin
colorRankings := bot.PostgresInterface.ColorRankingForPlayer(userID)
if len(colorRankings) > 0 {
buf := bytes.NewBuffer([]byte{})
for i := 0; i < len(colorRankings); i++ {
for i := 0; i < len(colorRankings) && i < UserLeaderboardCount; i++ {
elem := colorRankings[i]
emoji := bot.StatusEmojis[true][elem.Mode]
buf.WriteString(fmt.Sprintf("%s | %.0f%%", emoji.FormatForInline(), 100.0*float64(elem.Count)/float64(gamesPlayed)))
if i < len(colorRankings)-1 {
if i < len(colorRankings)-1 && i < UserLeaderboardCount-1 {
buf.WriteByte('\n')
}
}
@ -81,13 +81,13 @@ func (bot *Bot) UserStatsEmbed(userID, guildID string, sett *storage.GuildSettin
Inline: true,
})
}
nameRankings := bot.PostgresInterface.NamesRanking(userID)
nameRankings := bot.PostgresInterface.NamesRankingForPlayer(userID)
if len(nameRankings) > 0 {
buf := bytes.NewBuffer([]byte{})
for i := 0; i < len(nameRankings); i++ {
for i := 0; i < len(nameRankings) && i < UserLeaderboardCount; i++ {
elem := nameRankings[i]
buf.WriteString(fmt.Sprintf("%s | %.0f%%", elem.Mode, 100.0*float64(elem.Count)/float64(gamesPlayed)))
if i < len(nameRankings)-1 {
if i < len(nameRankings)-1 && i < UserLeaderboardCount-1 {
buf.WriteByte('\n')
}
}
@ -147,7 +147,7 @@ func (bot *Bot) UserStatsEmbed(userID, guildID string, sett *storage.GuildSettin
}),
Description: sett.LocalizeMessage(&i18n.Message{
ID: "responses.userStatsEmbed.Desc",
Other: "User stats for {{.User}}",
Other: "User stats for {{.User}} on this Server",
}, map[string]interface{}{
"User": "<@!" + userID + ">",
}) + "\n\n" + extraDesc,
@ -168,6 +168,8 @@ func (bot *Bot) UserStatsEmbed(userID, guildID string, sett *storage.GuildSettin
return &embed
}
const LeaderboardSize = 5
func (bot *Bot) GuildStatsEmbed(guildID string, sett *storage.GuildSettings, premium storage.PremiumTier) *discordgo.MessageEmbed {
gname := ""
avatarUrl := ""
@ -187,7 +189,7 @@ func (bot *Bot) GuildStatsEmbed(guildID string, sett *storage.GuildSettings, pre
fields[0] = &discordgo.MessageEmbedField{
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.guildStatsEmbed.GamesPlayed",
Other: "Total Games Played",
Other: "Games Played",
}),
Value: fmt.Sprintf("%d", gamesPlayed),
Inline: true,
@ -205,6 +207,64 @@ func (bot *Bot) GuildStatsEmbed(guildID string, sett *storage.GuildSettings, pre
ID: "responses.guildStatsEmbed.Premium",
Other: "Showing additional Premium Stats!",
})
gid, err := strconv.ParseUint(guildID, 10, 64)
if err == nil {
totalGameRankings := bot.PostgresInterface.TotalGamesRankingForServer(gid)
buf := bytes.NewBuffer([]byte{})
for i := 0; i < len(totalGameRankings) && i < LeaderboardSize; i++ {
elem := totalGameRankings[i]
buf.WriteString(fmt.Sprintf("<@%d> | %d Games", elem.Mode, elem.Count))
if i < len(totalGameRankings)-1 && i < LeaderboardSize-1 {
buf.WriteByte('\n')
}
}
fields = append(fields, &discordgo.MessageEmbedField{
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.guildStatsEmbed.MostGames",
Other: "Games Played",
}),
Value: buf.String(),
Inline: true,
})
crewmateGameRankings := bot.PostgresInterface.TotalWinRankingForServerByRole(gid, 0)
buf = bytes.NewBuffer([]byte{})
for i := 0; i < len(crewmateGameRankings) && i < LeaderboardSize; i++ {
elem := crewmateGameRankings[i]
buf.WriteString(fmt.Sprintf("<@%d> | %d Wins", elem.Mode, elem.Count))
if i < len(crewmateGameRankings)-1 && i < LeaderboardSize-1 {
buf.WriteByte('\n')
}
}
fields = append(fields, &discordgo.MessageEmbedField{
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.guildStatsEmbed.CrewmateWins",
Other: "Crewmate Wins",
}),
Value: buf.String(),
Inline: true,
})
imposterGameRankings := bot.PostgresInterface.TotalWinRankingForServerByRole(gid, 1)
buf = bytes.NewBuffer([]byte{})
for i := 0; i < len(imposterGameRankings) && i < LeaderboardSize; i++ {
elem := imposterGameRankings[i]
buf.WriteString(fmt.Sprintf("<@%d> | %d Wins", elem.Mode, elem.Count))
if i < len(imposterGameRankings)-1 && i < LeaderboardSize-1 {
buf.WriteByte('\n')
}
}
fields = append(fields, &discordgo.MessageEmbedField{
Name: sett.LocalizeMessage(&i18n.Message{
ID: "responses.guildStatsEmbed.ImposterWins",
Other: "Imposter Wins",
}),
Value: buf.String(),
Inline: true,
})
}
}
var embed = discordgo.MessageEmbed{

View File

@ -0,0 +1,250 @@
"ascii.AsciiStarfield.isWas" = "was An Impostor."
"ascii.AsciiStarfield.isWasNot" = "was not An Impostor."
"commands.AllCommands.Ascii.args" = "<@discord user> <is imposter> (true|false) <x impostor remains> (count)"
"commands.AllCommands.Ascii.desc" = "Print an ASCII crewmate"
"commands.AllCommands.Ascii.shortDesc" = "Print an ASCII crewmate"
"commands.AllCommands.Cache.args" = "<player> (optionally, \"clear\")"
"commands.AllCommands.Cache.desc" = "View a player's cached in-game names, and/or clear them"
"commands.AllCommands.Cache.shortDesc" = "View cached usernames"
"commands.AllCommands.DebugState.args" = "None"
"commands.AllCommands.DebugState.desc" = "View the full state of the Discord Guild Data"
"commands.AllCommands.DebugState.shortDesc" = "View the full state of the Discord Guild Data"
"commands.AllCommands.End.args" = "None"
"commands.AllCommands.End.desc" = "End the current game"
"commands.AllCommands.End.shortDesc" = "End the game"
"commands.AllCommands.Force.args" = "<phase name> (task, discuss, or lobby / t,d, or l)"
"commands.AllCommands.Force.desc" = "Force the bot to transition to another game stage, if it doesn't transition properly"
"commands.AllCommands.Force.shortDesc" = "Force the bot to transition"
"commands.AllCommands.Help.args" = "None, or optional command to see info for"
"commands.AllCommands.Help.desc" = "Display bot help message, or see info about a command"
"commands.AllCommands.Help.shortDesc" = "Display help"
"commands.AllCommands.Info.args" = "None"
"commands.AllCommands.Info.desc" = "View info about the bot, like total guild number, active games, etc"
"commands.AllCommands.Info.shortDesc" = "View Bot info"
"commands.AllCommands.Link.args" = "<discord User> <in-game color or name>"
"commands.AllCommands.Link.desc" = "Manually link a Discord User to their in-game color or name"
"commands.AllCommands.Link.shortDesc" = "Link a Discord User"
"commands.AllCommands.Log.args" = "<message>"
"commands.AllCommands.Log.desc" = "Log if something bad happened, so you can find the event in your logs later"
"commands.AllCommands.Log.shortDesc" = "Log a weird event"
"commands.AllCommands.New.args" = "None"
"commands.AllCommands.New.desc" = "Start a new game"
"commands.AllCommands.New.shortDesc" = "Start a new game"
"commands.AllCommands.Pause.args" = "None"
"commands.AllCommands.Pause.desc" = "Pause the bot so it doesn't automute/deafen. Will unmute/undeafen all players!"
"commands.AllCommands.Pause.shortDesc" = "Pause the bot"
"commands.AllCommands.Premium.args" = "None"
"commands.AllCommands.Premium.desc" = "View all the features and perks of Premium AutoMuteUs membership"
"commands.AllCommands.Premium.shortDesc" = "View Premium Bot Features"
"commands.AllCommands.Privacy.args" = "showme, optin, or optout"
"commands.AllCommands.Privacy.desc" = "AutoMuteUs privacy and data collection details.\\nMore details [here](https://github.com/denverquane/automuteus/blob/master/PRIVACY.md)"
"commands.AllCommands.Privacy.shortDesc" = "View AutoMuteUs privacy information"
"commands.AllCommands.Refresh.args" = "None"
"commands.AllCommands.Refresh.desc" = "Recreate the bot status message if it ends up too far in the chat"
"commands.AllCommands.Refresh.shortDesc" = "Refresh the bot status"
"commands.AllCommands.Settings.args" = "<setting> <value>"
"commands.AllCommands.Settings.desc" = "Adjust the bot settings. Type `{{.CommandPrefix}} settings` with no arguments to see more."
"commands.AllCommands.Settings.shortDesc" = "Adjust bot settings"
"commands.AllCommands.Stats.args" = "<@discord user> or \"guild\""
"commands.AllCommands.Stats.desc" = "View Player and Guild stats"
"commands.AllCommands.Stats.shortDesc" = "View Player and Guild stats"
"commands.AllCommands.Unlink.args" = "<discord User>"
"commands.AllCommands.Unlink.desc" = "Manually unlink a Discord User from their in-game player"
"commands.AllCommands.Unlink.shortDesc" = "Unlink a Discord User"
"commands.AllCommands.UnmuteAll.args" = "None"
"commands.AllCommands.UnmuteAll.desc" = "Force the bot to unmute all linked players"
"commands.AllCommands.UnmuteAll.shortDesc" = "Force the bot to unmute all"
"commands.ConstructEmbedForCommand.Fields.Aliases" = "Aliases"
"commands.ConstructEmbedForCommand.Fields.Arguments" = "Arguments"
"commands.ConstructEmbedForCommand.Fields.Example" = "Example"
"commands.HandleCommand.Cache.Success" = "Successfully deleted all cached names for that user!"
"commands.HandleCommand.Cache.cachedNames" = "Cached in-game names:"
"commands.HandleCommand.Cache.emptyCachedNames" = "I don't have any cached player names stored for that user!"
"commands.HandleCommand.ForgetMe.Success" = "✅ {{.User}} I successfully deleted your cached player names"
"commands.HandleCommand.Help.notFound" = "I didn't recognize that command! View `help` for all available commands!"
"commands.HandleCommand.ShowMe.cachedNames" = "❗ {{.User}} Here's your cached in-game names:"
"commands.HandleCommand.ShowMe.emptyCachedNames" = "❌ {{.User}} I don't have any cached player names stored for you!"
"commands.HandleCommand.ShowMe.linkedID" = "❗ {{.User}} You are opted **in** to data collection for game statistics"
"commands.HandleCommand.ShowMe.unlinkedID" = "❌ {{.User}} You are opted **out** of data collection for game statistics, or you haven't played a game yet"
"commands.HandleCommand.default" = "Sorry, I didn't understand that command! Please see `{{.CommandPrefix}} help` for commands"
"commands.HandleCommand.optin.FailDB" = "❌ {{.User}} You are already opted into data collection"
"commands.HandleCommand.optin.SuccessDB" = "✅ {{.User}} I successfully opted you into data collection"
"commands.HandleCommand.optout.FailDB" = "❌ {{.User}} You are already opted out of data collection"
"commands.HandleCommand.optout.SuccessDB" = "✅ {{.User}} I successfully opted you out of data collection"
"discordGameState.ToDescString.anyVoiceChannel" = "**no Voice Channel! Use `{{.CommandPrefix}} track`!**"
"discordGameState.ToDescString.voiceChannelName" = "the **{{.channelName}}** voice channel!"
"discordGameState.ToEmojiEmbedFields.Unlinked" = "Unlinked"
"discordGameState.ToStatusString.anyVoiceChannel" = "**No Voice Channel! Use `{{.CommandPrefix}} track`!**"
"discordGameState.trackChannel.voiceChannelNotfound" = "No channel found by the name {{.channelName}}!\\n"
"discordGameState.trackChannel.voiceChannelSet" = "Now Tracking \"{{.channelName}}\" Voice Channel for Automute!"
"eventHandler.gameOver.deleteMessageFooter" = "Deleting message {{.Mins}} mins from:"
"eventHandler.gameOver.matchID" = "Game Over. View the match's stats using Match ID: `{{.MatchID}}`"
"helpers.getRoomAndRegionFromArgs.regionUnprovided" = "Unprovided"
"helpers.getRoomAndRegionFromArgs.roomUnprovided" = "Unprovided"
"locale.language.name" = "English"
"message_handlers.generalRatelimit" = "{{.User}}, you're issuing commands too fast! Please slow down!"
"message_handlers.handleMessageCreate.noPerms" = "User does not have the required permissions to execute this command!"
"message_handlers.handleMessageCreate.respondPrefix" = "I respond to the prefix {{.CommandPrefix}}"
"message_handlers.handleNewGameMessage.embed.Description" = "Click the following link to link your capture: \\n <{{.hyperlink}}>\\n\\nDon't have the capture installed? Latest version [here]({{.downloadURL}})\\n\\nTo link your capture manually:"
"message_handlers.handleNewGameMessage.embed.Fields.Code" = "Code"
"message_handlers.handleNewGameMessage.embed.Fields.URL" = "URL"
"message_handlers.handleNewGameMessage.embed.Title" = "You just started a game!"
"message_handlers.handleNewGameMessage.lockout" = "Discord is rate-limiting me and I cannot accept any new games right now 😦\\nPlease try again in a few minutes, or consider AutoMuteUs Premium"
"message_handlers.handleNewGameMessage.noChannel" = "{{.User}}, please join a voice channel before starting a match!"
"message_handlers.handleNewGameMessage.specificRatelimit" = "{{.User}} You're creating games too fast! Please slow down!"
"message_handlers.handleReactionGameStartAdd.generalRatelimit" = "{{.User}}, you're reacting too fast! Please slow down!"
"message_handlers.softban" = "{{.User}} I'm ignoring your messages for the next 5 minutes, stop spamming"
"responses.guildStatsEmbed.CrewmateWins" = "Crewmate Wins"
"responses.guildStatsEmbed.Desc" = "Guild stats for {{.GuildName}}"
"responses.guildStatsEmbed.GamesPlayed" = "Games Played"
"responses.guildStatsEmbed.ImposterWins" = "Imposter Wins"
"responses.guildStatsEmbed.MostGames" = "Games Played"
"responses.guildStatsEmbed.NoPremium" = "Detailed stats are only available for AutoMuteUs Premium users; type `{{.CommandPrefix}} premium` to learn more"
"responses.guildStatsEmbed.Premium" = "Showing additional Premium Stats!"
"responses.guildStatsEmbed.Title" = "Guild Stats"
"responses.helpResponse.SubTitle" = "[View the Github Project](https://github.com/denverquane/automuteus) or [Join our Discord](https://discord.gg/ZkqZSWF)\\n\\nType `{{.CommandPrefix}} help <command>` to see more details on a command!"
"responses.helpResponse.Title" = "AutoMuteUs Bot Commands:\\n"
"responses.lobbyMessage.Footer.Text" = "React to this message with your in-game color! (or {{.emojiLeave}} to leave)"
"responses.lobbyMessage.Title" = "Lobby"
"responses.lobbyMessage.notLinked.Description" = "❌**No capture linked! Click the link in your DMs to connect!**❌"
"responses.lobbyMetaEmbedFields.PlayersLinked" = "Players Linked"
"responses.lobbyMetaEmbedFields.Region" = "🌎 REGION"
"responses.lobbyMetaEmbedFields.RoomCode" = "🔒 ROOM CODE"
"responses.makeDescription.GameNotRunning" = "\\n⚠ **Bot is Paused!** ⚠\\n\\n"
"responses.makeDescription.author" = "<@{{.author}}> is running an Among Us game!\\nThe game is happening in "
"responses.menuMessage.Linked.FooterText" = "(Enter a game lobby in Among Us to start the match)"
"responses.menuMessage.Title" = "Main Menu"
"responses.menuMessage.notLinked.Description" = "❌**No capture linked! Click the link in your DMs to connect!**❌"
"responses.nonPremiumSetting.Desc" = "Sorry, but that setting is reserved for AutoMuteUs Premium users! See `{{.CommandPrefix}} premium` for details"
"responses.premiumInviteResponse.Title" = "Premium Bot Invites"
"responses.premiumInviteResponse.desc" = "{{.Tier}} users have access to {{.Count}} Priority mute bots: invites provided below!"
"responses.premiumInviteResponseNoAccess.desc" = "{{.Tier}} users don't have access to Priority mute bots!\\nPlease type `{{.CommandPrefix}} premium` to see more details about AutoMuteUs Premium"
"responses.premiumResponse.FastMute" = "🙊 Fast Mute/Deafen"
"responses.premiumResponse.FastMuteDesc" = "Premium users get access to \"helper\" bots that make sure muting is fast!"
"responses.premiumResponse.FreeDescription" = "Check out the cool things that Premium AutoMuteUs has to offer!\\n\\n[Get AutoMuteUs Premium](https://patreon.com/automuteus)"
"responses.premiumResponse.Invites" = "View a list of Premium bots you can invite with `{{.CommandPrefix}} premium invites`!"
"responses.premiumResponse.PremiumDescription" = "Looks like you have AutoMuteUs **{{.Tier}}**! Thanks for the support!\\n\\nBelow are some of the benefits you can customize with your Premium status!"
"responses.premiumResponse.PriorityGameAccess" = "👑 Priority Game Access"
"responses.premiumResponse.PriorityGameAccessDesc" = "If the Bot is under heavy load, Premium users will always be able to make new games!"
"responses.premiumResponse.Settings" = "🛠 Special Settings"
"responses.premiumResponse.SettingsDesc" = "Premium users can specify additional settings, like displaying an end-game status message, or auto-refreshing the status message!"
"responses.premiumResponse.Stats" = "📊 Game Stats and Leaderboards"
"responses.premiumResponse.StatsDesc" = "Premium users have access to a full suite of player stats and leaderboards!"
"responses.premiumResponse.Support" = "👂 Premium Support"
"responses.premiumResponse.SupportDesc" = "Premium users get access to private channels on our official Discord channel!"
"responses.premiumResponse.Title" = "💎 AutoMuteUs Premium 💎"
"responses.privacyResponse.Title" = "AutoMuteUs Privacy"
"responses.settingResponse.Description" = "Type `{{.CommandPrefix}} settings <setting>` to change a setting from those listed below"
"responses.settingResponse.Title" = "Settings"
"responses.statsResponse.BotInfo" = "v{{.Version}}-{{.Commit}} | Shard {{.ID}}/{{.Num}}"
"responses.statsResponse.Creator" = "Creator"
"responses.statsResponse.Donate" = "Donate"
"responses.statsResponse.Games" = "Active Games"
"responses.statsResponse.Guilds" = "Total Guilds"
"responses.statsResponse.Invite" = "Invite"
"responses.statsResponse.Library" = "Library"
"responses.statsResponse.Title" = "Bot Info"
"responses.statsResponse.Version" = "Version"
"responses.statsResponse.Website" = "Website"
"responses.title.GameOver" = "**Game Over**"
"responses.userStatsEmbed.CrewmateWins" = "Crewmate Wins"
"responses.userStatsEmbed.Desc" = "User stats for {{.User}} on this Server"
"responses.userStatsEmbed.FavoriteColors" = "Favorite Colors"
"responses.userStatsEmbed.FavoriteNames" = "Favorite Names"
"responses.userStatsEmbed.GamesPlayed" = "Games Played"
"responses.userStatsEmbed.ImposterWins" = "Imposter Wins"
"responses.userStatsEmbed.NoPremium" = "Detailed stats are only available for AutoMuteUs Premium users; type `{{.CommandPrefix}} premium` to learn more"
"responses.userStatsEmbed.Premium" = "Showing additional Premium Stats!"
"responses.userStatsEmbed.Title" = "User Stats"
"responses.userStatsEmbed.TotalWins" = "Total Wins"
"settings.AllSettings.AdminUserIDs.args" = "<User @ mentions>..."
"settings.AllSettings.AdminUserIDs.desc" = "Specify which individual users have admin bot permissions"
"settings.AllSettings.AdminUserIDs.shortDesc" = "Bot Admins"
"settings.AllSettings.AutoRefresh.args" = "<true/false>"
"settings.AllSettings.AutoRefresh.desc" = "Specify if the bot should auto-refresh the status message after a match ends"
"settings.AllSettings.AutoRefresh.shortDesc" = "Autorefresh Status Message"
"settings.AllSettings.Delays.args" = "<start phase> <end phase> <delay>"
"settings.AllSettings.Delays.desc" = "Specify the delays for automute/deafen between stages of the game, like lobby->tasks"
"settings.AllSettings.Delays.shortDesc" = "Delays Between Stages"
"settings.AllSettings.Language.args" = "<language> or reload"
"settings.AllSettings.Language.desc" = "Change the bot messages language"
"settings.AllSettings.Language.shortDesc" = "Bot Language"
"settings.AllSettings.MatchSummary.args" = "<minutes>"
"settings.AllSettings.MatchSummary.desc" = "Specify minutes before the match summary message is deleted. 0 for instant deletion, -1 for never delete"
"settings.AllSettings.MatchSummary.shortDesc" = "Match Summary Message"
"settings.AllSettings.Prefix.args" = "<prefix>"
"settings.AllSettings.Prefix.desc" = "Change the prefix that the bot uses to detect commands"
"settings.AllSettings.Prefix.shortDesc" = "Bot Prefix"
"settings.AllSettings.Reset.args" = "None"
"settings.AllSettings.Reset.desc" = "Reset all bot settings to their default values"
"settings.AllSettings.Reset.shortDesc" = "Reset Bot Settings"
"settings.AllSettings.RoleIDs.args" = "<role @ mentions>..."
"settings.AllSettings.RoleIDs.desc" = "Specify which roles have permissions to invoke the bot"
"settings.AllSettings.RoleIDs.shortDesc" = "Bot Operators"
"settings.AllSettings.Show.args" = "None"
"settings.AllSettings.Show.desc" = "Show all the Bot settings for this server"
"settings.AllSettings.Show.shortDesc" = "Show All Settings"
"settings.AllSettings.UnmuteDead.args" = "<true/false>"
"settings.AllSettings.UnmuteDead.desc" = "Specify if the bot should immediately unmute players when they die. **CAUTION. Leaks information!**"
"settings.AllSettings.UnmuteDead.shortDesc" = "Bot Unmutes Deaths"
"settings.AllSettings.VoiceRules.args" = "<mute/deaf> <game phase> <dead/alive> <true/false>"
"settings.AllSettings.VoiceRules.desc" = "Specify mute/deafen rules for the game, depending on the stage and the alive/deadness of players. Example given would mute dead players during the tasks stage"
"settings.AllSettings.VoiceRules.shortDesc" = "Mute/deafen Rules"
"settings.CommandPrefixSetting.changes" = "Guild prefix changed from `{{.From}}` to `{{.To}}`. Use that from now on!"
"settings.CommandPrefixSetting.tooLong" = "Sorry, the prefix `{{.CommandPrefix}}` is too long ({{.Length}} characters, max 10). Try something shorter."
"settings.ConstructEmbedForSetting.Fields.Aliases" = "Aliases"
"settings.ConstructEmbedForSetting.Fields.Arguments" = "Arguments"
"settings.ConstructEmbedForSetting.Fields.CurrentValue" = "Current Value"
"settings.ConstructEmbedForSetting.Fields.Example" = "Example"
"settings.HandleSettingsCommand.default" = "Sorry, `{{.Arg}}` is not a valid setting!\\n"
"settings.SettingAdminUserIDs.clearAdmins" = "Clearing all AdminUserIDs!"
"settings.SettingAdminUserIDs.newBotAdmin" = "<@{{.UserID}}> is now a bot admin!"
"settings.SettingAdminUserIDs.noBotAdmins" = "No Bot Admins"
"settings.SettingAdminUserIDs.notFound" = "Sorry, I don't know who `{{.UserName}}` is. You can pass in ID, username, username#XXXX, nickname or @mention"
"settings.SettingAutoRefresh.False" = "From now on, I will not AutoRefresh the game status message"
"settings.SettingAutoRefresh.True" = "From now on, I'll AutoRefresh the game status message"
"settings.SettingAutoRefresh.Unrecognized" = "{{.Arg}} is not a true/false value. See `{{.CommandPrefix}} settings autorefresh` for usage"
"settings.SettingDelays.Phase.UNINITIALIZED" = "I don't know what `{{.PhaseName}}` is. The list of game phases are `Lobby`, `Tasks` and `Discussion`."
"settings.SettingDelays.delayBetweenPhases" = "Currently, the delay when passing from `{{.PhaseA}}` to `{{.PhaseB}}` is {{.OldDelay}}."
"settings.SettingDelays.missingPhases" = "The list of game phases are `Lobby`, `Tasks` and `Discussion`.\\nYou need to type both phases the game is transitioning from and to to change the delay."
"settings.SettingDelays.setDelayBetweenPhases" = "The delay when passing from `{{.PhaseA}}` to `{{.PhaseB}}` changed from {{.OldDelay}} to {{.NewDelay}}."
"settings.SettingDelays.wrongNumber" = "`{{.Number}}` is not a valid number! Please try again"
"settings.SettingLanguage.list" = "Available languages: {{.Langs}}"
"settings.SettingLanguage.notFound" = "Language not found! Available language codes: {{.Langs}}"
"settings.SettingLanguage.notLoaded" = "Localization files were not loaded! {{.Langs}}"
"settings.SettingLanguage.reloaded" = "Localization files are reloaded ({{.Count}}). Available language codes: {{.Langs}}"
"settings.SettingLanguage.set" = "Localization is set to {{.LangName}}"
"settings.SettingLanguage.tooShort" = "Sorry, the language code is short. Available language codes: {{.Langs}}."
"settings.SettingMatchSummary.OutOfRange" = "You provided a number too high or too low. Please specify a number between [0-60], or -1 to never delete match summaries"
"settings.SettingMatchSummary.Success" = "From now on, I'll delete match summary messages after {{.Minutes}} minutes."
"settings.SettingMatchSummary.Success0" = "From now on, I'll delete match summary messages immediately."
"settings.SettingMatchSummary.Unrecognized" = "{{.Minutes}} is not a valid number. See `{{.CommandPrefix}} settings matchSummary` for usage"
"settings.SettingPermissionRoleIDs.clearRoles" = "Clearing all PermissionRoleIDs!"
"settings.SettingPermissionRoleIDs.newBotAdmins" = "<@&{{.UserID}}>s are now bot admins!"
"settings.SettingPermissionRoleIDs.noRoleAdmins" = "No Role Admins"
"settings.SettingPermissionRoleIDs.notFound" = "Sorry, I don't know the role `{{.RoleName}}` is. Please use @role"
"settings.SettingUnmuteDeadDuringTasks.false_noUnmuteDead" = "It's already false!"
"settings.SettingUnmuteDeadDuringTasks.false_unmuteDead" = "I will no longer immediately unmute dead people. Good choice!"
"settings.SettingUnmuteDeadDuringTasks.true_noUnmuteDead" = "I will now unmute the dead people immediately after they die. Careful, this reveals who died during the match!"
"settings.SettingUnmuteDeadDuringTasks.true_unmuteDead" = "It's already true!"
"settings.SettingUnmuteDeadDuringTasks.wrongArg" = "Sorry, `{{.Arg}}` is neither `true` nor `false`."
"settings.SettingVoiceRules.NA" = "N/A"
"settings.SettingVoiceRules.Phase.UNINITIALIZED" = "I don't know what {{.PhaseName}} is. The list of game phases are `Lobby`, `Tasks` and `Discussion`."
"settings.SettingVoiceRules.enoughArgs" = "You didn't pass enough arguments! Correct syntax is: `voiceRules [mute/deaf] [game phase] [alive/dead] [true/false]`"
"settings.SettingVoiceRules.neitherAliveDead" = "`{{.Arg}}` is neither `alive` or `dead`!"
"settings.SettingVoiceRules.neitherMuteDeaf" = "`{{.Arg}}` is neither `mute` nor `deaf`!"
"settings.SettingVoiceRules.neitherTrueFalse" = "`{{.Arg}}` is neither `true` or `false`!"
"settings.SettingVoiceRules.queryingAlreadyUnValues" = "When in `{{.PhaseName}}` phase, {{.PlayerGameState}} players are already un{{.PlayerDiscordState}}!"
"settings.SettingVoiceRules.queryingAlreadyValues" = "When in `{{.PhaseName}}` phase, {{.PlayerGameState}} players are already {{.PlayerDiscordState}}!"
"settings.SettingVoiceRules.queryingCurrentlyOldValues" = "When in `{{.PhaseName}}` phase, {{.PlayerGameState}} players are currently {{.PlayerDiscordState}}."
"settings.SettingVoiceRules.queryingCurrentlyValues" = "When in `{{.PhaseName}}` phase, {{.PlayerGameState}} players are currently NOT {{.PlayerDiscordState}}."
"settings.SettingVoiceRules.setUnValues" = "From now on, when in `{{.PhaseName}}` phase, {{.PlayerGameState}} players will be un{{.PlayerDiscordState}}."
"settings.SettingVoiceRules.setValues" = "From now on, when in `{{.PhaseName}}` phase, {{.PlayerGameState}} players will be {{.PlayerDiscordState}}."
"state.phase.DISCUSSION" = "DISCUSSION"
"state.phase.LOBBY" = "LOBBY"
"state.phase.MENU" = "MENU"
"state.phase.TASKS" = "TASKS"
["ascii.AsciiStarfield.remains"]
one = "Impostor remains"
other = "Impostors remain"

View File

@ -18,6 +18,7 @@ type GuildSettings struct {
VoiceRules game.VoiceRules `json:"voiceRules"`
UnmuteDeadDuringTasks bool `json:"unmuteDeadDuringTasks"`
DeleteGameSummaryMinutes int `json:"deleteGameSummary"`
AutoRefresh bool `json:"autoRefresh"`
lock sync.RWMutex
}
@ -32,6 +33,7 @@ func MakeGuildSettings() *GuildSettings {
VoiceRules: game.MakeMuteAndDeafenRules(),
UnmuteDeadDuringTasks: false,
DeleteGameSummaryMinutes: 0, //-1 for never delete the match summary
AutoRefresh: false,
lock: sync.RWMutex{},
}
}
@ -105,6 +107,14 @@ func (gs *GuildSettings) SetDeleteGameSummaryMinutes(num int) {
gs.DeleteGameSummaryMinutes = num
}
func (gs *GuildSettings) GetAutoRefresh() bool {
return gs.AutoRefresh
}
func (gs *GuildSettings) SetAutoRefresh(n bool) {
gs.AutoRefresh = n
}
func (gs *GuildSettings) SetUnmuteDeadDuringTasks(v bool) {
gs.UnmuteDeadDuringTasks = v
}

View File

@ -25,6 +25,8 @@ const (
BronzeTier
SilverTier
GoldTier
PlatTier
SelfHostTier
)
var PremiumTierStrings = []string{
@ -32,6 +34,8 @@ var PremiumTierStrings = []string{
"Bronze",
"Silver",
"Gold",
"Platinum",
"SelfHost",
}
func ConstructPsqlConnectURL(addr, username, password string) string {
@ -113,6 +117,13 @@ func (psqlInterface *PsqlInterface) OptUserByString(userID string, opt bool) (bo
if err != nil {
return false, err
}
if !opt {
_, err = psqlInterface.pool.Exec(context.Background(), "UPDATE game_events SET (hashed_user_id) = (NULL) WHERE hashed_user_id = $1;", user.HashedUserID)
if err != nil {
log.Println(err)
}
}
return true, nil
}
@ -153,17 +164,17 @@ func (psqlInterface *PsqlInterface) insertPlayer(player *PostgresUserGame) error
}
func (psqlInterface *PsqlInterface) GetGuildPremiumStatus(guildID string) PremiumTier {
//self-hosting; only return the true guild status if this variable is set
if os.Getenv("OFFICIAL") == "" {
return SelfHostTier
}
gid, err := strconv.ParseUint(guildID, 10, 64)
if err != nil {
log.Println(err)
return FreeTier
}
//self-hosting; only return the true guild status if this variable is set
if os.Getenv("OFFICIAL") == "" {
return GoldTier
}
guild, err := psqlInterface.GetGuild(gid)
if err != nil {
return FreeTier

View File

@ -47,15 +47,18 @@ create table if not exists users_games
create index if not exists guilds_id_index ON guilds (guild_id); --query guilds by ID
create index if not exists guilds_premium_index ON guilds (premium); --query guilds by prem status
create index if not exists games_id_index ON games (game_id); --query games by ID
create index if not exists guild_games_id_index ON games (guild_id); --query games by guild ID
create index if not exists games_game_id_index ON games (game_id); --query games by ID
create index if not exists games_guild_id_index ON games (guild_id); --query games by guild ID
create index if not exists games_win_type_index on games (win_type); --query games by win type
create index if not exists games_connect_code_index on games (connect_code); --query games by connect code
create index if not exists users_id_index ON users (user_id); --query for user info by their ID
create index if not exists users_user_id_index ON users (user_id); --query for user info by their ID
create index if not exists users_games_hashed_id_index ON users_games (user_id); --query games by hashed ID
create index if not exists users_games_user_id_index ON users_games (user_id); --query games by user ID
create index if not exists users_games_game_id_index ON users_games (game_id); --query games by game ID
create index if not exists users_games_guild_id_index ON users_games (guild_id); --query games by guild ID
create index if not exists users_games_role_index ON users_games (player_role); --query games by win status
create index if not exists users_games_won_index ON users_games (player_won); --query games by win status
create index if not exists game_events_game_id_index on game_events (game_id); --query for game events by the game ID
create index if not exists game_events_game_id_index on game_events (game_id); --query for game events by the game ID
create index if not exists game_events_hashed_user_id_index on game_events (hashed_user_id); --query for game events by the game ID

View File

@ -20,7 +20,8 @@ func TestPsqlInterface_Init(t *testing.T) {
}
gid := uint64(141082723635691521)
uid := uint64(140581066283941888)
userID := "140581066283941888"
//userID := "140581066283941888"
//guildID := "140581066283941888"
_, err = psql.EnsureGuildExists(gid, "test")
if err != nil {
@ -57,10 +58,10 @@ func TestPsqlInterface_Init(t *testing.T) {
//rs := psql.NumGamesPlayedByUserOnServer(uid, "141082723635691521")
//log.Println(rs)
//
//dd := psql.NamesRanking(uid)
//for _, v := range dd {
// log.Printf("Mode: %s, Count: %d\n", v.Mode, v.Count)
//}
dd := psql.TotalGamesRankingForServer(gid)
for _, v := range dd {
log.Printf("Mode: %d, Count: %d\n", v.Mode, v.Count)
}
//err = psql.EnsureGuildUserExists(guildID, hashedID)
//if err != nil {
@ -87,7 +88,4 @@ func TestPsqlInterface_Init(t *testing.T) {
//if err != nil {
// log.Fatal(err)
//}
log.Println(psql.NumWinsAsRole(userID, 0))
log.Println(psql.NumWinsAsRole(userID, 1))
}

View File

@ -72,18 +72,22 @@ func (psqlInterface *PsqlInterface) NumWinsOnServer(userID, guildID string) int6
return r[0]
}
type IntModeCount struct {
type Int16ModeCount struct {
Count int64 `db:"count"`
Mode int16 `db:"mode"`
}
type Uint64ModeCount struct {
Count int64 `db:"count"`
Mode uint64 `db:"mode"`
}
type StringModeCount struct {
Count int64 `db:"count"`
Mode string `db:"mode"`
}
func (psqlInterface *PsqlInterface) ColorRankingForPlayer(userID string) []*IntModeCount {
r := []*IntModeCount{}
func (psqlInterface *PsqlInterface) ColorRankingForPlayer(userID string) []*Int16ModeCount {
r := []*Int16ModeCount{}
err := pgxscan.Select(context.Background(), psqlInterface.pool, &r, "SELECT count(*),mode() within GROUP (ORDER BY player_color) AS mode FROM users_games WHERE user_id=$1 GROUP BY player_color ORDER BY count desc;", userID)
if err != nil {
@ -92,7 +96,7 @@ func (psqlInterface *PsqlInterface) ColorRankingForPlayer(userID string) []*IntM
return r
}
func (psqlInterface *PsqlInterface) NamesRanking(userID string) []*StringModeCount {
func (psqlInterface *PsqlInterface) NamesRankingForPlayer(userID string) []*StringModeCount {
r := []*StringModeCount{}
err := pgxscan.Select(context.Background(), psqlInterface.pool, &r, "SELECT count(*),mode() within GROUP (ORDER BY player_name) AS mode FROM users_games WHERE user_id=$1 GROUP BY player_name ORDER BY count desc;", userID)
@ -101,3 +105,23 @@ func (psqlInterface *PsqlInterface) NamesRanking(userID string) []*StringModeCou
}
return r
}
func (psqlInterface *PsqlInterface) TotalGamesRankingForServer(guildID uint64) []*Uint64ModeCount {
r := []*Uint64ModeCount{}
err := pgxscan.Select(context.Background(), psqlInterface.pool, &r, "SELECT count(*),mode() within GROUP (ORDER BY user_id) AS mode FROM users_games WHERE guild_id=$1 GROUP BY user_id ORDER BY count desc;", guildID)
if err != nil {
log.Println(err)
}
return r
}
func (psqlInterface *PsqlInterface) TotalWinRankingForServerByRole(guildID uint64, role int16) []*Uint64ModeCount {
r := []*Uint64ModeCount{}
err := pgxscan.Select(context.Background(), psqlInterface.pool, &r, "SELECT count(*),mode() within GROUP (ORDER BY user_id) AS mode FROM users_games WHERE guild_id=$1 AND player_role=$2 AND player_won=true GROUP BY user_id ORDER BY count desc;", guildID, role)
if err != nil {
log.Println(err)
}
return r
}