finished plugin system base

This commit is contained in:
2024-04-15 18:17:12 +02:00
parent 31e96c80f4
commit 077054109f
51 changed files with 2218 additions and 2160 deletions

15
build_plugins.py Normal file
View File

@@ -0,0 +1,15 @@
import os
import subprocess
src_dir = './plugin_src'
output_dir = './plugins'
if not os.path.exists(output_dir):
os.makedirs(output_dir)
for folder_name in os.listdir(src_dir):
folder_path = os.path.join(src_dir, folder_name)
if os.path.isdir(folder_path):
command = f'go build -buildmode=plugin -o {output_dir}/{folder_name}.so {folder_path}'
subprocess.run(command, shell=True, check=True)
print(f'Built plugin: {folder_name}.so')

View File

@@ -1,24 +0,0 @@
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/disgoorg/disgo v0.18.2 h1:pZCvaFamfHcnXrj0XA73qtVofP0R8dYEyQfPNgv8dLE=
github.com/disgoorg/disgo v0.18.2/go.mod h1:gkl6DBdbKUvmOOJayWPSvS52KPN/8uJGJ2f13gCEB1o=
github.com/disgoorg/json v1.1.0 h1:7xigHvomlVA9PQw9bMGO02PHGJJPqvX5AnwlYg/Tnys=
github.com/disgoorg/json v1.1.0/go.mod h1:BHDwdde0rpQFDVsRLKhma6Y7fTbQKub/zdGO5O9NqqA=
github.com/disgoorg/snowflake/v2 v2.0.1 h1:CuUxGLwggUxEswZOmZ+mZ5i0xSumQdXW9tXW7uGqe+0=
github.com/disgoorg/snowflake/v2 v2.0.1/go.mod h1:SPU9c2CNn5DSyb86QcKtdZgix9osEtKrHLW4rMhfLCs=
github.com/gorilla/websocket v1.5.1 h1:gmztn0JnHVt9JZquRuzLw3g4wouNVzKL15iLr/zn/QY=
github.com/gorilla/websocket v1.5.1/go.mod h1:x3kM2JMyaluk02fnUJpQuwD2dCS5NDG2ZHL0uE0tcaY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad h1:qIQkSlF5vAUHxEmTbaqt1hkJ/t6skqEGYiMag343ucI=
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad/go.mod h1:/pA7k3zsXKdjjAiUhB5CjuKib9KJGCaLvZwtxGC8U0s=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=
golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
golang.org/x/sys v0.17.0 h1:25cE3gD+tdBA7lp7QfhuV+rJiE9YXTcS3VG1SqssI/Y=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@@ -1,95 +0,0 @@
package main
import (
"bytes"
"io"
"net/http"
"regexp"
"strings"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
)
var cmd_addemoji cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "add-emoji",
Description: "Add an external emoji directly to the server.",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageGuildExpressions),
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionString{
Name: "emoji",
Description: "The emoji you want to add",
Required: true,
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
emojiRegex := regexp.MustCompile(`<(.+):(\d+)>`)
emojistring := emojiRegex.FindString(e.SlashCommandInteractionData().String("emoji"))
emojiArray := strings.Split(emojistring, ":")
var emojiName string
var emojiID string
var emojiFileName string
if len(emojiArray) > 1 {
emojiName = strings.TrimSuffix(emojiArray[1], ">")
emojiID = strings.TrimSuffix(emojiArray[2], ">")
}
imageType, emojiReadBit64 := getEmoji(emojiID)
emojiData, err := discord.NewIcon(imageType, emojiReadBit64)
if err != nil {
logrus.Error(err)
}
_, err = e.Client().Rest().CreateEmoji(*e.GuildID(), discord.EmojiCreate{
Name: emojiName,
Image: *emojiData,
})
if err != nil {
if strings.HasPrefix(err.Error(), "50035") {
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Failed adding emoji. Did you provide a correct one?").SetEphemeral(true).Build())
return
}
if strings.HasPrefix(err.Error(), "50138") {
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Failed adding emoji. Unable to resize the emoji image.").SetEphemeral(true).Build())
return
}
logrus.Error(err)
return
}
if imageType == discord.IconTypeGIF {
emojiFileName = emojiName + ".gif"
} else {
emojiFileName = emojiName + ".png"
}
_, emojiRead := getEmoji(emojiID) // for some reason any []bit variable thats used with NewIcon gets corrupted even when its redeclared in a new variable
err = e.CreateMessage(discord.NewMessageCreateBuilder().
SetContentf("Emoji %s sucessfully added to this server!", emojiName).SetFiles(discord.NewFile(emojiFileName, "", emojiRead)).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
},
}
func getEmoji(emojiID string) (discord.IconType, io.Reader) {
resp, err := http.Get("https://cdn.discordapp.com/emojis/" + emojiID)
if err != nil {
logrus.Error(err)
return discord.IconTypePNG, nil
}
defer resp.Body.Close()
imageData, err := io.ReadAll(resp.Body)
if err != nil {
logrus.Error(err)
return discord.IconTypePNG, nil
}
isAnimated := isGIFImage(imageData)
if isAnimated {
return discord.IconTypeGIF, bytes.NewReader(imageData)
} else {
return discord.IconTypePNG, bytes.NewReader(imageData)
}
}

View File

@@ -1,75 +0,0 @@
package main
import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
)
var cmd_autojoinroles cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "autojoinroles",
Description: "Give users a role when they join",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageRoles),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionSubCommand{
Name: "bot",
Description: "Give bots a role when they join (Leave empty to remove current)",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionRole{
Name: "role",
Description: "The role bots should get when they join the server",
},
},
},
&discord.ApplicationCommandOptionSubCommand{
Name: "user",
Description: "Give users a role when they join (Leave empty to remove current)",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionRole{
Name: "role",
Description: "The role users should get when they join the server",
}},
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
var role string
option := *e.SlashCommandInteractionData().SubCommandName
var content string
if len(e.SlashCommandInteractionData().Options) == 1 {
var givenRole discord.Role = e.SlashCommandInteractionData().Role("role")
role = givenRole.ID.String()
botrole, err := getHighestRole(e.GuildID().String(), e.Client())
if err != nil {
logrus.Error(err)
}
if givenRole.Position >= botrole.Position {
content = "<@&" + role + "> is not below the Bot's current highest role(<@&" + botrole.ID.String() + ">). That makes it unable to manage it."
} else {
if setAutoJoinRole(e.GuildID().String(), option, role) {
content = "Updated auto join role for " + option + "s as <@&" + role + ">"
} else {
content = "Setup auto join role for " + option + "s as <@&" + role + ">"
}
}
} else if setAutoJoinRole(e.GuildID().String(), option, role) {
content = "Deleted auto join role for " + option + "s"
}
if content == "" {
content = "No auto join role set for " + option + "s to delete."
}
err := e.CreateMessage(discord.NewMessageCreateBuilder().SetContent(content).SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
purgeUnusedAutoJoinRoles(e.GuildID().String())
},
}

View File

@@ -1,49 +0,0 @@
package main
import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
)
var cmd_autopublish cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "autopublish",
Description: "Toggle automatically publishing every post in a announcement channel",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageChannels),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
channel := e.Channel()
if channel.Type() == discord.ChannelTypeGuildNews {
if toggleAutoPublish(e.GuildID().String(), e.Channel().ID().String()) {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Autopublishing is now disabled on " + discord.ChannelMention(e.Channel().ID())).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Autopublishing is now enabled on " + discord.ChannelMention(e.Channel().ID())).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("This is not an announcement channel!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
},
}

View File

@@ -1,88 +0,0 @@
package main
import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
)
var cmd_blockpolls cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "block-polls",
Description: "Block polls from beeing posted in this channel.",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageChannels),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionSubCommand{
Name: "toggle",
Description: "Toggle blocking polls from beeing posted in this channel.",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionBool{
Name: "global",
Description: "If polls are blocked server wide or only in the current channel.",
},
&discord.ApplicationCommandOptionRole{
Name: "allowed-role",
Description: "The role that bypasses this block role.",
},
},
},
/*&discord.ApplicationCommandOptionSubCommand{
Name: "list",
Description: "List the current block polls rules for this server.",
},*/
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
switch *e.SlashCommandInteractionData().SubCommandName {
case "toggle":
isGlobal := isGlobalBlockPolls(e.GuildID().String())
if isGlobal && !e.SlashCommandInteractionData().Bool("global") {
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Polls are currently globally blocked. Disable global blocking to enable channel specific blocking.").SetEphemeral(true).Build())
} else {
exists, isGlobal := toggleBlockPolls(e.GuildID().String(), e.Channel().ID().String(), e.SlashCommandInteractionData().Bool("global"), e.SlashCommandInteractionData().Role("allowed-role").ID.String())
if exists {
if e.SlashCommandInteractionData().Bool("global") {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Polls are now globally unblocked.").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Polls are now unblocked in " + discord.ChannelMention(e.Channel().ID())).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
} else {
if isGlobal {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Polls are now globally blocked.").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Polls are now blocked in " + discord.ChannelMention(e.Channel().ID())).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
}
}
/*case "list":
list := listBlockPolls(e.GuildID().String())*/
}
},
}

View File

@@ -1,474 +0,0 @@
package main
import (
"bytes"
"fmt"
"strings"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/disgoorg/snowflake/v2"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
"github.com/vaporvee/acecore/custom"
)
var cmd_form cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "form",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageChannels),
Description: "Create custom forms right inside Discord",
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionSubCommand{
Name: "help",
Description: "Gives you an example file and demo for creating custom forms",
},
&discord.ApplicationCommandOptionSubCommand{
Name: "custom",
Description: "Create a new custom form right inside Discord",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionAttachment{
Name: "json",
Description: "Your edited form file",
Required: true,
},
},
},
&discord.ApplicationCommandOptionSubCommand{
Name: "add",
Description: "Adds existing forms to this channel",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionChannel{
Name: "result_channel",
Description: "Where the form results should appear",
ChannelTypes: []discord.ChannelType{discord.ChannelTypeGuildText},
},
&discord.ApplicationCommandOptionMentionable{
Name: "moderator",
Description: "Who can interact with moderating buttons.",
},
&discord.ApplicationCommandOptionString{
Name: "type",
Description: "Which type of form you want to add",
Autocomplete: true,
},
&discord.ApplicationCommandOptionString{
Name: "title",
Description: "The title the form should have",
},
&discord.ApplicationCommandOptionChannel{
Name: "approve_channel",
Description: "Channel for results that need to be accepted by a moderator before sending it to the result channel",
ChannelTypes: []discord.ChannelType{discord.ChannelTypeGuildText},
},
&discord.ApplicationCommandOptionBool{
Name: "mods_can_answer",
Description: "Moderators can open a new channel on the form result, which then pings the user who submitted it",
},
},
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
switch *e.SlashCommandInteractionData().SubCommandName {
case "help":
fileData, err := formTemplates.ReadFile("form_templates/form_demo.json")
if err != nil {
logrus.Error(err)
return
}
fileReader := bytes.NewReader(fileData)
err = e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("NOT SUPPORTED YET!(use `/form add` instead)\n\nGet the example file edit it (make sure to have a unique \"form_type\") and submit it via `/form create`.\nOr use the demo button to get an idea of how the example would look like.").
SetFiles(discord.NewFile("example.json", "json", fileReader)).
SetContainerComponents(discord.ActionRowComponent{discord.NewPrimaryButton("Demo", "form_demo").WithEmoji(discord.ComponentEmoji{Name: "📑"})}).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
case "custom":
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Feature not available yet use `/form add` instead").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
case "add":
var title, formID, overwriteTitle, acceptChannelID string
var modsCanAnswer bool
var resultChannelID string
data := e.SlashCommandInteractionData()
if data.Channel("result_channel").ID.String() != "0" {
resultChannelID = data.Channel("result_channel").ID.String()
}
moderator := data.Role("moderator").ID.String()
if moderator == "0" {
moderator = e.User().ID.String()
}
formID = data.String("type")
overwriteTitle = data.String("title")
if overwriteTitle != "" {
title = overwriteTitle
}
if data.Channel("approve_channel").ID.String() != "0" {
acceptChannelID = data.Channel("approve_channel").ID.String()
}
modsCanAnswer = data.Bool("mods_can_answer")
if formID == "" {
formID = "template_general"
}
if title == "" {
formTitles := map[string]string{
"template_ticket": "Make a new ticket",
"template_url": "Add your URL",
"template_general": "Form",
}
if val, ok := formTitles[formID]; ok {
title = val
}
}
var exists bool = true
var formManageID uuid.UUID = uuid.New()
for exists {
formManageID = uuid.New()
exists = getFormManageIdExists(formManageID)
}
messagebuild := discord.NewMessageCreateBuilder().SetEmbeds(discord.NewEmbedBuilder().
SetTitle(title).SetDescription("Press the bottom button to open a form popup.").SetColor(custom.GetColor("primary")).
Build()).SetContainerComponents(discord.ActionRowComponent{
discord.NewSuccessButton("Submit", "form:"+formManageID.String()).WithEmoji(discord.ComponentEmoji{
Name: "anim_rocket",
ID: snowflake.MustParse("1215740398706757743"),
Animated: true,
})}).
Build()
message, err := e.Client().Rest().CreateMessage(e.Channel().ID(), messagebuild)
if err != nil {
logrus.Error(err)
}
var category string
if modsCanAnswer {
c, err := e.Client().Rest().CreateGuildChannel(*e.GuildID(), discord.GuildCategoryChannelCreate{Name: title + " mod answers"})
if err != nil {
logrus.Error(err)
}
category = c.ID().String()
}
addFormButton(e.GuildID().String(), e.Channel().ID().String(), message.ID.String(), formManageID.String(), formID, resultChannelID, overwriteTitle, acceptChannelID, category, moderator)
err = e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Successfully added form button!").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
}
},
DynamicComponentIDs: func() []string { return getFormButtonIDs() },
DynamicModalIDs: func() []string { return getFormButtonIDs() },
ComponentInteract: func(e *events.ComponentInteractionCreate) {
if e.Data.Type() == discord.ComponentTypeButton {
if strings.ContainsAny(e.ButtonInteractionData().CustomID(), ";") {
var form_manage_id string = strings.TrimPrefix(strings.Split(e.ButtonInteractionData().CustomID(), ";")[0], "form:")
switch strings.Split(e.ButtonInteractionData().CustomID(), ";")[1] {
case "decline":
err := e.Client().Rest().DeleteMessage(e.Channel().ID(), e.Message.ID)
if err != nil {
logrus.Error(err)
}
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Submission declined!").SetEphemeral(true).Build())
case "approve":
embed := e.Message.Embeds[0]
embed.Description = fmt.Sprintf("This submission was approved by <@%s>.", e.User().ID)
_, err := e.Client().Rest().CreateMessage(snowflake.MustParse(getFormResultValues(form_manage_id).ResultChannelID), discord.NewMessageCreateBuilder().
SetEmbeds(embed).
Build())
if err != nil {
logrus.Error(err)
}
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Submission accepted!").SetEphemeral(true).Build())
err = e.Client().Rest().DeleteMessage(e.Channel().ID(), e.Message.ID)
if err != nil {
logrus.Error(err)
}
case "comment":
author := strings.TrimSuffix(strings.Split(e.Message.Embeds[0].Fields[len(e.Message.Embeds[0].Fields)-1].Value, "<@")[1], ">")
embed := e.Message.Embeds[0]
moderator := e.User().ID
channel := createFormComment(form_manage_id, snowflake.MustParse(author), moderator, "answer", embed, *e.GuildID(), e.Client())
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Created channel " + discord.ChannelMention(channel.ID())).SetEphemeral(true).Build())
}
} else {
if strings.HasPrefix(e.ButtonInteractionData().CustomID(), "form:") {
var formManageID string = strings.TrimPrefix(e.ButtonInteractionData().CustomID(), "form:")
e.Modal(jsonStringBuildModal(e.User().ID.String(), formManageID, getFormType(formManageID), getFormOverwriteTitle(formManageID)))
} else if e.ButtonInteractionData().CustomID() == "form_demo" {
e.Modal(jsonStringBuildModal(e.User().ID.String(), "form_demo", "form_demo"))
}
}
}
},
ModalSubmit: func(e *events.ModalSubmitInteractionCreate) {
if !strings.HasPrefix(e.Data.CustomID, "form_demo") {
var form_manage_id string = strings.Split(e.Data.CustomID, ":")[1]
var result FormResult = getFormResultValues(form_manage_id)
var fields []discord.EmbedField
var modal ModalJson = getModalByFormID(getFormType(form_manage_id))
var overwrite_title string = getFormOverwriteTitle(form_manage_id)
if overwrite_title != "" {
modal.Title = overwrite_title
}
var inline bool
var index int = 0
for _, component := range e.Data.Components {
var input discord.TextInputComponent = component.(discord.TextInputComponent)
inline = input.Style == discord.TextInputStyleShort
fields = append(fields, discord.EmbedField{
Name: modal.Form[index].Label,
Value: input.Value,
Inline: &inline,
})
index++
}
fields = append(fields, discord.EmbedField{
Value: "From <#" + e.Channel().ID().String() + "> by " + e.User().Mention(),
})
if result.ResultChannelID == "" {
if result.CommentCategoryID != "" {
channel := createFormComment(form_manage_id, e.User().ID, snowflake.MustParse(result.ModeratorID), "answer", discord.NewEmbedBuilder().
SetAuthorName(*e.User().GlobalName).SetAuthorIcon(*e.User().AvatarURL()).SetTitle("\""+modal.Title+"\"").SetDescription("This is the submitted result").
SetColor(custom.GetColor("primary")).SetFields(fields...).
Build(), *e.GuildID(), e.Client())
err := e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Created channel " + discord.ChannelMention(channel.ID())).SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
} else {
e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("You need to provide either a `result_channel` or enable `mods_can_answer` to create a valid form.").SetEphemeral(true).
Build())
}
} else {
if result.AcceptChannelID == "" {
_, err := e.Client().Rest().CreateMessage(snowflake.MustParse(result.ResultChannelID), discord.NewMessageCreateBuilder().
SetEmbeds(discord.NewEmbedBuilder().
SetAuthorName(*e.User().GlobalName).SetAuthorIcon(*e.User().AvatarURL()).SetTitle("\""+modal.Title+"\"").SetDescription("This is the submitted result").
SetColor(custom.GetColor("primary")).SetFields(fields...).
Build()).
SetContainerComponents(discord.NewActionRow(discord.
NewButton(discord.ButtonStylePrimary, "Comment", "form:"+form_manage_id+";comment", "").
WithEmoji(discord.ComponentEmoji{Name: "👥"}))).
Build())
if err != nil {
logrus.Error(err)
} else {
err = e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Submitted!").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
}
} else {
var buttons []discord.InteractiveComponent
if result.CommentCategoryID != "" {
buttons = []discord.InteractiveComponent{discord.
NewButton(discord.ButtonStylePrimary, "Comment", "form:"+form_manage_id+";comment", "").
WithEmoji(discord.ComponentEmoji{Name: "👥"})}
}
buttons = append(buttons, discord.
NewButton(discord.ButtonStyleDanger, "Decline", "form:"+form_manage_id+";decline", "").
WithEmoji(discord.ComponentEmoji{Name: "🛑"}),
discord.
NewButton(discord.ButtonStyleSuccess, "Approve", "form:"+form_manage_id+";approve", "").
WithEmoji(discord.ComponentEmoji{Name: "🎉"}))
_, err := e.Client().Rest().CreateMessage(snowflake.MustParse(result.AcceptChannelID), discord.NewMessageCreateBuilder().
SetEmbeds(discord.NewEmbedBuilder().
SetAuthorName(*e.User().GlobalName).SetAuthorIcon(*e.User().AvatarURL()).SetTitle("\""+modal.Title+"\"").SetDescription("**This submission needs approval.**").
SetColor(custom.GetColor("primary")).SetFields(fields...).
Build()).
SetContainerComponents(discord.NewActionRow(buttons...)).
Build())
if err != nil {
logrus.Error(err)
} else {
err = e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Submitted!").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
}
}
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("The results would be submited...").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
}
},
Autocomplete: func(e *events.AutocompleteInteractionCreate) {
err := e.AutocompleteResult([]discord.AutocompleteChoice{
&discord.AutocompleteChoiceString{
Name: "Support Ticket",
Value: "template_ticket",
},
&discord.AutocompleteChoiceString{
Name: "Submit URL",
Value: "template_url",
},
&discord.AutocompleteChoiceString{
Name: "General",
Value: "template_general",
},
})
if err != nil {
logrus.Error(err)
}
},
}
var cmd_ticket_form cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "ticket",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageChannels),
Description: "A quick command to create Ticketpanels. (/form for more)",
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionString{
Name: "title",
Description: "The title the ticket should have",
},
&discord.ApplicationCommandOptionMentionable{
Name: "moderator",
Description: "Who can interact with moderating buttons.",
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
var title string = "Ticket"
var moderator string
data := e.SlashCommandInteractionData()
if data.String("title") != "" {
title = data.String("title")
}
moderator = data.Role("moderator").ID.String()
if moderator == "" {
moderator = data.User("moderator").ID.String()
}
var exists bool = true
var formManageID uuid.UUID = uuid.New()
for exists {
formManageID = uuid.New()
exists = getFormManageIdExists(formManageID)
}
messagebuild := discord.NewMessageCreateBuilder().SetEmbeds(discord.NewEmbedBuilder().
SetTitle(title).SetDescription("Press the bottom button to open a form popup.").SetColor(custom.GetColor("primary")).
Build()).SetContainerComponents(discord.ActionRowComponent{
discord.NewSuccessButton("Submit", "form:"+formManageID.String()).WithEmoji(discord.ComponentEmoji{
Name: "anim_rocket",
ID: snowflake.MustParse("1215740398706757743"),
Animated: true,
})}).
Build()
message, err := e.Client().Rest().CreateMessage(e.Channel().ID(), messagebuild)
if err != nil {
logrus.Error(err)
return
}
if title == "" {
title = "Ticket"
}
var category string
c, err := e.Client().Rest().CreateGuildChannel(*e.GuildID(), discord.GuildCategoryChannelCreate{Name: title + " mod answers"})
if err != nil {
logrus.Error(err)
}
category = c.ID().String()
if title == "Ticket" {
title = ""
}
addFormButton(e.GuildID().String(), e.Channel().ID().String(), message.ID.String(), formManageID.String(), "template_ticket", "", title, "", category, moderator)
err = e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Successfully added ticket panel!\n(`/form` for more options or custom ticket forms.)").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
},
}
// moderator can be userID as well as roleID
func createFormComment(form_manage_id string, author snowflake.ID, moderator snowflake.ID, commentName string, embed discord.Embed, guildID snowflake.ID, client bot.Client) discord.Channel {
var category snowflake.ID = snowflake.MustParse(getFormResultValues(form_manage_id).CommentCategoryID)
_, err := client.Rest().GetChannel(category)
if err != nil {
c, err := client.Rest().CreateGuildChannel(guildID, discord.GuildCategoryChannelCreate{Name: strings.Trim(embed.Title, "\"") + " mod " + commentName + "s"})
if err != nil {
logrus.Error(err)
}
category = c.ID()
updateFormCommentCategory(form_manage_id, category.String())
}
ch, err := client.Rest().CreateGuildChannel(guildID, discord.GuildTextChannelCreate{
ParentID: category,
Name: strings.ToLower(embed.Author.Name) + "-" + commentName,
})
if err != nil {
logrus.Error(err)
}
var permissionOverwrites []discord.PermissionOverwrite = []discord.PermissionOverwrite{
discord.RolePermissionOverwrite{
RoleID: guildID,
Deny: discord.PermissionViewChannel,
}}
if isIDRole(client, guildID, moderator) {
permissionOverwrites = append(permissionOverwrites, discord.RolePermissionOverwrite{
RoleID: moderator,
Allow: discord.PermissionViewChannel,
})
} else {
permissionOverwrites = append(permissionOverwrites, discord.MemberPermissionOverwrite{
UserID: moderator,
Allow: discord.PermissionViewChannel,
})
}
permissionOverwrites = append(permissionOverwrites, discord.RolePermissionOverwrite{
RoleID: author,
Allow: discord.PermissionViewChannel,
})
_, err = client.Rest().UpdateChannel(ch.ID(), discord.GuildTextChannelUpdate{PermissionOverwrites: &permissionOverwrites})
if err != nil {
logrus.Error(err)
}
modTypeChar := ""
if isIDRole(client, guildID, moderator) {
modTypeChar = "&"
}
embed.Description = "This was submitted"
_, err = client.Rest().CreateMessage(ch.ID(), discord.NewMessageCreateBuilder().
SetContent("<@"+modTypeChar+moderator.String()+"> <@"+author.String()+">").SetEmbeds(embed).
Build())
if err != nil {
logrus.Error(err)
}
return ch
}
func getFormButtonIDs() []string {
var IDs []string = []string{"form_demo"}
var formButtonIDs []string = getFormManageIDs()
for _, buttonID := range formButtonIDs {
IDs = append(IDs, "form:"+buttonID)
}
return IDs
}

View File

@@ -1,65 +0,0 @@
package main
import (
"fmt"
"net/http"
"time"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
"github.com/vaporvee/acecore/custom"
)
var cmd_ping cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "ping",
Description: "Returns the ping of the bot",
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel,
discord.InteractionContextTypeBotDM,
},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall,
discord.ApplicationIntegrationTypeUserInstall,
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
start := time.Now()
client := http.Client{
Timeout: 5 * time.Second,
}
resp, err := client.Get("https://discord.com/api/v9/gateway/bot")
if err != nil {
logrus.Error(err)
return
}
defer resp.Body.Close()
ping := time.Since(start)
var pingColor string
if ping.Milliseconds() < 200 {
pingColor = "green"
} else if ping.Milliseconds() < 400 {
pingColor = "yellow"
} else {
pingColor = "red"
}
app, err := e.Client().Rest().GetCurrentApplication()
if err != nil {
logrus.Error(err)
}
err = e.CreateMessage(discord.NewMessageCreateBuilder().
SetEmbeds(discord.NewEmbedBuilder().
SetTitle(app.Bot.Username + " ping").
SetDescription(fmt.Sprintf("# %.2fms", ping.Seconds()*1000)).
SetColor(custom.GetColor(pingColor)).Build()).SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
},
}

View File

@@ -1,118 +0,0 @@
package main
import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/disgoorg/snowflake/v2"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
"github.com/vaporvee/acecore/custom"
)
var cmd_sticky cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "sticky",
Description: "Stick or unstick messages to the bottom of the current channel",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageMessages),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionString{
Name: "message",
Description: "The message you want to stick to the bottom of this channel",
Required: false,
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
if len(e.SlashCommandInteractionData().Options) == 0 {
if hasSticky(e.GuildID().String(), e.Channel().ID().String()) {
err := e.Client().Rest().DeleteMessage(e.Channel().ID(), snowflake.MustParse(getStickyMessageID(e.GuildID().String(), e.Channel().ID().String())))
if err != nil {
logrus.Error(err)
}
removeSticky(e.GuildID().String(), e.Channel().ID().String())
err = e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("The sticky message was removed from this channel!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("This channel has no sticky message!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
} else {
inputStickyMessage(e)
}
},
}
var context_sticky cmd.Command = cmd.Command{
Definition: discord.MessageCommandCreate{
Name: "Stick to channel",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageMessages),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
inputStickyMessage(e)
},
}
func inputStickyMessage(e *events.ApplicationCommandInteractionCreate) {
var messageText string
if e.ApplicationCommandInteraction.Data.Type() == discord.ApplicationCommandTypeMessage {
messageText = e.MessageCommandInteractionData().TargetMessage().Content //TODO add more data then just content
} else {
messageText = e.SlashCommandInteractionData().String("message")
}
if messageText == "" {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Can't add empty sticky messages!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
message, err := e.Client().Rest().CreateMessage(e.Channel().ID(), discord.MessageCreate{Embeds: []discord.Embed{
{Description: messageText, Footer: &discord.EmbedFooter{Text: "📌 Sticky message"}, Color: custom.GetColor("primary")}}})
if err != nil {
logrus.Error(err)
}
if hasSticky(e.GuildID().String(), e.Channel().ID().String()) {
err = e.Client().Rest().DeleteMessage(e.Channel().ID(), snowflake.MustParse(getStickyMessageID(e.GuildID().String(), e.Channel().ID().String())))
if err != nil {
logrus.Error(err, getStickyMessageID(e.GuildID().String(), e.Channel().ID().String()))
}
removeSticky(e.GuildID().String(), e.Channel().ID().String())
addSticky(e.GuildID().String(), e.Channel().ID().String(), messageText, message.ID.String())
err = e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Sticky message in this channel was updated!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
addSticky(e.GuildID().String(), e.Channel().ID().String(), messageText, message.ID.String())
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Message sticked to the channel!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
}
}

View File

@@ -1,194 +0,0 @@
package main
import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
)
// TODO: make user installable tag command using userIDs instead of guildIDs
var cmd_tag cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "tag",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageGuild),
Description: "A command to show and edit saved presaved messages.",
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
discord.ApplicationCommandOptionSubCommand{
Name: "get",
Description: "A command to get messages saved to the bot.",
Options: []discord.ApplicationCommandOption{
discord.ApplicationCommandOptionString{
Name: "tag",
Description: "Your predefined tag for the saved message",
Required: true,
Autocomplete: true,
},
},
},
discord.ApplicationCommandOptionSubCommand{
Name: "add",
Description: "A command to add messages saved to the bot.",
},
discord.ApplicationCommandOptionSubCommand{
Name: "remove",
Description: "A command to remove messages saved to the bot.",
Options: []discord.ApplicationCommandOption{
discord.ApplicationCommandOptionString{
Name: "tag",
Description: "The tag you want to remove",
Required: true,
Autocomplete: true,
},
},
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
switch *e.SlashCommandInteractionData().SubCommandName {
case "get":
GetTagCommand(e)
case "add":
AddTagCommand(e)
case "remove":
removeTag(e.GuildID().String(), e.SlashCommandInteractionData().String("tag"))
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Tag removed!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
},
ModalIDs: []string{"tag_add_modal"},
ModalSubmit: func(e *events.ModalSubmitInteractionCreate) {
tagName := e.Data.Text("tag_add_modal_name")
tagContent := e.Data.Text("tag_add_modal_content")
addTag(e.GuildID().String(), tagName, tagContent)
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Tag \"" + tagName + "\" added!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
},
Autocomplete: func(e *events.AutocompleteInteractionCreate) {
AutocompleteTag(e)
},
}
var cmd_tag_short cmd.Command = cmd.Command{
Definition: discord.SlashCommandCreate{
Name: "g",
Description: "A short command to get presaved messages.",
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
discord.ApplicationCommandOptionString{
Name: "tag",
Description: "Your predefined tag for the saved message",
Required: true,
Autocomplete: true,
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
GetTagCommand(e)
},
Autocomplete: func(e *events.AutocompleteInteractionCreate) {
AutocompleteTag(e)
},
}
var context_tag cmd.Command = cmd.Command{
Definition: discord.MessageCommandCreate{
Name: "Save as tag",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageGuild),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
AddTagCommand(e)
},
}
func GetTagCommand(e *events.ApplicationCommandInteractionCreate) {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent(getTagContent(e.GuildID().String(), e.SlashCommandInteractionData().String("tag"))).
Build())
if err != nil {
logrus.Error(err)
}
}
func AddTagCommand(e *events.ApplicationCommandInteractionCreate) {
var prevalue string
if e.ApplicationCommandInteraction.Data.Type() == discord.ApplicationCommandTypeMessage {
prevalue = e.MessageCommandInteractionData().TargetMessage().Content
}
err := e.Modal(discord.ModalCreate{
CustomID: "tag_add_modal" + e.User().ID.String(),
Title: "Add a custom tag command",
Components: []discord.ContainerComponent{
discord.ActionRowComponent{
discord.TextInputComponent{
CustomID: "tag_add_modal_name",
Label: "Name",
Style: discord.TextInputStyleShort,
Required: true,
MaxLength: 20,
Value: "",
},
},
discord.ActionRowComponent{
discord.TextInputComponent{
CustomID: "tag_add_modal_content",
Label: "Content",
Style: discord.TextInputStyleParagraph,
Required: true,
MaxLength: 2000,
Value: prevalue,
},
},
},
})
if err != nil {
logrus.Error(err)
}
}
func AutocompleteTag(e *events.AutocompleteInteractionCreate) {
err := e.AutocompleteResult(generateTagChoices(e.GuildID().String()))
if err != nil {
logrus.Error(err)
}
}
func generateTagChoices(guildID string) []discord.AutocompleteChoice {
choices := []discord.AutocompleteChoice{}
IDs, err := getTagIDs(guildID)
if err != nil {
logrus.Error(err)
return choices
}
for _, id := range IDs {
id_name := getTagName(guildID, id)
choices = append(choices, &discord.AutocompleteChoiceString{
Name: id_name,
Value: id,
})
}
return choices
}

View File

@@ -1,23 +1,22 @@
package main package main
import ( import (
"database/sql"
"github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/events"
"github.com/sirupsen/logrus" "github.com/vaporvee/acecore/shared"
"github.com/vaporvee/acecore/cmd"
) )
var Plugin = &cmd.Plugin{ var db *sql.DB
var Plugin = &shared.Plugin{
Name: "testplugin", Name: "testplugin",
Register: func(e *events.Ready) error { Init: func(d *sql.DB) error {
app, err := e.Client().Rest().GetCurrentApplication() db = d
if err != nil {
return err
}
logrus.Infof("%s has a working plugin called \"testplugin\"", app.Bot.Username)
return nil return nil
}, },
Commands: []cmd.Command{ Commands: []shared.Command{
{ {
Definition: discord.SlashCommandCreate{ Definition: discord.SlashCommandCreate{
Name: "testplugincommand", Name: "testplugincommand",

4
go.mod
View File

@@ -10,7 +10,7 @@ require (
github.com/joho/godotenv v1.5.1 github.com/joho/godotenv v1.5.1
github.com/lib/pq v1.10.9 github.com/lib/pq v1.10.9
github.com/sirupsen/logrus v1.9.3 github.com/sirupsen/logrus v1.9.3
github.com/vaporvee/acecore/cmd v0.0.0-20240414204205-9ac9f89071f6 github.com/vaporvee/acecore/shared v0.0.0-20240414204205-9ac9f89071f6
) )
require ( require (
@@ -20,3 +20,5 @@ require (
golang.org/x/net v0.21.0 // indirect golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect golang.org/x/sys v0.17.0 // indirect
) )
replace github.com/vaporvee/acecore/shared => ./shared

4
go.sum
View File

@@ -25,8 +25,8 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/vaporvee/acecore/cmd v0.0.0-20240414204205-9ac9f89071f6 h1:QyVs7R8dTS5KEnOjUbTV2jFe7VqGyi8q4krdjy8f9j0= github.com/vaporvee/acecore/shared v0.0.0-20240414204205-9ac9f89071f6 h1:QyVs7R8dTS5KEnOjUbTV2jFe7VqGyi8q4krdjy8f9j0=
github.com/vaporvee/acecore/cmd v0.0.0-20240414204205-9ac9f89071f6/go.mod h1:03W2NrAPbAqa7gsqAM+2K/wT28stj9BmEhC5NzeGe3A= github.com/vaporvee/acecore/shared v0.0.0-20240414204205-9ac9f89071f6/go.mod h1:03W2NrAPbAqa7gsqAM+2K/wT28stj9BmEhC5NzeGe3A=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=

View File

@@ -3,31 +3,22 @@ package main
import ( import (
"fmt" "fmt"
"os" "os"
"path/filepath"
"plugin"
"runtime"
"slices" "slices"
"strings" "strings"
"github.com/disgoorg/disgo/bot" "github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/events"
"github.com/disgoorg/snowflake/v2"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
"github.com/vaporvee/acecore/custom" "github.com/vaporvee/acecore/shared"
) )
var commands []cmd.Command = []cmd.Command{cmd_tag, cmd_tag_short, context_tag, cmd_sticky, context_sticky, cmd_ping, cmd_addemoji, cmd_form, cmd_ticket_form, cmd_blockpolls, cmd_autopublish, cmd_autojoinroles} var commands []shared.Command
func ready(e *events.Ready) { func ready(e *events.Ready) {
logrus.Info("Starting up...") logrus.Info("Starting up...")
findAndDeleteUnusedMessages(e.Client())
removeOldCommandFromAllGuilds(e.Client()) removeOldCommandFromAllGuilds(e.Client())
err := loadPlugins("plugins/", e)
if err != nil {
logrus.Warn(err)
}
var existingCommandNames []string var existingCommandNames []string
existingCommands, err := e.Client().Rest().GetGlobalCommands(e.Client().ApplicationID(), false) existingCommands, err := e.Client().Rest().GetGlobalCommands(e.Client().ApplicationID(), false)
if err != nil { if err != nil {
@@ -56,68 +47,6 @@ func ready(e *events.Ready) {
logrus.Info("Successfully started the Bot!") logrus.Info("Successfully started the Bot!")
} }
func loadPlugins(directory string, e *events.Ready) error {
files, err := os.ReadDir(directory)
if err != nil {
return err
}
// Determine the appropriate file extension for dynamic libraries
var ext string
switch runtime.GOOS {
case "windows":
ext = ".dll"
case "linux":
ext = ".so"
case "darwin":
ext = ".dylib"
default:
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
for _, file := range files {
if filepath.Ext(file.Name()) == ext {
p, err := plugin.Open(filepath.Join(directory, file.Name()))
if err != nil {
return err
}
symPlugin, err := p.Lookup("Plugin")
if err != nil {
logrus.Errorf("Error looking up symbol 'Plugin' in %s: %v", file.Name(), err)
continue
}
pluginPtr, ok := symPlugin.(**cmd.Plugin)
if !ok {
logrus.Errorf("Plugin does not match expected type")
continue
}
plugin := *pluginPtr
if plugin.Name == "" {
logrus.Warn("Plugin is unnamed")
}
if plugin.Commands != nil {
commands = append(commands, plugin.Commands...)
} else {
logrus.Errorf("Plugin %s has no commands set", plugin.Name)
continue
}
if plugin.Register != nil {
err = plugin.Register(e, db)
if err != nil {
logrus.Errorf("Error running plugin register %s function: %v", plugin.Name, err)
continue
}
}
}
}
return nil
}
func applicationCommandInteractionCreate(e *events.ApplicationCommandInteractionCreate) { func applicationCommandInteractionCreate(e *events.ApplicationCommandInteractionCreate) {
for _, command := range commands { for _, command := range commands {
if command.Interact != nil && e.Data.CommandName() == command.Definition.CommandName() { if command.Interact != nil && e.Data.CommandName() == command.Definition.CommandName() {
@@ -202,65 +131,3 @@ func removeOldCommandFromAllGuilds(c bot.Client) {
} }
} }
} }
func messageCreate(e *events.MessageCreate) {
if len(e.Message.Embeds) == 0 || e.Message.Embeds[0].Footer == nil || e.Message.Embeds[0].Footer.Text != "📌 Sticky message" {
if hasSticky(e.Message.GuildID.String(), e.Message.ChannelID.String()) {
stickymessageID := getStickyMessageID(e.Message.GuildID.String(), e.Message.ChannelID.String())
err := e.Client().Rest().DeleteMessage(e.ChannelID, snowflake.MustParse(stickymessageID))
stickyMessage, _ := e.Client().Rest().CreateMessage(e.ChannelID, discord.MessageCreate{
Embeds: []discord.Embed{
{
Footer: &discord.EmbedFooter{
Text: "📌 Sticky message",
},
Color: custom.GetColor("primary"),
Description: getStickyMessageContent(e.Message.GuildID.String(), e.Message.ChannelID.String()),
},
},
})
if err != nil {
logrus.Error(err)
}
updateStickyMessageID(e.Message.GuildID.String(), e.Message.ChannelID.String(), stickyMessage.ID.String())
}
}
channel, err := e.Client().Rest().GetChannel(e.Message.ChannelID)
if err != nil {
logrus.Error(err)
}
if channel != nil {
isBlockPollsEnabledGlobal := isGlobalBlockPolls(e.GuildID.String())
isBlockPollsEnabled, allowedRole := getBlockPollsEnabled(e.GuildID.String(), e.Message.ChannelID.String())
var hasAllowedRole bool
if allowedRole != "" {
hasAllowedRole = slices.Contains(e.Message.Member.RoleIDs, snowflake.MustParse(allowedRole))
}
if (isBlockPollsEnabledGlobal || isBlockPollsEnabled) && !hasAllowedRole && messageIsPoll(e.Message.ChannelID.String(), e.Message.ID.String(), e.Client()) {
e.Client().Rest().DeleteMessage(e.Message.ChannelID, e.Message.ID)
}
if channel.Type() == discord.ChannelTypeGuildNews {
if isAutopublishEnabled(e.GuildID.String(), e.ChannelID.String()) {
_, err := e.Client().Rest().CrosspostMessage(e.ChannelID, e.MessageID)
if err != nil {
logrus.Error(err)
return
}
}
}
}
}
func messageDelete(e *events.MessageDelete) { //TODO: also clear on bot start when message doesn't exist
tryDeleteUnusedMessage(e.MessageID.String())
}
func guildMemberJoin(e *events.GuildMemberJoin) {
role := getAutoJoinRole(e.GuildID.String(), e.Member.User.Bot)
if role != "" {
err := e.Client().Rest().AddMemberRole(e.GuildID, e.Member.User.ID, snowflake.MustParse(role))
if err != nil {
logrus.Error(err)
}
}
}

93
main.go
View File

@@ -3,10 +3,14 @@ package main
import ( import (
"context" "context"
"database/sql" "database/sql"
"fmt"
"io" "io"
"net/url" "net/url"
"os" "os"
"os/signal" "os/signal"
"path/filepath"
"plugin"
"runtime"
"strconv" "strconv"
"syscall" "syscall"
"time" "time"
@@ -19,6 +23,7 @@ import (
"github.com/joho/godotenv" "github.com/joho/godotenv"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/log2webhook" "github.com/vaporvee/acecore/log2webhook"
"github.com/vaporvee/acecore/shared"
"github.com/vaporvee/acecore/web" "github.com/vaporvee/acecore/web"
) )
@@ -26,6 +31,8 @@ var (
db *sql.DB db *sql.DB
) )
var listeners []func()
func main() { func main() {
logrusInitFile() logrusInitFile()
var err error var err error
@@ -35,8 +42,16 @@ func main() {
if err != nil { if err != nil {
logrus.Fatal(err) logrus.Fatal(err)
} }
initTables() err = loadPlugins("plugins/")
client, err := disgo.New(os.Getenv("BOT_TOKEN"), if err != nil {
logrus.Warn(err)
}
shared.BotConfigs = append(shared.BotConfigs,
bot.WithEventListenerFunc(ready),
bot.WithEventListenerFunc(applicationCommandInteractionCreate),
bot.WithEventListenerFunc(autocompleteInteractionCreate),
bot.WithEventListenerFunc(componentInteractionCreate),
bot.WithEventListenerFunc(modalSubmitInteractionCreate),
bot.WithGatewayConfigOpts( bot.WithGatewayConfigOpts(
gateway.WithIntents( gateway.WithIntents(
gateway.IntentGuilds, gateway.IntentGuilds,
@@ -45,15 +60,9 @@ func main() {
gateway.IntentGuildMembers, gateway.IntentGuildMembers,
gateway.IntentDirectMessages, gateway.IntentDirectMessages,
), ),
), ))
bot.WithEventListenerFunc(ready), client, err := disgo.New(os.Getenv("BOT_TOKEN"),
bot.WithEventListenerFunc(applicationCommandInteractionCreate), shared.BotConfigs...,
bot.WithEventListenerFunc(autocompleteInteractionCreate),
bot.WithEventListenerFunc(componentInteractionCreate),
bot.WithEventListenerFunc(modalSubmitInteractionCreate),
bot.WithEventListenerFunc(messageCreate),
bot.WithEventListenerFunc(messageDelete),
bot.WithEventListenerFunc(guildMemberJoin),
) )
if err != nil { if err != nil {
logrus.Fatal("error creating Discord session,", err) logrus.Fatal("error creating Discord session,", err)
@@ -102,3 +111,65 @@ func logrusInitFile() {
mw := io.MultiWriter(os.Stdout, log, &log2webhook.WebhookWriter{}) mw := io.MultiWriter(os.Stdout, log, &log2webhook.WebhookWriter{})
logrus.SetOutput(mw) logrus.SetOutput(mw)
} }
func loadPlugins(directory string) error {
files, err := os.ReadDir(directory)
if err != nil {
return err
}
// Determine the appropriate file extension for dynamic libraries
var ext string
switch runtime.GOOS {
case "windows":
ext = ".dll"
case "linux":
ext = ".so"
case "darwin":
ext = ".dylib"
default:
return fmt.Errorf("unsupported operating system: %s", runtime.GOOS)
}
for _, file := range files {
if filepath.Ext(file.Name()) == ext {
p, err := plugin.Open(filepath.Join(directory, file.Name()))
if err != nil {
return err
}
symPlugin, err := p.Lookup("Plugin")
if err != nil {
logrus.Errorf("Error looking up symbol 'Plugin' in %s: %v", file.Name(), err)
continue
}
pluginPtr, ok := symPlugin.(**shared.Plugin)
if !ok {
logrus.Errorf("Plugin does not match expected type")
continue
}
plugin := *pluginPtr
if plugin.Name == "" {
logrus.Warn("Plugin is unnamed")
}
if plugin.Commands != nil {
commands = append(commands, plugin.Commands...)
} else {
logrus.Errorf("Plugin %s has no commands set", plugin.Name)
continue
}
if plugin.Init != nil {
err = plugin.Init(db)
if err != nil {
logrus.Errorf("Error running plugin register %s function: %v", plugin.Name, err)
continue
}
}
}
}
return nil
}

View File

@@ -1,514 +0,0 @@
package main
import (
"log"
"os"
"slices"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)
func initTables() {
createTableQuery := `CREATE TABLE IF NOT EXISTS tags (
tag_id TEXT NOT NULL,
tag_name TEXT NOT NULL,
tag_content TEXT NOT NULL,
guild_id TEXT NOT NULL,
PRIMARY KEY (tag_id, guild_id)
);
CREATE TABLE IF NOT EXISTS sticky (
message_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
message_content TEXT NOT NULL,
guild_id TEXT NOT NULL,
PRIMARY KEY (channel_id, guild_id)
);
CREATE TABLE IF NOT EXISTS custom_forms (
form_type TEXT NOT NULL,
title TEXT NOT NULL,
json JSON NOT NULL,
guild_id TEXT NOT NULL,
PRIMARY KEY (form_type, guild_id)
);
CREATE TABLE IF NOT EXISTS form_manage (
form_manage_id TEXT NOT NULL,
form_type TEXT NOT NULL,
overwrite_title TEXT,
message_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
guild_id TEXT NOT NULL,
result_channel_id TEXT NOT NULL,
accept_channel_id TEXT,
comment_category TEXT,
moderator_id TEXT,
PRIMARY KEY (form_manage_id, form_type)
);
CREATE TABLE IF NOT EXISTS autojoinroles (
guild_id TEXT NOT NULL,
bot_role TEXT,
user_role TEXT,
PRIMARY KEY (guild_id)
);
CREATE TABLE IF NOT EXISTS autopublish (
guild_id TEXT NOT NULL,
news_channel_id TEXT NOT NULL,
PRIMARY KEY (guild_id, news_channel_id)
);
CREATE TABLE IF NOT EXISTS blockpolls (
guild_id TEXT NOT NULL,
channel_id TEXT,
global BOOLEAN,
allowed_role TEXT,
PRIMARY KEY (guild_id)
)
`
_, err := db.Exec(createTableQuery)
if err != nil {
log.Fatal(err)
}
if slices.Contains(os.Args, "--form-db-update") {
_, err = db.Exec("ALTER TABLE blockpolls ADD global BOOLEAN;")
if err != nil {
log.Fatal(err)
}
_, err = db.Exec("ALTER TABLE blockpolls ADD allowed_role TEXT;")
if err != nil {
log.Fatal(err)
}
}
}
type FormResult struct {
OverwriteTitle string
ResultChannelID string
AcceptChannelID string
CommentCategoryID string
ModeratorID string
}
type BlockPoll struct {
ChannelID string
Global bool
AllowedRole string
}
func addTag(guildID, tagName, tagContent string) bool {
var exists bool = true
//TODO: add modify command
id := uuid.New()
for exists {
id = uuid.New()
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM tags WHERE guild_id = $1 AND tag_id = $2)", guildID, id).Scan(&exists)
if err != nil {
logrus.Error(err)
}
}
_, err := db.Exec("INSERT INTO tags (guild_id, tag_name, tag_content, tag_id) VALUES ($1, $2, $3, $4)", guildID, tagName, tagContent, id)
if err != nil {
logrus.Error(err)
}
return exists
}
func removeTag(guildID string, tagID string) {
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM tags WHERE guild_id = $1 AND tag_id = $2)", guildID, tagID).Scan(&exists)
if err != nil {
logrus.Error(err)
}
if exists {
_, err = db.Exec("DELETE FROM tags WHERE guild_id = $1 AND tag_id = $2", guildID, tagID)
if err != nil {
logrus.Error(err)
}
}
}
func getTagIDs(guildID string) ([]string, error) {
var IDs []string
rows, err := db.Query("SELECT tag_id FROM tags WHERE guild_id = $1", guildID)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var id string
if err := rows.Scan(&id); err != nil {
return nil, err
}
IDs = append(IDs, id)
}
if err := rows.Err(); err != nil {
return nil, err
}
return IDs, nil
}
func getTagName(guildID string, tagID string) string {
var tagName string
db.QueryRow("SELECT tag_name FROM tags WHERE guild_id = $1 AND tag_id = $2", guildID, tagID).Scan(&tagName)
return tagName
}
func getTagContent(guildID string, tagID string) string {
var tagContent string
db.QueryRow("SELECT tag_content FROM tags WHERE guild_id = $1 AND tag_id = $2", guildID, tagID).Scan(&tagContent)
return tagContent
}
func addSticky(guildID string, channelID string, messageContent string, messageID string) {
_, err := db.Exec("INSERT INTO sticky (guild_id, channel_id, message_id, message_content) VALUES ($1, $2, $3, $4)", guildID, channelID, messageID, messageContent)
if err != nil {
logrus.Error(err)
}
}
func hasSticky(guildID string, channelID string) bool {
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM sticky WHERE guild_id = $1 AND channel_id = $2)", guildID, channelID).Scan(&exists)
if err != nil {
logrus.Error(err)
}
return exists
}
func getStickyMessageID(guildID string, channelID string) string {
var messageID string
exists := hasSticky(guildID, channelID)
if exists {
err := db.QueryRow("SELECT message_id FROM sticky WHERE guild_id = $1 AND channel_id = $2", guildID, channelID).Scan(&messageID)
if err != nil {
logrus.Error(err)
}
}
return messageID
}
func getStickyMessageContent(guildID string, channelID string) string {
var messageID string
exists := hasSticky(guildID, channelID)
if exists {
err := db.QueryRow("SELECT message_content FROM sticky WHERE guild_id = $1 AND channel_id = $2", guildID, channelID).Scan(&messageID)
if err != nil {
logrus.Error(err)
}
}
return messageID
}
func updateStickyMessageID(guildID string, channelID string, messageID string) {
exists := hasSticky(guildID, channelID)
if exists {
_, err := db.Exec("UPDATE sticky SET message_id = $1 WHERE guild_id = $2 AND channel_id = $3", messageID, guildID, channelID)
if err != nil {
logrus.Error(err)
}
}
}
func removeSticky(guildID string, channelID string) {
_, err := db.Exec("DELETE FROM sticky WHERE guild_id = $1 AND channel_id = $2", guildID, channelID)
if err != nil {
logrus.Error(err)
}
}
func getFormManageIdExists(id uuid.UUID) bool {
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM form_manage WHERE form_manage_id = $1)", id).Scan(&exists)
if err != nil {
logrus.Error(err)
}
return exists
}
func addFormButton(guildID string, channelID string, messageID string, formManageID string, formType string, resultChannelID string, overwriteTitle string, acceptChannelID string, commentCategory string, moderator_id string) {
_, err := db.Exec(
`INSERT INTO form_manage (
guild_id,
form_manage_id,
channel_id,
message_id,
form_type,
result_channel_id,
overwrite_title,
accept_channel_id,
comment_category,
moderator_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
guildID, formManageID, channelID, messageID, formType, resultChannelID, overwriteTitle, acceptChannelID, commentCategory, moderator_id)
if err != nil {
logrus.Error(err)
}
}
func getFormManageIDs() []string {
if db == nil {
return nil
}
var IDs []string
rows, err := db.Query("SELECT form_manage_id FROM form_manage")
if err != nil {
logrus.Error(err)
return nil
}
defer rows.Close()
for rows.Next() {
var id string
if err := rows.Scan(&id); err != nil {
logrus.Error(err)
return nil
}
IDs = append(IDs, id)
}
if err := rows.Err(); err != nil {
logrus.Error(err)
return nil
}
return IDs
}
func getFormType(formManageID string) string {
var formType string
err := db.QueryRow("SELECT form_type FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&formType)
if err != nil {
logrus.Error(err)
}
return formType
}
func getFormResultValues(formManageID string) FormResult {
var result FormResult
err := db.QueryRow("SELECT overwrite_title FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.OverwriteTitle)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT result_channel_id FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.ResultChannelID)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT accept_channel_id FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.AcceptChannelID)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT comment_category FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.CommentCategoryID)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT moderator_id FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.ModeratorID)
if err != nil {
logrus.Error(err)
}
return result
}
func getFormOverwriteTitle(formManageID string) string {
var overwriteTitle string
err := db.QueryRow("SELECT overwrite_title FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&overwriteTitle)
if err != nil {
logrus.Error(err)
}
return overwriteTitle
}
func updateFormCommentCategory(formManageID string, comment_category string) {
_, err := db.Exec("UPDATE form_manage SET comment_category = $1 WHERE form_manage_id = $2", comment_category, formManageID)
if err != nil {
logrus.Error(err)
}
}
func setAutoJoinRole(guildID string, option string, roleID string) bool {
var role_exists bool
var autojoinroles_exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM autojoinroles WHERE guild_id = $1)", guildID).Scan(&autojoinroles_exists)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT EXISTS (SELECT 1 FROM autojoinroles WHERE guild_id = $1 AND "+option+"_role IS NOT NULL AND "+option+"_role != '')", guildID).Scan(&role_exists)
if err != nil {
logrus.Error(err)
}
if autojoinroles_exists {
_, err = db.Exec("UPDATE autojoinroles SET "+option+"_role = $1 WHERE guild_id = $2", roleID, guildID)
if err != nil {
logrus.Error(err)
}
} else {
_, err = db.Exec("INSERT INTO autojoinroles (guild_id, "+option+"_role) VALUES ($1, $2)", guildID, roleID)
if err != nil {
logrus.Error(err)
}
}
return role_exists
}
func purgeUnusedAutoJoinRoles(guildID string) {
_, err := db.Exec("DELETE FROM autojoinroles WHERE guild_id = $1 AND user_role = '' OR user_role IS NULL AND bot_role = '' OR bot_role IS NULL", guildID)
if err != nil {
logrus.Error(err)
}
}
func getAutoJoinRole(guildID string, isBot bool) string {
var isBotString string
var role string
if isBot {
isBotString = "bot"
} else {
isBotString = "user"
}
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM autojoinroles WHERE guild_id = $1)", guildID).Scan(&exists)
if err != nil {
logrus.Error(err)
return role
}
if exists {
err = db.QueryRow("SELECT "+isBotString+"_role FROM autojoinroles WHERE guild_id = $1", guildID).Scan(&role)
if err != nil {
logrus.Error(err, guildID)
}
}
return role
}
func toggleAutoPublish(guildID string, newsChannelID string) bool {
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM autopublish WHERE guild_id = $1 AND news_channel_id = $2)", guildID, newsChannelID).Scan(&exists)
if err != nil {
logrus.Error(err)
}
if exists {
_, err := db.Exec("DELETE FROM autopublish WHERE guild_id = $1 AND news_channel_id = $2", guildID, newsChannelID)
if err != nil {
logrus.Error(err)
}
} else {
_, err := db.Exec("INSERT INTO autopublish (guild_id, news_channel_id) VALUES ($1, $2)", guildID, newsChannelID)
if err != nil {
logrus.Error(err)
}
}
return exists
}
func isAutopublishEnabled(guildID string, newsChannelID string) bool {
var enabled bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM autopublish WHERE guild_id = $1 AND news_channel_id = $2)", guildID, newsChannelID).Scan(&enabled)
if err != nil {
logrus.Error(err)
}
return enabled
}
func isGlobalBlockPolls(guildID string) bool {
var globalexists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM blockpolls WHERE guild_id = $1 AND global = true)", guildID).Scan(&globalexists)
if err != nil {
logrus.Error(err)
}
return globalexists
}
func toggleBlockPolls(guildID string, channelID string, global bool, allowedRole string) (e bool, isGlobal bool) {
globalexists := isGlobalBlockPolls(guildID)
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM blockpolls WHERE guild_id = $1 AND channel_id = $2)", guildID, channelID).Scan(&exists)
if err != nil {
logrus.Error(err)
}
if globalexists {
_, err := db.Exec("DELETE FROM blockpolls WHERE guild_id = $1 AND global = true", guildID)
if err != nil {
logrus.Error(err)
}
return true, true
} else if global {
_, err = db.Exec("DELETE FROM blockpolls WHERE guild_id = $1", guildID)
if err != nil {
logrus.Error(err)
}
_, err := db.Exec("INSERT INTO blockpolls (guild_id, global, channel_id, allowed_role) VALUES ($1, true, $2, $3)", guildID, channelID, allowedRole)
if err != nil {
logrus.Error(err)
}
return false, true
} else if exists && !globalexists {
_, err := db.Exec("DELETE FROM blockpolls WHERE guild_id = $1 AND channel_id = $2", guildID, channelID)
if err != nil {
logrus.Error(err)
}
return true, false
} else if !globalexists {
_, err := db.Exec("INSERT INTO blockpolls (guild_id, channel_id, allowed_role) VALUES ($1, $2, $3)", guildID, channelID, allowedRole)
if err != nil {
logrus.Error(err)
}
return false, false
} else {
return false, false
}
}
func listBlockPolls(guildID string) []BlockPoll {
var list []BlockPoll
rows, err := db.Query("SELECT channel_id, global, allowed_role FROM blockpolls WHERE guild_id = $1", guildID)
if err != nil {
log.Fatal(err)
}
for rows.Next() {
var bp BlockPoll
err := rows.Scan(&bp.ChannelID, &bp.Global, &bp.AllowedRole)
if err != nil {
log.Fatal(err)
}
list = append(list, bp)
}
return list
}
func getBlockPollsEnabled(guildID string, channelID string) (isEnabled bool, allowedRole string) {
var enabled bool
var v_allowedRole string
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM blockpolls WHERE guild_id = $1 AND channel_id = $2)", guildID, channelID).Scan(&enabled)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT allowed_role FROM blockpolls WHERE guild_id = $1 AND channel_id = $2", guildID, channelID).Scan(&v_allowedRole)
if err != nil && err.Error() != "sql: no rows in result set" {
logrus.Error(err)
}
return enabled, v_allowedRole
}
func tryDeleteUnusedMessage(messageID string) {
_, err := db.Exec("DELETE FROM form_manage WHERE message_id = $1", messageID)
if err != nil {
logrus.Error(err)
}
}
func getAllSavedMessages() []MessageIDs {
var savedMessages []MessageIDs
rows, err := db.Query("SELECT message_id, channel_id FROM form_manage")
if err != nil {
logrus.Error(err)
return nil
}
defer rows.Close()
for rows.Next() {
var messageID, channelID string
if err := rows.Scan(&messageID, &channelID); err != nil {
logrus.Error(err)
continue
}
savedMessages = append(savedMessages, MessageIDs{ID: messageID, ChannelID: channelID})
}
if err := rows.Err(); err != nil {
logrus.Error(err)
}
return savedMessages
}

115
plugin_src/addemoji/main.go Normal file
View File

@@ -0,0 +1,115 @@
package main
import (
"bytes"
"io"
"net/http"
"regexp"
"strings"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/shared"
)
var Plugin = &shared.Plugin{
Name: "Add Emoji",
Commands: []shared.Command{
{
Definition: discord.SlashCommandCreate{
Name: "add-emoji",
Description: "Add an external emoji directly to the server.",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageGuildExpressions),
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionString{
Name: "emoji",
Description: "The emoji you want to add",
Required: true,
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
emojiRegex := regexp.MustCompile(`<(.+):(\d+)>`)
emojistring := emojiRegex.FindString(e.SlashCommandInteractionData().String("emoji"))
emojiArray := strings.Split(emojistring, ":")
var emojiName string
var emojiID string
var emojiFileName string
if len(emojiArray) > 1 {
emojiName = strings.TrimSuffix(emojiArray[1], ">")
emojiID = strings.TrimSuffix(emojiArray[2], ">")
}
imageType, emojiReadBit64 := getEmoji(emojiID)
emojiData, err := discord.NewIcon(imageType, emojiReadBit64)
if err != nil {
logrus.Error(err)
}
_, err = e.Client().Rest().CreateEmoji(*e.GuildID(), discord.EmojiCreate{
Name: emojiName,
Image: *emojiData,
})
if err != nil {
if strings.HasPrefix(err.Error(), "50035") {
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Failed adding emoji. Did you provide a correct one?").SetEphemeral(true).Build())
return
}
if strings.HasPrefix(err.Error(), "50138") {
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Failed adding emoji. Unable to resize the emoji image.").SetEphemeral(true).Build())
return
}
logrus.Error(err)
return
}
if imageType == discord.IconTypeGIF {
emojiFileName = emojiName + ".gif"
} else {
emojiFileName = emojiName + ".png"
}
_, emojiRead := getEmoji(emojiID) // for some reason any []bit variable thats used with NewIcon gets corrupted even when its redeclared in a new variable
err = e.CreateMessage(discord.NewMessageCreateBuilder().
SetContentf("Emoji %s sucessfully added to this server!", emojiName).SetFiles(discord.NewFile(emojiFileName, "", emojiRead)).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
},
},
},
}
func getEmoji(emojiID string) (discord.IconType, io.Reader) {
resp, err := http.Get("https://cdn.discordapp.com/emojis/" + emojiID)
if err != nil {
logrus.Error(err)
return discord.IconTypePNG, nil
}
defer resp.Body.Close()
imageData, err := io.ReadAll(resp.Body)
if err != nil {
logrus.Error(err)
return discord.IconTypePNG, nil
}
isAnimated := isGIFImage(imageData)
if isAnimated {
return discord.IconTypeGIF, bytes.NewReader(imageData)
} else {
return discord.IconTypePNG, bytes.NewReader(imageData)
}
}
func isGIFImage(imageData []byte) bool {
if len(imageData) < 6 {
return false
}
// Check for the GIF89a header at the beginning of the byte array
if string(imageData[0:6]) == "GIF89a" {
return true
}
// Check for the GIF87a header at the beginning of the byte array
if string(imageData[0:6]) == "GIF87a" {
return true
}
return false
}

View File

@@ -0,0 +1,60 @@
package main
import (
"github.com/sirupsen/logrus"
)
func setAutoJoinRole(guildID string, option string, roleID string) bool {
var role_exists bool
var autojoinroles_exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM autojoinroles WHERE guild_id = $1)", guildID).Scan(&autojoinroles_exists)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT EXISTS (SELECT 1 FROM autojoinroles WHERE guild_id = $1 AND "+option+"_role IS NOT NULL AND "+option+"_role != '')", guildID).Scan(&role_exists)
if err != nil {
logrus.Error(err)
}
if autojoinroles_exists {
_, err = db.Exec("UPDATE autojoinroles SET "+option+"_role = $1 WHERE guild_id = $2", roleID, guildID)
if err != nil {
logrus.Error(err)
}
} else {
_, err = db.Exec("INSERT INTO autojoinroles (guild_id, "+option+"_role) VALUES ($1, $2)", guildID, roleID)
if err != nil {
logrus.Error(err)
}
}
return role_exists
}
func purgeUnusedAutoJoinRoles(guildID string) {
_, err := db.Exec("DELETE FROM autojoinroles WHERE guild_id = $1 AND user_role = '' OR user_role IS NULL AND bot_role = '' OR bot_role IS NULL", guildID)
if err != nil {
logrus.Error(err)
}
}
func getAutoJoinRole(guildID string, isBot bool) string {
var isBotString string
var role string
if isBot {
isBotString = "bot"
} else {
isBotString = "user"
}
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM autojoinroles WHERE guild_id = $1)", guildID).Scan(&exists)
if err != nil {
logrus.Error(err)
return role
}
if exists {
err = db.QueryRow("SELECT "+isBotString+"_role FROM autojoinroles WHERE guild_id = $1", guildID).Scan(&role)
if err != nil {
logrus.Error(err, guildID)
}
}
return role
}

View File

@@ -0,0 +1,17 @@
package main
import (
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/snowflake/v2"
"github.com/sirupsen/logrus"
)
func guildMemberJoin(e *events.GuildMemberJoin) {
role := getAutoJoinRole(e.GuildID.String(), e.Member.User.Bot)
if role != "" {
err := e.Client().Rest().AddMemberRole(e.GuildID, e.Member.User.ID, snowflake.MustParse(role))
if err != nil {
logrus.Error(err)
}
}
}

View File

@@ -0,0 +1,127 @@
package main
import (
"database/sql"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/disgoorg/snowflake/v2"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/shared"
)
var db *sql.DB
var dbCreateQuery string = `
CREATE TABLE IF NOT EXISTS autojoinroles (
guild_id TEXT NOT NULL,
bot_role TEXT,
user_role TEXT,
PRIMARY KEY (guild_id)
);
`
var Plugin = &shared.Plugin{
Name: "Auto Join Roles",
Init: func(d *sql.DB) error {
db = d
_, err := d.Exec(dbCreateQuery)
if err != nil {
return err
}
shared.BotConfigs = append(shared.BotConfigs, bot.WithEventListenerFunc(guildMemberJoin))
return nil
},
Commands: []shared.Command{
{
Definition: discord.SlashCommandCreate{
Name: "autojoinroles",
Description: "Give users a role when they join",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageRoles),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionSubCommand{
Name: "bot",
Description: "Give bots a role when they join (Leave empty to remove current)",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionRole{
Name: "role",
Description: "The role bots should get when they join the server",
},
},
},
&discord.ApplicationCommandOptionSubCommand{
Name: "user",
Description: "Give users a role when they join (Leave empty to remove current)",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionRole{
Name: "role",
Description: "The role users should get when they join the server",
}},
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
var role string
option := *e.SlashCommandInteractionData().SubCommandName
var content string
if len(e.SlashCommandInteractionData().Options) == 1 {
var givenRole discord.Role = e.SlashCommandInteractionData().Role("role")
role = givenRole.ID.String()
botrole, err := getHighestRole(e.GuildID().String(), e.Client())
if err != nil {
logrus.Error(err)
}
if givenRole.Position >= botrole.Position {
content = "<@&" + role + "> is not below the Bot's current highest role(<@&" + botrole.ID.String() + ">). That makes it unable to manage it."
} else {
if setAutoJoinRole(e.GuildID().String(), option, role) {
content = "Updated auto join role for " + option + "s as <@&" + role + ">"
} else {
content = "Setup auto join role for " + option + "s as <@&" + role + ">"
}
}
} else if setAutoJoinRole(e.GuildID().String(), option, role) {
content = "Deleted auto join role for " + option + "s"
}
if content == "" {
content = "No auto join role set for " + option + "s to delete."
}
err := e.CreateMessage(discord.NewMessageCreateBuilder().SetContent(content).SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
purgeUnusedAutoJoinRoles(e.GuildID().String())
},
},
},
}
func getHighestRole(guildID string, c bot.Client) (*discord.Role, error) {
botmember, err := c.Rest().GetMember(snowflake.MustParse(guildID), c.ApplicationID())
if err != nil {
return nil, err
}
roles, err := c.Rest().GetRoles(snowflake.MustParse(guildID))
if err != nil {
return nil, err
}
var highestRole *discord.Role
for _, roleID := range botmember.RoleIDs {
for _, role := range roles {
if role.ID == roleID {
if highestRole == nil || role.Position > highestRole.Position {
highestRole = &role
}
break
}
}
}
return highestRole, nil
}

View File

@@ -0,0 +1,34 @@
package main
import (
"github.com/sirupsen/logrus"
)
func toggleAutoPublish(guildID string, newsChannelID string) bool {
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM autopublish WHERE guild_id = $1 AND news_channel_id = $2)", guildID, newsChannelID).Scan(&exists)
if err != nil {
logrus.Error(err)
}
if exists {
_, err := db.Exec("DELETE FROM autopublish WHERE guild_id = $1 AND news_channel_id = $2", guildID, newsChannelID)
if err != nil {
logrus.Error(err)
}
} else {
_, err := db.Exec("INSERT INTO autopublish (guild_id, news_channel_id) VALUES ($1, $2)", guildID, newsChannelID)
if err != nil {
logrus.Error(err)
}
}
return exists
}
func isAutopublishEnabled(guildID string, newsChannelID string) bool {
var enabled bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM autopublish WHERE guild_id = $1 AND news_channel_id = $2)", guildID, newsChannelID).Scan(&enabled)
if err != nil {
logrus.Error(err)
}
return enabled
}

View File

@@ -0,0 +1,23 @@
package main
import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/sirupsen/logrus"
)
func messageCreate(e *events.MessageCreate) {
channel, err := e.Client().Rest().GetChannel(e.Message.ChannelID)
if err != nil {
logrus.Error(err)
}
if channel.Type() == discord.ChannelTypeGuildNews {
if isAutopublishEnabled(e.GuildID.String(), e.ChannelID.String()) {
_, err := e.Client().Rest().CrosspostMessage(e.ChannelID, e.MessageID)
if err != nil {
logrus.Error(err)
return
}
}
}
}

View File

@@ -0,0 +1,76 @@
package main
import (
"database/sql"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/shared"
)
var db *sql.DB
var dbCreateQuery string = `
CREATE TABLE IF NOT EXISTS autopublish (
guild_id TEXT NOT NULL,
news_channel_id TEXT NOT NULL,
PRIMARY KEY (guild_id, news_channel_id)
);
`
var Plugin = &shared.Plugin{
Name: "Auto Publish",
Init: func(d *sql.DB) error {
db = d
_, err := d.Exec(dbCreateQuery)
if err != nil {
return err
}
shared.BotConfigs = append(shared.BotConfigs, bot.WithEventListenerFunc(messageCreate))
return nil
},
Commands: []shared.Command{
{
Definition: discord.SlashCommandCreate{
Name: "autopublish",
Description: "Toggle automatically publishing every post in a announcement channel",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageChannels),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
channel := e.Channel()
if channel.Type() == discord.ChannelTypeGuildNews {
if toggleAutoPublish(e.GuildID().String(), e.Channel().ID().String()) {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Autopublishing is now disabled on " + discord.ChannelMention(e.Channel().ID())).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Autopublishing is now enabled on " + discord.ChannelMention(e.Channel().ID())).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("This is not an announcement channel!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
},
},
},
}

View File

@@ -0,0 +1,93 @@
package main
import (
"log"
"github.com/sirupsen/logrus"
)
type BlockPoll struct {
ChannelID string
Global bool
AllowedRole string
}
func isGlobalBlockPolls(guildID string) bool {
var globalexists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM blockpolls WHERE guild_id = $1 AND global = true)", guildID).Scan(&globalexists)
if err != nil {
logrus.Error(err)
}
return globalexists
}
func toggleBlockPolls(guildID string, channelID string, global bool, allowedRole string) (e bool, isGlobal bool) {
globalexists := isGlobalBlockPolls(guildID)
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM blockpolls WHERE guild_id = $1 AND channel_id = $2)", guildID, channelID).Scan(&exists)
if err != nil {
logrus.Error(err)
}
if globalexists {
_, err := db.Exec("DELETE FROM blockpolls WHERE guild_id = $1 AND global = true", guildID)
if err != nil {
logrus.Error(err)
}
return true, true
} else if global {
_, err = db.Exec("DELETE FROM blockpolls WHERE guild_id = $1", guildID)
if err != nil {
logrus.Error(err)
}
_, err := db.Exec("INSERT INTO blockpolls (guild_id, global, channel_id, allowed_role) VALUES ($1, true, $2, $3)", guildID, channelID, allowedRole)
if err != nil {
logrus.Error(err)
}
return false, true
} else if exists && !globalexists {
_, err := db.Exec("DELETE FROM blockpolls WHERE guild_id = $1 AND channel_id = $2", guildID, channelID)
if err != nil {
logrus.Error(err)
}
return true, false
} else if !globalexists {
_, err := db.Exec("INSERT INTO blockpolls (guild_id, channel_id, allowed_role) VALUES ($1, $2, $3)", guildID, channelID, allowedRole)
if err != nil {
logrus.Error(err)
}
return false, false
} else {
return false, false
}
}
func listBlockPolls(guildID string) []BlockPoll {
var list []BlockPoll
rows, err := db.Query("SELECT channel_id, global, allowed_role FROM blockpolls WHERE guild_id = $1", guildID)
if err != nil {
log.Fatal(err)
}
for rows.Next() {
var bp BlockPoll
err := rows.Scan(&bp.ChannelID, &bp.Global, &bp.AllowedRole)
if err != nil {
log.Fatal(err)
}
list = append(list, bp)
}
return list
}
func getBlockPollsEnabled(guildID string, channelID string) (isEnabled bool, allowedRole string) {
var enabled bool
var v_allowedRole string
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM blockpolls WHERE guild_id = $1 AND channel_id = $2)", guildID, channelID).Scan(&enabled)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT allowed_role FROM blockpolls WHERE guild_id = $1 AND channel_id = $2", guildID, channelID).Scan(&v_allowedRole)
if err != nil && err.Error() != "sql: no rows in result set" {
logrus.Error(err)
}
return enabled, v_allowedRole
}

View File

@@ -0,0 +1,27 @@
package main
import (
"slices"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/snowflake/v2"
"github.com/sirupsen/logrus"
)
func messageCreate(e *events.MessageCreate) {
channel, err := e.Client().Rest().GetChannel(e.Message.ChannelID)
if err != nil {
logrus.Error(err)
}
if channel != nil {
isBlockPollsEnabledGlobal := isGlobalBlockPolls(e.GuildID.String())
isBlockPollsEnabled, allowedRole := getBlockPollsEnabled(e.GuildID.String(), e.Message.ChannelID.String())
var hasAllowedRole bool
if allowedRole != "" {
hasAllowedRole = slices.Contains(e.Message.Member.RoleIDs, snowflake.MustParse(allowedRole))
}
if (isBlockPollsEnabledGlobal || isBlockPollsEnabled) && !hasAllowedRole && messageIsPoll(e.Message.ChannelID.String(), e.Message.ID.String(), e.Client()) {
e.Client().Rest().DeleteMessage(e.Message.ChannelID, e.Message.ID)
}
}
}

View File

@@ -0,0 +1,156 @@
package main
import (
"database/sql"
"io"
"net/http"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/disgo/rest"
"github.com/disgoorg/json"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/shared"
)
var db *sql.DB
var dbCreateQuery string = `
CREATE TABLE IF NOT EXISTS blockpolls (
guild_id TEXT NOT NULL,
channel_id TEXT,
global BOOLEAN,
allowed_role TEXT,
PRIMARY KEY (guild_id)
)
`
var Plugin = &shared.Plugin{
Name: "Block Polls",
Init: func(d *sql.DB) error {
db = d
_, err := d.Exec(dbCreateQuery)
if err != nil {
return err
}
shared.BotConfigs = append(shared.BotConfigs, bot.WithEventListenerFunc(messageCreate))
return nil
},
Commands: []shared.Command{
{
Definition: discord.SlashCommandCreate{
Name: "block-polls",
Description: "Block polls from beeing posted in this channel.",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageChannels),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionSubCommand{
Name: "toggle",
Description: "Toggle blocking polls from beeing posted in this channel.",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionBool{
Name: "global",
Description: "If polls are blocked server wide or only in the current channel.",
},
&discord.ApplicationCommandOptionRole{
Name: "allowed-role",
Description: "The role that bypasses this block role.",
},
},
},
/*&discord.ApplicationCommandOptionSubCommand{
Name: "list",
Description: "List the current block polls rules for this server.",
},*/
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
switch *e.SlashCommandInteractionData().SubCommandName {
case "toggle":
isGlobal := isGlobalBlockPolls(e.GuildID().String())
if isGlobal && !e.SlashCommandInteractionData().Bool("global") {
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Polls are currently globally blocked. Disable global blocking to enable channel specific blocking.").SetEphemeral(true).Build())
} else {
exists, isGlobal := toggleBlockPolls(e.GuildID().String(), e.Channel().ID().String(), e.SlashCommandInteractionData().Bool("global"), e.SlashCommandInteractionData().Role("allowed-role").ID.String())
if exists {
if e.SlashCommandInteractionData().Bool("global") {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Polls are now globally unblocked.").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Polls are now unblocked in " + discord.ChannelMention(e.Channel().ID())).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
} else {
if isGlobal {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Polls are now globally blocked.").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Polls are now blocked in " + discord.ChannelMention(e.Channel().ID())).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
}
}
/*case "list":
list := listBlockPolls(e.GuildID().String())*/
}
},
},
},
}
func messageIsPoll(channelID string, messageID string, client bot.Client) bool {
url := rest.DefaultConfig().URL + "/channels/" + channelID + "/messages/" + messageID
req, err := http.NewRequest("GET", url, nil)
if err != nil {
logrus.Error(err)
return false
}
auth := "Bot " + client.Token()
req.Header.Set("Authorization", auth)
resp, err := client.Rest().HTTPClient().Do(req)
if err != nil {
logrus.Error(err)
return false
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
logrus.Error(err)
return false
}
var data map[string]interface{}
err = json.Unmarshal(body, &data)
if err != nil {
logrus.Error(err)
return false
}
_, ok := data["poll"]
return ok
}

154
plugin_src/forms/data.go Normal file
View File

@@ -0,0 +1,154 @@
package main
import (
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)
type FormResult struct {
OverwriteTitle string
ResultChannelID string
AcceptChannelID string
CommentCategoryID string
ModeratorID string
}
type MessageIDs struct {
ID string
ChannelID string
}
func getFormManageIdExists(id uuid.UUID) bool {
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM form_manage WHERE form_manage_id = $1)", id).Scan(&exists)
if err != nil {
logrus.Error(err)
}
return exists
}
func addFormButton(guildID string, channelID string, messageID string, formManageID string, formType string, resultChannelID string, overwriteTitle string, acceptChannelID string, commentCategory string, moderator_id string) {
_, err := db.Exec(
`INSERT INTO form_manage (
guild_id,
form_manage_id,
channel_id,
message_id,
form_type,
result_channel_id,
overwrite_title,
accept_channel_id,
comment_category,
moderator_id) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`,
guildID, formManageID, channelID, messageID, formType, resultChannelID, overwriteTitle, acceptChannelID, commentCategory, moderator_id)
if err != nil {
logrus.Error(err)
}
}
func getFormManageIDs() []string {
if db == nil {
return nil
}
var IDs []string
rows, err := db.Query("SELECT form_manage_id FROM form_manage")
if err != nil {
logrus.Error(err)
return nil
}
defer rows.Close()
for rows.Next() {
var id string
if err := rows.Scan(&id); err != nil {
logrus.Error(err)
return nil
}
IDs = append(IDs, id)
}
if err := rows.Err(); err != nil {
logrus.Error(err)
return nil
}
return IDs
}
func getFormType(formManageID string) string {
var formType string
err := db.QueryRow("SELECT form_type FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&formType)
if err != nil {
logrus.Error(err)
}
return formType
}
func getFormResultValues(formManageID string) FormResult {
var result FormResult
err := db.QueryRow("SELECT overwrite_title FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.OverwriteTitle)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT result_channel_id FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.ResultChannelID)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT accept_channel_id FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.AcceptChannelID)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT comment_category FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.CommentCategoryID)
if err != nil {
logrus.Error(err)
}
err = db.QueryRow("SELECT moderator_id FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&result.ModeratorID)
if err != nil {
logrus.Error(err)
}
return result
}
func getFormOverwriteTitle(formManageID string) string {
var overwriteTitle string
err := db.QueryRow("SELECT overwrite_title FROM form_manage WHERE form_manage_id = $1", formManageID).Scan(&overwriteTitle)
if err != nil {
logrus.Error(err)
}
return overwriteTitle
}
func updateFormCommentCategory(formManageID string, comment_category string) {
_, err := db.Exec("UPDATE form_manage SET comment_category = $1 WHERE form_manage_id = $2", comment_category, formManageID)
if err != nil {
logrus.Error(err)
}
}
func tryDeleteUnusedMessage(messageID string) {
_, err := db.Exec("DELETE FROM form_manage WHERE message_id = $1", messageID)
if err != nil {
logrus.Error(err)
}
}
func getAllSavedMessages() []MessageIDs {
var savedMessages []MessageIDs
rows, err := db.Query("SELECT message_id, channel_id FROM form_manage")
if err != nil {
logrus.Error(err)
return nil
}
defer rows.Close()
for rows.Next() {
var messageID, channelID string
if err := rows.Scan(&messageID, &channelID); err != nil {
logrus.Error(err)
continue
}
savedMessages = append(savedMessages, MessageIDs{ID: messageID, ChannelID: channelID})
}
if err := rows.Err(); err != nil {
logrus.Error(err)
}
return savedMessages
}

515
plugin_src/forms/main.go Normal file
View File

@@ -0,0 +1,515 @@
package main
import (
"bytes"
"database/sql"
"fmt"
"strings"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/disgoorg/snowflake/v2"
"github.com/google/uuid"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/custom"
"github.com/vaporvee/acecore/shared"
)
var db *sql.DB
var dbCreateQuery string = `
CREATE TABLE IF NOT EXISTS custom_forms (
form_type TEXT NOT NULL,
title TEXT NOT NULL,
json JSON NOT NULL,
guild_id TEXT NOT NULL,
PRIMARY KEY (form_type, guild_id)
);
CREATE TABLE IF NOT EXISTS form_manage (
form_manage_id TEXT NOT NULL,
form_type TEXT NOT NULL,
overwrite_title TEXT,
message_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
guild_id TEXT NOT NULL,
result_channel_id TEXT NOT NULL,
accept_channel_id TEXT,
comment_category TEXT,
moderator_id TEXT,
PRIMARY KEY (form_manage_id, form_type)
);
`
var Plugin = &shared.Plugin{
Name: "Forms",
Init: func(d *sql.DB) error {
db = d
_, err := d.Exec(dbCreateQuery)
if err != nil {
return err
}
shared.BotConfigs = append(shared.BotConfigs,
bot.WithEventListenerFunc(func(e *events.MessageCreate) { tryDeleteUnusedMessage(e.MessageID.String()) }),
bot.WithEventListenerFunc(func(e *events.Ready) { findAndDeleteUnusedMessages(e.Client()) }))
return nil
},
Commands: []shared.Command{
{
Definition: discord.SlashCommandCreate{
Name: "form",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageChannels),
Description: "Create custom forms right inside Discord",
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionSubCommand{
Name: "help",
Description: "Gives you an example file and demo for creating custom forms",
},
&discord.ApplicationCommandOptionSubCommand{
Name: "custom",
Description: "Create a new custom form right inside Discord",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionAttachment{
Name: "json",
Description: "Your edited form file",
Required: true,
},
},
},
&discord.ApplicationCommandOptionSubCommand{
Name: "add",
Description: "Adds existing forms to this channel",
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionChannel{
Name: "result_channel",
Description: "Where the form results should appear",
ChannelTypes: []discord.ChannelType{discord.ChannelTypeGuildText},
},
&discord.ApplicationCommandOptionMentionable{
Name: "moderator",
Description: "Who can interact with moderating buttons.",
},
&discord.ApplicationCommandOptionString{
Name: "type",
Description: "Which type of form you want to add",
Autocomplete: true,
},
&discord.ApplicationCommandOptionString{
Name: "title",
Description: "The title the form should have",
},
&discord.ApplicationCommandOptionChannel{
Name: "approve_channel",
Description: "Channel for results that need to be accepted by a moderator before sending it to the result channel",
ChannelTypes: []discord.ChannelType{discord.ChannelTypeGuildText},
},
&discord.ApplicationCommandOptionBool{
Name: "mods_can_answer",
Description: "Moderators can open a new channel on the form result, which then pings the user who submitted it",
},
},
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
switch *e.SlashCommandInteractionData().SubCommandName {
case "help":
fileData, err := shared.FormTemplates.ReadFile("form_templates/form_demo.json")
if err != nil {
logrus.Error(err)
return
}
fileReader := bytes.NewReader(fileData)
err = e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("NOT SUPPORTED YET!(use `/form add` instead)\n\nGet the example file edit it (make sure to have a unique \"form_type\") and submit it via `/form create`.\nOr use the demo button to get an idea of how the example would look like.").
SetFiles(discord.NewFile("example.json", "json", fileReader)).
SetContainerComponents(discord.ActionRowComponent{discord.NewPrimaryButton("Demo", "form_demo").WithEmoji(discord.ComponentEmoji{Name: "📑"})}).SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
case "custom":
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Feature not available yet use `/form add` instead").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
case "add":
var title, formID, overwriteTitle, acceptChannelID string
var modsCanAnswer bool
var resultChannelID string
data := e.SlashCommandInteractionData()
if data.Channel("result_channel").ID.String() != "0" {
resultChannelID = data.Channel("result_channel").ID.String()
}
moderator := data.Role("moderator").ID.String()
if moderator == "0" {
moderator = e.User().ID.String()
}
formID = data.String("type")
overwriteTitle = data.String("title")
if overwriteTitle != "" {
title = overwriteTitle
}
if data.Channel("approve_channel").ID.String() != "0" {
acceptChannelID = data.Channel("approve_channel").ID.String()
}
modsCanAnswer = data.Bool("mods_can_answer")
if formID == "" {
formID = "template_general"
}
if title == "" {
formTitles := map[string]string{
"template_ticket": "Make a new ticket",
"template_url": "Add your URL",
"template_general": "Form",
}
if val, ok := formTitles[formID]; ok {
title = val
}
}
var exists bool = true
var formManageID uuid.UUID = uuid.New()
for exists {
formManageID = uuid.New()
exists = getFormManageIdExists(formManageID)
}
messagebuild := discord.NewMessageCreateBuilder().SetEmbeds(discord.NewEmbedBuilder().
SetTitle(title).SetDescription("Press the bottom button to open a form popup.").SetColor(custom.GetColor("primary")).
Build()).SetContainerComponents(discord.ActionRowComponent{
discord.NewSuccessButton("Submit", "form:"+formManageID.String()).WithEmoji(discord.ComponentEmoji{
Name: "anim_rocket",
ID: snowflake.MustParse("1215740398706757743"),
Animated: true,
})}).
Build()
message, err := e.Client().Rest().CreateMessage(e.Channel().ID(), messagebuild)
if err != nil {
logrus.Error(err)
}
var category string
if modsCanAnswer {
c, err := e.Client().Rest().CreateGuildChannel(*e.GuildID(), discord.GuildCategoryChannelCreate{Name: title + " mod answers"})
if err != nil {
logrus.Error(err)
}
category = c.ID().String()
}
addFormButton(e.GuildID().String(), e.Channel().ID().String(), message.ID.String(), formManageID.String(), formID, resultChannelID, overwriteTitle, acceptChannelID, category, moderator)
err = e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Successfully added form button!").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
}
},
DynamicComponentIDs: func() []string { return getFormButtonIDs() },
DynamicModalIDs: func() []string { return getFormButtonIDs() },
ComponentInteract: func(e *events.ComponentInteractionCreate) {
if e.Data.Type() == discord.ComponentTypeButton {
if strings.ContainsAny(e.ButtonInteractionData().CustomID(), ";") {
var form_manage_id string = strings.TrimPrefix(strings.Split(e.ButtonInteractionData().CustomID(), ";")[0], "form:")
switch strings.Split(e.ButtonInteractionData().CustomID(), ";")[1] {
case "decline":
err := e.Client().Rest().DeleteMessage(e.Channel().ID(), e.Message.ID)
if err != nil {
logrus.Error(err)
}
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Submission declined!").SetEphemeral(true).Build())
case "approve":
embed := e.Message.Embeds[0]
embed.Description = fmt.Sprintf("This submission was approved by <@%s>.", e.User().ID)
_, err := e.Client().Rest().CreateMessage(snowflake.MustParse(getFormResultValues(form_manage_id).ResultChannelID), discord.NewMessageCreateBuilder().
SetEmbeds(embed).
Build())
if err != nil {
logrus.Error(err)
}
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Submission accepted!").SetEphemeral(true).Build())
err = e.Client().Rest().DeleteMessage(e.Channel().ID(), e.Message.ID)
if err != nil {
logrus.Error(err)
}
case "comment":
author := strings.TrimSuffix(strings.Split(e.Message.Embeds[0].Fields[len(e.Message.Embeds[0].Fields)-1].Value, "<@")[1], ">")
embed := e.Message.Embeds[0]
moderator := e.User().ID
channel := createFormComment(form_manage_id, snowflake.MustParse(author), moderator, "answer", embed, *e.GuildID(), e.Client())
e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Created channel " + discord.ChannelMention(channel.ID())).SetEphemeral(true).Build())
}
} else {
if strings.HasPrefix(e.ButtonInteractionData().CustomID(), "form:") {
var formManageID string = strings.TrimPrefix(e.ButtonInteractionData().CustomID(), "form:")
e.Modal(shared.JsonStringBuildModal(e.User().ID.String(), formManageID, getFormType(formManageID), getFormOverwriteTitle(formManageID)))
} else if e.ButtonInteractionData().CustomID() == "form_demo" {
e.Modal(shared.JsonStringBuildModal(e.User().ID.String(), "form_demo", "form_demo"))
}
}
}
},
ModalSubmit: func(e *events.ModalSubmitInteractionCreate) {
if !strings.HasPrefix(e.Data.CustomID, "form_demo") {
var form_manage_id string = strings.Split(e.Data.CustomID, ":")[1]
var result FormResult = getFormResultValues(form_manage_id)
var fields []discord.EmbedField
var modal shared.ModalJson = shared.GetModalByFormID(getFormType(form_manage_id))
var overwrite_title string = getFormOverwriteTitle(form_manage_id)
if overwrite_title != "" {
modal.Title = overwrite_title
}
var inline bool
var index int = 0
for _, component := range e.Data.Components {
var input discord.TextInputComponent = component.(discord.TextInputComponent)
inline = input.Style == discord.TextInputStyleShort
fields = append(fields, discord.EmbedField{
Name: modal.Form[index].Label,
Value: input.Value,
Inline: &inline,
})
index++
}
fields = append(fields, discord.EmbedField{
Value: "From <#" + e.Channel().ID().String() + "> by " + e.User().Mention(),
})
if result.ResultChannelID == "" {
if result.CommentCategoryID != "" {
channel := createFormComment(form_manage_id, e.User().ID, snowflake.MustParse(result.ModeratorID), "answer", discord.NewEmbedBuilder().
SetAuthorName(*e.User().GlobalName).SetAuthorIcon(*e.User().AvatarURL()).SetTitle("\""+modal.Title+"\"").SetDescription("This is the submitted result").
SetColor(custom.GetColor("primary")).SetFields(fields...).
Build(), *e.GuildID(), e.Client())
err := e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Created channel " + discord.ChannelMention(channel.ID())).SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
} else {
e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("You need to provide either a `result_channel` or enable `mods_can_answer` to create a valid form.").SetEphemeral(true).
Build())
}
} else {
if result.AcceptChannelID == "" {
_, err := e.Client().Rest().CreateMessage(snowflake.MustParse(result.ResultChannelID), discord.NewMessageCreateBuilder().
SetEmbeds(discord.NewEmbedBuilder().
SetAuthorName(*e.User().GlobalName).SetAuthorIcon(*e.User().AvatarURL()).SetTitle("\""+modal.Title+"\"").SetDescription("This is the submitted result").
SetColor(custom.GetColor("primary")).SetFields(fields...).
Build()).
SetContainerComponents(discord.NewActionRow(discord.
NewButton(discord.ButtonStylePrimary, "Comment", "form:"+form_manage_id+";comment", "").
WithEmoji(discord.ComponentEmoji{Name: "👥"}))).
Build())
if err != nil {
logrus.Error(err)
} else {
err = e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Submitted!").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
}
} else {
var buttons []discord.InteractiveComponent
if result.CommentCategoryID != "" {
buttons = []discord.InteractiveComponent{discord.
NewButton(discord.ButtonStylePrimary, "Comment", "form:"+form_manage_id+";comment", "").
WithEmoji(discord.ComponentEmoji{Name: "👥"})}
}
buttons = append(buttons, discord.
NewButton(discord.ButtonStyleDanger, "Decline", "form:"+form_manage_id+";decline", "").
WithEmoji(discord.ComponentEmoji{Name: "🛑"}),
discord.
NewButton(discord.ButtonStyleSuccess, "Approve", "form:"+form_manage_id+";approve", "").
WithEmoji(discord.ComponentEmoji{Name: "🎉"}))
_, err := e.Client().Rest().CreateMessage(snowflake.MustParse(result.AcceptChannelID), discord.NewMessageCreateBuilder().
SetEmbeds(discord.NewEmbedBuilder().
SetAuthorName(*e.User().GlobalName).SetAuthorIcon(*e.User().AvatarURL()).SetTitle("\""+modal.Title+"\"").SetDescription("**This submission needs approval.**").
SetColor(custom.GetColor("primary")).SetFields(fields...).
Build()).
SetContainerComponents(discord.NewActionRow(buttons...)).
Build())
if err != nil {
logrus.Error(err)
} else {
err = e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Submitted!").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
}
}
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("The results would be submited...").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
}
},
Autocomplete: func(e *events.AutocompleteInteractionCreate) {
err := e.AutocompleteResult([]discord.AutocompleteChoice{
&discord.AutocompleteChoiceString{
Name: "Support Ticket",
Value: "template_ticket",
},
&discord.AutocompleteChoiceString{
Name: "Submit URL",
Value: "template_url",
},
&discord.AutocompleteChoiceString{
Name: "General",
Value: "template_general",
},
})
if err != nil {
logrus.Error(err)
}
},
},
{
Definition: discord.SlashCommandCreate{
Name: "ticket",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageChannels),
Description: "A quick command to create Ticketpanels. (/form for more)",
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionString{
Name: "title",
Description: "The title the ticket should have",
},
&discord.ApplicationCommandOptionMentionable{
Name: "moderator",
Description: "Who can interact with moderating buttons.",
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
var title string = "Ticket"
var moderator string
data := e.SlashCommandInteractionData()
if data.String("title") != "" {
title = data.String("title")
}
moderator = data.Role("moderator").ID.String()
if moderator == "" {
moderator = data.User("moderator").ID.String()
}
var exists bool = true
var formManageID uuid.UUID = uuid.New()
for exists {
formManageID = uuid.New()
exists = getFormManageIdExists(formManageID)
}
messagebuild := discord.NewMessageCreateBuilder().SetEmbeds(discord.NewEmbedBuilder().
SetTitle(title).SetDescription("Press the bottom button to open a form popup.").SetColor(custom.GetColor("primary")).
Build()).SetContainerComponents(discord.ActionRowComponent{
discord.NewSuccessButton("Submit", "form:"+formManageID.String()).WithEmoji(discord.ComponentEmoji{
Name: "anim_rocket",
ID: snowflake.MustParse("1215740398706757743"),
Animated: true,
})}).
Build()
message, err := e.Client().Rest().CreateMessage(e.Channel().ID(), messagebuild)
if err != nil {
logrus.Error(err)
return
}
if title == "" {
title = "Ticket"
}
var category string
c, err := e.Client().Rest().CreateGuildChannel(*e.GuildID(), discord.GuildCategoryChannelCreate{Name: title + " mod answers"})
if err != nil {
logrus.Error(err)
}
category = c.ID().String()
if title == "Ticket" {
title = ""
}
addFormButton(e.GuildID().String(), e.Channel().ID().String(), message.ID.String(), formManageID.String(), "template_ticket", "", title, "", category, moderator)
err = e.CreateMessage(discord.NewMessageCreateBuilder().SetContent("Successfully added ticket panel!\n(`/form` for more options or custom ticket forms.)").SetEphemeral(true).Build())
if err != nil {
logrus.Error(err)
}
},
},
},
}
// moderator can be userID as well as roleID
func createFormComment(form_manage_id string, author snowflake.ID, moderator snowflake.ID, commentName string, embed discord.Embed, guildID snowflake.ID, client bot.Client) discord.Channel {
var category snowflake.ID = snowflake.MustParse(getFormResultValues(form_manage_id).CommentCategoryID)
_, err := client.Rest().GetChannel(category)
if err != nil {
c, err := client.Rest().CreateGuildChannel(guildID, discord.GuildCategoryChannelCreate{Name: strings.Trim(embed.Title, "\"") + " mod " + commentName + "s"})
if err != nil {
logrus.Error(err)
}
category = c.ID()
updateFormCommentCategory(form_manage_id, category.String())
}
ch, err := client.Rest().CreateGuildChannel(guildID, discord.GuildTextChannelCreate{
ParentID: category,
Name: strings.ToLower(embed.Author.Name) + "-" + commentName,
})
if err != nil {
logrus.Error(err)
}
var permissionOverwrites []discord.PermissionOverwrite = []discord.PermissionOverwrite{
discord.RolePermissionOverwrite{
RoleID: guildID,
Deny: discord.PermissionViewChannel,
}}
if shared.IsIDRole(client, guildID, moderator) {
permissionOverwrites = append(permissionOverwrites, discord.RolePermissionOverwrite{
RoleID: moderator,
Allow: discord.PermissionViewChannel,
})
} else {
permissionOverwrites = append(permissionOverwrites, discord.MemberPermissionOverwrite{
UserID: moderator,
Allow: discord.PermissionViewChannel,
})
}
permissionOverwrites = append(permissionOverwrites, discord.RolePermissionOverwrite{
RoleID: author,
Allow: discord.PermissionViewChannel,
})
_, err = client.Rest().UpdateChannel(ch.ID(), discord.GuildTextChannelUpdate{PermissionOverwrites: &permissionOverwrites})
if err != nil {
logrus.Error(err)
}
modTypeChar := ""
if shared.IsIDRole(client, guildID, moderator) {
modTypeChar = "&"
}
embed.Description = "This was submitted"
_, err = client.Rest().CreateMessage(ch.ID(), discord.NewMessageCreateBuilder().
SetContent("<@"+modTypeChar+moderator.String()+"> <@"+author.String()+">").SetEmbeds(embed).
Build())
if err != nil {
logrus.Error(err)
}
return ch
}
func getFormButtonIDs() []string {
var IDs []string = []string{"form_demo"}
var formButtonIDs []string = getFormManageIDs()
for _, buttonID := range formButtonIDs {
IDs = append(IDs, "form:"+buttonID)
}
return IDs
}

15
plugin_src/forms/tool.go Normal file
View File

@@ -0,0 +1,15 @@
package main
import (
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/snowflake/v2"
)
func findAndDeleteUnusedMessages(c bot.Client) {
for _, message := range getAllSavedMessages() {
_, err := c.Rest().GetMessage(snowflake.MustParse(message.ChannelID), snowflake.MustParse(message.ID))
if err != nil {
tryDeleteUnusedMessage(message.ID)
}
}
}

View File

@@ -3,12 +3,12 @@ package main
import ( import (
"github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/events"
"github.com/vaporvee/acecore/cmd" "github.com/vaporvee/acecore/shared"
) )
var Plugin = &cmd.Plugin{ var Plugin = &shared.Plugin{
Name: "Info", Name: "Info",
Commands: []cmd.Command{ Commands: []shared.Command{
{ {
Definition: discord.SlashCommandCreate{ Definition: discord.SlashCommandCreate{
Name: "info", Name: "info",

View File

@@ -4,11 +4,11 @@ import (
"github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/events"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
"github.com/vaporvee/acecore/custom" "github.com/vaporvee/acecore/custom"
"github.com/vaporvee/acecore/shared"
) )
var cmd_ask = cmd.Command{ var cmd_ask = shared.Command{
Definition: discord.SlashCommandCreate{ Definition: discord.SlashCommandCreate{
Name: "ask", Name: "ask",
Description: "Ask anything and get a gif as response!", Description: "Ask anything and get a gif as response!",

View File

@@ -8,11 +8,11 @@ import (
"github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/events"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd"
"github.com/vaporvee/acecore/custom" "github.com/vaporvee/acecore/custom"
"github.com/vaporvee/acecore/shared"
) )
var cmd_cat = cmd.Command{ var cmd_cat = shared.Command{
Definition: discord.SlashCommandCreate{ Definition: discord.SlashCommandCreate{
Name: "cat", Name: "cat",
Description: "Random cat pictures", Description: "Random cat pictures",

View File

@@ -4,10 +4,10 @@ import (
"github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/events"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/cmd" "github.com/vaporvee/acecore/shared"
) )
var cmd_dadjoke = cmd.Command{ var cmd_dadjoke = shared.Command{
Definition: discord.SlashCommandCreate{ Definition: discord.SlashCommandCreate{
Name: "dadjoke", Name: "dadjoke",
Description: "Gives you a random joke that is as bad as your dad would tell them", Description: "Gives you a random joke that is as bad as your dad would tell them",

View File

@@ -1,20 +0,0 @@
module github.com/vaporvee/acecore/simplefun
go 1.22.1
require (
github.com/disgoorg/disgo v0.18.2
github.com/sirupsen/logrus v1.9.3
github.com/vaporvee/acecore v0.0.0-20240414202630-d814d39cf135
github.com/vaporvee/acecore/cmd v0.0.0-20240414202630-d814d39cf135
)
require (
github.com/disgoorg/json v1.1.0 // indirect
github.com/disgoorg/snowflake/v2 v2.0.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect
golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect
)

View File

@@ -1,10 +1,40 @@
package main package main
import ( import (
"github.com/vaporvee/acecore/cmd" "encoding/json"
"io"
"net/http"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/shared"
) )
var Plugin = &cmd.Plugin{ var Plugin = &shared.Plugin{
Name: "Simple Fun", Name: "Simple Fun",
Commands: []cmd.Command{cmd_ask, cmd_cat, cmd_dadjoke}, Commands: []shared.Command{cmd_ask, cmd_cat, cmd_dadjoke},
}
func simpleGetFromAPI(key string, url string) interface{} {
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
logrus.Error("Error creating request:", err)
}
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
logrus.Error("Error making request:", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
logrus.Error("Error reading response body:", err)
}
var result map[string]interface{}
err = json.Unmarshal(body, &result)
if err != nil {
logrus.Error("Error decoding JSON:", err)
}
return result[key]
} }

View File

@@ -1,34 +0,0 @@
package main
import (
"encoding/json"
"io"
"net/http"
"github.com/sirupsen/logrus"
)
func simpleGetFromAPI(key string, url string) interface{} {
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
logrus.Error("Error creating request:", err)
}
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
logrus.Error("Error making request:", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
logrus.Error("Error reading response body:", err)
}
var result map[string]interface{}
err = json.Unmarshal(body, &result)
if err != nil {
logrus.Error("Error decoding JSON:", err)
}
return result[key]
}

View File

@@ -0,0 +1,62 @@
package main
import (
"github.com/sirupsen/logrus"
)
func addSticky(guildID string, channelID string, messageContent string, messageID string) {
_, err := db.Exec("INSERT INTO sticky (guild_id, channel_id, message_id, message_content) VALUES ($1, $2, $3, $4)", guildID, channelID, messageID, messageContent)
if err != nil {
logrus.Error(err)
}
}
func hasSticky(guildID string, channelID string) bool {
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM sticky WHERE guild_id = $1 AND channel_id = $2)", guildID, channelID).Scan(&exists)
if err != nil {
logrus.Error(err)
}
return exists
}
func getStickyMessageID(guildID string, channelID string) string {
var messageID string
exists := hasSticky(guildID, channelID)
if exists {
err := db.QueryRow("SELECT message_id FROM sticky WHERE guild_id = $1 AND channel_id = $2", guildID, channelID).Scan(&messageID)
if err != nil {
logrus.Error(err)
}
}
return messageID
}
func getStickyMessageContent(guildID string, channelID string) string {
var messageID string
exists := hasSticky(guildID, channelID)
if exists {
err := db.QueryRow("SELECT message_content FROM sticky WHERE guild_id = $1 AND channel_id = $2", guildID, channelID).Scan(&messageID)
if err != nil {
logrus.Error(err)
}
}
return messageID
}
func updateStickyMessageID(guildID string, channelID string, messageID string) {
exists := hasSticky(guildID, channelID)
if exists {
_, err := db.Exec("UPDATE sticky SET message_id = $1 WHERE guild_id = $2 AND channel_id = $3", messageID, guildID, channelID)
if err != nil {
logrus.Error(err)
}
}
}
func removeSticky(guildID string, channelID string) {
_, err := db.Exec("DELETE FROM sticky WHERE guild_id = $1 AND channel_id = $2", guildID, channelID)
if err != nil {
logrus.Error(err)
}
}

View File

@@ -0,0 +1,33 @@
package main
import (
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/snowflake/v2"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/custom"
)
func messageCreate(e *events.MessageCreate) {
if len(e.Message.Embeds) == 0 || e.Message.Embeds[0].Footer == nil || e.Message.Embeds[0].Footer.Text != "📌 Sticky message" {
if hasSticky(e.Message.GuildID.String(), e.Message.ChannelID.String()) {
stickymessageID := getStickyMessageID(e.Message.GuildID.String(), e.Message.ChannelID.String())
err := e.Client().Rest().DeleteMessage(e.ChannelID, snowflake.MustParse(stickymessageID))
stickyMessage, _ := e.Client().Rest().CreateMessage(e.ChannelID, discord.MessageCreate{
Embeds: []discord.Embed{
{
Footer: &discord.EmbedFooter{
Text: "📌 Sticky message",
},
Color: custom.GetColor("primary"),
Description: getStickyMessageContent(e.Message.GuildID.String(), e.Message.ChannelID.String()),
},
},
})
if err != nil {
logrus.Error(err)
}
updateStickyMessageID(e.Message.GuildID.String(), e.Message.ChannelID.String(), stickyMessage.ID.String())
}
}
}

View File

@@ -0,0 +1,145 @@
package main
import (
"database/sql"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/disgoorg/snowflake/v2"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/custom"
"github.com/vaporvee/acecore/shared"
)
var db *sql.DB
var dbCreateQuery string = `CREATE TABLE IF NOT EXISTS sticky (
message_id TEXT NOT NULL,
channel_id TEXT NOT NULL,
message_content TEXT NOT NULL,
guild_id TEXT NOT NULL,
PRIMARY KEY (channel_id, guild_id)
);
`
var Plugin = &shared.Plugin{
Name: "Sticky Messages",
Init: func(d *sql.DB) error {
db = d
_, err := d.Exec(dbCreateQuery)
if err != nil {
return err
}
shared.BotConfigs = append(shared.BotConfigs, bot.WithEventListenerFunc(messageCreate))
return nil
},
Commands: []shared.Command{
{
Definition: discord.SlashCommandCreate{
Name: "sticky",
Description: "Stick or unstick messages to the bottom of the current channel",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageMessages),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
&discord.ApplicationCommandOptionString{
Name: "message",
Description: "The message you want to stick to the bottom of this channel",
Required: false,
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
if len(e.SlashCommandInteractionData().Options) == 0 {
if hasSticky(e.GuildID().String(), e.Channel().ID().String()) {
err := e.Client().Rest().DeleteMessage(e.Channel().ID(), snowflake.MustParse(getStickyMessageID(e.GuildID().String(), e.Channel().ID().String())))
if err != nil {
logrus.Error(err)
}
removeSticky(e.GuildID().String(), e.Channel().ID().String())
err = e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("The sticky message was removed from this channel!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("This channel has no sticky message!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
} else {
inputStickyMessage(e)
}
},
},
{
Definition: discord.MessageCommandCreate{
Name: "Stick to channel",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageMessages),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
inputStickyMessage(e)
},
},
},
}
func inputStickyMessage(e *events.ApplicationCommandInteractionCreate) {
var messageText string
if e.ApplicationCommandInteraction.Data.Type() == discord.ApplicationCommandTypeMessage {
messageText = e.MessageCommandInteractionData().TargetMessage().Content //TODO add more data then just content
} else {
messageText = e.SlashCommandInteractionData().String("message")
}
if messageText == "" {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Can't add empty sticky messages!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
message, err := e.Client().Rest().CreateMessage(e.Channel().ID(), discord.MessageCreate{Embeds: []discord.Embed{
{Description: messageText, Footer: &discord.EmbedFooter{Text: "📌 Sticky message"}, Color: custom.GetColor("primary")}}})
if err != nil {
logrus.Error(err)
}
if hasSticky(e.GuildID().String(), e.Channel().ID().String()) {
err = e.Client().Rest().DeleteMessage(e.Channel().ID(), snowflake.MustParse(getStickyMessageID(e.GuildID().String(), e.Channel().ID().String())))
if err != nil {
logrus.Error(err, getStickyMessageID(e.GuildID().String(), e.Channel().ID().String()))
}
removeSticky(e.GuildID().String(), e.Channel().ID().String())
addSticky(e.GuildID().String(), e.Channel().ID().String(), messageText, message.ID.String())
err = e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Sticky message in this channel was updated!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
} else {
addSticky(e.GuildID().String(), e.Channel().ID().String(), messageText, message.ID.String())
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Message sticked to the channel!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
}
}

217
plugin_src/tags/cmd_tag.go Normal file
View File

@@ -0,0 +1,217 @@
package main
import (
"database/sql"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events"
"github.com/disgoorg/json"
"github.com/sirupsen/logrus"
"github.com/vaporvee/acecore/shared"
)
var db *sql.DB
var createTableQuery string = `CREATE TABLE IF NOT EXISTS tags (
tag_id TEXT NOT NULL,
tag_name TEXT NOT NULL,
tag_content TEXT NOT NULL,
guild_id TEXT NOT NULL,
PRIMARY KEY (tag_id, guild_id)
);
`
var Plugin = &shared.Plugin{
Name: "Tag System",
Init: func(d *sql.DB) error {
db = d
_, err := db.Exec(createTableQuery)
if err != nil {
logrus.Fatal(err)
}
return nil
},
Commands: []shared.Command{
{
Definition: discord.SlashCommandCreate{
Name: "tag",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageGuild),
Description: "A command to show and edit saved presaved messages.",
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
discord.ApplicationCommandOptionSubCommand{
Name: "get",
Description: "A command to get messages saved to the bot.",
Options: []discord.ApplicationCommandOption{
discord.ApplicationCommandOptionString{
Name: "tag",
Description: "Your predefined tag for the saved message",
Required: true,
Autocomplete: true,
},
},
},
discord.ApplicationCommandOptionSubCommand{
Name: "add",
Description: "A command to add messages saved to the bot.",
},
discord.ApplicationCommandOptionSubCommand{
Name: "remove",
Description: "A command to remove messages saved to the bot.",
Options: []discord.ApplicationCommandOption{
discord.ApplicationCommandOptionString{
Name: "tag",
Description: "The tag you want to remove",
Required: true,
Autocomplete: true,
},
},
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
switch *e.SlashCommandInteractionData().SubCommandName {
case "get":
GetTagCommand(e)
case "add":
AddTagCommand(e)
case "remove":
removeTag(e.GuildID().String(), e.SlashCommandInteractionData().String("tag"))
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Tag removed!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
}
},
ModalIDs: []string{"tag_add_modal"},
ModalSubmit: func(e *events.ModalSubmitInteractionCreate) {
tagName := e.Data.Text("tag_add_modal_name")
tagContent := e.Data.Text("tag_add_modal_content")
addTag(e.GuildID().String(), tagName, tagContent)
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent("Tag \"" + tagName + "\" added!").SetEphemeral(true).
Build())
if err != nil {
logrus.Error(err)
}
},
Autocomplete: func(e *events.AutocompleteInteractionCreate) {
AutocompleteTag(e)
},
},
{
Definition: discord.SlashCommandCreate{
Name: "g",
Description: "A short command to get presaved messages.",
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
Options: []discord.ApplicationCommandOption{
discord.ApplicationCommandOptionString{
Name: "tag",
Description: "Your predefined tag for the saved message",
Required: true,
Autocomplete: true,
},
},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
GetTagCommand(e)
},
Autocomplete: func(e *events.AutocompleteInteractionCreate) {
AutocompleteTag(e)
},
},
{
Definition: discord.MessageCommandCreate{
Name: "Save as tag",
DefaultMemberPermissions: json.NewNullablePtr(discord.PermissionManageGuild),
Contexts: []discord.InteractionContextType{
discord.InteractionContextTypeGuild,
discord.InteractionContextTypePrivateChannel},
IntegrationTypes: []discord.ApplicationIntegrationType{
discord.ApplicationIntegrationTypeGuildInstall},
},
Interact: func(e *events.ApplicationCommandInteractionCreate) {
AddTagCommand(e)
},
},
},
}
func GetTagCommand(e *events.ApplicationCommandInteractionCreate) {
err := e.CreateMessage(discord.NewMessageCreateBuilder().
SetContent(getTagContent(e.GuildID().String(), e.SlashCommandInteractionData().String("tag"))).
Build())
if err != nil {
logrus.Error(err)
}
}
func AddTagCommand(e *events.ApplicationCommandInteractionCreate) {
var prevalue string
if e.ApplicationCommandInteraction.Data.Type() == discord.ApplicationCommandTypeMessage {
prevalue = e.MessageCommandInteractionData().TargetMessage().Content
}
err := e.Modal(discord.ModalCreate{
CustomID: "tag_add_modal" + e.User().ID.String(),
Title: "Add a custom tag command",
Components: []discord.ContainerComponent{
discord.ActionRowComponent{
discord.TextInputComponent{
CustomID: "tag_add_modal_name",
Label: "Name",
Style: discord.TextInputStyleShort,
Required: true,
MaxLength: 20,
Value: "",
},
},
discord.ActionRowComponent{
discord.TextInputComponent{
CustomID: "tag_add_modal_content",
Label: "Content",
Style: discord.TextInputStyleParagraph,
Required: true,
MaxLength: 2000,
Value: prevalue,
},
},
},
})
if err != nil {
logrus.Error(err)
}
}
func AutocompleteTag(e *events.AutocompleteInteractionCreate) {
err := e.AutocompleteResult(generateTagChoices(e.GuildID().String()))
if err != nil {
logrus.Error(err)
}
}
func generateTagChoices(guildID string) []discord.AutocompleteChoice {
choices := []discord.AutocompleteChoice{}
IDs, err := getTagIDs(guildID)
if err != nil {
logrus.Error(err)
return choices
}
for _, id := range IDs {
id_name := getTagName(guildID, id)
choices = append(choices, &discord.AutocompleteChoiceString{
Name: id_name,
Value: id,
})
}
return choices
}

70
plugin_src/tags/data.go Normal file
View File

@@ -0,0 +1,70 @@
package main
import (
"github.com/google/uuid"
"github.com/sirupsen/logrus"
)
func addTag(guildID, tagName, tagContent string) bool {
var exists bool = true
//TODO: add modify command
id := uuid.New()
for exists {
id = uuid.New()
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM tags WHERE guild_id = $1 AND tag_id = $2)", guildID, id).Scan(&exists)
if err != nil {
logrus.Error(err)
}
}
_, err := db.Exec("INSERT INTO tags (guild_id, tag_name, tag_content, tag_id) VALUES ($1, $2, $3, $4)", guildID, tagName, tagContent, id)
if err != nil {
logrus.Error(err)
}
return exists
}
func removeTag(guildID string, tagID string) {
var exists bool
err := db.QueryRow("SELECT EXISTS (SELECT 1 FROM tags WHERE guild_id = $1 AND tag_id = $2)", guildID, tagID).Scan(&exists)
if err != nil {
logrus.Error(err)
}
if exists {
_, err = db.Exec("DELETE FROM tags WHERE guild_id = $1 AND tag_id = $2", guildID, tagID)
if err != nil {
logrus.Error(err)
}
}
}
func getTagIDs(guildID string) ([]string, error) {
var IDs []string
rows, err := db.Query("SELECT tag_id FROM tags WHERE guild_id = $1", guildID)
if err != nil {
return nil, err
}
defer rows.Close()
for rows.Next() {
var id string
if err := rows.Scan(&id); err != nil {
return nil, err
}
IDs = append(IDs, id)
}
if err := rows.Err(); err != nil {
return nil, err
}
return IDs, nil
}
func getTagName(guildID string, tagID string) string {
var tagName string
db.QueryRow("SELECT tag_name FROM tags WHERE guild_id = $1 AND tag_id = $2", guildID, tagID).Scan(&tagName)
return tagName
}
func getTagContent(guildID string, tagID string) string {
var tagContent string
db.QueryRow("SELECT tag_content FROM tags WHERE guild_id = $1 AND tag_id = $2", guildID, tagID).Scan(&tagContent)
return tagContent
}

View File

@@ -1,4 +1,4 @@
module github.com/vaporvee/acecore/cmd module github.com/vaporvee/acecore/shared
go 1.22.1 go 1.22.1
@@ -9,6 +9,7 @@ require (
github.com/disgoorg/snowflake/v2 v2.0.1 // indirect github.com/disgoorg/snowflake/v2 v2.0.1 // indirect
github.com/gorilla/websocket v1.5.1 // indirect github.com/gorilla/websocket v1.5.1 // indirect
github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect github.com/sasha-s/go-csync v0.0.0-20240107134140-fcbab37b09ad // indirect
github.com/sirupsen/logrus v1.9.3 // indirect
golang.org/x/crypto v0.19.0 // indirect golang.org/x/crypto v0.19.0 // indirect
golang.org/x/net v0.21.0 // indirect golang.org/x/net v0.21.0 // indirect
golang.org/x/sys v0.17.0 // indirect golang.org/x/sys v0.17.0 // indirect

View File

@@ -19,10 +19,6 @@ github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/vaporvee/acecore v0.0.0-20240414202630-d814d39cf135 h1:LdMezyBJ9Q8/VIugcvc6X37/0L9poxKU6zq36ooJN3U=
github.com/vaporvee/acecore v0.0.0-20240414202630-d814d39cf135/go.mod h1:kRHYJwIF7PW99J8BzDqVtV7c0PoitgdpR/e06emJm1s=
github.com/vaporvee/acecore/cmd v0.0.0-20240414202630-d814d39cf135 h1:SbwOoMX4LViUCuH8syFUKPsKXBONY3PhSrkKVNmFJYc=
github.com/vaporvee/acecore/cmd v0.0.0-20240414202630-d814d39cf135/go.mod h1:03W2NrAPbAqa7gsqAM+2K/wT28stj9BmEhC5NzeGe3A=
golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo= golang.org/x/crypto v0.19.0 h1:ENy+Az/9Y1vSrlrvBSyna3PITt4tiZLf7sgCjZBX7Wo=
golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4= golang.org/x/net v0.21.0 h1:AQyQV4dYCvJ7vGmJyKki9+PBdyvhkSd8EIx/qb0AYv4=

View File

@@ -1,13 +1,15 @@
package cmd package shared
import ( import (
"database/sql" "database/sql"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord" "github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/events" "github.com/disgoorg/disgo/events"
) )
type Command struct { type Command struct {
Ready func(e *events.Ready)
Definition discord.ApplicationCommandCreate Definition discord.ApplicationCommandCreate
Interact func(e *events.ApplicationCommandInteractionCreate) Interact func(e *events.ApplicationCommandInteractionCreate)
Autocomplete func(e *events.AutocompleteInteractionCreate) Autocomplete func(e *events.AutocompleteInteractionCreate)
@@ -21,6 +23,8 @@ type Command struct {
type Plugin struct { type Plugin struct {
Name string Name string
Register func(e *events.Ready, db *sql.DB) error Init func(d *sql.DB) error
Commands []Command Commands []Command
} }
var BotConfigs []bot.ConfigOpt

114
shared/tool.go Normal file
View File

@@ -0,0 +1,114 @@
package shared
import (
"embed"
"encoding/json"
"fmt"
"strings"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/snowflake/v2"
"github.com/sirupsen/logrus"
)
type ModalJsonField struct {
Label string `json:"label"`
IsParagraph bool `json:"is_paragraph"`
Value string `json:"value"`
Required bool `json:"required"`
Placeholder string `json:"placeholder"`
MinLength int `json:"min_length"`
MaxLength int `json:"max_length"`
}
type ModalJson struct {
FormType string `json:"form_type"`
Title string `json:"title"`
Form []ModalJsonField `json:"form"`
}
// Why does the golang compiler care about commands??
//
//go:embed form_templates/*.json
var FormTemplates embed.FS
func GetModalByFormID(formID string) ModalJson {
var modal ModalJson
if formID == "" {
return modal
}
entries, err := FormTemplates.ReadDir("form_templates")
if err != nil {
logrus.Error(err)
return modal
}
for _, entry := range entries {
if strings.HasPrefix(entry.Name(), formID) {
jsonFile, err := FormTemplates.ReadFile("form_templates/" + entry.Name())
if err != nil {
logrus.Error(err)
continue
}
err = json.Unmarshal(jsonFile, &modal)
if err != nil {
logrus.Error(err)
continue
}
break
}
}
return modal
}
func JsonStringBuildModal(userID string, manageID string, formID string, overwrite ...string) discord.ModalCreate {
var modal ModalJson = GetModalByFormID(formID)
var components []discord.ContainerComponent
for index, component := range modal.Form {
var style discord.TextInputStyle = discord.TextInputStyleShort
if component.IsParagraph {
style = discord.TextInputStyleParagraph
}
components = append(components, discord.ActionRowComponent{
discord.TextInputComponent{
CustomID: fmt.Sprint(index),
Label: component.Label,
Style: style,
Placeholder: component.Placeholder,
Required: component.Required,
MaxLength: component.MaxLength,
MinLength: &component.MinLength,
Value: component.Value,
},
})
}
if overwrite != nil && overwrite[0] != "" {
modal.Title = overwrite[0]
}
return discord.ModalCreate{
CustomID: "form:" + manageID + ":" + userID,
Title: modal.Title,
Components: components,
}
}
func IsIDRole(c bot.Client, guildID snowflake.ID, id snowflake.ID) bool {
_, err1 := c.Rest().GetMember(guildID, id)
if err1 == nil {
return false
}
roles, err2 := c.Rest().GetRoles(guildID)
if err2 == nil {
for _, role := range roles {
if role.ID == id {
return true
}
}
}
logrus.Error(err1)
logrus.Error(err2)
return false
}

230
tool.go
View File

@@ -1,230 +0,0 @@
package main
import (
"embed"
"encoding/json"
"fmt"
"io"
"net/http"
"strings"
"github.com/disgoorg/disgo/bot"
"github.com/disgoorg/disgo/discord"
"github.com/disgoorg/disgo/rest"
"github.com/disgoorg/snowflake/v2"
"github.com/sirupsen/logrus"
)
type ModalJsonField struct {
Label string `json:"label"`
IsParagraph bool `json:"is_paragraph"`
Value string `json:"value"`
Required bool `json:"required"`
Placeholder string `json:"placeholder"`
MinLength int `json:"min_length"`
MaxLength int `json:"max_length"`
}
type ModalJson struct {
FormType string `json:"form_type"`
Title string `json:"title"`
Form []ModalJsonField `json:"form"`
}
type MessageIDs struct {
ID string
ChannelID string
}
func jsonStringBuildModal(userID string, manageID string, formID string, overwrite ...string) discord.ModalCreate {
var modal ModalJson = getModalByFormID(formID)
var components []discord.ContainerComponent
for index, component := range modal.Form {
var style discord.TextInputStyle = discord.TextInputStyleShort
if component.IsParagraph {
style = discord.TextInputStyleParagraph
}
components = append(components, discord.ActionRowComponent{
discord.TextInputComponent{
CustomID: fmt.Sprint(index),
Label: component.Label,
Style: style,
Placeholder: component.Placeholder,
Required: component.Required,
MaxLength: component.MaxLength,
MinLength: &component.MinLength,
Value: component.Value,
},
})
}
if overwrite != nil && overwrite[0] != "" {
modal.Title = overwrite[0]
}
return discord.ModalCreate{
CustomID: "form:" + manageID + ":" + userID,
Title: modal.Title,
Components: components,
}
}
// Why does the golang compiler care about commands??
//
//go:embed form_templates/*.json
var formTemplates embed.FS
func getModalByFormID(formID string) ModalJson {
var modal ModalJson
if formID == "" {
return modal
}
entries, err := formTemplates.ReadDir("form_templates")
if err != nil {
logrus.Error(err)
return modal
}
for _, entry := range entries {
if strings.HasPrefix(entry.Name(), formID) {
jsonFile, err := formTemplates.ReadFile("form_templates/" + entry.Name())
if err != nil {
logrus.Error(err)
continue
}
err = json.Unmarshal(jsonFile, &modal)
if err != nil {
logrus.Error(err)
continue
}
break
}
}
return modal
}
func getHighestRole(guildID string, c bot.Client) (*discord.Role, error) {
botmember, err := c.Rest().GetMember(snowflake.MustParse(guildID), c.ApplicationID())
if err != nil {
return nil, err
}
roles, err := c.Rest().GetRoles(snowflake.MustParse(guildID))
if err != nil {
return nil, err
}
var highestRole *discord.Role
for _, roleID := range botmember.RoleIDs {
for _, role := range roles {
if role.ID == roleID {
if highestRole == nil || role.Position > highestRole.Position {
highestRole = &role
}
break
}
}
}
return highestRole, nil
}
func simpleGetFromAPI(key string, url string) interface{} {
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
logrus.Error("Error creating request:", err)
}
req.Header.Set("Accept", "application/json")
resp, err := client.Do(req)
if err != nil {
logrus.Error("Error making request:", err)
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
logrus.Error("Error reading response body:", err)
}
var result map[string]interface{}
err = json.Unmarshal(body, &result)
if err != nil {
logrus.Error("Error decoding JSON:", err)
}
return result[key]
}
func findAndDeleteUnusedMessages(c bot.Client) {
for _, message := range getAllSavedMessages() {
_, err := c.Rest().GetMessage(snowflake.MustParse(message.ChannelID), snowflake.MustParse(message.ID))
if err != nil {
tryDeleteUnusedMessage(message.ID)
}
}
}
func isIDRole(c bot.Client, guildID snowflake.ID, id snowflake.ID) bool {
_, err1 := c.Rest().GetMember(guildID, id)
if err1 == nil {
return false
}
roles, err2 := c.Rest().GetRoles(guildID)
if err2 == nil {
for _, role := range roles {
if role.ID == id {
return true
}
}
}
logrus.Error(err1)
logrus.Error(err2)
return false
}
func isGIFImage(imageData []byte) bool {
if len(imageData) < 6 {
return false
}
// Check for the GIF89a header at the beginning of the byte array
if string(imageData[0:6]) == "GIF89a" {
return true
}
// Check for the GIF87a header at the beginning of the byte array
if string(imageData[0:6]) == "GIF87a" {
return true
}
return false
}
func messageIsPoll(channelID string, messageID string, client bot.Client) bool {
url := rest.DefaultConfig().URL + "/channels/" + channelID + "/messages/" + messageID
req, err := http.NewRequest("GET", url, nil)
if err != nil {
logrus.Error(err)
return false
}
auth := "Bot " + client.Token()
req.Header.Set("Authorization", auth)
resp, err := client.Rest().HTTPClient().Do(req)
if err != nil {
logrus.Error(err)
return false
}
defer resp.Body.Close()
body, err := io.ReadAll(resp.Body)
if err != nil {
logrus.Error(err)
return false
}
var data map[string]interface{}
err = json.Unmarshal(body, &data)
if err != nil {
logrus.Error(err)
return false
}
_, ok := data["poll"]
return ok
}

View File

@@ -43,7 +43,7 @@
services. services.
</li> </li>
<li> <li>
cmd.Command usage: We store data about some commands that you use within shared.Command usage: We store data about some commands that you use within
acecore in order to make the commands usable and functioning. acecore in order to make the commands usable and functioning.
</li> </li>
<li> <li>