mirror of https://github.com/hak5/scuzzy.git
Create Scuzzy
Features: Hastily add usercolor feature Main: Show prefix in bot status Features: add cat Main/Features/Auth: Add basic Role Name/ID based command authentication and bot configuration. Main: Sanity check for missing flags. Features: Implement command deletion and tag-responses Misc: Add cmd/main to .gitignore Misc: Delete built binary Main: Refactor main.go - Use log.Fatal instead of fmt.Printf and os.Exit - Move Config reading into seperate function - Parse command line flags in main() - Instantiate Features.go struct inside main() - Use simple ticker to set bot status every 15 minutes Auth: Refactor auth - Return early when building Admin role list - Move CheckAdminrole() into auth.go, remove roles.go Features: Refactor features - Remove New() function - Move help.go, ping.go functionality into misc.go Features: Add bot control feature to set status. Features: Implement helper function for embeds, return errors. Main: Rename config file to bot_config.json Features: Use success embed for user color. Features: Add purge messages feature. Main: Add status_text property to config, use it for Bot Status. Features: Send welcome message to new users, implement auto-join roles. Main: Use complex user status Main: Reduce ticker interval to 10 minutes for bot status update. Main: Create helper function for complex custom embeds. Features: Implement info command. Features: Convert requested user color to lowercase. Features: Add markdown helper Features: Fix markdown helper typo Features: Use error embed for handleSetStatus incorrect permissions. Features: Add strikethrough and spoilers information to markdown info. Features: Add info and md to help text. Features: Use backtick instead of single quote for command prefix in help text. Features: Remove markdown info message after 15 seconds. Features: Fix typo in status setting permission error. Features: Add commands to convert between Celsius and Farenheit Features: Update help to include temperature conversion commands. Features/Models/Main: Load color roles from JSON config. Auth/Main/Features/Models: Implement config-based channel white/blacklisting. Main/Models: Remove unused admin_only config field. Features: Simplify error handling. Auth: Remove leftover AdminOnly field. Features: Use command restriction for usercolor commands. Main: Update default config to restrict color and colors commands. Auth/Features: Return true for undefined commands in restrictions list by default. Features: Use better error messages for channel white/blacklist Auth: Improve channel white/blacklist behavior. Main: Replace broken ticker for status updates Features: Get bot avatar via Discord for embed. Features: Delete requesting message for markdown info. Features: Handle potential error in deleting help requester message.master
commit
d54d2bb812
|
@ -0,0 +1,3 @@
|
||||||
|
.idea/
|
||||||
|
main
|
||||||
|
cmd/main
|
|
@ -0,0 +1,92 @@
|
||||||
|
package auth
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bwmarrin/discord.go"
|
||||||
|
"github.com/foxtrot/scuzzy/models"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type AdminRole struct {
|
||||||
|
Name string
|
||||||
|
ID string
|
||||||
|
}
|
||||||
|
|
||||||
|
type Auth struct {
|
||||||
|
AdminRoles []AdminRole
|
||||||
|
CommandRestrictions []models.CommandRestriction
|
||||||
|
Guild *discordgo.Guild
|
||||||
|
|
||||||
|
Config *models.Configuration
|
||||||
|
}
|
||||||
|
|
||||||
|
func New(config *models.Configuration, guild *discordgo.Guild) *Auth {
|
||||||
|
var ars []AdminRole
|
||||||
|
for _, gRole := range guild.Roles {
|
||||||
|
for _, aRole := range config.AdminRoles {
|
||||||
|
if aRole != gRole.Name {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
ar := AdminRole{
|
||||||
|
Name: gRole.Name,
|
||||||
|
ID: gRole.ID,
|
||||||
|
}
|
||||||
|
ars = append(ars, ar)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var crs []models.CommandRestriction
|
||||||
|
for _, cRes := range config.CommandRestrictions {
|
||||||
|
cr := models.CommandRestriction{
|
||||||
|
Command: cRes.Command,
|
||||||
|
Mode: cRes.Mode,
|
||||||
|
Channels: cRes.Channels,
|
||||||
|
}
|
||||||
|
crs = append(crs, cr)
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Auth{
|
||||||
|
AdminRoles: ars,
|
||||||
|
CommandRestrictions: crs,
|
||||||
|
Config: config,
|
||||||
|
Guild: guild,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Auth) CheckAdminRole(m *discordgo.Member) bool {
|
||||||
|
for _, aR := range a.AdminRoles {
|
||||||
|
for _, mID := range m.Roles {
|
||||||
|
if aR.ID == mID {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func (a *Auth) CheckCommandRestrictions(m *discordgo.MessageCreate) bool {
|
||||||
|
cName := strings.Split(m.Content, " ")[0]
|
||||||
|
cName = strings.Replace(cName, a.Config.CommandKey, "", 1)
|
||||||
|
cChanID := m.ChannelID
|
||||||
|
|
||||||
|
for _, cR := range a.CommandRestrictions {
|
||||||
|
if cName == cR.Command {
|
||||||
|
for _, cID := range cR.Channels {
|
||||||
|
if cID == cChanID && cR.Mode == "white" {
|
||||||
|
return true
|
||||||
|
} else if cID == cChanID && cR.Mode == "black" {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if cR.Mode == "white" {
|
||||||
|
return false
|
||||||
|
} else {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
|
@ -0,0 +1,24 @@
|
||||||
|
{
|
||||||
|
"guild_id": "506629366659153951",
|
||||||
|
|
||||||
|
"status_text": ".help",
|
||||||
|
"welcome_text": "Hi! Welcome to the Hak5 Discord Server. Please be sure to check out the #rules channel and say hello in #general!",
|
||||||
|
"command_key": ".",
|
||||||
|
|
||||||
|
"admin_roles": ["Admin", "Moderator"],
|
||||||
|
"join_role_ids": ["697929841638637778"],
|
||||||
|
|
||||||
|
"command_restrictions": [
|
||||||
|
{ "command": "color", "mode": "white", "channels": ["698567678025138277", "698519835532984470"] },
|
||||||
|
{ "command": "colors", "mode": "white", "channels": ["698567678025138277", "698519835532984470"] }
|
||||||
|
],
|
||||||
|
|
||||||
|
"color_roles": [
|
||||||
|
{ "color": "red", "id": "697930042491142174" },
|
||||||
|
{ "color": "blue", "id": "697930093766639699" },
|
||||||
|
{ "color": "green", "id": "697930441939877970" },
|
||||||
|
{ "color": "purple", "id": "697930578485444610" },
|
||||||
|
{ "color": "yellow", "id": "697930630247350402" },
|
||||||
|
{ "color": "pink", "id": "697965484313935966" }
|
||||||
|
]
|
||||||
|
}
|
|
@ -0,0 +1,134 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"github.com/foxtrot/scuzzy/auth"
|
||||||
|
"github.com/foxtrot/scuzzy/features"
|
||||||
|
"github.com/foxtrot/scuzzy/models"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discord.go"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Core Bot Properties
|
||||||
|
var (
|
||||||
|
Token string
|
||||||
|
ConfigPath string
|
||||||
|
Config models.Configuration
|
||||||
|
)
|
||||||
|
|
||||||
|
func getConfig() error {
|
||||||
|
cf, err := ioutil.ReadFile(ConfigPath)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = json.Unmarshal(cf, &Config)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
// Parse and Check Flags
|
||||||
|
flag.StringVar(&Token, "t", "", "Bot Token")
|
||||||
|
flag.StringVar(&ConfigPath, "c", "", "Config Path")
|
||||||
|
flag.Parse()
|
||||||
|
|
||||||
|
if len(Token) == 0 {
|
||||||
|
log.Fatal("Error: No Auth Token supplied.")
|
||||||
|
}
|
||||||
|
if len(ConfigPath) == 0 {
|
||||||
|
log.Fatal("Error: No Config Path supplied.")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get Config
|
||||||
|
err := getConfig()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Instantiate Bot
|
||||||
|
bot, err := discordgo.New("Bot " + Token)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Open Connection
|
||||||
|
err = bot.Open()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Setup Auth
|
||||||
|
Config.Guild, err = bot.Guild(Config.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error: " + err.Error())
|
||||||
|
}
|
||||||
|
var a *auth.Auth
|
||||||
|
a = auth.New(&Config, Config.Guild)
|
||||||
|
|
||||||
|
// Setup Handlers
|
||||||
|
f := features.Features{
|
||||||
|
Token: Token,
|
||||||
|
Auth: a,
|
||||||
|
Config: Config,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register Handlers
|
||||||
|
bot.AddHandler(f.OnMessageCreate)
|
||||||
|
bot.AddHandler(f.OnUserJoin)
|
||||||
|
|
||||||
|
fmt.Println("Bot Running.")
|
||||||
|
|
||||||
|
// Set Bot Status
|
||||||
|
go func() {
|
||||||
|
usd := discordgo.UpdateStatusData{
|
||||||
|
IdleSince: nil,
|
||||||
|
Game: &discordgo.Game{
|
||||||
|
Name: Config.StatusText,
|
||||||
|
Type: 0,
|
||||||
|
URL: "",
|
||||||
|
Details: "",
|
||||||
|
State: "",
|
||||||
|
TimeStamps: discordgo.TimeStamps{},
|
||||||
|
Assets: discordgo.Assets{},
|
||||||
|
ApplicationID: "",
|
||||||
|
Instance: -1,
|
||||||
|
},
|
||||||
|
AFK: false,
|
||||||
|
Status: "online",
|
||||||
|
}
|
||||||
|
err = bot.UpdateStatusComplex(usd)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error: " + err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
// For some reason the bot's status will regularly disappear...
|
||||||
|
for _ = range time.Tick(10 * time.Minute) {
|
||||||
|
err := bot.UpdateStatusComplex(usd)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Catch SIGINT
|
||||||
|
sc := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sc, syscall.SIGINT, syscall.SIGKILL)
|
||||||
|
<-sc
|
||||||
|
|
||||||
|
err = bot.Close()
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal("Error: " + err.Error())
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
package features
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
discordgo "github.com/bwmarrin/discord.go"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Features) handleSetStatus(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
if !f.Auth.CheckAdminRole(m.Member) {
|
||||||
|
return errors.New("You do not have permissions to use that command.")
|
||||||
|
}
|
||||||
|
|
||||||
|
stSplit := strings.SplitN(m.Content, " ", 2)
|
||||||
|
if len(stSplit) < 2 {
|
||||||
|
return errors.New("You did not specify a status.")
|
||||||
|
}
|
||||||
|
|
||||||
|
st := stSplit[1]
|
||||||
|
|
||||||
|
err := s.UpdateStatus(0, st)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := f.CreateDefinedEmbed("Set Status", "Operation completed successfully.", "success")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
package features
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/foxtrot/scuzzy/auth"
|
||||||
|
"github.com/foxtrot/scuzzy/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Features struct {
|
||||||
|
Token string
|
||||||
|
Auth *auth.Auth
|
||||||
|
Config models.Configuration
|
||||||
|
}
|
|
@ -0,0 +1,171 @@
|
||||||
|
package features
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bwmarrin/discord.go"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Features) OnMessageCreate(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
// Ignore the bot itself
|
||||||
|
if m.Author.ID == s.State.User.ID {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
cKey := f.Config.CommandKey
|
||||||
|
cName := strings.Split(m.Content, " ")[0]
|
||||||
|
|
||||||
|
if !strings.HasPrefix(cName, cKey) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
switch cName {
|
||||||
|
/* Misc Commands */
|
||||||
|
case cKey + "help":
|
||||||
|
err = f.handleHelp(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (Help)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case cKey + "info":
|
||||||
|
err = f.handleInfo(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (Info)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case cKey + "ping":
|
||||||
|
err = f.handlePing(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (Ping)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case cKey + "no":
|
||||||
|
err = f.handleCat(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (No)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case cKey + "md":
|
||||||
|
err = f.handleMarkdownInfo(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (Markdown)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case cKey + "ctof":
|
||||||
|
err = f.handleCtoF(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (CtoF)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case cKey + "ftoc":
|
||||||
|
err = f.handleFtoC(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (FtoC)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
/* Moderation */
|
||||||
|
case cKey + "purge":
|
||||||
|
err = f.handlePurgeChannel(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (Purge)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
/* Bot Control */
|
||||||
|
case cKey + "status":
|
||||||
|
err = f.handleSetStatus(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (Status)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
/* Role Colors */
|
||||||
|
case cKey + "colors":
|
||||||
|
err = f.listUserColors(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (Colors)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case cKey + "color":
|
||||||
|
err = f.setUserColor(s, m)
|
||||||
|
if err != nil {
|
||||||
|
eMsg := f.CreateDefinedEmbed("Error (Color)", err.Error(), "error")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, eMsg)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err.Error())
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) OnUserJoin(s *discordgo.Session, m *discordgo.GuildMemberAdd) {
|
||||||
|
userChannel, err := s.UserChannelCreate(m.User.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Print("Error (User Join): " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = s.ChannelMessageSend(userChannel.ID, f.Config.WelcomeText)
|
||||||
|
if err != nil {
|
||||||
|
log.Print("Error (User Join): " + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, roleID := range f.Config.JoinRoleIDs {
|
||||||
|
err = s.GuildMemberRoleAdd(f.Auth.Guild.ID, m.User.ID, roleID)
|
||||||
|
if err != nil {
|
||||||
|
log.Print("Error (User Join)" + err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,94 @@
|
||||||
|
package features
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
discordgo "github.com/bwmarrin/discord.go"
|
||||||
|
"github.com/foxtrot/scuzzy/models"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Features) PrintError(component string, error string) {
|
||||||
|
fmt.Printf("Error: %s: %s\n", component, error)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) CreateDefinedEmbed(title string, desc string, status string) *discordgo.MessageEmbed {
|
||||||
|
msgColor := 0x000000
|
||||||
|
|
||||||
|
switch status {
|
||||||
|
case "error":
|
||||||
|
msgColor = 0xCC0000
|
||||||
|
break
|
||||||
|
case "success":
|
||||||
|
msgColor = 0x00CC00
|
||||||
|
break
|
||||||
|
default:
|
||||||
|
msgColor = 0xFFA500
|
||||||
|
}
|
||||||
|
|
||||||
|
ftr := discordgo.MessageEmbedFooter{
|
||||||
|
Text: "Something broken? Tell foxtrot#1337",
|
||||||
|
IconURL: "https://cdn.discordapp.com/avatars/514163441548656641/a4ede220fea0ad8872b86f3eebc45524.png?size=128",
|
||||||
|
ProxyIconURL: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := discordgo.MessageEmbed{
|
||||||
|
URL: "",
|
||||||
|
Type: "",
|
||||||
|
Title: title,
|
||||||
|
Description: desc,
|
||||||
|
Timestamp: "",
|
||||||
|
Color: msgColor,
|
||||||
|
Footer: &ftr,
|
||||||
|
Image: nil,
|
||||||
|
Thumbnail: nil,
|
||||||
|
Video: nil,
|
||||||
|
Provider: nil,
|
||||||
|
Author: nil,
|
||||||
|
Fields: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &msg
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) CreateCustomEmbed(embedData *models.CustomEmbed) *discordgo.MessageEmbed {
|
||||||
|
var ftr discordgo.MessageEmbedFooter
|
||||||
|
var img discordgo.MessageEmbedImage
|
||||||
|
var thm discordgo.MessageEmbedThumbnail
|
||||||
|
var prv discordgo.MessageEmbedProvider
|
||||||
|
var atr discordgo.MessageEmbedAuthor
|
||||||
|
|
||||||
|
ftr.Text = embedData.FooterText
|
||||||
|
ftr.IconURL = embedData.FooterImageURL
|
||||||
|
|
||||||
|
img.URL = embedData.ImageURL
|
||||||
|
img.Height = embedData.ImageH
|
||||||
|
img.Width = embedData.ImageW
|
||||||
|
|
||||||
|
thm.URL = embedData.ThumbnailURL
|
||||||
|
thm.Height = embedData.ThumbnailH
|
||||||
|
thm.Width = embedData.ThumbnailW
|
||||||
|
|
||||||
|
prv.Name = embedData.ProviderText
|
||||||
|
prv.URL = embedData.ProviderURL
|
||||||
|
|
||||||
|
atr.Name = embedData.AuthorText
|
||||||
|
atr.URL = embedData.AuthorURL
|
||||||
|
atr.IconURL = embedData.AuthorImageURL
|
||||||
|
|
||||||
|
msg := discordgo.MessageEmbed{
|
||||||
|
URL: embedData.URL,
|
||||||
|
Type: embedData.Type,
|
||||||
|
Title: embedData.Title,
|
||||||
|
Description: embedData.Desc,
|
||||||
|
Timestamp: "",
|
||||||
|
Color: embedData.Color,
|
||||||
|
Footer: &ftr,
|
||||||
|
Image: &img,
|
||||||
|
Thumbnail: &thm,
|
||||||
|
Video: nil,
|
||||||
|
Provider: &prv,
|
||||||
|
Author: &atr,
|
||||||
|
Fields: nil,
|
||||||
|
}
|
||||||
|
|
||||||
|
return &msg
|
||||||
|
}
|
|
@ -0,0 +1,212 @@
|
||||||
|
package features
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/bwmarrin/discord.go"
|
||||||
|
"github.com/foxtrot/scuzzy/models"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Features) handleCat(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
if f.Auth.CheckAdminRole(m.Member) {
|
||||||
|
_, _ = s.ChannelMessageSend(m.ChannelID, "https://giphy.com/gifs/cat-cute-no-rCxogJBzaeZuU")
|
||||||
|
_ = s.ChannelMessageDelete(m.ChannelID, m.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) handlePing(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
var r *discordgo.Message
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if !f.Auth.CheckAdminRole(m.Member) {
|
||||||
|
return errors.New("You do not have permissions to use that command.")
|
||||||
|
} else {
|
||||||
|
msg := f.CreateDefinedEmbed("Ping", "Pong", "success")
|
||||||
|
r, err = s.ChannelMessageSendEmbed(m.ChannelID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(5 * time.Second)
|
||||||
|
|
||||||
|
err = s.ChannelMessageDelete(m.ChannelID, r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.ChannelMessageDelete(m.ChannelID, m.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) handleInfo(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
desc := "**Source**: https://github.com/foxtrot/scuzzy\n"
|
||||||
|
desc += "**Language**: Go\n"
|
||||||
|
desc += "**Commands**: See `" + f.Config.CommandKey + "help`\n\n\n"
|
||||||
|
|
||||||
|
gm, err := s.GuildMember(f.Config.GuildID, s.State.User.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
d := models.CustomEmbed{
|
||||||
|
Title: "Scuzzy Information",
|
||||||
|
Desc: desc,
|
||||||
|
ImageURL: "",
|
||||||
|
ImageH: 100,
|
||||||
|
ImageW: 100,
|
||||||
|
Color: 0xFFA500,
|
||||||
|
URL: "",
|
||||||
|
Type: "",
|
||||||
|
Timestamp: "",
|
||||||
|
FooterText: "Made with ❤ by Foxtrot",
|
||||||
|
FooterImageURL: "https://cdn.discordapp.com/avatars/514163441548656641/a4ede220fea0ad8872b86f3eebc45524.png",
|
||||||
|
ThumbnailURL: gm.User.AvatarURL(""),
|
||||||
|
ThumbnailH: 150,
|
||||||
|
ThumbnailW: 150,
|
||||||
|
ProviderURL: "",
|
||||||
|
ProviderText: "",
|
||||||
|
AuthorText: "",
|
||||||
|
AuthorURL: "",
|
||||||
|
AuthorImageURL: "",
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := f.CreateCustomEmbed(&d)
|
||||||
|
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) handleHelp(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
desc := "**Available Commands**\n"
|
||||||
|
desc += "`help` - This help dialog\n"
|
||||||
|
desc += "`info` - Display Scuzzy info\n"
|
||||||
|
desc += "`md` - Display Discord markdown information\n"
|
||||||
|
desc += "`colors` - Available color roles\n"
|
||||||
|
desc += "`color` - Set an available color role\n"
|
||||||
|
desc += "`ctof` - Convert Celsius to Farenheit\n"
|
||||||
|
desc += "`ftoc` - Convert Farenheit to Celsius\n"
|
||||||
|
|
||||||
|
if f.Auth.CheckAdminRole(m.Member) {
|
||||||
|
desc += "\n"
|
||||||
|
desc += "**Admin Commands**\n"
|
||||||
|
desc += "`ping` - Ping the bot\n"
|
||||||
|
desc += "`status` - Set the bot status\n"
|
||||||
|
desc += "`purge` - Purge channel messages\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
desc += "\n\nAll commands are prefixed with `" + f.Config.CommandKey + "`\n"
|
||||||
|
|
||||||
|
msg := f.CreateDefinedEmbed("Help", desc, "")
|
||||||
|
|
||||||
|
_, err := s.ChannelMessageSendEmbed(m.ChannelID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.ChannelMessageDelete(m.ChannelID, m.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) handleMarkdownInfo(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
desc := "*Italic* text goes between `*single asterisks*`\n"
|
||||||
|
desc += "**Bold** text goes between `**double asterisks**`\n"
|
||||||
|
desc += "***Bold and Italic*** text goes between `***triple asterisks***`\n"
|
||||||
|
desc += "__Underlined__ text goes between `__double underscore__`\n"
|
||||||
|
desc += "~~Strikethrough~~ text goes between `~~double tilde~~`\n"
|
||||||
|
desc += "||Spoilers|| go between `|| double pipe ||`\n\n"
|
||||||
|
desc += "You can combine the above styles.\n\n"
|
||||||
|
desc += "Inline Code Blocks start and end with a single `````\n"
|
||||||
|
desc += "Multi line Code Blocks start and end with ```````\n"
|
||||||
|
desc += "Multi line Code Blocks can also specify a language with `````language`` at the start\n\n"
|
||||||
|
desc += "Single line quotes start with `>`\n"
|
||||||
|
desc += "Multi line quotes start with `>>>`\n"
|
||||||
|
|
||||||
|
msg := f.CreateDefinedEmbed("Discord Markdown", desc, "")
|
||||||
|
r, err := s.ChannelMessageSendEmbed(m.ChannelID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
time.Sleep(15 * time.Second)
|
||||||
|
|
||||||
|
err = s.ChannelMessageDelete(m.ChannelID, r.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
err = s.ChannelMessageDelete(m.ChannelID, m.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) handleCtoF(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
inS := strings.Split(m.Content, " ")
|
||||||
|
|
||||||
|
if len(inS) < 2 {
|
||||||
|
return errors.New("You did not specify a temperature")
|
||||||
|
}
|
||||||
|
in := inS[1]
|
||||||
|
|
||||||
|
inF, err := strconv.ParseFloat(in, 2)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("You did not specify a valid number")
|
||||||
|
}
|
||||||
|
|
||||||
|
cels := (inF * 9.0 / 5.0) + 32.0
|
||||||
|
celsF := float64(cels)
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("`%.1f°c` is `%.1f°f`", inF, celsF)
|
||||||
|
|
||||||
|
e := f.CreateDefinedEmbed("Celsius to Farenheit", msg, "")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) handleFtoC(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
inS := strings.Split(m.Content, " ")
|
||||||
|
|
||||||
|
if len(inS) < 2 {
|
||||||
|
return errors.New("You did not specify a temperature")
|
||||||
|
}
|
||||||
|
in := inS[1]
|
||||||
|
|
||||||
|
inF, err := strconv.ParseFloat(in, 2)
|
||||||
|
if err != nil {
|
||||||
|
return errors.New("You did not specify a valid number")
|
||||||
|
}
|
||||||
|
|
||||||
|
faren := (inF - 32) * 5 / 9
|
||||||
|
farenF := float64(faren)
|
||||||
|
|
||||||
|
msg := fmt.Sprintf("`%.1f°f` is `%.1f°c`", inF, farenF)
|
||||||
|
|
||||||
|
e := f.CreateDefinedEmbed("Farenheit to Celsius", msg, "")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, e)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,58 @@
|
||||||
|
package features
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/bwmarrin/discord.go"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Features) handlePurgeChannel(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
if !f.Auth.CheckAdminRole(m.Member) {
|
||||||
|
return errors.New("You do not have permissions to use that command.")
|
||||||
|
}
|
||||||
|
|
||||||
|
purgeSplit := strings.SplitN(m.Content, " ", 2)
|
||||||
|
if len(purgeSplit) < 2 {
|
||||||
|
return errors.New("No message count supplied")
|
||||||
|
}
|
||||||
|
|
||||||
|
msgCount, err := strconv.Atoi(purgeSplit[1])
|
||||||
|
if err != nil {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
if msgCount > 100 {
|
||||||
|
return errors.New("You may only purge upto 100 messages at a time.")
|
||||||
|
}
|
||||||
|
|
||||||
|
chanMsgs, err := s.ChannelMessages(m.ChannelID, msgCount, "", "", "")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
msg := f.CreateDefinedEmbed("Purge Channel", "Purging `"+purgeSplit[1]+"` messages.", "normal")
|
||||||
|
r, err := s.ChannelMessageSendEmbed(m.ChannelID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
var delMsgs []string
|
||||||
|
for _, v := range chanMsgs {
|
||||||
|
delMsgs = append(delMsgs, v.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.ChannelMessagesBulkDelete(m.ChannelID, delMsgs)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.ChannelMessageDelete(m.ChannelID, r.ID)
|
||||||
|
msg = f.CreateDefinedEmbed("Purge Channel", "Purged `"+purgeSplit[1]+"` messages!", "success")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package features
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"github.com/bwmarrin/discord.go"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func (f *Features) listUserColors(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
if !f.Auth.CheckCommandRestrictions(m) {
|
||||||
|
return errors.New("This command is not allowed in this channel.")
|
||||||
|
}
|
||||||
|
|
||||||
|
msgC := "You can choose from the following colors:\n\n"
|
||||||
|
for _, v := range f.Config.ColorRoles {
|
||||||
|
msgC += "<@&" + v.ID + ">\n"
|
||||||
|
}
|
||||||
|
msgC += "\n\nUse `" + f.Config.CommandKey + "color <color_name>` to set.\n"
|
||||||
|
|
||||||
|
msg := f.CreateDefinedEmbed("User Colors", msgC, "")
|
||||||
|
|
||||||
|
_, err := s.ChannelMessageSendEmbed(m.ChannelID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (f *Features) setUserColor(s *discordgo.Session, m *discordgo.MessageCreate) error {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
if !f.Auth.CheckCommandRestrictions(m) {
|
||||||
|
return errors.New("This command is not allowed in this channel.")
|
||||||
|
}
|
||||||
|
|
||||||
|
rUserID := m.Author.ID
|
||||||
|
|
||||||
|
userInput := strings.Split(m.Content, " ")
|
||||||
|
if len(userInput) < 2 {
|
||||||
|
err = f.listUserColors(s, m)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
roleColorName := userInput[1]
|
||||||
|
roleColorName = strings.ToLower(roleColorName)
|
||||||
|
|
||||||
|
roleColorID := ""
|
||||||
|
for _, role := range f.Config.ColorRoles {
|
||||||
|
if role.Name == roleColorName {
|
||||||
|
roleColorID = role.ID
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(roleColorID) == 0 {
|
||||||
|
err = f.listUserColors(s, m)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, role := range f.Config.ColorRoles {
|
||||||
|
// Attempt to remove all color roles regardless of if they have them or not.
|
||||||
|
// Slow because of the REST requests...
|
||||||
|
_ = s.GuildMemberRoleRemove(m.GuildID, rUserID, role.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.GuildMemberRoleAdd(m.GuildID, rUserID, roleColorID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
} else {
|
||||||
|
msg := f.CreateDefinedEmbed("User Color", "<@"+m.Author.ID+">: Your color has been set to <@&"+roleColorID+">!", "success")
|
||||||
|
_, err = s.ChannelMessageSendEmbed(m.ChannelID, msg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
err = s.ChannelMessageDelete(m.ChannelID, m.ID)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
import "github.com/bwmarrin/discord.go"
|
||||||
|
|
||||||
|
type ColorRole struct {
|
||||||
|
Name string `json:"color"`
|
||||||
|
ID string `json:"id"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type CommandRestriction struct {
|
||||||
|
Command string `json:"command"`
|
||||||
|
Mode string `json:"mode"`
|
||||||
|
Channels []string `json:"channels"`
|
||||||
|
}
|
||||||
|
|
||||||
|
type Configuration struct {
|
||||||
|
GuildID string `json:"guild_id"`
|
||||||
|
|
||||||
|
StatusText string `json:"status_text"`
|
||||||
|
WelcomeText string `json:"welcome_text"`
|
||||||
|
CommandKey string `json:"command_key"`
|
||||||
|
|
||||||
|
AdminRoles []string `json:"admin_roles"`
|
||||||
|
JoinRoleIDs []string `json:"join_role_ids"`
|
||||||
|
|
||||||
|
CommandRestrictions []CommandRestriction `json:"command_restrictions"`
|
||||||
|
|
||||||
|
ColorRoles []ColorRole `json:"color_roles"`
|
||||||
|
|
||||||
|
Guild *discordgo.Guild `json:"reserved_guild"`
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
package models
|
||||||
|
|
||||||
|
type CustomEmbed struct {
|
||||||
|
URL string
|
||||||
|
Title string
|
||||||
|
Desc string
|
||||||
|
Type string
|
||||||
|
Timestamp string
|
||||||
|
Color int
|
||||||
|
|
||||||
|
FooterText string
|
||||||
|
FooterImageURL string
|
||||||
|
|
||||||
|
ImageURL string
|
||||||
|
ImageH int
|
||||||
|
ImageW int
|
||||||
|
|
||||||
|
ThumbnailURL string
|
||||||
|
ThumbnailH int
|
||||||
|
ThumbnailW int
|
||||||
|
|
||||||
|
ProviderURL string
|
||||||
|
ProviderText string
|
||||||
|
|
||||||
|
AuthorText string
|
||||||
|
AuthorURL string
|
||||||
|
AuthorImageURL string
|
||||||
|
}
|
Loading…
Reference in New Issue