package commands import ( "bytes" "encoding/json" "fmt" "log" "net/http" "strconv" "strings" "github.com/bwmarrin/discordgo" ) func newIdeashowCommand() *Cmd { return &Cmd{ Command: &discordgo.ApplicationCommand{ Name: "ideashow", Description: "Show your ideas with interactive controls", }, Handler: handleIdeashow, } } func handleIdeashow(s *discordgo.Session, ic *discordgo.InteractionCreate) { user := ic.User if user == nil { user = ic.Member.User } cfg, ok, err := GetUserConfig(user.ID) if err != nil { respondError(s, ic, "Error retrieving configuration.") return } if !ok { respondError(s, ic, "You are not logged in. Use `/setup` first.") return } client := &http.Client{} req, _ := http.NewRequest("GET", cfg.InstanceURL+"/api/ideas/list", nil) req.Header.Set("API-Authentication", cfg.APIKey) resp, err := client.Do(req) if err != nil { respondError(s, ic, "Failed to connect to Thoughtful instance: "+err.Error()) return } defer resp.Body.Close() if resp.StatusCode == 401 { respondError(s, ic, "Unauthorized. Check your API Key.") return } if resp.StatusCode != 200 { respondError(s, ic, fmt.Sprintf("API Error: %d", resp.StatusCode)) return } var result ideaListResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { respondError(s, ic, "Failed to parse API response.") return } if !result.Success { respondError(s, ic, "API reported failure.") return } if len(result.Ideas) == 0 { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "No ideas found.", }, }) return } // Show first page page := 0 embed, components := buildIdeasEmbed(result.Ideas, page) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{embed}, Components: components, }, }) } func buildIdeasEmbed(ideas []Idea, page int) (*discordgo.MessageEmbed, []discordgo.MessageComponent) { perPage := 4 start := page * perPage end := start + perPage if end > len(ideas) { end = len(ideas) } embed := &discordgo.MessageEmbed{ Title: "Your Ideas", Color: 0x00ff00, Fields: []*discordgo.MessageEmbedField{}, Footer: &discordgo.MessageEmbedFooter{ Text: fmt.Sprintf("Page %d", page+1), }, } for i := start; i < end; i++ { index := i + 1 idea := ideas[i] field := &discordgo.MessageEmbedField{ Name: fmt.Sprintf("%d. %s", index, idea.Title), Value: idea.Description, Inline: false, } embed.Fields = append(embed.Fields, field) } // Components components := []discordgo.MessageComponent{ discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ discordgo.Button{ Label: "Prev", Style: discordgo.SecondaryButton, CustomID: fmt.Sprintf("ideashow_prev_%d", page), Disabled: page == 0, }, discordgo.Button{ Label: "Next", Style: discordgo.SecondaryButton, CustomID: fmt.Sprintf("ideashow_next_%d", page), Disabled: end >= len(ideas), }, }, }, } if end-start > 0 { row := discordgo.ActionsRow{ Components: []discordgo.MessageComponent{}, } for i := start; i < end; i++ { local := i - start + 1 row.Components = append(row.Components, discordgo.Button{ Label: strconv.Itoa(local), Style: discordgo.PrimaryButton, CustomID: fmt.Sprintf("ideashow_select_%d", i), }) } components = append(components, row) } return embed, components } func buildIdeaEmbed(idea Idea) (*discordgo.MessageEmbed, []discordgo.MessageComponent) { embed := &discordgo.MessageEmbed{ Title: idea.Title, Description: idea.Description, Color: 0x00ff00, Fields: []*discordgo.MessageEmbedField{}, } if len(idea.Tags) > 0 { embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{ Name: "Tags", Value: strings.Join(idea.Tags, ", "), Inline: true, }) } if len(idea.Todos) > 0 { todoText := "" for _, todo := range idea.Todos { todoText += fmt.Sprintf("**%s**\n", todo.Title) for _, item := range todo.Items { status := "☐" if item.Completed { status = "☑" } todoText += fmt.Sprintf("%s %s\n", status, item.Text) } todoText += "\n" } embed.Fields = append(embed.Fields, &discordgo.MessageEmbedField{ Name: "Todos", Value: todoText, Inline: false, }) } components := []discordgo.MessageComponent{ discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ discordgo.Button{ Label: "Add Todo", Style: discordgo.SuccessButton, CustomID: fmt.Sprintf("ideashow_add_todo_%s", idea.ID), }, discordgo.Button{ Label: "Check Todo", Style: discordgo.PrimaryButton, CustomID: fmt.Sprintf("ideashow_check_todo_%s", idea.ID), }, }, }, } return embed, components } func buildToggleEmbed(idea Idea) (*discordgo.MessageEmbed, []discordgo.MessageComponent) { embed := &discordgo.MessageEmbed{ Title: "Toggle Todo Items", Description: fmt.Sprintf("**%s**\n%s", idea.Title, idea.Description), Color: 0x0000ff, } components := []discordgo.MessageComponent{} row := discordgo.ActionsRow{ Components: []discordgo.MessageComponent{}, } for todoIndex, todo := range idea.Todos { for itemIndex, item := range todo.Items { label := item.Text if len(label) > 80 { label = label[:77] + "..." } style := discordgo.SecondaryButton if item.Completed { style = discordgo.SuccessButton } row.Components = append(row.Components, discordgo.Button{ Label: label, Style: style, CustomID: fmt.Sprintf("ideashow_toggle_%s_%d_%d", idea.ID, todoIndex, itemIndex), }) if len(row.Components) == 5 { components = append(components, row) row = discordgo.ActionsRow{ Components: []discordgo.MessageComponent{}, } } } } if len(row.Components) > 0 { components = append(components, row) } // Add back button components = append(components, discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ discordgo.Button{ Label: "Back", Style: discordgo.SecondaryButton, CustomID: fmt.Sprintf("ideashow_back_%s", idea.ID), }, }, }) return embed, components } func toggleTodoItem(cfg UserConfig, ideaID string, todoIndex, itemIndex int) (*Idea, error) { // First, fetch the idea client := &http.Client{} req, _ := http.NewRequest("GET", cfg.InstanceURL+"/api/ideas/list", nil) req.Header.Set("API-Authentication", cfg.APIKey) resp, err := client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { return nil, fmt.Errorf("API error: %d", resp.StatusCode) } var result ideaListResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, err } var idea *Idea for i := range result.Ideas { if result.Ideas[i].ID == ideaID { idea = &result.Ideas[i] break } } if idea == nil { return nil, fmt.Errorf("idea not found") } if todoIndex < 0 || todoIndex >= len(idea.Todos) { return nil, fmt.Errorf("invalid todo index") } todo := &idea.Todos[todoIndex] if itemIndex < 0 || itemIndex >= len(todo.Items) { return nil, fmt.Errorf("invalid item index") } item := &todo.Items[itemIndex] item.Completed = !item.Completed // Update via PUT updateURL := cfg.InstanceURL + "/api/ideas/update/" updatePayload := map[string]interface{}{ "id": idea.ID, "todos": idea.Todos, } b, _ := json.Marshal(updatePayload) req, _ = http.NewRequest("PUT", updateURL, bytes.NewReader(b)) req.Header.Set("API-Authentication", cfg.APIKey) req.Header.Set("Content-Type", "application/json") resp, err = client.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != 200 { log.Printf("[IDEASHOW] Update failed with status: %d", resp.StatusCode) return nil, fmt.Errorf("update failed: %d", resp.StatusCode) } return idea, nil } func handleIdeashowComponent(s *discordgo.Session, ic *discordgo.InteractionCreate) { data := ic.MessageComponentData() user := ic.User if user == nil { user = ic.Member.User } // Refetch ideas cfg, ok, err := GetUserConfig(user.ID) if err != nil { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Error retrieving configuration.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } if !ok { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "You are not logged in.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } client := &http.Client{} req, _ := http.NewRequest("GET", cfg.InstanceURL+"/api/ideas/list", nil) req.Header.Set("API-Authentication", cfg.APIKey) resp, err := client.Do(req) if err != nil { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Failed to connect.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } defer resp.Body.Close() if resp.StatusCode != 200 { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "API Error.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } var result ideaListResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Parse error.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } if !result.Success || len(result.Ideas) == 0 { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "No ideas.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } parts := strings.Split(data.CustomID, "_") if len(parts) < 2 { return } switch parts[1] { case "prev": if len(parts) < 3 { return } page, err := strconv.Atoi(parts[2]) if err != nil { return } newPage := page - 1 if newPage < 0 { return } embed, components := buildIdeasEmbed(result.Ideas, newPage) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseUpdateMessage, Data: &discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{embed}, Components: components, }, }) case "next": if len(parts) < 3 { return } page, err := strconv.Atoi(parts[2]) if err != nil { return } newPage := page + 1 perPage := 4 if newPage*perPage >= len(result.Ideas) { return } embed, components := buildIdeasEmbed(result.Ideas, newPage) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseUpdateMessage, Data: &discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{embed}, Components: components, }, }) case "select": if len(parts) < 3 { return } index, err := strconv.Atoi(parts[2]) if err != nil || index < 0 || index >= len(result.Ideas) { return } idea := result.Ideas[index] embed, components := buildIdeaEmbed(idea) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseUpdateMessage, Data: &discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{embed}, Components: components, }, }) case "add": if len(parts) < 4 { return } sub := parts[2] var ideaID string if sub == "todo" { ideaID := parts[3] // find idea var idea *Idea for i := range result.Ideas { if result.Ideas[i].ID == ideaID { idea = &result.Ideas[i] break } } if idea == nil { return } // If there are existing todos, ask whether to create new or add to existing if len(idea.Todos) > 0 { // Build components: New Todo button + buttons for existing todos (max 5) rows := []discordgo.MessageComponent{} rows = append(rows, discordgo.ActionsRow{Components: []discordgo.MessageComponent{ discordgo.Button{Label: "New Todo", Style: discordgo.PrimaryButton, CustomID: fmt.Sprintf("ideashow_add_new_%s", ideaID)}, }}) // Add a row of existing todo buttons row := discordgo.ActionsRow{Components: []discordgo.MessageComponent{}} for ti := 0; ti < len(idea.Todos) && ti < 5; ti++ { label := idea.Todos[ti].Title if len(label) > 80 { label = label[:77] + "..." } row.Components = append(row.Components, discordgo.Button{Label: label, Style: discordgo.SecondaryButton, CustomID: fmt.Sprintf("ideashow_add_to_%s_%d", ideaID, ti)}) } rows = append(rows, row) // Update the message to show choice s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseUpdateMessage, Data: &discordgo.InteractionResponseData{ Content: "Choose to create a new todo list or add to an existing one:", Components: rows, }, }) return } // No existing todos; open modal for new todo ideaID = parts[3] modal := &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseModal, Data: &discordgo.InteractionResponseData{ CustomID: "ideashow_add_todo_modal_" + ideaID, Title: "Add Todo", Components: []discordgo.MessageComponent{ discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ discordgo.TextInput{ CustomID: "title", Label: "Todo Title", Style: discordgo.TextInputShort, Placeholder: "Enter todo title", Required: true, MaxLength: 200, }, }, }, discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ discordgo.TextInput{ CustomID: "items", Label: "Todo Items (one per line)", Style: discordgo.TextInputParagraph, Placeholder: "Item 1\nItem 2\nItem 3", Required: true, MaxLength: 4000, }, }, }, }, }, } err := s.InteractionRespond(ic.Interaction, modal) if err != nil { log.Printf("[IDEASHOW] Failed to open modal: %v", err) return } return } else if sub == "new" { // pattern: ideashow_add_new_ if len(parts) < 4 { return } ideaID = parts[3] modal := &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseModal, Data: &discordgo.InteractionResponseData{ CustomID: "ideashow_add_todo_modal_" + ideaID, Title: "Add Todo", Components: []discordgo.MessageComponent{ discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ discordgo.TextInput{CustomID: "title", Label: "Todo Title", Style: discordgo.TextInputShort, Placeholder: "Enter todo title", Required: true, MaxLength: 200}, }, }, discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ discordgo.TextInput{CustomID: "items", Label: "Todo Items (one per line)", Style: discordgo.TextInputParagraph, Placeholder: "Item 1\nItem 2\nItem 3", Required: true, MaxLength: 4000}, }, }, }, }, } err := s.InteractionRespond(ic.Interaction, modal) if err != nil { log.Printf("[IDEASHOW] Failed to open modal: %v", err) return } return } else if sub == "to" { // pattern: ideashow_add_to__ if len(parts) < 5 { return } ideaID = parts[3] todoIndex := parts[4] modal := &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseModal, Data: &discordgo.InteractionResponseData{ CustomID: fmt.Sprintf("ideashow_add_todo_modal_%s_%s", ideaID, todoIndex), Title: "Add Items to Todo", Components: []discordgo.MessageComponent{ discordgo.ActionsRow{ Components: []discordgo.MessageComponent{ discordgo.TextInput{CustomID: "items", Label: "Todo Items (one per line)", Style: discordgo.TextInputParagraph, Placeholder: "Item 1\nItem 2\nItem 3", Required: true, MaxLength: 4000}, }, }, }, }, } err := s.InteractionRespond(ic.Interaction, modal) if err != nil { log.Printf("[IDEASHOW] Failed to open modal for existing todo: %v", err) return } return } case "check": if len(parts) < 4 || parts[2] != "todo" { return } ideaID := parts[3] // Find the idea var idea *Idea for i := range result.Ideas { if result.Ideas[i].ID == ideaID { idea = &result.Ideas[i] break } } if idea == nil { return } embed, components := buildToggleEmbed(*idea) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseUpdateMessage, Data: &discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{embed}, Components: components, }, }) case "toggle": if len(parts) < 5 { return } ideaID := parts[2] todoIndex, err1 := strconv.Atoi(parts[3]) itemIndex, err2 := strconv.Atoi(parts[4]) if err1 != nil || err2 != nil { return } // Update the item idea, err := toggleTodoItem(cfg, ideaID, todoIndex, itemIndex) if err != nil { log.Printf("[IDEASHOW] Failed to toggle item: %v", err) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Failed to update: " + err.Error(), Flags: discordgo.MessageFlagsEphemeral, }, }) return } embed, components := buildToggleEmbed(*idea) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseUpdateMessage, Data: &discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{embed}, Components: components, }, }) case "back": if len(parts) < 3 { return } ideaID := parts[2] var idea *Idea for i := range result.Ideas { if result.Ideas[i].ID == ideaID { idea = &result.Ideas[i] break } } if idea == nil { return } embed, components := buildIdeaEmbed(*idea) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseUpdateMessage, Data: &discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{embed}, Components: components, }, }) } } func handleAddTodoSubmit(s *discordgo.Session, ic *discordgo.InteractionCreate) { data := ic.ModalSubmitData() user := ic.User if user == nil { user = ic.Member.User } parts := strings.Split(data.CustomID, "_") if len(parts) < 5 { return } ideaID := parts[4] // ideashow_add_todo_modal_ (optionally _) targetTodoIndex := -1 if len(parts) >= 6 { if idx, err := strconv.Atoi(parts[5]); err == nil { targetTodoIndex = idx } } cfg, ok, err := GetUserConfig(user.ID) if err != nil { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Config error.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } if !ok { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Not logged in.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } // Fetch idea client := &http.Client{} req, _ := http.NewRequest("GET", cfg.InstanceURL+"/api/ideas/list", nil) req.Header.Set("API-Authentication", cfg.APIKey) resp, err := client.Do(req) if err != nil { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Fetch error.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } defer resp.Body.Close() if resp.StatusCode != 200 { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "API error.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } var result ideaListResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Parse error.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } var idea *Idea for i := range result.Ideas { if result.Ideas[i].ID == ideaID { idea = &result.Ideas[i] break } } if idea == nil { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Idea not found.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } // Parse modal data var title, itemsText string for _, comp := range data.Components { row := comp.(*discordgo.ActionsRow) for _, c := range row.Components { if input, ok := c.(*discordgo.TextInput); ok { if input.CustomID == "title" { title = input.Value } else if input.CustomID == "items" { itemsText = input.Value } } } } if targetTodoIndex >= 0 { // Adding items to existing todo: only itemsText required if itemsText == "" { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Invalid input.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } } else { if title == "" || itemsText == "" { s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Invalid input.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } } // Parse items lines := strings.Split(itemsText, "\n") var items []TodoItem for _, line := range lines { line = strings.TrimSpace(line) if line != "" { items = append(items, TodoItem{ ID: fmt.Sprintf("item_%d", len(items)), Text: line, Completed: false, }) } } if targetTodoIndex >= 0 { // Append items to existing todo if targetTodoIndex < 0 || targetTodoIndex >= len(idea.Todos) { log.Printf("[IDEASHOW] Invalid target todo index: %d", targetTodoIndex) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Invalid todo selection.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } base := &idea.Todos[targetTodoIndex] for _, it := range items { it.ID = fmt.Sprintf("item_%d", len(base.Items)) base.Items = append(base.Items, it) } } else { // Add todo newTodo := Todo{ ID: fmt.Sprintf("todo_%d", len(idea.Todos)), Title: title, Items: items, } idea.Todos = append(idea.Todos, newTodo) } // Update updateURL := cfg.InstanceURL + "/api/ideas/update/" updatePayload := map[string]interface{}{ "id": idea.ID, "todos": idea.Todos, } b, _ := json.Marshal(updatePayload) req, _ = http.NewRequest("PUT", updateURL, bytes.NewReader(b)) req.Header.Set("API-Authentication", cfg.APIKey) req.Header.Set("Content-Type", "application/json") resp, err = client.Do(req) if err != nil { log.Printf("[IDEASHOW] Add todo request error: %v", err) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Update error.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } defer resp.Body.Close() if resp.StatusCode != 200 { log.Printf("[IDEASHOW] Add todo update failed with status: %d", resp.StatusCode) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseChannelMessageWithSource, Data: &discordgo.InteractionResponseData{ Content: "Update failed.", Flags: discordgo.MessageFlagsEphemeral, }, }) return } // Respond with updated embed embed, components := buildIdeaEmbed(*idea) s.InteractionRespond(ic.Interaction, &discordgo.InteractionResponse{ Type: discordgo.InteractionResponseUpdateMessage, Data: &discordgo.InteractionResponseData{ Embeds: []*discordgo.MessageEmbed{embed}, Components: components, }, }) }