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 +4 -4
- data/README.md +33 -4
- data/lib/Badge.rb +133 -0
- data/lib/Confirmation.rb +4 -6
- data/lib/Guard.rb +4 -2
- data/lib/Inventory.rb +111 -0
- data/lib/LoginExecutor.rb +15 -6
- data/lib/Misc.rb +37 -1
- data/lib/Playerinfo.rb +28 -0
- data/lib/Trade.rb +82 -24
- data/lib/blueprints/appid_title.json +1 -0
- data/lib/blueprints/byappid.json +1 -0
- data/lib/meta/version.rb +4 -0
- data/lib/steam-trade.rb +50 -11
- data/steam-trade.gemspec +6 -5
- metadata +9 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 34bdbd304683a4b23f616f4b8ebfb86f05c6a5f5
|
4
|
+
data.tar.gz: 0af0c76c9407399eaec724220221219bba88382b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
|
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
|
data/lib/Badge.rb
ADDED
@@ -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
|
data/lib/Confirmation.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
|
1
|
+
|
2
2
|
|
3
3
|
|
4
4
|
module ConfirmationCommands
|
5
|
-
|
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
|
-
|
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
|
-
|
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
|
data/lib/Guard.rb
CHANGED
@@ -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(
|
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))
|
data/lib/Inventory.rb
ADDED
@@ -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
|
data/lib/LoginExecutor.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
|
data/lib/Misc.rb
CHANGED
@@ -1,5 +1,17 @@
|
|
1
|
-
|
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
|