Bot: Added Usager, improved help

This commit is contained in:
diamondburned (Forefront) 2020-04-08 21:25:50 -07:00
parent be12849668
commit 3ee307b788
7 changed files with 113 additions and 41 deletions

View File

@ -17,6 +17,12 @@ type Parser interface {
Parse(string) error
}
// Usager is used in place of the automatically parsed struct name for Parser
// and other interfaces.
type Usager interface {
Usage() string
}
// ManualParser has a ParseContent(string) method. If the library sees
// this for an argument, it will send all of the arguments (including the
// command) into the method. If used, this should be the only argument followed
@ -145,7 +151,7 @@ func getArgumentValueFn(t reflect.Type) (*Argument, error) {
}
return &Argument{
String: t.String(),
String: fromUsager(typeI),
Type: typeI,
pointer: ptr,
fn: avfn,
@ -219,3 +225,16 @@ func quickRet(v interface{}, err error, t reflect.Type) (reflect.Value, error) {
return rv.Convert(t), nil
}
func fromUsager(typeI reflect.Type) string {
if typeI.Implements(typeIUsager) {
mt, ok := typeI.MethodByName("Usage")
if !ok {
panic("BUG: type IUsager does not implement Usage")
}
vs := mt.Func.Call([]reflect.Value{reflect.New(typeI.Elem())})
return vs[0].String()
}
s := strings.Split(typeI.String(), ".")
return s[len(s)-1]
}

View File

@ -361,38 +361,27 @@ func (ctx *Context) help(hideAdmin bool) string {
help.WriteString("\n---\n")
// Generate all commands
help.WriteString("__Commands__\n")
for _, cmd := range ctx.Commands {
if cmd.Flag.Is(AdminOnly) && hideAdmin {
continue
}
help.WriteString(indent + cmd.Command)
switch {
case len(cmd.Usage()) > 0:
help.WriteString(" " + strings.Join(cmd.Usage(), " "))
case cmd.Description != "":
help.WriteString(": " + cmd.Description)
}
help.WriteByte('\n')
}
help.WriteString("__Commands__")
help.WriteString(ctx.Subcommand.Help(indent, hideAdmin))
help.WriteByte('\n')
var subHelp = strings.Builder{}
var subcommands = ctx.Subcommands()
for _, sub := range subcommands {
if help := sub.Help(indent, hideAdmin); help != "" {
subHelp.WriteString(help)
for _, line := range strings.Split(help, "\n") {
subHelp.WriteString(indent)
subHelp.WriteString(line)
subHelp.WriteByte('\n')
}
}
}
if sub := subHelp.String(); sub != "" {
if subHelp.Len() > 0 {
help.WriteString("---\n")
help.WriteString("__Subcommands__\n")
help.WriteString(sub)
help.WriteString(subHelp.String())
}
return help.String()

View File

@ -55,6 +55,10 @@ func (e Emoji) URL() string {
}
}
func (e *Emoji) Usage() string {
return "emoji"
}
func (e *Emoji) Parse(arg string) error {
// Check if Unicode emoji
if rate.StringIsEmojiOnly(arg) {

View File

@ -0,0 +1,62 @@
package arguments
import (
"errors"
"regexp"
"github.com/diamondburned/arikawa/discord"
)
// (empty) so it matches standard links
// | OR
// canary. matches canary MessageURL
// 3 `(\d+)` for guild ID, channel ID and message ID
var Regex = regexp.MustCompile(
`https://(|ptb\.|canary\.)discordapp\.com/channels/(\d+)/(\d+)/(\d+)`,
)
// MessageURL contains info from a MessageURL
type MessageURL struct {
GuildID discord.Snowflake
ChannelID discord.Snowflake
MessageID discord.Snowflake
}
func (url *MessageURL) Parse(arg string) error {
u := ParseMessageURL(arg)
if u == nil {
return errors.New("Invalid MessageURL format.")
}
*url = *u
return nil
}
func (url *MessageURL) Usage() string {
return "https\u200b://discordapp.com/channels/\\*/\\*/\\*"
}
// ParseMessageURL parses the MessageURL into a smartlink
func ParseMessageURL(url string) *MessageURL {
ss := Regex.FindAllStringSubmatch(url, -1)
if ss == nil {
return nil
}
if len(ss) == 0 || len(ss[0]) != 5 {
return nil
}
gID, err1 := discord.ParseSnowflake(ss[0][2])
cID, err2 := discord.ParseSnowflake(ss[0][3])
mID, err3 := discord.ParseSnowflake(ss[0][4])
if err1 != nil || err2 != nil || err3 != nil {
return nil
}
return &MessageURL{
GuildID: gID,
ChannelID: cID,
MessageID: mID,
}
}

View File

@ -18,8 +18,7 @@ var (
type ChannelMention discord.Snowflake
func (m *ChannelMention) Parse(arg string) error {
return grabFirst(ChannelRegex, "channel mention",
arg, (*discord.Snowflake)(m))
return grabFirst(ChannelRegex, "channel mention", arg, (*discord.Snowflake)(m))
}
func (m *ChannelMention) Usage() string {
@ -39,8 +38,7 @@ func (m *ChannelMention) Mention() string {
type UserMention discord.Snowflake
func (m *UserMention) Parse(arg string) error {
return grabFirst(UserRegex, "user mention",
arg, (*discord.Snowflake)(m))
return grabFirst(UserRegex, "user mention", arg, (*discord.Snowflake)(m))
}
func (m *UserMention) Usage() string {
@ -60,8 +58,7 @@ func (m *UserMention) Mention() string {
type RoleMention discord.Snowflake
func (m *RoleMention) Parse(arg string) error {
return grabFirst(RoleRegex, "role mention",
arg, (*discord.Snowflake)(m))
return grabFirst(RoleRegex, "role mention", arg, (*discord.Snowflake)(m))
}
func (m *RoleMention) Usage() string {
@ -78,9 +75,7 @@ func (m *RoleMention) Mention() string {
//
func grabFirst(reg *regexp.Regexp,
item, input string, output *discord.Snowflake) error {
func grabFirst(reg *regexp.Regexp, item, input string, output *discord.Snowflake) error {
matches := reg.FindStringSubmatch(input)
if len(matches) < 2 {
return errors.New("Invalid " + item)

View File

@ -23,6 +23,7 @@ var (
typeIManP = reflect.TypeOf((*ManualParser)(nil)).Elem()
typeICusP = reflect.TypeOf((*CustomParser)(nil)).Elem()
typeIParser = reflect.TypeOf((*Parser)(nil)).Elem()
typeIUsager = reflect.TypeOf((*Usager)(nil)).Elem()
typeSetupFn = func() reflect.Type {
method, _ := reflect.TypeOf((*CanSetup)(nil)).
Elem().
@ -182,14 +183,12 @@ func (sub *Subcommand) Help(indent string, hideAdmin bool) string {
var header string
if sub.Command != "" {
header += indent + sub.Command
header += sub.Command
}
if sub.Description != "" {
if header != "" {
header += ": "
} else {
header += indent
}
header += sub.Description
@ -200,21 +199,27 @@ func (sub *Subcommand) Help(indent string, hideAdmin bool) string {
// The commands part:
var commands = ""
for _, cmd := range sub.Commands {
for i, cmd := range sub.Commands {
if cmd.Flag.Is(AdminOnly) && hideAdmin {
continue
}
commands += indent + indent + sub.Command + " " + cmd.Command
if sub.Command != "" {
commands += indent + sub.Command + " " + cmd.Command
} else {
commands += indent + cmd.Command
}
switch {
case len(cmd.Usage()) > 0:
commands += " " + strings.Join(cmd.Usage(), " ")
commands += " **" + strings.Join(cmd.Usage(), " ") + "**"
case cmd.Description != "":
commands += ": " + cmd.Description
}
commands += "\n"
if i != len(sub.Commands)-1 {
commands += "\n"
}
}
if commands == "" {

View File

@ -148,9 +148,7 @@ func (s *State) MemberColor(guildID, userID discord.Snowflake) discord.Color {
////
func (s *State) Permissions(
channelID, userID discord.Snowflake) (discord.Permissions, error) {
func (s *State) Permissions(channelID, userID discord.Snowflake) (discord.Permissions, error) {
ch, err := s.Channel(channelID)
if err != nil {
return 0, errors.Wrap(err, "Failed to get channel")