Documentation
¶
Overview ¶
Package nexusmods is a client for the Nexus Mods API: the v1 REST calls and v2 GraphQL queries a mod manager makes, the rate-limit budget Nexus reports in its X-Rl-* headers, nxm:// link parsing and mod-page URLs.
Every request carries the Application-Name and Application-Version the caller gave New, and none is sent once a window is at LimitFloor until it resets. No function returns a mod file's bytes: DownloadLinks redeems an nxm:// link from Nexus's Mod Manager Download button, PremiumDownloadLinks asks as a premium account, and fetching a mirror is the caller's code. The package sets no User-Agent and opens no browser; the sign-in flows live in the sso subpackage.
Index ¶
- Constants
- Variables
- func ModURL(domain string, modID int) string
- func NXMDomain(raw string) (string, error)
- func NewerFile(a string, aID int64, b string, bID int64) bool
- func ParseModURL(text string) (domain string, modID int, ok bool)
- func Requests(n int) int
- type ArchiveName
- type BatchFile
- type Changelog
- type Client
- func (c *Client) Abstain(ctx context.Context, t Title, modID int, version string) (EndorseStatus, error)
- func (c *Client) Categories(ctx context.Context, t Title) (map[int]string, error)
- func (c *Client) Changelogs(ctx context.Context, t Title, modID, limit int) ([]Changelog, error)
- func (c *Client) Collection(ctx context.Context, domain, slug string, revision int) (Collection, error)
- func (c *Client) DownloadLinks(ctx context.Context, t Title, l NXMLink) ([]Link, error)
- func (c *Client) Endorse(ctx context.Context, t Title, modID int, version string) (EndorseStatus, error)
- func (c *Client) Files(ctx context.Context, t Title, modID int) ([]File, error)
- func (c *Client) FilesOf(ctx context.Context, t Title, modIDs []int) (map[int][]BatchFile, error)
- func (c *Client) ForgetTracked()
- func (c *Client) Limits() Limits
- func (c *Client) ModsByDomain(ctx context.Context, domain string, modIDs []int) (map[int]ModInfo, error)
- func (c *Client) Page(ctx context.Context, t Title, modID int) (Page, error)
- func (c *Client) PremiumCollectionArchive(ctx context.Context, downloadLink string) ([]byte, error)
- func (c *Client) PremiumDownloadLinks(ctx context.Context, t Title, modID, fileID int) ([]Link, error)
- func (c *Client) ScanStatuses(ctx context.Context, t Title, modID int) (map[int]string, error)
- func (c *Client) SetLimitsHook(fn func(Limits))
- func (c *Client) Track(ctx context.Context, t Title, modID int) error
- func (c *Client) TrackedMods(ctx context.Context) ([]TrackedMod, error)
- func (c *Client) Untrack(ctx context.Context, t Title, modID int) error
- func (c *Client) Validate(ctx context.Context) (User, error)
- func (c *Client) WithBearer(token TokenSource) *Client
- func (c *Client) WithKey(key string) *Client
- type Collection
- type CollectionExternal
- type CollectionFile
- type EndorseStatus
- type File
- type GameOf
- type Limits
- type Link
- type MessageError
- type ModInfo
- type NXMLink
- type Page
- type RejectError
- type Requirement
- type Title
- type TokenSource
- type TrackedMod
- type User
- type Window
Constants ¶
const ( // BaseURL is the public API. BaseURL = "https://api.nexusmods.com" // LimitFloor is the remaining-call count at or under which requests are refused until the window resets. LimitFloor = 5 )
const ( ReasonForm = "form" ReasonGame = "game" ReasonExpired = "expired" ReasonUser = "user" ReasonSignedIn = "signedIn" )
Why a link was refused. They are stable strings a UI can translate.
const MaxCollectionArchive = 64 << 20
MaxCollectionArchive caps the curator's collection archive PremiumCollectionArchive reads into memory.
Variables ¶
var ( ErrUnauthorized = errors.New("nexus rejected the API key") // ErrPremiumRequired means a download link needs the key and expires from an nxm:// link, or a premium account. ErrPremiumRequired = errors.New("nexus requires a premium account or an nxm:// link for this download") )
Functions ¶
func NXMDomain ¶
NXMDomain is the nxm URL's game segment (the host), or a *RejectError when raw is not an nxm link.
func NewerFile ¶
NewerFile reports whether file a (version a, file id aID) is newer than file b of the same mod: by version where both parse and differ, else the later upload, which has the higher file id. Nexus file ids grow with each upload, so the id breaks a tie when an author re-uploads a fix under the same version.
func ParseModURL ¶
ParseModURL reads a Nexus mod page link, in the old (/<domain>/mods/<id>) or new (/games/<domain>/mods/<id>) form; tabs, queries and fragments are ignored.
Types ¶
type ArchiveName ¶ added in v0.2.0
type ArchiveName struct {
// Name is the mod's name as written in the file name, where Nexus has turned spaces into underscores.
Name string
ModID int
// Version is the file's version with its dots written as '-', as in the file name ("2-8-2").
Version string
// Timestamp is the Unix time Nexus stamped on the download.
Timestamp int64
// Ext is the archive extension, lower case, without the dot.
Ext string
}
ArchiveName is what a Nexus download's file name carries.
func ParseArchiveName ¶ added in v0.2.0
func ParseArchiveName(file string) (ArchiveName, bool)
ParseArchiveName reads the name Nexus gives a downloaded archive, "<name>-<mod id>-<version>-<timestamp>.<ext>" with ext zip, 7z or rar. A directory part is ignored. ok is false for any other name or a mod id of 0. A name that itself holds "-<digits>-" is read at its first such span, since the pattern cannot tell them apart.
type BatchFile ¶
type BatchFile struct {
FileID int `json:"fileId"`
Name string `json:"name"`
Version string `json:"version"`
Category string `json:"category"`
// ReplacedBy is the author's file_updates successor. The batched query does not carry it, so it comes from a
// mod's cached full file list when there is one.
ReplacedBy int `json:"replacedBy,omitempty"`
}
BatchFile is one file of a mod as Nexus's v2 API lists it.
type Client ¶
type Client struct {
// HTTP sends every request; nil means http.DefaultClient. Its redirect policy is chained with
// httpkit.SameOrigin, so the credential never leaves the API origin. Give it an httpkit.Transport to set the
// User-Agent and to observe every round trip through OnResult.
HTTP *http.Client
// BaseURL is the API origin; empty means BaseURL.
BaseURL string
// Now is the clock; nil means time.Now in UTC.
Now func() time.Time
// contains filtered or unexported fields
}
Client talks to the API with one user's key or token. Copies made by WithKey and WithBearer share the rate-limit state and the caches.
func New ¶
New returns a client that sends Application-Name: name and Application-Version: version on every request, as Nexus asks of every app. It has no credential until WithKey or WithBearer.
func (*Client) Abstain ¶
func (c *Client) Abstain(ctx context.Context, t Title, modID int, version string) (EndorseStatus, error)
Abstain withdraws an endorsement of modID at version for the signed-in user.
func (*Client) Categories ¶
Categories maps the game's category IDs to their names.
func (*Client) Changelogs ¶
Changelogs returns a mod's limit newest changelog versions. The versions are object keys, which a map would lose along with duplicates, so the object is read token by token; a mod with none answers [].
func (*Client) Collection ¶
func (c *Client) Collection(ctx context.Context, domain, slug string, revision int) (Collection, error)
Collection reads a collection revision, the latest when revision is 0. A collection Nexus does not know wraps httpkit.ErrNotFound.
func (*Client) DownloadLinks ¶
DownloadLinks asks for a file's download mirrors in Nexus's order, redeeming the key and expiry of an nxm:// link that ParseNXM accepted. A link with no key or no expiry is refused before any request, so a hand-built literal cannot fall through to the keyless form.
func (*Client) Endorse ¶
func (c *Client) Endorse(ctx context.Context, t Title, modID int, version string) (EndorseStatus, error)
Endorse records an endorsement of modID at version for the signed-in user.
func (*Client) FilesOf ¶
FilesOf lists the files of many mods with one GraphQL request per 50 mods (aliased modFiles queries), instead of one request per mod.
func (*Client) ForgetTracked ¶
func (c *Client) ForgetTracked()
ForgetTracked drops the remembered tracked-mods list, which belongs to the account that fetched it.
func (*Client) ModsByDomain ¶
func (c *Client) ModsByDomain(ctx context.Context, domain string, modIDs []int) (map[int]ModInfo, error)
ModsByDomain looks up many of one game's mods with one GraphQL request per 50 ids, instead of one request per mod. A mod Nexus does not return is absent from the map. A refused or failed request stops further requests and the lookup returns the mods found and the error; a rate limit is returned as such, never retried.
func (*Client) PremiumCollectionArchive ¶
PremiumCollectionArchive downloads a collection revision's curator archive (collection.json plus bundled files). downloadLink is Collection's DownloadLink, the API path Nexus reports for the revision. It asks with no key, which Nexus allows only premium accounts, and a free account gets ErrPremiumRequired. The archive is the curator's manifest, not a mod file, and it is the one body this package downloads from outside Nexus's API.
func (*Client) PremiumDownloadLinks ¶
func (c *Client) PremiumDownloadLinks(ctx context.Context, t Title, modID, fileID int) ([]Link, error)
PremiumDownloadLinks asks for a file's download mirrors with no key, which Nexus allows only premium accounts; a free account gets ErrPremiumRequired.
func (*Client) ScanStatuses ¶
ScanStatuses returns Nexus's v2 virus-scan status for each file. Results are cached for this client.
func (*Client) SetLimitsHook ¶
SetLimitsHook sets fn, called after a response updates the rate-limit budget and with no lock held, so it may call Limits. Set it before sharing the client between goroutines.
func (*Client) TrackedMods ¶
func (c *Client) TrackedMods(ctx context.Context) ([]TrackedMod, error)
TrackedMods lists mods the signed-in user tracks. The list is reused for trackedCacheTTL.
func (*Client) WithBearer ¶
func (c *Client) WithBearer(token TokenSource) *Client
WithBearer returns a client that sends an OAuth access token, which Nexus accepts in the Authorization header on both the REST and GraphQL APIs.
type Collection ¶
type Collection struct {
Name string
Author string
Slug string
Revision int
Files []CollectionFile
// Instructions is the curator's installationInfo text, shown before importing.
Instructions string
// External lists what the curator points to outside Nexus; each URL is an http or https address or empty.
External []CollectionExternal
// DownloadLink is the Nexus API path that resolves to the curator's collection archive.
DownloadLink string
}
Collection is one revision of a Nexus collection. It has no JSON tags: the Go field names are its stored form.
type CollectionExternal ¶
type CollectionExternal struct {
Name string
Type string
URL string
Version string
Author string
Optional bool
}
CollectionExternal is one off-Nexus resource of a collection. Type is Nexus's resourceType ("direct", "browse" or "manual").
type CollectionFile ¶
CollectionFile is one mod file a collection installs.
type EndorseStatus ¶
type EndorseStatus string
EndorseStatus is the signed-in user's endorsement of one mod.
const ( EndorseUndecided EndorseStatus = "Undecided" EndorseEndorsed EndorseStatus = "Endorsed" EndorseAbstained EndorseStatus = "Abstained" )
type File ¶
type File struct {
FileID int `json:"fileId"`
FileName string `json:"fileName"`
Name string `json:"name"`
Description string `json:"description"`
Version string `json:"version"`
ModVersion string `json:"modVersion"`
Category string `json:"category"`
SizeKB int64 `json:"sizeKb"`
IsPrimary bool `json:"isPrimary"`
Uploaded time.Time `json:"uploaded"`
MD5 string `json:"md5,omitempty"`
// ReplacedBy is the file the author uploaded as this one's update (Nexus file_updates), or 0.
ReplacedBy int `json:"replacedBy"`
}
File is one downloadable file of a mod; Category is empty when Nexus reports null. Name is its title on the page and Description its BBCode.
type GameOf ¶
GameOf maps an nxm link's Nexus domain to the caller's game id; ok is false for a game the caller does not serve.
type Limits ¶
type Limits struct {
Known bool `json:"known"`
Daily Window `json:"daily"`
Hourly Window `json:"hourly"`
}
Limits is the daily and hourly budget; Known is false until a response has carried the headers.
type Link ¶
type Link struct {
Name string `json:"name"`
ShortName string `json:"shortName"`
URI string `json:"uri"`
}
Link is one download mirror.
type MessageError ¶
MessageError is a non-200 API answer that carried a JSON message (endorse-without-download uses this).
func (*MessageError) Error ¶
func (e *MessageError) Error() string
type ModInfo ¶
type ModInfo struct {
ModID int
Name string
Version string
Status string
Summary string
Author string
PictureURL string
Category string
Endorsements int
Downloads int
Adult bool
// Endorsed is whether the signed-in user endorsed the mod.
Endorsed bool
Created time.Time
Updated time.Time
// Requirements are what the mod page lists under "Nexus requirements".
Requirements []Requirement
}
ModInfo is one mod page as the batched lookup reports it.
type NXMLink ¶
type NXMLink struct {
// Game is the caller's id for the game the link's Nexus domain belongs to.
Game string `json:"game"`
ModID int `json:"modId"`
FileID int `json:"fileId"`
Key string `json:"key"`
Expires int64 `json:"expires"`
UserID int `json:"userId"`
}
NXMLink is an accepted nxm:// download link, the value Nexus's Mod Manager Download button produces.
type Page ¶
type Page struct {
ModID int `json:"modId"`
Name string `json:"name"`
Summary string `json:"summary"`
Description string `json:"description"`
PictureURL string `json:"pictureUrl"`
Version string `json:"version"`
Author string `json:"author"`
UploadedBy string `json:"uploadedBy"`
UploaderURL string `json:"uploaderUrl"`
CategoryID int `json:"categoryId"`
Endorsements int `json:"endorsements"`
Downloads int `json:"downloads"`
UniqueDownloads int `json:"uniqueDownloads"`
Created time.Time `json:"created"`
Updated time.Time `json:"updated"`
Adult bool `json:"adult"`
Status string `json:"status"`
Available bool `json:"available"`
// Endorsement is the signed-in user's endorse_status (Endorsed, Abstained, Undecided); empty when Nexus omitted it.
Endorsement string `json:"endorsement"`
// Requirements come from the batched GraphQL lookup; the REST page has none.
Requirements []Requirement `json:"requirements"`
}
Page is everything a mod page reports. Description is Nexus's BBCode with <br /> line breaks, unrendered. Status is published, under_moderation, not_published, publish_with_game, removed, wastebinned or hidden; an unavailable page comes without its text.
type RejectError ¶
type RejectError struct{ Reason string }
RejectError says why a link was refused.
func (*RejectError) Error ¶
func (e *RejectError) Error() string
type Requirement ¶
type Requirement struct {
ModID int `json:"modId"`
Name string `json:"name"`
URL string `json:"url"`
External bool `json:"external"`
Notes string `json:"notes"`
}
Requirement is one mod or outside download a mod page says is needed. A Nexus mod of the same game has ModID and its page as URL; anything else (another game's mod, an outside site) is External, with whatever URL Nexus gave.
type TokenSource ¶
TokenSource returns the current OAuth access token, refreshing it when it is about to expire.
type TrackedMod ¶
TrackedMod is one entry from GET /v1/user/tracked_mods.json.