maimai_net 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a9649c531c49ff62f1391875bcce1cf77c46b93e563c673afe188a688066f2d7
4
- data.tar.gz: 60436b79e63a417b1a11dd636d988c4c48a5ce7fd7ccf6b469754399ac2a322f
3
+ metadata.gz: 1536775b0efe6486ed28430da4fdeb186acf36aa70a18f3b3e67ad14b8f6db79
4
+ data.tar.gz: f6dae985de61be79aa0a249bb3b4bddfd174a2398ae23a9441528d0a4a833afe
5
5
  SHA512:
6
- metadata.gz: d084e5b1969ee15a674c717415faa9eb358ac929e14d4e2c29e59bac212d81d0882382a436a5adf46384dcc2294bc31e0ac18fcf7c603169e8df8f97658c1c44
7
- data.tar.gz: 2043fa38bacd2f093e0ba60bbe2d45c036c1bfc50c1d9aac2f7aa385dbd219ca4ea7ee0dd77f3e14fa89c3d3d2c5af970044539d38970a14331563ad6c4f5684
6
+ metadata.gz: a7d0544158ff4426a591cd89b12d5a8436c654732c07ea2b013691d15c04277639e1db624ea5c1a059a103c039e402d9a30ad2a0532d65fadb88a4a1a7a01f01
7
+ data.tar.gz: d1da766e1d1bf3095b811fd2f35ccd51e31c4cc700b67082043eb4f9528a49e173f7184539d6ef4763ba6b2864a7fcadeafafbdfb7a6efe0f662b46507c7488b
@@ -61,12 +61,16 @@ module MaimaiNet
61
61
  end
62
62
 
63
63
  class Connection
64
+ extend Forwardable
65
+
64
66
  # @param client [Base] client data
65
67
  def initialize(client)
66
68
  @client = client
67
69
  @conn = nil
68
70
  end
69
71
 
72
+ def_delegator :@client, :cookies
73
+
70
74
  # automatically private hook methods
71
75
  # @return [void]
72
76
  def self.method_added(meth)
@@ -314,7 +318,7 @@ module MaimaiNet
314
318
  # @param opts [Hash{Symbol => Object}]
315
319
  # @option response_page [Class<Page::Base>] response parser class
316
320
  # @option response [#call] response method, takes one argument, response body raw.
317
- # @return [Model::Base::Struct] returns page data based from provided response_page field
321
+ # @return [Model::Base] returns page data based from provided response_page field
318
322
  # @return [void]
319
323
  def send_request(method, url, data, **opts)
320
324
  fail NotImplementedError, 'abstract method called' if Connection == method(__method__).owner
@@ -358,7 +362,7 @@ module MaimaiNet
358
362
  # @!api private
359
363
  # @param url [URI] response url
360
364
  # @param body [String] response body
361
- # @return [Model::Base::Struct, Array<Model::Base::Struct>] response page handled result
365
+ # @return [Model::Base, Array<Model::Base>] response page handled result
362
366
  # @return [nil] no response page defined to handle the response
363
367
  def process_response(url:, body:, request_options:)
364
368
  info = @client.class.region_info
@@ -1,11 +1,12 @@
1
1
  module MaimaiNet
2
2
  module CoreExt
3
- module KernelAutoConstantInclusion
3
+ module AutoConstantInclusion
4
4
  MaimaiNet.constants.each do |k|
5
5
  cls = MaimaiNet.const_get(k)
6
6
  next unless Class === cls && cls < MaimaiNet::Constant
7
7
 
8
8
  define_method k do |key| cls.new(key) end
9
+ private k
9
10
  end
10
11
  end
11
12
  end
@@ -3,31 +3,30 @@ module MaimaiNet
3
3
  module Model
4
4
  require 'maimai_net/model-typing'
5
5
 
6
- module Base
7
- class Struct < ::Struct
8
- using GenericComparison
9
- # @param kwargs [Hash] options are strong-typed based on class definition
10
- def initialize(**kwargs)
11
- props = self.class.instance_variable_get(:@_properties)
12
- keys = props.keys
13
- optional_keys = props.select do |k, pr|
14
- Either === pr[:class] &&
15
- pr[:class].variants.include?(NilClass)
16
- end.keys
17
-
18
- missing_keys = keys - (kwargs.keys | optional_keys)
19
- fail KeyError, "#{missing_keys.join(', ')} is not defined for #{self.class}" unless missing_keys.empty?
20
- kwargs.each do |key, value|
21
- fail KeyError, "#{key} is not defined as struct member" unless keys.include?(key)
22
- class_str = value.respond_to?(:map_class) ? value.map_class : value.class
23
- fail TypeError, "#{key} type mismatch, given #{class_str}, expected #{props[key][:class]}" unless props[key][:class] === value
24
- end
25
-
26
- args = kwargs.values_at(*keys)
27
- super(*args)
6
+ class Base < ::Struct
7
+ using GenericComparison
8
+ # @param kwargs [Hash] options are strong-typed based on class definition
9
+ def initialize(**kwargs)
10
+ props = self.class.instance_variable_get(:@_properties)
11
+ keys = props.keys
12
+ optional_keys = props.select do |k, pr|
13
+ Either === pr[:class] &&
14
+ pr[:class].variants.include?(NilClass)
15
+ end.keys
16
+
17
+ missing_keys = keys - (kwargs.keys | optional_keys)
18
+ fail KeyError, "#{missing_keys.join(', ')} is not defined for #{self.class}" unless missing_keys.empty?
19
+ kwargs.each do |key, value|
20
+ fail KeyError, "#{key} is not defined as struct member" unless keys.include?(key)
21
+ class_str = value.respond_to?(:map_class) ? value.map_class : value.class
22
+ fail TypeError, "#{key} type mismatch, given #{class_str}, expected #{props[key][:class]}" unless props[key][:class] === value
28
23
  end
24
+
25
+ args = kwargs.values_at(*keys)
26
+ super(*args)
29
27
  end
30
- class << Struct
28
+
29
+ class << self
31
30
  # creates a strong-typed struct data
32
31
  # @param opts [Hash{Symbol => Module}]
33
32
  # list of struct members along with respective type definition
@@ -52,7 +51,7 @@ module MaimaiNet
52
51
  end
53
52
  end
54
53
 
55
- SongCount = Base::Struct.new(achieved: Integer, total: Integer) do
54
+ SongCount = Base.new(achieved: Integer, total: Integer) do
56
55
  def to_s
57
56
  "#{achieved}/#{total}"
58
57
  end
@@ -60,7 +59,7 @@ module MaimaiNet
60
59
  end
61
60
 
62
61
  module PlayerCommon
63
- Info = Base::Struct.new(
62
+ Info = Base.new(
64
63
  name: String,
65
64
  title: String,
66
65
  grade: String,
@@ -68,16 +67,16 @@ module MaimaiNet
68
67
  end
69
68
 
70
69
  module PlayerData
71
- Decoration = Base::Struct.new(
70
+ Decoration = Base.new(
72
71
  icon: URI::Generic,
73
72
  )
74
- ExtendedInfo = Base::Struct.new(
73
+ ExtendedInfo = Base.new(
75
74
  rating: Integer,
76
75
  class_grade: String,
77
76
  partner_star_total: Integer,
78
77
  )
79
78
 
80
- DifficultyStatistic = Base::Struct.new(
79
+ DifficultyStatistic = Base.new(
81
80
  clears: SongCount,
82
81
  ranks: Generic[Hash, Symbol, SongCount],
83
82
  dx_ranks: Generic[Hash, Integer, SongCount],
@@ -85,22 +84,22 @@ module MaimaiNet
85
84
  sync_flags: Generic[Hash, Symbol, SongCount],
86
85
  )
87
86
 
88
- InfoPlate = Base::Struct.new(
87
+ InfoPlate = Base.new(
89
88
  info: PlayerCommon::Info,
90
89
  decoration: Decoration,
91
90
  extended: ExtendedInfo,
92
91
  )
93
- Lite = Base::Struct.new(
92
+ Lite = Base.new(
94
93
  name: String,
95
94
  rating: Integer,
96
95
  )
97
- Data = Base::Struct.new(
96
+ Data = Base.new(
98
97
  plate: InfoPlate,
99
98
  statistics: Generic[Hash, Symbol, DifficultyStatistic],
100
99
  )
101
100
  end
102
101
 
103
- WebID = Base::Struct.new(
102
+ WebID = Base.new(
104
103
  item_hash: String,
105
104
  item_key: String,
106
105
  ) do
@@ -131,9 +130,11 @@ module MaimaiNet
131
130
  title: String,
132
131
  type: String,
133
132
  difficulty: Integer,
133
+ variant: Optional[String],
134
+ flags: Optional[Integer],
134
135
  }
135
136
 
136
- InfoLite = Base::Struct.new(**info_base) do
137
+ InfoLite = Base.new(**info_base) do
137
138
  def to_info(level_text: '?')
138
139
  Info.new(
139
140
  web_id: WebID::DUMMY,
@@ -145,7 +146,7 @@ module MaimaiNet
145
146
  end
146
147
  end
147
148
 
148
- Info = Base::Struct.new(
149
+ Info = Base.new(
149
150
  web_id: WebID,
150
151
  **info_base,
151
152
  level_text: String,
@@ -155,7 +156,7 @@ module MaimaiNet
155
156
  end
156
157
  end
157
158
 
158
- Song = Base::Struct.new(
159
+ Song = Base.new(
159
160
  title: String,
160
161
  artist: String,
161
162
  genre: String,
@@ -163,18 +164,18 @@ module MaimaiNet
163
164
  )
164
165
  end
165
166
 
166
- SongEntry = Base::Struct.new(
167
+ SongEntry = Base.new(
167
168
  web_id: WebID,
168
169
  title: String,
169
170
  genre: String,
170
171
  )
171
172
 
172
- SongFavoriteInfo = Base::Struct.new(
173
+ SongFavoriteInfo = Base.new(
173
174
  song: SongEntry,
174
175
  flag: Boolean,
175
176
  )
176
177
 
177
- PhotoUpload = Base::Struct.new(
178
+ PhotoUpload = Base.new(
178
179
  info: Chart::InfoLite,
179
180
  url: URI::Generic,
180
181
  location: String,
@@ -182,7 +183,7 @@ module MaimaiNet
182
183
  )
183
184
 
184
185
  module Result
185
- Progress = Base::Struct.new(
186
+ Progress = Base.new(
186
187
  value: Integer,
187
188
  max: Integer,
188
189
  ) do
@@ -191,18 +192,23 @@ module MaimaiNet
191
192
  alias inspect to_s
192
193
  end
193
194
 
194
- RivalInfo = Base::Struct.new(
195
+ RivalInfo = Base.new(
195
196
  player: PlayerData::Lite,
196
197
  score: Float,
197
198
  )
198
199
 
199
- TourMember = Base::Struct.new(
200
+ PlayerInfo = Base.new(
201
+ player_name: String,
202
+ difficulty: Integer,
203
+ )
204
+
205
+ TourMember = Base.new(
200
206
  icon: URI::Generic,
201
207
  grade: Integer,
202
208
  level: Integer,
203
209
  )
204
210
 
205
- Judgment = Base::Struct.new(
211
+ Judgment = Base.new(
206
212
  just: Integer,
207
213
  perfect: Integer,
208
214
  great: Integer,
@@ -210,33 +216,35 @@ module MaimaiNet
210
216
  miss: Integer,
211
217
  )
212
218
 
213
- Offset = Base::Struct.new(
219
+ Offset = Base.new(
214
220
  early: Integer,
215
221
  late: Integer,
216
222
  )
217
223
 
218
- Challenge = Base::Struct.new(
224
+ Challenge = Base.new(
219
225
  type: Symbol,
220
226
  lives: Progress,
221
227
  )
222
228
 
223
- ScoreLite = Base::Struct.new(
229
+ ScoreLite = Base.new(
224
230
  score: Float,
225
231
  deluxe_score: Progress,
226
232
  grade: Symbol,
227
233
  flags: Generic[Array, Symbol],
234
+ position: Optional[Integer],
228
235
  )
229
236
 
230
- Score = Base::Struct.new(
237
+ Score = Base.new(
231
238
  score: Float,
232
239
  deluxe_score: Progress,
233
240
  combo: Progress,
234
241
  sync_score: Progress,
235
242
  grade: Symbol,
236
243
  flags: Generic[Array, Symbol],
244
+ position: Optional[Integer],
237
245
  )
238
246
 
239
- ReferenceWebID = Base::Struct.new(
247
+ ReferenceWebID = Base.new(
240
248
  order: Integer,
241
249
  time: Time,
242
250
  ) do
@@ -251,7 +259,7 @@ module MaimaiNet
251
259
  alias to_s to_str
252
260
  end
253
261
 
254
- Track = Base::Struct.new(
262
+ Track = Base.new(
255
263
  info: Chart::Info,
256
264
  score: Either[Score, ScoreLite],
257
265
  order: Integer,
@@ -259,32 +267,33 @@ module MaimaiNet
259
267
  challenge: Optional[Challenge],
260
268
  )
261
269
 
262
- TrackReference = Base::Struct.new(
270
+ TrackReference = Base.new(
263
271
  track: Track,
264
272
  ref_web_id: ReferenceWebID,
265
273
  )
266
274
 
267
- Data = Base::Struct.new(
275
+ Data = Base.new(
268
276
  track: Track,
269
277
  breakdown: Generic[Hash, Symbol, Judgment],
270
278
  timing: Offset,
271
279
  members: Generic[Array, TourMember],
272
280
  rival: Optional[RivalInfo],
281
+ players: Generic[Array, PlayerInfo],
273
282
  )
274
283
  end
275
284
 
276
285
  module Record
277
- History = Base::Struct.new(
286
+ History = Base.new(
278
287
  play_count: Integer,
279
288
  last_played: Time,
280
289
  )
281
290
 
282
- ScoreOnly = Base::Struct.new(
291
+ ScoreOnly = Base.new(
283
292
  score: Float,
284
293
  grade: Symbol,
285
294
  )
286
295
 
287
- Score = Base::Struct.new(
296
+ Score = Base.new(
288
297
  web_id: WebID,
289
298
  score: Float,
290
299
  deluxe_score: Result::Progress,
@@ -293,50 +302,50 @@ module MaimaiNet
293
302
  flags: Generic[Array, Symbol],
294
303
  )
295
304
 
296
- ChartRecord = Base::Struct.new(
305
+ ChartRecord = Base.new(
297
306
  info: Chart::Info,
298
307
  record: Optional[Score],
299
308
  history: Optional[History],
300
309
  )
301
310
 
302
- InfoCategory = Base::Struct.new(
311
+ InfoCategory = Base.new(
303
312
  info: Chart::Info,
304
313
  score: Optional[Result::ScoreLite],
305
314
  )
306
315
 
307
- InfoBest = Base::Struct.new(
316
+ InfoBest = Base.new(
308
317
  info: Chart::Info,
309
318
  play_count: Integer,
310
319
  )
311
320
 
312
- InfoRating = Base::Struct.new(
321
+ InfoRating = Base.new(
313
322
  info: Chart::Info,
314
323
  score: ScoreOnly,
315
324
  )
316
325
 
317
- Data = Base::Struct.new(
326
+ Data = Base.new(
318
327
  info: Chart::Song,
319
328
  charts: Generic[Hash, Symbol, ChartRecord],
320
329
  )
321
330
  end
322
331
 
323
332
  module FinaleArchive
324
- Decoration = Base::Struct.new(
333
+ Decoration = Base.new(
325
334
  icon: URI::Generic,
326
335
  player_frame: URI::Generic,
327
336
  nameplate: URI::Generic,
328
337
  )
329
- Currency = Base::Struct.new(
338
+ Currency = Base.new(
330
339
  amount: Integer, piece: Integer, parts: Integer,
331
340
  )
332
- ExtendedInfo = Base::Struct.new(
341
+ ExtendedInfo = Base.new(
333
342
  rating: Float, rating_highest: Float,
334
343
  region_count: Integer,
335
344
  currency: Currency,
336
345
  partner_level_total: Integer,
337
346
  )
338
347
 
339
- DifficultyStatistic = Base::Struct.new(
348
+ DifficultyStatistic = Base.new(
340
349
  total_score: Integer,
341
350
  clears: SongCount,
342
351
  ranks: Generic[Hash, Symbol, SongCount],
@@ -345,7 +354,7 @@ module MaimaiNet
345
354
  multi_flags: Generic[Hash, Symbol, SongCount],
346
355
  )
347
356
 
348
- Data = Base::Struct.new(
357
+ Data = Base.new(
349
358
  info: PlayerCommon::Info,
350
359
  decoration: Decoration,
351
360
  extended: ExtendedInfo,
@@ -10,7 +10,12 @@ module MaimaiNet
10
10
  )
11
11
  HelperBlock.send(:new, nil).instance_exec do
12
12
  header_block = elm.at_css('.playlog_top_container')
13
- difficulty = ::Kernel.Difficulty(::Kernel.Pathname(src(header_block.at_css('img.playlog_diff'))).sub_ext('').sub(/.+_/, '').basename)
13
+ difficulty = Difficulty(::Kernel.Pathname(src(header_block.at_css('img.playlog_diff'))).sub_ext('').sub(/.+_/, '').basename)
14
+ utage_variant = header_block.at_css('.playlog_music_kind_icon_utage').yield_self do |elm|
15
+ next if elm.nil?
16
+
17
+ strip(elm)
18
+ end
14
19
 
15
20
  dx_container_classes = MaimaiNet::Difficulty::DELUXE.select do |k, v| v.positive? end
16
21
  .keys.map do |k| ".playlog_#{k}_container" end
@@ -27,9 +32,10 @@ module MaimaiNet
27
32
  song_name = strip(chart_header_block.children.last)
28
33
  chart_level = strip(chart_header_block.at_css('div:nth-of-type(1)'))
29
34
  song_jacket = src(result_block.at_css('img.music_img'))
30
- chart_type = nil
31
- result_block.at_css('img.playlog_music_kind_icon')&.tap do |elm|
32
- chart_type = ::Kernel.Pathname(src(elm))&.sub_ext('')&.sub(/.+_/, '')&.basename&.to_s
35
+ chart_type = result_block.at_css('img.playlog_music_kind_icon').yield_self do |elm|
36
+ next if elm.nil?
37
+
38
+ ::Kernel.Pathname(src(elm))&.sub_ext('')&.sub(/.+_/, '')&.basename&.to_s
33
39
  end
34
40
 
35
41
  result_score = strip(result_block.at_css('.playlog_achievement_txt')).to_f
@@ -38,10 +44,13 @@ module MaimaiNet
38
44
  result_flags = result_block.css('.playlog_result_innerblock > img').map do |elm|
39
45
  flag = ::Kernel.Pathname(::Kernel.URI(src(elm)).path).sub_ext('')&.basename.to_s
40
46
  case flag
41
- when *MaimaiNet::AchievementFlag::RESULT.values; MaimaiNet::AchievementFlag.new(result_key: flag)
47
+ when *MaimaiNet::AchievementFlag::RESULT.values; AchievementFlag(result_key: flag)
42
48
  when /_dummy$/; nil
43
49
  end
44
50
  end.compact
51
+ result_position = result_block.at_css('.playlog_result_innerblock img.playlog_matching_icon')&.yield_self do |elm|
52
+ /^\d+/.match(::Kernel.Pathname(::Kernel.URI(src(elm)).path).sub_ext('')&.basename.to_s)[0].to_i
53
+ end
45
54
 
46
55
  challenge_info = nil
47
56
  result_block.at_css('div:has(> .playlog_life_block)')&.tap do |elm|
@@ -63,6 +72,7 @@ module MaimaiNet
63
72
  end.to_h,
64
73
  grade: result_grade,
65
74
  flags: result_flags.map(&:to_sym),
75
+ position: result_position,
66
76
  }
67
77
 
68
78
  score_cls = score_data.key?(:combo) && score_data.key?(:sync_score) ?
@@ -76,6 +86,7 @@ module MaimaiNet
76
86
  title: song_name,
77
87
  type: (chart_type or -'unknown'),
78
88
  difficulty: difficulty.id,
89
+ variant: utage_variant,
79
90
  level_text: chart_level,
80
91
  ),
81
92
  score: score_info,
@@ -265,6 +265,7 @@ module MaimaiNet
265
265
 
266
266
  start_anchor = @root.at_css('img.title')
267
267
  @otomodachi_block = start_anchor.at_css('~ div#vsUser > :first-child')
268
+ @multiplayer_block = start_anchor.at_css('~ div#matching')
268
269
  @score_block = start_anchor.at_css('~ div:not(#vsUser):nth-of-type(1)')
269
270
  @breakdown_block = start_anchor.at_css('~ div:not(#vsUser):nth-of-type(2)')
270
271
  end
@@ -310,6 +311,16 @@ module MaimaiNet
310
311
  )
311
312
  end
312
313
 
314
+ result_players = @multiplayer_block&.css(':has(img[src*="/diff"])').to_a.map do |elm|
315
+ difficulty = Difficulty(Pathname(src(elm.at_css('> img:nth-of-type(1)'))).sub_ext('').sub(/.+_/, '').basename)
316
+ name = strip(elm.at_css('> div.basic_block:nth-of-type(1)'))
317
+
318
+ Model::Result::PlayerInfo.new(
319
+ player_name: name,
320
+ difficulty: difficulty.id,
321
+ )
322
+ end
323
+
313
324
  chart_web_id = Model::WebID.parse(@root.at_css('form[action$="/record/musicDetail/"] input[name=idx]')['value'])
314
325
 
315
326
  Model::Result::Data.new(
@@ -323,6 +334,7 @@ module MaimaiNet
323
334
  timing: Model::Result::Offset.new(**Model::Result::Offset.members.zip(result_offset_breakdown).to_h),
324
335
  members: result_tour_members,
325
336
  rival: result_otomodachi_rival,
337
+ players: result_players,
326
338
  )
327
339
  end
328
340
  end
@@ -350,24 +362,51 @@ module MaimaiNet
350
362
  if track_group_blocks.empty? then
351
363
  track_segmented_blocks[current_group] = @root.css('div:has(> form[action$="/musicDetail/"] input[name=idx])')
352
364
  else
353
- @root.css('.see_through_block ~ div').each do |elm|
354
- if elm.classes.include? 'screw_block' then
355
- current_group = elm.content
356
- next
365
+ %w(.see_through_block .scroll_point).each do |anchor_class|
366
+ anchor_elm = @root.at_css(anchor_class)
367
+ next if anchor_elm.nil?
368
+
369
+ anchor_elm.css('~ div').each do |elm|
370
+ if elm.classes.include? 'screw_block' then
371
+ current_group = elm.content
372
+ next
373
+ end
374
+
375
+ track_segmented_blocks[current_group] ||= Nokogiri::XML::NodeSet.new(@document)
376
+ track_segmented_blocks[current_group] << elm
357
377
  end
358
378
 
359
- track_segmented_blocks[current_group] ||= Nokogiri::XML::NodeSet.new(@document)
360
- track_segmented_blocks[current_group] << elm
379
+ break
361
380
  end
362
381
  end
363
382
 
364
383
  result = track_segmented_blocks.transform_values do |elm_group|
365
384
  elm_group.map do |elm|
385
+ chart_info = {}
386
+ chart_info[:flags] = 0
387
+
388
+ chart_info[:type] = elm.at_css('.music_kind_icon').yield_self do |_elm|
389
+ next if _elm.nil?
390
+
391
+ Pathname(URI(src(_elm)).path).sub_ext('').sub(/.+_/, '').basename.to_s
392
+ end.yield_self do |type|
393
+ type || -'unknown'
394
+ end
395
+
396
+ nil.tap do
397
+ next if elm.at_css('.music_kind_icon_utage').nil?
398
+
399
+ chart_info[:variant] = strip(elm.at_css('.music_kind_icon_utage_text:nth-of-type(1)'))
400
+ chart_info[:flags] |= elm.at_css('.music_kind_icon_utage:has(img[src*=music_utage_buddy])').nil? ? 0 : 1
401
+ end
402
+
366
403
  chart_info = Model::Chart::Info.new(
367
404
  web_id: Model::WebID.parse(elm.at_css('input[name=idx][type=hidden]')['value']),
368
405
  title: elm.at_css('.music_name_block').content,
369
- type: Pathname(URI(src(elm.at_css('.music_kind_icon'))).path).sub_ext('').sub(/.+_/, '').basename.to_s,
406
+ type: chart_info.fetch(:type, -'unknown'),
370
407
  difficulty: Difficulty(Pathname(URI(src(elm.at_css('form > img:nth-of-type(1)'))).path).sub_ext('').sub(/.+_/, '').basename.to_s).id,
408
+ variant: chart_info.fetch(:variant, nil),
409
+ flags: chart_info[:flags],
371
410
  level_text: elm.at_css('.music_lv_block').content,
372
411
  )
373
412
 
@@ -2,7 +2,10 @@ module MaimaiNet
2
2
  # includes AutoConstants into invokable class
3
3
  module IncludeAutoConstant
4
4
  refine Kernel do
5
- include CoreExt::KernelAutoConstantInclusion
5
+ include CoreExt::AutoConstantInclusion
6
+ end
7
+ refine BasicObject do
8
+ include CoreExt::AutoConstantInclusion
6
9
  end
7
10
  end
8
11
 
@@ -1,3 +1,3 @@
1
1
  module MaimaiNet
2
- VERSION = -'0.0.1'
2
+ VERSION = -'0.0.2'
3
3
  end
data/lib/maimai_net.rb CHANGED
@@ -1,3 +1,5 @@
1
+ require 'forwardable'
2
+
1
3
  module MaimaiNet
2
4
  require 'maimai_net/version'
3
5
  require 'maimai_net/error'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: maimai_net
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rei Hakurei
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-19 00:00:00.000000000 Z
11
+ date: 2025-10-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rake
@@ -115,15 +115,8 @@ executables: []
115
115
  extensions: []
116
116
  extra_rdoc_files: []
117
117
  files:
118
- - ".github/workflows/gem.yml"
119
- - ".gitignore"
120
- - ".rspec"
121
- - Gemfile
122
118
  - LICENSE
123
119
  - README.md
124
- - Rakefile
125
- - bin/console
126
- - bin/setup
127
120
  - lib/maimai_net.rb
128
121
  - lib/maimai_net/client.rb
129
122
  - lib/maimai_net/constants.rb
@@ -143,7 +136,6 @@ files:
143
136
  - lib/maimai_net/user_option.rb
144
137
  - lib/maimai_net/util.rb
145
138
  - lib/maimai_net/version.rb
146
- - maimai-net.gemspec
147
139
  homepage: https://github.com/ReiFan49/rb.maimai-net
148
140
  licenses:
149
141
  - BSD-3-Clause-Clear
@@ -165,7 +157,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
165
157
  - !ruby/object:Gem::Version
166
158
  version: '0'
167
159
  requirements: []
168
- rubygems_version: 3.1.6
160
+ rubygems_version: 3.2.33
169
161
  signing_key:
170
162
  specification_version: 4
171
163
  summary: Parses maimai-net into readable data.
@@ -1,53 +0,0 @@
1
- name: Ruby Gem
2
-
3
- on:
4
- push:
5
- tags:
6
- - v*
7
-
8
- concurrency:
9
- group: ${{ github.ref_name }}
10
- cancel-in-progress: true
11
-
12
- jobs:
13
- build:
14
- name: Build + Publish
15
- runs-on: ubuntu-latest
16
- permissions:
17
- contents: read
18
- packages: write
19
- id-token: write
20
- env:
21
- RUBY_VERSION: '2.7'
22
- steps:
23
- - uses: actions/checkout@v4
24
- - name: Set up Ruby ${{ env.RUBY_VERSION }}
25
- uses: ruby/setup-ruby@v1
26
- with:
27
- bundler-cache: true
28
- ruby-version: ${{ env.RUBY_VERSION }}
29
- # Publish
30
- - name: Publish to GPR
31
- run: |
32
- mkdir -p $HOME/.gem
33
- touch $HOME/.gem/credentials
34
- chmod 0600 $HOME/.gem/credentials
35
- printf -- "---\n:github: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
36
- gem build *.gemspec
37
- gem push --KEY github --host https://rubygems.pkg.github.com/${OWNER} *.gem
38
- env:
39
- GEM_HOST_API_KEY: "Bearer ${{secrets.GITHUB_TOKEN}}"
40
- OWNER: ${{ github.repository_owner }}
41
- - name: Publish to RubyGems
42
- if: ${{ false }}
43
- run: |
44
- mkdir -p $HOME/.gem
45
- touch $HOME/.gem/credentials
46
- chmod 0600 $HOME/.gem/credentials
47
- printf -- "---\n:rubygems_api_key: ${GEM_HOST_API_KEY}\n" > $HOME/.gem/credentials
48
- gem build *.gemspec
49
- gem push *.gem
50
- env:
51
- GEM_HOST_API_KEY: "${{secrets.RUBYGEMS_AUTH_TOKEN}}"
52
- - name: Publish to RubyGems (Trusted)
53
- uses: rubygems/release-gem@v1
data/.gitignore DELETED
@@ -1,165 +0,0 @@
1
- # Created by https://www.toptal.com/developers/gitignore/api/ruby,rubymine+all,linux
2
- # Edit at https://www.toptal.com/developers/gitignore?templates=ruby,rubymine+all,linux
3
-
4
- ### Linux ###
5
- *~
6
- .*.swp
7
-
8
- # temporary files which can be created if a process still has a handle open of a deleted file
9
- .fuse_hidden*
10
-
11
- # KDE directory preferences
12
- .directory
13
-
14
- # Linux trash folder which might appear on any partition or disk
15
- .Trash-*
16
-
17
- # .nfs files are created when an open file is removed but is still being accessed
18
- .nfs*
19
-
20
- ### Ruby ###
21
- *.gem
22
- *.rbc
23
- /.config
24
- /coverage/
25
- /InstalledFiles
26
- /pkg/
27
- /spec/reports/
28
- /spec/examples.txt
29
- /test/tmp/
30
- /test/version_tmp/
31
- /tmp/
32
-
33
- # Used by dotenv library to load environment variables.
34
- # .env
35
-
36
- # Ignore Byebug command history file.
37
- .byebug_history
38
-
39
- ## Specific to RubyMotion:
40
- .dat*
41
- .repl_history
42
- build/
43
- *.bridgesupport
44
- build-iPhoneOS/
45
- build-iPhoneSimulator/
46
-
47
- ## Specific to RubyMotion (use of CocoaPods):
48
- #
49
- # We recommend against adding the Pods directory to your .gitignore. However
50
- # you should judge for yourself, the pros and cons are mentioned at:
51
- # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
52
- # vendor/Pods/
53
-
54
- ## Documentation cache and generated files:
55
- /.yardoc/
56
- /_yardoc/
57
- /doc/
58
- /rdoc/
59
-
60
- ## Environment normalization:
61
- /.bundle/
62
- /vendor/bundle
63
- /lib/bundler/man/
64
-
65
- # for a library or gem, you might want to ignore these files since the code is
66
- # intended to run in multiple environments; otherwise, check them in:
67
- Gemfile.lock
68
- .ruby-version
69
- .ruby-gemset
70
-
71
- # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
72
- .rvmrc
73
-
74
- # Used by RuboCop. Remote config files pulled in from inherit_from directive.
75
- # .rubocop-https?--*
76
-
77
- ### RubyMine+all ###
78
- # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio, WebStorm and Rider
79
- # Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
80
-
81
- # User-specific stuff
82
- .idea/**/workspace.xml
83
- .idea/**/tasks.xml
84
- .idea/**/usage.statistics.xml
85
- .idea/**/dictionaries
86
- .idea/**/shelf
87
-
88
- # AWS User-specific
89
- .idea/**/aws.xml
90
-
91
- # Generated files
92
- .idea/**/contentModel.xml
93
-
94
- # Sensitive or high-churn files
95
- .idea/**/dataSources/
96
- .idea/**/dataSources.ids
97
- .idea/**/dataSources.local.xml
98
- .idea/**/sqlDataSources.xml
99
- .idea/**/dynamic.xml
100
- .idea/**/uiDesigner.xml
101
- .idea/**/dbnavigator.xml
102
-
103
- # Gradle
104
- .idea/**/gradle.xml
105
- .idea/**/libraries
106
-
107
- # Gradle and Maven with auto-import
108
- # When using Gradle or Maven with auto-import, you should exclude module files,
109
- # since they will be recreated, and may cause churn. Uncomment if using
110
- # auto-import.
111
- # .idea/artifacts
112
- # .idea/compiler.xml
113
- # .idea/jarRepositories.xml
114
- # .idea/modules.xml
115
- # .idea/*.iml
116
- # .idea/modules
117
- # *.iml
118
- # *.ipr
119
-
120
- # CMake
121
- cmake-build-*/
122
-
123
- # Mongo Explorer plugin
124
- .idea/**/mongoSettings.xml
125
-
126
- # File-based project format
127
- *.iws
128
-
129
- # IntelliJ
130
- out/
131
-
132
- # mpeltonen/sbt-idea plugin
133
- .idea_modules/
134
-
135
- # JIRA plugin
136
- atlassian-ide-plugin.xml
137
-
138
- # Cursive Clojure plugin
139
- .idea/replstate.xml
140
-
141
- # SonarLint plugin
142
- .idea/sonarlint/
143
-
144
- # Crashlytics plugin (for Android Studio and IntelliJ)
145
- com_crashlytics_export_strings.xml
146
- crashlytics.properties
147
- crashlytics-build.properties
148
- fabric.properties
149
-
150
- # Editor-based Rest Client
151
- .idea/httpRequests
152
-
153
- # Android studio 3.1+ serialized cache file
154
- .idea/caches/build_file_checksums.ser
155
-
156
- ### RubyMine+all Patch ###
157
- # Ignore everything but code style settings and run configurations
158
- # that are supposed to be shared within teams.
159
-
160
- .idea/*
161
-
162
- !.idea/codeStyles
163
- !.idea/runConfigurations
164
-
165
- # End of https://www.toptal.com/developers/gitignore/api/ruby,rubymine+all,linux
data/.rspec DELETED
@@ -1,5 +0,0 @@
1
- --format documentation
2
- --color
3
- --require spec_helper
4
- --tag ~planned
5
- --tag ~priority:low
data/Gemfile DELETED
@@ -1,4 +0,0 @@
1
- source "https://rubygems.org"
2
-
3
- # Specify your gem's dependencies in maimai-net.gemspec
4
- gemspec
data/Rakefile DELETED
@@ -1,6 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec)
5
-
6
- task :default => :spec
data/bin/console DELETED
@@ -1,16 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require "bundler/setup"
4
- begin; require 'byebug'; rescue LoadError; end
5
- require "maimai_net"
6
-
7
- console = nil
8
- begin; require 'pry'
9
- rescue LoadError; require 'irb'; console = :irb
10
- else; console = :pry
11
- end
12
-
13
- case console
14
- when :pry; Pry.start
15
- else; IRB.start(__FILE__)
16
- end
data/bin/setup DELETED
@@ -1,6 +0,0 @@
1
- #!/usr/bin/env bash
2
- set -euo pipefail
3
- IFS=$'\n\t'
4
- set -vx
5
-
6
- bundle install
data/maimai-net.gemspec DELETED
@@ -1,44 +0,0 @@
1
- require_relative 'lib/maimai_net/version'
2
-
3
- Gem::Specification.new do |spec|
4
- spec.name = "maimai_net"
5
- spec.version = MaimaiNet::VERSION
6
- spec.authors = [
7
- %(Rei Hakurei),
8
- ]
9
- spec.email = %w(contact@bloom-juery.net)
10
-
11
- spec.summary = %q(Parses maimai-net into readable data.)
12
- spec.homepage = 'https://github.com/ReiFan49/rb.maimai-net'
13
- spec.license = 'BSD-3-Clause-Clear'
14
- spec.required_ruby_version = Gem::Requirement.new(">= 2.7.0")
15
-
16
- # spec.metadata["allowed_push_host"] = "TODO: Set to 'http://mygemserver.com'"
17
-
18
- spec.metadata["homepage_uri"] = spec.homepage
19
- spec.metadata["source_code_uri"] = spec.homepage
20
- # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
21
-
22
- # Specify which files should be added to the gem when it is released.
23
- # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
24
- begin
25
- spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
26
- `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
27
- end
28
- rescue Errno::ENOENT
29
- spec.files = Dir.glob('lib/**/*.rb', base: File.expand_path('..', __FILE__))
30
- end
31
- spec.bindir = "exe"
32
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
- spec.require_paths = ["lib"]
34
-
35
- spec.add_development_dependency 'rake'
36
- spec.add_development_dependency 'rspec', '~> 3.0'
37
- spec.add_development_dependency 'byebug'
38
-
39
- spec.add_runtime_dependency 'nokogiri', '~> 1.13'
40
- spec.add_runtime_dependency 'faraday', '~> 2.0'
41
- spec.add_runtime_dependency 'faraday-follow_redirects'
42
-
43
- spec.add_runtime_dependency 'http-cookie', '~> 1.0.0'
44
- end