756 lines
20 KiB
Go
756 lines
20 KiB
Go
package framework
|
|
|
|
import (
|
|
"errors"
|
|
"fmt"
|
|
"github.com/QPixel/orderedmap"
|
|
"github.com/bwmarrin/discordgo"
|
|
"github.com/dlclark/regexp2"
|
|
"strconv"
|
|
"strings"
|
|
)
|
|
|
|
// Arguments.go
|
|
// File for all argument based functions which includes: parsing, creating, and more
|
|
// Woo
|
|
|
|
// pixel wrote this
|
|
|
|
// -- TypeDefs --
|
|
|
|
// ArgTypes
|
|
// A way to get type safety in AddArg
|
|
type ArgTypes string
|
|
|
|
var (
|
|
ArgOption ArgTypes = "option"
|
|
ArgContent ArgTypes = "content"
|
|
ArgFlag ArgTypes = "flag"
|
|
)
|
|
|
|
// ArgTypeGuards
|
|
// A way to get type safety in AddArg
|
|
type ArgTypeGuards string
|
|
|
|
var (
|
|
Int ArgTypeGuards = "int"
|
|
String ArgTypeGuards = "string"
|
|
Channel ArgTypeGuards = "channel"
|
|
User ArgTypeGuards = "user"
|
|
Role ArgTypeGuards = "role"
|
|
GuildArg ArgTypeGuards = "guild"
|
|
Message ArgTypeGuards = "message"
|
|
Boolean ArgTypeGuards = "bool"
|
|
Id ArgTypeGuards = "id"
|
|
SubCmd ArgTypeGuards = "subcmd"
|
|
SubCmdGrp ArgTypeGuards = "subcmdgrp"
|
|
ArrString ArgTypeGuards = "arrString"
|
|
Time ArgTypeGuards = "time"
|
|
)
|
|
|
|
// ArgInfo
|
|
// Describes a CommandInfo argument
|
|
type ArgInfo struct {
|
|
Match ArgTypes
|
|
TypeGuard ArgTypeGuards
|
|
Description string
|
|
Required bool
|
|
Flag bool
|
|
DefaultOption string
|
|
Choices []string
|
|
Regex *regexp2.Regexp
|
|
}
|
|
|
|
// CommandArg
|
|
// Describes what a cmd ctx will receive
|
|
type CommandArg struct {
|
|
info ArgInfo
|
|
Value interface{}
|
|
}
|
|
|
|
// Arguments
|
|
// Type of the arguments field in the command ctx
|
|
type Arguments map[string]CommandArg
|
|
|
|
// -- Command Configuration --
|
|
|
|
// CreateCommandInfo
|
|
// Creates a pointer to a CommandInfo
|
|
func CreateCommandInfo(trigger string, description string, public bool, group Group) *CommandInfo {
|
|
cI := &CommandInfo{
|
|
Aliases: nil,
|
|
Arguments: orderedmap.New(),
|
|
Description: description,
|
|
Group: group,
|
|
Public: public,
|
|
IsTyping: false,
|
|
Trigger: trigger,
|
|
}
|
|
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) {
|
|
if !isParent {
|
|
cI.IsChild = true
|
|
}
|
|
cI.IsParent = isParent
|
|
cI.ParentID = parentID
|
|
}
|
|
|
|
//AddCmdAlias
|
|
// Adds a list of strings as aliases for the command
|
|
func (cI *CommandInfo) AddCmdAlias(aliases []string) *CommandInfo {
|
|
if len(aliases) < 1 {
|
|
return cI
|
|
}
|
|
cI.Aliases = aliases
|
|
return cI
|
|
}
|
|
|
|
// AddArg
|
|
// Adds an arg to the CommandInfo
|
|
func (cI *CommandInfo) AddArg(argument string, typeGuard ArgTypeGuards, match ArgTypes, description string, required bool, defaultOption string) *CommandInfo {
|
|
cI.Arguments.Set(argument, &ArgInfo{
|
|
TypeGuard: typeGuard,
|
|
Description: description,
|
|
Required: required,
|
|
Match: match,
|
|
DefaultOption: defaultOption,
|
|
Choices: nil,
|
|
Regex: nil,
|
|
})
|
|
return cI
|
|
}
|
|
|
|
// AddFlagArg
|
|
// Adds a flag arg, which is a special type of argument
|
|
// This type of argument allows for the user to place the "phrase" (e.g: --debug) anywhere
|
|
// in the command string and the parser will find it.
|
|
func (cI *CommandInfo) AddFlagArg(flag string, typeGuard ArgTypeGuards, match ArgTypes, description string, required bool, defaultOption string) *CommandInfo {
|
|
regexString := flag
|
|
if match == ArgOption {
|
|
// Currently, it only supports a limited character set.
|
|
// todo figure out how to detect any character
|
|
regexString = fmt.Sprintf("--%s (([a-zA-Z0-9:/.]+)|(\"[a-zA-Z0-9:/. ]+\"))", flag)
|
|
} else {
|
|
regexString = fmt.Sprintf("--%s", flag)
|
|
}
|
|
regex, err := regexp2.Compile(regexString, 0)
|
|
if err != nil {
|
|
log.Fatalf("Unable to create regex for flag on command %s flag: %s", cI.Trigger, flag)
|
|
}
|
|
cI.Arguments.Set(flag, &ArgInfo{
|
|
Description: description,
|
|
Required: required,
|
|
Flag: true,
|
|
Match: match,
|
|
TypeGuard: typeGuard,
|
|
DefaultOption: defaultOption,
|
|
Regex: regex,
|
|
})
|
|
return cI
|
|
}
|
|
|
|
// AddChoices
|
|
// Adds SubCmd choices
|
|
func (cI *CommandInfo) AddChoices(arg string, choices []string) *CommandInfo {
|
|
v, ok := cI.Arguments.Get(arg)
|
|
if ok {
|
|
vv := v.(*ArgInfo)
|
|
vv.Choices = choices
|
|
cI.Arguments.Set(arg, vv)
|
|
} else {
|
|
log.Errorf("Unable to get argument %s in AddChoices", arg)
|
|
return cI
|
|
}
|
|
return cI
|
|
}
|
|
|
|
func (cI *CommandInfo) SetTyping(isTyping bool) *CommandInfo {
|
|
cI.IsTyping = isTyping
|
|
return cI
|
|
}
|
|
|
|
//todo subcommand stuff
|
|
//// BindToChoice
|
|
//// Bind an arg to choice (subcmd)
|
|
//func (cI *CommandInfo) BindToChoice(arg string, choice string) {
|
|
//
|
|
//}
|
|
|
|
// CreateAppOptSt
|
|
// Creates an ApplicationOptionsStruct for all the args.
|
|
func (cI *CommandInfo) CreateAppOptSt() *discordgo.ApplicationCommandOption {
|
|
return &discordgo.ApplicationCommandOption{}
|
|
}
|
|
|
|
// -- Argument Parser --
|
|
|
|
// ParseArguments
|
|
// Version two of the argument parser
|
|
func ParseArguments(args string, infoArgs *orderedmap.OrderedMap) *Arguments {
|
|
ar := make(Arguments)
|
|
|
|
if args == "" || len(infoArgs.Keys()) < 1 {
|
|
return &ar
|
|
}
|
|
// Split string on spaces to get every "phrase"
|
|
|
|
// bool to parse content strings
|
|
moreContent := false
|
|
// Keys of infoArgs
|
|
k := infoArgs.Keys()
|
|
var modK []string
|
|
// First find all flags in the string.
|
|
splitString, ar, modK := findAllFlags(args, k, infoArgs, &ar)
|
|
// Find all the option args (e.g. single 'phrases' or quoted strings)
|
|
// Then return the currentPos, so we can index k and find remaining keys.
|
|
// Also return a modified Arguments struct
|
|
|
|
ar, moreContent, splitString, modK = findAllOptionArgs(splitString, modK, infoArgs, &ar)
|
|
|
|
// If there is more content, lets find it
|
|
if moreContent == true {
|
|
v, ok := infoArgs.Get(modK[0])
|
|
if !ok {
|
|
return &ar
|
|
}
|
|
vv := v.(*ArgInfo)
|
|
commandContent, _ := createContentString(splitString, 0)
|
|
ar[modK[0]] = CommandArg{
|
|
info: *vv,
|
|
Value: commandContent,
|
|
}
|
|
return &ar
|
|
// Else return the args struct
|
|
} else {
|
|
return &ar
|
|
}
|
|
}
|
|
|
|
/* Argument Parsing Helpers */
|
|
|
|
func createContentString(splitString []string, currentPos int) (string, int) {
|
|
str := ""
|
|
for i := currentPos; i < len(splitString); i++ {
|
|
str += splitString[i] + " "
|
|
currentPos = i
|
|
}
|
|
return strings.TrimSuffix(str, " "), currentPos
|
|
}
|
|
|
|
// Finds all the 'option' type args
|
|
func findAllOptionArgs(argString []string, keys []string, infoArgs *orderedmap.OrderedMap, args *Arguments) (Arguments, bool, []string, []string) {
|
|
if len(keys) == 0 || keys == nil {
|
|
return *args, false, []string{}, []string{}
|
|
}
|
|
modifiedArgString := ""
|
|
var modKeys []string
|
|
var indexes []int
|
|
|
|
// (semi) Brute force method
|
|
// First lets find all required args
|
|
currentPos := 0
|
|
for i, v := range keys {
|
|
// error handling
|
|
iA, ok := infoArgs.Get(v)
|
|
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)
|
|
if vv.Required {
|
|
if vv.TypeGuard != String {
|
|
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 {
|
|
(*args)[v] = handleArgOption(vv.DefaultOption, *vv)
|
|
indexes = append(indexes, i)
|
|
continue
|
|
}
|
|
} else {
|
|
break
|
|
}
|
|
}
|
|
// Remove already found keys and clear the index list
|
|
// We also reset some values that we reuse
|
|
//if
|
|
modKeys = RemoveItems(keys, indexes)
|
|
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 i, v := range modKeys {
|
|
// error handling
|
|
iA, ok := infoArgs.Get(v)
|
|
if !ok {
|
|
err := errors.New(fmt.Sprintf("Unable to find map relating to key: %s", v))
|
|
SendErrorReport("", "", "", "Argument Parsing error", err)
|
|
continue
|
|
}
|
|
vv := iA.(*ArgInfo)
|
|
// If we find an arg that is required send an error and return
|
|
if vv.Required {
|
|
err := errors.New(fmt.Sprintf("Found a required arg where there is supposed to be none %s", v))
|
|
SendErrorReport("", "", "", "Argument Parsing error", err)
|
|
break
|
|
}
|
|
if vv.Match == ArgContent {
|
|
modKeys = RemoveItems(modKeys, indexes)
|
|
return *args, true, argString, modKeys
|
|
}
|
|
// Break early if current pos is the length of the array
|
|
if currentPos == len(argString) {
|
|
break
|
|
}
|
|
if vv.TypeGuard != String {
|
|
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 {
|
|
|
|
}
|
|
}
|
|
//
|
|
return *args, false, createSplitString(modifiedArgString), modKeys
|
|
}
|
|
|
|
func findTypeGuard(input string, array []string, typeguard ArgTypeGuards) (string, []string) {
|
|
switch typeguard {
|
|
case Int:
|
|
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
|
|
case Channel:
|
|
if match, isMatch := MentionStringRegexes["channel"].FindStringMatch(input); isMatch == nil && match != nil {
|
|
return match.String(), RemoveItem(array, match.String())
|
|
} else if match, isMatch := MentionStringRegexes["id"].FindStringMatch(input); isMatch == nil && match != nil {
|
|
return match.String(), RemoveItem(array, match.String())
|
|
}
|
|
return "", array
|
|
case Role:
|
|
if match, isMatch := MentionStringRegexes["role"].FindStringMatch(input); isMatch == nil && match != nil {
|
|
return match.String(), RemoveItem(array, match.String())
|
|
} else if match, isMatch := MentionStringRegexes["id"].FindStringMatch(input); isMatch == nil && match != nil {
|
|
return match.String(), RemoveItem(array, match.String())
|
|
}
|
|
return "", array
|
|
case User:
|
|
if match, isMatch := MentionStringRegexes["user"].FindStringMatch(input); isMatch == nil && match != nil {
|
|
return match.String(), RemoveItem(array, match.String())
|
|
} else if match, isMatch := MentionStringRegexes["id"].FindStringMatch(input); isMatch == nil && match != nil {
|
|
return match.String(), RemoveItem(array, match.String())
|
|
}
|
|
return "", array
|
|
case ArrString:
|
|
if match, isMatch := TypeGuard["arrString"].FindStringMatch(input); isMatch == nil && match != nil {
|
|
return match.String(), RemoveItem(array, match.String())
|
|
}
|
|
return "", array
|
|
case Message:
|
|
if match, isMatch := TypeGuard["message_url"].FindStringMatch(input); isMatch == nil && match != nil {
|
|
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) {
|
|
modifiedArgString := argString
|
|
var indexes []int
|
|
var modKeys []string
|
|
for index, a := range keys {
|
|
v, _ := infoArgs.Get(a)
|
|
vv := v.(*ArgInfo)
|
|
// Skip because the argument has no flag
|
|
if !vv.Flag {
|
|
continue
|
|
}
|
|
// Use the compiled regex to search the arg string for a matching result.
|
|
match, err := vv.Regex.FindStringMatch(argString)
|
|
// Error handling/no match
|
|
if err != nil || match == nil {
|
|
if vv.Match == ArgOption {
|
|
(*args)[a] = handleArgOption(vv.DefaultOption, *vv)
|
|
} else {
|
|
(*args)[a] = CommandArg{info: *vv, Value: "false"}
|
|
}
|
|
// Set the modified arg string to the mod string
|
|
indexes = append(indexes, index)
|
|
continue
|
|
}
|
|
|
|
// Check to see if the flag is a string 'option' or a boolean 'flag'
|
|
if vv.Match == ArgOption {
|
|
val := strings.Trim(strings.SplitN(match.String(), " ", 2)[1], "\"")
|
|
if checkTypeGuard(val, vv.TypeGuard) {
|
|
(*args)[a] = handleArgOption(val, *vv)
|
|
}
|
|
} else if vv.Match == ArgFlag {
|
|
(*args)[a] = CommandArg{info: *vv, Value: "true"}
|
|
} // todo figure out if indexes need to put an else statement here
|
|
|
|
// Replace all reference to the flag in the string.
|
|
modString, err := vv.Regex.Replace(modifiedArgString, "", -1, -1)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
// Set the modified arg string to the mod string
|
|
modifiedArgString = modString
|
|
indexes = append(indexes, index)
|
|
}
|
|
if len(indexes) > 0 {
|
|
// set keys to nil if flags have already gotten all the args
|
|
if len(indexes) == len(keys) {
|
|
modKeys = nil
|
|
return []string{}, *args, keys
|
|
}
|
|
modKeys = RemoveItems(keys, indexes)
|
|
}
|
|
if modifiedArgString == "" {
|
|
modifiedArgString = argString
|
|
}
|
|
if len(modKeys) == 0 || modKeys == nil {
|
|
modKeys = keys
|
|
}
|
|
return createSplitString(modifiedArgString), *args, modKeys
|
|
}
|
|
|
|
// Creates a "split" string (array of strings that is split off of spaces
|
|
func createSplitString(argString string) []string {
|
|
splitStr := strings.SplitAfter(argString, " ")
|
|
var newSplitStr []string
|
|
quotedStringBuffer := ""
|
|
isQuotedString := false
|
|
for _, v := range splitStr {
|
|
if v == "" || v == " " {
|
|
continue
|
|
}
|
|
// Checks to see if the string is a quoted argument.
|
|
// If so, it will combine it into one string
|
|
if strings.Contains(v, "\"") || isQuotedString {
|
|
if strings.HasSuffix(strings.Trim(v, " "), "\"") {
|
|
// Trim quotes and trim space suffix
|
|
quotedStringBuffer = strings.TrimSuffix(strings.Trim(quotedStringBuffer+strings.Trim(v, " "), "\""), " ")
|
|
newSplitStr = append(newSplitStr, quotedStringBuffer)
|
|
|
|
isQuotedString = false
|
|
quotedStringBuffer = ""
|
|
continue
|
|
}
|
|
isQuotedString = true
|
|
quotedStringBuffer = quotedStringBuffer + v
|
|
continue
|
|
} else {
|
|
// If the string suffix contains a whitespace character, we need to remove that
|
|
v = strings.TrimSuffix(v, " ")
|
|
newSplitStr = append(newSplitStr, v)
|
|
}
|
|
}
|
|
return newSplitStr
|
|
}
|
|
|
|
func handleArgOption(str string, info ArgInfo) CommandArg {
|
|
return CommandArg{
|
|
info: info,
|
|
Value: str,
|
|
}
|
|
}
|
|
|
|
func checkTypeGuard(str string, typeguard ArgTypeGuards) bool {
|
|
switch typeguard {
|
|
case String:
|
|
return true
|
|
case Int:
|
|
if _, err := strconv.Atoi(str); err == nil {
|
|
return true
|
|
}
|
|
return false
|
|
case Boolean:
|
|
if _, err := strconv.ParseBool(str); err == nil {
|
|
return true
|
|
}
|
|
case Channel:
|
|
if isMatch, _ := MentionStringRegexes["channel"].MatchString(str); isMatch {
|
|
return true
|
|
} else if isMatch, _ := MentionStringRegexes["id"].MatchString(str); isMatch {
|
|
return true
|
|
}
|
|
case Role:
|
|
if isMatch, _ := MentionStringRegexes["role"].MatchString(str); isMatch {
|
|
return true
|
|
} else if isMatch, _ := MentionStringRegexes["id"].MatchString(str); isMatch {
|
|
return true
|
|
}
|
|
case User:
|
|
if isMatch, _ := MentionStringRegexes["user"].MatchString(str); isMatch {
|
|
return true
|
|
} else if isMatch, _ := MentionStringRegexes["id"].MatchString(str); isMatch {
|
|
return true
|
|
}
|
|
return false
|
|
case ArrString:
|
|
if isMatch, _ := TypeGuard["arrString"].MatchString(str); isMatch {
|
|
return true
|
|
}
|
|
return false
|
|
case Message:
|
|
if isMatch, _ := TypeGuard["message_url"].MatchString(str); isMatch {
|
|
return true
|
|
}
|
|
return false
|
|
}
|
|
return false
|
|
}
|
|
|
|
/* Argument Casting s*/
|
|
|
|
// StringValue
|
|
// Returns the string value of the arg
|
|
func (ag CommandArg) StringValue() string {
|
|
if ag.Value == nil {
|
|
return ""
|
|
}
|
|
if v, ok := ag.Value.(string); ok {
|
|
return v
|
|
} else if v := strconv.FormatFloat(ag.Value.(float64), 'f', 2, 64); v != "" {
|
|
return v
|
|
} else if v = strconv.FormatBool(ag.Value.(bool)); v != "" {
|
|
return v
|
|
}
|
|
return ""
|
|
}
|
|
|
|
// Int64Value
|
|
// Returns the int64 value of the arg
|
|
func (ag CommandArg) Int64Value() int64 {
|
|
if ag.Value == nil {
|
|
return 0
|
|
}
|
|
if v, ok := ag.Value.(float64); ok {
|
|
return int64(v)
|
|
} else if v, err := strconv.ParseInt(ag.StringValue(), 10, 64); err == nil {
|
|
return v
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// IntValue
|
|
// Returns the int value of the arg
|
|
func (ag CommandArg) IntValue() int {
|
|
if ag.Value == nil {
|
|
return 0
|
|
}
|
|
if v, ok := ag.Value.(float64); ok {
|
|
return int(v)
|
|
} else if v, err := strconv.Atoi(ag.StringValue()); err == nil {
|
|
return v
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// FloatValue
|
|
// Returns the int value of the arg
|
|
func (ag CommandArg) FloatValue() float64 {
|
|
if ag.Value == nil {
|
|
return 0.0
|
|
}
|
|
if v, ok := ag.Value.(float64); ok {
|
|
return v
|
|
} else if v, err := strconv.ParseFloat(ag.StringValue(), 64); err == nil {
|
|
return v
|
|
}
|
|
return 0.0
|
|
}
|
|
|
|
// BoolValue
|
|
// Returns the int value of the arg
|
|
func (ag CommandArg) BoolValue() bool {
|
|
if ag.Value == nil {
|
|
return false
|
|
}
|
|
stringValue := ag.StringValue()
|
|
if v, err := strconv.ParseBool(stringValue); err == nil {
|
|
return v
|
|
}
|
|
return false
|
|
}
|
|
|
|
// ChannelValue is a utility function for casting value to a channel struct
|
|
// Returns a channel struct, partial channel struct, or a nil value
|
|
func (ag CommandArg) ChannelValue(s *discordgo.Session) (*discordgo.Channel, error) {
|
|
chanID := ag.StringValue()
|
|
if chanID == "" {
|
|
return &discordgo.Channel{ID: chanID}, errors.New("no channel id")
|
|
}
|
|
|
|
if s == nil {
|
|
return &discordgo.Channel{ID: chanID}, errors.New("no session")
|
|
}
|
|
cleanedId := CleanId(chanID)
|
|
|
|
if cleanedId == "" {
|
|
return &discordgo.Channel{ID: chanID}, errors.New("not an id")
|
|
}
|
|
ch, err := s.State.Channel(cleanedId)
|
|
|
|
if err != nil {
|
|
ch, err = s.Channel(cleanedId)
|
|
if err != nil {
|
|
return &discordgo.Channel{ID: chanID}, errors.New("could not find channel")
|
|
}
|
|
}
|
|
return ch, nil
|
|
}
|
|
|
|
// MemberValue is a utility function for casting value to a member struct
|
|
// Returns a user struct, partial user struct, or a nil value
|
|
func (ag CommandArg) MemberValue(s *discordgo.Session, g string) (*discordgo.Member, error) {
|
|
userID := ag.StringValue()
|
|
if userID == "" {
|
|
return &discordgo.Member{
|
|
GuildID: g,
|
|
User: &discordgo.User{
|
|
ID: userID,
|
|
},
|
|
}, errors.New("no userid")
|
|
}
|
|
cleanedId := CleanId(userID)
|
|
if cleanedId == "" {
|
|
return &discordgo.Member{
|
|
GuildID: g,
|
|
User: &discordgo.User{
|
|
ID: userID,
|
|
},
|
|
}, errors.New("invalid userid")
|
|
}
|
|
if s == nil {
|
|
return &discordgo.Member{
|
|
GuildID: g,
|
|
User: &discordgo.User{
|
|
ID: cleanedId,
|
|
},
|
|
}, errors.New("session is nil")
|
|
}
|
|
u, err := s.State.Member(g, cleanedId)
|
|
|
|
if err != nil {
|
|
u, err = s.GuildMember(g, cleanedId)
|
|
if err != nil {
|
|
return &discordgo.Member{
|
|
GuildID: g,
|
|
User: &discordgo.User{
|
|
ID: userID,
|
|
},
|
|
}, errors.New("cant find user")
|
|
}
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
// UserValue is a utility function for casting value to a member struct
|
|
// Returns a user struct, partial user struct, or a nil value
|
|
func (ag CommandArg) UserValue(s *discordgo.Session) (*discordgo.User, error) {
|
|
userID := ag.StringValue()
|
|
if userID == "" {
|
|
return &discordgo.User{
|
|
ID: userID,
|
|
}, errors.New("no userid")
|
|
}
|
|
cleanedId := CleanId(userID)
|
|
if cleanedId == "" {
|
|
return &discordgo.User{
|
|
ID: userID,
|
|
}, errors.New("invalid userid")
|
|
}
|
|
if s == nil {
|
|
return &discordgo.User{
|
|
ID: userID,
|
|
}, errors.New("session is nil")
|
|
}
|
|
u, err := s.User(cleanedId)
|
|
|
|
if err != nil {
|
|
return &discordgo.User{
|
|
|
|
ID: cleanedId,
|
|
}, errors.New("cant find user")
|
|
}
|
|
return u, nil
|
|
}
|
|
|
|
// RoleValue is a utility function for casting value to a user struct
|
|
// Returns a user struct, partial user struct, or a nil value
|
|
func (ag CommandArg) RoleValue(s *discordgo.Session, gID string) (*discordgo.Role, error) {
|
|
roleID := ag.StringValue()
|
|
if roleID == "" {
|
|
return nil, errors.New("unable to find roleid")
|
|
}
|
|
cleanedId := CleanId(roleID)
|
|
if cleanedId == "" {
|
|
return &discordgo.Role{
|
|
ID: roleID,
|
|
}, errors.New("invalid roleid")
|
|
}
|
|
if s == nil || gID == "" {
|
|
return &discordgo.Role{ID: cleanedId}, errors.New("no session (and/or) guild id")
|
|
}
|
|
r, err := s.State.Role(cleanedId, gID)
|
|
|
|
if err != nil {
|
|
roles, err := s.GuildRoles(gID)
|
|
if err == nil {
|
|
for _, r = range roles {
|
|
if r.ID == cleanedId {
|
|
return r, nil
|
|
}
|
|
}
|
|
}
|
|
return &discordgo.Role{ID: roleID}, errors.New("could not find role")
|
|
}
|
|
return r, nil
|
|
}
|