Compare commits
1 Commits
Author | SHA1 | Date | |
---|---|---|---|
kageru | ce83264a39 |
12
CHANGELOG.md
12
CHANGELOG.md
|
@ -1,18 +1,6 @@
|
||||||
# Selphybot Changelog
|
# Selphybot Changelog
|
||||||
Updates are listed in reverse chronological order.
|
Updates are listed in reverse chronological order.
|
||||||
|
|
||||||
### 1.5 (dev)
|
|
||||||
- added !vc to create temporary voice channels
|
|
||||||
|
|
||||||
### 1.4
|
|
||||||
- a copy of each deleted message is now send via DM to the author (suggested by CommanderLook)
|
|
||||||
- seasonal fluff
|
|
||||||
- finally use gofmt
|
|
||||||
- better logging of own messages and embeds
|
|
||||||
- logging always occurs after the action that is being logged
|
|
||||||
- redirect function is much cleaner now
|
|
||||||
- commands can be limited to certain channels
|
|
||||||
|
|
||||||
### 1.3
|
### 1.3
|
||||||
- use global array of pointers to commands to allow easier modification and avoid unnecessary memcpy
|
- use global array of pointers to commands to allow easier modification and avoid unnecessary memcpy
|
||||||
- added feedback to !complain (used to work but was forgotten when refactoring)
|
- added feedback to !complain (used to work but was forgotten when refactoring)
|
||||||
|
|
433
command.go
433
command.go
|
@ -1,333 +1,156 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bwmarrin/discordgo"
|
"github.com/bwmarrin/discordgo"
|
||||||
"github.com/deckarep/golang-set"
|
"github.com/deckarep/golang-set"
|
||||||
"log"
|
"strings"
|
||||||
"regexp"
|
"time"
|
||||||
"strings"
|
"log"
|
||||||
"time"
|
"regexp"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CommandType int
|
type CommandType int
|
||||||
|
|
||||||
// These are used to specify Command.CommandType when registering new commands
|
// These are used to specify Command.CommandType when registering new commands
|
||||||
const (
|
const (
|
||||||
CommandTypePrefix CommandType = 0
|
CommandTypePrefix CommandType = 0
|
||||||
CommandTypeFullMatch CommandType = 1
|
CommandTypeFullMatch CommandType = 1
|
||||||
CommandTypeRegex CommandType = 2
|
CommandTypeRegex CommandType = 2
|
||||||
CommandTypeContains CommandType = 3
|
CommandTypeContains CommandType = 3
|
||||||
)
|
)
|
||||||
|
|
||||||
/*
|
/*
|
||||||
This struct represents a command object.
|
This struct represents a command object.
|
||||||
The options should be self-explanatory, but they are also explained in the readme.
|
The options should be self-explanatory, but they are also explained in the readme.
|
||||||
A struct can be initialized by passing any number of its attributes as parameters.
|
A struct can be initialized by passing any number of its attributes as parameters.
|
||||||
Everything not set will be set to the go-usual defaults ("" for string, 0 for int, false for bool, nil for the rest)
|
Everything not set will be set to the go-usual defaults ("" for string, 0 for int, false for bool, nil for the rest)
|
||||||
Any command that has a Trigger is valid (but useless if nothing else is specified)
|
Any command that has a Trigger is valid (but useless if nothing else is specified)
|
||||||
*/
|
*/
|
||||||
type Command struct {
|
type Command struct {
|
||||||
Trigger string // must be specified
|
Trigger string // must be specified
|
||||||
Output string // no output if unspecified
|
Output string // no output if unspecified
|
||||||
OutputEmbed *discordgo.MessageEmbed // no embed output if unspecified
|
OutputEmbed *discordgo.MessageEmbed // no embed output if unspecified
|
||||||
Type CommandType // defaults to Prefix
|
Type CommandType // defaults to Prefix
|
||||||
Cooldown int // defaults to 0 (no cooldown)
|
Cooldown int // defaults to 0 (no cooldown)
|
||||||
OutputIsReply bool
|
OutputIsReply bool
|
||||||
RequiresMention bool
|
RequiresMention bool
|
||||||
DeleteInput bool
|
DeleteInput bool
|
||||||
DMOnly bool
|
DMOnly bool
|
||||||
AdminOnly bool
|
AdminOnly bool
|
||||||
IgnoreCase bool
|
IgnoreCase bool
|
||||||
// for custom commands that go beyond prints and deletions
|
// for custom commands that go beyond prints and deletions
|
||||||
Function func(*discordgo.Session, *discordgo.MessageCreate)
|
Function func(*discordgo.Session, *discordgo.MessageCreate)
|
||||||
AllowedChannels mapset.Set // allowed everywhere if blank
|
|
||||||
|
|
||||||
UsersOnCooldown mapset.Set // don’t set this manually (it’s overwritten anyway)
|
UsersOnCooldown mapset.Set // don’t set this manually (it’s overwritten anyway)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Performs basic input validation on a given command and adds it to the global command array
|
// Performs basic input validation on a given command and adds it to the global command array
|
||||||
func registerCommand(command Command) {
|
func registerCommand(command Command) {
|
||||||
if command.Trigger == "" {
|
if command.Trigger == "" {
|
||||||
fmt.Println("Cannot register a command with no trigger. Skipping.")
|
fmt.Println("Cannot register a command with no trigger. Skipping.")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if command.IgnoreCase {
|
if command.IgnoreCase {
|
||||||
command.Trigger = strings.ToLower(command.Trigger)
|
command.Trigger = strings.ToLower(command.Trigger)
|
||||||
}
|
}
|
||||||
command.UsersOnCooldown = mapset.NewSet()
|
command.UsersOnCooldown = mapset.NewSet()
|
||||||
if command.AllowedChannels == nil {
|
commands = append(commands, &command)
|
||||||
command.AllowedChannels = mapset.NewSet()
|
|
||||||
}
|
|
||||||
commands = append(commands, &command)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Any message that the bot can read is evaluated here.
|
Any message that the bot can read is evaluated here.
|
||||||
The message is matched against each of the command triggers depending on the respective match type.
|
The message is matched against each of the command triggers depending on the respective match type.
|
||||||
If one of the commands matches, execute that command and return.
|
If one of the commands matches, execute that command and return.
|
||||||
Only one command can be executed per message. Earlier defined commands take precedence.
|
Only one command can be executed per message. Earlier defined commands take precedence.
|
||||||
This is a deliberate choice (for now).
|
This is a deliberate choice (for now).
|
||||||
*/
|
|
||||||
func evaluateMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
||||||
if m.Author.ID == s.State.User.ID {
|
|
||||||
// Properly log embeds
|
|
||||||
if len(m.Embeds) > 0 {
|
|
||||||
log.Printf("<Self> %s", m.Embeds[0].Description)
|
|
||||||
} else {
|
|
||||||
log.Printf("<Self> %s", m.Content)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, command := range commands {
|
|
||||||
content := m.Content
|
|
||||||
if command.IgnoreCase {
|
|
||||||
content = strings.ToLower(content)
|
|
||||||
}
|
|
||||||
if command.RequiresMention {
|
|
||||||
command.Trigger = fmt.Sprintf(command.Trigger, s.State.User.ID)
|
|
||||||
}
|
|
||||||
switch command.Type {
|
|
||||||
case CommandTypePrefix:
|
|
||||||
if strings.HasPrefix(content, command.Trigger) {
|
|
||||||
executeCommand(s, m, command)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case CommandTypeFullMatch:
|
|
||||||
if content == command.Trigger {
|
|
||||||
executeCommand(s, m, command)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case CommandTypeRegex:
|
|
||||||
match, _ := regexp.MatchString(command.Trigger, content)
|
|
||||||
if match {
|
|
||||||
executeCommand(s, m, command)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
case CommandTypeContains:
|
|
||||||
if strings.Contains(content, command.Trigger) {
|
|
||||||
executeCommand(s, m, command)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
Executes the given command on the given message and session.
|
|
||||||
Sets command cooldowns if necessary and also clears them again.
|
|
||||||
*/
|
|
||||||
func executeCommand(session *discordgo.Session, message *discordgo.MessageCreate, command *Command) {
|
|
||||||
if commandAllowed(session, message, command) {
|
|
||||||
log.Printf("Executed command %s triggered by user %s", command.Trigger, userToString(message.Author))
|
|
||||||
if command.Cooldown > 0 && !isDM(session, message) && !isAdmin(message.Author) {
|
|
||||||
command.UsersOnCooldown.Add(message.Author.ID)
|
|
||||||
go removeCooldown(command, message.Author.ID)
|
|
||||||
}
|
|
||||||
if command.Function == nil {
|
|
||||||
// simple reply
|
|
||||||
if command.OutputEmbed == nil {
|
|
||||||
messageContent := generateReply(message, command)
|
|
||||||
session.ChannelMessageSend(message.ChannelID, messageContent)
|
|
||||||
} else {
|
|
||||||
session.ChannelMessageSendEmbed(message.ChannelID, command.OutputEmbed)
|
|
||||||
}
|
|
||||||
if command.DeleteInput {
|
|
||||||
deleteAndSendViaDM(session, message)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// execute custom function
|
|
||||||
command.Function(session, message)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Printf("Denied command %s to user %s.", command.Trigger, userToString(message.Author))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
* Check if a user has the permission to trigger a given command.
|
|
||||||
* To be honest, this whole logic is a mess, but I don’t know a better way to handle it.
|
|
||||||
*/
|
*/
|
||||||
func commandAllowed(session *discordgo.Session, message *discordgo.MessageCreate, command *Command) bool {
|
func evaluateMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
|
||||||
// no restrictions for admins
|
if m.Author.ID == s.State.User.ID {
|
||||||
if isAdmin(message.Author) {
|
log.Printf("<Self> %s", m.Content)
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
// blacklist admin commands for everyone else
|
for _, command := range commands {
|
||||||
if command.AdminOnly {
|
content := m.Content
|
||||||
return false
|
if command.IgnoreCase {
|
||||||
}
|
content = strings.ToLower(content)
|
||||||
// cooldowns are irrelevant in DMs
|
}
|
||||||
if !isDM(session, message) && command.UsersOnCooldown.Contains(message.Author.ID) {
|
if command.RequiresMention {
|
||||||
return false
|
command.Trigger = fmt.Sprintf(command.Trigger, s.State.User.ID)
|
||||||
}
|
}
|
||||||
// the command is not limited to DMs or we are inside a DM chat
|
switch command.Type {
|
||||||
if command.DMOnly && !isDM(session, message) {
|
case CommandTypePrefix:
|
||||||
return false
|
if strings.HasPrefix(content, command.Trigger) {
|
||||||
}
|
executeCommand(s, m, command)
|
||||||
// no allowed channels = all channels are allowed.
|
return
|
||||||
// DMs are whitelisted by default
|
}
|
||||||
if command.AllowedChannels.Cardinality() != 0 &&
|
case CommandTypeFullMatch:
|
||||||
!command.AllowedChannels.Contains(message.ChannelID) &&
|
if content == command.Trigger {
|
||||||
isDM(session, message) {
|
executeCommand(s, m, command)
|
||||||
return false
|
return
|
||||||
}
|
}
|
||||||
return true
|
case CommandTypeRegex:
|
||||||
|
match, _ := regexp.MatchString(command.Trigger, content)
|
||||||
|
if match {
|
||||||
|
executeCommand(s, m, command)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
case CommandTypeContains:
|
||||||
|
if strings.Contains(content, command.Trigger) {
|
||||||
|
executeCommand(s, m, command)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Executes the given command on the given message and session.
|
||||||
|
Sets command cooldowns if necessary and also clears them again.
|
||||||
|
*/
|
||||||
|
func executeCommand(session *discordgo.Session, message *discordgo.MessageCreate, command *Command) {
|
||||||
|
if isAdmin(message.Author) || // no restrictions for admins
|
||||||
|
(!command.AdminOnly && (isDM(session, message) || !command.UsersOnCooldown.Contains(message.Author.ID)) &&
|
||||||
|
(!command.DMOnly || isDM(session, message))) {
|
||||||
|
|
||||||
|
log.Printf("Executed command %s triggered by user %s", command.Trigger, userToString(message.Author))
|
||||||
|
if command.Cooldown > 0 && !isDM(session, message) && !isAdmin(message.Author) {
|
||||||
|
command.UsersOnCooldown.Add(message.Author.ID)
|
||||||
|
go removeCooldown(command, message.Author.ID)
|
||||||
|
}
|
||||||
|
if command.Function == nil {
|
||||||
|
// simple reply
|
||||||
|
if command.OutputEmbed == nil {
|
||||||
|
messageContent := generateReply(message, command)
|
||||||
|
session.ChannelMessageSend(message.ChannelID, messageContent)
|
||||||
|
} else {
|
||||||
|
session.ChannelMessageSendEmbed(message.ChannelID, command.OutputEmbed)
|
||||||
|
}
|
||||||
|
if command.DeleteInput {
|
||||||
|
session.ChannelMessageDelete(message.ChannelID, message.ID)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// execute custom function
|
||||||
|
command.Function(session, message)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
log.Printf("Denied command %s to user %s.", command.Trigger, userToString(message.Author))
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func removeCooldown(command *Command, uid string) {
|
func removeCooldown(command *Command, uid string) {
|
||||||
time.Sleep(time.Duration(command.Cooldown) * time.Second)
|
time.Sleep(time.Duration(command.Cooldown) * time.Second)
|
||||||
if command.UsersOnCooldown.Contains(uid) {
|
if command.UsersOnCooldown.Contains(uid) {
|
||||||
command.UsersOnCooldown.Remove(uid)
|
command.UsersOnCooldown.Remove(uid)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
func deleteAndSendViaDM(s *discordgo.Session, message *discordgo.MessageCreate) {
|
|
||||||
s.ChannelMessageDelete(message.ChannelID, message.ID)
|
|
||||||
dm := getDMChannelFromMessage(s, message)
|
|
||||||
s.ChannelMessageSend(dm.ID, "Deine Nachricht wurde gelöscht, weil sie ein verbotenes Wort enthielt. Falls du sie editieren und erneut abschicken willst, hier die Nachricht:")
|
|
||||||
s.ChannelMessageSend(dm.ID, message.Content)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func generateReply(message *discordgo.MessageCreate, command *Command) string {
|
func generateReply(message *discordgo.MessageCreate, command *Command) string {
|
||||||
output := command.Output
|
output := command.Output
|
||||||
if command.OutputIsReply {
|
if command.OutputIsReply {
|
||||||
output = fmt.Sprintf(output, message.Author.ID)
|
output = fmt.Sprintf(output, message.Author.ID)
|
||||||
}
|
}
|
||||||
return output
|
return output
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* I’m beginning to doubt my own self-imposed limitations of
|
|
||||||
* only allowing func(session, message) to be attached to commands,
|
|
||||||
* but refactoring that might be more effort than it’s worth.
|
|
||||||
* Hence, small wrappers around the redirect function.
|
|
||||||
*/
|
|
||||||
func modComplain(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
||||||
success := redirectMessage(s, m, config.ModChannel, true)
|
|
||||||
dm, _ := s.UserChannelCreate(m.Author.ID)
|
|
||||||
if success {
|
|
||||||
s.ChannelMessageSend(dm.ID, config.ComplaintReceivedMessage)
|
|
||||||
} else {
|
|
||||||
s.ChannelMessageSend(dm.ID, "Could not send message. Please tell kageru about this.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func selphyComplain(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
||||||
dm_target, _ := s.UserChannelCreate("190958368301645824")
|
|
||||||
success := redirectMessage(s, m, dm_target.ID, true)
|
|
||||||
dm, _ := s.UserChannelCreate(m.Author.ID)
|
|
||||||
if success {
|
|
||||||
s.ChannelMessageSend(dm.ID, config.ComplaintReceivedMessage)
|
|
||||||
} else {
|
|
||||||
s.ChannelMessageSend(dm.ID, "Could not send message. Please tell kageru about this.")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func redirectMessage(s *discordgo.Session, m *discordgo.MessageCreate, target string, isEmbed bool) bool {
|
|
||||||
var err error
|
|
||||||
if isEmbed {
|
|
||||||
embed := &discordgo.MessageEmbed{
|
|
||||||
// Embed are anonymized by default for now. Fits the use case.
|
|
||||||
Author: &discordgo.MessageEmbedAuthor{},
|
|
||||||
Color: 0xbb0000,
|
|
||||||
Description: m.Content,
|
|
||||||
}
|
|
||||||
_, err = s.ChannelMessageSendEmbed(target, embed)
|
|
||||||
} else {
|
|
||||||
_, err = s.ChannelMessageSend(target, messageToString(m.Message))
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Could not redirect message", err)
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
|
|
||||||
func echoMessage(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
||||||
s.ChannelMessageSend(m.ChannelID, m.Content)
|
|
||||||
}
|
|
||||||
|
|
||||||
func giveAgeRole(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
||||||
Member, err := s.GuildMember(config.ServerID, m.Author.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("User could not be retrieved for role assignment\n%s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
dm, err := s.UserChannelCreate(Member.User.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Could not reply to user\n%s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
required := mapset.NewSetWith("416184227672096780", "416184208470310922", "416184150404628480", "416184132473847810", "440996904948465664")
|
|
||||||
for command, role := range config.RoleCommands {
|
|
||||||
if m.Content == command {
|
|
||||||
// Found the command that was triggered
|
|
||||||
// This is a restriction imposed by my own wrapper,
|
|
||||||
// but working around it is not actually necessary for performance and makes the code uglier in other places.
|
|
||||||
for _, newRole := range config.RoleCommands {
|
|
||||||
// check if the user has a twitch sub or any of the patreon roles
|
|
||||||
isAllowed := false
|
|
||||||
for _, curRole := range Member.Roles {
|
|
||||||
if required.Contains(curRole) {
|
|
||||||
isAllowed = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if !isAllowed {
|
|
||||||
s.ChannelMessageSend(dm.ID, "Du kannst dir keine Rolle zuweisen, da weder Patreon noch Twitch mit deinem Account verlinkt ist oder du Selphy auf keiner dieser Plattformen unterstützt. Bei Problemen wende dich bitte an die `!mods`.")
|
|
||||||
log.Printf("Denied role %s to %s. User is neither patron nor sub.", roleName(s.State, newRole), userToString(m.Author))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
for _, curRole := range Member.Roles {
|
|
||||||
// If the user already has one of the available roles, tell them and exit
|
|
||||||
if newRole == curRole {
|
|
||||||
if curRole == role {
|
|
||||||
// User is trying to get the role they already have
|
|
||||||
s.ChannelMessageSend(dm.ID, "Baka, die Rolle hast du doch schon.")
|
|
||||||
log.Printf("Denied Role %s to %s. User already has %s", roleName(s.State, curRole), userToString(m.Author), roleName(s.State, curRole))
|
|
||||||
} else {
|
|
||||||
s.ChannelMessageSend(dm.ID, "Baka, du kannst nur eine der Rollen haben.")
|
|
||||||
log.Printf("Denied Role %s to %s. User already has %s", roleName(s.State, curRole), userToString(m.Author), roleName(s.State, curRole))
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.GuildMemberRoleAdd(config.ServerID, m.Author.ID, role)
|
|
||||||
s.ChannelMessageSend(dm.ID, "Haaai, Ryoukai desu~")
|
|
||||||
log.Printf("Giving Role %s to %s", roleName(s.State, role), userToString(m.Author))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func getHelpEmbed() *discordgo.MessageEmbed {
|
|
||||||
commandList := "Im Folgenden findest du eine automatisch generierte Liste aller Commands. Um herauszufinden, was sie tun, probiere sie aus oder lies den Source Code (siehe unten).\n```- !complain\n- !scomplain\n"
|
|
||||||
for _, command := range commands {
|
|
||||||
if command.Type != CommandTypeRegex && !command.AdminOnly && !command.DMOnly {
|
|
||||||
commandList += "- " + command.Trigger + "\n"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
commandList += "```"
|
|
||||||
embed := &discordgo.MessageEmbed{
|
|
||||||
Author: &discordgo.MessageEmbedAuthor{},
|
|
||||||
Color: 0xffb90f,
|
|
||||||
Description: "__Hilfe__",
|
|
||||||
Fields: []*discordgo.MessageEmbedField{
|
|
||||||
&discordgo.MessageEmbedField{
|
|
||||||
Name: "__Commands__",
|
|
||||||
Value: commandList,
|
|
||||||
Inline: true,
|
|
||||||
},
|
|
||||||
&discordgo.MessageEmbedField{
|
|
||||||
Name: "__Bugs__",
|
|
||||||
Value: fmt.Sprintf("Bei Fragen zum Bot, Vorschlägen, Bugs etc. wende dich bitte an <@%s> oder öffne eine Issue auf https://git.kageru.moe/kageru/discord-selphybot.", config.Admins[0]),
|
|
||||||
Inline: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Thumbnail: &discordgo.MessageEmbedThumbnail{
|
|
||||||
URL: "https://static-cdn.jtvnw.net/emoticons/v1/1068185/3.0",
|
|
||||||
},
|
|
||||||
}
|
|
||||||
return embed
|
|
||||||
}
|
|
||||||
|
|
39
config.go
39
config.go
|
@ -1,38 +1,21 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"os"
|
||||||
"os"
|
"encoding/json"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Embed struct {
|
|
||||||
Message string
|
|
||||||
QuestionsTitle string
|
|
||||||
QuestionsText string
|
|
||||||
BugsTitle string
|
|
||||||
BugsText string
|
|
||||||
Image string
|
|
||||||
}
|
|
||||||
|
|
||||||
type Config struct {
|
type Config struct {
|
||||||
Admins []string
|
Admins []string
|
||||||
ServerID string
|
ServerID string
|
||||||
LockedRoleID string
|
Token string
|
||||||
Token string
|
|
||||||
WelcomeChannel string
|
|
||||||
GeneralChannel string
|
|
||||||
SendWelcomeDM bool
|
|
||||||
RequireAccept bool
|
|
||||||
ComplaintReceivedMessage string
|
|
||||||
ModChannel string
|
|
||||||
WelcomeEmbed Embed
|
|
||||||
RoleCommands map[string]string
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func readConfig() Config {
|
func readConfig() Config {
|
||||||
file, _ := os.Open("config.json")
|
file, _ := os.Open("config.json")
|
||||||
conf := Config{}
|
conf := Config{}
|
||||||
json.NewDecoder(file).Decode(&conf)
|
json.NewDecoder(file).Decode(&conf)
|
||||||
file.Close()
|
file.Close()
|
||||||
return conf
|
return conf
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
24
config.json
24
config.json
|
@ -1,28 +1,8 @@
|
||||||
{
|
{
|
||||||
"Token": "your login token",
|
"Token": "token",
|
||||||
"Admins": [
|
"Admins": [
|
||||||
"your user ID",
|
"your user ID",
|
||||||
"another admin ID"
|
"another admin ID"
|
||||||
],
|
],
|
||||||
"ServerID": "the server the bot will be running on",
|
"ServerID": "356414885292277771"
|
||||||
"RequireAccept": false,
|
|
||||||
"LockedRoleID": "the role to be given to locked users (readme)",
|
|
||||||
"WelcomeChannel": "channel for welcome messages",
|
|
||||||
"GeneralChannel": "channel to @mention users who disabled their DMs",
|
|
||||||
"SendWelcomeDM": true,
|
|
||||||
"ModChannel": "channel for anonymous complaints",
|
|
||||||
"ComplaintReceivedMessage": "Complaint received.",
|
|
||||||
"RoleCommands": {
|
|
||||||
"!u18": "role id for users under 18",
|
|
||||||
"!18+": "role over 18",
|
|
||||||
"!18": "aliases are possible, so this could be the same role as above"
|
|
||||||
},
|
|
||||||
"WelcomeEmbed": {
|
|
||||||
"Message": "Welcome, new user. this message will be part of an embed",
|
|
||||||
"QuestionsTitle": "__Questions__",
|
|
||||||
"QuestionsText": "pls ask the mods. kthxbye",
|
|
||||||
"BugsTitle": "__Bugs__",
|
|
||||||
"BugsText": "please ask <@%s>. (this will be formatted with Admins[0] above",
|
|
||||||
"Image": "https://static-cdn.jtvnw.net/emoticons/v1/970966/3.0"
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
53
events.go
53
events.go
|
@ -1,53 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/bwmarrin/discordgo"
|
|
||||||
"log"
|
|
||||||
)
|
|
||||||
|
|
||||||
func onJoin(s *discordgo.Session, member *discordgo.GuildMemberAdd) {
|
|
||||||
if !member.User.Bot && config.RequireAccept {
|
|
||||||
s.GuildMemberRoleAdd(config.ServerID, member.User.ID, config.LockedRoleID)
|
|
||||||
}
|
|
||||||
if !member.User.Bot && config.SendWelcomeDM {
|
|
||||||
dm, err := s.UserChannelCreate(member.User.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(fmt.Sprintf("Error creating DM with %s", userToString(member.User), err))
|
|
||||||
} else {
|
|
||||||
embed := getWelcomeEmbed()
|
|
||||||
_, err = s.ChannelMessageSendEmbed(dm.ID, embed)
|
|
||||||
if err != nil {
|
|
||||||
log.Println(fmt.Sprintf("Error sending DM to %s", userToString(member.User), err))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if err != nil {
|
|
||||||
// if any of the preceding operations produced an error
|
|
||||||
log.Printf("Sending welcome @mention @%s", userToString(member.User))
|
|
||||||
s.ChannelMessageSend(config.GeneralChannel, fmt.Sprintf("Wilkommen <@%s>. Bitte aktiviere vorübergehend DMs für diesen Server und sende eine Nachricht mit !welcome an mich. https://cdn.discordapp.com/attachments/333450842151714816/591581555219234826/Selphy-queen.png", member.User.ID))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
log.Printf("User joined: %s", userToString(member.User))
|
|
||||||
}
|
|
||||||
|
|
||||||
func onDM(s *discordgo.Session, m *discordgo.MessageCreate) {
|
|
||||||
log.Printf("Received DM from %s with content: “%s”", userToString(m.Author), m.Content)
|
|
||||||
Member, _ := s.GuildMember(config.ServerID, m.Author.ID)
|
|
||||||
dm, _ := s.UserChannelCreate(Member.User.ID)
|
|
||||||
for comm, role := range config.RoleCommands {
|
|
||||||
if m.Content == comm {
|
|
||||||
for _, irole := range config.RoleCommands {
|
|
||||||
for _, mrole := range Member.Roles {
|
|
||||||
if irole == mrole {
|
|
||||||
s.ChannelMessageSend(dm.ID, "Baka, du kannst nur eine der Rollen haben.")
|
|
||||||
log.Printf("Denied Role %s to %s. User already has %s", roleName(s.State, irole), userToString(m.Author), roleName(s.State, irole))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s.ChannelMessageSend(dm.ID, "Haaai, Ryoukai desu~")
|
|
||||||
s.GuildMemberRoleAdd(config.ServerID, m.Author.ID, role)
|
|
||||||
log.Printf("Giving Role %s to %s", roleName(s.State, role), userToString(m.Author))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
57
helpers.go
57
helpers.go
|
@ -1,67 +1,46 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/bwmarrin/discordgo"
|
||||||
"github.com/bwmarrin/discordgo"
|
"fmt"
|
||||||
"log"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
func unlockUser(s *discordgo.Session, id string) {
|
|
||||||
s.GuildMemberRoleRemove(config.ServerID, id, config.LockedRoleID)
|
|
||||||
log.Printf("Removed lock from user: %s", userToString(getUser(s, id)))
|
|
||||||
}
|
|
||||||
|
|
||||||
func userToString(u *discordgo.User) string {
|
func userToString(u *discordgo.User) string {
|
||||||
return fmt.Sprintf("%s#%s (ID: %s)", u.Username, u.Discriminator, u.ID)
|
return fmt.Sprintf("%s#%s (ID: %s)", u.Username, u.Discriminator, u.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func roleName(s *discordgo.State, rid string) string {
|
func roleName(s *discordgo.State, rid string) string {
|
||||||
role, _ := s.Role(config.ServerID, rid)
|
role, _ := s.Role(config.ServerID, rid)
|
||||||
return role.Name
|
return role.Name
|
||||||
}
|
}
|
||||||
|
|
||||||
func channelToString(c *discordgo.Channel) string {
|
func channelToString(c *discordgo.Channel) string {
|
||||||
return fmt.Sprintf("%s (ID: %s) on %s", c.Name, c.ID, c.GuildID)
|
return fmt.Sprintf("%s (ID: %s) on %s", c.Name, c.ID, c.GuildID)
|
||||||
}
|
}
|
||||||
|
|
||||||
func messageToString(m *discordgo.Message) string {
|
func messageToString(m *discordgo.Message) string {
|
||||||
return fmt.Sprintf("<%s#%s>: %s", m.Author.Username, m.Author.Discriminator, m.Content)
|
return fmt.Sprintf("<%s#%s>: %s", m.Author.Username, m.Author.Discriminator, m.Content)
|
||||||
}
|
}
|
||||||
|
|
||||||
func getChannel(s *discordgo.State, cid string) *discordgo.Channel {
|
func getChannel(s *discordgo.State, cid string) *discordgo.Channel {
|
||||||
channel, _ := s.Channel(cid)
|
channel, _ := s.Channel(cid)
|
||||||
return channel
|
return channel
|
||||||
}
|
}
|
||||||
|
|
||||||
func getUser(s *discordgo.Session, uid string) *discordgo.User {
|
func getUser(s *discordgo.Session, uid string) *discordgo.User {
|
||||||
user, _ := s.User(uid)
|
user, _ := s.User(uid)
|
||||||
return user
|
return user
|
||||||
}
|
}
|
||||||
|
|
||||||
func isDM(s *discordgo.Session, m *discordgo.MessageCreate) bool {
|
func isDM(s *discordgo.Session, m *discordgo.MessageCreate) bool {
|
||||||
return (getChannel(s.State, m.ChannelID).Type == discordgo.ChannelTypeDM)
|
return (getChannel(s.State, m.ChannelID).Type == discordgo.ChannelTypeDM)
|
||||||
}
|
|
||||||
|
|
||||||
func getDMChannelFromMessage(s *discordgo.Session, m *discordgo.MessageCreate) *discordgo.Channel {
|
|
||||||
dm, _ := s.UserChannelCreate(m.Author.ID)
|
|
||||||
return dm
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func isAdmin(u *discordgo.User) bool {
|
func isAdmin(u *discordgo.User) bool {
|
||||||
for _, admin := range config.Admins {
|
for _, admin := range config.Admins {
|
||||||
if u.ID == admin {
|
if u.ID == admin {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
|
||||||
|
|
||||||
func getServer() (*discordgo.Guild, error) {
|
|
||||||
server, err := state.Guild(config.ServerID)
|
|
||||||
return server, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func remove(channels []*discordgo.Channel, position int) []*discordgo.Channel {
|
|
||||||
channels[len(channels)-1], channels[position] = channels[position], channels[len(channels)-1]
|
|
||||||
return channels[:len(channels)-1]
|
|
||||||
}
|
}
|
||||||
|
|
126
main.go
126
main.go
|
@ -1,103 +1,63 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"github.com/bwmarrin/discordgo"
|
"os/signal"
|
||||||
"github.com/deckarep/golang-set"
|
"os"
|
||||||
"log"
|
"syscall"
|
||||||
"os"
|
"log"
|
||||||
"os/signal"
|
"github.com/bwmarrin/discordgo"
|
||||||
"syscall"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
var config = readConfig()
|
var config = readConfig()
|
||||||
var commands []*Command
|
var commands []*Command
|
||||||
var state *discordgo.State
|
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
session, err := discordgo.New("Bot " + config.Token)
|
dg, err := discordgo.New("Bot " + config.Token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("error: ", err)
|
fmt.Println("error: ", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
defer session.Close()
|
defer dg.Close()
|
||||||
|
|
||||||
session.AddHandler(evaluateMessage)
|
dg.AddHandler(evaluateMessage)
|
||||||
session.AddHandler(onJoin)
|
dg.AddHandler()
|
||||||
err = session.Open()
|
err = dg.Open()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("No connection:\n", err)
|
fmt.Println("No connection:\n", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
f, err := os.OpenFile("selphybot.log", os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666)
|
f, err := os.OpenFile("selphybot.log", os.O_RDWR | os.O_CREATE | os.O_APPEND, 0666)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Println("Error opening log file:\n", err)
|
fmt.Println("Error opening log file:\n", err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
log.SetOutput(f)
|
log.SetOutput(f)
|
||||||
session.UpdateStatus(0, "!help")
|
dg.UpdateStatus(0, "!help")
|
||||||
state = discordgo.NewState()
|
addCommands()
|
||||||
server, err := session.Guild(config.ServerID)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Guild incorrectly configured. Exiting...")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
state.GuildAdd(server)
|
|
||||||
go checkAndDeleteUnusedChannels(session)
|
|
||||||
addCommands()
|
|
||||||
|
|
||||||
fmt.Println("Bot running. selphyWoo")
|
fmt.Println("Bot running. selphyWoo")
|
||||||
log.Println("Bot running. selphyWoo")
|
log.Println("Bot running. selphyWoo")
|
||||||
sc := make(chan os.Signal, 1)
|
sc := make(chan os.Signal, 1)
|
||||||
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
||||||
<-sc
|
<-sc
|
||||||
|
|
||||||
for _, channel := range tempChannels {
|
fmt.Println("Exiting...")
|
||||||
session.ChannelDelete(channel.ID)
|
log.Println("Exiting...")
|
||||||
}
|
|
||||||
fmt.Println("Exiting...")
|
|
||||||
log.Println("Exiting...")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// I’ll just put all of the commands here for now.
|
// I’ll just put all of the commands here for now.
|
||||||
func addCommands() {
|
func addCommands() {
|
||||||
// Moderation
|
// Moderation
|
||||||
registerCommand(Command{Trigger: "^[^`]*([()|DoOvVcC][-=^']?;|;[-=^']?[()|DoOpPvVcC3]|:wink:|😉)[^`]*$", Output: "<@%s> Oboe!", DeleteInput: true, OutputIsReply: true, Type: CommandTypeRegex})
|
registerCommand(Command{Trigger: "(\\s|\n|^)[nN][hH]([ ?.,\n]|$)", Output: "<@%s> „nh“ ist kein Wort, du Oboe!", DeleteInput: true, OutputIsReply: true, Type: CommandTypeRegex})
|
||||||
registerCommand(Command{Trigger: "(\\s|\n|^)[nN][hH]([ ?.,\n]|$)", Output: "<@%s> „nh“ ist kein Wort, du Oboe!", DeleteInput: true, OutputIsReply: true, Type: CommandTypeRegex})
|
registerCommand(Command{Trigger: "einzigste", Output: "<@%s> Es heißt „einzige“, du Tuba.", DeleteInput: true, OutputIsReply: true, Type: CommandTypeContains})
|
||||||
registerCommand(Command{Trigger: "einzigste", Output: "<@%s> Es heißt „einzige“, du Tuba.", DeleteInput: true, OutputIsReply: true, Type: CommandTypeContains})
|
|
||||||
registerCommand(Command{Trigger: "!complain", Type: CommandTypePrefix, DMOnly: true, Function: modComplain})
|
|
||||||
registerCommand(Command{Trigger: "!scomplain", Type: CommandTypePrefix, DMOnly: true, Function: selphyComplain})
|
|
||||||
registerCommand(Command{Trigger: "!beschwerde", Type: CommandTypePrefix, DMOnly: true, Function: modComplain})
|
|
||||||
|
|
||||||
for comm, _ := range config.RoleCommands {
|
// Misc
|
||||||
registerCommand(Command{Trigger: comm, Type: CommandTypeFullMatch, DMOnly: true, Function: giveAgeRole})
|
registerCommand(Command{Trigger: "<@%s> <3", Output: "<@%s> <3", Type: CommandTypeFullMatch, AdminOnly: false, OutputIsReply: true, RequiresMention: true})
|
||||||
}
|
registerCommand(Command{Trigger: "ist flauschig", Output: "Du bist flauschig! <3", Type: CommandTypeContains})
|
||||||
|
|
||||||
// Fluff
|
fmt.Printf("Successfully initialized %d commands\n", len(commands))
|
||||||
registerCommand(Command{Trigger: "o/", Output: "\\o", Type: CommandTypeFullMatch, Cooldown: 10})
|
log.Printf("Successfully initialized %d commands", len(commands))
|
||||||
registerCommand(Command{Trigger: "\\o", Output: "o/", Type: CommandTypeFullMatch, Cooldown: 10})
|
|
||||||
registerCommand(Command{Trigger: "\\o/", Output: "/o\\", Type: CommandTypeFullMatch, Cooldown: 10})
|
|
||||||
registerCommand(Command{Trigger: "/o\\", Output: "\\o/", Type: CommandTypeFullMatch, Cooldown: 10})
|
|
||||||
registerCommand(Command{Trigger: "!heil", Output: "(ノ・ェ・)ノ Selphy (ノ・ェ・)ノ", Type: CommandTypeFullMatch, Cooldown: 30})
|
|
||||||
registerCommand(Command{Trigger: "ayy", Output: "lmao", Type: CommandTypeFullMatch, Cooldown: 0})
|
|
||||||
registerCommand(Command{Trigger: "<:selphyDango:531594585424527370>", Output: "<:dango:430669469799677953> :notes: Dango Daikazoku :notes: <:dango:430669469799677953>", Type: CommandTypeFullMatch, Cooldown: 10800})
|
|
||||||
registerCommand(Command{Trigger: "praise the sun", Output: "If only I could be so grossly incandescent \\\\[T]/", Type: CommandTypeContains, IgnoreCase: true, Cooldown: 85600})
|
|
||||||
|
|
||||||
// Information
|
|
||||||
registerCommand(Command{Trigger: "!welcome", OutputEmbed: getWelcomeEmbed(), Type: CommandTypeFullMatch, DMOnly: true})
|
|
||||||
registerCommand(Command{Trigger: "!mods", Output: "Bei Fragen, Problemen und Beschwerden wende dich bitte an die Moderatoren oder schick mir eine Nachricht beginnend mit !complain, um dich anonym zu beschweren.\nAktuell anwesende Mods werden dir rechts mit dem Rang „Maid“ angezeigt.", Type: CommandTypeFullMatch})
|
|
||||||
|
|
||||||
// Features :Pog:
|
|
||||||
registerCommand(Command{Trigger: "!vc ", Type: CommandTypePrefix, Function: parseVoiceChannelCommand, AllowedChannels: mapset.NewSetWith("525852491976278016")})
|
|
||||||
|
|
||||||
// Admin and/or debug
|
|
||||||
registerCommand(Command{Trigger: "<@%s> <3", Output: "<@%s> <3", Type: CommandTypeFullMatch, AdminOnly: true, OutputIsReply: true, RequiresMention: true})
|
|
||||||
registerCommand(Command{Trigger: "echo", Type: CommandTypePrefix, Function: echoMessage, AdminOnly: true})
|
|
||||||
|
|
||||||
// This needs to be the last command because getHelpEmbed is evaluated here once, not on every function call. Putting it too early will result in missing commands in the output.
|
|
||||||
registerCommand(Command{Trigger: "!help", OutputEmbed: getHelpEmbed(), Type: CommandTypeFullMatch})
|
|
||||||
|
|
||||||
fmt.Printf("Successfully initialized %d commands\n", len(commands))
|
|
||||||
log.Printf("Successfully initialized %d commands", len(commands))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,74 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/bwmarrin/discordgo"
|
|
||||||
"log"
|
|
||||||
"strconv"
|
|
||||||
"strings"
|
|
||||||
"time"
|
|
||||||
)
|
|
||||||
|
|
||||||
var tempChannels []*discordgo.Channel
|
|
||||||
|
|
||||||
func parseVoiceChannelCommand(session *discordgo.Session, message *discordgo.MessageCreate) {
|
|
||||||
userLimit, err := strconv.Atoi(strings.Split(message.Content, " ")[1])
|
|
||||||
if err != nil {
|
|
||||||
session.ChannelMessageSend(message.ChannelID, "Error: Expected a number after !vc")
|
|
||||||
log.Printf("Incorrect syntax for !vc, “%s” triggered by %s", message.Content, userToString(message.Author))
|
|
||||||
return
|
|
||||||
}
|
|
||||||
if userLimit > 99 {
|
|
||||||
session.ChannelMessageSend(message.ChannelID, fmt.Sprintf("Als ob %d Leute *mit dir* in einen Channel wollen.", userLimit-1))
|
|
||||||
log.Printf("%s tried to create a channel with %d slots", userToString(message.Author), userLimit)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
createData := discordgo.GuildChannelCreateData{
|
|
||||||
Name: fmt.Sprintf("%s’s Volatile Corner", message.Author.Username),
|
|
||||||
Type: discordgo.ChannelTypeGuildVoice,
|
|
||||||
UserLimit: userLimit,
|
|
||||||
ParentID: "410162599762853909",
|
|
||||||
}
|
|
||||||
channel, err := session.GuildChannelCreateComplex(message.GuildID, createData)
|
|
||||||
if err != nil {
|
|
||||||
session.ChannelMessageSend(message.ChannelID, "Couldn’t create the voice channel. Please bug kageru about this.")
|
|
||||||
log.Printf("Failed to create voice channel, %s", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
tempChannels = append(tempChannels, channel)
|
|
||||||
err = session.GuildMemberMove(config.ServerID, message.Author.ID, channel.ID)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Couldn’t move user %s: %s", userToString(message.Author), err)
|
|
||||||
}
|
|
||||||
session.ChannelMessageSend(message.ChannelID, "haaaai~")
|
|
||||||
log.Printf("Created channel %s", channel.ID)
|
|
||||||
}
|
|
||||||
|
|
||||||
func checkAndDeleteUnusedChannels(session *discordgo.Session) {
|
|
||||||
for true {
|
|
||||||
time.Sleep(30 * time.Second)
|
|
||||||
server, err := getServer()
|
|
||||||
if err == nil {
|
|
||||||
for i, channel := range tempChannels {
|
|
||||||
if channelIsEmpty(channel.ID, server.VoiceStates) {
|
|
||||||
session.ChannelDelete(channel.ID)
|
|
||||||
tempChannels = remove(tempChannels, i)
|
|
||||||
log.Printf("Deleted channel %s", channel.ID)
|
|
||||||
log.Printf("Tempchannels: %d", len(tempChannels))
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
log.Printf("Could not retrieve voice state from API, %s", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func channelIsEmpty(channelID string, voiceStates []*discordgo.VoiceState) bool {
|
|
||||||
for _, state := range voiceStates {
|
|
||||||
if channelID == state.ChannelID {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
|
||||||
}
|
|
29
welcome.go
29
welcome.go
|
@ -1,29 +0,0 @@
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"fmt"
|
|
||||||
"github.com/bwmarrin/discordgo"
|
|
||||||
)
|
|
||||||
|
|
||||||
func getWelcomeEmbed() *discordgo.MessageEmbed {
|
|
||||||
return &discordgo.MessageEmbed{
|
|
||||||
Author: &discordgo.MessageEmbedAuthor{},
|
|
||||||
Color: 0xffb90f,
|
|
||||||
Description: config.WelcomeEmbed.Message,
|
|
||||||
Fields: []*discordgo.MessageEmbedField{
|
|
||||||
&discordgo.MessageEmbedField{
|
|
||||||
Name: config.WelcomeEmbed.QuestionsTitle,
|
|
||||||
Value: config.WelcomeEmbed.QuestionsText,
|
|
||||||
Inline: true,
|
|
||||||
},
|
|
||||||
&discordgo.MessageEmbedField{
|
|
||||||
Name: config.WelcomeEmbed.BugsTitle,
|
|
||||||
Value: fmt.Sprintf(config.WelcomeEmbed.BugsText, config.Admins[0]),
|
|
||||||
Inline: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
Thumbnail: &discordgo.MessageEmbedThumbnail{
|
|
||||||
URL: config.WelcomeEmbed.Image,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
}
|
|
Loading…
Reference in New Issue
Block a user