z_mirror/fmm

git clone https://git.raiguard.me/z_mirror/fmm

Caleb HeuerFetch dependencies from portal8b551e4

predep-parsing
3.8 KiB170 linesraw
1package main
2
3import (
4	"bytes"
5	"encoding/json"
6	"errors"
7	"fmt"
8	"io"
9	"mime/multipart"
10	"net/http"
11	"os"
12	"path"
13
14	"github.com/cavaliergopher/grab/v3"
15)
16
17const initUploadUrl string = "https://mods.factorio.com/api/v2/mods/releases/init_upload"
18
19func portalDownloadMod(mod Dependency, dir *Dir) error {
20	url := fmt.Sprintf("https://mods.factorio.com/api/mods/%s/full", mod.Ident.Name)
21	res, err := http.Get(url)
22	if err != nil {
23		return err
24	}
25
26	body, err := io.ReadAll(res.Body)
27	if err != nil {
28		return err
29	}
30	res.Body.Close()
31
32	var unmarshaled PortalFullMod
33	err = json.Unmarshal(body, &unmarshaled)
34	if err != nil {
35		return err
36	}
37
38	// Check releases from newest to oldest and find the first matching one
39	var release *PortalModRelease
40	for i := len(unmarshaled.Releases) - 1; i >= 0; i -= 1 {
41		toCheck := unmarshaled.Releases[i]
42		if mod.Test(&ModIdent{mod.Ident.Name, &toCheck.Version}) {
43			release = &toCheck
44			break
45		}
46	}
47
48	if release == nil {
49		return errors.New(fmt.Sprintf("%s was not found on the mod portal",
50			mod.Ident.toString()))
51	}
52
53	downloadUrl := fmt.Sprintf("https://mods.factorio.com/%s?username=%s&token=%s",
54		release.DownloadUrl, downloadUsername, downloadToken)
55	outPath := path.Join(modsDir, release.FileName)
56
57	fmt.Printf("Downloading %s\n", release.FileName)
58	if _, err := grab.Get(outPath, downloadUrl); err != nil {
59		return err
60	}
61
62	return nil
63}
64
65func portalUploadMod(filepath string) error {
66	// Init upload
67	initUploadBody := &bytes.Buffer{}
68	w := multipart.NewWriter(initUploadBody)
69	ident := newModIdent(path.Base(filepath))
70	w.WriteField("mod", ident.Name)
71	w.Close()
72	req, err := http.NewRequest("POST", initUploadUrl, initUploadBody)
73	if err != nil {
74		return err
75	}
76	req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", apiKey))
77	req.Header.Set("Content-Type", w.FormDataContentType())
78	res, err := http.DefaultClient.Do(req)
79	if err != nil {
80		return err
81	}
82	var decoded ModInitUploadRes
83	err = json.NewDecoder(res.Body).Decode(&decoded)
84	if err != nil {
85		return err
86	}
87	if res.StatusCode != http.StatusOK {
88		return errors.New(*decoded.Message)
89	}
90	defer res.Body.Close()
91
92	// Open file
93	file, err := os.Open(filepath)
94	if err != nil {
95		return err
96	}
97	defer file.Close()
98
99	fmt.Printf("Uploading %s\n", filepath)
100
101	// Upload file
102	uploadBody := &bytes.Buffer{}
103	w = multipart.NewWriter(uploadBody)
104	part, err := w.CreateFormFile("file", path.Base(file.Name()))
105	io.Copy(part, file)
106	w.Close()
107
108	r, err := http.NewRequest("POST", *decoded.UploadUrl, uploadBody)
109	if err != nil {
110		return err
111	}
112	r.Header.Add("Content-Type", w.FormDataContentType())
113	http.DefaultClient.Do(r)
114
115	return nil
116}
117
118func portalGetDependencies(mod ModIdent) ([]Dependency, error) {
119	fmt.Println("Fetching dependencies for", mod.toString())
120	url := fmt.Sprintf("https://mods.factorio.com/api/mods/%s/full", mod.Name)
121	res, err := http.Get(url)
122	if err != nil {
123		return nil, err
124	}
125	if res.StatusCode != http.StatusOK {
126		return nil, errors.New(fmt.Sprintf("%s was not found on the mod portal", mod.toString()))
127	}
128
129	body, err := io.ReadAll(res.Body)
130	if err != nil {
131		return nil, err
132	}
133	res.Body.Close()
134
135	var unmarshaled PortalFullMod
136	err = json.Unmarshal(body, &unmarshaled)
137	if err != nil {
138		return nil, err
139	}
140
141	version := mod.Version
142	if version != nil {
143		for _, release := range unmarshaled.Releases {
144			if release.Version == *version {
145				return release.InfoJson.Dependencies, nil
146			}
147		}
148		return nil, nil
149	}
150
151	return unmarshaled.Releases[len(unmarshaled.Releases)-1].InfoJson.Dependencies, nil
152}
153
154type ModInitUploadRes struct {
155	UploadUrl *string `json:"upload_url"`
156	Message   *string // When an error occurs
157}
158
159type PortalFullMod struct {
160	Name     string
161	Releases []PortalModRelease
162	Title    string
163}
164
165type PortalModRelease struct {
166	DownloadUrl string   `json:"download_url"`
167	FileName    string   `json:"file_name"`
168	InfoJson    InfoJson `json:"info_json"`
169	Version     Version
170}