blob: 5e507fd97fad971520d8de2725c8aa2b6d494e44 [file] [log] [blame]
package modportal
import (
"context"
"encoding/json"
"fmt"
"io"
"net/http"
"net/url"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
type Mod struct {
Name string `json:"name"`
Releases []Release `json:"releases"`
}
type Release struct {
DownloadURL string `json:"download_url"`
FileName string `json:"file_name"`
Info InfoJSON `json:"info_json"`
ReleasedAt string `json:"released_at"`
SHA1 string `json:"sha1"`
Version string `json:"version"`
}
type InfoJSON struct {
Dependencies []string `json:"dependencies"`
FactorioVersion string `json:"factorio_json"`
}
func GetMod(ctx context.Context, name string) (*Mod, error) {
url := fmt.Sprintf("https://mods.factorio.com/api/mods/%s", url.PathEscape(name))
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, status.Errorf(codes.Internal, "NewRequest(%q): %v", url, err)
}
req = req.WithContext(ctx)
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, status.Errorf(codes.Unavailable, "Do(req{%q}): %v", url, err)
}
defer res.Body.Close()
if res.StatusCode != 200 {
return nil, status.Errorf(codes.Unavailable, "mod portal responded with code: %d", res.StatusCode)
}
mod := &Mod{}
err = json.NewDecoder(res.Body).Decode(mod)
if err != nil {
return nil, status.Errorf(codes.Internal, "could not decode mod portal JSON: %v", err)
}
return mod, nil
}
func (m *Mod) ReleaseBySHA1(sha1 string) *Release {
for _, r := range m.Releases {
if r.SHA1 == sha1 {
return &r
}
}
return nil
}
func (m *Mod) ReleaseByVersion(version string) *Release {
for _, r := range m.Releases {
if r.Version == version {
return &r
}
}
return nil
}
func (r *Release) Download(ctx context.Context, username, token string) (io.ReadCloser, error) {
url := fmt.Sprintf("https://mods.factorio.com/%s?username=%s&token=%s", r.DownloadURL, username, token)
req, err := http.NewRequest("GET", url, nil)
if err != nil {
return nil, status.Errorf(codes.Internal, "NewRequest(%q): %v", url, err)
}
req = req.WithContext(ctx)
res, err := http.DefaultClient.Do(req)
if err != nil {
return nil, status.Errorf(codes.Unavailable, "Do(req{%q}): %v", url, err)
}
if res.StatusCode != 200 {
res.Body.Close()
return nil, status.Errorf(codes.Unavailable, "mod portal responded with code: %d", res.StatusCode)
}
return res.Body, err
}