- Introduced a new default role service and repository for managing default roles assigned to new members. - Added command to set the default role through the bot's configuration. - Implemented event handling to assign the default role to new guild members upon joining. - Updated main application to include the new default role service in the bot's services.
473 lines
13 KiB
Go
473 lines
13 KiB
Go
package public
|
|
|
|
import (
|
|
"context"
|
|
"log"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"velox-bot/internal/db/services"
|
|
|
|
"github.com/bwmarrin/discordgo"
|
|
)
|
|
|
|
var Config = &discordgo.ApplicationCommand{
|
|
Name: "config",
|
|
Description: "Server configuration",
|
|
Options: []*discordgo.ApplicationCommandOption{
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionSubCommand,
|
|
Name: "addstreamer",
|
|
Description: "Add a Twitch streamer for live notifications",
|
|
Options: []*discordgo.ApplicationCommandOption{
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionString,
|
|
Name: "username",
|
|
Description: "Twitch username (without https://)",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionSubCommand,
|
|
Name: "removestreamer",
|
|
Description: "Remove a Twitch streamer from live notifications",
|
|
Options: []*discordgo.ApplicationCommandOption{
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionString,
|
|
Name: "username",
|
|
Description: "Twitch username to remove",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionSubCommand,
|
|
Name: "settwitchnotificationchannel",
|
|
Description: "Set the channel for Twitch live notifications",
|
|
Options: []*discordgo.ApplicationCommandOption{
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionChannel,
|
|
Name: "channel",
|
|
Description: "Text channel for notifications",
|
|
Required: true,
|
|
ChannelTypes: []discordgo.ChannelType{discordgo.ChannelTypeGuildText, discordgo.ChannelTypeGuildNews},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionSubCommand,
|
|
Name: "setwelcomechannel",
|
|
Description: "Set the channel for welcome messages",
|
|
Options: []*discordgo.ApplicationCommandOption{
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionChannel,
|
|
Name: "channel",
|
|
Description: "Text channel for welcomes",
|
|
Required: true,
|
|
ChannelTypes: []discordgo.ChannelType{discordgo.ChannelTypeGuildText, discordgo.ChannelTypeGuildNews},
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionSubCommand,
|
|
Name: "setwelcomemessage",
|
|
Description: "Set the welcome message template shown in the server",
|
|
Options: []*discordgo.ApplicationCommandOption{
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionString,
|
|
Name: "message",
|
|
Description: "Use {user} where the new member mention should appear",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionSubCommand,
|
|
Name: "setwelcomedm",
|
|
Description: "Set the welcome DM template sent to new members",
|
|
Options: []*discordgo.ApplicationCommandOption{
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionString,
|
|
Name: "message",
|
|
Description: "Use {user} where the new member mention should appear",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionSubCommand,
|
|
Name: "setwelcomegif",
|
|
Description: "Set the GIF/image URL used in welcome messages",
|
|
Options: []*discordgo.ApplicationCommandOption{
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionString,
|
|
Name: "url",
|
|
Description: "Direct image or GIF URL",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionSubCommand,
|
|
Name: "setdefaultrole",
|
|
Description: "Set the default role given to new members",
|
|
Options: []*discordgo.ApplicationCommandOption{
|
|
{
|
|
Type: discordgo.ApplicationCommandOptionRole,
|
|
Name: "role",
|
|
Description: "Role to assign on join",
|
|
Required: true,
|
|
},
|
|
},
|
|
},
|
|
},
|
|
}
|
|
|
|
func ConfigHandler(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
if i.GuildID == "" {
|
|
respondEphemeral(s, i, "This command can only be used in a server.")
|
|
return
|
|
}
|
|
if i.Member == nil || (i.Member.Permissions&discordgo.PermissionManageGuild) == 0 {
|
|
respondEphemeral(s, i, "You need the **Manage Server** permission to use this.")
|
|
return
|
|
}
|
|
if services.Global == nil {
|
|
respondEphemeral(s, i, "Configuration services are not available.")
|
|
return
|
|
}
|
|
|
|
data := i.ApplicationCommandData()
|
|
if len(data.Options) == 0 {
|
|
respondEphemeral(s, i, "Missing subcommand.")
|
|
return
|
|
}
|
|
|
|
switch data.Options[0].Name {
|
|
case "addstreamer":
|
|
log.Printf("twitch: /config addstreamer invoked by %s in guild %s", i.Member.User.ID, i.GuildID)
|
|
handleAddStreamer(s, i)
|
|
case "removestreamer":
|
|
log.Printf("twitch: /config removestreamer invoked by %s in guild %s", i.Member.User.ID, i.GuildID)
|
|
handleRemoveStreamer(s, i)
|
|
case "settwitchnotificationchannel":
|
|
log.Printf("twitch: /config settwitchnotificationchannel invoked by %s in guild %s", i.Member.User.ID, i.GuildID)
|
|
handleSetChannel(s, i)
|
|
case "setwelcomechannel":
|
|
log.Printf("welcome: /config setwelcomechannel invoked by %s in guild %s", i.Member.User.ID, i.GuildID)
|
|
handleSetWelcomeChannel(s, i)
|
|
case "setwelcomemessage":
|
|
log.Printf("welcome: /config setwelcomemessage invoked by %s in guild %s", i.Member.User.ID, i.GuildID)
|
|
handleSetWelcomeMessage(s, i)
|
|
case "setwelcomedm":
|
|
log.Printf("welcome: /config setwelcomedm invoked by %s in guild %s", i.Member.User.ID, i.GuildID)
|
|
handleSetWelcomeDM(s, i)
|
|
case "setwelcomegif":
|
|
log.Printf("welcome: /config setwelcomegif invoked by %s in guild %s", i.Member.User.ID, i.GuildID)
|
|
handleSetWelcomeGIF(s, i)
|
|
case "setdefaultrole":
|
|
log.Printf("defaultrole: /config setdefaultrole invoked by %s in guild %s", i.Member.User.ID, i.GuildID)
|
|
handleSetDefaultRole(s, i)
|
|
default:
|
|
respondEphemeral(s, i, "Unknown subcommand.")
|
|
}
|
|
}
|
|
|
|
func handleAddStreamer(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
guildID, err := strconv.ParseInt(i.GuildID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid guild ID.")
|
|
return
|
|
}
|
|
|
|
opt := i.ApplicationCommandData().Options[0]
|
|
var username string
|
|
for _, o := range opt.Options {
|
|
if o.Name == "username" {
|
|
username = strings.TrimSpace(o.StringValue())
|
|
}
|
|
}
|
|
username = strings.TrimPrefix(username, "https://www.twitch.tv/")
|
|
username = strings.TrimPrefix(username, "http://www.twitch.tv/")
|
|
username = strings.TrimPrefix(username, "twitch.tv/")
|
|
username = strings.TrimSpace(username)
|
|
|
|
if username == "" {
|
|
respondEphemeral(s, i, "Username cannot be empty.")
|
|
return
|
|
}
|
|
|
|
if err := services.Global.Twitch.UpsertStreamer(context.Background(), guildID, username); err != nil {
|
|
respondEphemeral(s, i, "Failed to add streamer.")
|
|
return
|
|
}
|
|
|
|
respondEphemeral(s, i, "Added `"+username+"` for Twitch live notifications.")
|
|
}
|
|
|
|
func handleRemoveStreamer(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
guildID, err := strconv.ParseInt(i.GuildID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid guild ID.")
|
|
return
|
|
}
|
|
|
|
opt := i.ApplicationCommandData().Options[0]
|
|
var username string
|
|
for _, o := range opt.Options {
|
|
if o.Name == "username" {
|
|
username = strings.TrimSpace(o.StringValue())
|
|
}
|
|
}
|
|
username = strings.TrimSpace(username)
|
|
if username == "" {
|
|
respondEphemeral(s, i, "Username cannot be empty.")
|
|
return
|
|
}
|
|
|
|
if err := services.Global.Twitch.RemoveStreamer(context.Background(), guildID, username); err != nil {
|
|
respondEphemeral(s, i, "Failed to remove streamer.")
|
|
return
|
|
}
|
|
|
|
respondEphemeral(s, i, "Removed `"+username+"` from Twitch live notifications.")
|
|
}
|
|
|
|
func handleSetChannel(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
guildID, err := strconv.ParseInt(i.GuildID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid guild ID.")
|
|
return
|
|
}
|
|
|
|
opt := i.ApplicationCommandData().Options[0]
|
|
var chOpt *discordgo.ApplicationCommandInteractionDataOption
|
|
for _, o := range opt.Options {
|
|
if o.Name == "channel" {
|
|
chOpt = o
|
|
break
|
|
}
|
|
}
|
|
if chOpt == nil {
|
|
respondEphemeral(s, i, "Missing channel option.")
|
|
return
|
|
}
|
|
|
|
ch := chOpt.ChannelValue(s)
|
|
if ch == nil {
|
|
respondEphemeral(s, i, "Invalid channel.")
|
|
return
|
|
}
|
|
|
|
chID, err := strconv.ParseInt(ch.ID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid channel ID.")
|
|
return
|
|
}
|
|
|
|
if err := services.Global.Twitch.SetNotificationChannel(context.Background(), guildID, chID); err != nil {
|
|
respondEphemeral(s, i, "Failed to set notification channel.")
|
|
return
|
|
}
|
|
|
|
respondEphemeral(s, i, "Twitch notifications will be sent to "+ch.Mention()+".")
|
|
}
|
|
|
|
func handleSetWelcomeChannel(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
if services.Global.Welcome == nil {
|
|
respondEphemeral(s, i, "Welcome configuration is not available.")
|
|
return
|
|
}
|
|
|
|
guildID, err := strconv.ParseInt(i.GuildID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid guild ID.")
|
|
return
|
|
}
|
|
|
|
opt := i.ApplicationCommandData().Options[0]
|
|
var chOpt *discordgo.ApplicationCommandInteractionDataOption
|
|
for _, o := range opt.Options {
|
|
if o.Name == "channel" {
|
|
chOpt = o
|
|
break
|
|
}
|
|
}
|
|
if chOpt == nil {
|
|
respondEphemeral(s, i, "Missing channel option.")
|
|
return
|
|
}
|
|
|
|
ch := chOpt.ChannelValue(s)
|
|
if ch == nil {
|
|
respondEphemeral(s, i, "Invalid channel.")
|
|
return
|
|
}
|
|
|
|
chID, err := strconv.ParseInt(ch.ID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid channel ID.")
|
|
return
|
|
}
|
|
|
|
if err := services.Global.Welcome.SetChannel(context.Background(), guildID, chID); err != nil {
|
|
respondEphemeral(s, i, "Failed to set welcome channel.")
|
|
return
|
|
}
|
|
|
|
respondEphemeral(s, i, "Welcome messages will be sent to "+ch.Mention()+".")
|
|
}
|
|
|
|
func handleSetWelcomeMessage(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
if services.Global.Welcome == nil {
|
|
respondEphemeral(s, i, "Welcome configuration is not available.")
|
|
return
|
|
}
|
|
|
|
guildID, err := strconv.ParseInt(i.GuildID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid guild ID.")
|
|
return
|
|
}
|
|
|
|
opt := i.ApplicationCommandData().Options[0]
|
|
var msg string
|
|
for _, o := range opt.Options {
|
|
if o.Name == "message" {
|
|
msg = strings.TrimSpace(o.StringValue())
|
|
}
|
|
}
|
|
if msg == "" {
|
|
respondEphemeral(s, i, "Message cannot be empty.")
|
|
return
|
|
}
|
|
|
|
if err := services.Global.Welcome.SetMessage(context.Background(), guildID, msg); err != nil {
|
|
respondEphemeral(s, i, "Failed to set welcome message.")
|
|
return
|
|
}
|
|
|
|
respondEphemeral(s, i, "Welcome message updated.")
|
|
}
|
|
|
|
func handleSetWelcomeDM(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
if services.Global.Welcome == nil {
|
|
respondEphemeral(s, i, "Welcome configuration is not available.")
|
|
return
|
|
}
|
|
|
|
guildID, err := strconv.ParseInt(i.GuildID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid guild ID.")
|
|
return
|
|
}
|
|
|
|
opt := i.ApplicationCommandData().Options[0]
|
|
var msg string
|
|
for _, o := range opt.Options {
|
|
if o.Name == "message" {
|
|
msg = strings.TrimSpace(o.StringValue())
|
|
}
|
|
}
|
|
if msg == "" {
|
|
respondEphemeral(s, i, "DM message cannot be empty.")
|
|
return
|
|
}
|
|
|
|
if err := services.Global.Welcome.SetDM(context.Background(), guildID, msg); err != nil {
|
|
respondEphemeral(s, i, "Failed to set welcome DM.")
|
|
return
|
|
}
|
|
|
|
respondEphemeral(s, i, "Welcome DM updated.")
|
|
}
|
|
|
|
func handleSetWelcomeGIF(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
if services.Global.Welcome == nil {
|
|
respondEphemeral(s, i, "Welcome configuration is not available.")
|
|
return
|
|
}
|
|
|
|
guildID, err := strconv.ParseInt(i.GuildID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid guild ID.")
|
|
return
|
|
}
|
|
|
|
opt := i.ApplicationCommandData().Options[0]
|
|
var url string
|
|
for _, o := range opt.Options {
|
|
if o.Name == "url" {
|
|
url = strings.TrimSpace(o.StringValue())
|
|
}
|
|
}
|
|
if url == "" {
|
|
respondEphemeral(s, i, "URL cannot be empty.")
|
|
return
|
|
}
|
|
|
|
if err := services.Global.Welcome.SetGIF(context.Background(), guildID, url); err != nil {
|
|
respondEphemeral(s, i, "Failed to set welcome GIF.")
|
|
return
|
|
}
|
|
|
|
respondEphemeral(s, i, "Welcome GIF updated.")
|
|
}
|
|
|
|
func handleSetDefaultRole(s *discordgo.Session, i *discordgo.InteractionCreate) {
|
|
if services.Global.DefaultRole == nil {
|
|
respondEphemeral(s, i, "Default role configuration is not available.")
|
|
return
|
|
}
|
|
|
|
guildID, err := strconv.ParseInt(i.GuildID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid guild ID.")
|
|
return
|
|
}
|
|
|
|
opt := i.ApplicationCommandData().Options[0]
|
|
var roleOpt *discordgo.ApplicationCommandInteractionDataOption
|
|
for _, o := range opt.Options {
|
|
if o.Name == "role" {
|
|
roleOpt = o
|
|
break
|
|
}
|
|
}
|
|
if roleOpt == nil {
|
|
respondEphemeral(s, i, "Missing role option.")
|
|
return
|
|
}
|
|
|
|
role := roleOpt.RoleValue(s, i.GuildID)
|
|
if role == nil {
|
|
respondEphemeral(s, i, "Invalid role.")
|
|
return
|
|
}
|
|
|
|
roleID, err := strconv.ParseInt(role.ID, 10, 64)
|
|
if err != nil {
|
|
respondEphemeral(s, i, "Invalid role ID.")
|
|
return
|
|
}
|
|
|
|
if err := services.Global.DefaultRole.SetRole(context.Background(), guildID, roleID); err != nil {
|
|
respondEphemeral(s, i, "Failed to set default role.")
|
|
return
|
|
}
|
|
|
|
respondEphemeral(s, i, "Default role set to "+role.Mention()+".")
|
|
}
|
|
|
|
func respondEphemeral(s *discordgo.Session, i *discordgo.InteractionCreate, msg string) {
|
|
_ = s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{
|
|
Type: discordgo.InteractionResponseChannelMessageWithSource,
|
|
Data: &discordgo.InteractionResponseData{
|
|
Content: msg,
|
|
Flags: discordgo.MessageFlagsEphemeral,
|
|
},
|
|
})
|
|
}
|
|
|