update from upstream
This commit is contained in:
parent
f743f1efbd
commit
3e3f761b01
69
arguments.go
69
arguments.go
|
|
@ -45,6 +45,7 @@ var (
|
|||
SubCmd ArgTypeGuards = "subcmd"
|
||||
SubCmdGrp ArgTypeGuards = "subcmdgrp"
|
||||
ArrString ArgTypeGuards = "arrString"
|
||||
Time ArgTypeGuards = "time"
|
||||
)
|
||||
|
||||
// ArgInfo
|
||||
|
|
@ -75,7 +76,7 @@ type Arguments map[string]CommandArg
|
|||
|
||||
// CreateCommandInfo
|
||||
// Creates a pointer to a CommandInfo
|
||||
func CreateCommandInfo(trigger string, description string, public bool, group string) *CommandInfo {
|
||||
func CreateCommandInfo(trigger string, description string, public bool, group Group) *CommandInfo {
|
||||
cI := &CommandInfo{
|
||||
Aliases: nil,
|
||||
Arguments: orderedmap.New(),
|
||||
|
|
@ -88,6 +89,13 @@ func CreateCommandInfo(trigger string, description string, public bool, group st
|
|||
return cI
|
||||
}
|
||||
|
||||
// CreateRawCmdInfo
|
||||
// Creates a pointer to a CommandInfo
|
||||
func CreateRawCmdInfo(cI *CommandInfo) *CommandInfo {
|
||||
cI.Arguments = orderedmap.New()
|
||||
return cI
|
||||
}
|
||||
|
||||
// SetParent
|
||||
// Sets the parent properties
|
||||
func (cI *CommandInfo) SetParent(isParent bool, parentID string) {
|
||||
|
|
@ -248,34 +256,7 @@ func findAllOptionArgs(argString []string, keys []string, infoArgs *orderedmap.O
|
|||
modifiedArgString := ""
|
||||
var modKeys []string
|
||||
var indexes []int
|
||||
// If the length of the argString is equal to the length of keys
|
||||
// We can just set each value in argString to a CommandArg
|
||||
// If a match field is equal to the content we will return early.
|
||||
// This is a rare occurrence. But this allows for a faster result
|
||||
if len(argString) == len(keys) {
|
||||
for i, v := range argString {
|
||||
// error handling
|
||||
iA, ok := infoArgs.Get(keys[i])
|
||||
if !ok {
|
||||
err := errors.New(fmt.Sprintf("Unable to find map relating to key: %s", keys[i]))
|
||||
SendErrorReport("", "", "", "Argument Parsing error", err)
|
||||
continue
|
||||
}
|
||||
vv := iA.(*ArgInfo)
|
||||
// ArgContent type should always be the last item in the slice
|
||||
// Should be safe to return early
|
||||
if vv.Match == ArgContent {
|
||||
modifiedArgString = strings.Join(argString[i:], " ")
|
||||
modKeys = RemoveItems(keys, indexes)
|
||||
return *args, true, createSplitString(modifiedArgString), modKeys
|
||||
}
|
||||
if checkTypeGuard(v, vv.TypeGuard) {
|
||||
(*args)[keys[i]] = handleArgOption(v, *vv)
|
||||
indexes = append(indexes, i)
|
||||
}
|
||||
}
|
||||
return *args, false, createSplitString(modifiedArgString), modKeys
|
||||
}
|
||||
|
||||
// (semi) Brute force method
|
||||
// First lets find all required args
|
||||
currentPos := 0
|
||||
|
|
@ -314,8 +295,13 @@ func findAllOptionArgs(argString []string, keys []string, infoArgs *orderedmap.O
|
|||
argString = argString[currentPos:]
|
||||
indexes = nil
|
||||
currentPos = 0
|
||||
// Return early if the argument parser has found all args
|
||||
if argString == nil || len(argString) == 0 || len(modKeys) == 0 || modKeys == nil {
|
||||
return *args, false, argString, modKeys
|
||||
}
|
||||
|
||||
// Now lets find the not required args
|
||||
for _, v := range modKeys {
|
||||
for i, v := range modKeys {
|
||||
// error handling
|
||||
iA, ok := infoArgs.Get(v)
|
||||
if !ok {
|
||||
|
|
@ -331,8 +317,8 @@ func findAllOptionArgs(argString []string, keys []string, infoArgs *orderedmap.O
|
|||
break
|
||||
}
|
||||
if vv.Match == ArgContent {
|
||||
modifiedArgString = strings.Join(argString, " ")
|
||||
return *args, true, createSplitString(modifiedArgString), modKeys
|
||||
modKeys = RemoveItems(modKeys, indexes)
|
||||
return *args, true, argString, modKeys
|
||||
}
|
||||
// Break early if current pos is the length of the array
|
||||
if currentPos == len(argString) {
|
||||
|
|
@ -342,9 +328,11 @@ func findAllOptionArgs(argString []string, keys []string, infoArgs *orderedmap.O
|
|||
var value string
|
||||
value, argString = findTypeGuard(strings.Join(argString, " "), argString, vv.TypeGuard)
|
||||
(*args)[v] = handleArgOption(value, *vv)
|
||||
indexes = append(indexes, i)
|
||||
} else if checkTypeGuard(argString[currentPos], vv.TypeGuard) {
|
||||
(*args)[v] = handleArgOption(argString[currentPos], *vv)
|
||||
currentPos++
|
||||
indexes = append(indexes, i)
|
||||
} else {
|
||||
|
||||
}
|
||||
|
|
@ -356,7 +344,12 @@ func findAllOptionArgs(argString []string, keys []string, infoArgs *orderedmap.O
|
|||
func findTypeGuard(input string, array []string, typeguard ArgTypeGuards) (string, []string) {
|
||||
switch typeguard {
|
||||
case Int:
|
||||
if match, isMatch := Misc["int"].FindStringMatch(input); isMatch == nil && match != nil {
|
||||
if match, isMatch := TypeGuard["int"].FindStringMatch(input); isMatch == nil && match != nil {
|
||||
return match.String(), RemoveItem(array, match.String())
|
||||
}
|
||||
return "", array
|
||||
case Boolean:
|
||||
if match, isMatch := TypeGuard["boolean"].FindStringMatch(input); isMatch == nil && match != nil {
|
||||
return match.String(), RemoveItem(array, match.String())
|
||||
}
|
||||
return "", array
|
||||
|
|
@ -391,8 +384,18 @@ func findTypeGuard(input string, array []string, typeguard ArgTypeGuards) (strin
|
|||
return match.String(), RemoveItem(array, match.String())
|
||||
}
|
||||
return "", array
|
||||
case Time:
|
||||
match := strings.Join(FindAllString(TimeRegexes["all"], input), "")
|
||||
//if match, isMatch := TimeRegexes["all"].Mat(input); isMatch == nil && match != nil {
|
||||
// return match.String(), RemoveItem(array, match.String())
|
||||
//}
|
||||
if match != "" {
|
||||
return match, RemoveItem(array, match)
|
||||
}
|
||||
return "", array
|
||||
default:
|
||||
return "", array
|
||||
}
|
||||
}
|
||||
|
||||
func findAllFlags(argString string, keys []string, infoArgs *orderedmap.OrderedMap, args *Arguments) ([]string, Arguments, []string) {
|
||||
|
|
|
|||
71
commands.go
71
commands.go
|
|
@ -2,19 +2,22 @@ package framework
|
|||
|
||||
import (
|
||||
"github.com/QPixel/orderedmap"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
// commands.go
|
||||
// This file contains everything required to add core commands to the bot, and parse commands from a message
|
||||
|
||||
// GroupTypes
|
||||
const (
|
||||
Moderation = "moderation"
|
||||
Utility = "utility"
|
||||
type Group string
|
||||
|
||||
var (
|
||||
Moderation Group = "moderation"
|
||||
Utility Group = "utility"
|
||||
)
|
||||
|
||||
// CommandInfo
|
||||
|
|
@ -23,7 +26,7 @@ type CommandInfo struct {
|
|||
Aliases []string // Aliases for the normal trigger
|
||||
Arguments *orderedmap.OrderedMap // Arguments for the command
|
||||
Description string // A short description of what the command does
|
||||
Group string // The group this command belongs to
|
||||
Group Group // The group this command belongs to
|
||||
ParentID string // The ID of the parent command
|
||||
Public bool // Whether non-admins and non-mods can use this command
|
||||
IsTyping bool // Whether the command will show a typing thing when ran.
|
||||
|
|
@ -86,6 +89,9 @@ var commandAliases = make(map[string]string)
|
|||
// This is also private so other commands cannot modify it
|
||||
var slashCommands = make(map[string]discordgo.ApplicationCommand)
|
||||
|
||||
// commandsGC
|
||||
var commandsGC = 0
|
||||
|
||||
// AddCommand
|
||||
// Add a command to the bot
|
||||
func AddCommand(info *CommandInfo, function BotFunction) {
|
||||
|
|
@ -227,13 +233,6 @@ func commandHandler(session *discordgo.Session, message *discordgo.MessageCreate
|
|||
}
|
||||
}
|
||||
|
||||
// The command is valid, so now we need to delete the invoking message if that is configured
|
||||
if g.Info.DeletePolicy {
|
||||
err := Session.ChannelMessageDelete(message.ChannelID, message.ID)
|
||||
if err != nil {
|
||||
SendErrorReport(message.GuildID, message.ChannelID, message.Author.ID, "Failed to delete message: "+message.ID, err)
|
||||
}
|
||||
}
|
||||
if !isCustom {
|
||||
//Get the command to run
|
||||
// Error Checking
|
||||
|
|
@ -253,6 +252,14 @@ func commandHandler(session *discordgo.Session, message *discordgo.MessageCreate
|
|||
if command.Info.IsTyping && g.Info.ResponseChannelId == "" {
|
||||
_ = Session.ChannelTyping(message.ChannelID)
|
||||
}
|
||||
// The command is valid, so now we need to delete the invoking message if that is configured
|
||||
if g.Info.DeletePolicy {
|
||||
err := Session.ChannelMessageDelete(message.ChannelID, message.ID)
|
||||
if err != nil {
|
||||
SendErrorReport(message.GuildID, message.ChannelID, message.Author.ID, "Failed to delete message: "+message.ID, err)
|
||||
}
|
||||
}
|
||||
|
||||
defer handleCommandError(g.ID, channel.ID, message.Author.ID)
|
||||
if command.Info.IsParent {
|
||||
handleChildCommand(*argString, command, message.Message, g)
|
||||
|
|
@ -264,6 +271,13 @@ func commandHandler(session *discordgo.Session, message *discordgo.MessageCreate
|
|||
Args: *ParseArguments(*argString, command.Info.Arguments),
|
||||
Message: message.Message,
|
||||
})
|
||||
// Makes sure that variables ran in ParseArguments are gone.
|
||||
if commandsGC == 25 && commandsGC > 25 {
|
||||
debug.FreeOSMemory()
|
||||
commandsGC = 0
|
||||
} else {
|
||||
commandsGC++
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
|
|
@ -272,24 +286,9 @@ func commandHandler(session *discordgo.Session, message *discordgo.MessageCreate
|
|||
// -- Helper Methods
|
||||
func handleChildCommand(argString string, command Command, message *discordgo.Message, g *Guild) {
|
||||
split := strings.SplitN(argString, " ", 2)
|
||||
// First lets see if this subcmd even exists
|
||||
v, ok := command.Info.Arguments.Get("subcmdgrp")
|
||||
// the command doesn't even have a subcmdgrp arg, return
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
|
||||
choices := v.(*ArgInfo).Choices
|
||||
subCmdExist := false
|
||||
for _, choice := range choices {
|
||||
if split[0] != choice {
|
||||
continue
|
||||
} else {
|
||||
subCmdExist = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !subCmdExist {
|
||||
childCmd, ok := childCommands[command.Info.Trigger][split[0]]
|
||||
if !ok {
|
||||
command.Function(&Context{
|
||||
Guild: g,
|
||||
Cmd: command.Info,
|
||||
|
|
@ -298,12 +297,11 @@ func handleChildCommand(argString string, command Command, message *discordgo.Me
|
|||
})
|
||||
return
|
||||
}
|
||||
childCmd, ok := childCommands[command.Info.Trigger][split[0]]
|
||||
if !ok || len(split) < 2 {
|
||||
command.Function(&Context{
|
||||
if len(split) < 2 {
|
||||
childCmd.Function(&Context{
|
||||
Guild: g,
|
||||
Cmd: command.Info,
|
||||
Args: nil,
|
||||
Cmd: childCmd.Info,
|
||||
Args: *ParseArguments("", childCmd.Info.Arguments),
|
||||
Message: message,
|
||||
})
|
||||
return
|
||||
|
|
@ -326,6 +324,7 @@ func handleCommandError(gID string, cId string, uId string) {
|
|||
if err != nil {
|
||||
log.Errorf("err sending message %s", err)
|
||||
}
|
||||
time.Sleep(5 * time.Second)
|
||||
_ = Session.ChannelMessageDelete(cId, message.ID)
|
||||
return
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,20 +11,19 @@ var (
|
|||
"hours": regexp2.MustCompile("^[0-9]+h$", 0),
|
||||
"days": regexp2.MustCompile("^[0-9]+d$", 0),
|
||||
"weeks": regexp2.MustCompile("^[0-9]+w$", 0),
|
||||
"years": regexp2.MustCompile("^[0-9]+y$", 0),
|
||||
"years": regexp2.MustCompile("[0-9]+y", 0),
|
||||
"all": regexp2.MustCompile("(([0-9]+)(s|m|h|d|w|y))", 0),
|
||||
}
|
||||
MentionStringRegexes = regex{
|
||||
"all": regexp2.MustCompile("<((@!?\\d+)|(#?\\d+)|(@&?\\d+))>", 0),
|
||||
"role": regexp2.MustCompile("<((@&?\\d+))>", 0),
|
||||
"user": regexp2.MustCompile("<((@!?\\d+))>", 0),
|
||||
"channel": regexp2.MustCompile("<((#?\\d+))>", 0),
|
||||
"id": regexp2.MustCompile("^[0-9]{18}$", 0),
|
||||
"id": regexp2.MustCompile("^[0-9]{18}", 0),
|
||||
}
|
||||
TypeGuard = regex{
|
||||
"message_url": regexp2.MustCompile("((https:\\/\\/canary.discord.com\\/channels\\/)+([0-9]{18})\\/+([0-9]{18})\\/+([0-9]{18})$)", regexp2.IgnoreCase|regexp2.Multiline),
|
||||
}
|
||||
Misc = regex{
|
||||
"quoted_string": regexp2.MustCompile("^\"[a-zA-Z0-9]+\"$", 0),
|
||||
"int": regexp2.MustCompile("\\b(0*(?:[0-9]{1,8}))\\b", 0),
|
||||
"boolean": regexp2.MustCompile("\\b((?:true|false))\\b", 0),
|
||||
}
|
||||
)
|
||||
|
|
|
|||
68
core.go
68
core.go
|
|
@ -1,11 +1,12 @@
|
|||
package framework
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
tlog "github.com/ubergeek77/tinylog"
|
||||
"os"
|
||||
"os/signal"
|
||||
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
|
@ -15,13 +16,17 @@ import (
|
|||
// This file contains the main code responsible for driving core bot functionality
|
||||
|
||||
// messageState
|
||||
// Tells discordgo the amount of mesesages to cache
|
||||
var messageState = 20
|
||||
// Tells discordgo the amount of messages to cache
|
||||
var messageState = 500
|
||||
|
||||
// log
|
||||
// The logger for the core bot
|
||||
var log = tlog.NewTaggedLogger("BotCore", tlog.NewColor("38;5;111"))
|
||||
|
||||
// dlog
|
||||
// The logger for discordgo
|
||||
var dlog = tlog.NewTaggedLogger("DG", tlog.NewColor("38;5;111"))
|
||||
|
||||
// Session
|
||||
// The Discord session, made public so commands can use it
|
||||
var Session *discordgo.Session
|
||||
|
|
@ -33,9 +38,6 @@ var Session *discordgo.Session
|
|||
// This is a boolean map, because checking its values is dead simple this way
|
||||
var botAdmins = make(map[string]bool)
|
||||
|
||||
// BotPresence
|
||||
var botPresence discordgo.GatewayStatusUpdate
|
||||
|
||||
// BotToken
|
||||
// A string of the current bot token, usually set by the main method
|
||||
// Similar to BotAdmins, this isn't saved to .json and is added programmatically
|
||||
|
|
@ -77,15 +79,32 @@ func IsCommand(trigger string) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
// SetPresence
|
||||
// Sets the presence struct after a session has been created
|
||||
func SetPresence(presence discordgo.GatewayStatusUpdate) {
|
||||
botPresence = presence
|
||||
return
|
||||
// dgoLog
|
||||
// Allows for discordgo to call tinylog
|
||||
func dgoLog(msgL, caller int, format string, a ...interface{}) {
|
||||
pc, file, line, _ := runtime.Caller(caller)
|
||||
files := strings.Split(file, "/")
|
||||
file = files[len(files)-1]
|
||||
|
||||
name := runtime.FuncForPC(pc).Name()
|
||||
fns := strings.Split(name, ".")
|
||||
name = fns[len(fns)-1]
|
||||
msg := fmt.Sprintf(format, a...)
|
||||
switch msgL {
|
||||
case discordgo.LogError:
|
||||
dlog.Errorf("%s:%d:%s() %s", file, line, name, msg)
|
||||
case discordgo.LogWarning:
|
||||
dlog.Warningf("%s:%d:%s() %s", file, line, name, msg)
|
||||
case discordgo.LogInformational:
|
||||
dlog.Infof("%s:%d:%s() %s", file, line, name, msg)
|
||||
case discordgo.LogDebug:
|
||||
dlog.Debugf("%s:%d:%s() %s", file, line, name, msg)
|
||||
}
|
||||
}
|
||||
|
||||
// Start the bot!
|
||||
// Start uberbot!
|
||||
func Start() {
|
||||
discordgo.Logger = dgoLog
|
||||
// Load all the guilds
|
||||
loadGuilds()
|
||||
|
||||
|
|
@ -103,8 +122,19 @@ func Start() {
|
|||
}
|
||||
// Setup State specific variables
|
||||
Session.State.MaxMessageCount = messageState
|
||||
Session.LogLevel = discordgo.LogWarning
|
||||
Session.SyncEvents = false
|
||||
Session.Identify.Intents = discordgo.MakeIntent(discordgo.IntentsAll)
|
||||
Session.Identify.Presence = botPresence
|
||||
// Set the bots status
|
||||
Session.Identify.Presence = discordgo.GatewayStatusUpdate{
|
||||
Game: discordgo.Activity{
|
||||
Name: "Mega Man Battle Network",
|
||||
Type: 3,
|
||||
},
|
||||
Status: "dnd",
|
||||
AFK: true,
|
||||
Since: 91879201,
|
||||
}
|
||||
// Open the session
|
||||
log.Info("Connecting to Discord...")
|
||||
err = Session.Open()
|
||||
|
|
@ -117,6 +147,7 @@ func Start() {
|
|||
|
||||
// Add the slash command handler to the list of user-defined handlers
|
||||
AddHandler(handleInteraction)
|
||||
|
||||
// Add the handlers to the session
|
||||
addHandlers()
|
||||
|
||||
|
|
@ -141,18 +172,19 @@ func Start() {
|
|||
log.Warning("You have not added any bot admins! Only moderators will be able to run commands, and permissions cannot be changed!")
|
||||
}
|
||||
// Register slash commands
|
||||
slashChannel := make(chan string)
|
||||
log.Info("Registering slash commands")
|
||||
go AddSlashCommands("833901685054242846", slashChannel)
|
||||
//slashChannel := make(chan string)
|
||||
//log.Info("Registering slash commands")
|
||||
//go AddSlashCommands("833901685054242846", slashChannel)
|
||||
|
||||
// Bot ready
|
||||
log.Info("Initialization complete! The bot is now ready.")
|
||||
|
||||
// Info about slash commands
|
||||
log.Info(<-slashChannel)
|
||||
//log.Info(<-slashChannel)
|
||||
|
||||
// -- GRACEFUL TERMINATION -- //
|
||||
|
||||
// Set up a sigterm channel so we can detect when the application receives a TERM signal
|
||||
// Set up a sigterm channel, so we can detect when the application receives a TERM signal
|
||||
sigChannel := make(chan os.Signal, 1)
|
||||
signal.Notify(sigChannel, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL, os.Interrupt, os.Kill)
|
||||
|
||||
|
|
|
|||
|
|
@ -4,8 +4,8 @@ package framework
|
|||
// Everything required for commands to pass their own handlers to discordgo
|
||||
|
||||
// handlers
|
||||
// This list stores all of the handlers that can be added to the bot
|
||||
// It's basically a passthrough for discordgo.AddHandler, but having a list
|
||||
// This list stores all the handlers that can be added to the bot
|
||||
// It's basically a passthroughs for discordgo.AddHandler, but having a list
|
||||
// allows them to be collected ahead of time and then added all at once
|
||||
var handlers []interface{}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,6 @@
|
|||
package framework
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"runtime"
|
||||
)
|
||||
|
|
@ -22,9 +21,6 @@ var slashCommandTypes = map[ArgTypeGuards]discordgo.ApplicationCommandOptionType
|
|||
//SubCmdGrp: discordgo.ApplicationCommandOptionSubCommandGroup,
|
||||
}
|
||||
|
||||
//var componentHandlers
|
||||
|
||||
//
|
||||
// getSlashCommandStruct
|
||||
// Creates a slash command struct
|
||||
// todo work on sub command stuff
|
||||
|
|
@ -44,8 +40,14 @@ func createSlashCommandStruct(info *CommandInfo) (st *discordgo.ApplicationComma
|
|||
for i, k := range info.Arguments.Keys() {
|
||||
v, _ := info.Arguments.Get(k)
|
||||
vv := v.(*ArgInfo)
|
||||
var sType discordgo.ApplicationCommandOptionType
|
||||
if val, ok := slashCommandTypes[vv.TypeGuard]; ok {
|
||||
sType = val
|
||||
} else {
|
||||
sType = slashCommandTypes["String"]
|
||||
}
|
||||
optionStruct := discordgo.ApplicationCommandOption{
|
||||
Type: slashCommandTypes[vv.TypeGuard],
|
||||
Type: sType,
|
||||
Name: k,
|
||||
Description: vv.Description,
|
||||
Required: vv.Required,
|
||||
|
|
@ -105,47 +107,8 @@ func handleInteraction(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|||
// handleInteractionCommand
|
||||
// Handles a slash command
|
||||
func handleInteractionCommand(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
||||
if i.ApplicationCommandData().Name == "rickroll-em" {
|
||||
err := s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
Content: "Operation rickroll has begun",
|
||||
Flags: 1 << 6,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
ch, err := s.UserChannelCreate(
|
||||
i.ApplicationCommandData().TargetID,
|
||||
)
|
||||
if err != nil {
|
||||
_, err = s.FollowupMessageCreate(Session.State.User.ID, i.Interaction, true, &discordgo.WebhookParams{
|
||||
Content: fmt.Sprintf("Mission failed. Cannot send a message to this user: %q", err.Error()),
|
||||
Flags: 1 << 6,
|
||||
})
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
_, err = s.ChannelMessageSend(
|
||||
ch.ID,
|
||||
fmt.Sprintf("%s sent you this: https://youtu.be/dQw4w9WgXcQ", i.Member.Mention()),
|
||||
)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return
|
||||
}
|
||||
g := getGuild(i.GuildID)
|
||||
|
||||
if g.Info.DeletePolicy {
|
||||
err := Session.ChannelMessageDelete(i.ChannelID, i.ID)
|
||||
if err != nil {
|
||||
SendErrorReport(i.GuildID, i.ChannelID, i.Member.User.ID, "Failed to delete message: "+i.ID, err)
|
||||
}
|
||||
}
|
||||
trigger := i.ApplicationCommandData().Name
|
||||
if !IsAdmin(i.Member.User.ID) {
|
||||
// Ignore the command if it is globally disabled
|
||||
|
|
@ -175,6 +138,7 @@ func handleInteractionCommand(s *discordgo.Session, i *discordgo.InteractionCrea
|
|||
if IsAdmin(i.Member.User.ID) || command.Info.Public || g.IsMod(i.Member.User.ID) {
|
||||
// Check if the command is public, or if the current user is a bot moderator
|
||||
// Bot admins supercede both checks
|
||||
|
||||
defer handleSlashCommandError(*i.Interaction)
|
||||
command.Function(&Context{
|
||||
Guild: g,
|
||||
|
|
@ -198,7 +162,7 @@ func handleMessageComponents(s *discordgo.Session, i *discordgo.InteractionCreat
|
|||
i.Message.Embeds[0].Description = content
|
||||
s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
||||
// Buttons also may update the message which they was attached to.
|
||||
// Or may just acknowledge (InteractionResponseDredeferMessageUpdate) that the event was received and not update the message.
|
||||
// Or may just acknowledge (InteractionResponseDeferredMessageUpdate) that the event was received and not update the message.
|
||||
// To update it later you need to use interaction response edit endpoint.
|
||||
Type: discordgo.InteractionResponseUpdateMessage,
|
||||
Data: &discordgo.InteractionResponseData{
|
||||
|
|
|
|||
164
util.go
164
util.go
|
|
@ -2,7 +2,9 @@ package framework
|
|||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/bwmarrin/discordgo"
|
||||
"github.com/dlclark/regexp2"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
|
@ -27,11 +29,20 @@ func RemoveItem(slice []string, delete string) []string {
|
|||
// Removes items from a slice by index
|
||||
func RemoveItems(slice []string, indexes []int) []string {
|
||||
newSlice := make([]string, len(slice))
|
||||
if len(indexes) >= len(slice) {
|
||||
return newSlice
|
||||
}
|
||||
copy(newSlice, slice)
|
||||
for _, v := range indexes {
|
||||
newSlice[v] = newSlice[len(newSlice)-1]
|
||||
newSlice[len(newSlice)-1] = ""
|
||||
newSlice = newSlice[:len(newSlice)-1]
|
||||
if len(newSlice) > v+1 && v != 0 {
|
||||
v = v - 1
|
||||
}
|
||||
//newSlice[v] = newSlice[len(newSlice)-1]
|
||||
//newSlice[len(newSlice)-1] = ""
|
||||
//newSlice = newSlice[:len(newSlice)-1]
|
||||
copy(newSlice[v:], newSlice[v+1:]) // Shift a[i+1:] left one index.
|
||||
newSlice[len(newSlice)-1] = "" // Erase last element (write zero value).
|
||||
newSlice = newSlice[:len(newSlice)-1] // Truncate slice.
|
||||
}
|
||||
return newSlice
|
||||
}
|
||||
|
|
@ -107,22 +118,6 @@ func ExtractCommand(guild *GuildInfo, message string) (*string, *string) {
|
|||
|
||||
return &trigger, &fullArgs
|
||||
} else {
|
||||
if strings.Contains(message, "uber") {
|
||||
// Same process as above prefix method, but split on a bot mention instead
|
||||
split := strings.SplitN(message, "uber", 2)
|
||||
content := strings.TrimPrefix(split[1], " ")
|
||||
// If content is null someone just sent the prefix
|
||||
if content == "" {
|
||||
return nil, nil
|
||||
}
|
||||
// Attempt to pull the trigger out of the command content by splitting on spaces
|
||||
trigger := strings.Fields(content)[0]
|
||||
fullArgs := strings.SplitN(content, trigger, 2)[1]
|
||||
fullArgs = strings.TrimPrefix(fullArgs, " ")
|
||||
// Avoids issues with strings that are case sensitive
|
||||
trigger = strings.ToLower(trigger)
|
||||
return &trigger, &fullArgs
|
||||
}
|
||||
// The bot can only be mentioned with a space
|
||||
botMention := Session.State.User.Mention() + " "
|
||||
|
||||
|
|
@ -241,37 +236,122 @@ func ParseTime(content string) (int, string) {
|
|||
return 0, "error lol"
|
||||
}
|
||||
duration := 0
|
||||
displayDuration := "Indefinite"
|
||||
|
||||
multiplier := 1
|
||||
for k, v := range TimeRegexes {
|
||||
if isMatch, _ := v.MatchString(content); isMatch {
|
||||
multiplier, _ = strconv.Atoi(EnsureNumbers(v.String()))
|
||||
switch k {
|
||||
case "seconds":
|
||||
|
||||
matches := FindAllString(TimeRegexes["all"], content)
|
||||
if len(matches) <= 0 {
|
||||
return 0, "error lol"
|
||||
}
|
||||
for _, v := range matches {
|
||||
// Grab only the letters out of the duration, to detect the unit
|
||||
muteUnit := strings.ToLower(EnsureLetters(v))
|
||||
|
||||
// Grab the number out of the duration
|
||||
// Errors shouldn't be possible due to EnsureNumbers
|
||||
multiplier, _ = strconv.Atoi(EnsureNumbers(v))
|
||||
|
||||
// Use the string next to the number to check how long the mute should be for
|
||||
switch muteUnit {
|
||||
case "s":
|
||||
duration = multiplier + duration
|
||||
displayDuration = "Second"
|
||||
case "minutes":
|
||||
case "m":
|
||||
duration = multiplier*60 + duration
|
||||
displayDuration = "Minute"
|
||||
case "hours":
|
||||
case "h":
|
||||
duration = multiplier*60*60 + duration
|
||||
displayDuration = "Hour"
|
||||
case "days":
|
||||
case "d":
|
||||
duration = multiplier*60*60*24 + duration
|
||||
displayDuration = "Day"
|
||||
case "weeks":
|
||||
case "w":
|
||||
duration = multiplier*60*60*24*7 + duration
|
||||
displayDuration = "Week"
|
||||
case "years":
|
||||
duration = multiplier*60*60*24*7*365 + duration
|
||||
displayDuration = "Year"
|
||||
case "y":
|
||||
duration = multiplier*60*60*24*7*52 + duration
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return duration, createDisplayDurationString(content)
|
||||
}
|
||||
// Plurals matter!
|
||||
if multiplier != 1 {
|
||||
displayDuration += "s"
|
||||
|
||||
func createDisplayDurationString(content string) (str string) {
|
||||
// First tokenize
|
||||
str = ""
|
||||
matches := FindAllString(TimeRegexes["all"], content)
|
||||
if matches == nil || len(matches) == 0 {
|
||||
str = "Indefinite"
|
||||
return
|
||||
}
|
||||
displayDuration = strconv.Itoa(multiplier) + " " + displayDuration
|
||||
return duration, displayDuration
|
||||
for i, v := range matches {
|
||||
prefixChar := ""
|
||||
if i+1 == len(matches) && len(matches) > 1 {
|
||||
prefixChar = " & "
|
||||
} else if i != 0 {
|
||||
prefixChar = ", "
|
||||
}
|
||||
// Grab only the letters out of the duration, to detect the unit
|
||||
muteUnit := strings.ToLower(EnsureLetters(v))
|
||||
|
||||
// Grab the number out of the duration
|
||||
// Errors shouldn't be possible due to EnsureNumbers
|
||||
multiplier, _ := strconv.Atoi(EnsureNumbers(v))
|
||||
|
||||
// clean this up
|
||||
switch muteUnit {
|
||||
case "s":
|
||||
if multiplier > 1 {
|
||||
str += prefixChar + fmt.Sprintf("%d Seconds", multiplier)
|
||||
break
|
||||
}
|
||||
str += prefixChar + "Second"
|
||||
break
|
||||
case "m":
|
||||
if multiplier > 1 {
|
||||
str += prefixChar + fmt.Sprintf("%d Minutes", multiplier)
|
||||
break
|
||||
}
|
||||
str += prefixChar + fmt.Sprintf("%d Minute", multiplier)
|
||||
break
|
||||
case "h":
|
||||
if multiplier > 1 {
|
||||
str += prefixChar + fmt.Sprintf("%d Hours", multiplier)
|
||||
break
|
||||
}
|
||||
str += prefixChar + fmt.Sprintf("%d Hours", multiplier)
|
||||
break
|
||||
case "d":
|
||||
if multiplier > 1 {
|
||||
str += prefixChar + fmt.Sprintf("%d Days", multiplier)
|
||||
break
|
||||
}
|
||||
str += prefixChar + fmt.Sprintf("%d Day", multiplier)
|
||||
break
|
||||
case "w":
|
||||
if multiplier > 1 {
|
||||
str += prefixChar + fmt.Sprintf("%d Weeks", multiplier)
|
||||
break
|
||||
}
|
||||
str += prefixChar + fmt.Sprintf("%d Week", multiplier)
|
||||
break
|
||||
case "y":
|
||||
if multiplier > 1 {
|
||||
str += prefixChar + fmt.Sprintf("%d Years", multiplier)
|
||||
break
|
||||
}
|
||||
str += prefixChar + fmt.Sprintf("%d Year", multiplier)
|
||||
break
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func FindAllString(re *regexp2.Regexp, s string) []string {
|
||||
var matches []string
|
||||
m, _ := re.FindStringMatch(s)
|
||||
for m != nil {
|
||||
matches = append(matches, m.String())
|
||||
m, _ = re.FindNextMatch(m)
|
||||
}
|
||||
return matches
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue