forked from Philipp15b/go-steam
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
3695e24
commit 87f4ff5
Showing
10 changed files
with
898 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
// Includes helper types for working with JSON data | ||
package jsont | ||
|
||
import ( | ||
"encoding/json" | ||
) | ||
|
||
// A boolean value that can be unmarshaled from a number in JSON. | ||
type UintBool bool | ||
|
||
func (u *UintBool) UnmarshalJSON(data []byte) error { | ||
var n uint | ||
err := json.Unmarshal(data, &n) | ||
if err != nil { | ||
return err | ||
} | ||
*u = n != 0 | ||
return nil | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
package trade | ||
|
||
import ( | ||
"errors" | ||
"github.com/Philipp15b/go-steam/trade/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) getFullInventory(getFirst func() (*inventory.PartialInventory, error), getNext func(start uint) (*inventory.PartialInventory, error)) (*inventory.Inventory, error) { | ||
first, err := getFirst() | ||
if err != nil { | ||
return nil, err | ||
} | ||
if !first.Success { | ||
return nil, errors.New("getFullInventory API call failed.") | ||
} | ||
|
||
result := &first.Inventory | ||
var next *inventory.PartialInventory | ||
for latest := first; latest.More; latest = next { | ||
next, err := getNext(uint(latest.MoreStart)) | ||
if err != nil { | ||
return nil, err | ||
} | ||
if !next.Success { | ||
return nil, errors.New("getFullInventory API call failed.") | ||
} | ||
|
||
result = inventory.Merge(result, &next.Inventory) | ||
} | ||
|
||
return result, nil | ||
} | ||
|
||
func (t *Trade) GetTheirInventory(contextId uint64, appId uint32) (*inventory.Inventory, error) { | ||
return t.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.getFullInventory(func() (*inventory.PartialInventory, error) { | ||
return t.api.GetOwnInventory(contextId, appId, nil) | ||
}, func(start uint) (*inventory.PartialInventory, error) { | ||
return t.api.GetOwnInventory(contextId, appId, &start) | ||
}) | ||
} | ||
|
||
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()) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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, 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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
package inventory | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
"github.com/Philipp15b/go-steam/jsont" | ||
) | ||
|
||
type Inventory struct { | ||
Items Items `json:"rgInventory"` | ||
Currencies Currencies `json:"rgCurrency"` | ||
Descriptions Descriptions `json:"rgDescriptions"` | ||
AppInfo *AppInfo `json:"rgAppInfo"` | ||
} | ||
|
||
type Items map[string]*Item | ||
|
||
func (i *Items) UnmarshalJSON(data []byte) error { | ||
if bytes.Equal(data, []byte("[]")) { | ||
return nil | ||
} | ||
return json.Unmarshal(data, (*map[string]*Item)(i)) | ||
} | ||
|
||
type Currencies map[string]*Currency | ||
|
||
func (c *Currencies) UnmarshalJSON(data []byte) error { | ||
if bytes.Equal(data, []byte("[]")) { | ||
return nil | ||
} | ||
return json.Unmarshal(data, (*map[string]*Currency)(c)) | ||
} | ||
|
||
type Descriptions map[string]*Description | ||
|
||
func (d *Descriptions) UnmarshalJSON(data []byte) error { | ||
if bytes.Equal(data, []byte("[]")) { | ||
return nil | ||
} | ||
return json.Unmarshal(data, (*map[string]*Description)(d)) | ||
} | ||
|
||
type Item struct { | ||
Id uint64 `json:",string"` | ||
ClassId uint64 `json:",string"` | ||
InstanceId uint64 `json:",string"` | ||
Amount uint64 `json:",string"` | ||
Pos uint32 | ||
} | ||
|
||
type Currency struct { | ||
Id uint64 `json:",string"` | ||
ClassId uint64 `json:",string"` | ||
IsCurrency bool `json:"is_currency"` | ||
Pos uint32 | ||
} | ||
|
||
type Description struct { | ||
AppId uint32 `json:",string"` | ||
ClassId uint64 `json:",string"` | ||
InstanceId uint64 `json:",string"` | ||
|
||
IconUrl string `json:"icon_url"` | ||
IconDragUrl string `json:"icon_drag_url"` | ||
|
||
Name string | ||
MarketName string `json:"market_name"` | ||
|
||
// Colors in hex, for example `B2B2B2` | ||
NameColor string `json:"name_color"` | ||
BackgroundColor string `json:"background_color"` | ||
|
||
Type string | ||
|
||
Tradable jsont.UintBool | ||
Marketable jsont.UintBool | ||
Commodity jsont.UintBool | ||
|
||
Descriptions DescriptionLines | ||
Actions []*Action | ||
// Application-specific data, like "def_index" and "quality" for TF2 | ||
AppData map[string]string | ||
} | ||
|
||
type DescriptionLines []*DescriptionLine | ||
|
||
func (d *DescriptionLines) UnmarshalJSON(data []byte) error { | ||
if bytes.Equal(data, []byte(`""`)) { | ||
return nil | ||
} | ||
return json.Unmarshal(data, (*[]*DescriptionLine)(d)) | ||
} | ||
|
||
type DescriptionLine struct { | ||
Value string | ||
Type *string // Is `html` for HTML descriptions | ||
Label *string | ||
} | ||
|
||
type Action struct { | ||
Name string | ||
Link string | ||
} | ||
|
||
type AppInfo struct { | ||
AppId uint32 | ||
Name string | ||
Icon string | ||
Link string | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
package inventory | ||
|
||
import ( | ||
"bytes" | ||
"encoding/json" | ||
) | ||
|
||
// A partial inventory as sent by the Steam API. | ||
type PartialInventory struct { | ||
Success bool | ||
Inventory | ||
More bool | ||
MoreStart MoreStart `json:"more_start"` | ||
} | ||
|
||
type MoreStart uint | ||
|
||
func (m *MoreStart) UnmarshalJSON(data []byte) error { | ||
if bytes.Equal(data, []byte("false")) { | ||
return nil | ||
} | ||
return json.Unmarshal(data, (*uint)(m)) | ||
} | ||
|
||
// Merges the given Inventory into a single Inventory. | ||
// The given slice must have at least one element. The first element of the slice is used | ||
// and modified. | ||
func Merge(p ...*Inventory) *Inventory { | ||
inv := p[0] | ||
for idx, i := range p { | ||
if idx == 0 { | ||
continue | ||
} | ||
|
||
for key, value := range i.Items { | ||
inv.Items[key] = value | ||
} | ||
for key, value := range i.Descriptions { | ||
inv.Descriptions[key] = value | ||
} | ||
for key, value := range i.Currencies { | ||
inv.Currencies[key] = value | ||
} | ||
} | ||
|
||
return inv | ||
} |
Oops, something went wrong.