package gamebanana import ( "encoding/json" "io" "net/http" "net/url" "github.com/charmbracelet/log" ) // all fields for the "mod" type got at GET api.gamebanana.com/Core/Item/Data/AllowedFields?itemtype=Mod // struct is gotten from requesting all fields and then converting them with https://transform.tools/json-to-go type GameBananaMod struct { AppsUsed string `json:"apps_used"` Authors string `json:"authors"` CategoryName string `json:"Category().name"` Catid int `json:"catid"` Contestid int `json:"contestid"` Creator string `json:"creator"` CreditsAAuthors [][]string `json:"Credits().aAuthors()"` CreditsAAuthorsAndGroups struct { KeyAuthor [][]string `json:"Key Author"` ModelSources [][]string `json:"Model Sources"` SpecialThanks [][]string `json:"Special Thanks"` } `json:"Credits().aAuthorsAndGroups()"` CreditsSsvAuthorNames string `json:"Credits().ssvAuthorNames()"` Date int `json:"date"` Description string `json:"description"` Downloads int `json:"downloads"` FeedbackInstructions string `json:"feedback_instructions"` FilesAFiles struct { Num884808 struct { IDRow string `json:"_idRow"` SFile string `json:"_sFile"` NFilesize int `json:"_nFilesize"` TsDateAdded int `json:"_tsDateAdded"` NDownloadCount int `json:"_nDownloadCount"` SDownloadURL string `json:"_sDownloadUrl"` SMd5Checksum string `json:"_sMd5Checksum"` SAnalysisState string `json:"_sAnalysisState"` SAnalysisResult string `json:"_sAnalysisResult"` SAnalysisResultVerbose string `json:"_sAnalysisResultVerbose"` SAvState string `json:"_sAvState"` SAvResult string `json:"_sAvResult"` BIsArchived bool `json:"_bIsArchived"` BHasContents bool `json:"_bHasContents"` } `json:"884808"` } `json:"Files().aFiles()"` GameName string `json:"Game().name"` InstallInstructions string `json:"install_instructions"` IsObsolete string `json:"is_obsolete"` LastpostDate int `json:"lastpost_date"` LastpostUserid int `json:"lastpost_userid"` Likes int `json:"likes"` Mdate int `json:"mdate"` Modnote string `json:"modnote"` Name string `json:"name"` NsfwBIsNsfw bool `json:"Nsfw().bIsNsfw()"` ObsolNotice string `json:"obsol_notice"` OwnerName string `json:"Owner().name"` Postcount int `json:"postcount"` PostsLastPostIDPosterRow int `json:"Posts().LastPost().idPosterRow()"` PostsLastPostSText string `json:"Posts().LastPost().sText()"` PostsLastPostTsDateAdded int `json:"Posts().LastPost().tsDateAdded()"` PostsPostcountNPostCount int `json:"Posts().Postcount().nPostCount()"` PreviewSStructuredDataFullsizeURL string `json:"Preview().sStructuredDataFullsizeUrl()"` PreviewSSubFeedImageURL string `json:"Preview().sSubFeedImageUrl()"` RootCategoryID int `json:"RootCategory().id"` RootCategoryName string `json:"RootCategory().name"` Screenshots string `json:"screenshots"` Studioid int `json:"studioid"` Text string `json:"text"` TrashBIsTrashed bool `json:"Trash().bIsTrashed()"` Udate int `json:"udate"` UpdatesAGetLatestUpdates []interface{} `json:"Updates().aGetLatestUpdates()"` UpdatesALatestUpdates []interface{} `json:"Updates().aLatestUpdates()"` UpdatesBSubmissionHasUpdates bool `json:"Updates().bSubmissionHasUpdates()"` UpdatesNUpdatesCount int `json:"Updates().nUpdatesCount()"` URLSDownloadURL string `json:"Url().sDownloadUrl()"` URLSEditURL string `json:"Url().sEditUrl()"` URLSEmbeddablesURL string `json:"Url().sEmbeddablesUrl()"` URLSHistoryURL string `json:"Url().sHistoryUrl()"` URLSProfileURL string `json:"Url().sProfileUrl()"` URLSTrashURL string `json:"Url().sTrashUrl()"` URLSUntrashURL string `json:"Url().sUntrashUrl()"` URLSUpdatesURL string `json:"Url().sUpdatesUrl()"` URLSWithholdURL string `json:"Url().sWithholdUrl()"` Userid int `json:"userid"` Views int `json:"views"` WithholdBIsWithheld bool `json:"Withhold().bIsWithheld()"` } func GetGameBananaMod(id string) (GameBananaMod, error) { // build URL params base, err := url.Parse("https://api.gamebanana.com/Core/Item/Data") if err != nil { log.Error("failed to parse URL", "err", err) return GameBananaMod{}, err } params := url.Values{} params.Add("itemtype", "Mod") params.Add("itemid", id) params.Add("format", "json_min") params.Add("return_keys", "1") params.Add("fields", "apps_used,authors,Category().name,catid,contestid,creator,Credits().aAuthors(),Credits().aAuthorsAndGroups(),Credits().ssvAuthorNames(),date,description,downloads,feedback_instructions,Files().aFiles(),Game().name,install_instructions,is_obsolete,lastpost_date,lastpost_userid,likes,mdate,modnote,name,Nsfw().bIsNsfw(),obsol_notice,Owner().name,postcount,Posts().LastPost().idPosterRow(),Posts().LastPost().sText(),Posts().LastPost().tsDateAdded(),Posts().Postcount().nPostCount(),Preview().sStructuredDataFullsizeUrl(),Preview().sSubFeedImageUrl(),RootCategory().id,RootCategory().name,screenshots,studioid,text,Trash().bIsTrashed(),udate,Updates().aGetLatestUpdates(),Updates().aLatestUpdates(),Updates().bSubmissionHasUpdates(),Updates().nUpdatesCount(),Url().sDownloadUrl(),Url().sEditUrl(),Url().sEmbeddablesUrl(),Url().sHistoryUrl(),Url().sProfileUrl(),Url().sTrashUrl(),Url().sUntrashUrl(),Url().sUpdatesUrl(),Url().sWithholdUrl(),userid,views,Withhold().bIsWithheld()") base.RawQuery = params.Encode() // send request log.Info("got request url for Mod information", "id", id, "url", base.String()) resp, err := http.Get(base.String()) if err != nil || resp.StatusCode != 200 { log.Error("failed to request URL", "url", base.String(), "err", err, "httpstatus", resp.StatusCode) return GameBananaMod{}, err } defer resp.Body.Close() // decode response body, err := io.ReadAll(resp.Body) if err != nil { log.Error("failed to decode body into bytes") return GameBananaMod{}, err } var dataStruct GameBananaMod err = json.Unmarshal(body, &dataStruct) if err != nil { log.Error("failed to decode request body", "err", err) log.Debug(string(body)) return GameBananaMod{}, err } return dataStruct, nil }