parent
23c0434ed3
commit
28d78bdb8c
@ -1,13 +1,20 @@
|
|||||||
FROM golang:1.14-alpine
|
FROM golang:1.14-alpine as dev
|
||||||
|
|
||||||
WORKDIR /go/src/Goff
|
WORKDIR /go/src/Goff
|
||||||
COPY ./go.mod .
|
COPY ./go.mod .
|
||||||
|
COPY ./go.sum .
|
||||||
|
|
||||||
RUN apk add --no-cache git
|
RUN go mod download
|
||||||
|
|
||||||
RUN go get -d -v ./...
|
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
RUN go install -v ./...
|
RUN go install github.com/dustinpianalto/goff
|
||||||
|
|
||||||
|
CMD [ "go", "run", "goff.go"]
|
||||||
|
|
||||||
|
from alpine
|
||||||
|
|
||||||
|
WORKDIR /bin
|
||||||
|
|
||||||
|
COPY --from=dev /go/bin/goff ./goff
|
||||||
|
|
||||||
ENTRYPOINT /go/bin/goff
|
CMD [ "goff" ]
|
||||||
|
|||||||
@ -0,0 +1,135 @@
|
|||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/dustinpianalto/goff/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OnGuildMemberAddLogging(s *discordgo.Session, member *discordgo.GuildMemberAdd) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Println("Recovered from panic in OnGuildMemberAddLogging", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
var channelID string
|
||||||
|
row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", member.GuildID)
|
||||||
|
err := row.Scan(&channelID)
|
||||||
|
if err != nil || channelID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guild, err := s.State.Guild(member.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var title string
|
||||||
|
if member.User.Bot {
|
||||||
|
title = "Bot Joined"
|
||||||
|
} else {
|
||||||
|
title = "Member Joined"
|
||||||
|
}
|
||||||
|
|
||||||
|
thumb := &discordgo.MessageEmbedThumbnail{
|
||||||
|
URL: member.User.AvatarURL(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
int64ID, _ := strconv.ParseInt(member.User.ID, 10, 64)
|
||||||
|
snow := utils.ParseSnowflake(int64ID)
|
||||||
|
|
||||||
|
field := &discordgo.MessageEmbedField{
|
||||||
|
Name: "User was created:",
|
||||||
|
Value: utils.ParseDateString(snow.CreationTime),
|
||||||
|
Inline: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
joinTime, _ := member.JoinedAt.Parse()
|
||||||
|
|
||||||
|
embed := &discordgo.MessageEmbed{
|
||||||
|
Title: title,
|
||||||
|
Description: fmt.Sprintf("%v (%v) Has Joined the Server", member.User.Mention(), member.User.ID),
|
||||||
|
Color: 0x0cc56a,
|
||||||
|
Thumbnail: thumb,
|
||||||
|
Footer: &discordgo.MessageEmbedFooter{
|
||||||
|
Text: fmt.Sprintf("Current Member Count: %v", guild.MemberCount),
|
||||||
|
IconURL: guild.IconURL(),
|
||||||
|
},
|
||||||
|
Timestamp: joinTime.Format(time.RFC3339),
|
||||||
|
Fields: []*discordgo.MessageEmbedField{field},
|
||||||
|
}
|
||||||
|
s.ChannelMessageSendEmbed(channelID, embed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnGuildMemberRemoveLogging(s *discordgo.Session, member *discordgo.GuildMemberRemove) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Println("Recovered from panic in OnGuildMemberAddLogging", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
timeNow := time.Now()
|
||||||
|
var channelID string
|
||||||
|
row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", member.GuildID)
|
||||||
|
err := row.Scan(&channelID)
|
||||||
|
if err != nil || channelID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
guild, err := s.State.Guild(member.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var title string
|
||||||
|
if member.User.Bot {
|
||||||
|
title = "Bot Left"
|
||||||
|
} else {
|
||||||
|
title = "Member Left"
|
||||||
|
}
|
||||||
|
|
||||||
|
thumb := &discordgo.MessageEmbedThumbnail{
|
||||||
|
URL: member.User.AvatarURL(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
desc := ""
|
||||||
|
al, err := s.GuildAuditLog(member.GuildID, "", "", 20, 1)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
} else {
|
||||||
|
for _, log := range al.AuditLogEntries {
|
||||||
|
if log.TargetID == member.User.ID {
|
||||||
|
int64ID, _ := strconv.ParseInt(log.ID, 10, 64)
|
||||||
|
logSnow := utils.ParseSnowflake(int64ID)
|
||||||
|
if timeNow.Sub(logSnow.CreationTime).Seconds() <= 10 {
|
||||||
|
user, err := s.User(log.UserID)
|
||||||
|
if err == nil {
|
||||||
|
desc = fmt.Sprintf("%v (%v) was Kicked by: %v\nReason: %v", member.User.String(), member.User.ID, user.String(), log.Reason)
|
||||||
|
} else {
|
||||||
|
desc = fmt.Sprintf("%v (%v) was Kicked by: %v\nReason: %v", member.User.String(), member.User.ID, log.UserID, log.Reason)
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if desc == "" {
|
||||||
|
desc = fmt.Sprintf("%v (%v) Has Left the Server", member.User.String(), member.User.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
embed := &discordgo.MessageEmbed{
|
||||||
|
Title: title,
|
||||||
|
Description: desc,
|
||||||
|
Color: 0xff9431,
|
||||||
|
Thumbnail: thumb,
|
||||||
|
Footer: &discordgo.MessageEmbedFooter{
|
||||||
|
Text: fmt.Sprintf("Current Member Count: %v", guild.MemberCount),
|
||||||
|
IconURL: guild.IconURL(),
|
||||||
|
},
|
||||||
|
Timestamp: timeNow.Format(time.RFC3339),
|
||||||
|
}
|
||||||
|
s.ChannelMessageSendEmbed(channelID, embed)
|
||||||
|
}
|
||||||
@ -0,0 +1,97 @@
|
|||||||
|
package events
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/dustinpianalto/goff/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func OnMessageUpdate(session *discordgo.Session, m *discordgo.MessageUpdate) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Println("Recovered from panic in OnMessageUpdate", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
msg := m.BeforeUpdate
|
||||||
|
if msg.Author.Bot {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var channelID string
|
||||||
|
row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", msg.GuildID)
|
||||||
|
err := row.Scan(&channelID)
|
||||||
|
if err != nil || channelID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := session.State.Channel(msg.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
embed := &discordgo.MessageEmbed{
|
||||||
|
Title: fmt.Sprintf("Message Edited: %v", msg.ID),
|
||||||
|
Description: fmt.Sprintf("**Before:** %v\n**After:** %v\nIn Channel: %v", msg.Content, m.Content, channel.Mention()),
|
||||||
|
Color: session.State.UserColor(msg.Author.ID, channelID),
|
||||||
|
Footer: &discordgo.MessageEmbedFooter{
|
||||||
|
Text: fmt.Sprintf("Author: %v", msg.Author.String()),
|
||||||
|
IconURL: msg.Author.AvatarURL(""),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
session.ChannelMessageSendEmbed(channelID, embed)
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnMessageDelete(session *discordgo.Session, m *discordgo.MessageDelete) {
|
||||||
|
defer func() {
|
||||||
|
if r := recover(); r != nil {
|
||||||
|
log.Println("Recovered from panic in OnMessageDelete", r)
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
msg := m.BeforeDelete
|
||||||
|
if msg == nil {
|
||||||
|
log.Printf("Message Deleted but the original message was not in my cache so we are ignoring it.\nMessage ID: %v\nGuild ID: %v\nChannel ID: %v\n", m.ID, m.GuildID, m.ChannelID)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if msg.Author.Bot {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var channelID string
|
||||||
|
row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", msg.GuildID)
|
||||||
|
err := row.Scan(&channelID)
|
||||||
|
if err != nil || channelID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := session.State.Channel(msg.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
desc := ""
|
||||||
|
al, err := session.GuildAuditLog(msg.GuildID, "", "", 72, 1)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
} else {
|
||||||
|
for _, log := range al.AuditLogEntries {
|
||||||
|
if log.TargetID == msg.Author.ID && log.Options.ChannelID == msg.ChannelID {
|
||||||
|
user, err := session.User(log.UserID)
|
||||||
|
if err == nil {
|
||||||
|
desc = fmt.Sprintf("**Content:** %v\nIn Channel: %v\nDeleted By: %v", msg.Content, channel.Mention(), user.Mention())
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if desc == "" {
|
||||||
|
desc = fmt.Sprintf("**Content:** %v\nIn Channel: %v", msg.Content, channel.Mention())
|
||||||
|
}
|
||||||
|
embed := &discordgo.MessageEmbed{
|
||||||
|
Title: fmt.Sprintf("Message Deleted: %v", msg.ID),
|
||||||
|
Description: desc,
|
||||||
|
Color: session.State.UserColor(msg.Author.ID, channelID),
|
||||||
|
Footer: &discordgo.MessageEmbedFooter{
|
||||||
|
Text: fmt.Sprintf("Author: %v", msg.Author.String()),
|
||||||
|
IconURL: msg.Author.AvatarURL(""),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
session.ChannelMessageSendEmbed(channelID, embed)
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
package exts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pCommand(ctx disgoman.Context, args []string) {
|
||||||
|
input := strings.Join(args, "")
|
||||||
|
const LENGTH = 1999
|
||||||
|
var mem [LENGTH]byte
|
||||||
|
pointer := 0
|
||||||
|
l := 0
|
||||||
|
for i := 0; i < len(input); i++ {
|
||||||
|
if input[i] == 'L' {
|
||||||
|
if pointer == 0 {
|
||||||
|
pointer = LENGTH - 1
|
||||||
|
} else {
|
||||||
|
pointer--
|
||||||
|
}
|
||||||
|
} else if input[i] == 'R' {
|
||||||
|
if pointer == LENGTH-1 {
|
||||||
|
pointer = 0
|
||||||
|
} else {
|
||||||
|
pointer++
|
||||||
|
}
|
||||||
|
} else if input[i] == '+' {
|
||||||
|
mem[pointer]++
|
||||||
|
} else if input[i] == '-' {
|
||||||
|
mem[pointer]--
|
||||||
|
} else if input[i] == '(' {
|
||||||
|
if mem[pointer] == 0 {
|
||||||
|
i++
|
||||||
|
for l > 0 || input[i] != ')' {
|
||||||
|
if input[i] == '(' {
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
if input[i] == ')' {
|
||||||
|
l--
|
||||||
|
}
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if input[i] == ')' {
|
||||||
|
if mem[pointer] != 0 {
|
||||||
|
i--
|
||||||
|
for l > 0 || input[i] != '(' {
|
||||||
|
if input[i] == ')' {
|
||||||
|
l++
|
||||||
|
}
|
||||||
|
if input[i] == '(' {
|
||||||
|
l--
|
||||||
|
}
|
||||||
|
i--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: fmt.Sprintf("Invalid Character: %v", input[i]),
|
||||||
|
Error: errors.New("invalid character"),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
var out []byte
|
||||||
|
for _, i := range mem {
|
||||||
|
if i != 0 {
|
||||||
|
out = append(out, i)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, err := ctx.Send(string(out))
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Couldn't send results",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -0,0 +1,84 @@
|
|||||||
|
package exts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
"github.com/dustinpianalto/rpnparse"
|
||||||
|
)
|
||||||
|
|
||||||
|
func interleave(ctx disgoman.Context, args []string) {
|
||||||
|
if len(args) == 2 {
|
||||||
|
x, err := strconv.ParseInt(args[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
y, err := strconv.ParseInt(args[1], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var z = int64(0)
|
||||||
|
for i := 0; i < 64; i++ {
|
||||||
|
x_masked_i := x & (1 << i)
|
||||||
|
y_masked_i := y & (1 << i)
|
||||||
|
|
||||||
|
z |= x_masked_i << i
|
||||||
|
z |= y_masked_i << (i + 1)
|
||||||
|
}
|
||||||
|
ctx.Send(fmt.Sprintf("%v", z))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func deinterleave(ctx disgoman.Context, args []string) {
|
||||||
|
if len(args) == 1 {
|
||||||
|
z, err := strconv.ParseInt(args[0], 10, 64)
|
||||||
|
if err != nil {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var x = int64(0)
|
||||||
|
var y = int64(0)
|
||||||
|
i := 0
|
||||||
|
for z > 0 {
|
||||||
|
x |= (z & 1) << i
|
||||||
|
z >>= 1
|
||||||
|
y |= (z & 1) << i
|
||||||
|
z >>= 1
|
||||||
|
i++
|
||||||
|
}
|
||||||
|
ctx.Send(fmt.Sprintf("(%v, %v)", x, y))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func generateRPNCommand(ctx disgoman.Context, args []string) {
|
||||||
|
rpn, err := rpnparse.GenerateRPN(args)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Send(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Send(rpn)
|
||||||
|
}
|
||||||
|
|
||||||
|
func parseRPNCommand(ctx disgoman.Context, args []string) {
|
||||||
|
res, err := rpnparse.ParseRPN(args)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Send(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Send(fmt.Sprintf("The result is: %v", res))
|
||||||
|
}
|
||||||
|
|
||||||
|
func solveCommand(ctx disgoman.Context, args []string) {
|
||||||
|
rpn, err := rpnparse.GenerateRPN(args)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Send(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
res, err := rpnparse.ParseRPN(strings.Split(rpn, " "))
|
||||||
|
if err != nil {
|
||||||
|
ctx.Send(err.Error())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Send(fmt.Sprintf("The result is: %v", res))
|
||||||
|
}
|
||||||
@ -0,0 +1,290 @@
|
|||||||
|
package exts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
"github.com/dustinpianalto/goff/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Guild management commands
|
||||||
|
|
||||||
|
func loggingChannel(ctx disgoman.Context, args []string) {
|
||||||
|
var idString string
|
||||||
|
if len(args) > 0 {
|
||||||
|
idString = args[0]
|
||||||
|
if strings.HasPrefix(idString, "<#") && strings.HasSuffix(idString, ">") {
|
||||||
|
idString = idString[2 : len(idString)-1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
idString = ""
|
||||||
|
}
|
||||||
|
fmt.Println(idString)
|
||||||
|
if idString == "" {
|
||||||
|
_, err := utils.Database.Exec("UPDATE guilds SET logging_channel='' WHERE id=$1;", ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error Updating Database",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send("Logging Channel Updated.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := ctx.Session.State.Channel(idString)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Can't find that channel.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if channel.GuildID != ctx.Guild.ID {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "The channel passed is not in this guild.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = utils.Database.Exec("UPDATE guilds SET logging_channel=$1 WHERE id=$2;", idString, ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error Updating Database",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send("Logging Channel Updated.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getLoggingChannel(ctx disgoman.Context, _ []string) {
|
||||||
|
var channelID string
|
||||||
|
row := utils.Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", ctx.Guild.ID)
|
||||||
|
err := row.Scan(&channelID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error getting data from the database.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if channelID == "" {
|
||||||
|
_, _ = ctx.Send("The logging channel is not set.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := ctx.Session.State.GuildChannel(ctx.Guild.ID, channelID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "I got the channel ID but it does not appear to be a valid channel in this guild.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send(fmt.Sprintf("The logging channel is currently %s", channel.Mention()))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func welcomeChannel(ctx disgoman.Context, args []string) {
|
||||||
|
var idString string
|
||||||
|
if len(args) > 0 {
|
||||||
|
idString = args[0]
|
||||||
|
if strings.HasPrefix(idString, "<#") && strings.HasSuffix(idString, ">") {
|
||||||
|
idString = idString[2 : len(idString)-1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
idString = ""
|
||||||
|
}
|
||||||
|
fmt.Println(idString)
|
||||||
|
if idString == "" {
|
||||||
|
_, err := utils.Database.Exec("UPDATE guilds SET welcome_channel='' WHERE id=$1;", ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error Updating Database",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send("Welcomer Disabled.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := ctx.Session.State.Channel(idString)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Can't find that channel.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if channel.GuildID != ctx.Guild.ID {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "The channel passed is not in this guild.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = utils.Database.Exec("UPDATE guilds SET welcome_channel=$1 WHERE id=$2;", idString, ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error Updating Database",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send("Welcome Channel Updated.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func getWelcomeChannel(ctx disgoman.Context, _ []string) {
|
||||||
|
var channelID string
|
||||||
|
row := utils.Database.QueryRow("SELECT welcome_channel FROM guilds where id=$1", ctx.Guild.ID)
|
||||||
|
err := row.Scan(&channelID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error getting data from the database.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if channelID == "" {
|
||||||
|
_, _ = ctx.Send("The welcomer is disabled.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := ctx.Session.State.GuildChannel(ctx.Guild.ID, channelID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "I got the channel ID but it does not appear to be a valid channel in this guild.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send(fmt.Sprintf("The welcome channel is currently %s", channel.Mention()))
|
||||||
|
}
|
||||||
|
|
||||||
|
func addGuildCommand(ctx disgoman.Context, args []string) {
|
||||||
|
var guildID string
|
||||||
|
row := utils.Database.QueryRow("SELECT id FROM guilds where id=$1", ctx.Guild.ID)
|
||||||
|
err := row.Scan(&guildID)
|
||||||
|
if err == nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "This guild is already in my database",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, err = utils.Database.Query("INSERT INTO guilds (id) VALUES ($1)", ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "There was a problem inserting this guild into the database",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send("This guild has been added.")
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func puzzleChannel(ctx disgoman.Context, args []string) {
|
||||||
|
var idString string
|
||||||
|
if len(args) > 0 {
|
||||||
|
idString = args[0]
|
||||||
|
if strings.HasPrefix(idString, "<#") && strings.HasSuffix(idString, ">") {
|
||||||
|
idString = idString[2 : len(idString)-1]
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
idString = ""
|
||||||
|
}
|
||||||
|
fmt.Println(idString)
|
||||||
|
if idString == "" {
|
||||||
|
_, err := utils.Database.Exec("UPDATE guilds SET puzzle_channel='' WHERE id=$1;", ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error Updating Database",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send("Puzzle Channel Updated.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := ctx.Session.State.Channel(idString)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Can't find that channel.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if channel.GuildID != ctx.Guild.ID {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "The channel passed is not in this guild.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, err = utils.Database.Exec("UPDATE guilds SET puzzle_channel=$1 WHERE id=$2;", idString, ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error Updating Database",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send("Puzzle Channel Updated.")
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPuzzleChannel(ctx disgoman.Context, _ []string) {
|
||||||
|
var channelID string
|
||||||
|
row := utils.Database.QueryRow("SELECT puzzle_channel FROM guilds where id=$1", ctx.Guild.ID)
|
||||||
|
err := row.Scan(&channelID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error getting data from the database.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if channelID == "" {
|
||||||
|
_, _ = ctx.Send("The puzzle channel is not set.")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := ctx.Session.State.GuildChannel(ctx.Guild.ID, channelID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "I got the channel ID but it does not appear to be a valid channel in this guild.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send(fmt.Sprintf("The puzzle channel is currently %s", channel.Mention()))
|
||||||
|
return
|
||||||
|
}
|
||||||
@ -0,0 +1,234 @@
|
|||||||
|
package exts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
)
|
||||||
|
|
||||||
|
func AddCommandHandlers(h *disgoman.CommandManager) {
|
||||||
|
// Arguments:
|
||||||
|
// name - command name - string
|
||||||
|
// desc - command description - string
|
||||||
|
// owneronly - only allow owners to run - bool
|
||||||
|
// hidden - hide command from non-owners - bool
|
||||||
|
// perms - permissisions required - anpan.Permission (int)
|
||||||
|
// type - command type, sets where the command is available
|
||||||
|
// run - function to run - func(anpan.Context, []string) / CommandRunFunc
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "ping",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Check the bot's ping",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: pingCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "say",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Repeat a message",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
SanitizeEveryone: true,
|
||||||
|
Invoke: sayCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "user",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Get user info",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: userCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "git",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Show my github link",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: gitCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "tag",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Get a tag",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: tagCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "addtag",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Add a tag",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
SanitizeEveryone: true,
|
||||||
|
Invoke: addTagCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "invite",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Get the invite link for this bot or others",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: inviteCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "P",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Interpret a P\" program and return the results",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: pCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "set-logging-channel",
|
||||||
|
Aliases: []string{"slc"},
|
||||||
|
Description: "Set the channel logging messages will be sent to.",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionManageServer,
|
||||||
|
Invoke: loggingChannel,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "get-logging-channel",
|
||||||
|
Aliases: []string{"glc"},
|
||||||
|
Description: "Gets the channel logging messages will be sent to.",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionManageServer,
|
||||||
|
Invoke: getLoggingChannel,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "set-welcome-channel",
|
||||||
|
Aliases: []string{"swc"},
|
||||||
|
Description: "Set the channel welcome messages will be sent to.",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionManageServer,
|
||||||
|
Invoke: welcomeChannel,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "get-welcome-channel",
|
||||||
|
Aliases: []string{"gwc"},
|
||||||
|
Description: "Gets the channel welcome messages will be sent to.",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionManageServer,
|
||||||
|
Invoke: getWelcomeChannel,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "kick",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Kicks the given user with the given reason",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionKickMembers,
|
||||||
|
Invoke: kickUserCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "addGuild",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Adds the current guild to the database",
|
||||||
|
OwnerOnly: true,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionManageServer,
|
||||||
|
Invoke: addGuildCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "ban",
|
||||||
|
Aliases: []string{"ban-no-delete"},
|
||||||
|
Description: "Bans the given user with the given reason",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionBanMembers,
|
||||||
|
Invoke: banUserCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "unban",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Unbans the given user",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionBanMembers,
|
||||||
|
Invoke: unbanUserCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "remind",
|
||||||
|
Aliases: nil,
|
||||||
|
Description: "Remind me at a later time",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: addReminderCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "encode",
|
||||||
|
Aliases: []string{"e"},
|
||||||
|
Description: "Encode 2 numbers",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: interleave,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "decode",
|
||||||
|
Aliases: []string{"d"},
|
||||||
|
Description: "Decode 1 number into 2",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: deinterleave,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "set-puzzle-channel",
|
||||||
|
Aliases: []string{"spc"},
|
||||||
|
Description: "Set the channel puzzle messages will be sent to.",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionManageServer,
|
||||||
|
Invoke: puzzleChannel,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "get-puzzle-channel",
|
||||||
|
Aliases: []string{"gpc"},
|
||||||
|
Description: "Gets the channel puzzle messages will be sent to.",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: disgoman.PermissionManageServer,
|
||||||
|
Invoke: getPuzzleChannel,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "RPN",
|
||||||
|
Aliases: []string{"rpn"},
|
||||||
|
Description: "Convert infix to rpn",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: generateRPNCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "ParseRPN",
|
||||||
|
Aliases: []string{"PRPN", "prpn"},
|
||||||
|
Description: "Parse RPN string and return the result",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: parseRPNCommand,
|
||||||
|
})
|
||||||
|
_ = h.AddCommand(&disgoman.Command{
|
||||||
|
Name: "solve",
|
||||||
|
Aliases: []string{"math", "infix"},
|
||||||
|
Description: "Solve infix equation and return the result",
|
||||||
|
OwnerOnly: false,
|
||||||
|
Hidden: false,
|
||||||
|
RequiredPermissions: 0,
|
||||||
|
Invoke: solveCommand,
|
||||||
|
})
|
||||||
|
}
|
||||||
@ -0,0 +1,137 @@
|
|||||||
|
package exts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
"github.com/dustinpianalto/goff/utils"
|
||||||
|
"github.com/kballard/go-shellquote"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addTagCommand(ctx disgoman.Context, input []string) {
|
||||||
|
if len(input) >= 1 {
|
||||||
|
args, err := shellquote.Split(strings.Join(input, " "))
|
||||||
|
if err != nil {
|
||||||
|
if strings.Contains(err.Error(), "Unterminated") {
|
||||||
|
args = strings.SplitN(strings.Join(args, " "), " ", 2)
|
||||||
|
} else {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
queryString := `SELECT tags.id, tags.tag, tags.content from tags
|
||||||
|
WHERE tags.guild_id = $1
|
||||||
|
AND tags.tag = $2;`
|
||||||
|
row := utils.Database.QueryRow(queryString, ctx.Guild.ID, args[0])
|
||||||
|
var dest string
|
||||||
|
if err := row.Scan(&dest); err != nil {
|
||||||
|
tag := args[0]
|
||||||
|
if tag == "" {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "That is not a valid tag name",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if len(args) <= 1 {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "I got a name but no value",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
value := args[1]
|
||||||
|
if value == "" {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "You have to include a content for the tag",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queryString = `INSERT INTO tags (tag, content, creator, guild_id) VALUES ($1, $2, $3, $4);`
|
||||||
|
_, err := utils.Database.Exec(queryString, tag, value, ctx.Message.Author.ID, ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.Send(err.Error())
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
ctx.Send(fmt.Sprintf("Tag %v added successfully.", tag))
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "That tag already exists",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.Send("You need to tell me what tag you want to add...")
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "You need to tell me what tag you want to add...",
|
||||||
|
Error: errors.New("nothing to do"),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func tagCommand(ctx disgoman.Context, args []string) {
|
||||||
|
if len(args) >= 1 {
|
||||||
|
tagString := strings.Join(args, " ")
|
||||||
|
queryString := `SELECT tags.id, tags.tag, tags.content from tags
|
||||||
|
WHERE tags.guild_id = $1;`
|
||||||
|
rows, err := utils.Database.Query(queryString, ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
for rows.Next() {
|
||||||
|
var (
|
||||||
|
id int
|
||||||
|
tag string
|
||||||
|
content string
|
||||||
|
)
|
||||||
|
if err := rows.Scan(&id, &tag, &content); err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
if tagString == tag {
|
||||||
|
ctx.Send(content)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: fmt.Sprintf("Tag %v not found", args[0]),
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
ctx.Send("I need a tag to fetch...")
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "I need a tag to fetch...",
|
||||||
|
Error: errors.New("nothing to do"),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,56 @@
|
|||||||
|
package exts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
"github.com/dustinpianalto/goff/utils"
|
||||||
|
"github.com/olebedev/when"
|
||||||
|
"github.com/olebedev/when/rules/common"
|
||||||
|
"github.com/olebedev/when/rules/en"
|
||||||
|
)
|
||||||
|
|
||||||
|
func addReminderCommand(ctx disgoman.Context, args []string) {
|
||||||
|
w := when.New(nil)
|
||||||
|
w.Add(en.All...)
|
||||||
|
w.Add(common.All...)
|
||||||
|
|
||||||
|
text := strings.Join(args, " ")
|
||||||
|
r, err := w.Parse(text, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error parsing time",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if r == nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "You need to include a valid time",
|
||||||
|
Error: errors.New("no time found"),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
content := strings.Replace(text, r.Text+" ", "", 1)
|
||||||
|
query := "INSERT INTO tasks (type, content, guild_id, channel_id, user_id, trigger_time) " +
|
||||||
|
"VALUES ('Reminder', $1, $2, $3, $4, $5)"
|
||||||
|
_, err = utils.Database.Exec(query, content, ctx.Guild.ID, ctx.Channel.ID, ctx.User.ID, r.Time)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error adding task to database",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
_ = ctx.Session.MessageReactionAdd(ctx.Channel.ID, ctx.Message.ID, "✅")
|
||||||
|
_, _ = ctx.Session.ChannelMessageSend(
|
||||||
|
ctx.Channel.ID,
|
||||||
|
fmt.Sprintf("I will remind you at %v, with `%v`", r.Time.Format(time.RFC1123), content),
|
||||||
|
)
|
||||||
|
}
|
||||||
@ -0,0 +1,256 @@
|
|||||||
|
package exts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
"github.com/dustinpianalto/goff/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func kickUserCommand(ctx disgoman.Context, args []string) {
|
||||||
|
var member *discordgo.Member
|
||||||
|
var err error
|
||||||
|
if len(ctx.Message.Mentions) > 0 {
|
||||||
|
member, err = ctx.Session.GuildMember(ctx.Guild.ID, ctx.Message.Mentions[0].ID)
|
||||||
|
} else if len(args) >= 1 {
|
||||||
|
idString := args[0]
|
||||||
|
if strings.HasPrefix(idString, "<@!") && strings.HasSuffix(idString, ">") {
|
||||||
|
idString = idString[3 : len(idString)-1]
|
||||||
|
}
|
||||||
|
member, err = ctx.Session.GuildMember(ctx.Guild.ID, idString)
|
||||||
|
} else {
|
||||||
|
err = errors.New("that is not a valid id")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Couldn't get that member",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if higher, _ := disgoman.HasHigherRole(ctx.Session, ctx.Guild.ID, ctx.Message.Author.ID, member.User.ID); !higher {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "You must have a higher role than the person you are trying to kick",
|
||||||
|
Error: errors.New("need higher role"),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if higher, _ := disgoman.HasHigherRole(ctx.Session, ctx.Guild.ID, ctx.Session.State.User.ID, member.User.ID); !higher {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "I don't have a high enough role to kick that person",
|
||||||
|
Error: errors.New("need higher role"),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var reason string
|
||||||
|
if len(args) > 1 {
|
||||||
|
reason = strings.Join(args[1:], " ")
|
||||||
|
} else {
|
||||||
|
reason = "No Reason Given"
|
||||||
|
}
|
||||||
|
auditReason := fmt.Sprintf("%v#%v: %v", ctx.User.Username, ctx.User.Discriminator, reason)
|
||||||
|
err = ctx.Session.GuildMemberDeleteWithReason(ctx.Guild.ID, member.User.ID, auditReason)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: fmt.Sprintf("Something went wrong kicking %v", member.User.Username),
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event := &utils.LogEvent{
|
||||||
|
Embed: discordgo.MessageEmbed{
|
||||||
|
Title: "User Kicked",
|
||||||
|
Description: fmt.Sprintf(
|
||||||
|
"User %v#%v was kicked by %v.\nReason: %v",
|
||||||
|
member.User.Username,
|
||||||
|
member.User.Discriminator,
|
||||||
|
ctx.Message.Author.Username,
|
||||||
|
reason),
|
||||||
|
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
|
||||||
|
Color: 0xff8c00,
|
||||||
|
},
|
||||||
|
GuildID: ctx.Guild.ID,
|
||||||
|
Session: ctx.Session,
|
||||||
|
}
|
||||||
|
utils.LoggingChannel <- event
|
||||||
|
_, _ = ctx.Send(fmt.Sprintf("User %v#%v has been kicked.", member.User.Username, member.User.Discriminator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func banUserCommand(ctx disgoman.Context, args []string) {
|
||||||
|
var user *discordgo.User
|
||||||
|
var err error
|
||||||
|
if len(ctx.Message.Mentions) > 0 {
|
||||||
|
user, err = ctx.Session.User(ctx.Message.Mentions[0].ID)
|
||||||
|
} else if len(args) >= 1 {
|
||||||
|
idString := args[0]
|
||||||
|
if strings.HasPrefix(idString, "<@!") && strings.HasSuffix(idString, ">") {
|
||||||
|
idString = idString[3 : len(idString)-1]
|
||||||
|
}
|
||||||
|
user, err = ctx.Session.User(idString)
|
||||||
|
} else {
|
||||||
|
err = errors.New("that is not a valid id")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Couldn't get that user",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if higher, err := disgoman.HasHigherRole(ctx.Session, ctx.Guild.ID, ctx.Message.Author.ID, user.ID); err != nil {
|
||||||
|
if err.Error() == "can't find caller member" {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Who are you?",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if !higher {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "You must have a higher role than the person you are trying to ban",
|
||||||
|
Error: errors.New("need higher role"),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if higher, err := disgoman.HasHigherRole(ctx.Session, ctx.Guild.ID, ctx.Session.State.User.ID, user.ID); err != nil {
|
||||||
|
if err.Error() == "can't find caller member" {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Who am I?",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} else if !higher {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "I don't have a high enough role to ban that person",
|
||||||
|
Error: errors.New("need higher role"),
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var reason string
|
||||||
|
if len(args) > 1 {
|
||||||
|
reason = strings.Join(args[1:], " ")
|
||||||
|
} else {
|
||||||
|
reason = "No Reason Given"
|
||||||
|
}
|
||||||
|
auditReason := fmt.Sprintf("%v#%v: %v", ctx.User.Username, ctx.User.Discriminator, reason)
|
||||||
|
days := 7
|
||||||
|
if ctx.Invoked == "ban-no-delete" {
|
||||||
|
days = 0
|
||||||
|
}
|
||||||
|
err = ctx.Session.GuildBanCreateWithReason(ctx.Guild.ID, user.ID, auditReason, days)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: fmt.Sprintf("Something went wrong banning %v", user.Username),
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
event := &utils.LogEvent{
|
||||||
|
Embed: discordgo.MessageEmbed{
|
||||||
|
Title: "User Banned",
|
||||||
|
Description: fmt.Sprintf(
|
||||||
|
"User %v#%v was banned by %v.\nReason: %v",
|
||||||
|
user.Username,
|
||||||
|
user.Discriminator,
|
||||||
|
ctx.Message.Author.Username,
|
||||||
|
reason),
|
||||||
|
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
|
||||||
|
Color: 0xff0000,
|
||||||
|
},
|
||||||
|
GuildID: ctx.Guild.ID,
|
||||||
|
Session: ctx.Session,
|
||||||
|
}
|
||||||
|
utils.LoggingChannel <- event
|
||||||
|
_, _ = ctx.Send(fmt.Sprintf("User %v#%v has been banned.", user.Username, user.Discriminator))
|
||||||
|
}
|
||||||
|
|
||||||
|
func unbanUserCommand(ctx disgoman.Context, args []string) {
|
||||||
|
var user *discordgo.User
|
||||||
|
var err error
|
||||||
|
if len(ctx.Message.Mentions) > 0 {
|
||||||
|
user, err = ctx.Session.User(ctx.Message.Mentions[0].ID)
|
||||||
|
} else if len(args) >= 1 {
|
||||||
|
idString := args[0]
|
||||||
|
if strings.HasPrefix(idString, "<@!") && strings.HasSuffix(idString, ">") {
|
||||||
|
idString = idString[3 : len(idString)-1]
|
||||||
|
}
|
||||||
|
user, err = ctx.Session.User(idString)
|
||||||
|
} else {
|
||||||
|
err = errors.New("that is not a valid id")
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Couldn't get that user",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
bans, err := ctx.Session.GuildBans(ctx.Guild.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Error processing current bans",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, ban := range bans {
|
||||||
|
if ban.User.ID == user.ID {
|
||||||
|
err = ctx.Session.GuildBanDelete(ctx.Guild.ID, user.ID)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: fmt.Sprintf("Something went wrong unbanning %v", user.Username),
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
event := &utils.LogEvent{
|
||||||
|
Embed: discordgo.MessageEmbed{
|
||||||
|
Title: "User Banned",
|
||||||
|
Description: fmt.Sprintf(
|
||||||
|
"User %v#%v was unbanned by %v.\nOrignal Ban Reason: %v",
|
||||||
|
user.Username,
|
||||||
|
user.Discriminator,
|
||||||
|
ctx.Message.Author.Username,
|
||||||
|
ban.Reason),
|
||||||
|
Timestamp: time.Now().Format("2006-01-02 15:04:05"),
|
||||||
|
Color: 0x00ff00,
|
||||||
|
},
|
||||||
|
GuildID: ctx.Guild.ID,
|
||||||
|
Session: ctx.Session,
|
||||||
|
}
|
||||||
|
utils.LoggingChannel <- event
|
||||||
|
_, _ = ctx.Send(fmt.Sprintf("User %v#%v has been unbanned.", user.Username, user.Discriminator))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
_, _ = ctx.Send(fmt.Sprintf("%v#%v is not banned in this guild.", user.Username, user.Discriminator))
|
||||||
|
}
|
||||||
@ -0,0 +1,169 @@
|
|||||||
|
package exts
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"sort"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
"github.com/dustinpianalto/goff/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
func pingCommand(ctx disgoman.Context, _ []string) {
|
||||||
|
timeBefore := time.Now()
|
||||||
|
msg, _ := ctx.Send("Pong!")
|
||||||
|
took := time.Now().Sub(timeBefore)
|
||||||
|
_, err := ctx.Session.ChannelMessageEdit(ctx.Message.ChannelID, msg.ID, fmt.Sprintf("Pong!\nPing Took **%s**", took.String()))
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Ping Failed",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func inviteCommand(ctx disgoman.Context, args []string) {
|
||||||
|
var ids []string
|
||||||
|
if len(args) == 0 {
|
||||||
|
ids = []string{ctx.Session.State.User.ID}
|
||||||
|
} else {
|
||||||
|
for _, id := range args {
|
||||||
|
ids = append(ids, id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for _, id := range ids {
|
||||||
|
url := fmt.Sprintf("<https://discordapp.com/oauth2/authorize?client_id=%v&scope=bot>", id)
|
||||||
|
_, err := ctx.Send(url)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Couldn't send the invite link.",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func gitCommand(ctx disgoman.Context, _ []string) {
|
||||||
|
embed := &discordgo.MessageEmbed{
|
||||||
|
Title: "Hi there, My code is on Github",
|
||||||
|
Color: 0,
|
||||||
|
URL: "https://github.com/dustinpianalto/Goff",
|
||||||
|
}
|
||||||
|
_, err := ctx.Session.ChannelMessageSendEmbed(ctx.Channel.ID, embed)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Git failed",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sayCommand(ctx disgoman.Context, args []string) {
|
||||||
|
resp := strings.Join(args, " ")
|
||||||
|
resp = strings.ReplaceAll(resp, "@everyone", "@\ufff0everyone")
|
||||||
|
resp = strings.ReplaceAll(resp, "@here", "@\ufff0here")
|
||||||
|
_, err := ctx.Session.ChannelMessageSend(ctx.Message.ChannelID, resp)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Say Failed",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func userCommand(ctx disgoman.Context, args []string) {
|
||||||
|
var member *discordgo.Member
|
||||||
|
if len(args) == 0 {
|
||||||
|
member, _ = ctx.Session.GuildMember(ctx.Guild.ID, ctx.Message.Author.ID)
|
||||||
|
} else {
|
||||||
|
var err error
|
||||||
|
if len(ctx.Message.Mentions) > 0 {
|
||||||
|
member, err = ctx.Session.GuildMember(ctx.Guild.ID, ctx.Message.Mentions[0].ID)
|
||||||
|
} else {
|
||||||
|
member, err = ctx.Session.GuildMember(ctx.Guild.ID, args[0])
|
||||||
|
}
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Couldn't get that member",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
thumb := &discordgo.MessageEmbedThumbnail{
|
||||||
|
URL: member.User.AvatarURL(""),
|
||||||
|
}
|
||||||
|
|
||||||
|
var botString string
|
||||||
|
if member.User.Bot {
|
||||||
|
botString = "BOT"
|
||||||
|
} else {
|
||||||
|
botString = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
var roles []*discordgo.Role
|
||||||
|
for _, roleID := range member.Roles {
|
||||||
|
role, _ := ctx.Session.State.Role(ctx.Guild.ID, roleID)
|
||||||
|
roles = append(roles, role)
|
||||||
|
}
|
||||||
|
sort.Slice(roles, func(i, j int) bool { return roles[i].Position > roles[j].Position })
|
||||||
|
var roleMentions []string
|
||||||
|
for _, role := range roles {
|
||||||
|
roleMentions = append(roleMentions, role.Mention())
|
||||||
|
}
|
||||||
|
var rolesString string
|
||||||
|
if len(roleMentions) > 0 {
|
||||||
|
rolesString = strings.Join(roleMentions, " ")
|
||||||
|
} else {
|
||||||
|
rolesString = "None"
|
||||||
|
}
|
||||||
|
|
||||||
|
rolesField := &discordgo.MessageEmbedField{
|
||||||
|
Name: "Roles:",
|
||||||
|
Value: rolesString,
|
||||||
|
Inline: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
guildJoinTime, _ := member.JoinedAt.Parse()
|
||||||
|
guildJoinedField := &discordgo.MessageEmbedField{
|
||||||
|
Name: "Joined Guild:",
|
||||||
|
Value: utils.ParseDateString(guildJoinTime),
|
||||||
|
Inline: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
int64ID, _ := strconv.ParseInt(member.User.ID, 10, 64)
|
||||||
|
s := utils.ParseSnowflake(int64ID)
|
||||||
|
discordJoinedField := &discordgo.MessageEmbedField{
|
||||||
|
Name: "Joined Discord:",
|
||||||
|
Value: utils.ParseDateString(s.CreationTime),
|
||||||
|
Inline: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
embed := &discordgo.MessageEmbed{
|
||||||
|
Title: fmt.Sprintf("%v#%v %v", member.User.Username, member.User.Discriminator, botString),
|
||||||
|
Description: fmt.Sprintf("**%v** (%v)", member.Nick, member.User.ID),
|
||||||
|
Color: ctx.Session.State.UserColor(member.User.ID, ctx.Channel.ID),
|
||||||
|
Thumbnail: thumb,
|
||||||
|
Fields: []*discordgo.MessageEmbedField{
|
||||||
|
guildJoinedField,
|
||||||
|
discordJoinedField,
|
||||||
|
rolesField,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
_, err := ctx.Session.ChannelMessageSendEmbed(ctx.Channel.ID, embed)
|
||||||
|
if err != nil {
|
||||||
|
ctx.ErrorChannel <- disgoman.CommandError{
|
||||||
|
Context: ctx,
|
||||||
|
Message: "Couldn't send the user embed",
|
||||||
|
Error: err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,136 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
"github.com/dustinpianalto/disgoman"
|
||||||
|
"github.com/dustinpianalto/goff/events"
|
||||||
|
"github.com/dustinpianalto/goff/exts"
|
||||||
|
"github.com/dustinpianalto/goff/utils"
|
||||||
|
|
||||||
|
//"github.com/MikeModder/anpan"
|
||||||
|
"os"
|
||||||
|
"os/signal"
|
||||||
|
"syscall"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Token string
|
||||||
|
)
|
||||||
|
|
||||||
|
//func init() {
|
||||||
|
// flag.StringVar(&Token, "t", "", "Bot Token")
|
||||||
|
// flag.Parse()
|
||||||
|
//}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
Token = os.Getenv("DISCORDGO_TOKEN")
|
||||||
|
dg, err := discordgo.New("Bot " + Token)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("There was an error when creating the Discord Session, ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
dg.State.MaxMessageCount = 100
|
||||||
|
|
||||||
|
utils.ConnectDatabase(os.Getenv("DATABASE_URL"))
|
||||||
|
utils.InitializeDatabase()
|
||||||
|
//utils.LoadTestData()
|
||||||
|
|
||||||
|
//prefixes := []string{
|
||||||
|
// "Go.",
|
||||||
|
//}
|
||||||
|
owners := []string{
|
||||||
|
"351794468870946827",
|
||||||
|
}
|
||||||
|
|
||||||
|
// Arguments are:
|
||||||
|
// prefixes - []string
|
||||||
|
// owner ids - []string
|
||||||
|
// ignore bots - bool
|
||||||
|
// check perms - bool
|
||||||
|
handler := disgoman.CommandManager{
|
||||||
|
Prefixes: getPrefixes,
|
||||||
|
Owners: owners,
|
||||||
|
StatusManager: disgoman.GetDefaultStatusManager(),
|
||||||
|
ErrorChannel: make(chan disgoman.CommandError, 10),
|
||||||
|
Commands: make(map[string]*disgoman.Command),
|
||||||
|
IgnoreBots: true,
|
||||||
|
CheckPermissions: false,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add Command Handlers
|
||||||
|
exts.AddCommandHandlers(&handler)
|
||||||
|
|
||||||
|
//if _, ok := handler.Commands["help"]; !ok {
|
||||||
|
// handler.AddDefaultHelpCommand()
|
||||||
|
//}
|
||||||
|
|
||||||
|
dg.AddHandler(handler.OnMessage)
|
||||||
|
dg.AddHandler(handler.StatusManager.OnReady)
|
||||||
|
dg.AddHandler(events.OnMessageUpdate)
|
||||||
|
dg.AddHandler(events.OnMessageDelete)
|
||||||
|
dg.AddHandler(events.OnGuildMemberAddLogging)
|
||||||
|
dg.AddHandler(events.OnGuildMemberRemoveLogging)
|
||||||
|
|
||||||
|
err = dg.Open()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("There was an error opening the connection, ", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the Error handler in a goroutine
|
||||||
|
go ErrorHandler(handler.ErrorChannel)
|
||||||
|
|
||||||
|
// Start the Logging handler in a goroutine
|
||||||
|
go utils.LoggingHandler(utils.LoggingChannel)
|
||||||
|
|
||||||
|
// Start the task handler in a goroutine
|
||||||
|
go utils.ProcessTasks(dg, 1)
|
||||||
|
|
||||||
|
go utils.RecieveEmail(dg)
|
||||||
|
|
||||||
|
fmt.Println("The Bot is now running.")
|
||||||
|
sc := make(chan os.Signal, 1)
|
||||||
|
signal.Notify(sc, syscall.SIGINT, syscall.SIGTERM, os.Interrupt, os.Kill)
|
||||||
|
<-sc
|
||||||
|
|
||||||
|
fmt.Println("Shutting Down...")
|
||||||
|
err = dg.Close()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func getPrefixes(guildID string) []string {
|
||||||
|
queryString := "Select prefix from prefixes p, x_guilds_prefixes xgp where xgp.guild_id = $1 and xgp.prefix_id = p.id"
|
||||||
|
rows, err := utils.Database.Query(queryString, guildID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return []string{"Go.", "go."}
|
||||||
|
}
|
||||||
|
var prefixes []string
|
||||||
|
for rows.Next() {
|
||||||
|
var prefix string
|
||||||
|
err = rows.Scan(&prefix)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return []string{"Go.", "go."}
|
||||||
|
}
|
||||||
|
prefixes = append(prefixes, prefix)
|
||||||
|
}
|
||||||
|
return prefixes
|
||||||
|
}
|
||||||
|
|
||||||
|
func ErrorHandler(ErrorChan chan disgoman.CommandError) {
|
||||||
|
for ce := range ErrorChan {
|
||||||
|
msg := ce.Message
|
||||||
|
if msg == "" {
|
||||||
|
msg = ce.Error.Error()
|
||||||
|
}
|
||||||
|
_, _ = ce.Context.Send(msg)
|
||||||
|
fmt.Println(ce.Error)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,154 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"database/sql"
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
|
||||||
|
_ "github.com/lib/pq"
|
||||||
|
)
|
||||||
|
|
||||||
|
var (
|
||||||
|
Database *sql.DB
|
||||||
|
)
|
||||||
|
|
||||||
|
func ConnectDatabase(dbConnString string) {
|
||||||
|
db, err := sql.Open("postgres", dbConnString)
|
||||||
|
if err != nil {
|
||||||
|
panic(fmt.Sprintf("Can't connect to the database. %v", err))
|
||||||
|
} else {
|
||||||
|
fmt.Println("Database Connected.")
|
||||||
|
}
|
||||||
|
Database = db
|
||||||
|
}
|
||||||
|
|
||||||
|
func InitializeDatabase() {
|
||||||
|
_, err := Database.Query("CREATE TABLE IF NOT EXISTS users(" +
|
||||||
|
"id varchar(30) primary key," +
|
||||||
|
"banned bool not null default false," +
|
||||||
|
"logging bool not null default true," +
|
||||||
|
"steam_id varchar(30) NOT NULL DEFAULT ''," +
|
||||||
|
"is_active bool not null default true," +
|
||||||
|
"is_staff bool not null default false," +
|
||||||
|
"is_admin bool not null default false" +
|
||||||
|
")")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("CREATE TABLE IF NOT EXISTS guilds(" +
|
||||||
|
"id varchar(30) primary key," +
|
||||||
|
"welcome_message varchar(1000) NOT NULL DEFAULT ''," +
|
||||||
|
"goodbye_message varchar(1000) NOT NULL DEFAULT ''," +
|
||||||
|
"logging_channel varchar(30) NOT NULL DEFAULT ''," +
|
||||||
|
"welcome_channel varchar(30) NOT NULL DEFAULT ''" +
|
||||||
|
")")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("CREATE TABLE IF NOT EXISTS prefixes(" +
|
||||||
|
"id serial primary key," +
|
||||||
|
"prefix varchar(10) not null unique default 'Go.'" +
|
||||||
|
")")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("CREATE TABLE IF NOT EXISTS tags(" +
|
||||||
|
"id serial primary key," +
|
||||||
|
"tag varchar(100) not null unique," +
|
||||||
|
"content varchar(1000) not null," +
|
||||||
|
"creator varchar(30) not null references users(id)," +
|
||||||
|
"creation_time timestamp not null default NOW()," +
|
||||||
|
"guild_id varchar(30) not null references guilds(id)" +
|
||||||
|
")")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("CREATE TABLE IF NOT EXISTS x_users_guilds(" +
|
||||||
|
"guild_id varchar(30) not null references guilds(id)," +
|
||||||
|
"user_id varchar(30) not null references users(id)" +
|
||||||
|
")")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("CREATE TABLE IF NOT EXISTS x_guilds_prefixes(" +
|
||||||
|
"guild_id varchar(30) not null references guilds(id)," +
|
||||||
|
"prefix_id int not null references prefixes(id)" +
|
||||||
|
")")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("CREATE TABLE IF NOT EXISTS tasks(" +
|
||||||
|
"id serial primary key," +
|
||||||
|
"type varchar(10) not null," +
|
||||||
|
"content text not null," +
|
||||||
|
"guild_id varchar(30) not null references guilds(id)," +
|
||||||
|
"channel_id varchar(30) not null," +
|
||||||
|
"user_id varchar(30) not null," +
|
||||||
|
"creation_time timestamp not null default NOW()," +
|
||||||
|
"trigger_time timestamp not null," +
|
||||||
|
"completed bool not null default false," +
|
||||||
|
"processing bool default false)")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query(`CREATE TABLE IF NOT EXISTS postfixes(
|
||||||
|
id serial primary key,
|
||||||
|
name varchar(100) not null,
|
||||||
|
time timestamp not null default NOW())`)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Exec(`CREATE TABLE IF NOT EXISTS puzzles(
|
||||||
|
id serial primary key,
|
||||||
|
text text not null,
|
||||||
|
time timestamp not null
|
||||||
|
)`)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Exec(`CREATE TABLE IF NOT EXISTS x_guilds_puzzles(
|
||||||
|
id serial primary key,
|
||||||
|
guild_id varchar(30) not null references guilds(id),
|
||||||
|
puzzle_id int not null references puzzles(id),
|
||||||
|
message_id varchar(30) not null
|
||||||
|
)`)
|
||||||
|
RunPostfixes()
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoadTestData() {
|
||||||
|
_, err := Database.Query("INSERT INTO users (id, banned, logging, steam_id, is_active, is_staff, is_admin) values " +
|
||||||
|
"('351794468870946827', false, true, '76561198024193239', true, true, true)," +
|
||||||
|
"('692908139506434065', false, true, '', true, false, false)," +
|
||||||
|
"('396588996706304010', false, true, '', true, true, false)")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("INSERT INTO guilds (id, welcome_message, goodbye_message) VALUES " +
|
||||||
|
"('265828729970753537', 'Hey there is someone new here.', 'Well fine then... Just leave without saying goodbye')")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("INSERT INTO prefixes (prefix) VALUES ('Godev.'), ('godev.'), ('godev,')")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("INSERT INTO x_users_guilds (guild_id, user_id) VALUES " +
|
||||||
|
"('265828729970753537', '351794468870946827')," +
|
||||||
|
"('265828729970753537', '692908139506434065')," +
|
||||||
|
"('265828729970753537', '396588996706304010')")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("INSERT INTO x_guilds_prefixes (guild_id, prefix_id) VALUES " +
|
||||||
|
"('265828729970753537', 1)," +
|
||||||
|
"('265828729970753537', 2)," +
|
||||||
|
"('265828729970753537', 3)")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
_, err = Database.Query("INSERT INTO tags (tag, content, creator, guild_id) VALUES " +
|
||||||
|
"('test', 'This is a test of the tag system', '351794468870946827', '265828729970753537')")
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,64 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseDateString(inTime time.Time) string {
|
||||||
|
d := time.Now().Sub(inTime)
|
||||||
|
s := int64(d.Seconds())
|
||||||
|
days := s / 86400
|
||||||
|
s = s - (days * 86400)
|
||||||
|
hours := s / 3600
|
||||||
|
s = s - (hours * 3600)
|
||||||
|
minutes := s / 60
|
||||||
|
seconds := s - (minutes * 60)
|
||||||
|
dateString := ""
|
||||||
|
if days != 0 {
|
||||||
|
dateString += fmt.Sprintf("%v days ", days)
|
||||||
|
}
|
||||||
|
if hours != 0 {
|
||||||
|
dateString += fmt.Sprintf("%v hours ", hours)
|
||||||
|
}
|
||||||
|
if minutes != 0 {
|
||||||
|
dateString += fmt.Sprintf("%v minutes ", minutes)
|
||||||
|
}
|
||||||
|
if seconds != 0 {
|
||||||
|
dateString += fmt.Sprintf("%v seconds ", seconds)
|
||||||
|
}
|
||||||
|
if dateString != "" {
|
||||||
|
dateString += " ago."
|
||||||
|
} else {
|
||||||
|
dateString = "Now"
|
||||||
|
}
|
||||||
|
stamp := inTime.Format("2006-01-02 15:04:05")
|
||||||
|
return fmt.Sprintf("%v\n%v", dateString, stamp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseDurationString(inDur time.Duration) string {
|
||||||
|
s := int64(inDur.Seconds())
|
||||||
|
days := s / 86400
|
||||||
|
s = s - (days * 86400)
|
||||||
|
hours := s / 3600
|
||||||
|
s = s - (hours * 3600)
|
||||||
|
minutes := s / 60
|
||||||
|
seconds := s - (minutes * 60)
|
||||||
|
durString := ""
|
||||||
|
if days != 0 {
|
||||||
|
durString += fmt.Sprintf("%v days ", days)
|
||||||
|
}
|
||||||
|
if hours != 0 {
|
||||||
|
durString += fmt.Sprintf("%v hours ", hours)
|
||||||
|
}
|
||||||
|
if minutes != 0 {
|
||||||
|
durString += fmt.Sprintf("%v minutes ", minutes)
|
||||||
|
}
|
||||||
|
if seconds != 0 {
|
||||||
|
durString += fmt.Sprintf("%v seconds ", seconds)
|
||||||
|
}
|
||||||
|
if durString == "" {
|
||||||
|
durString = "0 seconds"
|
||||||
|
}
|
||||||
|
return fmt.Sprintf("%v", durString)
|
||||||
|
}
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
"sync"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
imap "github.com/emersion/go-imap"
|
||||||
|
"github.com/emersion/go-imap/client"
|
||||||
|
"github.com/emersion/go-message/mail"
|
||||||
|
)
|
||||||
|
|
||||||
|
const ()
|
||||||
|
|
||||||
|
var (
|
||||||
|
emailUsername = os.Getenv("GOFF_EMAIL_USERNAME")
|
||||||
|
emailPassword = os.Getenv("GOFF_EMAIL_PASSWORD")
|
||||||
|
puzzleAddress = mail.Address{
|
||||||
|
Name: "Daily Coding Problem",
|
||||||
|
Address: "founders@dailycodingproblem.com",
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
var EmailClient client.Client
|
||||||
|
|
||||||
|
func RecieveEmail(dg *discordgo.Session) {
|
||||||
|
for {
|
||||||
|
log.Println("Connecting to Email server.")
|
||||||
|
|
||||||
|
EmailClient, err := client.DialTLS("mail.djpianalto.com:993", nil)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if err = EmailClient.Login(emailUsername, emailPassword); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println("Connected to Email server.")
|
||||||
|
|
||||||
|
mbox, err := EmailClient.Select("INBOX", false)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if mbox.Messages == 0 {
|
||||||
|
log.Println("No Messages in Mailbox")
|
||||||
|
}
|
||||||
|
|
||||||
|
criteria := imap.NewSearchCriteria()
|
||||||
|
criteria.WithoutFlags = []string{"\\Seen"}
|
||||||
|
uids, err := EmailClient.Search(criteria)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
if len(uids) > 0 {
|
||||||
|
seqset := new(imap.SeqSet)
|
||||||
|
seqset.AddNum(uids...)
|
||||||
|
section := &imap.BodySectionName{}
|
||||||
|
items := []imap.FetchItem{section.FetchItem()}
|
||||||
|
messages := make(chan *imap.Message, 10)
|
||||||
|
go func() {
|
||||||
|
if err = EmailClient.Fetch(seqset, items, messages); err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
var wg sync.WaitGroup
|
||||||
|
|
||||||
|
for msg := range messages {
|
||||||
|
if msg == nil {
|
||||||
|
log.Println("No New Messages")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
r := msg.GetBody(section)
|
||||||
|
if r == nil {
|
||||||
|
log.Println("Server didn't send a message body")
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
wg.Add(1)
|
||||||
|
go processEmail(r, dg, &wg)
|
||||||
|
}
|
||||||
|
wg.Wait()
|
||||||
|
}
|
||||||
|
|
||||||
|
EmailClient.Logout()
|
||||||
|
time.Sleep(300 * time.Second)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func processEmail(r io.Reader, dg *discordgo.Session, wg *sync.WaitGroup) {
|
||||||
|
defer wg.Done()
|
||||||
|
mr, err := mail.CreateReader(r)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
header := mr.Header
|
||||||
|
from, err := header.AddressList("From")
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
subject, err := header.Subject()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println(from)
|
||||||
|
log.Println(subject)
|
||||||
|
if addressIn(from, puzzleAddress) &&
|
||||||
|
strings.Contains(subject, "Daily Coding Problem:") {
|
||||||
|
log.Println("Processing Puzzle")
|
||||||
|
ProcessPuzzleEmail(mr, dg)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func addressIn(s []*mail.Address, a mail.Address) bool {
|
||||||
|
for _, item := range s {
|
||||||
|
if item.String() == a.String() {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
@ -0,0 +1,34 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
)
|
||||||
|
|
||||||
|
var LoggingChannel = make(chan *LogEvent, 10)
|
||||||
|
|
||||||
|
type LogEvent struct {
|
||||||
|
// Embed with log message
|
||||||
|
Embed discordgo.MessageEmbed
|
||||||
|
// Guild to log event in
|
||||||
|
GuildID string
|
||||||
|
// Discordgo Session. Needed for sending messages
|
||||||
|
Session *discordgo.Session
|
||||||
|
}
|
||||||
|
|
||||||
|
func LoggingHandler(lc chan *LogEvent) {
|
||||||
|
for event := range lc {
|
||||||
|
var channelID string
|
||||||
|
row := Database.QueryRow("SELECT logging_channel FROM guilds where id=$1", event.GuildID)
|
||||||
|
err := row.Scan(&channelID)
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if channelID == "" {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
_, _ = event.Session.ChannelMessageSendEmbed(channelID, &event.Embed)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,77 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "log"
|
||||||
|
|
||||||
|
type postfix struct {
|
||||||
|
Name string
|
||||||
|
Invoke func(bool) error
|
||||||
|
}
|
||||||
|
|
||||||
|
var postfixes = []postfix{
|
||||||
|
postfix{
|
||||||
|
Name: "1_Update_Guild_for_Puzzle",
|
||||||
|
Invoke: updateGuildForPuzzle,
|
||||||
|
},
|
||||||
|
postfix{
|
||||||
|
Name: "1_Update_X_Guild_Prefixes_to_add_ID",
|
||||||
|
Invoke: updateXGuildPrefixesToAddID,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func RunPostfixes() {
|
||||||
|
for _, postfix := range postfixes {
|
||||||
|
queryString := "SELECT * from postfixes where name = $1"
|
||||||
|
rows, err := Database.Query(queryString, postfix.Name)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
if rows.Next() {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
err := postfix.Invoke(false)
|
||||||
|
if err != nil {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
_, err = Database.Exec("INSERT INTO postfixes (name) VALUES ($1)", postfix.Name)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateGuildForPuzzle(revert bool) error {
|
||||||
|
var queryString string
|
||||||
|
if !revert {
|
||||||
|
queryString = `ALTER TABLE guilds
|
||||||
|
ADD COLUMN puzzle_channel varchar(30) not null default ''`
|
||||||
|
} else {
|
||||||
|
queryString = `ALTER TABLE guilds
|
||||||
|
DROP COLUMN puzzleChat`
|
||||||
|
}
|
||||||
|
_, err := Database.Exec(queryString)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateXGuildPrefixesToAddID(revert bool) error {
|
||||||
|
var queryString string
|
||||||
|
if !revert {
|
||||||
|
queryString = `ALTER TABLE x_guilds_prefixes
|
||||||
|
ADD COLUMN id serial primary key`
|
||||||
|
} else {
|
||||||
|
queryString = `ALTER TABLE x_guilds_prefixes
|
||||||
|
DROP COLUMN id`
|
||||||
|
}
|
||||||
|
_, err := Database.Exec(queryString)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
@ -0,0 +1,93 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
|
"log"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"github.com/emersion/go-message/mail"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ProcessPuzzleEmail(mr *mail.Reader, dg *discordgo.Session) {
|
||||||
|
var body []byte
|
||||||
|
for {
|
||||||
|
p, err := mr.NextPart()
|
||||||
|
if err == io.EOF {
|
||||||
|
break
|
||||||
|
} else if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
switch h := p.Header.(type) {
|
||||||
|
case *mail.InlineHeader:
|
||||||
|
// This is the message's text (can be plain-text or HTML)
|
||||||
|
if t, _, _ := h.ContentType(); t == "text/plain" {
|
||||||
|
body, _ = ioutil.ReadAll(p.Body)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(body) > 0 {
|
||||||
|
s := string(body)
|
||||||
|
puzzle := strings.Split(s, "----------")[0]
|
||||||
|
date, err := mr.Header.Date()
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
e := discordgo.MessageEmbed{
|
||||||
|
Title: "Daily Coding Problem",
|
||||||
|
URL: "https://dailycodingproblem.com/",
|
||||||
|
Description: "```" + puzzle + "```",
|
||||||
|
Timestamp: date.Format(time.RFC3339),
|
||||||
|
Footer: &discordgo.MessageEmbedFooter{
|
||||||
|
Text: "Daily Coding Problem",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
var guilds []Guild
|
||||||
|
queryString := `SELECT id, puzzle_channel from guilds`
|
||||||
|
rows, err := Database.Query(queryString)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
for rows.Next() {
|
||||||
|
var guild Guild
|
||||||
|
err := rows.Scan(&guild.ID, &guild.PuzzleChannel)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
guilds = append(guilds, guild)
|
||||||
|
}
|
||||||
|
var puzzleID int64
|
||||||
|
queryString = "INSERT INTO puzzles (text, time) VALUES ($1, $2) RETURNING id"
|
||||||
|
err = Database.QueryRow(queryString, puzzle, date).Scan(&puzzleID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, g := range guilds {
|
||||||
|
if g.PuzzleChannel == "" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
msg := discordgo.MessageSend{
|
||||||
|
Embed: &e,
|
||||||
|
}
|
||||||
|
m, err := dg.ChannelMessageSendComplex(g.PuzzleChannel, &msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
queryString = "INSERT INTO x_guilds_puzzles (guild_id, puzzle_id, message_id) VALUES ($1, $2, $3)"
|
||||||
|
_, err = Database.Exec(queryString, g.ID, puzzleID, m.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@ -0,0 +1,156 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Operator struct {
|
||||||
|
Token string
|
||||||
|
Precedence int
|
||||||
|
Association string
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Operator) HasHigherPrecedence(t Operator) bool {
|
||||||
|
return o.Precedence < t.Precedence // lower number is higher precedence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Operator) HasEqualPrecedence(t Operator) bool {
|
||||||
|
return o.Precedence == t.Precedence
|
||||||
|
}
|
||||||
|
|
||||||
|
func (o Operator) IsLeftAssociative() bool {
|
||||||
|
return o.Association == "left"
|
||||||
|
}
|
||||||
|
|
||||||
|
var operators = map[string]Operator{
|
||||||
|
"+": Operator{
|
||||||
|
Token: "+",
|
||||||
|
Precedence: 4,
|
||||||
|
Association: "left",
|
||||||
|
},
|
||||||
|
"-": Operator{
|
||||||
|
Token: "-",
|
||||||
|
Precedence: 4,
|
||||||
|
Association: "left",
|
||||||
|
},
|
||||||
|
"*": Operator{
|
||||||
|
Token: "*",
|
||||||
|
Precedence: 3,
|
||||||
|
Association: "left",
|
||||||
|
},
|
||||||
|
"/": Operator{
|
||||||
|
Token: "/",
|
||||||
|
Precedence: 3,
|
||||||
|
Association: "left",
|
||||||
|
},
|
||||||
|
"%": Operator{
|
||||||
|
Token: "%",
|
||||||
|
Precedence: 3,
|
||||||
|
Association: "left",
|
||||||
|
},
|
||||||
|
"(": Operator{
|
||||||
|
Token: "(",
|
||||||
|
Precedence: 1,
|
||||||
|
Association: "left",
|
||||||
|
},
|
||||||
|
")": Operator{
|
||||||
|
Token: ")",
|
||||||
|
Precedence: 1,
|
||||||
|
Association: "left",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
type Stack []Operator
|
||||||
|
|
||||||
|
func (s *Stack) IsEmpty() bool {
|
||||||
|
return len(*s) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stack) Push(op Operator) {
|
||||||
|
*s = append(*s, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stack) Pop() (Operator, bool) {
|
||||||
|
if s.IsEmpty() {
|
||||||
|
return Operator{}, false
|
||||||
|
}
|
||||||
|
index := len(*s) - 1
|
||||||
|
element := (*s)[index]
|
||||||
|
*s = (*s)[:index]
|
||||||
|
return element, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *Stack) Top() Operator {
|
||||||
|
if s.IsEmpty() {
|
||||||
|
return Operator{}
|
||||||
|
}
|
||||||
|
return (*s)[len(*s)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func GenerateRPN(tokens []string) (string, error) {
|
||||||
|
output := ""
|
||||||
|
s := Stack{}
|
||||||
|
for _, token := range tokens {
|
||||||
|
err := processToken(token, &s, &output)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for !s.IsEmpty() {
|
||||||
|
ele, _ := s.Pop()
|
||||||
|
output += " " + ele.Token
|
||||||
|
}
|
||||||
|
|
||||||
|
return strings.TrimSpace(output), nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func processToken(t string, s *Stack, o *string) error {
|
||||||
|
if _, err := strconv.Atoi(t); err == nil {
|
||||||
|
*o += " " + t
|
||||||
|
return nil
|
||||||
|
} else if op, ok := operators[t]; ok {
|
||||||
|
if op.Token == "(" {
|
||||||
|
s.Push(op)
|
||||||
|
} else if op.Token == ")" {
|
||||||
|
if s.IsEmpty() {
|
||||||
|
return fmt.Errorf("mismatched parentheses")
|
||||||
|
}
|
||||||
|
for s.Top().Token != "(" {
|
||||||
|
if ele, ok := s.Pop(); ok {
|
||||||
|
*o += " " + ele.Token
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("mismatched parentheses")
|
||||||
|
}
|
||||||
|
if s.IsEmpty() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s.Pop() // Pop and discard the (
|
||||||
|
} else if !s.IsEmpty() {
|
||||||
|
for {
|
||||||
|
if (s.Top().HasHigherPrecedence(op) ||
|
||||||
|
(s.Top().HasEqualPrecedence(op) &&
|
||||||
|
op.IsLeftAssociative())) &&
|
||||||
|
s.Top().Token != "(" {
|
||||||
|
if ele, ok := s.Pop(); ok {
|
||||||
|
*o += " " + ele.Token
|
||||||
|
if s.IsEmpty() {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
s.Push(op)
|
||||||
|
} else {
|
||||||
|
s.Push(op)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return fmt.Errorf("invalid character %s", t)
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"math"
|
||||||
|
"strconv"
|
||||||
|
)
|
||||||
|
|
||||||
|
type FStack []float64
|
||||||
|
|
||||||
|
func (s *FStack) IsEmpty() bool {
|
||||||
|
return len(*s) == 0
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FStack) Push(op float64) {
|
||||||
|
*s = append(*s, op)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FStack) Pop() (float64, bool) {
|
||||||
|
if s.IsEmpty() {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
index := len(*s) - 1
|
||||||
|
element := (*s)[index]
|
||||||
|
*s = (*s)[:index]
|
||||||
|
return element, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FStack) PopTwo() (float64, float64, bool) {
|
||||||
|
if s.IsEmpty() || len(*s) < 2 {
|
||||||
|
return 0, 0, false
|
||||||
|
}
|
||||||
|
index := len(*s) - 1
|
||||||
|
b := (*s)[index]
|
||||||
|
a := (*s)[index-1]
|
||||||
|
*s = (*s)[:index-1]
|
||||||
|
return a, b, true
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func (s *FStack) Top() float64 {
|
||||||
|
if s.IsEmpty() {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
return (*s)[len(*s)-1]
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseRPN(args []string) (float64, error) {
|
||||||
|
s := FStack{}
|
||||||
|
for _, token := range args {
|
||||||
|
switch token {
|
||||||
|
case "+":
|
||||||
|
if a, b, ok := s.PopTwo(); ok {
|
||||||
|
s.Push(a + b)
|
||||||
|
} else {
|
||||||
|
return 0, fmt.Errorf("not enough operands on stack for +: %v", s)
|
||||||
|
}
|
||||||
|
case "-":
|
||||||
|
if a, b, ok := s.PopTwo(); ok {
|
||||||
|
s.Push(a - b)
|
||||||
|
} else {
|
||||||
|
return 0, fmt.Errorf("not enough operands on stack for -: %v", s)
|
||||||
|
}
|
||||||
|
case "*":
|
||||||
|
if a, b, ok := s.PopTwo(); ok {
|
||||||
|
s.Push(a * b)
|
||||||
|
} else {
|
||||||
|
return 0, fmt.Errorf("not enough operands on stack for *: %v", s)
|
||||||
|
}
|
||||||
|
case "/":
|
||||||
|
if a, b, ok := s.PopTwo(); ok {
|
||||||
|
s.Push(a / b)
|
||||||
|
} else {
|
||||||
|
return 0, fmt.Errorf("not enough operands on stack for /: %v", s)
|
||||||
|
}
|
||||||
|
case "%":
|
||||||
|
if a, b, ok := s.PopTwo(); ok {
|
||||||
|
s.Push(math.Mod(a, b))
|
||||||
|
} else {
|
||||||
|
return 0, fmt.Errorf("not enough operands on stack for %: %v", s)
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
f, err := strconv.ParseFloat(token, 64)
|
||||||
|
if err != nil {
|
||||||
|
return 0, err
|
||||||
|
}
|
||||||
|
s.Push(f)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if res, ok := s.Pop(); ok {
|
||||||
|
return res, nil
|
||||||
|
}
|
||||||
|
return 0, errors.New("no result")
|
||||||
|
}
|
||||||
@ -0,0 +1,32 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import "time"
|
||||||
|
|
||||||
|
type Snowflake struct {
|
||||||
|
CreationTime time.Time
|
||||||
|
WorkerID int8
|
||||||
|
ProcessID int8
|
||||||
|
Increment int16
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseSnowflake(s int64) Snowflake {
|
||||||
|
const (
|
||||||
|
DISCORD_EPOCH = 1420070400000
|
||||||
|
TIME_BITS_LOC = 22
|
||||||
|
WORKER_ID_LOC = 17
|
||||||
|
WORKER_ID_MASK = 0x3E0000
|
||||||
|
PROCESS_ID_LOC = 12
|
||||||
|
PROCESS_ID_MASK = 0x1F000
|
||||||
|
INCREMENT_MASK = 0xFFF
|
||||||
|
)
|
||||||
|
creationTime := time.Unix(((s>>TIME_BITS_LOC)+DISCORD_EPOCH)/1000.0, 0)
|
||||||
|
workerID := (s & WORKER_ID_MASK) >> WORKER_ID_LOC
|
||||||
|
processID := (s & PROCESS_ID_MASK) >> PROCESS_ID_LOC
|
||||||
|
increment := s & INCREMENT_MASK
|
||||||
|
return Snowflake{
|
||||||
|
CreationTime: creationTime,
|
||||||
|
WorkerID: int8(workerID),
|
||||||
|
ProcessID: int8(processID),
|
||||||
|
Increment: int16(increment),
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,132 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"github.com/bwmarrin/discordgo"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
type Task struct {
|
||||||
|
ID int64
|
||||||
|
Type string
|
||||||
|
Content string
|
||||||
|
GuildID string
|
||||||
|
ChannelID string
|
||||||
|
UserID string
|
||||||
|
CreationTime time.Time
|
||||||
|
TriggerTime time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
func processTask(task *Task, s *discordgo.Session) {
|
||||||
|
query := "SELECT completed, processing from tasks where id = $1"
|
||||||
|
res, err := Database.Query(query, task.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
var completed bool
|
||||||
|
var processing bool
|
||||||
|
res.Next()
|
||||||
|
err = res.Scan(&completed, &processing)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if completed || processing {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
closeQuery := "Update tasks set completed = true where id = $1"
|
||||||
|
processQuery := "UPDATE tasks SET processing = true WHERE id = $1"
|
||||||
|
defer Database.Exec(closeQuery, task.ID)
|
||||||
|
_, err = Database.Exec(processQuery, task.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
log.Println(fmt.Sprintf("Processing task %v", task.ID))
|
||||||
|
guild, err := s.Guild(task.GuildID)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(fmt.Sprintf("Can't find guild with ID %v. Canceling task %v.", task.GuildID, task.ID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
channel, err := s.Channel(task.ChannelID)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(fmt.Sprintf("Can't find channel with ID %v. Canceling task %v.", task.ChannelID, task.ID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if channel.GuildID != guild.ID {
|
||||||
|
log.Print(fmt.Sprintf("The channel %v is not in guild %v. Canceling task %v.", channel.Name, guild.Name, task.ID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
member, err := s.GuildMember(guild.ID, task.UserID)
|
||||||
|
if err != nil {
|
||||||
|
log.Print(fmt.Sprintf("Can't find user with ID %v in guild %v. Canceling task %v.", task.UserID, guild.Name, task.ID))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if task.Type == "Reminder" {
|
||||||
|
color := s.State.UserColor(member.User.ID, channel.ID)
|
||||||
|
e := discordgo.MessageEmbed{
|
||||||
|
Title: "REMINDER",
|
||||||
|
Description: task.Content,
|
||||||
|
Timestamp: task.CreationTime.Format(time.RFC3339),
|
||||||
|
Color: color,
|
||||||
|
Footer: &discordgo.MessageEmbedFooter{
|
||||||
|
Text: "Created: ",
|
||||||
|
},
|
||||||
|
}
|
||||||
|
msg := discordgo.MessageSend{
|
||||||
|
Content: member.Mention(),
|
||||||
|
Embed: &e,
|
||||||
|
}
|
||||||
|
_, err = s.ChannelMessageSendComplex(channel.ID, &msg)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
processQuery = "UPDATE tasks SET processing = false WHERE id = $1"
|
||||||
|
_, err = Database.Exec(processQuery, task.ID)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func getTasksToRun() []Task {
|
||||||
|
query := "SELECT id, type, content, guild_id, channel_id, user_id, creation_time, trigger_time " +
|
||||||
|
"from tasks where completed is false and processing is false and trigger_time < $1"
|
||||||
|
res, err := Database.Query(query, time.Now())
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
var tasks []Task
|
||||||
|
for res.Next() {
|
||||||
|
var t Task
|
||||||
|
err = res.Scan(&t.ID, &t.Type, &t.Content, &t.GuildID, &t.ChannelID, &t.UserID, &t.CreationTime, &t.TriggerTime)
|
||||||
|
if err != nil {
|
||||||
|
log.Println(err)
|
||||||
|
}
|
||||||
|
for _, task := range tasks {
|
||||||
|
if task.ID == t.ID {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tasks = append(tasks, t)
|
||||||
|
}
|
||||||
|
|
||||||
|
return tasks
|
||||||
|
}
|
||||||
|
|
||||||
|
func ProcessTasks(s *discordgo.Session, interval int) {
|
||||||
|
for {
|
||||||
|
time.Sleep(time.Duration(interval * 1e9))
|
||||||
|
|
||||||
|
tasks := getTasksToRun()
|
||||||
|
|
||||||
|
if len(tasks) > 0 {
|
||||||
|
for _, t := range tasks {
|
||||||
|
go processTask(&t, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,10 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
type Guild struct {
|
||||||
|
ID string
|
||||||
|
WelcomeMessage string
|
||||||
|
GoodbyeMessage string
|
||||||
|
LoggingChannel string
|
||||||
|
WelcomeChannel string
|
||||||
|
PuzzleChannel string
|
||||||
|
}
|
||||||
Loading…
Reference in new issue