Documentation
¶
Overview ¶
Package smapiweb is a client for the web services of the Stardew Valley modding toolchain: smapi.io's mod update API and log parser, the SMAPI compatibility list, and the StardewModDataset.
It keeps no state and makes no policy: no cache, no logging, no backoff between calls. It sends requests, bounds what it reads, and parses leniently, so callers decide what to remember and when to ask again.
Index ¶
- Constants
- Variables
- type Client
- func (c *Client) CompatList(ctx context.Context) (CompatIndex, error)
- func (c *Client) Index(ctx context.Context) (map[string][]Ref, error)
- func (c *Client) Page(ctx context.Context, id int) (Page, error)
- func (c *Client) Updates(ctx context.Context, req UpdateRequest) ([]UpdateResult, error)
- func (c *Client) UploadLog(ctx context.Context, text string, maxBytes int) (string, error)
- type CompatEntry
- type CompatIndex
- type Dependency
- type File
- type InstalledMod
- type Mod
- type Page
- type Ref
- type Update
- type UpdateRequest
- type UpdateResult
Constants ¶
const ( StatusOK = "ok" StatusOptional = "optional" StatusUnofficial = "unofficial" StatusBroken = "broken" StatusObsolete = "obsolete" StatusAbandoned = "abandoned" )
Variables ¶
var ( // ErrEmptyLog is UploadLog's answer to text with nothing in it. No request is sent. ErrEmptyLog = errors.New("the log is empty") // ErrLogTooLarge is UploadLog's answer to text over maxBytes. No request is sent. ErrLogTooLarge = errors.New("the log is too large to upload") // ErrLogRejected means the parser answered 200 with its page instead of a redirect, which is how it refuses a log. ErrLogRejected = errors.New("smapi.io did not accept the log") // ErrNoLogLink means the parser redirected somewhere that is not a /log/<id> page. ErrNoLogLink = errors.New("smapi.io did not return a log link") )
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// HTTP is the client every request goes through; nil means http.DefaultClient. UploadLog adds httpkit.NoFollow
// on a copy, so the client is never modified.
HTTP *http.Client
// UpdatesURL is the mod update API; "" means smapi.io's v4.0.0 endpoint.
UpdatesURL string
// CompatURL is the compatibility list; "" means SmapiCompatibilityList's data/mods.jsonc on its develop branch.
CompatURL string
// IndexURL is the dataset's page-by-UniqueID index; "" means StardewModDataset's on its main branch.
IndexURL string
// PageBase is the folder holding the dataset's Nexus pages; "" means StardewModDataset's on its main branch.
PageBase string
// LogURL is the log parser; "" means smapi.io's.
LogURL string
}
Client talks to smapi.io and the two GitHub-hosted datasets. The zero value uses the public endpoints and http.DefaultClient. It holds no state, so one Client is safe for concurrent use and a copy is as good as the original.
func (*Client) CompatList ¶
func (c *Client) CompatList(ctx context.Context) (CompatIndex, error)
CompatList fetches and parses the SMAPI compatibility list. It accepts every shape the list has been published in: a bare array, {"mods"} or {"data"}, with the status nested, flat or a string.
func (*Client) Index ¶
Index returns the dataset's index of which pages ship which mod, keyed by lowercased UniqueID. Entries that are not "Site:number" are skipped; an index with no usable entry is an error.
func (*Client) Page ¶
Page returns Nexus mod page id's files. A page the dataset lacks is an empty Page and a nil error: the dataset answered, it just does not know the page.
func (*Client) Updates ¶
func (c *Client) Updates(ctx context.Context, req UpdateRequest) ([]UpdateResult, error)
Updates asks about req.Mods in batches of 100, four at a time, and asks a failed batch once more in two halves. Results are in request order, one per mod, and each result's ID is the request's ID, not the API's casing. A mod whose batch was answered is Known (a mod the API does not list is Known with no Suggested); a mod whose batch failed, or was not sent because an earlier batch failed, has only its ID. err is nil when every batch was answered; otherwise it is errors.Join of the failed batches' errors in batch order, so a caller that needs one error takes the first.
func (*Client) UploadLog ¶
UploadLog posts text to the SMAPI log parser as the form field input and returns the public link, https://<host>/log/<id>. Success is a redirect to /log/<id>; the parser answers a failed or empty upload with HTTP 200 and its page, which is ErrLogRejected. Any other status is a *httpkit.StatusError. maxBytes caps len(text); 0 means no cap. The link is rebuilt from the redirect's scheme and host with the id path-escaped.
type CompatEntry ¶
type CompatEntry struct {
Status string `json:"status"`
Summary string `json:"summary"`
BrokeIn string `json:"brokeIn"`
UnofficialURL string `json:"unofficialUrl"`
// UnofficialVersion is the version of the unofficial update, so an install at or past it is not flagged.
UnofficialVersion string `json:"unofficialVersion"`
Replacement string `json:"replacement"`
}
CompatEntry is one SMAPI compatibility-list row, keyed later by UniqueID and Nexus id.
func (CompatEntry) BrokenOn ¶
func (e CompatEntry) BrokenOn(gameVersion string) bool
BrokenOn reports whether a broken entry applies to this Stardew Valley version. A mod that broke in a later game version (a beta the player does not run) is not broken for them; a break by anything else (SMAPI, Harmony), or an unknown version on either side, counts.
type CompatIndex ¶
type CompatIndex struct {
ByID map[string]CompatEntry `json:"ByID"`
ByNexus map[int]CompatEntry `json:"ByNexus"`
// NexusToID and GitHubToID (repo lowercased) name the UniqueID a Nexus page or GitHub repository publishes, which
// ties one mod's pages on different sites together.
NexusToID map[int]string `json:"NexusToID"`
GitHubToID map[string]string `json:"GitHubToID"`
}
CompatIndex maps UniqueID (lowercased) and Nexus page id onto the same entry.
func (CompatIndex) Lookup ¶
func (idx CompatIndex) Lookup(uniqueID string, nexusID int) (CompatEntry, bool)
Lookup finds the entry for uniqueID (ignoring case), else for Nexus page nexusID; either may be zero.
type Dependency ¶
type Dependency struct {
UniqueID string `json:"uniqueId"`
MinimumVersion string `json:"minimumVersion"`
Required bool `json:"required"`
}
Dependency is a Dependencies[] entry, or the ContentPackFor framework as a required one.
type File ¶
type File struct {
ID int64 `json:"id"`
Type string `json:"type"`
Version string `json:"version"`
FileName string `json:"fileName"`
SizeInBytes int64 `json:"sizeInBytes"`
Mods []Mod `json:"mods"`
}
File is one downloadable file; its Type is Nexus' category (Main, Optional, ...). FileName is empty when the dataset gave a storage path ("4a/35/fc/<uuid>"), which names nothing a user or Nexus would recognise.
type InstalledMod ¶
type InstalledMod struct {
ID string `json:"id"`
UpdateKeys []string `json:"updateKeys,omitempty"`
Version string `json:"installedVersion,omitempty"`
}
InstalledMod is one mod to check.
type Mod ¶
type Mod struct {
UniqueID string `json:"uniqueId"`
Name string `json:"name"`
Version string `json:"version"`
UpdateKeys []string `json:"updateKeys"`
Dependencies []Dependency `json:"dependencies"`
}
Mod is a SMAPI manifest found inside a file.
type Page ¶
type Page struct {
ID int `json:"id"`
Name string `json:"name"`
Author string `json:"author"`
PageURL string `json:"pageUrl"`
Version string `json:"version"`
Downloads []File `json:"downloads"`
}
Page is one Nexus mod page's current files as the dataset records them.
type Ref ¶
Ref names a mod page: Site is "Nexus" or "CurseForge", as the dataset names it. Its JSON tags are a format contract: callers cache the encoding.
type Update ¶
type Update struct {
Version string `json:"version"`
URL string `json:"url"`
Source string `json:"source,omitempty"`
}
Update is SMAPI's suggested newer version.
type UpdateRequest ¶
type UpdateRequest struct {
APIVersion string
GameVersion string
Platform string // Android, Linux, Mac or Windows
Mods []InstalledMod
}
UpdateRequest describes the installed environment and the mods to check.
type UpdateResult ¶
type UpdateResult struct {
ID string `json:"id"`
Known bool `json:"known"`
Suggested *Update `json:"suggested,omitempty"`
Unofficial *Update `json:"unofficial,omitempty"`
Compatibility string `json:"compatibility,omitempty"`
CompatibilitySummary string `json:"compatibilitySummary,omitempty"`
BrokeIn string `json:"brokeIn,omitempty"`
GitHubRepo string `json:"gitHubRepo,omitempty"`
}
UpdateResult is smapi.io's answer for one mod. Known is false when the mod's batch got no answer; a Known result with no Suggested has no update. Its JSON tags are a format contract: callers cache the encoding.