summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/Philipp15b/go-steam/trade
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/Philipp15b/go-steam/trade')
-rw-r--r--vendor/github.com/Philipp15b/go-steam/trade/actions.go84
-rw-r--r--vendor/github.com/Philipp15b/go-steam/trade/doc.go40
-rw-r--r--vendor/github.com/Philipp15b/go-steam/trade/trade.go122
-rw-r--r--vendor/github.com/Philipp15b/go-steam/trade/tradeapi/status.go111
-rw-r--r--vendor/github.com/Philipp15b/go-steam/trade/tradeapi/trade.go200
-rw-r--r--vendor/github.com/Philipp15b/go-steam/trade/types.go67
6 files changed, 624 insertions, 0 deletions
diff --git a/vendor/github.com/Philipp15b/go-steam/trade/actions.go b/vendor/github.com/Philipp15b/go-steam/trade/actions.go
new file mode 100644
index 00000000..e9301940
--- /dev/null
+++ b/vendor/github.com/Philipp15b/go-steam/trade/actions.go
@@ -0,0 +1,84 @@
+package trade
+
+import (
+ "github.com/Philipp15b/go-steam/economy/inventory"
+ "github.com/Philipp15b/go-steam/trade/tradeapi"
+ "time"
+)
+
+type Slot uint
+
+func (t *Trade) action(status *tradeapi.Status, err error) error {
+ if err != nil {
+ return err
+ }
+ t.onStatus(status)
+ return nil
+}
+
+// Returns the next batch of events to process. These can be queued from calls to methods
+// like `AddItem` or, if there are no queued events, from a new HTTP request to Steam's API (blocking!).
+// If the latter is the case, this method may also sleep before the request
+// to conform to the polling interval of the official Steam client.
+func (t *Trade) Poll() ([]interface{}, error) {
+ if t.queuedEvents != nil {
+ return t.Events(), nil
+ }
+
+ if d := time.Since(t.lastPoll); d < pollTimeout {
+ time.Sleep(pollTimeout - d)
+ }
+ t.lastPoll = time.Now()
+
+ err := t.action(t.api.GetStatus())
+ if err != nil {
+ return nil, err
+ }
+
+ return t.Events(), nil
+}
+
+func (t *Trade) GetTheirInventory(contextId uint64, appId uint32) (*inventory.Inventory, error) {
+ return inventory.GetFullInventory(func() (*inventory.PartialInventory, error) {
+ return t.api.GetForeignInventory(contextId, appId, nil)
+ }, func(start uint) (*inventory.PartialInventory, error) {
+ return t.api.GetForeignInventory(contextId, appId, &start)
+ })
+}
+
+func (t *Trade) GetOwnInventory(contextId uint64, appId uint32) (*inventory.Inventory, error) {
+ return t.api.GetOwnInventory(contextId, appId)
+}
+
+func (t *Trade) GetMain() (*tradeapi.Main, error) {
+ return t.api.GetMain()
+}
+
+func (t *Trade) AddItem(slot Slot, item *Item) error {
+ return t.action(t.api.AddItem(uint(slot), item.AssetId, item.ContextId, item.AppId))
+}
+
+func (t *Trade) RemoveItem(slot Slot, item *Item) error {
+ return t.action(t.api.RemoveItem(uint(slot), item.AssetId, item.ContextId, item.AppId))
+}
+
+func (t *Trade) Chat(message string) error {
+ return t.action(t.api.Chat(message))
+}
+
+func (t *Trade) SetCurrency(amount uint, currency *Currency) error {
+ return t.action(t.api.SetCurrency(amount, currency.CurrencyId, currency.ContextId, currency.AppId))
+}
+
+func (t *Trade) SetReady(ready bool) error {
+ return t.action(t.api.SetReady(ready))
+}
+
+// This may only be called after a successful `SetReady(true)`.
+func (t *Trade) Confirm() error {
+ return t.action(t.api.Confirm())
+}
+
+func (t *Trade) Cancel() error {
+ return t.action(t.api.Cancel())
+}
diff --git a/vendor/github.com/Philipp15b/go-steam/trade/doc.go b/vendor/github.com/Philipp15b/go-steam/trade/doc.go
new file mode 100644
index 00000000..549ad47d
--- /dev/null
+++ b/vendor/github.com/Philipp15b/go-steam/trade/doc.go
@@ -0,0 +1,40 @@
+/*
+Allows automation of Steam Trading.
+
+Usage
+
+Like go-steam, this package is event-based. Call Poll() until the trade has ended, that is until the TradeEndedEvent is emitted.
+
+ // After receiving the steam.TradeSessionStartEvent
+ t := trade.New(sessionIdCookie, steamLoginCookie, steamLoginSecure, event.Other)
+ for {
+ eventList, err := t.Poll()
+ if err != nil {
+ // error handling here
+ continue
+ }
+ for _, event := range eventList {
+ switch e := event.(type) {
+ case *trade.ChatEvent:
+ // respond to any chat message
+ t.Chat("Trading is awesome!")
+ case *trade.TradeEndedEvent:
+ return
+ // other event handlers here
+ }
+ }
+ }
+
+You can either log into steamcommunity.com and use the values of the `sessionId` and `steamLogin` cookies,
+or use go-steam and after logging in with client.Web.LogOn() and receiving the WebLoggedOnEvent use the `SessionId`
+and `SteamLogin` fields of steam.Web for the respective cookies.
+
+It is important that there is no delay between the Poll() calls greater than the timeout of the Steam client
+(currently five seconds before the trade partner sees a warning) or the trade will be closed automatically by Steam.
+
+Notes
+
+All method calls to Steam APIs are blocking. This packages' and its subpackages' types are not thread-safe and no calls to any method of the same
+trade instance may be done concurrently except when otherwise noted.
+*/
+package trade
diff --git a/vendor/github.com/Philipp15b/go-steam/trade/trade.go b/vendor/github.com/Philipp15b/go-steam/trade/trade.go
new file mode 100644
index 00000000..4889f407
--- /dev/null
+++ b/vendor/github.com/Philipp15b/go-steam/trade/trade.go
@@ -0,0 +1,122 @@
+package trade
+
+import (
+ "errors"
+ "time"
+
+ "github.com/Philipp15b/go-steam/steamid"
+ "github.com/Philipp15b/go-steam/trade/tradeapi"
+)
+
+const pollTimeout = time.Second
+
+type Trade struct {
+ ThemId steamid.SteamId
+
+ MeReady, ThemReady bool
+
+ lastPoll time.Time
+ queuedEvents []interface{}
+ api *tradeapi.Trade
+}
+
+func New(sessionId, steamLogin, steamLoginSecure string, other steamid.SteamId) *Trade {
+ return &Trade{
+ other,
+ false, false,
+ time.Unix(0, 0),
+ nil,
+ tradeapi.New(sessionId, steamLogin, steamLoginSecure, other),
+ }
+}
+
+func (t *Trade) Version() uint {
+ return t.api.Version
+}
+
+// Returns all queued events and removes them from the queue without performing a HTTP request, like Poll() would.
+func (t *Trade) Events() []interface{} {
+ qe := t.queuedEvents
+ t.queuedEvents = nil
+ return qe
+}
+
+func (t *Trade) onStatus(status *tradeapi.Status) error {
+ if !status.Success {
+ return errors.New("trade: returned status not successful! error message: " + status.Error)
+ }
+
+ if status.NewVersion {
+ t.api.Version = status.Version
+
+ t.MeReady = status.Me.Ready == true
+ t.ThemReady = status.Them.Ready == true
+ }
+
+ switch status.TradeStatus {
+ case tradeapi.TradeStatus_Complete:
+ t.addEvent(&TradeEndedEvent{TradeEndReason_Complete})
+ case tradeapi.TradeStatus_Cancelled:
+ t.addEvent(&TradeEndedEvent{TradeEndReason_Cancelled})
+ case tradeapi.TradeStatus_Timeout:
+ t.addEvent(&TradeEndedEvent{TradeEndReason_Timeout})
+ case tradeapi.TradeStatus_Failed:
+ t.addEvent(&TradeEndedEvent{TradeEndReason_Failed})
+ case tradeapi.TradeStatus_Open:
+ // nothing
+ default:
+ // ignore too
+ }
+
+ t.updateEvents(status.Events)
+ return nil
+}
+
+func (t *Trade) updateEvents(events tradeapi.EventList) {
+ if len(events) == 0 {
+ return
+ }
+
+ var lastLogPos uint
+ for i, event := range events {
+ if i < t.api.LogPos {
+ continue
+ }
+ if event.SteamId != t.ThemId {
+ continue
+ }
+
+ if lastLogPos < i {
+ lastLogPos = i
+ }
+
+ switch event.Action {
+ case tradeapi.Action_AddItem:
+ t.addEvent(&ItemAddedEvent{newItem(event)})
+ case tradeapi.Action_RemoveItem:
+ t.addEvent(&ItemRemovedEvent{newItem(event)})
+ case tradeapi.Action_Ready:
+ t.ThemReady = true
+ t.addEvent(new(ReadyEvent))
+ case tradeapi.Action_Unready:
+ t.ThemReady = false
+ t.addEvent(new(UnreadyEvent))
+ case tradeapi.Action_SetCurrency:
+ t.addEvent(&SetCurrencyEvent{
+ newCurrency(event),
+ event.OldAmount,
+ event.NewAmount,
+ })
+ case tradeapi.Action_ChatMessage:
+ t.addEvent(&ChatEvent{
+ event.Text,
+ })
+ }
+ }
+
+ t.api.LogPos = uint(lastLogPos) + 1
+}
+
+func (t *Trade) addEvent(event interface{}) {
+ t.queuedEvents = append(t.queuedEvents, event)
+}
diff --git a/vendor/github.com/Philipp15b/go-steam/trade/tradeapi/status.go b/vendor/github.com/Philipp15b/go-steam/trade/tradeapi/status.go
new file mode 100644
index 00000000..0a5278de
--- /dev/null
+++ b/vendor/github.com/Philipp15b/go-steam/trade/tradeapi/status.go
@@ -0,0 +1,111 @@
+package tradeapi
+
+import (
+ "encoding/json"
+ "github.com/Philipp15b/go-steam/jsont"
+ "github.com/Philipp15b/go-steam/steamid"
+ "strconv"
+)
+
+type Status struct {
+ Success bool
+ Error string
+ NewVersion bool `json:"newversion"`
+ TradeStatus TradeStatus `json:"trade_status"`
+ Version uint
+ LogPos int
+ Me User
+ Them User
+ Events EventList
+}
+
+type TradeStatus uint
+
+const (
+ TradeStatus_Open TradeStatus = 0
+ TradeStatus_Complete = 1
+ TradeStatus_Empty = 2 // when both parties trade no items
+ TradeStatus_Cancelled = 3
+ TradeStatus_Timeout = 4 // the partner timed out
+ TradeStatus_Failed = 5
+)
+
+type EventList map[uint]*Event
+
+// The EventList can either be an array or an object of id -> event
+func (e *EventList) UnmarshalJSON(data []byte) error {
+ // initialize the map if it's nil
+ if *e == nil {
+ *e = make(EventList)
+ }
+
+ o := make(map[string]*Event)
+ err := json.Unmarshal(data, &o)
+ // it's an object
+ if err == nil {
+ for is, event := range o {
+ i, err := strconv.ParseUint(is, 10, 32)
+ if err != nil {
+ panic(err)
+ }
+ (*e)[uint(i)] = event
+ }
+ return nil
+ }
+
+ // it's an array
+ var a []*Event
+ err = json.Unmarshal(data, &a)
+ if err != nil {
+ return err
+ }
+ for i, event := range a {
+ (*e)[uint(i)] = event
+ }
+ return nil
+}
+
+type Event struct {
+ SteamId steamid.SteamId `json:",string"`
+ Action Action `json:",string"`
+ Timestamp uint64
+
+ AppId uint32
+ ContextId uint64 `json:",string"`
+ AssetId uint64 `json:",string"`
+
+ Text string // only used for chat messages
+
+ // The following is used for SetCurrency
+ CurrencyId uint64 `json:",string"`
+ OldAmount uint64 `json:"old_amount,string"`
+ NewAmount uint64 `json:"amount,string"`
+}
+
+type Action uint
+
+const (
+ Action_AddItem Action = 0
+ Action_RemoveItem = 1
+ Action_Ready = 2
+ Action_Unready = 3
+ Action_Accept = 4
+ Action_SetCurrency = 6
+ Action_ChatMessage = 7
+)
+
+type User struct {
+ Ready jsont.UintBool
+ Confirmed jsont.UintBool
+ SecSinceTouch int `json:"sec_since_touch"`
+ ConnectionPending bool `json:"connection_pending"`
+ Assets interface{}
+ Currency interface{} // either []*Currency or empty string
+}
+
+type Currency struct {
+ AppId uint64 `json:",string"`
+ ContextId uint64 `json:",string"`
+ CurrencyId uint64 `json:",string"`
+ Amount uint64 `json:",string"`
+}
diff --git a/vendor/github.com/Philipp15b/go-steam/trade/tradeapi/trade.go b/vendor/github.com/Philipp15b/go-steam/trade/tradeapi/trade.go
new file mode 100644
index 00000000..5e9f2a98
--- /dev/null
+++ b/vendor/github.com/Philipp15b/go-steam/trade/tradeapi/trade.go
@@ -0,0 +1,200 @@
+/*
+Wrapper around the HTTP trading API for type safety 'n' stuff.
+*/
+package tradeapi
+
+import (
+ "encoding/json"
+ "errors"
+ "fmt"
+ "github.com/Philipp15b/go-steam/community"
+ "github.com/Philipp15b/go-steam/economy/inventory"
+ "github.com/Philipp15b/go-steam/netutil"
+ "github.com/Philipp15b/go-steam/steamid"
+ "io/ioutil"
+ "net/http"
+ "regexp"
+ "strconv"
+ "time"
+)
+
+const tradeUrl = "https://steamcommunity.com/trade/%d/"
+
+type Trade struct {
+ client *http.Client
+ other steamid.SteamId
+
+ LogPos uint // not automatically updated
+ Version uint // Incremented for each item change by Steam; not automatically updated.
+
+ // the `sessionid` cookie is sent as a parameter/POST data for CSRF protection.
+ sessionId string
+ baseUrl string
+}
+
+// Creates a new Trade based on the given cookies `sessionid`, `steamLogin`, `steamLoginSecure` and the trade partner's Steam ID.
+func New(sessionId, steamLogin, steamLoginSecure string, other steamid.SteamId) *Trade {
+ client := new(http.Client)
+ client.Timeout = 10 * time.Second
+
+ t := &Trade{
+ client: client,
+ other: other,
+ sessionId: sessionId,
+ baseUrl: fmt.Sprintf(tradeUrl, other),
+ Version: 1,
+ }
+ community.SetCookies(t.client, sessionId, steamLogin, steamLoginSecure)
+ return t
+}
+
+type Main struct {
+ PartnerOnProbation bool
+}
+
+var onProbationRegex = regexp.MustCompile(`var g_bTradePartnerProbation = (\w+);`)
+
+// Fetches the main HTML page and parses it. Thread-safe.
+func (t *Trade) GetMain() (*Main, error) {
+ resp, err := t.client.Get(t.baseUrl)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+ body, err := ioutil.ReadAll(resp.Body)
+ if err != nil {
+ return nil, err
+ }
+
+ match := onProbationRegex.FindSubmatch(body)
+ if len(match) == 0 {
+ return nil, errors.New("tradeapi.GetMain: Could not find probation info")
+ }
+
+ return &Main{
+ string(match[1]) == "true",
+ }, nil
+}
+
+// Ajax POSTs to an API endpoint that should return a status
+func (t *Trade) postWithStatus(url string, data map[string]string) (*Status, error) {
+ status := new(Status)
+
+ req := netutil.NewPostForm(url, netutil.ToUrlValues(data))
+ // Tales of Madness and Pain, Episode 1: If you forget this, Steam will return an error
+ // saying "missing required parameter", even though they are all there. IT WAS JUST THE HEADER, ARGH!
+ req.Header.Add("Referer", t.baseUrl)
+
+ resp, err := t.client.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ err = json.NewDecoder(resp.Body).Decode(status)
+ if err != nil {
+ return nil, err
+ }
+ return status, nil
+}
+
+func (t *Trade) GetStatus() (*Status, error) {
+ return t.postWithStatus(t.baseUrl+"tradestatus/", map[string]string{
+ "sessionid": t.sessionId,
+ "logpos": strconv.FormatUint(uint64(t.LogPos), 10),
+ "version": strconv.FormatUint(uint64(t.Version), 10),
+ })
+}
+
+// Thread-safe.
+func (t *Trade) GetForeignInventory(contextId uint64, appId uint32, start *uint) (*inventory.PartialInventory, error) {
+ data := map[string]string{
+ "sessionid": t.sessionId,
+ "steamid": fmt.Sprintf("%d", t.other),
+ "contextid": strconv.FormatUint(contextId, 10),
+ "appid": strconv.FormatUint(uint64(appId), 10),
+ }
+ if start != nil {
+ data["start"] = strconv.FormatUint(uint64(*start), 10)
+ }
+
+ req, err := http.NewRequest("GET", t.baseUrl+"foreigninventory?"+netutil.ToUrlValues(data).Encode(), nil)
+ if err != nil {
+ panic(err)
+ }
+ req.Header.Add("Referer", t.baseUrl)
+
+ return inventory.DoInventoryRequest(t.client, req)
+}
+
+// Thread-safe.
+func (t *Trade) GetOwnInventory(contextId uint64, appId uint32) (*inventory.Inventory, error) {
+ return inventory.GetOwnInventory(t.client, contextId, appId)
+}
+
+func (t *Trade) Chat(message string) (*Status, error) {
+ return t.postWithStatus(t.baseUrl+"chat", map[string]string{
+ "sessionid": t.sessionId,
+ "logpos": strconv.FormatUint(uint64(t.LogPos), 10),
+ "version": strconv.FormatUint(uint64(t.Version), 10),
+ "message": message,
+ })
+}
+
+func (t *Trade) AddItem(slot uint, itemId, contextId uint64, appId uint32) (*Status, error) {
+ return t.postWithStatus(t.baseUrl+"additem", map[string]string{
+ "sessionid": t.sessionId,
+ "slot": strconv.FormatUint(uint64(slot), 10),
+ "itemid": strconv.FormatUint(itemId, 10),
+ "contextid": strconv.FormatUint(contextId, 10),
+ "appid": strconv.FormatUint(uint64(appId), 10),
+ })
+}
+
+func (t *Trade) RemoveItem(slot uint, itemId, contextId uint64, appId uint32) (*Status, error) {
+ return t.postWithStatus(t.baseUrl+"removeitem", map[string]string{
+ "sessionid": t.sessionId,
+ "slot": strconv.FormatUint(uint64(slot), 10),
+ "itemid": strconv.FormatUint(itemId, 10),
+ "contextid": strconv.FormatUint(contextId, 10),
+ "appid": strconv.FormatUint(uint64(appId), 10),
+ })
+}
+
+func (t *Trade) SetCurrency(amount uint, currencyId, contextId uint64, appId uint32) (*Status, error) {
+ return t.postWithStatus(t.baseUrl+"setcurrency", map[string]string{
+ "sessionid": t.sessionId,
+ "amount": strconv.FormatUint(uint64(amount), 10),
+ "currencyid": strconv.FormatUint(uint64(currencyId), 10),
+ "contextid": strconv.FormatUint(contextId, 10),
+ "appid": strconv.FormatUint(uint64(appId), 10),
+ })
+}
+
+func (t *Trade) SetReady(ready bool) (*Status, error) {
+ return t.postWithStatus(t.baseUrl+"toggleready", map[string]string{
+ "sessionid": t.sessionId,
+ "version": strconv.FormatUint(uint64(t.Version), 10),
+ "ready": fmt.Sprint(ready),
+ })
+}
+
+func (t *Trade) Confirm() (*Status, error) {
+ return t.postWithStatus(t.baseUrl+"confirm", map[string]string{
+ "sessionid": t.sessionId,
+ "version": strconv.FormatUint(uint64(t.Version), 10),
+ })
+}
+
+func (t *Trade) Cancel() (*Status, error) {
+ return t.postWithStatus(t.baseUrl+"cancel", map[string]string{
+ "sessionid": t.sessionId,
+ })
+}
+
+func isSuccess(v interface{}) bool {
+ if m, ok := v.(map[string]interface{}); ok {
+ return m["success"] == true
+ }
+ return false
+}
diff --git a/vendor/github.com/Philipp15b/go-steam/trade/types.go b/vendor/github.com/Philipp15b/go-steam/trade/types.go
new file mode 100644
index 00000000..e6f9a537
--- /dev/null
+++ b/vendor/github.com/Philipp15b/go-steam/trade/types.go
@@ -0,0 +1,67 @@
+package trade
+
+import (
+ "github.com/Philipp15b/go-steam/trade/tradeapi"
+)
+
+type TradeEndedEvent struct {
+ Reason TradeEndReason
+}
+
+type TradeEndReason uint
+
+const (
+ TradeEndReason_Complete TradeEndReason = 1
+ TradeEndReason_Cancelled = 2
+ TradeEndReason_Timeout = 3
+ TradeEndReason_Failed = 4
+)
+
+func newItem(event *tradeapi.Event) *Item {
+ return &Item{
+ event.AppId,
+ event.ContextId,
+ event.AssetId,
+ }
+}
+
+type Item struct {
+ AppId uint32
+ ContextId uint64
+ AssetId uint64
+}
+
+type ItemAddedEvent struct {
+ Item *Item
+}
+
+type ItemRemovedEvent struct {
+ Item *Item
+}
+
+type ReadyEvent struct{}
+type UnreadyEvent struct{}
+
+func newCurrency(event *tradeapi.Event) *Currency {
+ return &Currency{
+ event.AppId,
+ event.ContextId,
+ event.CurrencyId,
+ }
+}
+
+type Currency struct {
+ AppId uint32
+ ContextId uint64
+ CurrencyId uint64
+}
+
+type SetCurrencyEvent struct {
+ Currency *Currency
+ OldAmount uint64
+ NewAmount uint64
+}
+
+type ChatEvent struct {
+ Message string
+}