hachem.wtf
aboutsummaryrefslogtreecommitdiff
path: root/internal/modrinth/modrinth.go
blob: 8060f3b9050664719db9bcd120e71353280a447b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
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
}