commit d1b62c2407402b02bba24fe7043974d3ed143568 Author: itzaname Date: Sat Jan 7 00:20:45 2017 -0500 Initial diff --git a/item.go b/item.go new file mode 100644 index 0000000..b03e287 --- /dev/null +++ b/item.go @@ -0,0 +1,199 @@ +package roblox + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" + "net/url" + "regexp" + "strings" +) + +// Item struct containing data on retrieved items +type Item struct { + ProductID string + ItemID string + AssetID string + UserID string + UserName string + Type string + Name string + CopyLocked bool + Owned bool +} + +// ItemInfo return info of selected item +func (s *Session) ItemInfo(url string) (Item, error) { + var item Item + + resp, err := s.client.Get(url) + if err != nil { + return item, err + } + + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return item, fmt.Errorf("Failed to get item info. Status %d", resp.StatusCode) + } + + body, _ := ioutil.ReadAll(resp.Body) + page := string(body) + + pItem := regexp.MustCompile("data-product-id=\"(.+)\"").FindStringSubmatch(page) + if len(pItem) > 0 { + item.ProductID, err = safeRetrieve("data-product-id=\"(.+)\"", page) + if err != nil { + return item, err + } + item.CopyLocked = false + item.UserID, err = safeRetrieve("data-expected-seller-id=\"(.+)\"", page) + if err != nil { + return item, err + } + item.UserName, err = safeRetrieve("data-seller-name=\"(.+)\"", page) + if err != nil { + return item, err + } + } else { + item.ProductID = "locked" + item.CopyLocked = true + item.UserID = "" + item.UserName = "" + item.AssetID = "" + } + + asset := regexp.MustCompile("data-userasset-id=\"(.+)\"").FindStringSubmatch(page) + if len(asset) > 0 { + item.AssetID, err = safeRetrieve("data-userasset-id=\"(.+)\"", page) + if err != nil { + return item, err + } + item.Owned = true + } else { + item.AssetID = "" + item.Owned = false + } + + item.ItemID, err = safeRetrieve("data-item-id=\"(.+)\"", page) + if err != nil { + return item, err + } + item.Name, err = safeRetrieve("data-item-name=\"(.+)\"", page) + if err != nil { + return item, err + } + item.Type, err = safeRetrieve("data-asset-type=\"(.+)\"", page) + if err != nil { + return item, err + } + + return item, nil +} + +// AddItem add item by id to inventory +func (s *Session) AddItem(id string, seller string) error { + v := url.Values{} + v.Set("rqtype", "purchase") + v.Set("productID", id) + v.Set("expectedCurrency", "1") + v.Set("expectedPrice", "0") + v.Set("expectedSellerID", seller) + v.Set("userAssetID", "") + resp, err := s.client.Post("https://www.roblox.com/api/item.ashx?"+v.Encode(), "application/json", bytes.NewBufferString("")) + if err != nil { + return err + } + + defer resp.Body.Close() + + if resp.StatusCode == 403 { + req, err := http.NewRequest("POST", "https://www.roblox.com/api/item.ashx?"+v.Encode(), bytes.NewBufferString("")) + req.Header.Set("X-Csrf-Token", resp.Header["X-Csrf-Token"][0]) + req.Header.Set("Content-Type", "application/json") + + resp, err := s.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return fmt.Errorf("Failed to add item. Status %d", resp.StatusCode) + } + } + + return nil +} + +// RemoveItem will remove item from inventory +func (s *Session) RemoveItem(id string) error { + v := url.Values{} + v.Set("assetId", id) + resp, err := s.client.Post("https://www.roblox.com/asset/delete-from-inventory", "application/x-www-form-urlencoded", bytes.NewBufferString(v.Encode())) + if err != nil { + return err + } + + defer resp.Body.Close() + + if resp.StatusCode == 403 { + req, err := http.NewRequest("POST", "https://www.roblox.com/asset/delete-from-inventory", bytes.NewBufferString(v.Encode())) + req.Header.Set("X-Csrf-Token", resp.Header["X-Csrf-Token"][0]) + req.Header.Set("Content-Type", "application/x-www-form-urlencoded") + + resp, err := s.client.Do(req) + if err != nil { + panic(err) + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return fmt.Errorf("Failed to remove item. Status %d", resp.StatusCode) + } + } + + return nil +} + +// GetModels will list models in inventory +func (s *Session) GetModels(user string) ([]Item, error) { + var Data []Item + + resp, err := s.client.Get("https://www.roblox.com/users/inventory/list-json?assetTypeId=10&userId=" + user) + if err != nil { + return Data, fmt.Errorf("Could not get list: %s", err) + } + + if resp.StatusCode == 200 { + var dat map[string]interface{} + defer resp.Body.Close() + body, _ := ioutil.ReadAll(resp.Body) + + if err := json.Unmarshal(body, &dat); err != nil { + panic(err) + } + + set := dat["Data"].(map[string]interface{}) + ilist := set["Items"].([]interface{}) + for _, obj := range ilist { + itm := obj.(map[string]interface{}) + iInfo := itm["Item"].(map[string]interface{}) + iCreator := itm["Creator"].(map[string]interface{}) + Data = append(Data, Item{"", strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.2f", iInfo["AssetId"].(float64)), "0"), "."), "", strings.TrimRight(strings.TrimRight(fmt.Sprintf("%.2f", iCreator["Id"].(float64)), "0"), "."), iCreator["Name"].(string), "Model", iInfo["Name"].(string), false, true}) + } + + return Data, nil + } + return Data, fmt.Errorf("Could not get models. Status: %d", resp.StatusCode) +} + +func safeRetrieve(pattern string, source string) (string, error) { + pItem := regexp.MustCompile(pattern).FindStringSubmatch(source) + if len(pItem) > 0 { + return pItem[1], nil + } + return "", fmt.Errorf("Could not find item on in string") +} diff --git a/message.go b/message.go new file mode 100644 index 0000000..fdb501f --- /dev/null +++ b/message.go @@ -0,0 +1,136 @@ +package roblox + +import ( + "bytes" + "encoding/json" + "fmt" + "io/ioutil" + "net/http" +) + +// Message structure for message data +type Message struct { + ID string + Subject string + Body string + FromID string + FromName string +} + +// GetInbox makes a request to retrieve message list +func (s *Session) GetInbox() ([]Message, error) { + var messages []Message + resp, err := s.client.Get("https://www.roblox.com/messages/api/get-messages") + if err != nil { + return messages, err + } + + if resp.StatusCode == 200 { + var data map[string]interface{} + defer resp.Body.Close() + + body, err := ioutil.ReadAll(resp.Body) + if err != nil { + return messages, err + } + + err = json.Unmarshal(body, &data) + if err != nil { + return messages, err + } + + collection, exists := data["Collection"] + if exists { + for _, value := range collection.([]interface{}) { + // We will assume if we got this far everything exists + msg := value.(map[string]interface{}) + sender := msg["Sender"].(map[string]interface{}) + + message := Message{} + message.ID = string(int(msg["Id"].(float64))) + message.Subject = msg["Subject"].(string) + message.Body = msg["Body"].(string) + message.FromID = string(int(sender["UserId"].(float64))) + message.FromName = sender["UserName"].(string) + messages = append(messages, message) + } + } + } + return messages, err +} + +// SendMessage will send a message via post +func (s *Session) SendMessage(subject string, body string, recipientid int) error { + payload := map[string]interface{}{ + "subject": subject, + "body": body, + "recipientId": recipientid, + } + data, err := json.Marshal(&payload) + if err != nil { + return err + } + + resp, err := s.client.Post("https://www.roblox.com/messages/send", "application/json", bytes.NewBuffer(data)) + if err != nil { + return err + } + + defer resp.Body.Close() + + // CSRF Token Status + if resp.StatusCode == 403 { + req, err := http.NewRequest("POST", "https://www.roblox.com/messages/send", bytes.NewBuffer(data)) + req.Header.Set("X-Csrf-Token", resp.Header["X-Csrf-Token"][0]) + req.Header.Set("Content-Type", "application/json") + + resp, err := s.client.Do(req) + if err != nil { + return err + } + + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return fmt.Errorf("Messaged send failed. Status %d", resp.StatusCode) + } + } + return nil +} + +// ArchiveMessages will archieve a list of messages +func (s *Session) ArchiveMessages(ids []string) error { + payload := map[string][]string{ + "messageIds": ids, + } + + data, err := json.Marshal(&payload) + if err != nil { + return err + } + + resp, err := s.client.Post("https://www.roblox.com/messages/api/archive-messages", "application/json", bytes.NewBuffer(data)) + if err != nil { + return err + } + + defer resp.Body.Close() + + // CSRF Token Status + if resp.StatusCode == 403 { + req, err := http.NewRequest("POST", "https://www.roblox.com/messages/api/archive-messages", bytes.NewBuffer(data)) + req.Header.Set("X-Csrf-Token", resp.Header["X-Csrf-Token"][0]) + req.Header.Set("Content-Type", "application/json") + + resp, err := s.client.Do(req) + if err != nil { + return err + } + defer resp.Body.Close() + + if resp.StatusCode != 200 { + return fmt.Errorf("Archive failed. Status %d", resp.StatusCode) + } + } + return nil +} diff --git a/session.go b/session.go new file mode 100644 index 0000000..2d786d8 --- /dev/null +++ b/session.go @@ -0,0 +1,38 @@ +package roblox + +import ( + "fmt" + "net/http" + "net/http/cookiejar" + "net/url" +) + +// Session struct for roblox login session data and members +type Session struct { + ID string + username string + client *http.Client +} + +// New create a new session and logs in with provided data +func New(user string, pass string, id string) (*Session, error) { + cookieJar, _ := cookiejar.New(nil) + client := &http.Client{ + Jar: cookieJar, + } + + v := url.Values{} + v.Set("username", user) + v.Set("password", pass) + v.Set("submitLogin", "Log In") + v.Set("ReturnUrl", "") + + session := Session{id, user, client} + + resp, err := client.PostForm("https://www.roblox.com/newlogin", v) + if resp.StatusCode != 200 { + return &session, fmt.Errorf("Messaged send failed. Status %d", resp.StatusCode) + } + + return &session, err +}