package public import ( "context" "encoding/json" "errors" "fmt" "net/http" "time" "velox-bot/internal/commands/fun/shared" "github.com/bwmarrin/discordgo" ) var Joke = &discordgo.ApplicationCommand{ Name: "joke", Description: "Get a random joke", Contexts: &[]discordgo.InteractionContextType{ discordgo.InteractionContextGuild, discordgo.InteractionContextBotDM, discordgo.InteractionContextPrivateChannel, }, Options: []*discordgo.ApplicationCommandOption{ { Type: discordgo.ApplicationCommandOptionString, Name: "type", Description: "The type of joke to get", Required: false, Choices: []*discordgo.ApplicationCommandOptionChoice{ { Name: "Programming", Value: "Programming", }, { Name: "Misc", Value: "Miscellaneous", }, { Name: "Dark", Value: "Dark", }, { Name: "Pun", Value: "Pun", }, { Name: "Spooky", Value: "Spooky", }, { Name: "Christmas", Value: "Christmas", }, }, }, }, } func JokeHandler(s *discordgo.Session, i *discordgo.InteractionCreate) { jokeType := "Any" if opts := i.ApplicationCommandData().Options; len(opts) > 0 { if v := opts[0].StringValue(); v != "" { jokeType = v } } if !isValidJokeType(jokeType) { jokeType = "Any" } ctx, cancel := context.WithTimeout(context.Background(), 6*time.Second) defer cancel() content, err := fetchJoke(ctx, jokeType) if err != nil { if i.GuildID != "" { shared.RespondEphemeral(s, i, "Failed to get joke.") } else { shared.Respond(s, i, "Failed to get joke.") } return } shared.Respond(s, i, content) } func isValidJokeType(t string) bool { switch t { case "Any", "Programming", "Misc", "Dark", "Pun", "Spooky", "Christmas": return true default: return false } } type jokeAPIResponse struct { Error bool `json:"error"` Message string `json:"message"` Type string `json:"type"` Joke string `json:"joke"` Setup string `json:"setup"` Delivery string `json:"delivery"` } func fetchJoke(ctx context.Context, jokeType string) (string, error) { const baseURL = "https://v2.jokeapi.dev/joke" req, err := http.NewRequestWithContext(ctx, http.MethodGet, fmt.Sprintf("%s/%s", baseURL, jokeType), nil) if err != nil { return "", err } resp, err := http.DefaultClient.Do(req) if err != nil { return "", err } defer resp.Body.Close() if resp.StatusCode < 200 || resp.StatusCode >= 300 { return "", fmt.Errorf("joke api status: %s", resp.Status) } var data jokeAPIResponse if err := json.NewDecoder(resp.Body).Decode(&data); err != nil { return "", err } if data.Error { if data.Message != "" { return "", errors.New(data.Message) } return "", errors.New("joke api returned error") } switch data.Type { case "single": if data.Joke == "" { return "", errors.New("empty joke") } return data.Joke, nil case "twopart": if data.Setup == "" && data.Delivery == "" { return "", errors.New("empty joke") } if data.Setup == "" { return data.Delivery, nil } if data.Delivery == "" { return data.Setup, nil } return data.Setup + "\n" + data.Delivery, nil default: return "", fmt.Errorf("unknown joke type: %q", data.Type) } }