Add basic command manager functionality

master
DustyP 6 years ago
parent 1c6570d157
commit b027e15cac

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2020 Dusty.P (https://github.com/dustinpianalto)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

@ -0,0 +1,7 @@
## Disgoman
Disgoman is a command handler for DiscordGo, inspired by [Anpan](https://github.com/MikeModder/anpan)
## Status
[![Go Report Card](https://goreportcard.com/badge/github.com/MikeModder/disgoman)](https://goreportcard.com/report/github.com/MikeModder/disgoman)
##

@ -0,0 +1,183 @@
package disgoman
/* command-manager.go
* The main command manager code
*
* Disgoman (c) 2020 Dusty.P/dustinpianalto
*/
import (
"errors"
"fmt"
"github.com/bwmarrin/discordgo"
"github.com/kballard/go-shellquote"
"log"
"strings"
)
func (c *CommandManager) AddCommand(name string, aliases []string, desc string, ownerOnly, hidden bool, perms Permission, command CommandInvokeFunc) error {
if _, ok := c.Commands[name]; ok {
return errors.New(fmt.Sprintf("A command named %v already exists", name))
}
for _, alias := range aliases {
if _, ok := c.Aliases[alias]; ok {
return errors.New(fmt.Sprintf("An alias named %v already exists", alias))
}
}
c.Commands[name] = &Command{
Name: name,
Description: desc,
OwnerOnly: ownerOnly,
Hidden: hidden,
RequiredPermissions: perms,
Invoke: command,
}
if len(aliases) > 0 {
for _, alias := range aliases {
c.Aliases[alias] = name
}
}
return nil
}
func (c *CommandManager) RemoveAliasesFor(name string) {
for alias, cmd := range c.Aliases {
if cmd == name {
delete(c.Aliases, alias)
}
}
}
func (c *CommandManager) RemoveCommand(name string) error {
if _, ok := c.Commands[name]; ok {
delete(c.Commands, name)
c.RemoveAliasesFor(name)
return nil
}
return errors.New(fmt.Sprintf("%v not in commands", name))
}
func (c *CommandManager) RemoveAlias(alias string) error {
if _, ok := c.Aliases[alias]; ok {
delete(c.Aliases, alias)
return nil
}
return errors.New(fmt.Sprintf("%v not in aliases", alias))
}
func (c *CommandManager) IsOwner(id string) bool {
for _, o := range c.Owners {
if o == id {
return true
}
}
return false
}
func (c *CommandManager) OnMessage(session *discordgo.Session, m *discordgo.MessageCreate) {
if m.Author.Bot && c.IgnoreBots {
return // If the author is a bot and ignore bots is set then just exit
}
content := m.Content
prefixes := c.Prefixes(m.GuildID)
var prefix string
for _, prefix = range prefixes {
if strings.HasPrefix(content, prefix) {
break
}
}
if prefix == "" {
return // If we didn't find a valid prefix then exit
}
// If we found our prefix then remove it and split the command into pieces
cmd, err := shellquote.Split(strings.TrimPrefix(content, prefix))
if err != nil {
log.Fatal(err)
return
}
var command *Command
invoked := cmd[0]
if n, ok := c.Aliases[invoked]; ok {
if cmnd, ok := c.Commands[n]; ok {
command = cmnd
} else {
log.Fatal("Alias Not Found in Commands")
return
}
} else if cmnd, ok := c.Commands[invoked]; ok {
command = cmnd
} else {
log.Fatal("Command Not Found")
return
}
channel, err := session.Channel(m.ChannelID)
if err != nil {
log.Fatal("Couldn't retrieve Channel.")
return
}
if !CheckPermissions(session, *m.Member, *channel, command.RequiredPermissions) {
embed := &discordgo.MessageEmbed{
Title: "Insufficient Permissions",
Description: "You don't have the correct permissions to run this command.",
Color: 0xFF0000,
}
if !command.Hidden {
session.ChannelMessageSendEmbed(m.ChannelID, embed)
}
return
}
me, err := session.GuildMember(m.GuildID, session.State.User.ID)
if err != nil {
log.Fatal(err)
return
}
if !CheckPermissions(session, *me, *channel, command.RequiredPermissions) {
embed := &discordgo.MessageEmbed{
Title: "Insufficient Permissions",
Description: "I don't have the correct permissions to run this command.",
Color: 0xFF0000,
}
if !command.Hidden {
session.ChannelMessageSendEmbed(m.ChannelID, embed)
}
return
}
if command.OwnerOnly && !c.IsOwner(m.Author.ID) {
embed := &discordgo.MessageEmbed{
Title: "You can't run that command!",
Description: "Sorry, only the bot owner(s) can run that command!",
Color: 0xff0000,
}
if !command.Hidden {
session.ChannelMessageSendEmbed(m.ChannelID, embed)
}
return
}
guild, _ := session.Guild(m.GuildID)
context := Context{
Session: session,
Channel: channel,
Message: m.Message,
User: m.Author,
Guild: guild,
Member: m.Member,
}
err = command.Invoke(context, cmd[1:])
if err != nil && c.OnErrorFunc != nil {
c.OnErrorFunc(context, cmd[0], err)
}
}

@ -0,0 +1,29 @@
package disgoman
import (
"github.com/bwmarrin/discordgo"
"io"
)
/* context.go:
* Utility functions for command context
*
* Disgoman (c) 2020 Dusty.P/dustinpianalto
*/
// Send message to originating channel
func (c *Context) Send(message string) (*discordgo.Message, error) {
return c.Session.ChannelMessageSend(c.Channel.ID, message)
}
// Send embed to originating channel
func (c *Context) SendEmbed(embed *discordgo.MessageEmbed) (*discordgo.Message, error) {
return c.Session.ChannelMessageSendEmbed(c.Channel.ID, embed)
}
// Send embed to originating channel
func (c *Context) SendFile(filename string, file io.Reader) (*discordgo.Message, error) {
return c.Session.ChannelFileSend(c.Channel.ID, filename, file)
}
// TODO Combine these to all use ChannelMessageSendComplex

@ -1 +1,37 @@
package disgoman package disgoman
/* Package Disgoman:
* Command Handler for DisgordGo.
* Inspired by:
* - Anpan (https://github.com/MikeModder/anpan)
*
* Disgoman (c) 2020 Dusty.P/dustinpianalto
*/
func GetCommandManager(prefixes PrefixesFunc, owners []string, ignoreBots, checkPerms bool) CommandManager {
return CommandManager{
Prefixes: prefixes,
Owners: owners,
StatusManager: GetDefaultStatusManager(),
Commands: make(map[string]*Command),
Aliases: make(map[string]string),
IgnoreBots: ignoreBots,
CheckPermissions: checkPerms,
}
}
func GetStatusManager(values []string, interval string) StatusManager {
return StatusManager{
Values: values,
Interval: interval,
}
}
func GetDefaultStatusManager() StatusManager {
return GetStatusManager(
[]string{
"Golang!",
"DiscordGo!",
"Disgoman!",
}, "10s")
}

@ -0,0 +1,61 @@
package disgoman
import (
"github.com/bwmarrin/discordgo"
"log"
"math/rand"
"time"
)
/* status-manager.go:
* Built in status manager which cycles through a list of status at a specified interval
*
* Disgoman (c) 2020 Dusty.P/dustinpianalto
*/
// Add a status to the manager
func (s *StatusManager) AddStatus(status string) {
s.Values = append(s.Values, status)
}
// Remove a status from the manager
func (s *StatusManager) RemoveStatus(status string) []string {
for i, v := range s.Values {
if v == status {
s.Values = append(s.Values[:i], s.Values[i+1:]...)
break
}
}
return s.Values
}
// Sets interval to new value
func (s *StatusManager) SetInterval(interval string) {
s.Interval = interval
}
func (s *StatusManager) UpdateStatus(session *discordgo.Session) error {
i := rand.Intn(len(s.Values))
err := session.UpdateStatus(0, s.Values[i])
return err
}
func (s *StatusManager) OnReady(session *discordgo.Session, _ *discordgo.Ready) {
interval, err := time.ParseDuration(s.Interval)
if err != nil {
return
}
err = s.UpdateStatus(session)
if err != nil {
log.Fatal(err)
}
ticker := time.NewTicker(interval)
for range ticker.C {
err = s.UpdateStatus(session)
if err != nil {
log.Fatal(err)
}
}
}

@ -0,0 +1,43 @@
package disgoman
/* structs.go
* Contains structs used in Disgoman
*
* Disgoman (c) 2020 Dusty.P/dustinpianalto
*/
import "github.com/bwmarrin/discordgo"
type CommandManager struct {
Prefixes PrefixesFunc
Owners []string
StatusManager StatusManager
OnErrorFunc OnErrorFunc
Commands map[string]*Command
Aliases map[string]string
IgnoreBots bool
CheckPermissions bool
}
type StatusManager struct {
Values []string
Interval string
}
type Command struct {
Name string
Description string
OwnerOnly bool
Hidden bool
RequiredPermissions Permission
Invoke CommandInvokeFunc
}
type Context struct {
Session *discordgo.Session
Channel *discordgo.Channel
Message *discordgo.Message
User *discordgo.User
Guild *discordgo.Guild
Member *discordgo.Member
}

@ -0,0 +1,54 @@
package disgoman
/* types.go
* Defines function types and other types
*
* Disgoman (c) 2020 Dusty.P/dustinpianalto
*/
// Function to invoke for command
type CommandInvokeFunc func(Context, []string) error
// Function to get prefixes
type PrefixesFunc func(string) []string
// Function to run on command error
type OnErrorFunc func(Context, string, error)
type Permission int
// Defining permissions based on the Discord API
const (
PermissionAdministrator Permission = 8
PermissionViewAuditLog Permission = 128
PermissionViewServerInsights Permission = 524288
PermissionManageServer Permission = 32
PermissionManageRoles Permission = 268435456
PermissionManageChannels Permission = 16
PermissionKickMembers Permission = 2
PermissionBanMembers Permission = 4
PermissionCreateInstantInvite Permission = 1
PermissionChangeNickname Permission = 67108864
PermissionManageNicknames Permission = 134217728
PermissionManageEmojis Permission = 1073741824
PermissionManageWebhooks Permission = 536870912
PermissionViewChannels Permission = 1024
PermissionMessagesSend Permission = 2048
PermissionMessagesSendTTS Permission = 4096
PermissionMessagesManage Permission = 8192
PermissionMessagesEmbedLinks Permission = 16384
PermissionMessagesAttachFiles Permission = 32768
PermissionMessagesReadHistory Permission = 65536
PermissionMessagesMentionEveryone Permission = 131072
PermissionMessagesUseExternalEmojis Permission = 262144
PermissionMessagesAddReactions Permission = 64
PermissionVoiceConnect Permission = 1048576
PermissionVoiceSpeak Permission = 2097152
PermissionVoiceMuteMembers Permission = 4194304
PermissionVoiceDeafenMembers Permission = 8388608
PermissionVoiceUseMembers Permission = 16777216
PermissionVoiceUseActivity Permission = 33554432
PermissionVoicePrioritySpeaker Permission = 256
)

@ -0,0 +1,53 @@
package disgoman
/* helpers.go:
* Utility functions to make my life easier.
*
* Disgoman (c) 2020 Dusty.P/dustinpianalto
*/
import "github.com/bwmarrin/discordgo"
// Checks the channel and guild permissions to see if the member has the needed permissions
func CheckPermissions(session *discordgo.Session, member discordgo.Member, channel discordgo.Channel, perms Permission) bool {
if perms == 0 {
return true // If no permissions are required then just return true
}
for _, overwrite := range channel.PermissionOverwrites {
if overwrite.ID == member.User.ID {
if overwrite.Allow&int(perms) != 0 {
return true // If the channel has an overwrite for the user then true
} else if overwrite.Deny&int(perms) != 0 {
return false // If there is an explicit deny then false
}
}
}
for _, roleID := range member.Roles {
role, err := session.State.Role(member.GuildID, roleID)
if err != nil {
return false // There is something wrong with the role, default to false
}
for _, overwrite := range channel.PermissionOverwrites {
if overwrite.ID == roleID {
if overwrite.Allow&int(perms) != 0 {
return true // If the channel has an overwrite for the role then true
} else if overwrite.Deny&int(perms) != 0 {
return false // If there is an explicit deny then false
}
}
}
if role.Permissions&int(PermissionAdministrator) != 0 {
return true // If they are an administrator then they automatically have all permissions
}
if role.Permissions&int(perms) != 0 {
return true // The role has the required permissions
}
}
return false // Default to false
}

@ -1,3 +1,8 @@
module djpianalto.com/disgoman module djpianalto.com/disgoman
go 1.14 go 1.14
require (
github.com/bwmarrin/discordgo v0.20.2
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51
)

Loading…
Cancel
Save