maimai_net 0.0.1
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 +7 -0
- data/.github/workflows/gem.yml +53 -0
- data/.gitignore +165 -0
- data/.rspec +5 -0
- data/Gemfile +4 -0
- data/LICENSE +32 -0
- data/README.md +47 -0
- data/Rakefile +6 -0
- data/bin/console +16 -0
- data/bin/setup +6 -0
- data/lib/maimai_net/client.rb +1104 -0
- data/lib/maimai_net/constants.rb +485 -0
- data/lib/maimai_net/core_ext.rb +12 -0
- data/lib/maimai_net/error.rb +55 -0
- data/lib/maimai_net/faraday_ext/cookie_jar.rb +38 -0
- data/lib/maimai_net/model-typing.rb +202 -0
- data/lib/maimai_net/model.rb +359 -0
- data/lib/maimai_net/module_ext.rb +437 -0
- data/lib/maimai_net/page-debug.rb +9 -0
- data/lib/maimai_net/page-html_helper.rb +131 -0
- data/lib/maimai_net/page-player_data_helper.rb +33 -0
- data/lib/maimai_net/page-track_result_helper.rb +90 -0
- data/lib/maimai_net/page.rb +606 -0
- data/lib/maimai_net/refines.rb +28 -0
- data/lib/maimai_net/region.rb +108 -0
- data/lib/maimai_net/user_option.rb +147 -0
- data/lib/maimai_net/util.rb +9 -0
- data/lib/maimai_net/version.rb +3 -0
- data/lib/maimai_net.rb +14 -0
- data/maimai-net.gemspec +44 -0
- metadata +172 -0
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
module MaimaiNet
|
|
2
|
+
module Constants
|
|
3
|
+
Constant = Module.new.freeze
|
|
4
|
+
|
|
5
|
+
module AutoConstant
|
|
6
|
+
def self.extend_object(cls)
|
|
7
|
+
super
|
|
8
|
+
return unless Module === cls
|
|
9
|
+
|
|
10
|
+
stack = caller_locations(0).find do |s| s.label == 'extend' end
|
|
11
|
+
|
|
12
|
+
cls.class_exec do
|
|
13
|
+
include Constant
|
|
14
|
+
|
|
15
|
+
attrs = instance_methods(false)
|
|
16
|
+
.map(&method(:instance_method))
|
|
17
|
+
.map(&:original_name)
|
|
18
|
+
.sort.uniq
|
|
19
|
+
const_set :VALID_ATTRIBUTES, attrs
|
|
20
|
+
|
|
21
|
+
alias_method :clone, :itself
|
|
22
|
+
alias_method :dup, :itself
|
|
23
|
+
undef_method :initialize_copy
|
|
24
|
+
|
|
25
|
+
constants.map(&method(:const_get))
|
|
26
|
+
.map(&:freeze)
|
|
27
|
+
|
|
28
|
+
attrs.each do |attr|
|
|
29
|
+
instance_eval <<~EOT, stack.path, stack.lineno
|
|
30
|
+
def #{attr}?(value)
|
|
31
|
+
new(#{attr}: value)
|
|
32
|
+
rescue TypeError
|
|
33
|
+
nil
|
|
34
|
+
end
|
|
35
|
+
EOT
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def define_new(
|
|
41
|
+
key_enforce: nil,
|
|
42
|
+
key_lookup_enforce: nil,
|
|
43
|
+
key_assign_enforce: nil,
|
|
44
|
+
extra_lookup_keys: []
|
|
45
|
+
)
|
|
46
|
+
builder = []
|
|
47
|
+
builder << <<~EOT
|
|
48
|
+
@map ||= {}
|
|
49
|
+
@keys ||= {}
|
|
50
|
+
EOT
|
|
51
|
+
|
|
52
|
+
key_conditions = {}
|
|
53
|
+
key_conditions[Pathname] = 'key = key.to_s'
|
|
54
|
+
key_conditions[Hash] = <<~EOS
|
|
55
|
+
if !@map.empty? && key.size == 1 then
|
|
56
|
+
dk, dv = key.first
|
|
57
|
+
key = [
|
|
58
|
+
@map.values.find do |obj|
|
|
59
|
+
val = obj.public_send(dk)
|
|
60
|
+
!val.nil? && val == dv
|
|
61
|
+
end&.key,
|
|
62
|
+
key,
|
|
63
|
+
].compact.each.next if const_get(:VALID_ATTRIBUTES).include? dk.to_sym
|
|
64
|
+
end
|
|
65
|
+
EOS
|
|
66
|
+
|
|
67
|
+
key_conditions[Integer] = <<-EOS if const_get(:VALID_ATTRIBUTES).include?(:id)
|
|
68
|
+
key = [
|
|
69
|
+
@map.values.find do |obj| obj.id == key end&.key,
|
|
70
|
+
key,
|
|
71
|
+
].compact.each.next
|
|
72
|
+
EOS
|
|
73
|
+
|
|
74
|
+
builder << "case key\n%s\nend" % [key_conditions.map do |k, v| "when #{k}\n #{v}" end.join($/)]
|
|
75
|
+
|
|
76
|
+
key_lookup_enforce = key_enforce if key_lookup_enforce.nil?
|
|
77
|
+
key_assign_enforce = key_enforce if key_assign_enforce.nil?
|
|
78
|
+
|
|
79
|
+
builder << <<~'EOT'
|
|
80
|
+
key = key.to_sym if key.respond_to?(:to_sym)
|
|
81
|
+
fail TypeError, "expected Symbol, given %s" % [key.class] unless Symbol === key
|
|
82
|
+
EOT
|
|
83
|
+
|
|
84
|
+
extra_lookup_builder = extra_lookup_keys.map do |k|
|
|
85
|
+
"names << obj.#{k}"
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
key_lookup_enforce_builder = ''
|
|
89
|
+
key_lookup_enforce_builder = <<~EOT if Symbol === key_lookup_enforce
|
|
90
|
+
elsif @keys.key?(key.#{key_lookup_enforce}) then
|
|
91
|
+
obj = @map[@keys[key.#{key_lookup_enforce}]]
|
|
92
|
+
EOT
|
|
93
|
+
|
|
94
|
+
key_assign_enforce_builder = ''
|
|
95
|
+
key_assign_enforce_builder = "key = key.#{key_assign_enforce}" if Symbol === key_assign_enforce
|
|
96
|
+
|
|
97
|
+
builder << <<~EOT
|
|
98
|
+
if @keys.key?(key) then
|
|
99
|
+
obj = @map[@keys[key]]
|
|
100
|
+
#{key_lookup_enforce_builder}
|
|
101
|
+
else
|
|
102
|
+
#{key_assign_enforce_builder}
|
|
103
|
+
obj = super
|
|
104
|
+
@map[obj.object_id] = obj
|
|
105
|
+
names = [key]
|
|
106
|
+
#{extra_lookup_builder * $/}
|
|
107
|
+
names.each do |k|
|
|
108
|
+
@keys.store k, obj.object_id
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
EOT
|
|
112
|
+
|
|
113
|
+
builder << 'obj'
|
|
114
|
+
|
|
115
|
+
stack = caller_locations(1).first
|
|
116
|
+
instance_eval "def new(key)\n%s\nend" % [builder.join($/)],
|
|
117
|
+
stack.path, stack.lineno
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def populate_entries(constant_or_list)
|
|
121
|
+
list = nil
|
|
122
|
+
|
|
123
|
+
case constant_or_list
|
|
124
|
+
when Symbol
|
|
125
|
+
list = const_get(constant_or_list)
|
|
126
|
+
else
|
|
127
|
+
list = constant_or_list
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
case list
|
|
131
|
+
when Hash
|
|
132
|
+
list = list.keys
|
|
133
|
+
when Enumerable
|
|
134
|
+
list = list.to_a
|
|
135
|
+
else
|
|
136
|
+
if Symbol === constant_or_list then
|
|
137
|
+
fail ArgumentError, "expected constant #{constant_or_list} is an enumerable, given #{list.class}"
|
|
138
|
+
else
|
|
139
|
+
fail TypeError, "expected a constant name or an enumerable, given #{constant_or_list.class}"
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
singleton_class.undef_method __method__ rescue 0
|
|
144
|
+
list.each &method(:new)
|
|
145
|
+
end
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
private_constant :AutoConstant
|
|
149
|
+
|
|
150
|
+
class AchievementFlag
|
|
151
|
+
COMBO = %i(fc ap)
|
|
152
|
+
SYNC = %i(sync fs fsd)
|
|
153
|
+
PLUS = %i(fc ap fs fsd)
|
|
154
|
+
RESULT_TO_RECORD = {
|
|
155
|
+
fsd: :fdx,
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
sym_iter = ->(&block){
|
|
159
|
+
(COMBO + SYNC).lazy.flat_map do |bk|
|
|
160
|
+
[bk, *(PLUS.include?(bk) ? [:"#{bk}+"] : [])]
|
|
161
|
+
end.map do |k|
|
|
162
|
+
is_plus = k.end_with?('+')
|
|
163
|
+
plusless = is_plus ? k[0...-1] : k.to_s
|
|
164
|
+
[k, plusless, is_plus]
|
|
165
|
+
end.map(&block)
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
KEYS = sym_iter.call do |k, bk, plus| k.upcase end
|
|
169
|
+
RECORD = sym_iter.call do |k, bk, plus|
|
|
170
|
+
bk = RESULT_TO_RECORD.fetch(bk.to_sym, bk).to_s
|
|
171
|
+
[k.upcase, -(plus ? "#{bk}p" : bk)]
|
|
172
|
+
end.to_h
|
|
173
|
+
RESULT = sym_iter.call do |k, bk, plus|
|
|
174
|
+
[k.upcase, -(plus ? "#{bk}plus" : bk)]
|
|
175
|
+
end.to_h
|
|
176
|
+
|
|
177
|
+
include ModuleExt
|
|
178
|
+
|
|
179
|
+
def initialize(key)
|
|
180
|
+
@key = key
|
|
181
|
+
|
|
182
|
+
@record_key = RECORD[key]
|
|
183
|
+
@result_key = RESULT[key]
|
|
184
|
+
|
|
185
|
+
freeze
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
attr_reader :key
|
|
189
|
+
attr_reader :record_key, :result_key
|
|
190
|
+
|
|
191
|
+
alias id key
|
|
192
|
+
alias to_sym key
|
|
193
|
+
|
|
194
|
+
extend AutoConstant
|
|
195
|
+
define_new key_enforce: :upcase
|
|
196
|
+
populate_entries :RECORD
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
class Difficulty
|
|
200
|
+
ORIGINAL = {
|
|
201
|
+
all: 0,
|
|
202
|
+
easy: 1,
|
|
203
|
+
basic: 2,
|
|
204
|
+
advanced: 3,
|
|
205
|
+
expert: 4,
|
|
206
|
+
master: 5,
|
|
207
|
+
remaster: 6,
|
|
208
|
+
utage: 10,
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
DELUXE = {
|
|
212
|
+
all: 0,
|
|
213
|
+
basic: 1,
|
|
214
|
+
advanced: 2,
|
|
215
|
+
expert: 3,
|
|
216
|
+
master: 4,
|
|
217
|
+
remaster: 5,
|
|
218
|
+
utage: 10,
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
DELUXE_WEBSITE = {
|
|
222
|
+
all: 99,
|
|
223
|
+
basic: 0,
|
|
224
|
+
advanced: 1,
|
|
225
|
+
expert: 2,
|
|
226
|
+
master: 3,
|
|
227
|
+
remaster: 4,
|
|
228
|
+
utage: 10,
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
LIBRARY = {
|
|
232
|
+
all: 0,
|
|
233
|
+
easy: 1,
|
|
234
|
+
basic: 2,
|
|
235
|
+
advanced: 3,
|
|
236
|
+
expert: 4,
|
|
237
|
+
master: 5,
|
|
238
|
+
remaster: 6,
|
|
239
|
+
utage: 10,
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
SHORTS = {
|
|
243
|
+
easy: :EM,
|
|
244
|
+
basic: :BS,
|
|
245
|
+
advanced: :AD,
|
|
246
|
+
expert: :EX,
|
|
247
|
+
master: :MS,
|
|
248
|
+
remaster: :RMS,
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
include ModuleExt
|
|
252
|
+
|
|
253
|
+
def initialize(key)
|
|
254
|
+
@key = key
|
|
255
|
+
|
|
256
|
+
@id = LIBRARY[key]
|
|
257
|
+
@original_id = ORIGINAL[key]
|
|
258
|
+
@deluxe_id = DELUXE[key]
|
|
259
|
+
@deluxe_web_id = DELUXE_WEBSITE[key]
|
|
260
|
+
|
|
261
|
+
@abbrev = SHORTS.fetch(key, key.upcase)
|
|
262
|
+
|
|
263
|
+
freeze
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
attr_reader :key, :abbrev
|
|
267
|
+
attr_reader :id, :original_id, :deluxe_id, :deluxe_web_id
|
|
268
|
+
|
|
269
|
+
alias long key
|
|
270
|
+
alias to_i id
|
|
271
|
+
alias to_sym key
|
|
272
|
+
|
|
273
|
+
extend AutoConstant
|
|
274
|
+
define_new key_enforce: :downcase, extra_lookup_keys: %i(abbrev)
|
|
275
|
+
populate_entries :LIBRARY
|
|
276
|
+
end
|
|
277
|
+
|
|
278
|
+
class Genre
|
|
279
|
+
ORIGINAL = {
|
|
280
|
+
pop_anime: 3,
|
|
281
|
+
niconico: 4,
|
|
282
|
+
touhou: 5,
|
|
283
|
+
sega: 6,
|
|
284
|
+
variety: 7,
|
|
285
|
+
original: 8,
|
|
286
|
+
all: 9,
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
DELUXE_WEBSITE = {
|
|
290
|
+
all: 99,
|
|
291
|
+
pop_anime: 101,
|
|
292
|
+
niconico: 102,
|
|
293
|
+
touhou: 103,
|
|
294
|
+
variety: 104,
|
|
295
|
+
maimai: 105,
|
|
296
|
+
siblings: 106,
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
include ModuleExt
|
|
300
|
+
|
|
301
|
+
def initialize(key)
|
|
302
|
+
@key = key
|
|
303
|
+
|
|
304
|
+
@original_id = ORIGINAL[key]
|
|
305
|
+
@deluxe_web_id = DELUXE_WEBSITE[key]
|
|
306
|
+
|
|
307
|
+
freeze
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
attr_reader :key, :abbrev
|
|
311
|
+
attr_reader :original_id, :deluxe_web_id
|
|
312
|
+
|
|
313
|
+
alias id key
|
|
314
|
+
alias to_sym key
|
|
315
|
+
|
|
316
|
+
extend AutoConstant
|
|
317
|
+
define_new key_enforce: :downcase
|
|
318
|
+
populate_entries :DELUXE_WEBSITE
|
|
319
|
+
end
|
|
320
|
+
|
|
321
|
+
class NameGroup
|
|
322
|
+
LIBRARY = {
|
|
323
|
+
japanese_a: 0,
|
|
324
|
+
japanese_ka: 1,
|
|
325
|
+
japanese_sa: 2,
|
|
326
|
+
japanese_ta: 3,
|
|
327
|
+
japanese_na: 4,
|
|
328
|
+
japanese_ha: 5,
|
|
329
|
+
japanese_ma: 6,
|
|
330
|
+
japanese_ya: 7,
|
|
331
|
+
japanese_ra: 8,
|
|
332
|
+
japanese_misc: 9,
|
|
333
|
+
latin_a: 10,
|
|
334
|
+
latin_e: 11,
|
|
335
|
+
latin_k: 12,
|
|
336
|
+
latin_p: 13,
|
|
337
|
+
latin_t: 14,
|
|
338
|
+
latin_misc: 15,
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
ORIGINAL = LIBRARY
|
|
342
|
+
DELUXE = LIBRARY
|
|
343
|
+
DELUXE_WEBSITE = LIBRARY
|
|
344
|
+
|
|
345
|
+
include ModuleExt
|
|
346
|
+
|
|
347
|
+
def initialize(key)
|
|
348
|
+
@key = key
|
|
349
|
+
|
|
350
|
+
@id = LIBRARY[key]
|
|
351
|
+
@deluxe_web_id = DELUXE_WEBSITE[key]
|
|
352
|
+
|
|
353
|
+
freeze
|
|
354
|
+
end
|
|
355
|
+
|
|
356
|
+
attr_reader :key
|
|
357
|
+
attr_reader :id, :deluxe_web_id
|
|
358
|
+
|
|
359
|
+
alias to_i id
|
|
360
|
+
alias to_sym key
|
|
361
|
+
|
|
362
|
+
extend AutoConstant
|
|
363
|
+
define_new key_enforce: :downcase
|
|
364
|
+
populate_entries :LIBRARY
|
|
365
|
+
end
|
|
366
|
+
|
|
367
|
+
class LevelGroup
|
|
368
|
+
LIBRARY = (1..15).flat_map do |i|
|
|
369
|
+
i < 7 ? i : [i, :"#{i}+"]
|
|
370
|
+
end.map do |k| :"L#{k}" end
|
|
371
|
+
.each_with_index.map do |k, i|
|
|
372
|
+
[k, i.succ]
|
|
373
|
+
end.to_h
|
|
374
|
+
|
|
375
|
+
DELUXE = LIBRARY
|
|
376
|
+
DELUXE_WEBSITE = LIBRARY
|
|
377
|
+
|
|
378
|
+
include ModuleExt
|
|
379
|
+
|
|
380
|
+
def initialize(key)
|
|
381
|
+
@key = key
|
|
382
|
+
|
|
383
|
+
@id = LIBRARY[key]
|
|
384
|
+
@deluxe_id = DELUXE[key]
|
|
385
|
+
@deluxe_web_id = DELUXE_WEBSITE[key]
|
|
386
|
+
|
|
387
|
+
freeze
|
|
388
|
+
end
|
|
389
|
+
|
|
390
|
+
attr_reader :key
|
|
391
|
+
attr_reader :id, :deluxe_id, :deluxe_web_id
|
|
392
|
+
|
|
393
|
+
alias to_i id
|
|
394
|
+
alias to_sym key
|
|
395
|
+
|
|
396
|
+
extend AutoConstant
|
|
397
|
+
define_new key_enforce: :upcase
|
|
398
|
+
populate_entries :LIBRARY
|
|
399
|
+
end
|
|
400
|
+
|
|
401
|
+
class GameVersion
|
|
402
|
+
ORIGINAL_VERSIONS = %w(maimai GReeN ORANGE PiNK MURASAKi MiLK FiNALE)
|
|
403
|
+
DELUXE_VERSIONS = %w(Deluxe Splash UNiVERSE FESTiVAL BUDDiES PRiSM CiRCLE)
|
|
404
|
+
|
|
405
|
+
VERSIONS = {}.tap do |ver|
|
|
406
|
+
ORIGINAL_VERSIONS.slice(0...-1).flat_map do |k|
|
|
407
|
+
[k, "#{k}_PLUS"]
|
|
408
|
+
end.push(ORIGINAL_VERSIONS.last).map(&:upcase).map(&:to_sym).each_with_index.map do |k, i|
|
|
409
|
+
case i
|
|
410
|
+
when 0..8
|
|
411
|
+
[k, 100 + i * 10]
|
|
412
|
+
else
|
|
413
|
+
[k, [180 + (i - 8) * 5, 199].min]
|
|
414
|
+
end
|
|
415
|
+
end.to_h.tap(&ver.method(:update))
|
|
416
|
+
DELUXE_VERSIONS.map(&:upcase).flat_map do |k|
|
|
417
|
+
[k, "#{k}_PLUS"]
|
|
418
|
+
end.map(&:to_sym).each_with_index.map do |k, i|
|
|
419
|
+
[k, 200 + i * 5]
|
|
420
|
+
end.to_h.tap(&ver.method(:update))
|
|
421
|
+
end
|
|
422
|
+
ORIGINAL = VERSIONS.select do |k, v| v < 200 end
|
|
423
|
+
DELUXE = VERSIONS.select do |k, v| v >= 200 end.transform_values do |v| v - 100 end
|
|
424
|
+
LIBRARY = VERSIONS
|
|
425
|
+
DELUXE_WEBSITE = LIBRARY.keys.each_with_index.to_h
|
|
426
|
+
|
|
427
|
+
include ModuleExt
|
|
428
|
+
|
|
429
|
+
def initialize(key)
|
|
430
|
+
@key = key
|
|
431
|
+
|
|
432
|
+
@id = LIBRARY[key]
|
|
433
|
+
@original_id = ORIGINAL[key]
|
|
434
|
+
@deluxe_id = DELUXE[key]
|
|
435
|
+
@deluxe_web_id = DELUXE_WEBSITE[key]
|
|
436
|
+
|
|
437
|
+
freeze
|
|
438
|
+
end
|
|
439
|
+
|
|
440
|
+
attr_reader :key
|
|
441
|
+
attr_reader :id, :original_id, :deluxe_id, :deluxe_web_id
|
|
442
|
+
|
|
443
|
+
alias to_i id
|
|
444
|
+
alias to_sym key
|
|
445
|
+
|
|
446
|
+
extend AutoConstant
|
|
447
|
+
define_new key_enforce: :upcase
|
|
448
|
+
populate_entries :LIBRARY
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
class BestScoreSortType
|
|
452
|
+
LIBRARY = %i(
|
|
453
|
+
achievement_high
|
|
454
|
+
achievement_low
|
|
455
|
+
deluxe_high
|
|
456
|
+
deluxe_low
|
|
457
|
+
combo_rank_high
|
|
458
|
+
combo_rank_low
|
|
459
|
+
).each_with_index.to_h
|
|
460
|
+
.transform_values(&:succ)
|
|
461
|
+
|
|
462
|
+
def initialize(key)
|
|
463
|
+
@key = key
|
|
464
|
+
|
|
465
|
+
@id = LIBRARY[key]
|
|
466
|
+
@deluxe_web_id = LIBRARY[key]
|
|
467
|
+
|
|
468
|
+
freeze
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
attr_reader :key
|
|
472
|
+
attr_reader :id, :deluxe_web_id
|
|
473
|
+
|
|
474
|
+
alias to_i id
|
|
475
|
+
alias to_sym key
|
|
476
|
+
|
|
477
|
+
extend AutoConstant
|
|
478
|
+
define_new key_enforce: :downcase
|
|
479
|
+
populate_entries :LIBRARY
|
|
480
|
+
end
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
include Constants
|
|
484
|
+
private_constant :Constants
|
|
485
|
+
end
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
module MaimaiNet
|
|
2
|
+
module CoreExt
|
|
3
|
+
module KernelAutoConstantInclusion
|
|
4
|
+
MaimaiNet.constants.each do |k|
|
|
5
|
+
cls = MaimaiNet.const_get(k)
|
|
6
|
+
next unless Class === cls && cls < MaimaiNet::Constant
|
|
7
|
+
|
|
8
|
+
define_method k do |key| cls.new(key) end
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
module MaimaiNet
|
|
2
|
+
module Error
|
|
3
|
+
class BaseError < StandardError
|
|
4
|
+
def maintenance?; false; end
|
|
5
|
+
end
|
|
6
|
+
class ClientError < BaseError; end
|
|
7
|
+
class ServerError < BaseError; end
|
|
8
|
+
|
|
9
|
+
class GeneralError < ServerError
|
|
10
|
+
def initialize(code)
|
|
11
|
+
super("Error #{code}")
|
|
12
|
+
@code = code
|
|
13
|
+
end
|
|
14
|
+
attr_reader :code
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
class LoginError < GeneralError; end
|
|
18
|
+
class SessionError < GeneralError; end
|
|
19
|
+
# This error is finnicky.
|
|
20
|
+
# Basically if this will happen when logging into non-home url
|
|
21
|
+
# either from previous cookie or through the provided callback.
|
|
22
|
+
class SessionRefreshError < SessionError
|
|
23
|
+
def initialize(code)
|
|
24
|
+
super
|
|
25
|
+
StandardError.instance_method(__method__).bind(self).call('Please access main page before accessing other pages.')
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
class SessionExpiredError < SessionError; end
|
|
29
|
+
|
|
30
|
+
class RequestRetry < ClientError; end
|
|
31
|
+
class RetryExhausted < ClientError; end
|
|
32
|
+
|
|
33
|
+
module Maintenance
|
|
34
|
+
def maintenance?
|
|
35
|
+
true
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
class RoutineMaintenance < ClientError
|
|
39
|
+
include Maintenance
|
|
40
|
+
def initialize(time_range)
|
|
41
|
+
start_time, end_time = time_range.begin, time_range.end
|
|
42
|
+
super("Maintenance from %s to %s." % [
|
|
43
|
+
start_time.strftime('%H:%M'),
|
|
44
|
+
end_time.strftime('%H:%M'),
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
@start_time = start_time
|
|
48
|
+
@end_time = end_time
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
attr_reader :start_time, :end_time
|
|
52
|
+
end
|
|
53
|
+
class UnderMaintenance < ServerError; include Maintenance end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
require 'faraday'
|
|
2
|
+
require 'http/cookie'
|
|
3
|
+
|
|
4
|
+
module MaimaiNet
|
|
5
|
+
module FaradayExt
|
|
6
|
+
# Slight modification from faraday-cookie_jar to follow domain redirects on response.
|
|
7
|
+
class CookieJar < Faraday::Middleware
|
|
8
|
+
def initialize(app, options = {})
|
|
9
|
+
super(app)
|
|
10
|
+
@jar = options[:jar] || HTTP::CookieJar.new
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def call(env)
|
|
14
|
+
cookies = @jar.cookies(env[:url])
|
|
15
|
+
unless cookies.empty?
|
|
16
|
+
cookie_header = {}
|
|
17
|
+
# assign them to dummy cookie to make it compatible
|
|
18
|
+
HTTP::Cookie.parse(env[:request_headers]["Cookie"], env[:url]).each do |cookie|
|
|
19
|
+
cookie_header[cookie.name] = cookie.cookie_value
|
|
20
|
+
end if env[:request_headers]['Cookie']
|
|
21
|
+
|
|
22
|
+
cookies.each do |cookie| cookie_header[cookie.name] = cookie.cookie_value end
|
|
23
|
+
env[:request_headers]["Cookie"] = HTTP::Cookie.cookie_value(cookie_header.values)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
@app.call(env).on_complete do |res|
|
|
27
|
+
if set_cookie = res[:response_headers]["Set-Cookie"]
|
|
28
|
+
@jar.parse(set_cookie, res[:url])
|
|
29
|
+
end if res[:response_headers]
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
Faraday::Middleware.tap do |m|
|
|
35
|
+
m.register_middleware cookie_jar: CookieJar
|
|
36
|
+
end if Faraday::Middleware.respond_to? :register_middleware
|
|
37
|
+
end
|
|
38
|
+
end
|