4
0
mirror of https://github.com/cwinfo/matterbridge.git synced 2025-06-27 21:39:22 +00:00

Add vendor (steam)

This commit is contained in:
Wim
2017-06-22 01:00:27 +02:00
parent 1f9874102a
commit 1f91461853
117 changed files with 115543 additions and 0 deletions

View File

@ -0,0 +1,188 @@
/*
Includes inventory types as used in the trade package
*/
package inventory
import (
"bytes"
"encoding/json"
"fmt"
"github.com/Philipp15b/go-steam/jsont"
"strconv"
)
type GenericInventory map[uint32]map[uint64]*Inventory
func NewGenericInventory() GenericInventory {
iMap := make(map[uint32]map[uint64]*Inventory)
return GenericInventory(iMap)
}
// Get inventory for specified AppId and ContextId
func (i *GenericInventory) Get(appId uint32, contextId uint64) (*Inventory, error) {
iMap := (map[uint32]map[uint64]*Inventory)(*i)
iMap2, ok := iMap[appId]
if !ok {
return nil, fmt.Errorf("inventory for specified appId not found")
}
inv, ok := iMap2[contextId]
if !ok {
return nil, fmt.Errorf("inventory for specified contextId not found")
}
return inv, nil
}
func (i *GenericInventory) Add(appId uint32, contextId uint64, inv *Inventory) {
iMap := (map[uint32]map[uint64]*Inventory)(*i)
iMap2, ok := iMap[appId]
if !ok {
iMap2 = make(map[uint64]*Inventory)
iMap[appId] = iMap2
}
iMap2[contextId] = inv
}
type Inventory struct {
Items Items `json:"rgInventory"`
Currencies Currencies `json:"rgCurrency"`
Descriptions Descriptions `json:"rgDescriptions"`
AppInfo *AppInfo `json:"rgAppInfo"`
}
// Items key is an AssetId
type Items map[string]*Item
func (i *Items) ToMap() map[string]*Item {
return (map[string]*Item)(*i)
}
func (i *Items) Get(assetId uint64) (*Item, error) {
iMap := (map[string]*Item)(*i)
if item, ok := iMap[strconv.FormatUint(assetId, 10)]; ok {
return item, nil
}
return nil, fmt.Errorf("item not found")
}
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) ToMap() map[string]*Currency {
return (map[string]*Currency)(*c)
}
func (c *Currencies) UnmarshalJSON(data []byte) error {
if bytes.Equal(data, []byte("[]")) {
return nil
}
return json.Unmarshal(data, (*map[string]*Currency)(c))
}
// Descriptions key format is %d_%d, first %d is ClassId, second is InstanceId
type Descriptions map[string]*Description
func (d *Descriptions) ToMap() map[string]*Description {
return (map[string]*Description)(*d)
}
func (d *Descriptions) Get(classId uint64, instanceId uint64) (*Description, error) {
dMap := (map[string]*Description)(*d)
descId := fmt.Sprintf("%v_%v", classId, instanceId)
if desc, ok := dMap[descId]; ok {
return desc, nil
}
return nil, fmt.Errorf("description not found")
}
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"`
IconUrlLarge string `json:"icon_url_large"`
IconDragUrl string `json:"icon_drag_url"`
Name string
MarketName string `json:"market_name"`
MarketHashName string `json:"market_hash_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
MarketTradableRestriction uint32 `json:"market_tradable_restriction,string"`
Descriptions DescriptionLines
Actions []*Action
// Application-specific data, like "def_index" and "quality" for TF2
AppData map[string]string
Tags []*Tag
}
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
Color *string
}
type Action struct {
Name string
Link string
}
type AppInfo struct {
AppId uint32
Name string
Icon string
Link string
}
type Tag struct {
InternalName string `json:internal_name`
Name string
Category string
CategoryName string `json:category_name`
}

View File

@ -0,0 +1,79 @@
package inventory
import (
"encoding/json"
"fmt"
"github.com/Philipp15b/go-steam/steamid"
"io/ioutil"
"net/http"
"regexp"
"strconv"
)
type InventoryApps map[string]*InventoryApp
func (i *InventoryApps) Get(appId uint32) (*InventoryApp, error) {
iMap := (map[string]*InventoryApp)(*i)
if inventoryApp, ok := iMap[strconv.FormatUint(uint64(appId), 10)]; ok {
return inventoryApp, nil
}
return nil, fmt.Errorf("inventory app not found")
}
func (i *InventoryApps) ToMap() map[string]*InventoryApp {
return (map[string]*InventoryApp)(*i)
}
type InventoryApp struct {
AppId uint32
Name string
Icon string
Link string
AssetCount uint32 `json:"asset_count"`
InventoryLogo string `json:"inventory_logo"`
TradePermissions string `json:"trade_permissions"`
Contexts Contexts `json:"rgContexts"`
}
type Contexts map[string]*Context
func (c *Contexts) Get(contextId uint64) (*Context, error) {
cMap := (map[string]*Context)(*c)
if context, ok := cMap[strconv.FormatUint(contextId, 10)]; ok {
return context, nil
}
return nil, fmt.Errorf("context not found")
}
func (c *Contexts) ToMap() map[string]*Context {
return (map[string]*Context)(*c)
}
type Context struct {
ContextId uint64 `json:"id,string"`
AssetCount uint32 `json:"asset_count"`
Name string
}
func GetInventoryApps(client *http.Client, steamId steamid.SteamId) (InventoryApps, error) {
resp, err := http.Get("http://steamcommunity.com/profiles/" + steamId.ToString() + "/inventory/")
if err != nil {
return nil, err
}
defer resp.Body.Close()
respBody, err := ioutil.ReadAll(resp.Body)
if err != nil {
return nil, err
}
reg := regexp.MustCompile("var g_rgAppContextData = (.*?);")
inventoryAppsMatches := reg.FindSubmatch(respBody)
if inventoryAppsMatches == nil {
return nil, fmt.Errorf("profile inventory not found in steam response")
}
var inventoryApps InventoryApps
if err = json.Unmarshal(inventoryAppsMatches[1], &inventoryApps); err != nil {
return nil, err
}
return inventoryApps, nil
}

View File

@ -0,0 +1,28 @@
package inventory
import (
"fmt"
"net/http"
"strconv"
)
func GetPartialOwnInventory(client *http.Client, contextId uint64, appId uint32, start *uint) (*PartialInventory, error) {
// TODO: the "trading" parameter can be left off to return non-tradable items too
url := fmt.Sprintf("http://steamcommunity.com/my/inventory/json/%d/%d?trading=1", appId, contextId)
if start != nil {
url += "&start=" + strconv.FormatUint(uint64(*start), 10)
}
req, err := http.NewRequest("GET", url, nil)
if err != nil {
panic(err)
}
return DoInventoryRequest(client, req)
}
func GetOwnInventory(client *http.Client, contextId uint64, appId uint32) (*Inventory, error) {
return GetFullInventory(func() (*PartialInventory, error) {
return GetPartialOwnInventory(client, contextId, appId, nil)
}, func(start uint) (*PartialInventory, error) {
return GetPartialOwnInventory(client, contextId, appId, &start)
})
}

View File

@ -0,0 +1,91 @@
package inventory
import (
"bytes"
"encoding/json"
"errors"
"net/http"
)
// A partial inventory as sent by the Steam API.
type PartialInventory struct {
Success bool
Error string
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))
}
func DoInventoryRequest(client *http.Client, req *http.Request) (*PartialInventory, error) {
resp, err := client.Do(req)
if err != nil {
return nil, err
}
defer resp.Body.Close()
inv := new(PartialInventory)
err = json.NewDecoder(resp.Body).Decode(inv)
if err != nil {
return nil, err
}
return inv, nil
}
func GetFullInventory(getFirst func() (*PartialInventory, error), getNext func(start uint) (*PartialInventory, error)) (*Inventory, error) {
first, err := getFirst()
if err != nil {
return nil, err
}
if !first.Success {
return nil, errors.New("GetFullInventory API call failed: " + first.Error)
}
result := &first.Inventory
var next *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: " + next.Error)
}
result = Merge(result, &next.Inventory)
}
return result, nil
}
// 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
}