package main import ( "encoding/json" "fmt" "io" "net/http" ) // AMOResponse is the structure returned by the AMO search endpoint. type AMOResponse struct { Count int `json:"count"` Results []Addon `json:"results"` } // Addon holds basic information from the search endpoint. type Addon struct { ID int `json:"id"` Name map[string]string `json:"name"` Slug string `json:"slug"` URL string `json:"url"` CurrentVersion CurrentVersion `json:"current_version,omitempty"` } // CurrentVersion holds details about the add-on's current version. type CurrentVersion struct { Files []AddonFile `json:"files"` File *AddonFile `json:"file"` } // AddonFile represents a file (typically the XPI) associated with an add-on. type AddonFile struct { URL string `json:"url"` } // fetchAddonDetails retrieves detailed information for a specific add-on, including file details. func fetchAddonDetails(slug string) (Addon, error) { apiURL := fmt.Sprintf("https://addons.mozilla.org/api/v5/addons/addon/%s/?include=files", slug) client := &http.Client{} req, err := http.NewRequest("GET", apiURL, nil) if err != nil { return Addon{}, err } req.Header.Set("User-Agent", "MyCustomBrowser/1.0") resp, err := client.Do(req) if err != nil { return Addon{}, err } defer resp.Body.Close() bodyBytes, err := io.ReadAll(resp.Body) if err != nil { return Addon{}, err } var addon Addon if err := json.Unmarshal(bodyBytes, &addon); err != nil { return Addon{}, err } return addon, nil }