Struct containing methods related to Roblox avatar customization
This commit is contained in:
parent
db3c7fef99
commit
e4b7bf44a9
|
@ -0,0 +1,244 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"encoding/json"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type OutfitManager struct {
|
||||||
|
AuthenticationCookie string // Used to wear outfits & retrieve CSRF-TOKENs
|
||||||
|
}
|
||||||
|
|
||||||
|
// TO DO:
|
||||||
|
// Warn user about 429s and other import server statusCodes
|
||||||
|
|
||||||
|
// JSON parsed endpoint response
|
||||||
|
type UserOutfits struct {
|
||||||
|
FilteredCount int `json:"filteredCount"`
|
||||||
|
|
||||||
|
Data []struct {
|
||||||
|
Id int
|
||||||
|
Name string
|
||||||
|
IsEditable bool
|
||||||
|
} `json:"data"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Struct representing an outfit
|
||||||
|
type UserOutfit struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
Assets []struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
|
||||||
|
AssetType struct {
|
||||||
|
Id int `json:"id"`
|
||||||
|
Name string `json:"name"`
|
||||||
|
} `json:"assetType"`
|
||||||
|
} `json:"assets"`
|
||||||
|
|
||||||
|
BodyColors struct {
|
||||||
|
HeadColorId int
|
||||||
|
TorsoColorId int
|
||||||
|
RightArmColorId int
|
||||||
|
LeftArmColorId int
|
||||||
|
RightLegColorId int
|
||||||
|
LeftLegColorId int
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get outfit IDs for Roblox user
|
||||||
|
func (manager OutfitManager) getUserOutfitIds(userId int) UserOutfits {
|
||||||
|
response, err := http.Get(
|
||||||
|
fmt.Sprintf("https://avatar.roblox.com/v1/users/%v/outfits?page=1&itemsPerPage=100", userId),
|
||||||
|
)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
outfits := UserOutfits{}
|
||||||
|
|
||||||
|
json.Unmarshal(body, &outfits)
|
||||||
|
|
||||||
|
return outfits
|
||||||
|
}
|
||||||
|
|
||||||
|
// Download Roblox outfits from endpoint response
|
||||||
|
func (manager OutfitManager) getUserOutfits(userOutfits UserOutfits) UserOutfit {
|
||||||
|
outfit := userOutfits.Data[0]
|
||||||
|
|
||||||
|
response, err := http.Get(fmt.Sprintf("https://avatar.roblox.com/v1/outfits/%v/details", outfit.Id))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
body, err := ioutil.ReadAll(response.Body)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
userOutfit := UserOutfit{}
|
||||||
|
|
||||||
|
err = json.Unmarshal(body, &userOutfit)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
return userOutfit
|
||||||
|
|
||||||
|
/* for _, outfit := range userOutfits.Data {
|
||||||
|
// TO DO:
|
||||||
|
// Create JSON file:
|
||||||
|
// Remove spaces from outfit name and replace with _
|
||||||
|
// Write data to JSON file
|
||||||
|
|
||||||
|
fmt.Println(outfit.Name)
|
||||||
|
} */
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save outfit (to JSON file)
|
||||||
|
func (manager OutfitManager) saveOutfit(outfit UserOutfit) {
|
||||||
|
if !folderExists("outfits") {
|
||||||
|
err := os.Mkdir("outfits", os.ModePerm)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
outfitEncoded, err := json.Marshal(&outfit)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
name := strings.ReplaceAll(outfit.Name, " ", "_")
|
||||||
|
|
||||||
|
err = ioutil.WriteFile(fmt.Sprintf("outfits/%v.json", name), outfitEncoded, os.ModePerm)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Load outfit (from JSON file)
|
||||||
|
func (manager OutfitManager) loadOutfit() {}
|
||||||
|
|
||||||
|
// Wear outfit
|
||||||
|
func (manager OutfitManager) wearOutfit(outfit UserOutfit) {
|
||||||
|
if manager.AuthenticationCookie == "" {
|
||||||
|
fmt.Println("wearOutfit requires a valid authentication cookie!")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
type requestAssetIds struct {
|
||||||
|
AssetIds []int
|
||||||
|
}
|
||||||
|
|
||||||
|
requestData := requestAssetIds{
|
||||||
|
AssetIds: make([]int, len(outfit.Assets)),
|
||||||
|
}
|
||||||
|
|
||||||
|
for i := 0; i < len(requestData.AssetIds); i++ {
|
||||||
|
requestData.AssetIds[i] = outfit.Assets[i].Id
|
||||||
|
}
|
||||||
|
|
||||||
|
requestBody, err := json.Marshal(requestData)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := http.NewRequest("POST", "https://avatar.roblox.com/v1/avatar/set-wearing-assets", bytes.NewBuffer(requestBody))
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
request.Header.Add("Content-Type", "application/json")
|
||||||
|
request.Header.Add("X-Csrf-Token", manager.getCSRFToken())
|
||||||
|
|
||||||
|
request.AddCookie(&http.Cookie{
|
||||||
|
Name: ".ROBLOSECURITY",
|
||||||
|
Value: manager.AuthenticationCookie,
|
||||||
|
})
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
|
||||||
|
response, err := client.Do(request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
body, _ := ioutil.ReadAll(response.Body)
|
||||||
|
|
||||||
|
fmt.Println("Status code:", response.StatusCode)
|
||||||
|
fmt.Println(string(body))
|
||||||
|
}
|
||||||
|
|
||||||
|
func (manager *OutfitManager) getCSRFToken() string {
|
||||||
|
if manager.AuthenticationCookie == "" {
|
||||||
|
fmt.Println("Cannot retrieve CSRF Token. No cookie provided.")
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
|
||||||
|
request, err := http.NewRequest("POST", "https://auth.roblox.com/v2/logout", nil)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
request.AddCookie(&http.Cookie{
|
||||||
|
Name: ".ROBLOSECURITY",
|
||||||
|
Value: manager.AuthenticationCookie,
|
||||||
|
})
|
||||||
|
|
||||||
|
client := &http.Client{}
|
||||||
|
|
||||||
|
response, err := client.Do(request)
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
defer response.Body.Close()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println(err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
switch response.StatusCode {
|
||||||
|
case 401:
|
||||||
|
fmt.Println("Invalid cookie!")
|
||||||
|
manager.AuthenticationCookie = ""
|
||||||
|
case 403:
|
||||||
|
return response.Header.Get("X-Csrf-Token")
|
||||||
|
default:
|
||||||
|
fmt.Println("Unexpected status code: ", response.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ""
|
||||||
|
}
|
Reference in New Issue