Sergiusz Bazanski | 0581bbf | 2020-05-11 03:21:32 +0200 | [diff] [blame] | 1 | package modportal |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "encoding/json" |
| 6 | "fmt" |
| 7 | "io" |
| 8 | "net/http" |
| 9 | "net/url" |
| 10 | |
| 11 | "google.golang.org/grpc/codes" |
| 12 | "google.golang.org/grpc/status" |
| 13 | ) |
| 14 | |
| 15 | type Mod struct { |
| 16 | Name string `json:"name"` |
| 17 | Releases []Release `json:"releases"` |
| 18 | } |
| 19 | |
| 20 | type Release struct { |
| 21 | DownloadURL string `json:"download_url"` |
| 22 | FileName string `json:"file_name"` |
| 23 | Info InfoJSON `json:"info_json"` |
| 24 | ReleasedAt string `json:"released_at"` |
| 25 | SHA1 string `json:"sha1"` |
| 26 | Version string `json:"version"` |
| 27 | } |
| 28 | |
| 29 | type InfoJSON struct { |
| 30 | Dependencies []string `json:"dependencies"` |
| 31 | FactorioVersion string `json:"factorio_json"` |
| 32 | } |
| 33 | |
| 34 | func GetMod(ctx context.Context, name string) (*Mod, error) { |
| 35 | url := fmt.Sprintf("https://mods.factorio.com/api/mods/%s", url.PathEscape(name)) |
| 36 | |
| 37 | req, err := http.NewRequest("GET", url, nil) |
| 38 | if err != nil { |
| 39 | return nil, status.Errorf(codes.Internal, "NewRequest(%q): %v", url, err) |
| 40 | } |
| 41 | |
| 42 | req = req.WithContext(ctx) |
| 43 | res, err := http.DefaultClient.Do(req) |
| 44 | if err != nil { |
| 45 | return nil, status.Errorf(codes.Unavailable, "Do(req{%q}): %v", url, err) |
| 46 | } |
| 47 | defer res.Body.Close() |
| 48 | |
| 49 | if res.StatusCode != 200 { |
| 50 | return nil, status.Errorf(codes.Unavailable, "mod portal responded with code: %d", res.StatusCode) |
| 51 | } |
| 52 | |
| 53 | mod := &Mod{} |
| 54 | err = json.NewDecoder(res.Body).Decode(mod) |
| 55 | if err != nil { |
| 56 | return nil, status.Errorf(codes.Internal, "could not decode mod portal JSON: %v", err) |
| 57 | } |
| 58 | |
| 59 | return mod, nil |
| 60 | } |
| 61 | |
| 62 | func (m *Mod) ReleaseBySHA1(sha1 string) *Release { |
| 63 | for _, r := range m.Releases { |
| 64 | if r.SHA1 == sha1 { |
| 65 | return &r |
| 66 | } |
| 67 | } |
| 68 | return nil |
| 69 | } |
| 70 | |
| 71 | func (m *Mod) ReleaseByVersion(version string) *Release { |
| 72 | for _, r := range m.Releases { |
| 73 | if r.Version == version { |
| 74 | return &r |
| 75 | } |
| 76 | } |
| 77 | return nil |
| 78 | } |
| 79 | |
| 80 | func (r *Release) Download(ctx context.Context, username, token string) (io.ReadCloser, error) { |
| 81 | url := fmt.Sprintf("https://mods.factorio.com/%s?username=%s&token=%s", r.DownloadURL, username, token) |
| 82 | req, err := http.NewRequest("GET", url, nil) |
| 83 | if err != nil { |
| 84 | return nil, status.Errorf(codes.Internal, "NewRequest(%q): %v", url, err) |
| 85 | } |
| 86 | |
| 87 | req = req.WithContext(ctx) |
| 88 | res, err := http.DefaultClient.Do(req) |
| 89 | if err != nil { |
| 90 | return nil, status.Errorf(codes.Unavailable, "Do(req{%q}): %v", url, err) |
| 91 | } |
| 92 | if res.StatusCode != 200 { |
| 93 | res.Body.Close() |
| 94 | return nil, status.Errorf(codes.Unavailable, "mod portal responded with code: %d", res.StatusCode) |
| 95 | } |
| 96 | |
| 97 | return res.Body, err |
| 98 | } |