steam-trade 0.0.4 → 0.0.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d174f053f0d7882ecbb2ec1af0cf9c56e8399d94
4
- data.tar.gz: 07d6eefabd7144cc36467186835c7d58d3bff31b
3
+ metadata.gz: 34bdbd304683a4b23f616f4b8ebfb86f05c6a5f5
4
+ data.tar.gz: 0af0c76c9407399eaec724220221219bba88382b
5
5
  SHA512:
6
- metadata.gz: 3337852cc85e4fcf85c65f22b263927c3cb470456b19fbdf285ea7bdb7448c6e6e109e6f7c6de366f603fa53c1bc4118dccfc2879f61b12e185efe1e45c8aa3a
7
- data.tar.gz: 3ded4edd8fb64a7c9d097c207670e4744d9e6d913929e1699f7271ec50ac8e6d81fd266f29ab7909063642d40f4f9efa19d64687d69dc3117f8339ec233c6349
6
+ metadata.gz: c44ba8afd2b5441d8b52b039ef7260473e11a6a23116021073dbd6c260132c8bfe0b856efce97ccc1446bb874c30666c9acd04df9ddba804c7c27b0b790d082f
7
+ data.tar.gz: f8f65ea446d9a23a221aad67baa992ea0d5805df846ad1ff5920eb972128d7f094d54650637290ad82b6937b2fbf352e70f36494a0534b506ebeeb2a1ba7cb8d
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # steam-trade
2
2
 
3
- This gem simplifes/allows sending steam trade offers programmatically
3
+ This gem simplifes/allows sending steam trade offers programmatically.
4
4
 
5
+ this gem is primarly for trading cards, tho can be used to CS:GO and other games inventories
5
6
  ## Installation
6
7
  in your commandline :
7
8
 
@@ -12,7 +13,7 @@ First you need to require the gem:
12
13
  ```ruby
13
14
  require 'steam-trade'
14
15
  ```
15
-
16
+ ## Logging-in
16
17
  then you need to login and optionally set your shared_secret and identity_secret:
17
18
  - `shared_secret` is used to generate steam authentication codes so you won't have to write them manually each time you login.
18
19
  - `identity_secret` is used to confirm trade offers automatically.
@@ -24,14 +25,42 @@ account = Handler.new('username','password','shared_secret') # share secret is o
24
25
 
25
26
 
26
27
  account.mobile_info('identity_secret')
27
- #identity_secret is requred
28
+ #identity_secret is required
28
29
 
29
30
  ```
31
+ ## Getting someone's inventory
32
+ #### `normal_get_inventory('steamid','inventoryappid','contextid')`
33
+ - `steamid` is the target's steamid
34
+ - `inventoryappid` is the inventory type you want to load, ex : normal inventory(the one which holds trading cards), it's is 753
35
+ - `contextid` required to make a correct request to steam servers, ex:for normal inventory(the one which holds trading cards), it's is 6
36
+ - if you call `normal_get_inventory()` with no params, it will be default use the current logged-in account `steamid`, `inventoryappid = 753` and `contextid = 6`
37
+
38
+ ```ruby
39
+ require 'steam-trade'
40
+
41
+ account = Handler.new('username','password','shared_secret')
42
+ account.mobile_info('identity_secret')
43
+
44
+ my_inventory = normal_get_inventory() #if you are logged in you can call this command with no parameters to get the logged account's inventory
45
+
46
+ partner_inventory = normal_get_inventory('76561198044170935')
47
+ ```
48
+ the returned items from `normal_get_inventory()` are in the form of an array example : `[item1,item2,item3...itemN]`.
49
+
50
+ each item is a hash which contains information about the item in the form of `{"appid"=>'xxx',"contextid"=>'xxx',"assetid" => 'xxx',"classid"=> 'xxx',......"name"=> 'xxxx',"market_fee_app" => 'xxx',....}`.
51
+
52
+ `market_fee_app` key gives you the appid of the game's app ( for trading cards), for other items technically `inventoryappid` is the games appid.
53
+
54
+ `name` key gives you the item name.
55
+ ####
30
56
 
31
- then you can send your offer
57
+ **IMPORTANT**: `normal_get_inventory()` will load the whole target inventory, for each **5k** of items, you are adding **~40MB** to your memory and of course will affect performance of the code and the computer
58
+ ## Sending a trade offer
59
+ then you can send your offer
32
60
  - `Myarray` is an array which contains hashes of selected items to send in the offer. (currently you must get this alone)
33
61
  - `Theirarray` is an array which contains hashes of selected items to receive in the offer. (currently you must get this alone)
34
62
  - `trade_offer_link` is the trade link of you partner `ex: https://steamcommunity.com/tradeoffer/new/?partner=410155236&token=H-yK-GFt`
63
+ - `trade_offer_link` can also be a steamID, however using a steamID requires you and your partner to be friends on steam
35
64
  - `message` is the comment you want to include in the trade offer
36
65
 
37
66
  - `Myarray`, `Theirarray`, `trade_offer_link` are required, `message` is optional
@@ -0,0 +1,133 @@
1
+ module BadgeCommands
2
+
3
+ def sets_count(steamid = @steamid, use_nonmarketable = true)
4
+
5
+
6
+ if steamid == nil
7
+ output "no steamid specified"
8
+ return
9
+ end
10
+
11
+ thread = Thread.new(steamid) { |steamid| ##getting name
12
+ targetname = ''
13
+ begin
14
+ data = get_player_summaries(steamid)
15
+ data.each { |acc|
16
+ if id["steamid"].to_s == steamid.to_s
17
+ targetname = id["personaname"]
18
+ end
19
+ }
20
+ rescue
21
+ targetname = ''
22
+ end
23
+ }
24
+
25
+ items = normal_get_inventory(steamid)
26
+ sorted = {}
27
+ items.each { |asset|
28
+ if use_nonmarketable == false
29
+ if asset["marketable"] == 0 || asset["tags"][-1]["localized_tag_name"] != "Trading Card" || asset["tags"][-2]["localized_tag_name"] == "Foil"
30
+ next
31
+ end
32
+ else
33
+ if asset["tags"][-1]["localized_tag_name"] != "Trading Card" || asset["tags"][-2]["localized_tag_name"] == "Foil"
34
+ next
35
+ end
36
+ end
37
+
38
+ name = asset["name"].sub(" (Trading Card)", "")
39
+ appid = asset["market_fee_app"].to_s
40
+ if sorted.has_key?(appid) == true
41
+ if sorted[appid].has_key?(name) == true
42
+ sorted[appid][name] = sorted[appid][name] + 1
43
+ elsif sorted[appid].has_key?(name) == false
44
+ sorted[appid][name] = 1
45
+ end
46
+ elsif sorted.has_key?(appid) == false
47
+ sorted[appid] = {}
48
+ sorted[appid][name] = 1
49
+ end
50
+ }
51
+
52
+ bigdata = JSON.parse(File.read("#{@libdir}/blueprints/byappid.json",:external_encoding => 'utf-8',:internal_encoding => 'utf-8'))
53
+ counted = {}
54
+
55
+ sorted.each { |appid,cards|
56
+
57
+
58
+ counted[appid] = bigdata[appid].merge(cards)
59
+ }
60
+
61
+ setsowned = {}
62
+ numberofsets = 0
63
+ total_non_foil = 0
64
+
65
+ counted.each { |appid,cards|
66
+ lowest = 9999
67
+ cards.each { |cardname, amount|
68
+ if amount < lowest then lowest = amount end
69
+ total_non_foil = total_non_foil + amount
70
+ }
71
+ setsowned[appid] = lowest
72
+ numberofsets = numberofsets + lowest
73
+ }
74
+
75
+ persona = thread.value
76
+ write_badges(counted,setsowned,numberofsets,total_non_foil, use_nonmarketable,persona,steamid)
77
+ if use_nonmarketable == false
78
+ return {'sets' => counted, 'appxsets' => setsowned, 'totalsets' => numberofsets, 'totalcards' => total_non_foil, 'marketable' => false}
79
+ else
80
+ return {'sets' => counted, 'appxsets' => setsowned, 'totalsets' => numberofsets, 'totalcards' => total_non_foil, 'marketable' => true}
81
+ end
82
+ end
83
+
84
+
85
+ private
86
+ def write_badges(hashofcards,eachappidsets,totalsets,total_non_foil,use_nonmarketable,persona,steamid)
87
+ if persona == ''
88
+ filename = steamid
89
+ else
90
+ filename = persona
91
+ end
92
+
93
+ "./#{filename}_badges.txt"
94
+ titles = JSON.parse(File.read("#{@libdir}/blueprints/appid_title.json",:external_encoding => 'utf-8',:internal_encoding => 'utf-8'))
95
+ eachappidsets = eachappidsets.sort_by do |k,v|
96
+ v
97
+ end
98
+ eachappidsets.reverse!
99
+ begin
100
+ File.truncate("./#{filename}_badges.txt", 0)
101
+ rescue
102
+ end
103
+
104
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts "for #{persona}(#{steamid})"}
105
+ if use_nonmarketable == false
106
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts "total non-foil trading cards #{total_non_foil}"}
107
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts "only marketable cards are counted"}
108
+ else
109
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts "total non-foil trading cards #{total_non_foil}"}
110
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts "all cards counted including non-marketable"}
111
+ end
112
+
113
+
114
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts "total sets in target account #{totalsets}"}
115
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts ""}
116
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts ""}
117
+
118
+
119
+ eachappidsets.each { |appid, sets|
120
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts "#{titles[appid]}, sets = #{sets}, appid = #{appid}"}
121
+ hashofcards[appid].each { |cardname, owned|
122
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts "#{cardname} xxx #{owned}"}
123
+ }
124
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts ""}
125
+ File.open("./#{filename}_badges.txt",'a+:UTF-8') {|f| f.puts ""}
126
+ }
127
+
128
+ output "badges.txt has been created"
129
+ end
130
+
131
+
132
+
133
+ end
@@ -1,8 +1,8 @@
1
- require 'Guard.rb'
1
+
2
2
 
3
3
 
4
4
  module ConfirmationCommands
5
- include GuardCommands
5
+
6
6
 
7
7
 
8
8
 
@@ -54,8 +54,7 @@ module ConfirmationCommands
54
54
  response = @session.get('https://steamcommunity.com/mobileconf/conf', params, no, headers)
55
55
  html = response.content
56
56
  if html.include?('Steam Guard Mobile Authenticator is providing incorrect Steam Guard codes.')
57
- puts "invalid steammguard"
58
- exit
57
+ raise("identity secret: #{@identity_secret} is incorrect")
59
58
  end
60
59
  return html
61
60
  end
@@ -71,8 +70,7 @@ module ConfirmationCommands
71
70
  return confirmhash
72
71
  end
73
72
  }
74
- puts "did not find a confirmation"
75
- exit
73
+ raise("Could not find the offer to confirm")
76
74
  end
77
75
 
78
76
  def fetch_confirmation_details_page(hash) ##eigth
@@ -4,13 +4,14 @@ module GuardCommands
4
4
 
5
5
 
6
6
 
7
- def fa()
7
+ def fa(shared_secret = @secret)
8
+ raise "No shared_secret given" if shared_secret == nil # cause upon initialization @secret = nil
8
9
  timestamp = Time.new.to_i
9
10
  math = timestamp / 30
10
11
  math = math.to_i
11
12
  time_buffer =[math].pack('Q>')
12
13
 
13
- hmac = OpenSSL::HMAC.digest('sha1', Base64.decode64(@secret), time_buffer)
14
+ hmac = OpenSSL::HMAC.digest('sha1', Base64.decode64(shared_secret), time_buffer)
14
15
 
15
16
  start = hmac[19].ord & 0xf
16
17
  last = start + 4
@@ -29,6 +30,7 @@ module GuardCommands
29
30
 
30
31
  end
31
32
 
33
+ private
32
34
  def generate_confirmation_key(tag_string, time_stamp)
33
35
  buffer = [time_stamp].pack('Q>') + tag_string.encode('ascii')
34
36
  return Base64.encode64(OpenSSL::HMAC.digest('sha1', Base64.decode64(@identity_secret), buffer))
@@ -0,0 +1,111 @@
1
+ module InventoryCommands
2
+
3
+
4
+
5
+
6
+
7
+ def normal_get_inventory(steamid = @steamid ,appid = 753,context = 6)
8
+ if steamid == nil
9
+ output "no steamid specified"
10
+ return
11
+ elsif steamid.to_i == 0 && steamid.include?("?partner=") ##supplied trade link
12
+ partner_raw = steamid.split('partner=',2)[1].split('&',2)[0]
13
+ steamid = partner_id_to_steam_id(partner_raw)
14
+ end
15
+
16
+
17
+ if @inventory_cache == true
18
+ verdict = verify_inventory_cache(steamid)
19
+ if verdict != false
20
+ return verdict
21
+ end
22
+ end
23
+
24
+
25
+ items = []
26
+ last_id = 0
27
+ until last_id == false
28
+ received = get_inventory_chunk_normal_way(appid,context,steamid,last_id)
29
+ last_id = received['new_last_id']
30
+ items = items + received['assets']
31
+ output "loaded #{items.length}"
32
+ sleep(2) if last_id != false
33
+ end
34
+
35
+ output "total loaded #{items.length} asset"
36
+ if @inventory_cache == true
37
+ File.open("./#{steamid}.inventory", 'w') {|f| f.puts items.to_json}
38
+ end
39
+
40
+ return items
41
+ end
42
+
43
+ private
44
+ def get_inventory_chunk_normal_way(appid,context,steamid,last_id)
45
+
46
+
47
+ html = open("https://steamcommunity.com/inventory/#{steamid}/#{appid}/#{context}?start_assetid=#{last_id}&count=5000").read
48
+
49
+ get = JSON.parse(html)
50
+
51
+ if get.keys[3].to_s == "last_assetid"
52
+
53
+ new_last_id = get.values[3].to_s
54
+
55
+ else
56
+ new_last_id = false
57
+
58
+ end
59
+
60
+ assets = get["assets"]
61
+ descriptions = get["descriptions"]
62
+
63
+
64
+ descriptions_classids = {} ###sorting descriptions by key value || key is classid of the item's description
65
+ descriptions.each {|description|
66
+ classidxinstance = description["classid"] + '_' + description["instanceid"] # some items has the same classid but different instane id
67
+ descriptions_classids[classidxinstance] = description
68
+ }
69
+
70
+ assets.each { |asset| ## merging assets with names
71
+ classidxinstane = asset["classid"] + '_' + asset["instanceid"]
72
+ asset.replace(asset.merge(descriptions_classids[classidxinstance]))
73
+ }
74
+
75
+
76
+ return {'assets' => assets, 'new_last_id' =>new_last_id}
77
+
78
+ end
79
+
80
+
81
+
82
+
83
+
84
+ def verify_inventory_cache(steamid)
85
+ if File.exists?("./#{steamid}.inventory") == false
86
+ return false
87
+ end
88
+ puts File.mtime("./#{steamid}.inventory").to_s
89
+ puts Time.now.to_s
90
+ file_last_time = Time.parse(File.mtime("./#{steamid}.inventory").to_s)
91
+ current_time = Time.parse(Time.now.to_s)
92
+ calcule = current_time - file_last_time
93
+ puts "difference #{calcule}"
94
+ if calcule.to_i > @inventory_validity
95
+ File.delete("./#{steamid}.inventory")
96
+ return false
97
+ else
98
+ output "gonna use cached inventory which is #{calcule} seconds old"
99
+ begin
100
+ return JSON.parse(File.read("./#{steamid}.inventory",:external_encoding => 'utf-8',:internal_encoding => 'utf-8'))
101
+ rescue
102
+ File.delete("./#{steamid}.inventory")
103
+ return false
104
+ end
105
+ end
106
+
107
+ end
108
+
109
+
110
+
111
+ end
@@ -1,7 +1,7 @@
1
1
  module LoginCommands
2
2
 
3
3
  ########################################################################################
4
- def login
4
+ def login()
5
5
  data = pass_stamp()
6
6
  encrypted_password = data["password"]
7
7
  timestamp = data["timestamp"]
@@ -30,13 +30,11 @@ module LoginCommands
30
30
 
31
31
  login = @session.post('https://store.steampowered.com/login/dologin', send )
32
32
  response = JSON::parse(login.body)
33
-
33
+ output "logging-in"
34
34
  if response["success"] == true
35
35
  repeater = true
36
36
  elsif repeater == 3
37
- puts "Could not login"
38
- puts "exiting"
39
- exit
37
+ raise "Login failed username: #{@username}, password: #{@password} tried 3 times"
40
38
  else
41
39
  puts "re-trying to login"
42
40
  puts "sleeping for 6 seconds"
@@ -71,7 +69,18 @@ module LoginCommands
71
69
 
72
70
  cookie = Mechanize::Cookie.new :domain => 'steamcommunity.com', :name =>'sessionid', :value =>steampowered_sessionid, :path => '/'
73
71
  @session.cookie_jar << cookie
74
- puts "logged-in with steamid: #{@steamid}"
72
+ @loggedin = true
73
+ @api_key = Nokogiri::HTML(@session.get("https://steamcommunity.com/dev/apikey").content).css('#bodyContents_ex').css('p').first.text.sub('Key: ','')
74
+
75
+ data = get_player_summaries(@steamid)
76
+ data.each { |element|
77
+ if element["steamid"].to_s == @steamid.to_s
78
+ @persona = element["personaname"]
79
+ end
80
+ }
81
+ output "logged in as #{@persona}"
82
+ output "your steamid is #{@steamid}"
83
+ output "loaded API_KEY : #{@api_key}"
75
84
  end
76
85
  ########################################################################################
77
86
 
@@ -1,5 +1,17 @@
1
- module Abilites
1
+
2
+ module MiscCommands
3
+
4
+
5
+
6
+
7
+
2
8
  ########################################################################################
9
+ def set_steamid(steamid)
10
+ @steamid = steamid
11
+ end
12
+
13
+
14
+
3
15
  def copy_session
4
16
  return @session
5
17
  end
@@ -37,6 +49,10 @@ module Abilites
37
49
 
38
50
 
39
51
 
52
+
53
+
54
+
55
+
40
56
  private
41
57
  def partner_id_to_steam_id(account_id)
42
58
  unknown_constant = 17825793 # or 0x1100001 idk wtf is this but ....
@@ -45,6 +61,26 @@ module Abilites
45
61
  collect = last_bytes + first_bytes
46
62
  return collect.unpack('Q>')[0].to_s
47
63
  end
64
+ def output(message)
65
+ time = Time.new
66
+ add = time.strftime("%d-%m-%Y %H:%M:%S")
67
+ puts "#{time} :: #{message}"
68
+ end
69
+
48
70
 
49
71
 
50
72
  end
73
+
74
+ module Util
75
+ def self.gem_libdir
76
+ require_relative 'meta/version.rb'
77
+ gem_name = 'steam-trade'
78
+ version = '0.0.5'
79
+ t = ["#{File.dirname(File.expand_path($0))}/#{Meta::GEM_NAME}.rb",
80
+ "#{Gem.dir}/gems/#{Meta::GEM_NAME}-#{Meta::VERSION}/lib/#{Meta::GEM_NAME}.rb"]
81
+ t.each {|i|
82
+ return i.gsub("#{Meta::GEM_NAME}.rb", '') if File.readable?(i)
83
+ }
84
+ raise "both paths are invalid: #{t}, while getting gemlib directory"
85
+ end
86
+ end