sleeper_api 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.editorconfig +17 -0
- data/.github/workflows/ci.yml +30 -0
- data/.gitignore +13 -0
- data/.rubocop.yml +114 -0
- data/.rubocop_todo.yml +26 -0
- data/CHANGELOG.md +28 -0
- data/Gemfile +13 -0
- data/LICENSE.txt +21 -0
- data/README.md +447 -0
- data/Rakefile +11 -0
- data/bin/console +11 -0
- data/bin/setup +8 -0
- data/lib/sleeper_api/client.rb +277 -0
- data/lib/sleeper_api/draft.rb +150 -0
- data/lib/sleeper_api/helpers.rb +24 -0
- data/lib/sleeper_api/league.rb +399 -0
- data/lib/sleeper_api/user.rb +137 -0
- data/lib/sleeper_api/version.rb +5 -0
- data/lib/sleeper_api.rb +87 -0
- data/sig/sleeper_api.rbs +4 -0
- data/sleeper_api.gemspec +53 -0
- metadata +184 -0
data/README.md
ADDED
@@ -0,0 +1,447 @@
|
|
1
|
+
# SleeperApi
|
2
|
+
|
3
|
+
A comprehensive Ruby gem for interacting with [Sleeper's fantasy football API](https://docs.sleeper.com/). Built with performance, reliability, and developer experience in mind.
|
4
|
+
|
5
|
+
## Features
|
6
|
+
|
7
|
+
- Complete API Coverage - Users, leagues, drafts, players, matchups, transactions
|
8
|
+
|
9
|
+
- Performance Optimized - Smart caching, connection pooling, rate limiting
|
10
|
+
|
11
|
+
- Robust Error Handling - Automatic retries, timeout management, detailed error messages
|
12
|
+
|
13
|
+
- Well Tested - 90%+ test coverage with RSpec
|
14
|
+
|
15
|
+
- Highly Configurable - Custom timeouts, retries, logging
|
16
|
+
|
17
|
+
- Production Ready - Type signatures, CI/CD, code quality tools
|
18
|
+
|
19
|
+
## Installation
|
20
|
+
|
21
|
+
Add this line to your application's Gemfile:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
gem 'sleeper_api'
|
25
|
+
```
|
26
|
+
|
27
|
+
## Quick Start
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
require 'sleeper_api'
|
31
|
+
|
32
|
+
# Basic usage with default configuration
|
33
|
+
client = SleeperApi.client
|
34
|
+
league = client.league("123456789012345678")
|
35
|
+
|
36
|
+
# Access league information
|
37
|
+
puts league.name # "My Fantasy League"
|
38
|
+
puts league.total_rosters # 12
|
39
|
+
puts league.status # "in_season"
|
40
|
+
|
41
|
+
# Get rosters
|
42
|
+
rosters = league.rosters
|
43
|
+
rosters.each do |roster|
|
44
|
+
puts "#{roster[:owner_display_name]}: #{roster[:wins]}-#{roster[:losses]}"
|
45
|
+
end
|
46
|
+
```
|
47
|
+
|
48
|
+
## Configuration
|
49
|
+
|
50
|
+
Customize the gem's behavior:
|
51
|
+
|
52
|
+
```ruby
|
53
|
+
SleeperApi.configure do |config|
|
54
|
+
config.timeout = 45 # Request timeout in seconds (10-60)
|
55
|
+
config.retries = 5 # Number of retries on failure (0-5)
|
56
|
+
config.logger = Logger.new(STDOUT) # Custom logger
|
57
|
+
end
|
58
|
+
|
59
|
+
# Configuration is applied to all subsequent client instances
|
60
|
+
client = SleeperApi.client
|
61
|
+
```
|
62
|
+
|
63
|
+
## API Coverage
|
64
|
+
|
65
|
+
### Users
|
66
|
+
|
67
|
+
Get user information and their leagues/drafts:
|
68
|
+
|
69
|
+
```ruby
|
70
|
+
# Find a user by username
|
71
|
+
user = SleeperApi.client.user('johndoe')
|
72
|
+
|
73
|
+
# Basic profile
|
74
|
+
user.username # => "johndoe"
|
75
|
+
user.display_name # => "John Doe"
|
76
|
+
user.avatar_url # => "https://sleepercdn.com/avatars/abc123"
|
77
|
+
user.is_bot # => false
|
78
|
+
|
79
|
+
# All leagues for current season
|
80
|
+
leagues = user.leagues
|
81
|
+
leagues.each do |league|
|
82
|
+
status = league[:status] == "finished" ? "🏆" : league[:status]
|
83
|
+
puts "#{status} #{league[:name]} (#{league[:total_rosters]} teams)"
|
84
|
+
end
|
85
|
+
|
86
|
+
# Specific season
|
87
|
+
old_leagues = user.leagues(2023)
|
88
|
+
championships = old_leagues.select { |l| l[:status] == "finished" }
|
89
|
+
|
90
|
+
# Get all my rosters across all leagues
|
91
|
+
rosters = user.rosters
|
92
|
+
rosters.each do |roster|
|
93
|
+
puts "#{roster[:league_name]}: #{roster[:team_name]} (#{roster[:wins]}-#{roster[:losses]})"
|
94
|
+
end
|
95
|
+
|
96
|
+
# Best performing team this season
|
97
|
+
best_roster = user.rosters.max_by { |r| r[:wins].to_i - r[:losses].to_i }
|
98
|
+
puts "Top team: #{best_roster[:team_name]} (#{best_roster[:wins]}-#{best_roster[:losses]})"
|
99
|
+
|
100
|
+
# All drafts with enhanced info
|
101
|
+
drafts = user.drafts(2024)
|
102
|
+
drafts.each do |draft|
|
103
|
+
status = draft[:status] == "complete" ? "✅" : draft[:status]
|
104
|
+
puts "#{status} #{draft[:league_name]} - #{draft[:type]} (#{draft[:total_teams]} teams)"
|
105
|
+
end
|
106
|
+
|
107
|
+
# Live drafts only
|
108
|
+
live_drafts = user.drafts.select { |d| d[:status] == "live" }
|
109
|
+
|
110
|
+
# Season summary
|
111
|
+
summary = user.summary
|
112
|
+
puts "#{summary[:total_leagues]} leagues this season"
|
113
|
+
puts "Winning record in #{summary[:winning_record].length} leagues"
|
114
|
+
puts "Overall: #{summary[:total_wins]}-#{summary[:total_losses]}"
|
115
|
+
```
|
116
|
+
|
117
|
+
### Leagues
|
118
|
+
|
119
|
+
Access league data, rosters, matchups, and transactions:
|
120
|
+
|
121
|
+
```ruby
|
122
|
+
league = SleeperApi.client.league("123456")
|
123
|
+
|
124
|
+
# Basic league info
|
125
|
+
league.name # => "My Fantasy League"
|
126
|
+
league.season # => "2024"
|
127
|
+
league.total_rosters # => 12
|
128
|
+
league.status # => "regular"
|
129
|
+
league.avatar_url # => "https://sleepercdn.com/avatars/abc123"
|
130
|
+
|
131
|
+
# Formatted rosters (with team names and records)
|
132
|
+
rosters = league.rosters
|
133
|
+
roster = rosters.first
|
134
|
+
roster[:owner_display_name] # => "John Doe"
|
135
|
+
roster[:team_name] # => "Gridiron Gang"
|
136
|
+
roster[:wins] # => 7
|
137
|
+
roster[:losses] # => 2
|
138
|
+
roster[:remaining_faab] # => 45
|
139
|
+
roster[:starters] # => ["player_id_1", "player_id_2", ...]
|
140
|
+
roster[:bench] # => ["player_id_4", "player_id_5", ...]
|
141
|
+
|
142
|
+
# My roster only
|
143
|
+
my_roster = league.rosters(user_id: my_user_id)
|
144
|
+
|
145
|
+
# Week-specific matchups
|
146
|
+
matchups = league.matchups_by_week(week: 5)
|
147
|
+
matchup = matchups.first
|
148
|
+
team = matchup[:rosters].first
|
149
|
+
team[:points] # => 142.5
|
150
|
+
team[:starter_points] # => [{ "player_id" => 18.2 }, ...]
|
151
|
+
|
152
|
+
# Transactions
|
153
|
+
transactions = league.transactions(week: 5)
|
154
|
+
tx = transactions.first
|
155
|
+
tx[:type] # => "tr"
|
156
|
+
tx[:adds] # => [{ player_id: "123", roster_id: 1 }]
|
157
|
+
tx[:drops] # => [{ player_id: "456", roster_id: 1 }]
|
158
|
+
tx[:waiver_bid] # => 12
|
159
|
+
|
160
|
+
# Users
|
161
|
+
users = league.users
|
162
|
+
user = users.find { |u| u[:commissioner] }
|
163
|
+
user[:display_name] # => "Commish McGee"
|
164
|
+
user[:team_name] # => "Champions"
|
165
|
+
|
166
|
+
# Playoffs
|
167
|
+
playoff_matchups = league.playoff_bracket
|
168
|
+
matchup = playoff_matchups.first
|
169
|
+
matchup[:team1_owner] # => "Team Alpha"
|
170
|
+
matchup[:winner_owner] # => "Team Alpha"
|
171
|
+
|
172
|
+
# Tourlet Bowl / Losers Bracket
|
173
|
+
toilet_bowl_matchups = league.toilet_bowl
|
174
|
+
matchup = toilet_bowl_matchups.first
|
175
|
+
matchup[:team1_owner] # => "Team Beta"
|
176
|
+
matchup[:winner_owner] # => "Team Beta"
|
177
|
+
```
|
178
|
+
|
179
|
+
### Drafts
|
180
|
+
|
181
|
+
Access draft information, picks, and traded picks:
|
182
|
+
|
183
|
+
```ruby
|
184
|
+
# Get a specific draft
|
185
|
+
draft = SleeperApi.client.draft("draft_123456")
|
186
|
+
|
187
|
+
# Basic draft info
|
188
|
+
draft.type # => "snake" or "auction"
|
189
|
+
draft.status # => "pre_draft", "live", "complete"
|
190
|
+
draft.season # => "2024"
|
191
|
+
draft.start_time # => "2024-08-27T20:00:00.000Z"
|
192
|
+
draft.league.name # => "My Fantasy League"
|
193
|
+
|
194
|
+
# All picks (180 picks for 15-round, 12-team draft)
|
195
|
+
picks = draft.picks
|
196
|
+
puts "#{picks.length} total picks made"
|
197
|
+
|
198
|
+
# Filter picks
|
199
|
+
round_1 = draft.picks(round: 1)
|
200
|
+
team_3_picks = draft.picks(roster_id: 3)
|
201
|
+
|
202
|
+
round_1.each do |pick|
|
203
|
+
player = SleeperApi.client.get_player_by_id(pick[:player_id])
|
204
|
+
puts "#{pick[:pick_no]}. #{player['full_name']} (#{player['position']}) - Team #{pick[:roster_id]}"
|
205
|
+
end
|
206
|
+
|
207
|
+
# Who drafted a specific player?
|
208
|
+
picker = draft.picked_by("4036") # User 1
|
209
|
+
|
210
|
+
# Traded picks
|
211
|
+
trades = draft.traded_picks
|
212
|
+
team_3_trades = draft.traded_picks(original_owner_id: 3)
|
213
|
+
|
214
|
+
team_3_trades.each do |trade|
|
215
|
+
puts "Team 3 traded away #{trade[:round]}.#{trade[:draft_slot]} to Team #{trade[:owner_id]}"
|
216
|
+
end
|
217
|
+
|
218
|
+
# Live draft - next pick info
|
219
|
+
if draft.status == "live"
|
220
|
+
next_pick = draft.next_pick
|
221
|
+
puts "Next up: Round #{next_pick[:round]}, Pick #{next_pick[:pick_no]} (slot #{next_pick[:draft_slot]})"
|
222
|
+
end
|
223
|
+
|
224
|
+
# Quick summary with top picks
|
225
|
+
summary = draft.summary
|
226
|
+
puts "#{summary[:total_picks]} picks in #{summary[:total_rounds]} rounds"
|
227
|
+
summary[:top_picks].each do |pick|
|
228
|
+
puts "#{pick[:pick_no]}. #{pick[:player_name]} (#{pick[:position]}, #{pick[:team]}) - #{pick[:roster_id]}"
|
229
|
+
end
|
230
|
+
|
231
|
+
# Grouped views
|
232
|
+
rounds = draft.rounds # { 1 => [picks], 2 => [picks], ... }
|
233
|
+
team_3_rounds = draft.team_picks[3] # Team 3's picks by round
|
234
|
+
```
|
235
|
+
|
236
|
+
### Players
|
237
|
+
|
238
|
+
Access player data with automatic caching:
|
239
|
+
|
240
|
+
```ruby
|
241
|
+
# Get all players (cached for 24 hours)
|
242
|
+
players = client.get_players
|
243
|
+
|
244
|
+
# Find specific player
|
245
|
+
player = client.get_player_by_id("1234")
|
246
|
+
puts "#{player['first_name']} #{player['last_name']} - #{player['position']}"
|
247
|
+
|
248
|
+
# Get trending players
|
249
|
+
trending_adds = client.trending_players(type: "add", limit: 10)
|
250
|
+
trending_drops = client.trending_players(type: "drop", limit: 10)
|
251
|
+
```
|
252
|
+
|
253
|
+
### Player Helper
|
254
|
+
|
255
|
+
All resource classes (`League`, `User`, `Draft`) include a `player_details` helper method that returns formatted Sleeper player data. This eliminates repetitive player lookups and ensures consistent symbolization.
|
256
|
+
|
257
|
+
```ruby
|
258
|
+
# In any resource (League, User, Draft)
|
259
|
+
league = SleeperApi.client.league("123456")
|
260
|
+
|
261
|
+
# Get player details with one method call
|
262
|
+
player = league.player_details("4036") # Christian McCaffrey
|
263
|
+
if player
|
264
|
+
puts "#{player[:full_name]} (#{player[:position]}, #{player[:team]})"
|
265
|
+
puts "Bye week: #{player[:bye]} | Injury status: #{player[:injury_status]}"
|
266
|
+
end
|
267
|
+
|
268
|
+
# Works in matchups
|
269
|
+
matchups = league.matchups_by_week(week: 5)
|
270
|
+
matchup = matchups.first
|
271
|
+
team = matchup[:rosters].first
|
272
|
+
|
273
|
+
# Get starter names and positions
|
274
|
+
team[:starters].each do |player_id|
|
275
|
+
player = league.player_details(player_id)
|
276
|
+
if player
|
277
|
+
puts "#{player[:full_name]} (#{player[:position]}) - #{team[:starter_points].find { |p| p.keys.first == player_id }&.values&.first || 0} pts"
|
278
|
+
end
|
279
|
+
end
|
280
|
+
|
281
|
+
# Works in rosters
|
282
|
+
roster = league.rosters.first
|
283
|
+
roster[:starters].each_with_index do |player_id, index|
|
284
|
+
player = league.player_details(player_id)
|
285
|
+
position = player ? player[:position] : "?"
|
286
|
+
points = roster[:starter_points]&.dig(index, player_id) || 0
|
287
|
+
puts "#{position}: #{player ? player[:full_name] : 'Unknown'} (#{points} pts)"
|
288
|
+
end
|
289
|
+
|
290
|
+
# Works in drafts
|
291
|
+
draft = SleeperApi.client.draft("draft_123")
|
292
|
+
round_1 = draft.picks(round: 1)
|
293
|
+
|
294
|
+
round_1.each do |pick|
|
295
|
+
player = draft.player_details(pick[:player_id])
|
296
|
+
if player
|
297
|
+
puts "#{pick[:pick_no]}. #{player[:full_name]} (#{player[:position]}, #{player[:team]})"
|
298
|
+
end
|
299
|
+
end
|
300
|
+
|
301
|
+
# Works across user's leagues
|
302
|
+
user = SleeperApi.client.user('johndoe')
|
303
|
+
rosters = user.rosters
|
304
|
+
|
305
|
+
rosters.each do |roster|
|
306
|
+
puts "\n#{roster[:team_name]} starters:"
|
307
|
+
roster[:starters].each do |player_id|
|
308
|
+
player = roster.player_details(player_id) # Note: uses the League instance under the hood
|
309
|
+
puts " • #{player ? player[:full_name] : 'Unknown'} (#{player[:position] if player})"
|
310
|
+
end
|
311
|
+
end
|
312
|
+
```
|
313
|
+
|
314
|
+
### Additional Endpoints
|
315
|
+
|
316
|
+
```ruby
|
317
|
+
# Get NFL state
|
318
|
+
state = client.get_nfl_state
|
319
|
+
puts "Current week: #{state['week']}"
|
320
|
+
|
321
|
+
# Get playoff brackets
|
322
|
+
winners_bracket = client.get_league_playoff_bracket("league_id")
|
323
|
+
losers_bracket = client.get_league_toilet_bowl("league_id")
|
324
|
+
|
325
|
+
# Get league drafts
|
326
|
+
league_drafts = client.get_league_drafts("league_id")
|
327
|
+
|
328
|
+
# Get traded picks
|
329
|
+
traded_picks = client.get_league_traded_picks("league_id")
|
330
|
+
```
|
331
|
+
|
332
|
+
## Error Handling
|
333
|
+
|
334
|
+
The gem provides comprehensive error handling:
|
335
|
+
|
336
|
+
```ruby
|
337
|
+
begin
|
338
|
+
league = client.league("invalid_id")
|
339
|
+
# Process league data
|
340
|
+
rescue SleeperApi::Error => e
|
341
|
+
puts "API Error: #{e.message}"
|
342
|
+
rescue ArgumentError => e
|
343
|
+
puts "Invalid parameter: #{e.message}"
|
344
|
+
end
|
345
|
+
```
|
346
|
+
|
347
|
+
### Error Types
|
348
|
+
|
349
|
+
- SleeperApi::Error - API-related errors (404, 500, timeouts, etc.)
|
350
|
+
|
351
|
+
- ArgumentError - Invalid parameters passed to methods
|
352
|
+
|
353
|
+
## Performance Considerations
|
354
|
+
|
355
|
+
### Caching
|
356
|
+
|
357
|
+
- Player data is cached for 24 hours to reduce API calls
|
358
|
+
|
359
|
+
- League/User/Draft data is cached per instance
|
360
|
+
|
361
|
+
- Cache files are stored in the current working directory
|
362
|
+
|
363
|
+
### Rate Limiting
|
364
|
+
|
365
|
+
- Be mindful of Sleeper's rate limits: stay under 1000 API calls per minute
|
366
|
+
|
367
|
+
- The gem automatically handles timeouts and retries
|
368
|
+
|
369
|
+
- Consider caching frequently accessed data in your application
|
370
|
+
|
371
|
+
### Memory Usage
|
372
|
+
|
373
|
+
- Large datasets (like all players) are cached to disk
|
374
|
+
|
375
|
+
- League rosters and matchups are fetched lazily
|
376
|
+
|
377
|
+
- Use no_data: true when initializing leagues if you don't need immediate data
|
378
|
+
|
379
|
+
## Testing
|
380
|
+
|
381
|
+
The gem includes comprehensive tests:
|
382
|
+
|
383
|
+
```shellscript
|
384
|
+
# Run all tests
|
385
|
+
bundle exec rspec
|
386
|
+
# Run with coverage
|
387
|
+
bundle exec rspec --coverage
|
388
|
+
# Run specific test file
|
389
|
+
bundle exec rspec spec/sleeper_api/client_spec.rb
|
390
|
+
```
|
391
|
+
|
392
|
+
### Setup
|
393
|
+
|
394
|
+
```shellscript
|
395
|
+
git clone https://github.com/eruity1/sleeper_api.git
|
396
|
+
cd sleeper_api
|
397
|
+
bundle install
|
398
|
+
```
|
399
|
+
|
400
|
+
### Code Quality
|
401
|
+
|
402
|
+
```shellscript
|
403
|
+
# Run all checks (tests + linting)
|
404
|
+
bundle exec rake ci
|
405
|
+
|
406
|
+
# Run RuboCop
|
407
|
+
bundle exec rubocop
|
408
|
+
|
409
|
+
# Auto-fix RuboCop issues
|
410
|
+
bundle exec rubocop -a
|
411
|
+
```
|
412
|
+
|
413
|
+
### Contributing
|
414
|
+
|
415
|
+
1. Fork the repository
|
416
|
+
|
417
|
+
1. Create a feature branch (git checkout -b feature/amazing-feature)
|
418
|
+
|
419
|
+
1. Commit your changes (git commit -m 'Add amazing feature')
|
420
|
+
|
421
|
+
1. Push to the branch (git push origin feature/amazing-feature)
|
422
|
+
|
423
|
+
1. Open a Pull Request
|
424
|
+
|
425
|
+
### Development Dependencies
|
426
|
+
|
427
|
+
- rspec - Testing framework
|
428
|
+
|
429
|
+
- rubocop - Code style and quality
|
430
|
+
|
431
|
+
- simplecov - Test coverage
|
432
|
+
|
433
|
+
- webmock - HTTP request mocking
|
434
|
+
|
435
|
+
## Requirements
|
436
|
+
|
437
|
+
- Ruby 2.6.0 or higher
|
438
|
+
|
439
|
+
- No external dependencies (HTTParty is bundled)
|
440
|
+
|
441
|
+
## License
|
442
|
+
|
443
|
+
The gem is available as open source under the terms of the MIT License.
|
444
|
+
|
445
|
+
## Changelog
|
446
|
+
|
447
|
+
See CHANGELOG.md for version history and updates.
|
data/Rakefile
ADDED
data/bin/console
ADDED
@@ -0,0 +1,11 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require "bundler/setup"
|
5
|
+
require "sleeper_api"
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
require "irb"
|
11
|
+
IRB.start(__FILE__)
|