steam-trade 0.2.1 → 0.2.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|