package api import ( "cmp" "context" "net/http" "slices" "strconv" "github.com/go-chi/chi/v5" ) // noSuchChat answers a request naming a chat the bot does not have. const noSuchChat = "no such chat" // chat is how the API shows a chat. The bot talks to people only one to // one, so its chats are its contacts, and a chat's id is its contact's. type chat struct { ID int64 `json:"id"` DisplayName string `json:"display_name"` ContactDeleted bool `json:"contact_deleted"` } // chats returns the bot's chats, ordered by id. GET /api/v1/chats // answers with this list, and chatID accepts only an id in it. func (h *handlers) chats(ctx context.Context) ([]chat, error) { contacts, err := h.client.Contacts(ctx, h.userID) if err != nil { return nil, err } chats := make([]chat, 0, len(contacts)) for _, c := range contacts { chats = append(chats, chat{ ID: c.ContactID, DisplayName: c.Profile.DisplayName, ContactDeleted: c.Deleted(), }) } slices.SortFunc(chats, func(a, b chat) int { return cmp.Compare(a.ID, b.ID) }) return chats, nil } // handleChats lists the bot's chats. func (h *handlers) handleChats() http.HandlerFunc { type response struct { Chats []chat `json:"chats"` } return func(w http.ResponseWriter, r *http.Request) { chats, err := h.chats(r.Context()) if err != nil { h.log.Error("listing the chats", "error", err) h.respondError(w, http.StatusInternalServerError, "the chats could not be read") return } h.respond(w, http.StatusOK, response{Chats: chats}) } } // chatID returns the chat id in the request's path, if it is one of the // bot's chats. Otherwise it answers the request itself, 404, or 500 if // the chats could not be read, and returns false. // // The id is looked up rather than passed to the chat client, which also // keeps contact records that are not chats, such as the bot's own // profile, and would read or send in them. func (h *handlers) chatID(w http.ResponseWriter, r *http.Request) (int64, bool) { id, err := strconv.ParseInt(chi.URLParam(r, "id"), 10, 64) if err != nil { h.respondError(w, http.StatusNotFound, noSuchChat) return 0, false } chats, err := h.chats(r.Context()) if err != nil { h.log.Error("looking up a chat", "error", err) h.respondError(w, http.StatusInternalServerError, "the chats could not be read") return 0, false } if !slices.ContainsFunc(chats, func(c chat) bool { return c.ID == id }) { h.respondError(w, http.StatusNotFound, noSuchChat) return 0, false } return id, true }