maimai_net 0.0.1 → 0.0.3

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: e6a043bf61fd5bd226b1f0772ee5a31014fdea73a61f66aac3f016b2f7fa3a74
4
+ data.tar.gz: 835c2b17b0fce9fe35dc83517193e135cd1bf99d052ad9618116dc311b2fa592
5
5
  SHA512:
6
- metadata.gz: d084e5b1969ee15a674c717415faa9eb358ac929e14d4e2c29e59bac212d81d0882382a436a5adf46384dcc2294bc31e0ac18fcf7c603169e8df8f97658c1c44
7
- data.tar.gz: 2043fa38bacd2f093e0ba60bbe2d45c036c1bfc50c1d9aac2f7aa385dbd219ca4ea7ee0dd77f3e14fa89c3d3d2c5af970044539d38970a14331563ad6c4f5684
6
+ metadata.gz: c7afa65b670dfb11d8f1827381c8b3264580784871581b6238f3de84040040607e6bd728b0aac6ad50abcb45707e28a398cd45d2c8a7798b9be98b61363b5584
7
+ data.tar.gz: ea17dd808b4c8030426715535325aed6eb5d12d152e7b888726cba95d63e12d9e79d3078e46e6332f43622662ad82a23719d088e329eb18a99521ed90c77bac0
@@ -25,6 +25,12 @@ module MaimaiNet
25
25
  versions: :version,
26
26
  }.freeze
27
27
 
28
+ module ErrorCodes
29
+ LOGIN_ERROR = 100_101
30
+ SESSION_REFRESH = 200_002
31
+ SESSION_INVALID = 200_004
32
+ end
33
+
28
34
  class Base
29
35
  include ModuleExt
30
36
 
@@ -61,12 +67,16 @@ module MaimaiNet
61
67
  end
62
68
 
63
69
  class Connection
70
+ extend Forwardable
71
+
64
72
  # @param client [Base] client data
65
73
  def initialize(client)
66
74
  @client = client
67
75
  @conn = nil
68
76
  end
69
77
 
78
+ def_delegator :@client, :cookies
79
+
70
80
  # automatically private hook methods
71
81
  # @return [void]
72
82
  def self.method_added(meth)
@@ -189,10 +199,14 @@ module MaimaiNet
189
199
  fail TypeError, 'expected a valid index ID format'
190
200
  end
191
201
 
202
+ actual_time = Time.at(/^(\d+),(\d+)$/.match(id).captures[1].to_i).localtime(32400).freeze
203
+
192
204
  send_request(
193
205
  'get', '/maimai-mobile/record/playlogDetail', {idx: id},
194
206
  response_page: Page::TrackResult,
195
- )
207
+ ).tap do |track_result|
208
+ track_result.track.time = actual_time
209
+ end
196
210
  end
197
211
 
198
212
  # access recent session gameplay detailed info
@@ -278,7 +292,16 @@ module MaimaiNet
278
292
  # @return [void]
279
293
  # @raise [Error::LoginError]
280
294
  def on_login_error
281
- fail Error::LoginError, 100101
295
+ fail Error::LoginError, ErrorCodes::LOGIN_ERROR
296
+ end
297
+
298
+ # hook upon receiving login expired page,
299
+ # triggering this hook causes client cookies wiped out.
300
+ # @return [void]
301
+ # @raise [Error::SessionExpiredError]
302
+ def on_login_expired_error
303
+ @client.cookies.clear
304
+ fail Error::SessionExpiredError, ErrorCodes::SESSION_INVALID
282
305
  end
283
306
 
284
307
  # hook upon receiving generic error page
@@ -294,12 +317,12 @@ module MaimaiNet
294
317
  error_code = error_note.match(/\d+/).to_s.to_i
295
318
 
296
319
  case error_code
297
- when 100101
298
- fail Error::LoginError, error_code
299
- when 200002
320
+ when ErrorCodes::LOGIN_ERROR
321
+ on_login_error
322
+ when ErrorCodes::SESSION_REFRESH
300
323
  fail Error::SessionRefreshError, error_code
301
- when 200004
302
- fail Error::SessionExpiredError, error_code
324
+ when ErrorCodes::SESSION_INVALID
325
+ on_login_expired_error
303
326
  else
304
327
  fail Error::GeneralError, error_code
305
328
  end
@@ -314,7 +337,7 @@ module MaimaiNet
314
337
  # @param opts [Hash{Symbol => Object}]
315
338
  # @option response_page [Class<Page::Base>] response parser class
316
339
  # @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
340
+ # @return [Model::Base] returns page data based from provided response_page field
318
341
  # @return [void]
319
342
  def send_request(method, url, data, **opts)
320
343
  fail NotImplementedError, 'abstract method called' if Connection == method(__method__).owner
@@ -358,7 +381,7 @@ module MaimaiNet
358
381
  # @!api private
359
382
  # @param url [URI] response url
360
383
  # @param body [String] response body
361
- # @return [Model::Base::Struct, Array<Model::Base::Struct>] response page handled result
384
+ # @return [Model::Base, Array<Model::Base>] response page handled result
362
385
  # @return [nil] no response page defined to handle the response
363
386
  def process_response(url:, body:, request_options:)
364
387
  info = @client.class.region_info
@@ -998,7 +1021,7 @@ module MaimaiNet
998
1021
 
999
1022
  @conn = Faraday.new(url: info[:base_host]) do |builder|
1000
1023
  builder.request :url_encoded
1001
- builder.response :follow_redirects
1024
+ builder.response :follow_redirects, limit: 5
1002
1025
  builder.use :cookie_jar, jar: client.cookies
1003
1026
  end
1004
1027
  end
@@ -1,11 +1,30 @@
1
1
  module MaimaiNet
2
2
  module CoreExt
3
- module KernelAutoConstantInclusion
3
+ # contains every {Constants AutoConstant} classes turned into a function.
4
+ # used for include or prepend in a scope.
5
+ #
6
+ # also used in `refine` {IncludeAutoConstant}.
7
+ module AutoConstantInclusion
4
8
  MaimaiNet.constants.each do |k|
5
9
  cls = MaimaiNet.const_get(k)
6
10
  next unless Class === cls && cls < MaimaiNet::Constant
7
11
 
8
12
  define_method k do |key| cls.new(key) end
13
+ private k
14
+ end
15
+ end
16
+
17
+ # adds JSON conversion support through `#to_h` conversion.
18
+ module JSONSupport
19
+ def as_json(options = nil)
20
+ to_h.transform_values do |val|
21
+ val.respond_to?(:as_json) ?
22
+ val.as_json(options) : val
23
+ end
24
+ end
25
+
26
+ def to_json(options = nil)
27
+ as_json.to_json(options)
9
28
  end
10
29
  end
11
30
  end
@@ -3,31 +3,32 @@ 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
+ include CoreExt::JSONSupport
30
+
31
+ class << self
31
32
  # creates a strong-typed struct data
32
33
  # @param opts [Hash{Symbol => Module}]
33
34
  # list of struct members along with respective type definition
@@ -52,7 +53,7 @@ module MaimaiNet
52
53
  end
53
54
  end
54
55
 
55
- SongCount = Base::Struct.new(achieved: Integer, total: Integer) do
56
+ SongCount = Base.new(achieved: Integer, total: Integer) do
56
57
  def to_s
57
58
  "#{achieved}/#{total}"
58
59
  end
@@ -60,7 +61,7 @@ module MaimaiNet
60
61
  end
61
62
 
62
63
  module PlayerCommon
63
- Info = Base::Struct.new(
64
+ Info = Base.new(
64
65
  name: String,
65
66
  title: String,
66
67
  grade: String,
@@ -68,16 +69,16 @@ module MaimaiNet
68
69
  end
69
70
 
70
71
  module PlayerData
71
- Decoration = Base::Struct.new(
72
+ Decoration = Base.new(
72
73
  icon: URI::Generic,
73
74
  )
74
- ExtendedInfo = Base::Struct.new(
75
+ ExtendedInfo = Base.new(
75
76
  rating: Integer,
76
77
  class_grade: String,
77
78
  partner_star_total: Integer,
78
79
  )
79
80
 
80
- DifficultyStatistic = Base::Struct.new(
81
+ DifficultyStatistic = Base.new(
81
82
  clears: SongCount,
82
83
  ranks: Generic[Hash, Symbol, SongCount],
83
84
  dx_ranks: Generic[Hash, Integer, SongCount],
@@ -85,22 +86,22 @@ module MaimaiNet
85
86
  sync_flags: Generic[Hash, Symbol, SongCount],
86
87
  )
87
88
 
88
- InfoPlate = Base::Struct.new(
89
+ InfoPlate = Base.new(
89
90
  info: PlayerCommon::Info,
90
91
  decoration: Decoration,
91
92
  extended: ExtendedInfo,
92
93
  )
93
- Lite = Base::Struct.new(
94
+ Lite = Base.new(
94
95
  name: String,
95
96
  rating: Integer,
96
97
  )
97
- Data = Base::Struct.new(
98
+ Data = Base.new(
98
99
  plate: InfoPlate,
99
100
  statistics: Generic[Hash, Symbol, DifficultyStatistic],
100
101
  )
101
102
  end
102
103
 
103
- WebID = Base::Struct.new(
104
+ WebID = Base.new(
104
105
  item_hash: String,
105
106
  item_key: String,
106
107
  ) do
@@ -131,9 +132,11 @@ module MaimaiNet
131
132
  title: String,
132
133
  type: String,
133
134
  difficulty: Integer,
135
+ variant: Optional[String],
136
+ flags: Optional[Integer],
134
137
  }
135
138
 
136
- InfoLite = Base::Struct.new(**info_base) do
139
+ InfoLite = Base.new(**info_base) do
137
140
  def to_info(level_text: '?')
138
141
  Info.new(
139
142
  web_id: WebID::DUMMY,
@@ -145,7 +148,7 @@ module MaimaiNet
145
148
  end
146
149
  end
147
150
 
148
- Info = Base::Struct.new(
151
+ Info = Base.new(
149
152
  web_id: WebID,
150
153
  **info_base,
151
154
  level_text: String,
@@ -155,7 +158,7 @@ module MaimaiNet
155
158
  end
156
159
  end
157
160
 
158
- Song = Base::Struct.new(
161
+ Song = Base.new(
159
162
  title: String,
160
163
  artist: String,
161
164
  genre: String,
@@ -163,18 +166,18 @@ module MaimaiNet
163
166
  )
164
167
  end
165
168
 
166
- SongEntry = Base::Struct.new(
169
+ SongEntry = Base.new(
167
170
  web_id: WebID,
168
171
  title: String,
169
172
  genre: String,
170
173
  )
171
174
 
172
- SongFavoriteInfo = Base::Struct.new(
175
+ SongFavoriteInfo = Base.new(
173
176
  song: SongEntry,
174
177
  flag: Boolean,
175
178
  )
176
179
 
177
- PhotoUpload = Base::Struct.new(
180
+ PhotoUpload = Base.new(
178
181
  info: Chart::InfoLite,
179
182
  url: URI::Generic,
180
183
  location: String,
@@ -182,7 +185,7 @@ module MaimaiNet
182
185
  )
183
186
 
184
187
  module Result
185
- Progress = Base::Struct.new(
188
+ Progress = Base.new(
186
189
  value: Integer,
187
190
  max: Integer,
188
191
  ) do
@@ -191,18 +194,23 @@ module MaimaiNet
191
194
  alias inspect to_s
192
195
  end
193
196
 
194
- RivalInfo = Base::Struct.new(
197
+ RivalInfo = Base.new(
195
198
  player: PlayerData::Lite,
196
199
  score: Float,
197
200
  )
198
201
 
199
- TourMember = Base::Struct.new(
202
+ PlayerInfo = Base.new(
203
+ player_name: String,
204
+ difficulty: Integer,
205
+ )
206
+
207
+ TourMember = Base.new(
200
208
  icon: URI::Generic,
201
209
  grade: Integer,
202
210
  level: Integer,
203
211
  )
204
212
 
205
- Judgment = Base::Struct.new(
213
+ Judgment = Base.new(
206
214
  just: Integer,
207
215
  perfect: Integer,
208
216
  great: Integer,
@@ -210,33 +218,35 @@ module MaimaiNet
210
218
  miss: Integer,
211
219
  )
212
220
 
213
- Offset = Base::Struct.new(
221
+ Offset = Base.new(
214
222
  early: Integer,
215
223
  late: Integer,
216
224
  )
217
225
 
218
- Challenge = Base::Struct.new(
226
+ Challenge = Base.new(
219
227
  type: Symbol,
220
228
  lives: Progress,
221
229
  )
222
230
 
223
- ScoreLite = Base::Struct.new(
231
+ ScoreLite = Base.new(
224
232
  score: Float,
225
233
  deluxe_score: Progress,
226
234
  grade: Symbol,
227
235
  flags: Generic[Array, Symbol],
236
+ position: Optional[Integer],
228
237
  )
229
238
 
230
- Score = Base::Struct.new(
239
+ Score = Base.new(
231
240
  score: Float,
232
241
  deluxe_score: Progress,
233
242
  combo: Progress,
234
243
  sync_score: Progress,
235
244
  grade: Symbol,
236
245
  flags: Generic[Array, Symbol],
246
+ position: Optional[Integer],
237
247
  )
238
248
 
239
- ReferenceWebID = Base::Struct.new(
249
+ ReferenceWebID = Base.new(
240
250
  order: Integer,
241
251
  time: Time,
242
252
  ) do
@@ -251,7 +261,7 @@ module MaimaiNet
251
261
  alias to_s to_str
252
262
  end
253
263
 
254
- Track = Base::Struct.new(
264
+ Track = Base.new(
255
265
  info: Chart::Info,
256
266
  score: Either[Score, ScoreLite],
257
267
  order: Integer,
@@ -259,32 +269,35 @@ module MaimaiNet
259
269
  challenge: Optional[Challenge],
260
270
  )
261
271
 
262
- TrackReference = Base::Struct.new(
272
+ TrackReference = Base.new(
263
273
  track: Track,
264
274
  ref_web_id: ReferenceWebID,
265
275
  )
266
276
 
267
- Data = Base::Struct.new(
277
+ Data = Base.new(
268
278
  track: Track,
269
279
  breakdown: Generic[Hash, Symbol, Judgment],
270
280
  timing: Offset,
281
+ rating_before: Integer,
282
+ rating_after: Integer,
271
283
  members: Generic[Array, TourMember],
272
284
  rival: Optional[RivalInfo],
285
+ players: Generic[Array, PlayerInfo],
273
286
  )
274
287
  end
275
288
 
276
289
  module Record
277
- History = Base::Struct.new(
290
+ History = Base.new(
278
291
  play_count: Integer,
279
292
  last_played: Time,
280
293
  )
281
294
 
282
- ScoreOnly = Base::Struct.new(
295
+ ScoreOnly = Base.new(
283
296
  score: Float,
284
297
  grade: Symbol,
285
298
  )
286
299
 
287
- Score = Base::Struct.new(
300
+ Score = Base.new(
288
301
  web_id: WebID,
289
302
  score: Float,
290
303
  deluxe_score: Result::Progress,
@@ -293,50 +306,50 @@ module MaimaiNet
293
306
  flags: Generic[Array, Symbol],
294
307
  )
295
308
 
296
- ChartRecord = Base::Struct.new(
309
+ ChartRecord = Base.new(
297
310
  info: Chart::Info,
298
311
  record: Optional[Score],
299
312
  history: Optional[History],
300
313
  )
301
314
 
302
- InfoCategory = Base::Struct.new(
315
+ InfoCategory = Base.new(
303
316
  info: Chart::Info,
304
317
  score: Optional[Result::ScoreLite],
305
318
  )
306
319
 
307
- InfoBest = Base::Struct.new(
320
+ InfoBest = Base.new(
308
321
  info: Chart::Info,
309
322
  play_count: Integer,
310
323
  )
311
324
 
312
- InfoRating = Base::Struct.new(
325
+ InfoRating = Base.new(
313
326
  info: Chart::Info,
314
327
  score: ScoreOnly,
315
328
  )
316
329
 
317
- Data = Base::Struct.new(
330
+ Data = Base.new(
318
331
  info: Chart::Song,
319
332
  charts: Generic[Hash, Symbol, ChartRecord],
320
333
  )
321
334
  end
322
335
 
323
336
  module FinaleArchive
324
- Decoration = Base::Struct.new(
337
+ Decoration = Base.new(
325
338
  icon: URI::Generic,
326
339
  player_frame: URI::Generic,
327
340
  nameplate: URI::Generic,
328
341
  )
329
- Currency = Base::Struct.new(
342
+ Currency = Base.new(
330
343
  amount: Integer, piece: Integer, parts: Integer,
331
344
  )
332
- ExtendedInfo = Base::Struct.new(
345
+ ExtendedInfo = Base.new(
333
346
  rating: Float, rating_highest: Float,
334
347
  region_count: Integer,
335
348
  currency: Currency,
336
349
  partner_level_total: Integer,
337
350
  )
338
351
 
339
- DifficultyStatistic = Base::Struct.new(
352
+ DifficultyStatistic = Base.new(
340
353
  total_score: Integer,
341
354
  clears: SongCount,
342
355
  ranks: Generic[Hash, Symbol, SongCount],
@@ -345,7 +358,7 @@ module MaimaiNet
345
358
  multi_flags: Generic[Hash, Symbol, SongCount],
346
359
  )
347
360
 
348
- Data = Base::Struct.new(
361
+ Data = Base.new(
349
362
  info: PlayerCommon::Info,
350
363
  decoration: Decoration,
351
364
  extended: ExtendedInfo,
@@ -64,6 +64,18 @@ module MaimaiNet
64
64
  # and de-group all of the string into array of integers
65
65
  # @return [Array<Integer>]
66
66
  def scan_int(content); content.scan(GROUPED_INTEGER).map(&method(:int)); end
67
+ # parse time string as JST
68
+ # @return [Time]
69
+ def jst(time); ::Time.strptime(time + ' +09:00', '%Y/%m/%d %H:%M %z'); end
70
+ # parse time string as JST from stripped text content
71
+ # @return [Time]
72
+ def jst_from(node); jst(strip(node)); end
73
+ # @return [String] basename part of the path without any prefixes
74
+ def subpath(uri); ::Kernel.Pathname(::Kernel.URI(uri).path)&.sub_ext('')&.sub(/.+_/, '')&.basename.to_s; end
75
+ # (see #subpath)
76
+ def subpath_from(node); node ? subpath(src(node)) : -'' end
77
+ # @return [String] text contained directly under the node
78
+ def text(node); node.children.select(&:text?).map(&:content).inject('', :concat).strip end
67
79
 
68
80
  inspect_permit_variable_exclude :_page
69
81
  inspect_permit_expression do |value| false end
@@ -73,6 +85,43 @@ module MaimaiNet
73
85
  private :new
74
86
  end
75
87
 
88
+ module TrackHelper
89
+ # @return [Constants::Difficulty] difficulty value of given html element
90
+ def get_chart_difficulty_from(node)
91
+ Difficulty(subpath_from(node))
92
+ end
93
+
94
+ # @return [String] normalized difficulty text
95
+ def get_chart_level_text_from(node)
96
+ strip(node).sub(/\?$/, '')
97
+ end
98
+
99
+ # @return [String] chart type of given html element
100
+ # @return ["unknown"] if the chart element is not defined
101
+ def get_chart_type_from(node)
102
+ return -'unknown' if node.nil?
103
+
104
+ subpath_from(node)
105
+ end
106
+
107
+ # @return [String] chart variant of given html element
108
+ # @return [nil] if the chart element is not utage
109
+ # @see HelperBlock#strip
110
+ # @note this is a semantic clarity for strip function.
111
+ def get_chart_variant_from(node)
112
+ node&.at_css('img[src*="music_utage.png"]').nil? ?
113
+ nil : strip(node)
114
+ end
115
+
116
+ # @return [0] for non buddy chart
117
+ # @return [1] for buddy chart
118
+ def get_chart_buddy_flag_from(node)
119
+ node&.at_css('img[src*="music_utage_buddy.png"]').nil? ? 0 : 1
120
+ end
121
+ end
122
+
123
+ HelperBlock.include TrackHelper
124
+
76
125
  # adds capability to inject methods using hidden helper block
77
126
  module HelperSupport
78
127
  # defines the method to be injected with hidden helper block.
@@ -10,7 +10,8 @@ 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 = get_chart_difficulty_from(header_block.at_css('img.playlog_diff'))
14
+ utage_variant = get_chart_variant_from(header_block.at_css('.playlog_music_kind_icon_utage'))
14
15
 
15
16
  dx_container_classes = MaimaiNet::Difficulty::DELUXE.select do |k, v| v.positive? end
16
17
  .keys.map do |k| ".playlog_#{k}_container" end
@@ -20,32 +21,29 @@ module MaimaiNet
20
21
  result_block = info_block.at_css('.basic_block ~ div:nth-of-type(1)')
21
22
 
22
23
  track_order = get_fullint(strip(header_block.at_css('div.sub_title > span:nth-of-type(1)')))
23
- play_time = Time.strptime(
24
- strip(header_block.at_css('div.sub_title > span:nth-of-type(2)')) + ' +09:00',
25
- '%Y/%m/%d %H:%M %z',
26
- )
24
+ play_time = jst_from(header_block.at_css('div.sub_title > span:nth-of-type(2)'))
27
25
  song_name = strip(chart_header_block.children.last)
28
- chart_level = strip(chart_header_block.at_css('div:nth-of-type(1)'))
26
+ chart_level = get_chart_level_text_from(chart_header_block.at_css('div:nth-of-type(1)'))
29
27
  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
33
- end
28
+ chart_type = get_chart_type_from(result_block.at_css('img.playlog_music_kind_icon'))
34
29
 
35
30
  result_score = strip(result_block.at_css('.playlog_achievement_txt')).to_f
36
31
  result_deluxe_scores = scan_int(strip(result_block.at_css('.playlog_result_innerblock .playlog_score_block div:nth-of-type(1)')))
37
- result_grade = ::Kernel.Pathname(::Kernel.URI(src(result_block.at_css('.playlog_scorerank'))).path).sub_ext('')&.sub(/.+_/, '')&.basename&.to_s.to_sym
32
+ result_grade = subpath_from(result_block.at_css('.playlog_scorerank')).to_sym
38
33
  result_flags = result_block.css('.playlog_result_innerblock > img').map do |elm|
39
- flag = ::Kernel.Pathname(::Kernel.URI(src(elm)).path).sub_ext('')&.basename.to_s
34
+ flag = subpath_from(elm)
40
35
  case flag
41
- when *MaimaiNet::AchievementFlag::RESULT.values; MaimaiNet::AchievementFlag.new(result_key: flag)
36
+ when *MaimaiNet::AchievementFlag::RESULT.values; AchievementFlag(result_key: flag)
42
37
  when /_dummy$/; nil
43
38
  end
44
39
  end.compact
40
+ result_position = result_block.at_css('.playlog_result_innerblock img.playlog_matching_icon')&.yield_self do |elm|
41
+ /^\d+/.match(subpath_from(elm))[0].to_i
42
+ end
45
43
 
46
44
  challenge_info = nil
47
45
  result_block.at_css('div:has(> .playlog_life_block)')&.tap do |elm|
48
- challenge_type = ::Kernel.Pathname(::Kernel.URI(src(elm.at_css('img:nth-of-type(1)'))).path).basename.sub_ext('').sub(/.+_/, '').to_s.to_sym
46
+ challenge_type = subpath_from(elm.at_css('img:nth-of-type(1)')).to_sym
49
47
  challenge_lives = scan_int(strip(elm.at_css('.playlog_life_block')))
50
48
 
51
49
  challenge_info = Model::Result::Challenge.new(
@@ -63,6 +61,7 @@ module MaimaiNet
63
61
  end.to_h,
64
62
  grade: result_grade,
65
63
  flags: result_flags.map(&:to_sym),
64
+ position: result_position,
66
65
  }
67
66
 
68
67
  score_cls = score_data.key?(:combo) && score_data.key?(:sync_score) ?
@@ -76,6 +75,7 @@ module MaimaiNet
76
75
  title: song_name,
77
76
  type: (chart_type or -'unknown'),
78
77
  difficulty: difficulty.id,
78
+ variant: utage_variant,
79
79
  level_text: chart_level,
80
80
  ),
81
81
  score: score_info,
@@ -135,22 +135,23 @@ module MaimaiNet
135
135
  images.map do |elm|
136
136
  elm = elm.at_css('> div')
137
137
 
138
- chart_type = Pathname(src(elm.at_css('> .music_kind_icon'))).sub_ext('').basename
139
- difficulty = Difficulty(Pathname(src(elm.at_css('> img:nth-of-type(2)'))).sub_ext('').sub('diff_', '').basename)
138
+ chart_type = get_chart_type_from(elm.at_css('> .music_kind_icon'))
139
+ difficulty = get_chart_difficulty_from(elm.at_css('> .block_info:nth-of-type(1) ~ img:nth-of-type(1)'))
140
+ chart_flags = [
141
+ get_chart_buddy_flag_from(elm.at_css('.music_kind_icon_utage:has(img[src*="music_utage_buddy.png"])')),
142
+ ].inject(0, :|)
140
143
 
141
144
  Model::PhotoUpload.new(
142
145
  info: Model::Chart::InfoLite.new(
143
- title: strip(elm.at_css('> div:not(.clearfix):nth-of-type(2)')),
146
+ title: strip(elm.at_css('> .clearfix:nth-of-type(1) ~ div:nth-of-type(1)')),
144
147
  type: chart_type.to_s,
148
+ variant: get_chart_variant_from(elm.at_css('.music_kind_icon_utage:has(img[src*="music_utage.png"])')),
149
+ flags: chart_flags,
145
150
  difficulty: difficulty.id,
146
151
  ),
147
- url: URI(src(elm.at_css('> img:nth-of-type(3)'))),
148
- location: strip(elm.at_css('> div:not(.clearfix):nth-of-type(4)')),
149
- time: Time.strptime(
150
- strip(elm.at_css('> div:not(.clearfix):nth-of-type(1)')) + ' +09:00',
151
- '%Y/%m/%d %H:%M %z',
152
- Time.now.localtime(32400),
153
- ),
152
+ url: URI(src(elm.at_css('> .block_info:nth-of-type(1) ~ img:nth-of-type(2)'))),
153
+ location: strip(elm.at_css('> .clearfix:nth-of-type(1) ~ div:nth-of-type(3)')),
154
+ time: jst_from(elm.at_css('> div:not(.clearfix):nth-of-type(1)')),
154
155
  )
155
156
  end
156
157
  end
@@ -167,8 +168,12 @@ module MaimaiNet
167
168
  song_info_elm = @summary_block.at_css('> div:nth-of-type(1)')
168
169
 
169
170
  song_jacket = URI(src(@summary_block.at_css('> img:nth-of-type(1)')))
170
- set_type = Pathname(src(song_info_elm.at_css('> div:nth-of-type(1) > img'))).sub_ext('').sub(/.+_/, '').basename.to_s
171
- song_genre = strip(song_info_elm.at_css('> div:nth-of-type(1)'))
171
+ set_type = get_chart_type_from(song_info_elm.at_css('> div:nth-of-type(1) > img'))
172
+ set_utage_variant = get_chart_variant_from(song_info_elm.at_css('> div:nth-of-type(1) > .music_kind_icon_utage:has(img[src*="music_utage.png"])'))
173
+ set_flag = [
174
+ get_chart_buddy_flag_from(song_info_elm.at_css('> div:nth-of-type(1) > .music_kind_icon_utage:has(img[src*="music_utage_buddy.png"])')),
175
+ ].inject(0, :|)
176
+ song_genre = text(song_info_elm.at_css('> div:nth-of-type(1)'))
172
177
  song_name = strip(song_info_elm.at_css('> div:nth-of-type(2)'))
173
178
  song_artist = strip(song_info_elm.at_css('> div:nth-of-type(3)'))
174
179
 
@@ -185,30 +190,33 @@ module MaimaiNet
185
190
  chart_score_blocks = @summary_block.css('~ div:has(~ img)')
186
191
 
187
192
  info_blocks.each do |info_block|
188
- level_text = strip(info_block.at_css('.music_lv_back'))
193
+ level_text = get_chart_level_text_from(info_block.at_css('.music_lv_back'))
189
194
  form_block = info_block.at_css('form')
190
195
  form_inputs = form_block.css('input[name][type=hidden]').map do |elm|
191
196
  [elm['name'].to_sym, elm['value']]
192
197
  end.to_h
198
+
193
199
  difficulty = Difficulty(deluxe_web_id: form_inputs[:diff].to_i)
194
200
  difficulty_data[difficulty.abbrev] ||= {}
195
201
  difficulty_data[difficulty.abbrev].store(:info, Model::Chart::Info.new(
196
202
  web_id: Model::WebID.parse(form_inputs[:idx]),
197
203
  title: song_name,
198
204
  type: set_type,
205
+ variant: set_utage_variant,
206
+ flags: set_flag,
199
207
  difficulty: difficulty.id,
200
208
  level_text: level_text,
201
209
  ))
202
210
  end
203
211
 
204
212
  chart_score_blocks.each do |chart_score_block|
205
- difficulty = Difficulty(Pathname(src(chart_score_block.at_css('> img:nth-of-type(1)'))).sub_ext('').sub(/.+_/, '').basename)
206
- chart_type = Pathname(src(chart_score_block.at_css('> img:nth-of-type(2)')))&.sub_ext('')&.sub(/.+_/, '')&.basename&.to_s or set_type
213
+ difficulty = get_chart_difficulty_from(chart_score_block.at_css('> img:nth-of-type(1)'))
214
+ chart_type = get_chart_type_from(chart_score_block.at_css('> img:nth-of-type(2)')) || set_type
207
215
  clearfixes = chart_score_block.css('.clearfix')
208
216
 
209
217
  chart_record_block = clearfixes[0].at_css('~ div:nth-of-type(2)')
210
218
  record_grade, record_flag, record_sync_flag = chart_record_block.css('> img').map do |elm|
211
- value = Pathname(URI(src(elm)).path).sub_ext('')&.sub(/.+_/, '')&.basename.to_s
219
+ value = subpath_from(elm)
212
220
  case value
213
221
  when 'back'; nil
214
222
  when *MaimaiNet::AchievementFlag::RECORD.values; MaimaiNet::AchievementFlag.new(record_key: value)
@@ -216,7 +224,7 @@ module MaimaiNet
216
224
  end
217
225
  end
218
226
  last_played_date, total_play_count = chart_record_block.css('table tr td:nth-of-type(2)').zip([
219
- ->(content){Time.strptime(content + ' +09:00', '%Y/%m/%d %H:%M %z')},
227
+ method(:jst),
220
228
  method(:int),
221
229
  ]).map do |elm, block|
222
230
  block.call(strip(elm))
@@ -227,7 +235,7 @@ module MaimaiNet
227
235
  chart_deluxe_scores = scan_int(strip(chart_best_block.at_css('.music_score_block:nth-of-type(2)')))
228
236
  chart_deluxe_grade_elm = chart_best_block.at_css('.music_score_block:nth-of-type(2) img:nth-child(2)')
229
237
  record_deluxe_grade = chart_deluxe_grade_elm ?
230
- Pathname(src(chart_deluxe_grade_elm))&.sub_ext('')&.sub(/.+_/, '')&.basename&.to_s.to_i :
238
+ subpath_from(chart_deluxe_grade_elm).to_i :
231
239
  0
232
240
 
233
241
  difficulty_data[difficulty.abbrev].tap do |d|
@@ -265,13 +273,14 @@ module MaimaiNet
265
273
 
266
274
  start_anchor = @root.at_css('img.title')
267
275
  @otomodachi_block = start_anchor.at_css('~ div#vsUser > :first-child')
276
+ @multiplayer_block = start_anchor.at_css('~ div#matching')
268
277
  @score_block = start_anchor.at_css('~ div:not(#vsUser):nth-of-type(1)')
269
278
  @breakdown_block = start_anchor.at_css('~ div:not(#vsUser):nth-of-type(2)')
270
279
  end
271
280
 
272
281
  helper_method :data do
273
282
  result_breakdown = @breakdown_block.css('table.playlog_notes_detail tr:not(:first-child)').map do |row|
274
- key = Pathname(URI(src(row.at_css('th img'))).path).sub_ext('').basename.to_s.to_sym
283
+ key = subpath_from(row.at_css('th img')).to_sym
275
284
  values = Model::Result::Judgment.new(**Model::Result::Judgment.members.zip(
276
285
  row.css('td').map(&method(:strip)).map(&method(:get_int))
277
286
  ).to_h)
@@ -281,8 +290,8 @@ module MaimaiNet
281
290
  result_offset_breakdown = @breakdown_block.css('.playlog_fl_block > div').map do |elm|
282
291
  get_int(strip(elm))
283
292
  end
284
- result_rating_after = int(strip(@breakdown_block.at_css('.playlog_rating_detail_block > div:nth-of-type(1) .rating_block')))
285
- result_rating_delta = int(strip(@breakdown_block.at_css('.playlog_rating_detail_block > div:nth-of-type(2)')))
293
+ result_rating_after = int(strip(@breakdown_block.at_css('.playlog_rating_detail_block > div:has(.rating_block) .rating_block')))
294
+ result_rating_delta = get_int(@breakdown_block.at_css('.playlog_rating_detail_block > div:has(.rating_block) ~ img[src*="/playlog/rating"] ~ div > span'))
286
295
  result_combos, result_sync_scores = @breakdown_block.css('.playlog_score_block').map do |elm|
287
296
  scan_int(strip(elm)).tap do |ary| ary.fill(0, ary.size...2) end
288
297
  end
@@ -310,6 +319,16 @@ module MaimaiNet
310
319
  )
311
320
  end
312
321
 
322
+ result_players = @multiplayer_block&.css(':has(img[src*="/diff"])').to_a.map do |elm|
323
+ difficulty = get_chart_difficulty_from(elm.at_css('> img:nth-of-type(1)'))
324
+ name = strip(elm.at_css('> div.basic_block:nth-of-type(1)'))
325
+
326
+ Model::Result::PlayerInfo.new(
327
+ player_name: name,
328
+ difficulty: difficulty.id,
329
+ )
330
+ end
331
+
313
332
  chart_web_id = Model::WebID.parse(@root.at_css('form[action$="/record/musicDetail/"] input[name=idx]')['value'])
314
333
 
315
334
  Model::Result::Data.new(
@@ -321,8 +340,11 @@ module MaimaiNet
321
340
  ),
322
341
  breakdown: result_breakdown,
323
342
  timing: Model::Result::Offset.new(**Model::Result::Offset.members.zip(result_offset_breakdown).to_h),
343
+ rating_before: result_rating_after - result_rating_delta,
344
+ rating_after: result_rating_after,
324
345
  members: result_tour_members,
325
346
  rival: result_otomodachi_rival,
347
+ players: result_players,
326
348
  )
327
349
  end
328
350
  end
@@ -350,25 +372,42 @@ module MaimaiNet
350
372
  if track_group_blocks.empty? then
351
373
  track_segmented_blocks[current_group] = @root.css('div:has(> form[action$="/musicDetail/"] input[name=idx])')
352
374
  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
375
+ %w(.see_through_block .scroll_point).each do |anchor_class|
376
+ anchor_elm = @root.at_css(anchor_class)
377
+ next if anchor_elm.nil?
378
+
379
+ anchor_elm.css('~ div').each do |elm|
380
+ if elm.classes.include? 'screw_block' then
381
+ current_group = elm.content
382
+ next
383
+ end
384
+
385
+ track_segmented_blocks[current_group] ||= Nokogiri::XML::NodeSet.new(@document)
386
+ track_segmented_blocks[current_group] << elm
357
387
  end
358
388
 
359
- track_segmented_blocks[current_group] ||= Nokogiri::XML::NodeSet.new(@document)
360
- track_segmented_blocks[current_group] << elm
389
+ break
361
390
  end
362
391
  end
363
392
 
364
393
  result = track_segmented_blocks.transform_values do |elm_group|
365
394
  elm_group.map do |elm|
395
+ chart_info = {}
396
+
397
+ chart_info[:type] = get_chart_type_from(elm.at_css('.music_kind_icon'))
398
+ chart_info[:variant] = get_chart_variant_from(elm.at_css('.music_kind_icon_utage:has(img[src*="music_utage.png"])'))
399
+ chart_info[:flags] = [
400
+ get_chart_buddy_flag_from(elm.at_css('.music_kind_icon_utage:has(img[src*="music_utage_buddy.png"])')),
401
+ ].inject(0, :|)
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
- 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,
370
- difficulty: Difficulty(Pathname(URI(src(elm.at_css('form > img:nth-of-type(1)'))).path).sub_ext('').sub(/.+_/, '').basename.to_s).id,
371
- level_text: elm.at_css('.music_lv_block').content,
405
+ title: strip(elm.at_css('.music_name_block')),
406
+ type: chart_info.fetch(:type, -'unknown'),
407
+ difficulty: get_chart_difficulty_from(elm.at_css('form > img:nth-of-type(1)')).id,
408
+ variant: chart_info.fetch(:variant, nil),
409
+ flags: chart_info[:flags],
410
+ level_text: get_chart_level_text_from(elm.at_css('.music_lv_block')),
372
411
  )
373
412
 
374
413
  score_info = nil
@@ -382,7 +421,7 @@ module MaimaiNet
382
421
  )
383
422
  else
384
423
  # ratingTargetMusic page
385
- best_grade = Pathname(URI(src(elm.at_css('.music_score_block:nth-of-type(1) > div > img'))).path).sub_ext('').sub(/.+_/, '').basename.to_s.to_sym
424
+ best_grade = subpath_from(elm.at_css('.music_score_block:nth-of-type(1) > div > img')).to_sym
386
425
  score_info = Model::Result::ScoreOnly.new(
387
426
  score: strip(elm.at_css('.music_score_block:nth-of-type(1)')).to_f,
388
427
  grade: best_grade,
@@ -398,7 +437,7 @@ module MaimaiNet
398
437
  if !elm.at_css('.music_score_block').nil? then
399
438
  best_deluxe_score = scan_int(strip(elm.at_css('.music_score_block:nth-of-type(2)')))
400
439
  flairs = elm.css('.music_score_block ~ img:has(~ .clearfix)').map do |img|
401
- Pathname(URI(src(img)).path).sub_ext('').sub(/.+_/, '').basename.to_s.to_sym.yield_self do |value|
440
+ subpath_from(img).to_sym.yield_self do |value|
402
441
  value == :back ? nil : value
403
442
  end
404
443
  end
@@ -1,12 +1,20 @@
1
1
  module MaimaiNet
2
- # includes AutoConstants into invokable class
2
+ # injects {CoreExt::AutoConstantInclusion} into {Kernel} and {BasicObject}.
3
+ # @note A bug noticed from v0.0.1 where refining Kernel only
4
+ # is not reliable due to prepending on Kernel causes
5
+ # all refines on Kernel are invalidated. A band-aid solution
6
+ # for this is also injecting BasicObject with the same refine.
3
7
  module IncludeAutoConstant
4
8
  refine Kernel do
5
- include CoreExt::KernelAutoConstantInclusion
9
+ include CoreExt::AutoConstantInclusion
10
+ end
11
+ refine BasicObject do
12
+ include CoreExt::AutoConstantInclusion
6
13
  end
7
14
  end
8
15
 
9
- # converts any object into a single-element array unless it's an array
16
+ # grants any object an ability to convert itself into a single-element array.
17
+ # unless it's an array already.
10
18
  module ObjectAsArray
11
19
  refine Object do
12
20
  def as_array
@@ -1,3 +1,3 @@
1
1
  module MaimaiNet
2
- VERSION = -'0.0.1'
2
+ VERSION = -'0.0.3'
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.3
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-20 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