steam-trade 0.2.1 → 0.2.3
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 +4 -4
- data/README.md +31 -3
- data/lib/Badge.rb +199 -0
- data/lib/Guard.rb +34 -0
- data/lib/Inventory.rb +199 -0
- data/lib/LoginExecutor.rb +74 -32
- data/lib/Misc.rb +59 -16
- data/lib/meta/version.rb +1 -1
- data/lib/steam-trade.rb +15 -17
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a214ab20a7c1cba3c0147c74099425a334b28ec3
|
4
|
+
data.tar.gz: 7b7d54c1e6b45ceb7097a9c5d9e3b19d03a1f89b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 83c85b9d9f2ffe433820c37bf7258d99363229cc0ff151e01cb00e678b897b23238739bed86fe3a79377fed71aa2c14317df4c65e98325451c02b36ffbd00484
|
7
|
+
data.tar.gz: a6045c9e2fbc067bc8db2b1c9af4c3a032bee4fcd6a5a4d1d1cd0b954809357c760b71c27798523f9b4458b08ce7eacdca6965b493f6a589958d6bcb1988693b
|
data/README.md
CHANGED
@@ -1,4 +1,7 @@
|
|
1
|
-
# steam-trade V0.2.
|
1
|
+
# steam-trade V0.2.3
|
2
|
+
|
3
|
+
**PLEASE IF SOMETHING DOES NOT WORK PROPERLY MAKE A GITHUB ISSUE**
|
4
|
+
|
2
5
|
Please check constantly for updates cause i'm still making this gem.
|
3
6
|
|
4
7
|
This gem simplifes/allows sending steam trade offers programmatically.
|
@@ -7,6 +10,16 @@ this gem is primarly for trading cards, tho can be used to CS:GO and other games
|
|
7
10
|
|
8
11
|
# Changelog
|
9
12
|
```
|
13
|
+
0.2.3:
|
14
|
+
- hotfix
|
15
|
+
|
16
|
+
0.2.2:
|
17
|
+
- fixed issues with ruby 2.4+
|
18
|
+
- added class methods for fa(), normal_get_inventory(), raw_get_inventory, sets_count()
|
19
|
+
|
20
|
+
0.2.1:
|
21
|
+
- many many bugs fixed
|
22
|
+
|
10
23
|
0.2.0:
|
11
24
|
- hotfix
|
12
25
|
|
@@ -50,7 +63,7 @@ this gem is primarly for trading cards, tho can be used to CS:GO and other games
|
|
50
63
|
- [Logging-in](#logging-in)
|
51
64
|
- [Hander.new() (this is how you login)](#handlernew)
|
52
65
|
- [Handler.new() (normal login)](#handlernewusername-passwordshared_secrettime_differenceremember_me)
|
53
|
-
- [Handler.new() (cookies login)](#
|
66
|
+
- [Handler.new() (cookies login)](#handlernewcookies_hashshared_secrettime_differenceremember_me)
|
54
67
|
- [get_auth_cookies()](#get_auth_cookies)
|
55
68
|
- [mobile_info()](#mobile_infoidentity_secret)
|
56
69
|
- [Getting someone's inventory](#getting-someones-inventory)
|
@@ -125,13 +138,19 @@ puts account.fa('v3dWNq2Ncutc7RelwRVXswT8CJX=v3dWNq2Ncutc7WelwRVXswT8CJk=') => r
|
|
125
138
|
|
126
139
|
```
|
127
140
|
|
128
|
-
##### `Handler.new(cookies_hash)`
|
141
|
+
##### `Handler.new(cookies_hash,shared_secret,time_difference,remember_me)`
|
129
142
|
- `cookies_hash` is hash containing `steamLogin`, `steamLoginSecure` and `steamMachineAuth` cookies.
|
130
143
|
this can be used with `get_auth_cookies()` for faster login.
|
144
|
+
- `shared_secret` is used to generate steam authentication codes so you won't have to write them manually each time you login.
|
145
|
+
- `time_difference`is the difference between your time and steam servers, this affects how 2FA codes are generated (**this MUST BE an integer**)
|
146
|
+
- `remember_me` is a boolean used to indicate whether you want cookies which expire shortly if set to **false** or stay valid for weeks if set to **true**
|
147
|
+
|
131
148
|
|
132
149
|
```ruby
|
133
150
|
require 'steam-trade'
|
134
151
|
account = Handler.new(JSON.parse(File.read('./creds.json'))) # creds.json is created by get_auth_cookies()
|
152
|
+
|
153
|
+
account = Handler.new(JSON.parse(File.read('./creds.json')), 'shared_secret', 50)
|
135
154
|
```
|
136
155
|
|
137
156
|
#### `get_auth_cookies()`
|
@@ -168,6 +187,8 @@ you might want to read this [guide](https://dev.doctormckay.com/topic/332-identi
|
|
168
187
|
|
169
188
|
```ruby
|
170
189
|
require 'steam-trade'
|
190
|
+
inventory = Handler.normal_get_inventory("nomg3r")
|
191
|
+
|
171
192
|
|
172
193
|
logged = Handler.new('username','password','shared_secret')
|
173
194
|
logged.mobile_info('identity_secret')
|
@@ -213,6 +234,9 @@ This command will return a hash nearly identitical to the one received from stea
|
|
213
234
|
|
214
235
|
```ruby
|
215
236
|
require 'steam-trade'
|
237
|
+
inv = Handler.raw_get_inventory("nomg3r")
|
238
|
+
|
239
|
+
|
216
240
|
logged = Handler.new('username','password','shared_secret')
|
217
241
|
inv = logged.raw_get_inventory() #works
|
218
242
|
inv = logged.raw_get_inventory(false) # returns non trimmed
|
@@ -422,6 +446,8 @@ polling = Thread.new(logged) { |logged|
|
|
422
446
|
- `'totalcards'` is an integer equals to the number of non-foil cards account for
|
423
447
|
```ruby
|
424
448
|
require 'steam-trade'
|
449
|
+
data = Handler.sets_count("CardExchange")
|
450
|
+
|
425
451
|
|
426
452
|
logged = Handler.new('username','password','shared_secret')
|
427
453
|
logged.mobile_info('identity_secret')
|
@@ -459,6 +485,8 @@ handler.update_blueprint()
|
|
459
485
|
**NOTE**: using this command with a new shared_secret will not change/set the current saved shared_secret for the account
|
460
486
|
```ruby
|
461
487
|
require 'steam-trade'
|
488
|
+
puts Hander.fa('random_shared_secret')
|
489
|
+
|
462
490
|
|
463
491
|
logged = Handler.new('username','password','inital_shared_secret')
|
464
492
|
puts logged.fa() #=> random code for your account
|
data/lib/Badge.rb
CHANGED
@@ -27,6 +27,7 @@ module BadgeCommands
|
|
27
27
|
targetname = ''
|
28
28
|
end
|
29
29
|
}
|
30
|
+
|
30
31
|
hash = raw_get_inventory(steamid)
|
31
32
|
sorted = {}
|
32
33
|
classxinstance = {}
|
@@ -198,6 +199,7 @@ module BadgeCommands
|
|
198
199
|
w_title = bigdata[appid]['title']
|
199
200
|
text << " #{w_title}, sets = #{sets}, appid = #{appid}"
|
200
201
|
hashofcards[appid].each { |cardname, owned|
|
202
|
+
next if !owned.is_a(Numeric)
|
201
203
|
text << "#{cardname} xxx #{owned}"
|
202
204
|
}
|
203
205
|
text << ""
|
@@ -207,6 +209,203 @@ module BadgeCommands
|
|
207
209
|
output "badges.txt has been created"
|
208
210
|
end
|
209
211
|
|
212
|
+
def self.included(base)
|
213
|
+
base.extend(BadgeCommands_ClassMethods)
|
214
|
+
end
|
215
|
+
|
216
|
+
module BadgeCommands_ClassMethods
|
217
|
+
@@libdir = Util.gem_libdir
|
218
|
+
def sets_count(steamid, use_nonmarketable = true)
|
219
|
+
|
220
|
+
|
221
|
+
|
222
|
+
|
223
|
+
steamid,token = verify_profileid_or_trade_link_or_steamid(steamid)
|
224
|
+
|
225
|
+
hash = raw_get_inventory(steamid)
|
226
|
+
sorted = {}
|
227
|
+
classxinstance = {}
|
228
|
+
|
229
|
+
hash["descriptions"].delete_if {|desc|
|
230
|
+
conc = desc["classid"] + "_" + desc["instanceid"]
|
231
|
+
classxinstance[conc] = desc
|
232
|
+
true
|
233
|
+
}
|
234
|
+
|
235
|
+
|
236
|
+
|
237
|
+
hash["assets"].each { |asset|
|
238
|
+
identity = asset["classid"] + "_" + asset["instanceid"]
|
239
|
+
assetdesc = classxinstance[identity]
|
240
|
+
if use_nonmarketable == false
|
241
|
+
if assetdesc["marketable"] == 0 || assetdesc["tags"][-1]["localized_tag_name"] != "Trading Card" || assetdesc["tags"][-2]["localized_tag_name"] == "Foil"
|
242
|
+
next
|
243
|
+
end
|
244
|
+
else
|
245
|
+
if assetdesc["tags"][-1]["localized_tag_name"] != "Trading Card" ||assetdesc["tags"][-2]["localized_tag_name"] == "Foil"
|
246
|
+
next
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
name = assetdesc["name"].sub(" (Trading Card)", "")
|
251
|
+
appid =assetdesc["market_fee_app"].to_s
|
252
|
+
if sorted.has_key?(appid) == true
|
253
|
+
if sorted[appid].has_key?(name) == true
|
254
|
+
sorted[appid][name] = sorted[appid][name] + 1
|
255
|
+
elsif sorted[appid].has_key?(name) == false
|
256
|
+
sorted[appid][name] = 1
|
257
|
+
end
|
258
|
+
elsif sorted.has_key?(appid) == false
|
259
|
+
sorted[appid] = {}
|
260
|
+
sorted[appid][name] = 1
|
261
|
+
end
|
262
|
+
}
|
263
|
+
|
264
|
+
bigdata = JSON.parse(File.read("#{@@libdir}blueprints/byappid.json",:external_encoding => 'utf-8',:internal_encoding => 'utf-8'))
|
265
|
+
|
266
|
+
counted = {}
|
267
|
+
|
268
|
+
sorted.each { |appid,cards|
|
269
|
+
begin
|
270
|
+
counted[appid.to_s] = bigdata[appid].merge(cards)
|
271
|
+
rescue
|
272
|
+
output "badges blueprint does not include #{appid}"
|
273
|
+
end
|
274
|
+
}
|
275
|
+
|
276
|
+
setsowned = {}
|
277
|
+
numberofsets = 0
|
278
|
+
total_non_foil = 0
|
279
|
+
|
280
|
+
counted.each { |appid,cards|
|
281
|
+
lowest = 9999
|
282
|
+
cards.each { |cardname, amount|
|
283
|
+
next if amount.class == String # apptitle
|
284
|
+
if amount < lowest then lowest = amount end
|
285
|
+
total_non_foil = total_non_foil + amount
|
286
|
+
}
|
287
|
+
setsowned[appid.to_s] = lowest
|
288
|
+
numberofsets = numberofsets + lowest
|
289
|
+
}
|
290
|
+
|
291
|
+
persona = ''
|
292
|
+
write_badges(counted,setsowned,numberofsets,total_non_foil, use_nonmarketable,persona,steamid)
|
293
|
+
if use_nonmarketable == false
|
294
|
+
return {'sets' => counted, 'appxsets' => setsowned, 'totalsets' => numberofsets, 'totalcards' => total_non_foil, 'marketable' => false}
|
295
|
+
else
|
296
|
+
return {'sets' => counted, 'appxsets' => setsowned, 'totalsets' => numberofsets, 'totalcards' => total_non_foil, 'marketable' => true}
|
297
|
+
end
|
298
|
+
end
|
299
|
+
|
300
|
+
|
301
|
+
|
302
|
+
|
303
|
+
def update_blueprint()
|
304
|
+
session = Mechanize.new
|
305
|
+
|
306
|
+
session.pre_connect_hooks << lambda do |agent, request|
|
307
|
+
request['Origin'] = 'http://steam.tools'
|
308
|
+
request['Referer'] = 'http://steam.tools/cards/'
|
309
|
+
request['User-Agent'] ='Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3325.181 Safari/537.36 OPR/52.0.2871.99'
|
310
|
+
end
|
311
|
+
|
312
|
+
|
313
|
+
pag = session.get('http://cdn.steam.tools/data/set_data.json')
|
314
|
+
data = JSON.parse(pag.content)
|
315
|
+
old = JSON.parse(File.read("#{@@libdir}blueprints/byappid.json" ,:external_encoding => 'utf-8',:internal_encoding => 'utf-8'))
|
316
|
+
|
317
|
+
newapps = []
|
318
|
+
data["sets"].each { |set|
|
319
|
+
newapps << set['appid']
|
320
|
+
}
|
321
|
+
get = []
|
322
|
+
haveapps = old.keys
|
323
|
+
newapps.each { |app|
|
324
|
+
get << app if haveapps.include?(app) == false
|
325
|
+
}
|
326
|
+
|
327
|
+
progress = 0
|
328
|
+
error = 0
|
329
|
+
(output("your blueprint is up-to-date");return) if get.length.zero?
|
330
|
+
get.each {|app|
|
331
|
+
begin
|
332
|
+
card = {}
|
333
|
+
steam_nokogiri = Nokogiri::HTML(session.get("http://www.steamcardexchange.net/index.php?inventorygame-appid-#{app}").content)
|
334
|
+
title = steam_nokogiri.css('h2[class=empty]').text.force_encoding(Encoding::UTF_8)
|
335
|
+
steam_nokogiri.css('div[class=name-image-container]').css('span').each do |e|
|
336
|
+
card[e.text.force_encoding(Encoding::UTF_8)] = 0
|
337
|
+
end
|
338
|
+
card['title'] = title
|
339
|
+
full_data = card
|
340
|
+
|
341
|
+
old[app] = full_data
|
342
|
+
File.open("#{@@libdir}blueprints/byappid.json", 'w:UTF-8') {|f| f.puts old.to_json}
|
343
|
+
|
344
|
+
progress = progress + 1
|
345
|
+
output "#{progress} / #{get.length} done, error = #{error}"
|
346
|
+
rescue Exception => e
|
347
|
+
File.open("#{@@libdir}blueprints/byappid.json", 'w:UTF-8') {|f| f.puts old.to_json}
|
348
|
+
error = error + 1
|
349
|
+
progress = progress + 1
|
350
|
+
output "#{progress} / #{get.length} done, error = #{error}"
|
351
|
+
output "error occured saved data"
|
352
|
+
raise e
|
353
|
+
end
|
354
|
+
}
|
355
|
+
|
356
|
+
|
357
|
+
end
|
358
|
+
|
359
|
+
|
360
|
+
|
361
|
+
|
362
|
+
|
363
|
+
private
|
364
|
+
def write_badges(hashofcards,eachappidsets,totalsets,total_non_foil,use_nonmarketable,persona,steamid)
|
365
|
+
if persona == ''
|
366
|
+
filename = steamid
|
367
|
+
else
|
368
|
+
filename = persona
|
369
|
+
end
|
370
|
+
|
371
|
+
|
372
|
+
bigdata = JSON.parse(File.read("#{@@libdir}blueprints/byappid.json",:external_encoding => 'utf-8',:internal_encoding => 'utf-8'))
|
373
|
+
eachappidsets = eachappidsets.sort_by do |k,v|
|
374
|
+
v
|
375
|
+
end
|
376
|
+
eachappidsets.reverse!
|
377
|
+
output "Writing the badges to #{filename}_badges.txt "
|
378
|
+
text = []
|
379
|
+
text << "for #{persona}(#{steamid})"
|
380
|
+
if use_nonmarketable == false
|
381
|
+
text << "only marketable cards are counted"
|
382
|
+
text << "total non-foil trading cards #{total_non_foil}"
|
383
|
+
else
|
384
|
+
text << "total non-foil trading cards #{total_non_foil}"
|
385
|
+
text << "all cards counted including non-marketable"
|
386
|
+
end
|
387
|
+
|
388
|
+
|
389
|
+
text << "total sets in target account #{totalsets}"
|
390
|
+
text << ""
|
391
|
+
text << ""
|
392
|
+
eachappidsets.each { |appid, sets|
|
393
|
+
w_title = bigdata[appid]['title']
|
394
|
+
text << " #{w_title}, sets = #{sets}, appid = #{appid}"
|
395
|
+
hashofcards[appid].each { |cardname, owned|
|
396
|
+
next if !owned.is_a?(Numeric)
|
397
|
+
text << "#{cardname} xxx #{owned}"
|
398
|
+
}
|
399
|
+
text << ""
|
400
|
+
text << ""
|
401
|
+
}
|
402
|
+
File.open("./#{filename}_badges.txt",'w:UTF-8') {|f| f.puts text}
|
403
|
+
output "badges.txt has been created"
|
404
|
+
end
|
405
|
+
|
406
|
+
|
407
|
+
|
408
|
+
end
|
210
409
|
|
211
410
|
|
212
411
|
end
|
data/lib/Guard.rb
CHANGED
@@ -30,6 +30,40 @@ module GuardCommands
|
|
30
30
|
|
31
31
|
end
|
32
32
|
|
33
|
+
def self.included(base)
|
34
|
+
base.extend(Guard_ClassMethods)
|
35
|
+
end
|
36
|
+
|
37
|
+
module Guard_ClassMethods
|
38
|
+
def fa(shared_secret,time_difference = 0)
|
39
|
+
raise "No shared_secret given" if shared_secret == nil # cause upon initialization @secret = nil
|
40
|
+
timestamp = Time.new.to_i + time_difference
|
41
|
+
math = timestamp.to_i / 30
|
42
|
+
math = math.to_i
|
43
|
+
time_buffer =[math].pack('Q>')
|
44
|
+
|
45
|
+
hmac = OpenSSL::HMAC.digest('sha1', Base64.decode64(shared_secret), time_buffer)
|
46
|
+
|
47
|
+
start = hmac[19].ord & 0xf
|
48
|
+
last = start + 4
|
49
|
+
pre = hmac[start..last]
|
50
|
+
fullcode = pre.unpack('I>')[0] & 0x7fffffff
|
51
|
+
|
52
|
+
chars = '23456789BCDFGHJKMNPQRTVWXY'
|
53
|
+
code= ''
|
54
|
+
for looper in 0..4 do
|
55
|
+
copy = fullcode #divmod
|
56
|
+
i = copy % chars.length #divmod
|
57
|
+
fullcode = copy / chars.length #divmod
|
58
|
+
code = code + chars[i]
|
59
|
+
end
|
60
|
+
return code
|
61
|
+
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
end
|
66
|
+
|
33
67
|
private
|
34
68
|
def generate_confirmation_key(tag_string, time_stamp)
|
35
69
|
buffer = [time_stamp].pack('Q>') + tag_string.encode('ascii')
|
data/lib/Inventory.rb
CHANGED
@@ -300,5 +300,204 @@ module InventoryCommands
|
|
300
300
|
end
|
301
301
|
|
302
302
|
|
303
|
+
def self.included(base)
|
304
|
+
base.extend(Inventory_ClassMethods)
|
305
|
+
end
|
306
|
+
|
307
|
+
module Inventory_ClassMethods
|
308
|
+
@@libdir = Util.gem_libdir
|
309
|
+
@@session = Mechanize.new
|
310
|
+
def normal_get_inventory(steamid ,appid = 753)
|
311
|
+
appid = appid.to_s
|
312
|
+
context = 6
|
313
|
+
e
|
314
|
+
if appid.to_s != "753"
|
315
|
+
context = 2
|
316
|
+
end
|
317
|
+
#end verify given another game
|
318
|
+
# end verify given appid only
|
319
|
+
#verify trade link
|
320
|
+
steamid,token = verify_profileid_or_trade_link_or_steamid(steamid)
|
321
|
+
raise "invalid steamid : #{steamid}, length of received :: #{steamid.to_s.length}, normal is 17" if steamid.to_s.length != 17
|
322
|
+
## verify appid
|
323
|
+
if ["753","730",'570','440'].include?(appid.to_s) == false
|
324
|
+
allgames = JSON.parse(File.read("#{@@libdir}blueprints/game_inv_list.json"))
|
325
|
+
raise "invalid appid: #{appid}" if allgames.include?(appid.to_s) == false
|
326
|
+
end
|
327
|
+
## end verify appid
|
328
|
+
|
329
|
+
items = []
|
330
|
+
last_id = 0
|
331
|
+
until last_id == false
|
332
|
+
received = get_inventory_chunk_normal_way(appid,context,steamid,last_id)
|
333
|
+
last_id = received['new_last_id']
|
334
|
+
items = items + received['assets']
|
335
|
+
output "loaded #{items.length}"
|
336
|
+
end
|
337
|
+
|
338
|
+
output "total loaded #{items.length} asset"
|
339
|
+
|
340
|
+
|
341
|
+
return items
|
342
|
+
end ##end normal get
|
343
|
+
|
344
|
+
###
|
345
|
+
|
346
|
+
|
347
|
+
def get_inventory_chunk_normal_way(appid,context,steamid,last_id)
|
348
|
+
html = ''
|
349
|
+
tries = 1
|
350
|
+
|
351
|
+
until html != ''
|
352
|
+
begin
|
353
|
+
html = @@session.get("https://steamcommunity.com/inventory/#{steamid}/#{appid}/#{context}?start_assetid=#{last_id}&count=5000").content
|
354
|
+
rescue
|
355
|
+
raise "Cannot get inventory, tried 3 times" if tries == 3
|
356
|
+
tries = tries + 1
|
357
|
+
sleep(0.5)
|
358
|
+
end
|
359
|
+
end
|
360
|
+
|
361
|
+
|
362
|
+
get = JSON.parse(html)
|
363
|
+
raise "something totally unexpected happened while getting inventory with appid #{appid} of steamid #{steamid} with contextid #{context}" if get.key?("error") == true
|
364
|
+
if get["total_inventory_count"] == 0
|
365
|
+
output "EMPTY :: inventory with appid #{appid} of steamid #{steamid} with contextid #{context}"
|
366
|
+
return {'assets' => [], 'new_last_id' =>false}
|
367
|
+
end
|
368
|
+
if get.keys[3].to_s == "last_assetid"
|
369
|
+
|
370
|
+
new_last_id = get.values[3].to_s
|
371
|
+
|
372
|
+
else
|
373
|
+
new_last_id = false
|
374
|
+
|
375
|
+
end
|
376
|
+
|
377
|
+
assets = get["assets"]
|
378
|
+
descriptions = get["descriptions"]
|
379
|
+
|
380
|
+
|
381
|
+
descriptions_classids = {} ###sorting descriptions by key value || key is classid of the item's description
|
382
|
+
descriptions.each {|description|
|
383
|
+
classidxinstance = description["classid"] + '_' + description["instanceid"] # some items has the same classid but different instane id
|
384
|
+
descriptions_classids[classidxinstance] = description
|
385
|
+
}
|
386
|
+
|
387
|
+
assets.each { |asset| ## merging assets with names
|
388
|
+
classidxinstance = asset["classid"] + '_' + asset["instanceid"]
|
389
|
+
asset.replace(asset.merge(descriptions_classids[classidxinstance]))
|
390
|
+
}
|
391
|
+
|
392
|
+
|
393
|
+
return {'assets' => assets, 'new_last_id' =>new_last_id}
|
394
|
+
|
395
|
+
end ## end inventory get normal
|
396
|
+
|
397
|
+
|
398
|
+
|
399
|
+
def raw_get_inventory(steamid, *params)#steamid = @steamid ,appid = 753, trim = true
|
400
|
+
raise "expected 2 paramters, given #{params.length}"if params.length > 2
|
401
|
+
appid = 753
|
402
|
+
trim = true
|
403
|
+
context = 6
|
404
|
+
v = params.length
|
405
|
+
if params.length == 2
|
406
|
+
(appid = params[0]; v= v - 1;) if (3..6).to_a.include?(params[0].to_i.to_s.length)
|
407
|
+
(trim = params[1]; v= v - 1;) if [TrueClass,FalseClass].include?(params[1].class)
|
408
|
+
elsif params.length == 1
|
409
|
+
(appid = params[0]; v= v - 1;) if (3..6).to_a.include?(params[0].to_i.to_s.length)
|
410
|
+
(trim = params[0]; v= v - 1;) if [TrueClass,FalseClass].include?(params[0].class)
|
411
|
+
end
|
412
|
+
raise "invalid params given" if v != 0
|
413
|
+
|
414
|
+
steamid,token = verify_profileid_or_trade_link_or_steamid(steamid)
|
415
|
+
raise "invalid steamid : #{steamid}, length of received :: #{steamid.to_s.length}, normal is 17" if steamid.to_s.length != 17
|
416
|
+
## verify appid
|
417
|
+
if ["753","730",'570','440'].include?(appid.to_s) == false
|
418
|
+
allgames = JSON.parse(File.read("#{@libdir}blueprints/game_inv_list.json"))
|
419
|
+
raise "invalid appid: #{appid}" if allgames.include?(appid.to_s) == false
|
420
|
+
end
|
421
|
+
## end verify appid
|
422
|
+
|
423
|
+
if appid.to_s != "753"
|
424
|
+
context = 2
|
425
|
+
end
|
426
|
+
|
427
|
+
|
428
|
+
last_id = 0
|
429
|
+
hash = {"assets" => [], "descriptions" => []}
|
430
|
+
until last_id == false
|
431
|
+
received = get_inventory_chunk_raw_way(appid,context,steamid,last_id,trim)
|
432
|
+
last_id = received['new_last_id']
|
433
|
+
hash["assets"] = hash["assets"] + received['assets']
|
434
|
+
hash["descriptions"] = hash["descriptions"] + received["descriptions"]
|
435
|
+
output "loaded #{hash["assets"].length}"
|
436
|
+
end
|
437
|
+
|
438
|
+
output "total loaded #{hash["assets"].length} asset"
|
439
|
+
|
440
|
+
|
441
|
+
return hash
|
442
|
+
end
|
443
|
+
|
444
|
+
|
445
|
+
|
446
|
+
|
447
|
+
def get_inventory_chunk_raw_way(appid,context,steamid,last_id,trim)
|
448
|
+
|
449
|
+
|
450
|
+
html = ''
|
451
|
+
tries = 1
|
452
|
+
|
453
|
+
until html != ''
|
454
|
+
begin
|
455
|
+
html = @@session.get("https://steamcommunity.com/inventory/#{steamid}/#{appid}/#{context}?start_assetid=#{last_id}&count=5000").content
|
456
|
+
rescue
|
457
|
+
raise "Cannot get inventory, tried 3 times" if tries == 3
|
458
|
+
tries = tries + 1
|
459
|
+
sleep(0.5)
|
460
|
+
end
|
461
|
+
end
|
462
|
+
|
463
|
+
get = JSON.parse(html)
|
464
|
+
raise "something totally unexpected happened while getting inventory with appid #{appid} of steamid #{steamid} with contextid #{context}" if get.key?("error") == true
|
465
|
+
if get["total_inventory_count"] == 0
|
466
|
+
output "EMPTY :: inventory with appid #{appid} of steamid #{steamid} with contextid #{context}"
|
467
|
+
return {'assets' => [], "descriptions" => [], 'new_last_id' =>false}
|
468
|
+
end
|
469
|
+
if get.keys[3].to_s == "last_assetid"
|
470
|
+
|
471
|
+
new_last_id = get.values[3].to_s
|
472
|
+
|
473
|
+
else
|
474
|
+
new_last_id = false
|
475
|
+
|
476
|
+
end
|
477
|
+
|
478
|
+
assets = get["assets"]
|
479
|
+
descriptions = get["descriptions"]
|
480
|
+
if trim == true
|
481
|
+
descriptions.each { |desc|
|
482
|
+
desc.delete_if {|key, value| key != "appid" && key != "classid" && key != "instanceid" && key != "tags" && key != "type" && key != "market_fee_app" && key != "marketable" &&key != "name" }
|
483
|
+
desc["tags"].delete_at(0)
|
484
|
+
desc["tags"].delete_at(0)
|
485
|
+
}
|
486
|
+
end
|
487
|
+
|
488
|
+
return {'assets' => get["assets"], "descriptions" => get["descriptions"], 'new_last_id' =>new_last_id}
|
489
|
+
|
490
|
+
end
|
491
|
+
|
492
|
+
|
493
|
+
end
|
494
|
+
|
495
|
+
|
496
|
+
|
497
|
+
|
498
|
+
|
499
|
+
|
500
|
+
|
501
|
+
|
303
502
|
|
304
503
|
end
|
data/lib/LoginExecutor.rb
CHANGED
@@ -8,45 +8,79 @@ module LoginCommands
|
|
8
8
|
encrypted_password = data["password"]
|
9
9
|
timestamp = data["timestamp"]
|
10
10
|
repeater = 0
|
11
|
-
until repeater == true
|
12
|
-
if @secret != nil
|
13
|
-
guardcode = fa(@secret,@time_difference)
|
14
|
-
else
|
15
|
-
puts "please write your 2FA code"
|
16
|
-
guardcode = gets.chomp
|
17
|
-
end
|
18
11
|
|
19
12
|
|
13
|
+
send = {
|
14
|
+
'password' => encrypted_password,
|
15
|
+
'username' => @username,
|
16
|
+
'twofactorcode' =>'', #update
|
17
|
+
'emailauth' => '',
|
18
|
+
'loginfriendlyname' => '',
|
19
|
+
'captchagid' => '-1',
|
20
|
+
'captcha_text' => '',
|
21
|
+
'emailsteamid' => '',
|
22
|
+
'rsatimestamp' => timestamp,
|
23
|
+
'remember_login' => @remember
|
24
|
+
}
|
25
|
+
login = @session.post('https://store.steampowered.com/login/dologin', send ).content
|
26
|
+
firstreq = JSON.parse(login)
|
27
|
+
|
28
|
+
raise "Incorrect username or password" if firstreq["message"] == "The account name or password that you have entered is incorrect."
|
29
|
+
|
30
|
+
|
31
|
+
until firstreq["success"] == true
|
32
|
+
sleep(0.3)
|
33
|
+
gid = '-1'
|
34
|
+
cap = ''
|
35
|
+
if firstreq['captcha_needed'] == true
|
36
|
+
gid = firstreq['captcha_needed']
|
37
|
+
File.delete("./#{username}_captcha.png") if File.exist?("./#{username}_captcha.png")
|
38
|
+
@session.get("https://store.steampowered.com/login/rendercaptcha?gid=#{gid}").save "./#{@username}_captcha.png"
|
39
|
+
puts "you need to write a captcha to continue"
|
40
|
+
puts "there is an image named #{@username}_captcha in the script directory"
|
41
|
+
puts "open it and write the captha here"
|
42
|
+
cap = gets.chomp
|
43
|
+
end
|
44
|
+
emailauth = ''
|
45
|
+
facode = ''
|
46
|
+
emailsteamid = ''
|
47
|
+
if firstreq['requires_twofactor'] == true
|
48
|
+
if @secret.nil?
|
49
|
+
puts "write 2FA code"
|
50
|
+
facode = gets.chomp
|
51
|
+
else
|
52
|
+
facode = fa(@secret,@time_difference)
|
53
|
+
end
|
54
|
+
elsif firstreq['emailauth_needed'] == true
|
55
|
+
emailsteamid = firstreq['emailsteamid']
|
56
|
+
puts "Guard code was sent to your email"
|
57
|
+
puts "write the code"
|
58
|
+
emailauth = gets.chomp
|
59
|
+
end
|
60
|
+
|
20
61
|
send = {
|
21
62
|
'password' => encrypted_password,
|
22
63
|
'username' => @username,
|
23
|
-
'twofactorcode' =>
|
24
|
-
'emailauth' =>
|
64
|
+
'twofactorcode' => facode, #update
|
65
|
+
'emailauth' => emailauth,
|
25
66
|
'loginfriendlyname' => '',
|
26
|
-
'captchagid' =>
|
27
|
-
'captcha_text' =>
|
28
|
-
'emailsteamid' =>
|
67
|
+
'captchagid' => gid,
|
68
|
+
'captcha_text' => cap,
|
69
|
+
'emailsteamid' => emailsteamid,
|
29
70
|
'rsatimestamp' => timestamp,
|
30
71
|
'remember_login' => @remember
|
31
72
|
}
|
73
|
+
output "attempting to login"
|
74
|
+
login = @session.post('https://store.steampowered.com/login/dologin', send ).content
|
75
|
+
firstreq = JSON.parse(login)
|
76
|
+
|
77
|
+
end
|
78
|
+
response = firstreq
|
79
|
+
|
80
|
+
|
32
81
|
|
33
|
-
login = @session.post('https://store.steampowered.com/login/dologin', send )
|
34
|
-
response = JSON::parse(login.body)
|
35
|
-
output "logging-in"
|
36
|
-
if response["success"] == true
|
37
|
-
repeater = true
|
38
|
-
elsif repeater == 3
|
39
|
-
raise "Login failed username: #{@username}, password: #{@password}, shared_scret: #{@secret} tried 3 times"
|
40
|
-
else
|
41
|
-
print response
|
42
|
-
puts "re-trying to login"
|
43
|
-
puts "sleeping for 6 seconds"
|
44
|
-
sleep(6)
|
45
|
-
repeater = repeater + 1
|
46
|
-
end
|
47
82
|
|
48
83
|
|
49
|
-
end
|
50
84
|
if @steamid != nil && @steamid != response["transfer_parameters"]["steamid"]
|
51
85
|
puts "the steamid you provided does not belong to the account you entered"
|
52
86
|
puts "steamid will be overwritten"
|
@@ -73,8 +107,11 @@ module LoginCommands
|
|
73
107
|
cookie = Mechanize::Cookie.new :domain => 'steamcommunity.com', :name =>'sessionid', :value =>steampowered_sessionid, :path => '/'
|
74
108
|
@session.cookie_jar << cookie
|
75
109
|
@loggedin = true
|
76
|
-
|
77
|
-
|
110
|
+
begin
|
111
|
+
@api_key = Nokogiri::HTML(@session.get("https://steamcommunity.com/dev/apikey").content).css('#bodyContents_ex').css('p').first.text.sub('Key: ','')
|
112
|
+
rescue
|
113
|
+
output "Could not retrieve api_key"
|
114
|
+
end
|
78
115
|
data = get_player_summaries(@steamid)
|
79
116
|
data.each { |element|
|
80
117
|
if element["steamid"].to_s == @steamid.to_s
|
@@ -83,7 +120,7 @@ module LoginCommands
|
|
83
120
|
}
|
84
121
|
output "logged in as #{@persona}"
|
85
122
|
output "your steamid is #{@steamid}"
|
86
|
-
output "loaded API_KEY : #{@api_key}"
|
123
|
+
output "loaded API_KEY : #{@api_key}" if !@api_key.nil?
|
87
124
|
end
|
88
125
|
########################################################################################
|
89
126
|
|
@@ -97,8 +134,13 @@ module LoginCommands
|
|
97
134
|
timestamp = data["timestamp"]
|
98
135
|
|
99
136
|
key = OpenSSL::PKey::RSA.new
|
100
|
-
|
101
|
-
|
137
|
+
if RUBY_VERSION.to_f <= 2.3
|
138
|
+
key.e = OpenSSL::BN.new(exp)
|
139
|
+
key.n = OpenSSL::BN.new(mod)
|
140
|
+
elsif RUBY_VERSION.to_f >= 2.4
|
141
|
+
#key.set_key(n, e, d)
|
142
|
+
key.set_key(OpenSSL::BN.new(mod), OpenSSL::BN.new(exp),nil)
|
143
|
+
end
|
102
144
|
ep = Base64.encode64(key.public_encrypt(password.force_encoding("utf-8"))).gsub("\n", '')
|
103
145
|
return {'password' => ep, 'timestamp' => timestamp }
|
104
146
|
end
|
data/lib/Misc.rb
CHANGED
@@ -34,7 +34,7 @@ module MiscCommands
|
|
34
34
|
|
35
35
|
|
36
36
|
|
37
|
-
|
37
|
+
|
38
38
|
def partner_id_to_steam_id(account_id)
|
39
39
|
unknown_constant = 17825793 # or 0x1100001 idk wtf is this but ....
|
40
40
|
first_bytes = [account_id.to_i].pack('i>')
|
@@ -43,10 +43,11 @@ module MiscCommands
|
|
43
43
|
return collect.unpack('Q>')[0].to_s
|
44
44
|
end
|
45
45
|
|
46
|
+
|
46
47
|
def output(message)
|
47
48
|
time = Time.new
|
48
49
|
add = time.strftime("%d-%m-%Y %H:%M:%S")
|
49
|
-
puts "#{add} :: #{message}" if
|
50
|
+
puts "#{add} :: #{message}" if message != ''
|
50
51
|
end
|
51
52
|
|
52
53
|
def verify_profileid_or_trade_link_or_steamid(steamid)
|
@@ -74,22 +75,18 @@ module MiscCommands
|
|
74
75
|
begin
|
75
76
|
value = @session.cookie_jar.jar["steamcommunity.com"]["/"]["sessionid"].value
|
76
77
|
rescue
|
77
|
-
|
78
|
-
|
79
|
-
if value == nil
|
80
|
-
begin
|
81
|
-
value = @session.cookie_jar.jar["store.steampowered.com"]["/"]["sessionid"].value
|
82
|
-
rescue
|
83
|
-
value = nil
|
84
|
-
end
|
78
|
+
@session.get('http://steamcommunity.com')
|
79
|
+
value = @session.cookie_jar.jar["steamcommunity.com"]["/"]["sessionid"].value
|
85
80
|
end
|
81
|
+
return value
|
82
|
+
end
|
86
83
|
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
84
|
+
def store_cookie()
|
85
|
+
begin
|
86
|
+
value = @session.cookie_jar.jar["store.steampowered.com"]["/"]["sessionid"].value
|
87
|
+
rescue
|
88
|
+
@session.get('http://store.steampowered.com')
|
89
|
+
value = @session.cookie_jar.jar["store.steampowered.com"]["/"]["sessionid"].value
|
93
90
|
end
|
94
91
|
return value
|
95
92
|
end
|
@@ -110,6 +107,52 @@ module MiscCommands
|
|
110
107
|
end
|
111
108
|
|
112
109
|
|
110
|
+
|
111
|
+
|
112
|
+
def self.included(base)
|
113
|
+
base.extend(Misc_ClassMethods)
|
114
|
+
end
|
115
|
+
|
116
|
+
module Misc_ClassMethods
|
117
|
+
def partner_id_to_steam_id(account_id)
|
118
|
+
unknown_constant = 17825793 # or 0x1100001 idk wtf is this but ....
|
119
|
+
first_bytes = [account_id.to_i].pack('i>')
|
120
|
+
last_bytes = [unknown_constant].pack('i>')
|
121
|
+
collect = last_bytes + first_bytes
|
122
|
+
return collect.unpack('Q>')[0].to_s
|
123
|
+
end
|
124
|
+
|
125
|
+
private
|
126
|
+
def output(message)
|
127
|
+
time = Time.new
|
128
|
+
add = time.strftime("%d-%m-%Y %H:%M:%S")
|
129
|
+
puts "#{add} :: #{message}"
|
130
|
+
end
|
131
|
+
|
132
|
+
def verify_profileid_or_trade_link_or_steamid(steamid)
|
133
|
+
if steamid.to_i == 0 && steamid.include?("?partner=") ##supplied trade link
|
134
|
+
partner_raw = steamid.split('partner=',2)[1].split('&',2)[0]
|
135
|
+
token = steamid.split('token=', 2)[1]
|
136
|
+
steamid = partner_id_to_steam_id(partner_raw)
|
137
|
+
return [steamid,token]
|
138
|
+
elsif steamid.to_i == 0
|
139
|
+
session = Mechanize.new
|
140
|
+
parser = Nokogiri::XML(session.get("https://steamcommunity.com/id/#{steamid}?xml=1").content)
|
141
|
+
if parser.xpath('//error').text == ('The specified profile could not be found.')
|
142
|
+
raise "No profile with #{steamid} as profileid"
|
143
|
+
end
|
144
|
+
steamid = parser.xpath('//steamID64').text
|
145
|
+
return steamid
|
146
|
+
elsif steamid.to_s.length == 17
|
147
|
+
return steamid
|
148
|
+
else
|
149
|
+
raise "invalid steamid : #{steamid}, length of received :: #{steamid.to_s.length}, normal is 17" if steamid.to_s.length != 17
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
end ## end module
|
154
|
+
|
155
|
+
|
113
156
|
end
|
114
157
|
|
115
158
|
module Util
|
data/lib/meta/version.rb
CHANGED
data/lib/steam-trade.rb
CHANGED
@@ -80,18 +80,18 @@ class Handler
|
|
80
80
|
|
81
81
|
if params.length == 3
|
82
82
|
raise "shared_secret must be string, received #{parasm[0].class}" if params[0].class != String
|
83
|
-
raise "time difference must be
|
83
|
+
raise "time difference must be a Numeric, received #{params[1].class}" if !params[1].is_a?(Numeric)
|
84
84
|
raise "remember_me must be a boolean, received #{params[2].class}" if !([TrueClass,FalseClass].include?(params[2].class))
|
85
85
|
@secret = params[0] if params[0].class == String
|
86
|
-
@time_difference = params[1] if params[1].
|
86
|
+
@time_difference = params[1] if params[1].is_a?(Numeric)
|
87
87
|
@remember = params[2] if [TrueClass,FalseClass].include?(params[2].class)
|
88
88
|
elsif params.length == 2
|
89
89
|
if params[0].class == String
|
90
|
-
raise "invalid fourth parameter type, received #{params[1].class}" if !([TrueClass,FalseClass].include?(params[1].class)) && params[1].
|
90
|
+
raise "invalid fourth parameter type, received #{params[1].class}" if !([TrueClass,FalseClass].include?(params[1].class)) && !params[1].is_a?(Numeric)
|
91
91
|
@secret = params[0]
|
92
|
-
@time_difference = params[1] if params[1].
|
92
|
+
@time_difference = params[1] if params[1].is_a?(Numeric)
|
93
93
|
@remember = params[1] if [TrueClass,FalseClass].include?(params[1].class)
|
94
|
-
elsif params[0].
|
94
|
+
elsif params[0].is_a?(Numeric)
|
95
95
|
raise "remember_me must be a boolean, received #{params[1].class}" if !([TrueClass,FalseClass].include?(params[1].class))
|
96
96
|
@time_difference = params[0]
|
97
97
|
@remember = params[1]
|
@@ -99,9 +99,9 @@ class Handler
|
|
99
99
|
raise "invalid third parameter type"
|
100
100
|
end
|
101
101
|
elsif params.length == 1
|
102
|
-
raise "invalid third parameter type, received #{params[0].class}" if !([TrueClass,FalseClass].include?(params[0].class)) && params[0].
|
102
|
+
raise "invalid third parameter type, received #{params[0].class}" if !([TrueClass,FalseClass].include?(params[0].class)) && !params[0].is_a?(Numeric) && params[0].class != String
|
103
103
|
@secret = params[0] if params[0].class == String
|
104
|
-
@time_difference = params[0] if params[0].
|
104
|
+
@time_difference = params[0] if params[0].is_a?(Numeric)
|
105
105
|
@remember = params[0] if [TrueClass,FalseClass].include?(params[0].class)
|
106
106
|
end
|
107
107
|
|
@@ -113,9 +113,9 @@ class Handler
|
|
113
113
|
if params.length == 0
|
114
114
|
|
115
115
|
|
116
|
-
raise "invalid parameter type, received #{password.class}" if !(password.class == String ||
|
116
|
+
raise "invalid parameter type, received #{password.class}" if !(password.class == String || params[0].is_a?(Numeric) || [TrueClass,FalseClass].include?(password.class) )
|
117
117
|
@secret = password if password.class == String
|
118
|
-
@time_difference = password if password.
|
118
|
+
@time_difference = password if password.is_a?(Numeric)
|
119
119
|
@remember = password if [TrueClass,FalseClass].include?(password.class)
|
120
120
|
|
121
121
|
|
@@ -124,10 +124,10 @@ class Handler
|
|
124
124
|
|
125
125
|
if password.class == String
|
126
126
|
@secret = password
|
127
|
-
raise "invalid paramter type, received #{params[0].class}" if !([TrueClass,FalseClass].include?(params[0].class) || params[0].
|
128
|
-
@time_difference = params[0] if params[0].
|
127
|
+
raise "invalid paramter type, received #{params[0].class}" if !([TrueClass,FalseClass].include?(params[0].class) || params[0].is_a?(Numeric) )
|
128
|
+
@time_difference = params[0] if params[0].is_a?(Numeric)
|
129
129
|
@remember = params[0] if [TrueClass,FalseClass].include?(params[0].class)
|
130
|
-
elsif password.
|
130
|
+
elsif password.is_a?(Numeric)
|
131
131
|
@time_difference = password
|
132
132
|
raise "invalid paramter type, received #{params[0].class}" if !([TrueClass,FalseClass].include?(params[0].class))
|
133
133
|
@remember = params[0] if [TrueClass,FalseClass].include?(params[0].class)
|
@@ -139,8 +139,8 @@ class Handler
|
|
139
139
|
elsif params.length == 2
|
140
140
|
raise "shared_secret must be a string, recieved #{password.class}" if password.class != String
|
141
141
|
@secret = password if password.class == String
|
142
|
-
raise "time difference must be
|
143
|
-
@time_difference = params[0] if params[0].
|
142
|
+
raise "time difference must be a Numeric, received #{params[0].class}" if !params[0].is_a?(Numeric)
|
143
|
+
@time_difference = params[0] if params[0].is_a?(Numeric)
|
144
144
|
raise "remeber_me must be a boolean, recieved #{params[1].class}" if !([TrueClass,FalseClass].include?(params[1].class))
|
145
145
|
@remember = params[1] if [TrueClass,FalseClass].include?(params[1].class)
|
146
146
|
|
@@ -161,9 +161,7 @@ class Handler
|
|
161
161
|
end
|
162
162
|
|
163
163
|
def set_inventory_cache(timer = 120)
|
164
|
-
|
165
|
-
float = 5.5
|
166
|
-
if timer.class == integer.class || timer.class == float.class
|
164
|
+
if timer.is_a?(Numeric)
|
167
165
|
@inventory_validity = timer.to_i
|
168
166
|
output "inventory validity set to #{timer}"
|
169
167
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: steam-trade
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- OmG3r
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-
|
11
|
+
date: 2018-06-09 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|