y_fantasy 0.1.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/.pryrc +8 -0
- data/.rspec +3 -0
- data/.rubocop.yml +20 -0
- data/.standard.yml +2 -0
- data/CHANGELOG.md +5 -0
- data/CODE_OF_CONDUCT.md +3 -0
- data/Gemfile +5 -0
- data/Gemfile.lock +220 -0
- data/LICENSE.txt +21 -0
- data/README.md +41 -0
- data/Rakefile +12 -0
- data/exe/y_fantasy +9 -0
- data/lib/y_fantasy/api/authentication.rb +177 -0
- data/lib/y_fantasy/api/client.rb +84 -0
- data/lib/y_fantasy/api/subresource_param_builder.rb +138 -0
- data/lib/y_fantasy/api/url_builder.rb +91 -0
- data/lib/y_fantasy/cli.rb +46 -0
- data/lib/y_fantasy/concerns/subresourceable.rb +87 -0
- data/lib/y_fantasy/ref/nfl.rb +57 -0
- data/lib/y_fantasy/resources/base_resource.rb +117 -0
- data/lib/y_fantasy/resources/base_subresource.rb +31 -0
- data/lib/y_fantasy/resources/game/game_week.rb +26 -0
- data/lib/y_fantasy/resources/game/position_type.rb +20 -0
- data/lib/y_fantasy/resources/game.rb +136 -0
- data/lib/y_fantasy/resources/group/settings.rb +53 -0
- data/lib/y_fantasy/resources/group/standings.rb +11 -0
- data/lib/y_fantasy/resources/group.rb +76 -0
- data/lib/y_fantasy/resources/league/scoreboard.rb +12 -0
- data/lib/y_fantasy/resources/league/settings.rb +51 -0
- data/lib/y_fantasy/resources/league/standings.rb +25 -0
- data/lib/y_fantasy/resources/league.rb +187 -0
- data/lib/y_fantasy/resources/pickem_team/pick.rb +31 -0
- data/lib/y_fantasy/resources/pickem_team/week_pick.rb +28 -0
- data/lib/y_fantasy/resources/pickem_team.rb +98 -0
- data/lib/y_fantasy/resources/player/draft_analysis.rb +26 -0
- data/lib/y_fantasy/resources/player/ownership_percentage.rb +26 -0
- data/lib/y_fantasy/resources/player/stat_collection.rb +32 -0
- data/lib/y_fantasy/resources/player.rb +157 -0
- data/lib/y_fantasy/resources/shared_subresources/draft_result.rb +39 -0
- data/lib/y_fantasy/resources/shared_subresources/matchup.rb +61 -0
- data/lib/y_fantasy/resources/shared_subresources/roster_position.rb +38 -0
- data/lib/y_fantasy/resources/shared_subresources/stat.rb +16 -0
- data/lib/y_fantasy/resources/shared_subresources/stat_category.rb +22 -0
- data/lib/y_fantasy/resources/shared_subresources/stat_modifier.rb +10 -0
- data/lib/y_fantasy/resources/shared_subresources/stat_winner.rb +8 -0
- data/lib/y_fantasy/resources/team/manager.rb +23 -0
- data/lib/y_fantasy/resources/team/roster.rb +18 -0
- data/lib/y_fantasy/resources/team/standings.rb +33 -0
- data/lib/y_fantasy/resources/team/stat_collection.rb +38 -0
- data/lib/y_fantasy/resources/team.rb +175 -0
- data/lib/y_fantasy/subresource_validator.rb +67 -0
- data/lib/y_fantasy/transformations/base_transform.rb +62 -0
- data/lib/y_fantasy/transformations/collection_transformer.rb +22 -0
- data/lib/y_fantasy/transformations/default_transformer.rb +22 -0
- data/lib/y_fantasy/transformations/game/position_types_transformer.rb +21 -0
- data/lib/y_fantasy/transformations/game_transformer.rb +54 -0
- data/lib/y_fantasy/transformations/group_transformer.rb +39 -0
- data/lib/y_fantasy/transformations/instantiator.rb +25 -0
- data/lib/y_fantasy/transformations/key_unwrapper.rb +12 -0
- data/lib/y_fantasy/transformations/league/scoreboard_transformer.rb +21 -0
- data/lib/y_fantasy/transformations/league/settings_transformer.rb +25 -0
- data/lib/y_fantasy/transformations/league/standings_transformer.rb +22 -0
- data/lib/y_fantasy/transformations/league_transformer.rb +57 -0
- data/lib/y_fantasy/transformations/matchups_transformer.rb +22 -0
- data/lib/y_fantasy/transformations/pickem_team/week_picks_transformer.rb +29 -0
- data/lib/y_fantasy/transformations/pickem_team_transformer.rb +41 -0
- data/lib/y_fantasy/transformations/player/ownership_percentage_transformer.rb +21 -0
- data/lib/y_fantasy/transformations/player/stats_transformer.rb +32 -0
- data/lib/y_fantasy/transformations/player_transformer.rb +34 -0
- data/lib/y_fantasy/transformations/stat_categories_transformer.rb +19 -0
- data/lib/y_fantasy/transformations/stat_modifiers_transformer.rb +19 -0
- data/lib/y_fantasy/transformations/t.rb +44 -0
- data/lib/y_fantasy/transformations/team/manager_transformer.rb +19 -0
- data/lib/y_fantasy/transformations/team/roster_transformer.rb +27 -0
- data/lib/y_fantasy/transformations/team/standings_transformer.rb +42 -0
- data/lib/y_fantasy/transformations/team/stats_transformer.rb +30 -0
- data/lib/y_fantasy/transformations/team_transformer.rb +54 -0
- data/lib/y_fantasy/transformations/user_transformer.rb +17 -0
- data/lib/y_fantasy/transformations.rb +54 -0
- data/lib/y_fantasy/version.rb +5 -0
- data/lib/y_fantasy.rb +36 -0
- data/sig/y_fantasy.rbs +4 -0
- data/y_fantasy.gemspec +49 -0
- metadata +364 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 4fa3a23dc0749a4169ee95d862127a9f88d85651439121a5cd694a541d7ec2e6
|
4
|
+
data.tar.gz: b7a3fc4a5c320c29eefbbbedbfdd55983dc2c737eb75ab6c5120927b9c04f455
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: a8b0e3a377c985aa5c90caa3e543d6cff2628f31dfb53826a1e37c15a0a3727687ec2ecc25f739f3d0ba8450d238a468b6b16751c9c20e0cd72c6f11ab805a52
|
7
|
+
data.tar.gz: 57d2e8415bd4f77426f4841b8e7ed23eb1d3848163f90eba7f2a6c4acd3efdd6c4466d0ff9adca6d72745b20043e4d433c5b34b1a52aeea361e735886016825e
|
data/.pryrc
ADDED
data/.rspec
ADDED
data/.rubocop.yml
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require: standard
|
2
|
+
|
3
|
+
inherit_gem:
|
4
|
+
standard: config/base.yml
|
5
|
+
|
6
|
+
AllCops:
|
7
|
+
TargetRubyVersion: 3.1
|
8
|
+
Exclude:
|
9
|
+
- "scratch/**/*"
|
10
|
+
|
11
|
+
Style/FrozenStringLiteralComment:
|
12
|
+
Enabled: true
|
13
|
+
|
14
|
+
Style/StringLiterals:
|
15
|
+
Enabled: true
|
16
|
+
EnforcedStyle: double_quotes
|
17
|
+
|
18
|
+
Style/StringLiteralsInInterpolation:
|
19
|
+
Enabled: true
|
20
|
+
EnforcedStyle: double_quotes
|
data/.standard.yml
ADDED
data/CHANGELOG.md
ADDED
data/CODE_OF_CONDUCT.md
ADDED
data/Gemfile
ADDED
data/Gemfile.lock
ADDED
@@ -0,0 +1,220 @@
|
|
1
|
+
PATH
|
2
|
+
remote: .
|
3
|
+
specs:
|
4
|
+
y_fantasy (0.1.0)
|
5
|
+
dry-configurable (~> 1.0)
|
6
|
+
dry-initializer (~> 3.0)
|
7
|
+
dry-transformer (~> 1.0)
|
8
|
+
dry-types (~> 1.0)
|
9
|
+
dry-validation (~> 1.0)
|
10
|
+
mechanize (~> 2.9)
|
11
|
+
ox (~> 2.14)
|
12
|
+
thor (~> 1.3)
|
13
|
+
zeitwerk (~> 2.5)
|
14
|
+
|
15
|
+
GEM
|
16
|
+
remote: https://rubygems.org/
|
17
|
+
specs:
|
18
|
+
activesupport (7.1.5.1)
|
19
|
+
base64
|
20
|
+
benchmark (>= 0.3)
|
21
|
+
bigdecimal
|
22
|
+
concurrent-ruby (~> 1.0, >= 1.0.2)
|
23
|
+
connection_pool (>= 2.2.5)
|
24
|
+
drb
|
25
|
+
i18n (>= 1.6, < 2)
|
26
|
+
logger (>= 1.4.2)
|
27
|
+
minitest (>= 5.1)
|
28
|
+
mutex_m
|
29
|
+
securerandom (>= 0.3)
|
30
|
+
tzinfo (~> 2.0)
|
31
|
+
addressable (2.8.7)
|
32
|
+
public_suffix (>= 2.0.2, < 7.0)
|
33
|
+
ast (2.4.2)
|
34
|
+
base64 (0.2.0)
|
35
|
+
benchmark (0.4.0)
|
36
|
+
bigdecimal (3.1.5)
|
37
|
+
byebug (11.1.3)
|
38
|
+
coderay (1.1.3)
|
39
|
+
concurrent-ruby (1.2.2)
|
40
|
+
connection_pool (2.5.0)
|
41
|
+
crack (0.4.5)
|
42
|
+
rexml
|
43
|
+
diff-lcs (1.5.0)
|
44
|
+
docile (1.4.1)
|
45
|
+
domain_name (0.6.20240107)
|
46
|
+
drb (2.2.1)
|
47
|
+
dry-configurable (1.1.0)
|
48
|
+
dry-core (~> 1.0, < 2)
|
49
|
+
zeitwerk (~> 2.6)
|
50
|
+
dry-core (1.0.1)
|
51
|
+
concurrent-ruby (~> 1.0)
|
52
|
+
zeitwerk (~> 2.6)
|
53
|
+
dry-inflector (1.0.0)
|
54
|
+
dry-initializer (3.1.1)
|
55
|
+
dry-logic (1.5.0)
|
56
|
+
concurrent-ruby (~> 1.0)
|
57
|
+
dry-core (~> 1.0, < 2)
|
58
|
+
zeitwerk (~> 2.6)
|
59
|
+
dry-schema (1.13.3)
|
60
|
+
concurrent-ruby (~> 1.0)
|
61
|
+
dry-configurable (~> 1.0, >= 1.0.1)
|
62
|
+
dry-core (~> 1.0, < 2)
|
63
|
+
dry-initializer (~> 3.0)
|
64
|
+
dry-logic (>= 1.4, < 2)
|
65
|
+
dry-types (>= 1.7, < 2)
|
66
|
+
zeitwerk (~> 2.6)
|
67
|
+
dry-transformer (1.0.1)
|
68
|
+
zeitwerk (~> 2.6)
|
69
|
+
dry-types (1.7.2)
|
70
|
+
bigdecimal (~> 3.0)
|
71
|
+
concurrent-ruby (~> 1.0)
|
72
|
+
dry-core (~> 1.0)
|
73
|
+
dry-inflector (~> 1.0)
|
74
|
+
dry-logic (~> 1.4)
|
75
|
+
zeitwerk (~> 2.6)
|
76
|
+
dry-validation (1.10.0)
|
77
|
+
concurrent-ruby (~> 1.0)
|
78
|
+
dry-core (~> 1.0, < 2)
|
79
|
+
dry-initializer (~> 3.0)
|
80
|
+
dry-schema (>= 1.12, < 2)
|
81
|
+
zeitwerk (~> 2.6)
|
82
|
+
factory_bot (6.5.0)
|
83
|
+
activesupport (>= 5.0.0)
|
84
|
+
hashdiff (1.1.0)
|
85
|
+
http-cookie (1.0.8)
|
86
|
+
domain_name (~> 0.5)
|
87
|
+
i18n (1.14.7)
|
88
|
+
concurrent-ruby (~> 1.0)
|
89
|
+
json (2.9.1)
|
90
|
+
language_server-protocol (3.17.0.3)
|
91
|
+
lint_roller (1.1.0)
|
92
|
+
logger (1.6.5)
|
93
|
+
mechanize (2.14.0)
|
94
|
+
addressable (~> 2.8)
|
95
|
+
base64
|
96
|
+
domain_name (~> 0.5, >= 0.5.20190701)
|
97
|
+
http-cookie (~> 1.0, >= 1.0.3)
|
98
|
+
mime-types (~> 3.3)
|
99
|
+
net-http-digest_auth (~> 1.4, >= 1.4.1)
|
100
|
+
net-http-persistent (>= 2.5.2, < 5.0.dev)
|
101
|
+
nkf
|
102
|
+
nokogiri (~> 1.11, >= 1.11.2)
|
103
|
+
rubyntlm (~> 0.6, >= 0.6.3)
|
104
|
+
webrick (~> 1.7)
|
105
|
+
webrobots (~> 0.1.2)
|
106
|
+
method_source (1.1.0)
|
107
|
+
mime-types (3.6.0)
|
108
|
+
logger
|
109
|
+
mime-types-data (~> 3.2015)
|
110
|
+
mime-types-data (3.2025.0107)
|
111
|
+
minitest (5.25.4)
|
112
|
+
mutex_m (0.3.0)
|
113
|
+
net-http-digest_auth (1.4.1)
|
114
|
+
net-http-persistent (4.0.5)
|
115
|
+
connection_pool (~> 2.2)
|
116
|
+
nkf (0.2.0)
|
117
|
+
nokogiri (1.18.2-arm64-darwin)
|
118
|
+
racc (~> 1.4)
|
119
|
+
nokogiri (1.18.2-x86_64-darwin)
|
120
|
+
racc (~> 1.4)
|
121
|
+
ox (2.14.20)
|
122
|
+
bigdecimal (>= 3.0)
|
123
|
+
parallel (1.26.3)
|
124
|
+
parser (3.3.7.0)
|
125
|
+
ast (~> 2.4.1)
|
126
|
+
racc
|
127
|
+
pry (0.14.2)
|
128
|
+
coderay (~> 1.1)
|
129
|
+
method_source (~> 1.0)
|
130
|
+
pry-byebug (3.10.1)
|
131
|
+
byebug (~> 11.0)
|
132
|
+
pry (>= 0.13, < 0.15)
|
133
|
+
public_suffix (6.0.1)
|
134
|
+
racc (1.8.1)
|
135
|
+
rainbow (3.1.1)
|
136
|
+
rake (13.1.0)
|
137
|
+
regexp_parser (2.10.0)
|
138
|
+
rexml (3.4.0)
|
139
|
+
rspec (3.12.0)
|
140
|
+
rspec-core (~> 3.12.0)
|
141
|
+
rspec-expectations (~> 3.12.0)
|
142
|
+
rspec-mocks (~> 3.12.0)
|
143
|
+
rspec-core (3.12.2)
|
144
|
+
rspec-support (~> 3.12.0)
|
145
|
+
rspec-expectations (3.12.3)
|
146
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
147
|
+
rspec-support (~> 3.12.0)
|
148
|
+
rspec-mocks (3.12.6)
|
149
|
+
diff-lcs (>= 1.2.0, < 2.0)
|
150
|
+
rspec-support (~> 3.12.0)
|
151
|
+
rspec-support (3.12.1)
|
152
|
+
rubocop (1.70.0)
|
153
|
+
json (~> 2.3)
|
154
|
+
language_server-protocol (>= 3.17.0)
|
155
|
+
parallel (~> 1.10)
|
156
|
+
parser (>= 3.3.0.2)
|
157
|
+
rainbow (>= 2.2.2, < 4.0)
|
158
|
+
regexp_parser (>= 2.9.3, < 3.0)
|
159
|
+
rubocop-ast (>= 1.36.2, < 2.0)
|
160
|
+
ruby-progressbar (~> 1.7)
|
161
|
+
unicode-display_width (>= 2.4.0, < 4.0)
|
162
|
+
rubocop-ast (1.37.0)
|
163
|
+
parser (>= 3.3.1.0)
|
164
|
+
rubocop-performance (1.23.1)
|
165
|
+
rubocop (>= 1.48.1, < 2.0)
|
166
|
+
rubocop-ast (>= 1.31.1, < 2.0)
|
167
|
+
ruby-progressbar (1.13.0)
|
168
|
+
rubyntlm (0.6.5)
|
169
|
+
base64
|
170
|
+
securerandom (0.4.1)
|
171
|
+
simplecov (0.22.0)
|
172
|
+
docile (~> 1.1)
|
173
|
+
simplecov-html (~> 0.11)
|
174
|
+
simplecov_json_formatter (~> 0.1)
|
175
|
+
simplecov-html (0.13.1)
|
176
|
+
simplecov_json_formatter (0.1.4)
|
177
|
+
standard (1.44.0)
|
178
|
+
language_server-protocol (~> 3.17.0.2)
|
179
|
+
lint_roller (~> 1.0)
|
180
|
+
rubocop (~> 1.70.0)
|
181
|
+
standard-custom (~> 1.0.0)
|
182
|
+
standard-performance (~> 1.6)
|
183
|
+
standard-custom (1.0.2)
|
184
|
+
lint_roller (~> 1.0)
|
185
|
+
rubocop (~> 1.50)
|
186
|
+
standard-performance (1.6.0)
|
187
|
+
lint_roller (~> 1.1)
|
188
|
+
rubocop-performance (~> 1.23.0)
|
189
|
+
thor (1.3.2)
|
190
|
+
tzinfo (2.0.6)
|
191
|
+
concurrent-ruby (~> 1.0)
|
192
|
+
unicode-display_width (3.1.4)
|
193
|
+
unicode-emoji (~> 4.0, >= 4.0.4)
|
194
|
+
unicode-emoji (4.0.4)
|
195
|
+
webmock (3.19.1)
|
196
|
+
addressable (>= 2.8.0)
|
197
|
+
crack (>= 0.3.2)
|
198
|
+
hashdiff (>= 0.4.0, < 2.0.0)
|
199
|
+
webrick (1.9.1)
|
200
|
+
webrobots (0.1.2)
|
201
|
+
zeitwerk (2.6.12)
|
202
|
+
|
203
|
+
PLATFORMS
|
204
|
+
arm64-darwin-21
|
205
|
+
arm64-darwin-23
|
206
|
+
x86_64-darwin-21
|
207
|
+
|
208
|
+
DEPENDENCIES
|
209
|
+
bundler (~> 2.3)
|
210
|
+
factory_bot (~> 6.5)
|
211
|
+
pry-byebug (~> 3.10)
|
212
|
+
rake (~> 13.0)
|
213
|
+
rspec (~> 3.0)
|
214
|
+
simplecov (~> 0.20)
|
215
|
+
standard (~> 1.10)
|
216
|
+
webmock (~> 3.0)
|
217
|
+
y_fantasy!
|
218
|
+
|
219
|
+
BUNDLED WITH
|
220
|
+
2.3.7
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2022 eaylward8
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
# YFantasy
|
2
|
+
|
3
|
+
A Ruby wrapper for the Yahoo Fantasy Sports API.
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
gem 'y_fantasy'
|
11
|
+
```
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle install
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install y_fantasy
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TBD
|
24
|
+
|
25
|
+
## Development
|
26
|
+
|
27
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run `bundle exec rspec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
28
|
+
|
29
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and the created tag, and push the `.gem` file to [rubygems.org](https://rubygems.org).
|
30
|
+
|
31
|
+
## Contributing
|
32
|
+
|
33
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/eaylward8/y_fantasy. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [code of conduct](https://github.com/eaylward8/y_fantasy/blob/master/CODE_OF_CONDUCT.md).
|
34
|
+
|
35
|
+
## License
|
36
|
+
|
37
|
+
The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
|
38
|
+
|
39
|
+
## Code of Conduct
|
40
|
+
|
41
|
+
Everyone interacting in the YFantasy project's codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/eaylward8/y_fantasy/blob/master/CODE_OF_CONDUCT.md).
|
data/Rakefile
ADDED
data/exe/y_fantasy
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "net/http"
|
5
|
+
require "uri"
|
6
|
+
require "mechanize"
|
7
|
+
require "timeout"
|
8
|
+
|
9
|
+
module YFantasy
|
10
|
+
module Api
|
11
|
+
class Authentication
|
12
|
+
YAHOO_CLIENT_ID = YFantasy.config.yahoo_client_id
|
13
|
+
YAHOO_CLIENT_SECRET = YFantasy.config.yahoo_client_secret
|
14
|
+
YAHOO_USERNAME = YFantasy.config.yahoo_username
|
15
|
+
YAHOO_PASSWORD = YFantasy.config.yahoo_password
|
16
|
+
|
17
|
+
REQUEST_AUTH_URL = "https://api.login.yahoo.com/oauth2/request_auth?client_id=#{YAHOO_CLIENT_ID}&redirect_uri=oob&response_type=code"
|
18
|
+
GET_TOKEN_URL = "https://api.login.yahoo.com/oauth2/get_token"
|
19
|
+
|
20
|
+
MSG_SEPARATOR = "#" * 50
|
21
|
+
|
22
|
+
# NOTE: access token expires in 1 hour (3600 seconds)
|
23
|
+
|
24
|
+
class << self
|
25
|
+
# TODO: I don't think any of these readers are needed besides access_token. Maybe the error ones?
|
26
|
+
attr_reader :access_token, :expires_at, :refresh_token, :error_type, :error_desc
|
27
|
+
|
28
|
+
def authenticate
|
29
|
+
return true if access_token_valid?
|
30
|
+
|
31
|
+
if YFantasy.config.yahoo_refresh_token
|
32
|
+
@refresh_token = YFantasy.config.yahoo_refresh_token
|
33
|
+
end
|
34
|
+
|
35
|
+
refresh_token? ? authenticate_with_refresh_token : authenticate_with_code
|
36
|
+
end
|
37
|
+
|
38
|
+
def access_token_valid?
|
39
|
+
!!access_token && !!expires_at && Time.now.to_i < expires_at
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def authenticate_with_refresh_token
|
45
|
+
puts "auth with refresh token"
|
46
|
+
response = post(GET_TOKEN_URL, post_data("refresh_token", refresh_token: refresh_token))
|
47
|
+
handle_response(response)
|
48
|
+
end
|
49
|
+
|
50
|
+
def authenticate_with_code
|
51
|
+
puts "auth with code"
|
52
|
+
code = YFantasy.config.automate_login ? get_auth_code_automated : get_auth_code_manual
|
53
|
+
response = post(GET_TOKEN_URL, post_data("authorization_code", code: code))
|
54
|
+
handle_response(response, print_tokens: !YFantasy.config.automate_login)
|
55
|
+
end
|
56
|
+
|
57
|
+
def handle_response(response, print_tokens: false)
|
58
|
+
body = JSON.parse(response.body)
|
59
|
+
|
60
|
+
case response
|
61
|
+
when Net::HTTPSuccess
|
62
|
+
display_token_message(body) if print_tokens # Display token data to user after manual login
|
63
|
+
set_token_data(body["access_token"], body["expires_in"], body["refresh_token"])
|
64
|
+
clear_error_data
|
65
|
+
true
|
66
|
+
when Net::HTTPClientError, Net::HTTPServerError
|
67
|
+
set_error_data(body["error"], body["error_description"])
|
68
|
+
false
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
def display_token_message(body)
|
73
|
+
puts "\nAccess token: #{body["access_token"]}"
|
74
|
+
puts "\nAccess token expires in: #{body["expires_in"]} seconds"
|
75
|
+
puts "\nRefresh token: #{body["refresh_token"]}"
|
76
|
+
puts "\nThe refresh token is long-lived and can be used to re-authenticate without the need for credentials."
|
77
|
+
puts "Store it as an ENV variable called YAHOO_REFRESH_TOKEN or save it to a database."
|
78
|
+
puts "Then, in your application, set YFantasy.config.yahoo_refresh_token = <your_refresh_token>"
|
79
|
+
puts "If the yahoo_refresh_token config is set, YFantasy will use it to obtain a new access token when needed."
|
80
|
+
puts "\nRead more about the Yahoo OAuth 2.0 Flow: https://developer.yahoo.com/oauth2/guide/flows_authcode/\n\n"
|
81
|
+
puts MSG_SEPARATOR
|
82
|
+
puts "# YFantasy Manual Auth Code Flow - END #"
|
83
|
+
puts MSG_SEPARATOR
|
84
|
+
end
|
85
|
+
|
86
|
+
def get_auth_code_manual
|
87
|
+
return if YFantasy.config.automate_login
|
88
|
+
|
89
|
+
puts MSG_SEPARATOR
|
90
|
+
puts "# YFantasy Manual Auth Code Flow - START #"
|
91
|
+
puts MSG_SEPARATOR
|
92
|
+
puts "\nGo to this URL and log into your Yahoo Account:"
|
93
|
+
puts REQUEST_AUTH_URL
|
94
|
+
puts "\nAfter logging in, copy and paste the code here:"
|
95
|
+
Timeout.timeout(YFantasy.config.manual_login_timeout_seconds) do
|
96
|
+
code = $stdin.gets.chomp
|
97
|
+
puts "\nReceived code: #{code}. Attempting to retrieve access token and refresh token...\n"
|
98
|
+
code
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
# :nocov:
|
103
|
+
def get_auth_code_automated
|
104
|
+
return if YFantasy.config.automate_login == false
|
105
|
+
|
106
|
+
# TODO: error handling
|
107
|
+
agent = Mechanize.new
|
108
|
+
login_page1 = agent.get(REQUEST_AUTH_URL)
|
109
|
+
username_form = login_page1.forms.first
|
110
|
+
# Enter username
|
111
|
+
username_form.username = YAHOO_USERNAME
|
112
|
+
# Click "Next" button
|
113
|
+
login_page2 = agent.submit(username_form, username_form.buttons.first)
|
114
|
+
pw_form = login_page2.forms.first
|
115
|
+
# Enter password
|
116
|
+
pw_form.password = YAHOO_PASSWORD
|
117
|
+
# Click "Verify password"
|
118
|
+
auth_code_page = agent.submit(pw_form, pw_form.button("verifyPassword"))
|
119
|
+
# Extract code
|
120
|
+
# TODO: sometimes below line fails w/ undefined method [] for nil, add a retry?
|
121
|
+
match = auth_code_page.uri.query.match(/code=(?<code>\w+)/)
|
122
|
+
return match[:code] if match
|
123
|
+
|
124
|
+
raise Error.new("Failed to extract auth code")
|
125
|
+
end
|
126
|
+
# :nocov:
|
127
|
+
|
128
|
+
def post(url, data)
|
129
|
+
Net::HTTP.post(URI(url), URI.encode_www_form(data), post_headers)
|
130
|
+
end
|
131
|
+
|
132
|
+
def post_headers
|
133
|
+
{
|
134
|
+
"Authorization" => "Basic #{basic_auth_token}",
|
135
|
+
"Content-Type" => "application/x-www-form-urlencoded"
|
136
|
+
}
|
137
|
+
end
|
138
|
+
|
139
|
+
def post_data(grant_type, refresh_token: nil, code: nil)
|
140
|
+
{
|
141
|
+
"grant_type" => grant_type,
|
142
|
+
"redirect_uri" => "oob",
|
143
|
+
"refresh_token" => refresh_token,
|
144
|
+
"code" => code
|
145
|
+
}
|
146
|
+
end
|
147
|
+
|
148
|
+
def basic_auth_token
|
149
|
+
@basic_auth_token ||= Base64.strict_encode64("#{YAHOO_CLIENT_ID}:#{YAHOO_CLIENT_SECRET}")
|
150
|
+
end
|
151
|
+
|
152
|
+
def set_token_data(access_token, expires_in, refresh_token)
|
153
|
+
@access_token = access_token
|
154
|
+
@expires_at = Time.now.to_i + expires_in.to_i
|
155
|
+
@refresh_token = refresh_token
|
156
|
+
end
|
157
|
+
|
158
|
+
def set_error_data(type, desc)
|
159
|
+
@error_type = type
|
160
|
+
@error_desc = desc
|
161
|
+
end
|
162
|
+
|
163
|
+
def clear_error_data
|
164
|
+
@error_type = nil
|
165
|
+
@error_desc = nil
|
166
|
+
end
|
167
|
+
|
168
|
+
def refresh_token?
|
169
|
+
@refresh_token && @refresh_token.length > 0
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class Error < StandardError
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "json"
|
4
|
+
require "net/http"
|
5
|
+
require "uri"
|
6
|
+
require "ox"
|
7
|
+
|
8
|
+
module YFantasy
|
9
|
+
module Api
|
10
|
+
class Client
|
11
|
+
@@retry = true
|
12
|
+
|
13
|
+
def self.get(resource, keys: [], game_codes: [], subresources: [], **options)
|
14
|
+
new.get(resource, keys: keys, game_codes: game_codes, subresources: subresources, **options)
|
15
|
+
rescue Client::Error, Authentication::Error
|
16
|
+
if @@retry
|
17
|
+
@@retry = false
|
18
|
+
retry
|
19
|
+
end
|
20
|
+
|
21
|
+
raise
|
22
|
+
end
|
23
|
+
|
24
|
+
def initialize
|
25
|
+
authenticate
|
26
|
+
end
|
27
|
+
|
28
|
+
# NOTE: URL construction needs to be sophisticated enough to know when it should use "out" params vs
|
29
|
+
# nested subresources.
|
30
|
+
# E.g. /league/380.l.190823/teams/stats
|
31
|
+
# this is how you get 1 league with all teams, and all stats for each team
|
32
|
+
# teams is a subresource of league
|
33
|
+
# stats is a subresource of teams
|
34
|
+
#
|
35
|
+
#
|
36
|
+
# This fails because "stats" is not a subresource of league: /league/380.l.190823;out=teams/stats
|
37
|
+
|
38
|
+
def get(resource, keys: [], game_codes: [], subresources: [], **options)
|
39
|
+
refresh_access_token_if_needed
|
40
|
+
|
41
|
+
url = UrlBuilder.new(resource, keys: keys, game_codes: game_codes, subresources: subresources, **options).build
|
42
|
+
|
43
|
+
# puts "\n Client#get #{url} \n" # TODO: remove
|
44
|
+
|
45
|
+
response = Net::HTTP.get_response(URI(url), "Authorization" => "Bearer #{@access_token}")
|
46
|
+
body = response.body
|
47
|
+
|
48
|
+
case response
|
49
|
+
when Net::HTTPSuccess
|
50
|
+
Ox.load(body, mode: :hash_no_attrs).fetch(:fantasy_content)
|
51
|
+
when Net::HTTPClientError
|
52
|
+
error = Ox.load(body, mode: :hash_no_attrs)
|
53
|
+
error_msg = error.dig(:"yahoo:error", :"yahoo:description") || error.dig(:error, :description)
|
54
|
+
msg = "#{response.code}: #{error_msg}"
|
55
|
+
raise YFantasy::Api::Client::Error.new(msg)
|
56
|
+
else
|
57
|
+
raise YFantasy::Api::Client::Error.new("An unknown error occurred")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def authenticate
|
62
|
+
if YFantasy::Api::Authentication.authenticate
|
63
|
+
@access_token = YFantasy::Api::Authentication.access_token
|
64
|
+
@refresh_token = YFantasy::Api::Authentication.refresh_token
|
65
|
+
else
|
66
|
+
@error_type = YFantasy::Api::Authentication.error_type
|
67
|
+
@error_desc = YFantasy::Api::Authentication.error_desc
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
def refresh_access_token_if_needed
|
72
|
+
return if YFantasy::Api::Authentication.access_token_valid?
|
73
|
+
|
74
|
+
authenticate
|
75
|
+
end
|
76
|
+
|
77
|
+
class Error < StandardError
|
78
|
+
def initialize(msg = "")
|
79
|
+
super
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|