package modrinth import ( "encoding/json" "fmt" "io" "net/http" "os" "time" ) const ( apiBase = "https://api.modrinth.com/v2" userAgent = "benson/2.0 (github.com/hachem-wtf/benson)" ) type File struct { URL string `json:"url"` Filename string `json:"filename"` Primary bool `json:"primary"` } type Version struct { ID string `json:"id"` VersionNumber string `json:"version_number"` DatePublished time.Time `json:"date_published"` GameVersions []string `json:"game_versions"` Loaders []string `json:"loaders"` Files []File `json:"files"` } type Client struct { http *http.Client } func NewClient() *Client { return &Client{http: &http.Client{Timeout: 30 * time.Second}} } func (c *Client) GetVersions(slug string) ([]Version, error) { url := fmt.Sprintf("%s/project/%s/version", apiBase, slug) req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return nil, err } req.Header.Set("User-Agent", userAgent) resp, err := c.http.Do(req) if err != nil { return nil, err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return nil, fmt.Errorf("modrinth: %s returned %s", slug, resp.Status) } var versions []Version if err := json.NewDecoder(resp.Body).Decode(&versions); err != nil { return nil, err } return versions, nil } func LatestVersion(versions []Version) *Version { if len(versions) == 0 { return nil } best := 0 for i := 1; i < len(versions); i++ { if versions[i].DatePublished.After(versions[best].DatePublished) { best = i } } return &versions[best] } func CompatibleVersion(versions []Version, mcVersion, loader string) *Version { var compatible []Version for _, v := range versions { if !contains(v.GameVersions, mcVersion) { continue } if loader != "" && !contains(v.Loaders, loader) { continue } compatible = append(compatible, v) } return LatestVersion(compatible) } func (c *Client) Download(url, dest string) error { req, err := http.NewRequest(http.MethodGet, url, nil) if err != nil { return err } req.Header.Set("User-Agent", userAgent) resp, err := c.http.Do(req) if err != nil { return err } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { return fmt.Errorf("download: %s returned %s", url, resp.Status) } f, err := os.Create(dest) if err != nil { return err } defer f.Close() _, err = io.Copy(f, resp.Body) return err } func contains(list []string, s string) bool { for _, item := range list { if item == s { return true } } return false }