rest-gw2 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGES.md +30 -0
  3. data/README.md +7 -3
  4. data/Rakefile +1 -0
  5. data/TODO.md +26 -0
  6. data/config.ru +6 -0
  7. data/lib/rest-gw2.rb +1 -0
  8. data/lib/rest-gw2/client.rb +249 -110
  9. data/lib/rest-gw2/client/item_detail.rb +80 -0
  10. data/lib/rest-gw2/server.rb +5 -583
  11. data/lib/rest-gw2/server/action.rb +283 -0
  12. data/lib/rest-gw2/server/cache.rb +14 -4
  13. data/lib/rest-gw2/server/imp.rb +270 -0
  14. data/lib/rest-gw2/server/runner.rb +1 -0
  15. data/lib/rest-gw2/server/view.rb +309 -0
  16. data/lib/rest-gw2/{view → server/view}/characters.erb +10 -6
  17. data/lib/rest-gw2/server/view/check_list.erb +10 -0
  18. data/lib/rest-gw2/server/view/check_percentage.erb +9 -0
  19. data/lib/rest-gw2/server/view/commerce.erb +24 -0
  20. data/lib/rest-gw2/{view → server/view}/dyes.erb +7 -7
  21. data/lib/rest-gw2/server/view/error.erb +1 -0
  22. data/lib/rest-gw2/server/view/exchange.erb +29 -0
  23. data/lib/rest-gw2/server/view/guild_info.erb +37 -0
  24. data/lib/rest-gw2/{view → server/view}/index.erb +0 -0
  25. data/lib/rest-gw2/{view → server/view}/info.erb +1 -1
  26. data/lib/rest-gw2/{view → server/view}/item_list.erb +0 -0
  27. data/lib/rest-gw2/{view/items.erb → server/view/item_section.erb} +0 -0
  28. data/lib/rest-gw2/{view → server/view}/item_show.erb +7 -1
  29. data/lib/rest-gw2/server/view/items.erb +8 -0
  30. data/lib/rest-gw2/server/view/items_from.erb +35 -0
  31. data/lib/rest-gw2/{view → server/view}/layout.erb +14 -4
  32. data/lib/rest-gw2/server/view/members.erb +23 -0
  33. data/lib/rest-gw2/server/view/menu.erb +13 -0
  34. data/lib/rest-gw2/server/view/menu_armors.erb +17 -0
  35. data/lib/rest-gw2/server/view/menu_commerce.erb +7 -0
  36. data/lib/rest-gw2/server/view/menu_guild.erb +11 -0
  37. data/lib/rest-gw2/server/view/menu_unlocks.erb +11 -0
  38. data/lib/rest-gw2/{view → server/view}/menu_weapons.erb +2 -1
  39. data/lib/rest-gw2/{view → server/view}/pages.erb +2 -2
  40. data/lib/rest-gw2/{view → server/view}/profile.erb +7 -7
  41. data/lib/rest-gw2/server/view/skins.erb +15 -0
  42. data/lib/rest-gw2/server/view/stash.erb +10 -0
  43. data/lib/rest-gw2/server/view/titles.erb +5 -0
  44. data/lib/rest-gw2/server/view/unlock_percentage.erb +1 -0
  45. data/lib/rest-gw2/server/view/unlocks_items.erb +5 -0
  46. data/lib/rest-gw2/server/view/unlocks_list.erb +3 -0
  47. data/lib/rest-gw2/{view → server/view}/wallet.erb +1 -1
  48. data/lib/rest-gw2/server/view/wip.erb +1 -0
  49. data/lib/rest-gw2/version.rb +2 -1
  50. data/rest-gw2.gemspec +43 -25
  51. data/task/README.md +8 -8
  52. data/task/gemgem.rb +29 -7
  53. metadata +42 -25
  54. data/lib/rest-gw2/view/error.erb +0 -1
  55. data/lib/rest-gw2/view/guild.erb +0 -14
  56. data/lib/rest-gw2/view/items_from.erb +0 -30
  57. data/lib/rest-gw2/view/menu.erb +0 -16
  58. data/lib/rest-gw2/view/menu_armors.erb +0 -11
  59. data/lib/rest-gw2/view/skins.erb +0 -14
  60. data/lib/rest-gw2/view/transactions.erb +0 -28
  61. data/lib/rest-gw2/view/wip.erb +0 -1
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RestGW2
4
+ ItemDetailStruct = Struct.new(:client, :raw, :opts)
5
+
6
+ class ItemDetail < ItemDetailStruct
7
+ attr_reader :detail, :upgrades, :stats, :skins
8
+
9
+ def populate
10
+ detail_promises = item_detail_group_by_id(raw)
11
+ upgrades_promises = extract_items_in_slots('upgrades', 'infusions')
12
+ stats_promises = [expand_stats_detail]
13
+
14
+ @detail = detail_promises_to_map(detail_promises)
15
+ @upgrades = detail_promises_to_map(upgrades_promises)
16
+ @stats = detail_promises_to_map(stats_promises)
17
+ @skins = client.all_skins.flatten.group_by{ |s| s['id'] }
18
+ end
19
+
20
+ def fill item
21
+ return item unless item
22
+
23
+ s = item['skin']
24
+ u = item['upgrades']
25
+ f = item['infusions']
26
+ t = item['stats']
27
+
28
+ # Items might not have details, but they could still have upgrades!
29
+ # Blame ANet for not populating whitelist for existing items.
30
+ item.merge(detail[item['id']] || {}).merge(
31
+ 'count' => item['count'] || 1,
32
+ 'skin' => s && skins.dig(s, 0),
33
+ 'upgrades' => u && u.flat_map(&upgrades.method(:[])),
34
+ 'infusions' => f && f.flat_map(&upgrades.method(:[])),
35
+ 'stats' => t && stats[t['id']].merge(t))
36
+ end
37
+
38
+ private
39
+
40
+ # Returns Array[Promise[Array[Detail]]]
41
+ # https://wiki.guildwars2.com/wiki/API:2/items
42
+ # https://wiki.guildwars2.com/wiki/API:2/commerce/prices
43
+ def item_detail_group_by_id items
44
+ items.map{ |i| i && i['id'] }.compact.each_slice(100).map do |slice|
45
+ q = {:ids => slice.join(',')}
46
+ [client.get('v2/items', q),
47
+ client.get('v2/commerce/prices', q,
48
+ {:error_detector => false}.merge(opts))]
49
+ end
50
+ end
51
+
52
+ # Returns Array[Promise[Array[Detail]]]
53
+ def extract_items_in_slots *slots
54
+ items = raw.flat_map do |i|
55
+ if i
56
+ i.values_at(*slots).flatten.compact.map do |id|
57
+ {'id' => id}
58
+ end
59
+ else
60
+ []
61
+ end
62
+ end
63
+ item_detail_group_by_id(items)
64
+ end
65
+
66
+ # https://wiki.guildwars2.com/wiki/API:2/itemstats
67
+ def expand_stats_detail
68
+ raw.map{ |i| i && i.dig('stats', 'id') }.
69
+ compact.uniq.each_slice(100).map do |ids|
70
+ client.get('v2/itemstats', :ids => ids.join(','))
71
+ end
72
+ end
73
+
74
+ # this is probably a dirty way to workaround converting hashes to arrays
75
+ def detail_promises_to_map promises
76
+ promises.flat_map(&:itself).map(&:to_a).flatten.group_by{ |i| i['id'] }.
77
+ inject({}){ |r, (id, v)| r[id] = v.inject(&:merge); r }
78
+ end
79
+ end
80
+ end
@@ -1,16 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rest-gw2/server/action'
1
4
 
2
- require 'rest-gw2/server/cache'
3
- require 'rest-gw2/client'
4
- require 'mime/types'
5
- require 'rest-core'
6
5
  require 'jellyfish'
7
6
  require 'rack'
8
7
 
9
- require 'timeout'
10
- require 'openssl'
11
- require 'erb'
12
- require 'cgi'
13
-
14
8
  module RestGW2
15
9
  CONFIG = ENV['RESTGW2_CONFIG'] || File.expand_path("#{__dir__}/../../.env")
16
10
 
@@ -18,7 +12,7 @@ module RestGW2
18
12
  return {} unless File.exist?(path)
19
13
  Hash[File.read(path).strip.squeeze("\n").each_line.map do |line|
20
14
  name, value = line.split('=')
21
- [name, value.chomp] if name && value
15
+ [name, value.chomp] if !line.start_with?('#') && name && value
22
16
  end.compact]
23
17
  end
24
18
 
@@ -26,574 +20,6 @@ module RestGW2
26
20
  ENV[k] ||= v
27
21
  end
28
22
 
29
- class ServerCore
30
- include Jellyfish
31
- SECRET = ENV['RESTGW2_SECRET'] || 'RESTGW2_SECRET'*2
32
- COINS = %w[gold silver copper].zip(%w[
33
- https://wiki.guildwars2.com/images/d/d1/Gold_coin.png
34
- https://wiki.guildwars2.com/images/3/3c/Silver_coin.png
35
- https://wiki.guildwars2.com/images/e/eb/Copper_coin.png
36
- ]).freeze
37
-
38
- def self.weapons
39
- %w[Greatsword Sword Hammer Mace Axe Dagger
40
- Staff Scepter
41
- LongBow ShortBow Rifle Pistol
42
- Shield Torch Focus Warhorn
43
- Harpoon Speargun Trident]
44
- end
45
-
46
- def self.armors
47
- %w[Helm Shoulders Coat Gloves Leggings Boots HelmAquatic]
48
- end
49
-
50
- def self.armors_weight
51
- %w[Light Medium Heavy Clothing]
52
- end
53
-
54
- def self.crafting
55
- %w[Weaponsmith Huntsman Artificer
56
- Armorsmith Leatherworker Tailor
57
- Jeweler Chef Scribe]
58
- end
59
-
60
- HTML = Class.new(Struct.new(:to_s))
61
-
62
- controller_include NormalizedPath, Module.new{
63
- # VIEW
64
- def render path, arg=nil
65
- erb(:layout){ erb(path, arg) }
66
- end
67
-
68
- def erb name, arg=nil, &block
69
- ERB.new(views(name)).result(binding, &block)
70
- end
71
-
72
- def h str
73
- case str
74
- when String
75
- CGI.escape_html(str)
76
- when HTML
77
- str.to_s
78
- end
79
- end
80
-
81
- def u str
82
- CGI.escape(str) if str.kind_of?(String)
83
- end
84
-
85
- def path str, q={}
86
- RC::Middleware.request_uri(
87
- RC::REQUEST_PATH => "#{ENV['RESTGW2_PREFIX']}#{str}",
88
- RC::REQUEST_QUERY => q)
89
- end
90
-
91
- def views name
92
- @views ||= {}
93
- @views[name] = File.read("#{__dir__}/view/#{name}.erb")
94
- end
95
-
96
- def refresh_path
97
- path(request.path, :p => p, :r => '1', :t => t)
98
- end
99
-
100
- # TODO: clean me up
101
- def menu item, title, query={}
102
- href = path(item, query.merge(:t => t))
103
- if path(request.path, :p => p, :t => t) == href
104
- title
105
- else
106
- %Q{<a href="#{href}">#{title}</a>}
107
- end
108
- end
109
-
110
- # TODO: clean me up
111
- def menu_sub prefix, item, title
112
- key = "#{prefix}#{item}"
113
- if path(request.path) == path(key)
114
- menu(key, title, :p => p)
115
- else
116
- menu(key, title)
117
- end
118
- end
119
-
120
- def menu_char name
121
- menu("/characters/#{RC::Middleware.escape(name)}", name)
122
- end
123
-
124
- def menu_skin item, title
125
- menu_sub('/skins', item, title)
126
- end
127
-
128
- def menu_trans item, title
129
- menu_sub('/transactions', item, title)
130
- end
131
-
132
- def page num
133
- menu(request.path, num.to_s, :p => zero_is_nil(num))
134
- end
135
-
136
- # HELPER
137
- def show_guild g
138
- HTML.new(menu("/guilds/#{g['guild_id']}",
139
- h("#{g['guild_name']} [#{g['tag']}]")))
140
- end
141
-
142
- def blank_icon
143
- %Q{<img class="icon" src="https://upload.wikimedia.org/wikipedia/commons/d/d2/Blank.png"/>}
144
- end
145
-
146
- def item_wiki_list items
147
- items.map(&method(:item_wiki)).join("\n")
148
- end
149
-
150
- def item_wiki item
151
- if item['name'] && item['icon']
152
- name = item['name'].tr(' ', '_')
153
- missing = if item['count'] == 0 then ' missing' else nil end
154
- img = %Q{<img class="icon#{missing}" title="#{item_title(item)}"} +
155
- %Q{ src="#{h item['icon']}"/>}
156
- %Q{<a href="http://wiki.guildwars2.com/wiki/#{u name}">#{img}</a>}
157
- else
158
- blank_icon
159
- end
160
- end
161
-
162
- def item_link item
163
- name = item_name(item)
164
- if item['nolink']
165
- name
166
- else
167
- menu("/items/#{item['id']}", name)
168
- end
169
- end
170
-
171
- def item_name item
172
- h(item['name'] || "?#{item['id']}?")
173
- end
174
-
175
- def item_title item
176
- d = item['description']
177
- d && d.unpack('U*').map{ |c| "&##{c};" }.join
178
- end
179
-
180
- def item_count item
181
- c = item['count']
182
- "(#{c})" if c > 1
183
- end
184
-
185
- def item_price item
186
- b = item['buys']
187
- s = item['sells']
188
- bb = b && price(b['unit_price'])
189
- ss = s && price(s['unit_price'])
190
- %Q{#{bb} / #{ss}} if bb || ss
191
- end
192
-
193
- def price copper
194
- g = copper / 100_00
195
- s = copper % 100_00 / 100
196
- c = copper % 100
197
- l = [g, s, c]
198
- n = l.index(&:nonzero?)
199
- return '-' unless n
200
- l.zip(COINS).drop(n).map do |(num, (title, src))|
201
- %Q{#{num}<img class="price" title="#{h title}" src="#{h src}"/>}
202
- end.join(' ')
203
- end
204
-
205
- def dye_color dye
206
- %w[cloth leather metal].map do |kind|
207
- rgb = dye[kind]['rgb']
208
- rgb && dye_preview(kind, rgb.join(', '))
209
- end.join("\n")
210
- end
211
-
212
- def dye_preview kind, rgb
213
- %Q{<span class="icon" title="#{kind}, rgb(#{rgb})"} +
214
- %Q{ style="background-color: rgb(#{rgb})"></span>}
215
- end
216
-
217
- def abbr_time_ago time, precision=1
218
- return unless time
219
- ago = time_ago(time)
220
- short = ago.take(precision).join(' ')
221
- %Q{(<abbr title="#{time}, #{ago.join(' ')} ago">#{short} ago</abbr>)}
222
- end
223
-
224
- def time_ago time
225
- duration((Time.now - Time.parse(time)).to_i)
226
- end
227
-
228
- def duration delta
229
- result = []
230
-
231
- [[ 60, :seconds],
232
- [ 60, :minutes],
233
- [ 24, :hours ],
234
- [365, :days ],
235
- [999, :years ]].
236
- inject(delta) do |length, (divisor, name)|
237
- quotient, remainder = length.divmod(divisor)
238
- result.unshift("#{remainder} #{name}")
239
- break if quotient == 0
240
- quotient
241
- end
242
-
243
- result
244
- end
245
-
246
- def sum_trans trans
247
- trans.inject(0) do |sum, t|
248
- sum + t['price'] * t['quantity']
249
- end
250
- end
251
-
252
- def sum_items items
253
- items.inject([0, 0]) do |sum, i|
254
- next sum unless i
255
- b = i['buys']
256
- s = i['sells']
257
- sum[0] += b['unit_price'] * i['count'] if b
258
- sum[1] += s['unit_price'] * i['count'] if s
259
- sum
260
- end
261
- end
262
-
263
- def all_items
264
- bank, mats, chars = all_items_defer
265
- flatten_chars = chars.flat_map do |c|
266
- c['equipment'] +
267
- c['bags'] +
268
- c['bags'].flat_map{ |c| c && c['inventory'] }
269
- end
270
- (bank + mats + flatten_chars).compact.
271
- sort_by{ |i| i['name'] || i['id'].to_s }.inject([]) do |r, i|
272
- last = r.last
273
- if last && last['id'] == i['id'] &&
274
- last.values_at('skin', 'upgrades', 'infusions').compact.empty?
275
- last['count'] += i['count']
276
- else
277
- r << i
278
- end
279
- r
280
- end
281
- end
282
-
283
- def find_my_item id
284
- bank, mats, chars = all_items_defer
285
- [select_item(bank.compact, id), select_item(mats.compact, id),
286
- chars.inject({}){ |r, c|
287
- equi = select_item(c['equipment'].compact, id)
288
- bags = c['bags'].reject(&:nil?).map do |b|
289
- selected = select_item(b['inventory'].compact, id)
290
- b.merge('inventory' => selected) if selected.any? ||
291
- b['id'] == id
292
- end.compact
293
- r[c['name']] = [equi, bags] if equi.any? || bags.any?
294
- r
295
- }]
296
- end
297
-
298
- def select_item items, id
299
- items.select{ |i| i['id'] == id }
300
- end
301
-
302
- def all_items_defer
303
- bank = gw2_defer(:with_item_detail, 'v2/account/bank')
304
- mats = gw2_defer(:with_item_detail, 'v2/account/materials')
305
- chars = gw2_defer(:characters_with_detail).map do |c|
306
- c['equipment'] = gw2_defer(:expand_item_detail, c['equipment'])
307
- c['bags'] = gw2_defer(:bags_with_detail , c['bags'])
308
- c
309
- end
310
- [bank, mats, chars]
311
- end
312
-
313
- # CONTROLLER
314
- def gw2_request msg, *args
315
- block ||= :itself.to_proc
316
- refresh = !!request.GET['r']
317
- opts = {'cache.update' => refresh, 'expires_in' => 600}
318
- args << {} if msg == :with_item_detail
319
- gw2.public_send(msg, *args, opts)
320
- end
321
-
322
- def gw2_defer msg, *args
323
- PromisePool::Promise.new.defer do
324
- gw2_request(msg, *args)
325
- end.future
326
- end
327
-
328
- def skin_request type, subtype=nil, weight=nil
329
- @items = gw2_request(:skins_with_detail).select do |i|
330
- i['type'] == type &&
331
- (subtype.nil? || subtype == i['details']['type']) &&
332
- (weight.nil? || weight == i['details']['weight_class'])
333
- end
334
- @skin_submenu = "menu_#{type.downcase}s" if subtype
335
- @subtype = subtype.downcase if subtype
336
- @weight = weight.downcase if weight
337
- @unlocked = @items.count{ |i| i['count'] > 0 }
338
- render :skins
339
- end
340
-
341
- def trans_request msg, path
342
- @trans = gw2_request(msg, path, :page => p)
343
- @pages = calculate_pages("v2/commerce/transactions/#{path}")
344
- @total = sum_trans(@trans)
345
- render :transactions
346
- end
347
-
348
- def group_by_crafting characters
349
- characters.inject(Hash.new{|h,k|h[k]=[]}) do |group, char|
350
- char['crafting'].each do |crafting|
351
- group[crafting['discipline']] <<
352
- [crafting['rating'], char['name'], crafting['active']]
353
- end
354
- group
355
- end
356
- end
357
-
358
- def calculate_pages path
359
- link = gw2.get(path, {:page_size => 200},
360
- RC::RESPONSE_KEY => RC::RESPONSE_HEADERS)['LINK']
361
- pages = RC::ParseLink.parse_link(link)
362
- parse_page(pages['first']['uri'])..parse_page(pages['last']['uri'])
363
- end
364
-
365
- def parse_page uri
366
- RC::ParseQuery.parse_query(URI.parse(uri).query)['page'].to_i
367
- end
368
-
369
- def gw2
370
- Client.new(:access_token => access_token,
371
- :log_method => logger(env).method(:info),
372
- :cache => RestGW2::Cache.default(logger(env)))
373
- end
374
-
375
- # ACCESS TOKEN
376
- def access_token
377
- t && decrypt(t) || ENV['RESTGW2_ACCESS_TOKEN']
378
- rescue ArgumentError, OpenSSL::Cipher::CipherError => e
379
- raise RestGW2::Error.new({'text' => e.message}, 0)
380
- end
381
-
382
- def t
383
- @t ||= begin
384
- r = request.GET['t']
385
- r if r && !r.strip.empty?
386
- end
387
- end
388
-
389
- def p
390
- @p ||= zero_is_nil(request.GET['p'])
391
- end
392
-
393
- def zero_is_nil n
394
- r = n.to_i
395
- r if r != 0
396
- end
397
-
398
- # UTILITIES
399
- def encrypt data
400
- cipher = new_cipher
401
- cipher.encrypt
402
- cipher.key = SECRET
403
- iv = cipher.random_iv
404
- encrypted = cipher.update(data) + cipher.final
405
- tag = auth_tag(cipher)
406
- encode_base64(iv, encrypted, tag)
407
- end
408
-
409
- def decrypt data
410
- iv, encrypted, tag = decode_base64(data)
411
- cipher = new_cipher
412
- cipher.decrypt
413
- cipher.key = SECRET
414
- cipher.iv = iv
415
- set_auth_tag(cipher, tag)
416
- cipher.update(encrypted) + cipher.final
417
- end
418
-
419
- def encode_base64 *data
420
- data.map{ |d| [d].pack('m0') }.join('.').tr('+/=', '-_~')
421
- end
422
-
423
- def decode_base64 str
424
- str.split('.').map{ |d| d.tr('-_~', '+/=').unpack('m0').first }
425
- end
426
-
427
- def new_cipher
428
- OpenSSL::Cipher.new(ENV['CIPHER_ALGO'] || 'aes-128-gcm')
429
- rescue OpenSSL::Cipher::CipherError
430
- OpenSSL::Cipher.new('aes-128-cbc')
431
- end
432
-
433
- def auth_tag cipher
434
- cipher.respond_to?(:auth_tag) && cipher.auth_tag || ''
435
- end
436
-
437
- def set_auth_tag cipher, tag
438
- cipher.respond_to?(:auth_tag=) && cipher.auth_tag = tag
439
- end
440
-
441
- # MISC
442
- def logger env
443
- env['rack.logger'] || begin
444
- require 'logger'
445
- Logger.new(env['rack.errors'])
446
- end
447
- end
448
- }
449
-
450
- handle Timeout::Error do
451
- status 504
452
- @error = 'Timeout. Please try again.'
453
- render :error
454
- end
455
-
456
- handle RestGW2::Error do |e|
457
- status 502
458
- @error = e.error['text']
459
- render :error
460
- end
461
-
462
- post '/access_token' do
463
- t = encrypt(request.POST['access_token'])
464
- r = request.POST['referrer']
465
- u = if r == path('/') then path('/account') else r end
466
- found "#{u}?t=#{t}"
467
- end
468
-
469
- get '/' do
470
- render :index
471
- end
472
-
473
- get '/account' do
474
- @info = gw2_request(:account_with_detail)
475
- @info['guilds'].map!(&method(:show_guild))
476
- render :info
477
- end
478
-
479
- get %r{\A/guilds/(?<uuid>[^/]+)\z} do |m|
480
- gid = m[:uuid]
481
- @guilds = gw2_request(:account_with_detail)['guilds']
482
- if @guilds.find{ |g| g['guild_id'] == gid }
483
- @treasury = gw2_defer(:treasury_with_detail, gid)
484
- @stash = gw2_defer( :stash_with_detail, gid)
485
- render :guild
486
- else
487
- status 404
488
- @error = "Cannot find guild id: #{gid}"
489
- render :error
490
- end
491
- end
492
-
493
- get '/characters' do
494
- @chars = gw2_request(:characters_with_detail)
495
- @total = @chars.inject(0){ |t, c| t + c['age'] }
496
- @craftings = group_by_crafting(@chars)
497
- render :characters
498
- end
499
-
500
- get %r{\A/characters/(?<name>[\w ]+)\z} do |m|
501
- characters = gw2_request(:characters_with_detail)
502
- @names = characters.map { |c| c['name'] }
503
- name = m[:name]
504
- char = characters.find{ |c| c['name'] == name }
505
-
506
- equi = gw2_defer(:expand_item_detail, char['equipment'])
507
- bags = gw2_defer(:bags_with_detail , char['bags'])
508
-
509
- @equi = equi
510
- @bags = bags
511
-
512
- @equi_buy, @equi_sell = sum_items(@equi)
513
- @bags_buy, @bags_sell = sum_items(@bags +
514
- @bags.flat_map{ |c| c && c['inventory'] })
515
- render :profile
516
- end
517
-
518
- get '/dyes' do
519
- @dyes = gw2_request(:dyes_with_detail)
520
- @buy, @sell = sum_items(@dyes)
521
- @unlocked = @dyes.count{ |d| d['count'] > 0 }
522
- render :dyes
523
- end
524
-
525
- get '/skins/backpacks' do
526
- skin_request('Back')
527
- end
528
-
529
- weapons.each do |weapon|
530
- get "/skins/weapons/#{weapon.downcase}" do
531
- skin_request('Weapon', weapon)
532
- end
533
- end
534
-
535
- armors.each do |armor|
536
- armors_weight.each do |weight|
537
- get "/skins/armors/#{armor.downcase}/#{weight.downcase}" do
538
- skin_request('Armor', armor, weight)
539
- end
540
- end
541
- end
542
-
543
- get '/minis' do
544
- render :items, gw2_request(:minis_with_detail)
545
- end
546
-
547
- get '/achievements' do
548
- render :wip
549
- end
550
-
551
- get '/bank' do
552
- render :items, gw2_request(:with_item_detail, 'v2/account/bank')
553
- end
554
-
555
- get '/materials' do
556
- render :items, gw2_request(:with_item_detail, 'v2/account/materials')
557
- end
558
-
559
- get '/wallet' do
560
- @wallet = gw2_request(:wallet_with_detail)
561
- render :wallet
562
- end
563
-
564
- get '/items' do
565
- render :items, all_items
566
- end
567
-
568
- get %r{\A/items/(?<id>\d+)\z} do |m|
569
- items = find_my_item(m[:id].to_i)
570
- @bank, @materials, @chars = items
571
- @buy, @sell = sum_items(@bank + @materials + @chars.values.flatten)
572
- render :items_from
573
- end
574
-
575
- get '/transactions/buying' do
576
- trans_request(:transactions_with_detail_compact, 'current/buys')
577
- end
578
-
579
- get '/transactions/selling' do
580
- trans_request(:transactions_with_detail_compact, 'current/sells')
581
- end
582
-
583
- get '/transactions/bought' do
584
- trans_request(:transactions_with_detail_compact, 'history/buys')
585
- end
586
-
587
- get '/transactions/sold' do
588
- trans_request(:transactions_with_detail_compact, 'history/sells')
589
- end
590
-
591
- get '/tokeninfo' do
592
- @info = gw2_request(:get, 'v2/tokeninfo')
593
- render :info
594
- end
595
- end
596
-
597
23
  Server = Jellyfish::Builder.app do
598
24
  use Rack::CommonLogger
599
25
  use Rack::Chunked
@@ -601,12 +27,8 @@ module RestGW2
601
27
  use Rack::Deflater
602
28
  use Rack::ContentType, 'text/html; charset=utf-8'
603
29
 
604
- map '/assets' do
605
- run Rack::Directory.new('public')
606
- end
607
-
608
30
  map '/' do
609
- run ServerCore.new
31
+ run ServerAction.new
610
32
  end
611
33
  end
612
34
  end