rest-gw2 0.4.0 → 0.5.0

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.
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