From b1b0af1613ef6958cfbd37fd00b35f8c5c54df5a Mon Sep 17 00:00:00 2001 From: ION606 Date: Mon, 24 Mar 2025 16:34:59 -0400 Subject: [PATCH] added actions and animal gif routes --- Bot/Commands/action.go | 209 +++++++++++++++++++++++++++++++++++++++++ Bot/Commands/tenor.go | 107 +++++++++++++++++++++ Bot/bot.go | 18 ++-- Bot/register.go | 2 + Makefile | 5 +- 5 files changed, 332 insertions(+), 9 deletions(-) create mode 100644 Bot/Commands/action.go create mode 100644 Bot/Commands/tenor.go diff --git a/Bot/Commands/action.go b/Bot/Commands/action.go new file mode 100644 index 0000000..b5e12fe --- /dev/null +++ b/Bot/Commands/action.go @@ -0,0 +1,209 @@ +package commands + +import ( + "fmt" + "strings" + + "github.com/bwmarrin/discordgo" +) + +var ActionCommand = &discordgo.ApplicationCommand{ + Name: "action", + Description: "Perform an action towards someone.", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "action", + Description: "Choose an action to perform", + Required: true, + Choices: []*discordgo.ApplicationCommandOptionChoice{ + {Name: "hugs", Value: "hugs"}, + {Name: "high-five", Value: "highfive"}, + {Name: "slaps", Value: "slaps"}, + {Name: "kisses", Value: "kisses"}, + {Name: "pokes", Value: "pokes"}, + {Name: "tickles", Value: "tickles"}, + {Name: "smiles", Value: "smiles"}, + {Name: "rolls eyes", Value: "rollseyes"}, + {Name: "scoffs", Value: "scoffs"}, + {Name: "glares", Value: "glares"}, + {Name: "shrugs", Value: "shrugs"}, + {Name: "snarks", Value: "snarks"}, + {Name: "pouts", Value: "pouts"}, + }, + }, + { + Type: discordgo.ApplicationCommandOptionUser, + Name: "receiver", + Description: "The person to receive the action", + Required: false, + }, + { + Type: discordgo.ApplicationCommandOptionBoolean, + Name: "useemojis", + Description: "Use emojis instead of emoticons (default is false)", + Required: false, + }, + }, +} + +var emojiMap = map[string]string{ + "hugs": "🤗", + "highfive": "✋", + "slaps": "😵", + "kisses": "😘", + "pokes": "👉", + "tickles": "😂", + "smiles": "😊", + "rollseyes": "🙄", + "scoffs": "😒", + "glares": "😠", + "shrugs": "🤷", + "snarks": "😏", + "pouts": "😤", +} + +var emoticonMap = map[string]string{ + "hugs": "(<^_^>)", + "highfive": "o/\\o", + "slaps": "*slaps*", + "kisses": ":-*", + "pokes": "->", + "tickles": "*tickles*", + "smiles": ":3", + "rollseyes": "*rolls eyes*", + "scoffs": "*scoffs*", + "glares": ">:(", + "shrugs": "¯\\_(ツ)_/¯", + "snarks": "*snarks*", + "pouts": ":(", +} + +// Special messages for when the sender targets themselves or the bot +var specialMessages = map[string]map[string]string{ + "hugs": { + "self": "You give yourself a warm hug. Sometimes self-love is the best love!", + "bot": "OWO thamkies for hug!!!", + }, + "highfive": { + "self": "High-fiving yourself? That's some self-confidence!", + "bot": "YAY high-five!", + }, + "slaps": { + "self": "Stop it, get some help", + "bot": "OI! Go away! *HMPH!*", + }, + "kisses": { + "self": "Do you taste good???", + "bot": ">///////////////<", + }, + "pokes": { + "self": "Poking yourself is a sign you need a break", + "bot": "*grrrrrrrrrrrrrrrrr*", + }, + "tickles": { + "self": "???????????????????", + "bot": "*FKLDJLKFSDLKFJDSKLFJLKSDF **CEASE!!!***", + }, + "smiles": { + "self": ":D", + "bot": ":D", + }, + "rollseyes": { + "self": "bleh 🙄", + "bot": "*SASSING ME???? W-WELL I BORK AT YOU!!!*", + }, + "scoffs": { + "self": "Scoffing at yourself isn't very productive.", + "bot": "I hear you, but try being kinder!", + }, + "glares": { + "self": "Maybe take a break from the mirror...", + "bot": ">~<\n*sh-shtooopppppp!*", + }, + "shrugs": { + "self": "IDK man it's a your problem", + "bot": "", + }, + "pouts": { + "self": "Pouting at yourself won't change a thing!", + "bot": "# *HMPH!*", + }, +} + +func HandleAction(s *discordgo.Session, i *discordgo.InteractionCreate) { + actionSelected := strings.ToLower(i.ApplicationCommandData().Options[0].StringValue()) + + // Check optional boolean parameter "useemojis" + useEmojis := false + if len(i.ApplicationCommandData().Options) > 2 { + option := i.ApplicationCommandData().Options[2] + if option.Value != nil { + useEmojis, _ = option.Value.(bool) + } + } + + // Choose the mapping based on the option + var mapping map[string]string + if useEmojis { + mapping = emojiMap + } else { + mapping = emoticonMap + } + + // Determine the display symbol for the action + symbol, exists := mapping[actionSelected] + if !exists { + symbol = "😄" + } + + // Get the receiver if provided; if not, default to "someone special" + var receiverID string + receiverMention := "someone special" + if len(i.ApplicationCommandData().Options) > 1 { + if id, ok := i.ApplicationCommandData().Options[1].Value.(string); ok { + receiverID = id + if i.ApplicationCommandData().Resolved != nil && i.ApplicationCommandData().Resolved.Users != nil { + if user, found := i.ApplicationCommandData().Resolved.Users[id]; found { + receiverMention = fmt.Sprintf("<@%s>", user.ID) + } else { + receiverMention = fmt.Sprintf("<@%s>", id) + } + } else { + receiverMention = fmt.Sprintf("<@%s>", id) + } + } + } + + // Determine the sender mention. + senderID := i.User.ID + if i.Member != nil { + senderID = i.Member.User.ID + } + senderMention := fmt.Sprintf("<@%s>", senderID) + + // Check for special cases. + responseMessage := fmt.Sprintf("%s %s %s %s", senderMention, actionSelected, receiverMention, symbol) + if receiverID != "" { + botID := s.State.User.ID + if receiverID == senderID { + // Special message for acting on yourself. + if special, ok := specialMessages[actionSelected]["self"]; ok { + responseMessage = special + } + } else if receiverID == botID { + // Special message for acting on the bot. + if special, ok := specialMessages[actionSelected]["bot"]; ok { + responseMessage = special + } + } + } + + // Send the interaction response. + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: responseMessage, + }, + }) +} diff --git a/Bot/Commands/tenor.go b/Bot/Commands/tenor.go new file mode 100644 index 0000000..15a5c54 --- /dev/null +++ b/Bot/Commands/tenor.go @@ -0,0 +1,107 @@ +package commands + +import ( + "encoding/json" + "fmt" + "io/ioutil" + "math/rand" + "net/http" + "net/url" + "os" + "strings" + "time" + + helpers "ion606_bot/Bot/Helpers" + + "github.com/bwmarrin/discordgo" +) + +var AnimalGifCommand = &discordgo.ApplicationCommand{ + Name: "animalgif", + Description: "Shows a random cute gif of an animal (default: seal)", + Options: []*discordgo.ApplicationCommandOption{ + { + Type: discordgo.ApplicationCommandOptionString, + Name: "animal", + Description: "Name of the animal (default: seal)", + Required: false, + }, + }, +} + +type TenorResponse struct { + Results []struct { + MediaFormats map[string]struct { + URL string `json:"url"` + } `json:"media_formats"` + } `json:"results"` + Next string `json:"next"` +} + +func HandleAnimalGif(s *discordgo.Session, i *discordgo.InteractionCreate) { + // Get the animal option, default to "seal" if not provided. + animal := "seal" + if len(i.ApplicationCommandData().Options) > 0 && i.ApplicationCommandData().Options[0].Value != nil { + animal = i.ApplicationCommandData().Options[0].StringValue() + } + + // Build the search query (e.g., "cute seal"). + searchQuery := fmt.Sprintf("cute %s", animal) + encodedQuery := url.QueryEscape(searchQuery) + + // Get the Tenor API key from environment. + tenorKey := os.Getenv("TENOR_KEY") + tenorKey = strings.TrimSpace(tenorKey) + if tenorKey == "" { + helpers.HandleError(s, i, fmt.Errorf("TENOR_KEY is not set in the environment")) + return + } + + // Build the Tenor API URL. + limit := 1 + apiURL := fmt.Sprintf("https://tenor.googleapis.com/v2/search?q=%s&key=%s&limit=%d&random=true", encodedQuery, tenorKey, limit) + + client := http.Client{Timeout: 10 * time.Second} + resp, err := client.Get(apiURL) + if err != nil { + helpers.HandleError(s, i, fmt.Errorf("error fetching gif: %w", err)) + return + } + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + helpers.HandleError(s, i, fmt.Errorf("error reading response: %w", err)) + return + } + + var tResponse TenorResponse + err = json.Unmarshal(body, &tResponse) + if err != nil { + helpers.HandleError(s, i, fmt.Errorf("error unmarshalling response: %w. Response body: %s", err, string(body))) + return + } + if len(tResponse.Results) == 0 { + helpers.HandleError(s, i, fmt.Errorf("no gif results returned from Tenor")) + return + } + + // Pick a random gif from the results. + rand.Seed(time.Now().UnixNano()) + randomIndex := rand.Intn(len(tResponse.Results)) + formats := tResponse.Results[randomIndex].MediaFormats + gifFormat, ok := formats["gif"] + if !ok { + helpers.HandleError(s, i, fmt.Errorf("gif format not available in Tenor response")) + return + } + gifURL := gifFormat.URL + + // Respond with the gif. + s.InteractionRespond(i.Interaction, &discordgo.InteractionResponse{ + Type: discordgo.InteractionResponseChannelMessageWithSource, + Data: &discordgo.InteractionResponseData{ + Content: gifURL, + }, + }) +} diff --git a/Bot/bot.go b/Bot/bot.go index 463e4d6..0958281 100644 --- a/Bot/bot.go +++ b/Bot/bot.go @@ -15,13 +15,15 @@ var BotToken string // commandHandlers maps command names to their interaction handler functions. var commandHandlers = map[string]func(*discordgo.Session, *discordgo.InteractionCreate){ - "meow": commands.HandleMeow, - "purr": commands.HandlePurr, - "boop": commands.HandleBoop, - "hug": commands.HandleHug, - "cuddle": commands.HandleCuddle, - "snuggle": commands.HandleSnuggle, - "catfact": commands.HandleCatfact, + "meow": commands.HandleMeow, + "purr": commands.HandlePurr, + "boop": commands.HandleBoop, + "hug": commands.HandleHug, + "cuddle": commands.HandleCuddle, + "snuggle": commands.HandleSnuggle, + "catfact": commands.HandleCatfact, + "animalgif": commands.HandleAnimalGif, + "action": commands.HandleAction, } // Run starts the Discord bot session and listens for both message and slash command events. @@ -59,7 +61,7 @@ func newMessage(s *discordgo.Session, m *discordgo.MessageCreate) { if m.Author.ID == s.State.User.ID { return } - // additional message handling logic can go here. + } func handleInteractionCreate(s *discordgo.Session, i *discordgo.InteractionCreate) { diff --git a/Bot/register.go b/Bot/register.go index 299b820..6a311bd 100644 --- a/Bot/register.go +++ b/Bot/register.go @@ -17,6 +17,8 @@ var commandsList = []*discordgo.ApplicationCommand{ Commands.CuddleCommand, Commands.SnuggleCommand, Commands.CatfactCommand, + Commands.AnimalGifCommand, + Commands.ActionCommand, } func RegisterCommands(s *discordgo.Session, guildID string) { diff --git a/Makefile b/Makefile index d9c7467..91e1313 100644 --- a/Makefile +++ b/Makefile @@ -2,4 +2,7 @@ build: docker build -t ion606-bot . run: - docker run --rm -d --env-file .env ion606-bot \ No newline at end of file + docker run --rm -d --env-file .env ion606-bot + +dev: build + docker run --rm --env-file .env ion606-bot \ No newline at end of file