2020-10-14 20:22:11 +00:00
|
|
|
package commands
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"encoding/json"
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"io/ioutil"
|
|
|
|
"strings"
|
|
|
|
|
2020-12-20 05:44:26 +00:00
|
|
|
"github.com/diamondburned/arikawa/v2/bot/extras/arguments"
|
|
|
|
"github.com/diamondburned/arikawa/v2/discord"
|
2020-10-14 20:22:11 +00:00
|
|
|
"github.com/diamondburned/cchat-discord/internal/discord/channel/shared"
|
|
|
|
"github.com/pkg/errors"
|
|
|
|
)
|
|
|
|
|
|
|
|
type Commands []Command
|
|
|
|
|
|
|
|
// Help renders the help text.
|
|
|
|
func (cmds Commands) Help() []byte {
|
|
|
|
var builder bytes.Buffer
|
|
|
|
for _, cmd := range cmds {
|
|
|
|
cmd.writeHelp(&builder)
|
|
|
|
builder.WriteString("\n")
|
|
|
|
}
|
|
|
|
|
|
|
|
return builder.Bytes()
|
|
|
|
}
|
|
|
|
|
|
|
|
// Run runs a command with the given words. It errors out if the command is not
|
|
|
|
// found.
|
2020-12-17 08:01:58 +00:00
|
|
|
func (cmds Commands) Run(ch shared.Channel, words []string) ([]byte, error) {
|
2020-10-14 20:22:11 +00:00
|
|
|
if words[0] == "help" {
|
|
|
|
return cmds.Help(), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
cmd := cmds.FindExact(words[0])
|
|
|
|
if cmd == nil {
|
|
|
|
return nil, fmt.Errorf("unknown command %q, refer to help", words[0])
|
|
|
|
}
|
|
|
|
|
2020-10-15 06:28:50 +00:00
|
|
|
return cmd.RunFunc(ch, words[1:])
|
2020-10-14 20:22:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// FindExact finds the exact command. It returns a pointer to the command
|
|
|
|
// directly in the slice if found. If not, nil is returned.
|
|
|
|
func (cmds Commands) FindExact(name string) *Command {
|
|
|
|
for i, cmd := range cmds {
|
|
|
|
if cmd.Name == name {
|
|
|
|
return &cmds[i]
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// Find finds commands with the given name. The searching is case insensitive.
|
|
|
|
func (cmds Commands) Find(name string) []Command {
|
|
|
|
name = strings.ToLower(name)
|
|
|
|
|
|
|
|
var found []Command
|
|
|
|
|
|
|
|
for _, cmd := range cmds {
|
|
|
|
if strings.HasPrefix(strings.ToLower(cmd.Name), name) {
|
|
|
|
// Micro-optimization.
|
|
|
|
if found == nil {
|
|
|
|
found = make([]Command, 1, len(cmds))
|
|
|
|
found[0] = cmd
|
|
|
|
} else {
|
|
|
|
found = append(found, cmd)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return found
|
|
|
|
}
|
|
|
|
|
|
|
|
// World is a list of commands.
|
|
|
|
var World = Commands{
|
|
|
|
{
|
|
|
|
Name: "send-embed",
|
|
|
|
Args: Arguments{"-t title", "-c color", "description"},
|
|
|
|
Desc: "Send a basic embed to the current channel",
|
2020-12-17 08:01:58 +00:00
|
|
|
RunFunc: func(ch shared.Channel, argv []string) ([]byte, error) {
|
2020-10-14 20:22:11 +00:00
|
|
|
var embed discord.Embed
|
|
|
|
var color uint // no Uint32Var
|
|
|
|
|
|
|
|
fs := flag.NewFlagSet("send-embed", 0)
|
|
|
|
fs.SetOutput(ioutil.Discard)
|
|
|
|
fs.StringVar(&embed.Title, "t", "", "Embed title")
|
|
|
|
fs.UintVar(&color, "c", 0xFFFFFF, "Embed color")
|
|
|
|
|
|
|
|
if err := fs.Parse(argv); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2020-10-15 06:28:50 +00:00
|
|
|
embed.Description = fs.Arg(0)
|
2020-10-14 20:22:11 +00:00
|
|
|
embed.Color = discord.Color(color)
|
|
|
|
|
|
|
|
m, err := ch.State.SendEmbed(ch.ID, embed)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to send embed")
|
|
|
|
}
|
|
|
|
|
|
|
|
return bprintf("Message %d sent at %v.", m.ID, m.Timestamp.Time()), nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "info",
|
|
|
|
Desc: "Print information as JSON",
|
2020-12-17 08:01:58 +00:00
|
|
|
RunFunc: func(ch shared.Channel, argv []string) ([]byte, error) {
|
2020-10-14 20:22:11 +00:00
|
|
|
channel, err := ch.State.Channel(ch.ID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to get channel")
|
|
|
|
}
|
|
|
|
|
|
|
|
b, err := json.MarshalIndent(channel, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to marshal to JSON")
|
|
|
|
}
|
|
|
|
|
|
|
|
return b, nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "list-channels",
|
|
|
|
Desc: "Print all channels of this guild and their topics",
|
2020-12-17 08:01:58 +00:00
|
|
|
RunFunc: func(ch shared.Channel, argv []string) ([]byte, error) {
|
2020-10-14 20:22:11 +00:00
|
|
|
channels, err := ch.State.Channels(ch.GuildID)
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to get channels")
|
|
|
|
}
|
|
|
|
|
|
|
|
var buf bytes.Buffer
|
|
|
|
for _, ch := range channels {
|
|
|
|
fmt.Fprintf(&buf, "#%s (NSFW %t): %s\n", ch.Name, ch.NSFW, ch.Topic)
|
|
|
|
}
|
|
|
|
|
|
|
|
return buf.Bytes(), nil
|
|
|
|
},
|
|
|
|
},
|
|
|
|
{
|
|
|
|
Name: "presence",
|
|
|
|
Args: Arguments{"mention:user"},
|
|
|
|
Desc: "Print JSON of a member/user's presence state",
|
2020-12-17 08:01:58 +00:00
|
|
|
RunFunc: func(ch shared.Channel, argv []string) ([]byte, error) {
|
2020-10-14 20:22:11 +00:00
|
|
|
if err := assertArgc(argv, 1); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
var user arguments.UserMention
|
|
|
|
if err := user.Parse(argv[0]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
p, err := ch.State.Presence(ch.GuildID, user.ID())
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return renderJSON(p)
|
|
|
|
},
|
|
|
|
},
|
2020-10-15 06:28:50 +00:00
|
|
|
{
|
|
|
|
Name: "member",
|
|
|
|
Args: Arguments{"mention:user"},
|
|
|
|
Desc: "Print JSON of a member/user's member state",
|
2020-12-17 08:01:58 +00:00
|
|
|
RunFunc: func(ch shared.Channel, argv []string) ([]byte, error) {
|
2020-10-15 06:28:50 +00:00
|
|
|
if err := assertArgc(argv, 1); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
if !ch.GuildID.IsValid() {
|
|
|
|
return nil, errors.New("channel not in guild")
|
|
|
|
}
|
|
|
|
|
|
|
|
var user arguments.UserMention
|
|
|
|
if err := user.Parse(argv[0]); err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
2021-01-06 05:02:54 +00:00
|
|
|
m, err := ch.State.Cabinet.Member(ch.GuildID, user.ID())
|
2020-10-15 06:28:50 +00:00
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
return renderJSON(m)
|
|
|
|
},
|
|
|
|
},
|
2020-10-14 20:22:11 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func assertArgc(argv []string, argc int) error {
|
|
|
|
switch {
|
|
|
|
case len(argv) > argc:
|
|
|
|
return errors.New("too many arguments")
|
|
|
|
case len(argv) < argc:
|
|
|
|
return errors.New("too few arguments")
|
|
|
|
default:
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func renderJSON(v interface{}) ([]byte, error) {
|
|
|
|
b, err := json.MarshalIndent(v, "", " ")
|
|
|
|
if err != nil {
|
|
|
|
return nil, errors.Wrap(err, "failed to marshal to JSON")
|
|
|
|
}
|
|
|
|
return b, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
// bprintf is sprintf but for byte slices.
|
|
|
|
func bprintf(f string, v ...interface{}) []byte {
|
|
|
|
var buf bytes.Buffer
|
|
|
|
fmt.Fprintf(&buf, f, v...)
|
|
|
|
return buf.Bytes()
|
|
|
|
}
|