...
 
Commits (3)
package main
import (
"encoding/json"
"github.com/bwmarrin/discordgo" // for running the bot
log "github.com/sirupsen/logrus" // logging suite
)
......@@ -17,17 +18,46 @@ type Command interface {
// commonly used to reply to or otherwise process a message.
// Returns an error that the handler can log.
Run(*discordgo.Session, *discordgo.MessageCreate) error
// Checks if the command can be used on a given guild and channel ID.
Check(string, string) bool
}
// Checks if a list contains something.
func listContains(list []string, id string) bool {
for _, listid := range list {
if listid == id {
return true
}
}
return false
}
// BaseCommand is the base command structure.
// It also serves as the schema
type BaseCommand struct {
Command
// Human-readable name of the command, for logging purposes.
// In the handler, This is retrieved through GetName().
Name string
Name string `json:"name"`
// Human-readable type of the command, for logging purposes.
// In the handler, This is retrieved through GetType().
Type string
Type string `json:"type"`
// Optional channel whitelist.
// If set, only channels in this list can use this command.
ChannelWhitelist []string `json:"channelwhitelist"`
// Optional channel blacklist.
// If set, channels in this list cannot use this command.
ChannelBlacklist []string `json:"channelblacklist"`
// Optional guild blacklist.
// If set, guilds in this list cannot use this command.
GuildWhitelist []string `json:"guildblacklist"`
// Optional guild blacklist.
// If set, guilds in this list cannot use this command.
GuildBlacklist []string `json:"guildwhitelist"`
// JSON-encoded list of options for the command.
// This is intended to be parsed and handled by the "NewXCommand" factory function
// after utilizing this BaseCommand.
Options json.RawMessage
}
// GetName prints the set name of the BaseCommand.
......@@ -40,6 +70,23 @@ func (b BaseCommand) GetType() string {
return b.Type
}
// Check ensures the command passes whitelist and blacklist checks.
func (b BaseCommand) Check(guildID, channelID string) bool {
if len(b.ChannelWhitelist) > 0 && !listContains(b.ChannelWhitelist, channelID) {
return false
}
if len(b.ChannelBlacklist) > 0 && listContains(b.ChannelBlacklist, channelID) {
return false
}
if len(b.GuildWhitelist) > 0 && !listContains(b.GuildWhitelist, guildID) {
return false
}
if len(b.GuildBlacklist) > 0 && listContains(b.GuildBlacklist, guildID) {
return false
}
return true
}
// MessageHandler handles Discordgo messages, testing them against Valerius-compatible commands.
// The struct itself only contains the list of commands.
type MessageHandler struct {
......@@ -78,9 +125,9 @@ func (c *MessageHandler) Handle(bot *discordgo.Session, evt *discordgo.MessageCr
for _, cmd := range c.commands {
// Handle it as a goroutine to speed things up
go func(cmd Command) {
// If the test checks out,
if cmd.Test(bot, evt) {
// log it,
// Test the command
if cmd.Check(evt.Message.GuildID, evt.Message.ChannelID) && cmd.Test(bot, evt) {
// If it passed, log it,
author := *evt.Message.Author
log.WithFields(log.Fields{
"text": evt.Message.Content,
......@@ -107,7 +154,8 @@ func (c *MessageHandler) Handle(bot *discordgo.Session, evt *discordgo.MessageCr
}
}
// Add commands to the handler.
func (c *MessageHandler) Add(cmds ...Command) {
c.commands = append(c.commands, cmds...)
// Add commands to the handler, validating whitelists/blacklists as well.
func (c *MessageHandler) Add(cmd Command) error {
c.commands = append(c.commands, cmd)
return nil
}
......@@ -33,7 +33,7 @@ type IASIPConfig struct {
}
// NewIASIPCommand generates a new IASIPCommand.
func NewIASIPCommand(config CommandConfig) (cmd IASIPCommand, err error) {
func NewIASIPCommand(config BaseCommand) (cmd IASIPCommand, err error) {
options := IASIPConfig{}
err = json.Unmarshal(config.Options, &options)
if err != nil {
......@@ -49,10 +49,7 @@ func NewIASIPCommand(config CommandConfig) (cmd IASIPCommand, err error) {
}
options.TriggerRegex = regex
cmd = IASIPCommand{
BaseCommand: BaseCommand{
Name: config.Name,
Type: config.Type,
},
BaseCommand: config,
IASIPConfig: options,
}
return cmd, nil
......
......@@ -59,7 +59,7 @@ type PingPongConfig struct {
}
// NewPingPongCommand creates a new PingPongCommand.
func NewPingPongCommand(config CommandConfig) (command PingPongCommand, err error) {
func NewPingPongCommand(config BaseCommand) (command PingPongCommand, err error) {
// Parse config
options := PingPongConfig{}
err = json.Unmarshal(config.Options, &options)
......@@ -99,10 +99,7 @@ func NewPingPongCommand(config CommandConfig) (command PingPongCommand, err erro
}
// Initialize command
command = PingPongCommand{
BaseCommand: BaseCommand{
Name: config.Name,
Type: config.Type,
},
BaseCommand: config,
PingPongConfig: options,
TriggerType: ttype,
ResponseType: rtype,
......
......@@ -30,7 +30,7 @@ type RESTConfig struct {
}
// NewRESTCommand generates a new RESTCommand.
func NewRESTCommand(config CommandConfig) (command RESTCommand, err error) {
func NewRESTCommand(config BaseCommand) (command RESTCommand, err error) {
var options RESTConfig
err = json.Unmarshal(config.Options, &options)
if err != nil {
......@@ -64,10 +64,7 @@ func NewRESTCommand(config CommandConfig) (command RESTCommand, err error) {
}
// generate the command
command = RESTCommand{
BaseCommand: BaseCommand{
Name: config.Name,
Type: config.Name,
},
BaseCommand: config,
RESTConfig: options,
regexp: rgx,
endpointstring: endpoint,
......
......@@ -16,18 +16,7 @@ type BotConfiguration struct {
// Token that the bot logs in with.
BotToken string `json:"botToken"`
// List of commands to try and create.
Commands []CommandConfig `json:"commands"`
}
// CommandConfig is the schema for each command in the BotConfiguration.
type CommandConfig struct {
// Name of the command.
Name string
// Type of the command.
Type string
// JSON-encoded list of options for the command.
// This is intended to be parsed and handled by the "NewXCommand" factory function.
Options json.RawMessage
Commands []BaseCommand `json:"commands"`
}
var config BotConfiguration
......@@ -95,18 +84,27 @@ func main() {
log.Fatal("Error with command "+config.Name+": ", err)
}
handler.Add(cmd)
if err != nil {
log.Fatal("Error with command "+config.Name+": ", err)
}
case "iasip":
cmd, err := NewIASIPCommand(config)
if err != nil {
log.Fatal("Error with command "+config.Name+": ", err)
}
handler.Add(cmd)
if err != nil {
log.Fatal("Error with command "+config.Name+": ", err)
}
case "rest":
cmd, err := NewRESTCommand(config)
if err != nil {
log.Fatal("Error with command "+config.Name+": ", err)
}
handler.Add(cmd)
if err != nil {
log.Fatal("Error with command "+config.Name+": ", err)
}
default:
log.Fatal("Command " + config.Name + " is of invalid type (" + config.Type + "). Exiting.")
}
......