mangadex 5.3.3.1 → 5.3.3.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 +4 -4
- data/Gemfile.lock +2 -2
- data/bin/console +4 -3
- data/docs/authentication.md +226 -0
- data/docs/context.md +93 -0
- data/lib/config.rb +50 -0
- data/lib/errors.rb +42 -0
- data/lib/mangadex/README.md +6 -2
- data/lib/mangadex/api/response.rb +5 -2
- data/lib/mangadex/api/user.rb +51 -7
- data/lib/mangadex/api.rb +0 -1
- data/lib/mangadex/auth.rb +42 -13
- data/lib/mangadex/internal/context.rb +141 -0
- data/lib/mangadex/internal/request.rb +8 -6
- data/lib/mangadex/internal.rb +1 -0
- data/lib/mangadex/manga.rb +5 -3
- data/lib/mangadex/storage/basic.rb +18 -0
- data/lib/mangadex/storage/memory.rb +23 -0
- data/lib/mangadex/storage/none.rb +9 -0
- data/lib/mangadex/storage.rb +3 -0
- data/lib/mangadex/user.rb +2 -2
- data/lib/mangadex/version.rb +1 -1
- data/lib/mangadex.rb +25 -12
- metadata +11 -3
- data/lib/mangadex/api/context.rb +0 -137
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 1d01b0fac553138d7ac86d1313737d86ba5415ee88e2ca6de08787402be7e3fb
|
|
4
|
+
data.tar.gz: 643923fe081fb046f6feec8ab4ce945a358a2fc9d66681d732a272b9bad2223d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: a196f878697f4c7f1a65178cee78a6d36b1c23c9466d0b997ee3ece51bbc0497db0fc0a3f6f16361a1d7c7518733afe7357bbc2a5cb785deaabe07d45df1aec5
|
|
7
|
+
data.tar.gz: b657aa48b952ae4b3136bc52c6e5f96f7d3287ae26d08089a0aaf294313a9662d1dfa2ca35477702b4269ad42af53f2b97e7e9eee575142b0c27b295c789de78
|
data/Gemfile.lock
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
PATH
|
|
2
2
|
remote: .
|
|
3
3
|
specs:
|
|
4
|
-
mangadex (5.3.3.
|
|
4
|
+
mangadex (5.3.3.2)
|
|
5
5
|
activesupport (~> 6.1)
|
|
6
6
|
psych (~> 4.0.1)
|
|
7
7
|
rest-client (~> 2.1)
|
|
@@ -82,7 +82,7 @@ GEM
|
|
|
82
82
|
smart_properties (1.16.3)
|
|
83
83
|
sorbet (0.5.9152)
|
|
84
84
|
sorbet-static (= 0.5.9152)
|
|
85
|
-
sorbet-runtime (0.5.
|
|
85
|
+
sorbet-runtime (0.5.9189)
|
|
86
86
|
sorbet-static (0.5.9152-universal-darwin-20)
|
|
87
87
|
sorbet-static (0.5.9152-x86_64-linux)
|
|
88
88
|
tilt (2.0.10)
|
data/bin/console
CHANGED
|
@@ -3,13 +3,14 @@
|
|
|
3
3
|
require "bundler/setup"
|
|
4
4
|
require "mangadex"
|
|
5
5
|
|
|
6
|
-
username, password = [
|
|
6
|
+
username, password, email = [
|
|
7
7
|
ENV['MD_USERNAME'],
|
|
8
8
|
ENV['MD_PASSWORD'],
|
|
9
|
+
ENV['MD_EMAIL'],
|
|
9
10
|
]
|
|
10
11
|
|
|
11
|
-
if username && password
|
|
12
|
-
Mangadex::Auth.login(username, password)
|
|
12
|
+
if (username || email) && password
|
|
13
|
+
Mangadex::Auth.login(username: username, email: email, password: password)
|
|
13
14
|
end
|
|
14
15
|
|
|
15
16
|
# You can add fixtures and/or initialization code here to make experimenting
|
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
# 🔒 Authenticating with Mangadex
|
|
2
|
+
|
|
3
|
+
## Beforehand
|
|
4
|
+
|
|
5
|
+
Any actions that can be performed on the mangadex site as a non authenticated user will not require a user to be logged
|
|
6
|
+
in. Authentication on Mangadex, as per version
|
|
7
|
+
<a href="https://rubygems.org/gems/mangadex"><img src="https://badgen.net/rubygems/v/mangadex" /></a>
|
|
8
|
+
will need an `Authorization` HTTP header to be present.
|
|
9
|
+
|
|
10
|
+
You can check details on the Mangadex API here: https://api.mangadex.org/docs.html#section/Authentication
|
|
11
|
+
|
|
12
|
+
## Authentication & Authorization flow
|
|
13
|
+
|
|
14
|
+
The authentication flow happens as such:
|
|
15
|
+
|
|
16
|
+
1. You login with your email or username, and password.
|
|
17
|
+
2. Upon successfully _authenticating_ your account, you will be given a `session` and a `refresh` tokens.
|
|
18
|
+
3. You must use the `session` token to _authorize_ your account to perform certain actions (ie: create resources, etc.)
|
|
19
|
+
4. You must use the `refresh` token to refresh the `session` when expired.
|
|
20
|
+
|
|
21
|
+
> - The `session` token expires **15 minutes** after been granted.
|
|
22
|
+
> - The `refresh` token refreshes **1 month** after been granted.
|
|
23
|
+
|
|
24
|
+
## Authentication using `mangadex` gem
|
|
25
|
+
|
|
26
|
+
Now that the basics of authentication have been covered, let's go over how it's done on with the gem.
|
|
27
|
+
|
|
28
|
+
### Logging in
|
|
29
|
+
|
|
30
|
+
It's simple to login, whether with your email address or your email address:
|
|
31
|
+
|
|
32
|
+
```ruby
|
|
33
|
+
# With your username
|
|
34
|
+
Mangadex::Auth.login(username: username, password: password)
|
|
35
|
+
|
|
36
|
+
# With your email address
|
|
37
|
+
Mangadex::Auth.login(email: email, password: password)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Upon successful authentication, an instance of `Mangadex::Api::User` will be returned:
|
|
41
|
+
|
|
42
|
+
```ruby
|
|
43
|
+
user = Mangadex::Auth.login(...)
|
|
44
|
+
|
|
45
|
+
# The session token, valid for 15 minutes (String).
|
|
46
|
+
user.session
|
|
47
|
+
|
|
48
|
+
# The refresh token, valid for 1 month (String)
|
|
49
|
+
user.refresh
|
|
50
|
+
|
|
51
|
+
# The logged in user's ID (String) (formatted as a UUID)
|
|
52
|
+
user.mangadex_user_id
|
|
53
|
+
|
|
54
|
+
# Time at the which user.session becomes invalid (Time)
|
|
55
|
+
user.session_valid_until
|
|
56
|
+
|
|
57
|
+
# Miscellaneaous data. When logging in, it's an instance of Mangadex::User
|
|
58
|
+
# (response from the server)
|
|
59
|
+
user.data
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
# Refreshes the tokens now (Boolean)
|
|
64
|
+
user.refresh!
|
|
65
|
+
|
|
66
|
+
# Refreshes the tokens if expired, then return user itself (Mangadex::Api::User)
|
|
67
|
+
user.with_valid_session
|
|
68
|
+
|
|
69
|
+
# Returns if user.session has expired (Boolean)
|
|
70
|
+
user.session_expired?
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
If there's an error, `Mangadex::Errors::AuthenticationError` will be raised. Here's how to handle that scenario:
|
|
74
|
+
|
|
75
|
+
```ruby
|
|
76
|
+
def login(email, password)
|
|
77
|
+
Mangadex::Auth.login(email: email, password: password)
|
|
78
|
+
rescue Mangadex::Errors::AuthenticationError => error
|
|
79
|
+
response = error.response
|
|
80
|
+
|
|
81
|
+
# A list of detailed errors from Mangadex. (Array of
|
|
82
|
+
# Mangadex::Api::Response::Error)
|
|
83
|
+
response.errors.each do |error|
|
|
84
|
+
puts error.id
|
|
85
|
+
puts error.status
|
|
86
|
+
puts error.title
|
|
87
|
+
puts error.detail
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
### Authenticating requests
|
|
93
|
+
|
|
94
|
+
When the user is logged in, all subsequent requests _should_ be authenticated. Here's an example to retrieve a list of manga that the logged in user is _reading_ at the moment:
|
|
95
|
+
|
|
96
|
+
```ruby
|
|
97
|
+
user = Mangadex::Auth.login(...)
|
|
98
|
+
response = Mangadex::Manga.all_reading_status('reading')
|
|
99
|
+
manga_ids = response['statuses'].keys
|
|
100
|
+
|
|
101
|
+
reading_now = Mangadex::Manga.list(ids: manga_ids)
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
If for whatever reason you want to a request not to be authenticated, you can do something like:
|
|
105
|
+
|
|
106
|
+
```ruby
|
|
107
|
+
Mangadex.context.without_user do
|
|
108
|
+
# your mangadex request(s) here
|
|
109
|
+
end
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
When logging in, the user's session information will be persisted in the storage. See below [for more details]().
|
|
113
|
+
|
|
114
|
+
### Logging out
|
|
115
|
+
|
|
116
|
+
Logging the user out is very easy:
|
|
117
|
+
|
|
118
|
+
```ruby
|
|
119
|
+
Mangadex::Auth.logout
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
Here, the `user`'s session will be revoked on Mangadex. It will try to delete the user's session. `Mangadex::Auth.logout` outside of the `with_user` block will not do anything.
|
|
123
|
+
|
|
124
|
+
This action also clears the context's user and the storage info associated to this user.
|
|
125
|
+
|
|
126
|
+
## Persisting the user session: storage stragegies
|
|
127
|
+
|
|
128
|
+
### What is this?
|
|
129
|
+
|
|
130
|
+
Using this gem should help you a little bit managing tokens. By default, the gem stores the following information in memory:
|
|
131
|
+
|
|
132
|
+
- For a particular user ID:
|
|
133
|
+
- User session
|
|
134
|
+
- User refresh token
|
|
135
|
+
- User session expiry date
|
|
136
|
+
|
|
137
|
+
### Why is this a thing?
|
|
138
|
+
|
|
139
|
+
Good question. We want to make session management with this gem as easy as possible. The session is used to retrieve a valid logged in user. Here's how it works:
|
|
140
|
+
|
|
141
|
+
- When the user logs in, the refresh token, the session token (as well as it's expired date) are stored for that user
|
|
142
|
+
- When requesting the tokens for the user, a `Mangadex::Api::User` is created with refreshed tokens (if expired).
|
|
143
|
+
- When logging out, if implemented by you, the users's session details are deleted.
|
|
144
|
+
|
|
145
|
+
Here's you retrieve a user from your storage at any point:
|
|
146
|
+
|
|
147
|
+
```ruby
|
|
148
|
+
mangadex_user_id = '...'
|
|
149
|
+
Mangadex::Api::User.from_storage(mangadex_user_id)
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
It's up to you to decide how you store the user ID. You don't need to worry about saving the storage, the gem takes care of that for you.
|
|
153
|
+
|
|
154
|
+
### Ok, ok. How can I use my own strategy?
|
|
155
|
+
|
|
156
|
+
By default, this gem ships with `Mangagex::Storage::Memory` which corresponds to the in-memory storage. This should be fine if you don't care much about persisting the user session at any point.
|
|
157
|
+
|
|
158
|
+
No assumptions can be made on which storage service you use. That is 100% up to you how the information is stored. Let's say you want to use [redis](https://github.com/redis/redis) for session instead of the default memory storage stragery:
|
|
159
|
+
|
|
160
|
+
```ruby
|
|
161
|
+
require 'redis'
|
|
162
|
+
|
|
163
|
+
class BasicRedisStragery < Mangadex::Storage::Basic
|
|
164
|
+
# Must be implemented
|
|
165
|
+
def get(mangadex_user_id, key)
|
|
166
|
+
client.hget(mangadex_user_id, key)
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
# Must be implemented
|
|
170
|
+
def set(mangadex_user_id, key, value)
|
|
171
|
+
client.hset(mangadex_user_id, key, value)
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
# Optional - It's a nice-to-have, especially for logging out.
|
|
175
|
+
def clear(mangadex_user_id)
|
|
176
|
+
client.del(mangadex_user_id)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
private
|
|
180
|
+
|
|
181
|
+
def client
|
|
182
|
+
@client ||= Redis.new(url: 'redis://localhost')
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
|
|
186
|
+
# Let the gem know which strategy needs to be used
|
|
187
|
+
Mangadex.configuration.storage_class = BasicRedisStragery
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
> On Rails, you can put this inside an initializer. Example: `config/initializers/mangadex.rb`.
|
|
191
|
+
|
|
192
|
+
The snippet of code is an example of how a storage strategy is implemented. It's important to make sure that neither `get` nor `set` raise exceptions.
|
|
193
|
+
|
|
194
|
+
> - We recommend using redis if you're developing a web app or a bot where authentication is involed.
|
|
195
|
+
> - You can even use a the filesystem if you're building a CLI (command line interface).
|
|
196
|
+
> - We **do not** recommend using SQL at the moment. This might be hard on your app's performance...
|
|
197
|
+
|
|
198
|
+
### Can I opt-out?
|
|
199
|
+
|
|
200
|
+
Of course. Set `Mangadex::Storage::None` as the prefered strategy:
|
|
201
|
+
|
|
202
|
+
```ruby
|
|
203
|
+
# Either
|
|
204
|
+
Mangadex.configure do |config|
|
|
205
|
+
config.storage_class = Mangadex::Storage::None
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Or
|
|
209
|
+
Mangadex.configuration.storage_class = Mangadex::Storage::None
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
## About content ratings
|
|
213
|
+
|
|
214
|
+
Each manga/chapter has a content rating (`safe`, `suggestive`, `erotica` and `pornographic`). It might be worth filtering certain titles depending on the audiance. By default, Mangadex filters out every `pornographic` entry.
|
|
215
|
+
|
|
216
|
+
Please note that content rating is not tied to the user at the moment on Mangadex. So it was decided **not** to add this responsiblity on this gem. Instead, the content ratings can be specified on context gem as well, like this:
|
|
217
|
+
|
|
218
|
+
```ruby
|
|
219
|
+
# Everything but "suggestive" content - this is an example :p
|
|
220
|
+
mangas = Mangadex::Api::Content.allow_content_rating('safe', 'erotica', 'pornographic') do
|
|
221
|
+
response = Mangadex::Manga.list
|
|
222
|
+
response.data
|
|
223
|
+
end
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The advantage of this approach is that you don't have to set the `content_rating` param yourself everywhere.
|
data/docs/context.md
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Mangadex contexts
|
|
2
|
+
|
|
3
|
+
There is a concept of concepts in this gem. This is there for you to access certain variables at any point in your app.
|
|
4
|
+
|
|
5
|
+
## User
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
Mangadex.context.user # => #<Mangadex::Api::User ...>
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This is set to `nil` before logging in.
|
|
12
|
+
|
|
13
|
+
When logging in, the user is stored in the context so that subsequent requests are set to be authenticated with this user.
|
|
14
|
+
|
|
15
|
+
```ruby
|
|
16
|
+
Mangadex::Auth.login(...)
|
|
17
|
+
Mangadex.context.user.nil? # => false
|
|
18
|
+
|
|
19
|
+
custom_lists = Mangadex::CustomList.list
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
If you're not logged in, `Mangadex::Errors::UnauthorizedError` will be raised for any request that requires you to be logged in and authorized to perform a certain account.
|
|
23
|
+
|
|
24
|
+
You can set the user in a temporary context:
|
|
25
|
+
|
|
26
|
+
```ruby
|
|
27
|
+
Mangadex.context.user # => nil
|
|
28
|
+
|
|
29
|
+
temp_user = Mangadex::Api::User.new(mangadex_user_id: 'blabla')
|
|
30
|
+
Mangadex.context.with_user(temp_user) do
|
|
31
|
+
Mangadex.context.user # => #<Mangadex::Api::User mangadex_user_id="blabla">
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
Mangadex.context.user # => nil
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
More info on authentication [here]().
|
|
38
|
+
|
|
39
|
+
## Content rating
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
Mangadex.context.allowed_content_ratings # => [#<Mangadex::ContentRating ...>, ...]
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Content ratings are not tied to the user. When set, requests that accept a [`content_rating`](https://api.mangadex.org/docs.html#section/Static-data/Manga-content-rating) parameter, this parameter will be set to `Mangadex.context.allowed_content_ratings` if nothing is specified.
|
|
46
|
+
|
|
47
|
+
By default, `safe`, `suggestive` and `erotica` are used on Mangadex. But however, if you want to allow all content ratings, you could do something like:
|
|
48
|
+
|
|
49
|
+
```ruby
|
|
50
|
+
Mangadex.context.allow_content_ratings('safe', 'suggestive', 'erotica', 'pornographic')
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Then, a query to fetch manga will make the following request:
|
|
54
|
+
|
|
55
|
+
```ruby
|
|
56
|
+
Mangadex::Manga.list
|
|
57
|
+
# GET https://api.mangadex.org/manga?contentRating%5B%5D=safe&contentRating%5B%5D=suggestive&contentRating%5B%5D=erotica&contentRating%5B%5D=pornographic
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
You can also use temporary content ratings:
|
|
61
|
+
|
|
62
|
+
```ruby
|
|
63
|
+
# old content ratings
|
|
64
|
+
Mangadex.context.allow_content_ratings('safe', 'suggestive', 'erotica', 'pornographic') do
|
|
65
|
+
# temporary content ratings
|
|
66
|
+
Mangadex::Manga.list
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
# back to old content ratings
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Tags
|
|
73
|
+
|
|
74
|
+
Get the list of possible tags on Mangadex:
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
Mangadex.context.tags
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### API version
|
|
81
|
+
|
|
82
|
+
Get the current Mangadex's latest API version
|
|
83
|
+
|
|
84
|
+
```ruby
|
|
85
|
+
Mangadex.context.version
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
A warning message will be printed if there's a mismatch between Mangadex's API version and the gem version. Example:
|
|
89
|
+
|
|
90
|
+
| Mangadex's API version | The gem's version | Result |
|
|
91
|
+
| ---------------------- | ----------------- | ------- |
|
|
92
|
+
| 5.3.3 | 5.3.3.1 | OK |
|
|
93
|
+
| 5.3.4 | 5.3.3.4 | Warning |
|
data/lib/config.rb
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
module Mangadex
|
|
4
|
+
class Config
|
|
5
|
+
extend T::Sig
|
|
6
|
+
|
|
7
|
+
# Class used to persist users
|
|
8
|
+
# Must respond to: :session, :refresh, :mangadex_user_id
|
|
9
|
+
sig { returns(Class) }
|
|
10
|
+
attr_accessor :user_class
|
|
11
|
+
|
|
12
|
+
# Persisting strategy. See Mangadex::Storage::Base for more details.
|
|
13
|
+
sig { returns(Class) }
|
|
14
|
+
attr_accessor :storage_class
|
|
15
|
+
|
|
16
|
+
sig { returns(T::Array[ContentRating]) }
|
|
17
|
+
attr_accessor :default_content_ratings
|
|
18
|
+
|
|
19
|
+
sig { void }
|
|
20
|
+
def initialize
|
|
21
|
+
@user_class = Api::User
|
|
22
|
+
@storage_class = Storage::Memory
|
|
23
|
+
@default_content_ratings = ContentRating.parse(['safe', 'suggestive', 'erotica'])
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
sig { params(klass: Class).void }
|
|
27
|
+
def user_class=(klass)
|
|
28
|
+
missing_methods = [:session, :refresh, :mangadex_user_id] - klass.instance_methods
|
|
29
|
+
if missing_methods.empty?
|
|
30
|
+
@user_class = klass
|
|
31
|
+
else
|
|
32
|
+
raise ArgumentError, 'user_class must respond to :session, :refresh, :mangadex_user_id'
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
sig { params(content_ratings: T::Array[T.any(String, ContentRating)]).void }
|
|
37
|
+
def default_content_ratings=(content_ratings)
|
|
38
|
+
@default_content_ratings = ContentRating.parse(content_ratings)
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def storage_class=(klass)
|
|
42
|
+
@storage = nil
|
|
43
|
+
@storage_class = klass
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def storage
|
|
47
|
+
@storage ||= storage_class.new
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
end
|
data/lib/errors.rb
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
|
|
3
|
+
module Mangadex
|
|
4
|
+
module Errors
|
|
5
|
+
# Standard error class for this gem.
|
|
6
|
+
#
|
|
7
|
+
# @author thedrummeraki
|
|
8
|
+
# @since 0.6.0
|
|
9
|
+
class StandardError < ::StandardError
|
|
10
|
+
extend T::Sig
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
class UserNotLoggedIn < StandardError
|
|
14
|
+
sig { returns(String) }
|
|
15
|
+
def message
|
|
16
|
+
"You are not logged in. Use [Mangadex::Auth.login] to log in."
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
class AuthenticationError < StandardError
|
|
21
|
+
sig { returns(Mangadex::Api::Response) }
|
|
22
|
+
attr_accessor :response
|
|
23
|
+
|
|
24
|
+
sig { params(response: Mangadex::Api::Response).void }
|
|
25
|
+
def initialize(response)
|
|
26
|
+
@response = response
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
sig { returns(String) }
|
|
30
|
+
def message
|
|
31
|
+
"Your username or password may not be correct."
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
class UnauthorizedError < AuthenticationError
|
|
36
|
+
sig { returns(String) }
|
|
37
|
+
def message
|
|
38
|
+
"Oops, you are not authorized to make this call. Make sure you log in with the right account."
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/mangadex/README.md
CHANGED
|
@@ -3,12 +3,14 @@
|
|
|
3
3
|
This is documentation for the `Mangadex` module.
|
|
4
4
|
|
|
5
5
|
### Directory
|
|
6
|
+
|
|
6
7
|
#### Sub-modules
|
|
7
8
|
|
|
8
9
|
- [`Mangadex::Api`](#)
|
|
9
10
|
- [`Mangadex::Internal`](#)
|
|
10
11
|
|
|
11
12
|
#### Fetchable/Resources
|
|
13
|
+
|
|
12
14
|
- [`Mangadex::Artist`](#)
|
|
13
15
|
- [`Mangadex::Auth`](#mangadexauth)
|
|
14
16
|
- [`Mangadex::Author`](#)
|
|
@@ -25,6 +27,7 @@ This is documentation for the `Mangadex` module.
|
|
|
25
27
|
- [`Mangadex::User`](#)
|
|
26
28
|
|
|
27
29
|
#### Other classes
|
|
30
|
+
|
|
28
31
|
- [`Mangadex::MangadexObject`](#)
|
|
29
32
|
- [`Mangadex::Types`](#)
|
|
30
33
|
- [`Mangadex::Version`](#)
|
|
@@ -51,7 +54,7 @@ Mangadex::Auth.login(username, password)
|
|
|
51
54
|
```
|
|
52
55
|
|
|
53
56
|
Login with your username and password. Upon successful login, the user will be available in a context from
|
|
54
|
-
`Mangadex
|
|
57
|
+
`Mangadex.context.user`. This variable can be used anywhere in your application. More info [here](#).
|
|
55
58
|
|
|
56
59
|
> - Returns `Mangadex::Api::Response` if request fails.
|
|
57
60
|
> - Returns `true` if user is logged in.
|
|
@@ -86,8 +89,9 @@ Mangadex::Auth.refresh_token
|
|
|
86
89
|
```
|
|
87
90
|
|
|
88
91
|
Manually cause a token refresh.
|
|
92
|
+
|
|
89
93
|
> Please note that simply calling `Mangadex::Api::Content.user` ensures that the token is valid. More info [here](#).
|
|
90
94
|
|
|
91
95
|
> - Returns `nil` if user is not logged (ie: `Mangadex::Api::Content.user` is `nil`)
|
|
92
96
|
> - Returns `true` if the refresh is successful
|
|
93
|
-
> - Returns `false` if the refresh is [not successful](#).
|
|
97
|
+
> - Returns `false` if the refresh is [not successful](#).
|
|
@@ -41,8 +41,11 @@ module Mangadex
|
|
|
41
41
|
end
|
|
42
42
|
end
|
|
43
43
|
|
|
44
|
-
def errored?
|
|
45
|
-
Array(errors).any?
|
|
44
|
+
def errored?(status=nil)
|
|
45
|
+
errored = Array(errors).any?
|
|
46
|
+
return errored if status.nil?
|
|
47
|
+
|
|
48
|
+
errors.select { |error| error.status.to_s == status.to_s }.any?
|
|
46
49
|
end
|
|
47
50
|
|
|
48
51
|
def as_json(*)
|
data/lib/mangadex/api/user.rb
CHANGED
|
@@ -7,13 +7,13 @@ module Mangadex
|
|
|
7
7
|
attr_accessor :mangadex_user_id, :session, :refresh, :session_valid_until
|
|
8
8
|
attr_reader :data
|
|
9
9
|
|
|
10
|
-
sig { params(mangadex_user_id: String, session: T.nilable(String), refresh: T.nilable(String), data: T.untyped).void }
|
|
11
|
-
def initialize(mangadex_user_id
|
|
10
|
+
sig { params(mangadex_user_id: String, session: T.nilable(String), refresh: T.nilable(String), data: T.untyped, session_valid_until: T.nilable(Time)).void }
|
|
11
|
+
def initialize(mangadex_user_id:, session: nil, refresh: nil, data: nil, session_valid_until: nil)
|
|
12
12
|
raise ArgumentError, 'Missing mangadex_user_id' if mangadex_user_id.to_s.empty?
|
|
13
13
|
|
|
14
14
|
@mangadex_user_id = mangadex_user_id
|
|
15
15
|
@session = session
|
|
16
|
-
@session_valid_until = session ? Time.now + (14 * 60) : nil
|
|
16
|
+
@session_valid_until = session_valid_until ? session_valid_until : (session ? Time.now + (14 * 60) : nil)
|
|
17
17
|
@refresh = refresh
|
|
18
18
|
@data = data
|
|
19
19
|
end
|
|
@@ -24,9 +24,7 @@ module Mangadex
|
|
|
24
24
|
def refresh!
|
|
25
25
|
return false if refresh.nil?
|
|
26
26
|
|
|
27
|
-
response = Mangadex::
|
|
28
|
-
Mangadex::Internal::Request.post('/auth/refresh', payload: { token: refresh })
|
|
29
|
-
end
|
|
27
|
+
response = Mangadex::Internal::Request.post('/auth/refresh', payload: { token: refresh })
|
|
30
28
|
return false unless response['token']
|
|
31
29
|
|
|
32
30
|
@session_valid_until = Time.now + (14 * 60)
|
|
@@ -36,7 +34,7 @@ module Mangadex
|
|
|
36
34
|
true
|
|
37
35
|
end
|
|
38
36
|
|
|
39
|
-
sig { returns(
|
|
37
|
+
sig { returns(User) }
|
|
40
38
|
def with_valid_session
|
|
41
39
|
session_expired? && refresh!
|
|
42
40
|
self
|
|
@@ -48,6 +46,52 @@ module Mangadex
|
|
|
48
46
|
def session_expired?
|
|
49
47
|
@session_valid_until.nil? || @session_valid_until <= Time.now
|
|
50
48
|
end
|
|
49
|
+
|
|
50
|
+
sig { returns(T::Boolean) }
|
|
51
|
+
def persist
|
|
52
|
+
return false unless valid?
|
|
53
|
+
|
|
54
|
+
Mangadex.storage.set(mangadex_user_id, 'session', session) if session
|
|
55
|
+
Mangadex.storage.set(mangadex_user_id, 'refresh', refresh) if refresh
|
|
56
|
+
if session_valid_until
|
|
57
|
+
Mangadex.storage.set(mangadex_user_id, 'session_valid_until', session_valid_until.to_s)
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
true
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
sig { returns(T::Boolean) }
|
|
64
|
+
def valid?
|
|
65
|
+
!mangadex_user_id.nil? && !mangadex_user_id.strip.empty?
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
sig { params(mangadex_user_id: T.nilable(String)).returns(T.nilable(User)) }
|
|
69
|
+
def self.from_storage(mangadex_user_id)
|
|
70
|
+
return if mangadex_user_id.nil?
|
|
71
|
+
|
|
72
|
+
session = Mangadex.storage.get(mangadex_user_id, 'session')
|
|
73
|
+
refresh = Mangadex.storage.get(mangadex_user_id, 'refresh')
|
|
74
|
+
session_valid_until = Mangadex.storage.get(mangadex_user_id, 'session_valid_until')
|
|
75
|
+
|
|
76
|
+
user = if session || refresh || session_valid_until
|
|
77
|
+
session_valid_until = session_valid_until ? Time.parse(session_valid_until) : nil
|
|
78
|
+
|
|
79
|
+
new(
|
|
80
|
+
mangadex_user_id: mangadex_user_id,
|
|
81
|
+
session: session,
|
|
82
|
+
refresh: refresh,
|
|
83
|
+
session_valid_until: session_valid_until,
|
|
84
|
+
).with_valid_session
|
|
85
|
+
else
|
|
86
|
+
nil
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
if user
|
|
90
|
+
Mangadex.context.user = user
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
user
|
|
94
|
+
end
|
|
51
95
|
end
|
|
52
96
|
end
|
|
53
97
|
end
|
data/lib/mangadex/api.rb
CHANGED
data/lib/mangadex/auth.rb
CHANGED
|
@@ -3,16 +3,20 @@ module Mangadex
|
|
|
3
3
|
class Auth
|
|
4
4
|
extend T::Sig
|
|
5
5
|
|
|
6
|
-
sig { params(username: String, password: String).returns(T.
|
|
7
|
-
def self.login(username, password)
|
|
6
|
+
sig { params(username: T.nilable(String), email: T.nilable(String), password: String).returns(T.nilable(Mangadex::Api::User)) }
|
|
7
|
+
def self.login(username: nil, email: nil, password: nil)
|
|
8
|
+
args = { password: password }
|
|
9
|
+
args.merge!(email: email) if email
|
|
10
|
+
args.merge!(username: username) if username
|
|
11
|
+
|
|
8
12
|
response = Mangadex::Internal::Request.post(
|
|
9
13
|
'/auth/login',
|
|
10
|
-
payload: {
|
|
11
|
-
username:
|
|
12
|
-
|
|
13
|
-
|
|
14
|
+
payload: Mangadex::Internal::Definition.validate(args, {
|
|
15
|
+
username: { accepts: String },
|
|
16
|
+
email: { accepts: String },
|
|
17
|
+
password: { accepts: String, required: true },
|
|
18
|
+
}),
|
|
14
19
|
)
|
|
15
|
-
return response if response.is_a?(Mangadex::Api::Response) && response.errored?
|
|
16
20
|
|
|
17
21
|
session = response.dig('token', 'session')
|
|
18
22
|
refresh = response.dig('token', 'refresh')
|
|
@@ -20,13 +24,19 @@ module Mangadex
|
|
|
20
24
|
mangadex_user = Mangadex::Internal::Request.get('/user/me', headers: { Authorization: session })
|
|
21
25
|
|
|
22
26
|
user = Mangadex::Api::User.new(
|
|
23
|
-
mangadex_user.data.id,
|
|
27
|
+
mangadex_user_id: mangadex_user.data.id,
|
|
24
28
|
session: session,
|
|
25
29
|
refresh: refresh,
|
|
26
30
|
data: mangadex_user.data,
|
|
27
31
|
)
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
return if user.session_expired?
|
|
33
|
+
|
|
34
|
+
Mangadex.context.user = user
|
|
35
|
+
|
|
36
|
+
user.persist
|
|
37
|
+
user
|
|
38
|
+
rescue Errors::UnauthorizedError => error
|
|
39
|
+
raise Errors::AuthenticationError.new(error.response)
|
|
30
40
|
end
|
|
31
41
|
|
|
32
42
|
sig { returns(Hash) }
|
|
@@ -41,20 +51,39 @@ module Mangadex
|
|
|
41
51
|
|
|
42
52
|
sig { returns(T.any(T::Boolean, Mangadex::Api::Response)) }
|
|
43
53
|
def self.logout
|
|
44
|
-
return true if Mangadex
|
|
54
|
+
return true if Mangadex.context.user.nil?
|
|
45
55
|
|
|
46
56
|
response = Mangadex::Internal::Request.post(
|
|
47
57
|
'/auth/logout',
|
|
48
58
|
)
|
|
49
59
|
return reponse if response.is_a?(Mangadex::Api::Response) && response.errored?
|
|
50
60
|
|
|
51
|
-
|
|
61
|
+
clear_user
|
|
62
|
+
true
|
|
63
|
+
rescue Mangadex::Errors::UnauthorizedError
|
|
64
|
+
clear_user
|
|
52
65
|
true
|
|
53
66
|
end
|
|
54
67
|
|
|
55
68
|
sig { returns(T::Boolean) }
|
|
56
69
|
def self.refresh_token
|
|
57
|
-
!(Mangadex
|
|
70
|
+
!(Mangadex.context.user&.refresh!).nil?
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
private
|
|
74
|
+
|
|
75
|
+
sig { void }
|
|
76
|
+
def self.clear_user
|
|
77
|
+
return if Mangadex.context.user.nil?
|
|
78
|
+
|
|
79
|
+
if Mangadex.context.user.respond_to?(:session=)
|
|
80
|
+
Mangadex.context.user.session = nil
|
|
81
|
+
end
|
|
82
|
+
if Mangadex.context.user.respond_to?(:refresh=)
|
|
83
|
+
Mangadex.context.user.refresh = nil
|
|
84
|
+
end
|
|
85
|
+
Mangadex.storage.clear(Mangadex.context.user.mangadex_user_id)
|
|
86
|
+
Mangadex.context.user = nil
|
|
58
87
|
end
|
|
59
88
|
end
|
|
60
89
|
end
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
# typed: true
|
|
2
|
+
module Mangadex
|
|
3
|
+
module Internal
|
|
4
|
+
class Context
|
|
5
|
+
extend T::Sig
|
|
6
|
+
|
|
7
|
+
sig { returns(T::Array[Mangadex::ContentRating]) }
|
|
8
|
+
attr_accessor :allowed_content_ratings, :ignore_user
|
|
9
|
+
|
|
10
|
+
def initialize
|
|
11
|
+
@allowed_content_ratings = Mangadex.configuration.default_content_ratings
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
sig { returns(T.nilable(String)) }
|
|
15
|
+
def version
|
|
16
|
+
@version ||= Mangadex::Api::VersionChecker.check_mangadex_version
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
sig { returns(T.nilable(Mangadex::Api::User)) }
|
|
20
|
+
def user
|
|
21
|
+
@ignore_user ? nil : @user&.with_valid_session
|
|
22
|
+
rescue Mangadex::Errors::UnauthorizedError
|
|
23
|
+
warn("A user is present but not authenticated!")
|
|
24
|
+
nil
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
sig { returns(T::Array[Mangadex::Tag]) }
|
|
28
|
+
def tags
|
|
29
|
+
@tags ||= Mangadex::Tag.list.data
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
sig { params(user: T.nilable(T.any(Hash, Mangadex::Api::User, Mangadex::User)), block: T.proc.returns(T.untyped)).returns(T.untyped) }
|
|
33
|
+
def with_user(user, &block)
|
|
34
|
+
temp_set_value("user", user) do
|
|
35
|
+
yield
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
sig { params(block: T.proc.returns(T.untyped)).returns(T.untyped) }
|
|
40
|
+
def without_user(&block)
|
|
41
|
+
temp_set_value("ignore_user", true) do
|
|
42
|
+
yield
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
sig { params(user: T.nilable(T.untyped)).void }
|
|
47
|
+
def user=(user)
|
|
48
|
+
if user.is_a?(Mangadex::Api::User)
|
|
49
|
+
@user = user
|
|
50
|
+
elsif user.is_a?(Mangadex::User)
|
|
51
|
+
@user = Mangadex::Api::User.new(
|
|
52
|
+
mangadex_user_id: user.id,
|
|
53
|
+
data: user,
|
|
54
|
+
)
|
|
55
|
+
elsif user.is_a?(Hash)
|
|
56
|
+
user = Mangadex::Internal::Definition.validate(user, {
|
|
57
|
+
mangadex_user_id: { accepts: String, required: true },
|
|
58
|
+
session: { accepts: String },
|
|
59
|
+
refresh: { accepts: String },
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
@user = Mangadex::Api::User.new(
|
|
63
|
+
mangadex_user_id: user[:mangadex_user_id],
|
|
64
|
+
session: user[:session],
|
|
65
|
+
refresh: user[:refresh],
|
|
66
|
+
)
|
|
67
|
+
elsif user_object?(user)
|
|
68
|
+
@user = Mangadex::Api::User.new(
|
|
69
|
+
mangadex_user_id: user.mangadex_user_id.to_s,
|
|
70
|
+
session: user.session,
|
|
71
|
+
refresh: user.refresh,
|
|
72
|
+
data: user,
|
|
73
|
+
)
|
|
74
|
+
elsif user.nil?
|
|
75
|
+
@user = nil
|
|
76
|
+
else
|
|
77
|
+
raise TypeError, "Invalid user type."
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def allow_content_ratings(*content_ratings, &block)
|
|
82
|
+
content_ratings = Mangadex::ContentRating.parse(Array(content_ratings))
|
|
83
|
+
if block_given?
|
|
84
|
+
content_ratings = Mangadex.context.allowed_content_ratings if content_ratings.empty?
|
|
85
|
+
|
|
86
|
+
# set temporarily
|
|
87
|
+
temp_set_value("allowed_content_ratings", content_ratings) do
|
|
88
|
+
yield
|
|
89
|
+
end
|
|
90
|
+
elsif content_ratings.any?
|
|
91
|
+
# set "permanently"
|
|
92
|
+
@allowed_content_ratings = content_ratings
|
|
93
|
+
else
|
|
94
|
+
# This is to throw an exception prompting to pass a block if there no params.
|
|
95
|
+
yield
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def with_allowed_content_ratings(*other_content_ratings, &block)
|
|
100
|
+
T.unsafe(self).allow_content_ratings(*(allowed_content_ratings + other_content_ratings)) do
|
|
101
|
+
yield
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
# Only recommended for development and debugging only
|
|
106
|
+
def force_raw_requests(&block)
|
|
107
|
+
if block_given?
|
|
108
|
+
temp_set_value("force_raw_requests", true) do
|
|
109
|
+
yield
|
|
110
|
+
end
|
|
111
|
+
else
|
|
112
|
+
!!@force_raw_requests
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
private
|
|
117
|
+
|
|
118
|
+
def user_object?(user)
|
|
119
|
+
return false if user.nil?
|
|
120
|
+
|
|
121
|
+
missing_methods = [:session, :refresh, :mangadex_user_id] - user.methods
|
|
122
|
+
return true if missing_methods.empty?
|
|
123
|
+
|
|
124
|
+
warn("Potential user object #{user} is missing #{missing_methods}")
|
|
125
|
+
false
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def temp_set_value(name, value, &block)
|
|
129
|
+
setter_method_name = "#{name}="
|
|
130
|
+
|
|
131
|
+
current_value = send(name)
|
|
132
|
+
send(setter_method_name, value)
|
|
133
|
+
response = yield
|
|
134
|
+
send(setter_method_name, current_value)
|
|
135
|
+
response
|
|
136
|
+
ensure
|
|
137
|
+
send(setter_method_name, current_value) if current_value
|
|
138
|
+
end
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
@@ -54,7 +54,7 @@ module Mangadex
|
|
|
54
54
|
payload_details = request_payload ? "Payload: #{request_payload}" : "{no-payload}"
|
|
55
55
|
puts("[#{self.class.name}] #{method.to_s.upcase} #{request_url} #{payload_details}")
|
|
56
56
|
|
|
57
|
-
raise Mangadex::UserNotLoggedIn.new if auth && Mangadex
|
|
57
|
+
raise Mangadex::Errors::UserNotLoggedIn.new if auth && Mangadex.context.user.nil?
|
|
58
58
|
|
|
59
59
|
start_time = Time.now
|
|
60
60
|
|
|
@@ -63,11 +63,13 @@ module Mangadex
|
|
|
63
63
|
elapsed_time = ((end_time - start_time) * 1000).to_i
|
|
64
64
|
puts("[#{self.class.name}] took #{elapsed_time} ms")
|
|
65
65
|
|
|
66
|
-
raw_request = raw || Mangadex
|
|
66
|
+
raw_request = raw || Mangadex.context.force_raw_requests
|
|
67
67
|
|
|
68
68
|
if (body = @response.body)
|
|
69
69
|
raw_request ? try_json(body) : Mangadex::Api::Response.coerce(try_json(body))
|
|
70
70
|
end
|
|
71
|
+
rescue RestClient::Unauthorized => error
|
|
72
|
+
raise Errors::UnauthorizedError.new(Mangadex::Api::Response.coerce(try_json(error.response.body)))
|
|
71
73
|
rescue RestClient::Exception => error
|
|
72
74
|
if (body = error.response.body)
|
|
73
75
|
raw_request ? try_json(body) : Mangadex::Api::Response.coerce(JSON.parse(body)) rescue raise error
|
|
@@ -90,8 +92,8 @@ module Mangadex
|
|
|
90
92
|
|
|
91
93
|
def self.with_content_rating(data)
|
|
92
94
|
content_rating = data.has_key?(:content_rating) ? data[:content_rating] : []
|
|
93
|
-
Mangadex
|
|
94
|
-
data[:content_rating] = Mangadex
|
|
95
|
+
Mangadex.context.allow_content_ratings(*content_rating) do
|
|
96
|
+
data[:content_rating] = Mangadex.context.allowed_content_ratings
|
|
95
97
|
end
|
|
96
98
|
data
|
|
97
99
|
end
|
|
@@ -108,10 +110,10 @@ module Mangadex
|
|
|
108
110
|
end
|
|
109
111
|
|
|
110
112
|
def request_headers
|
|
111
|
-
return headers if Mangadex
|
|
113
|
+
return headers if Mangadex.context.user.nil?
|
|
112
114
|
|
|
113
115
|
headers.merge({
|
|
114
|
-
Authorization: Mangadex
|
|
116
|
+
Authorization: Mangadex.context.user.with_valid_session.session,
|
|
115
117
|
})
|
|
116
118
|
end
|
|
117
119
|
|
data/lib/mangadex/internal.rb
CHANGED
data/lib/mangadex/manga.rb
CHANGED
|
@@ -123,14 +123,16 @@ module Mangadex
|
|
|
123
123
|
)
|
|
124
124
|
end
|
|
125
125
|
|
|
126
|
-
sig { params(
|
|
127
|
-
def self.all_reading_status(
|
|
126
|
+
sig { params(status: String).returns(T::Api::GenericResponse) }
|
|
127
|
+
def self.all_reading_status(status)
|
|
128
|
+
args = { status: status }
|
|
129
|
+
|
|
128
130
|
Mangadex::Internal::Request.get(
|
|
129
131
|
'/manga/status',
|
|
130
132
|
Mangadex::Internal::Definition.validate(args, {
|
|
131
133
|
status: {
|
|
132
134
|
accepts: %w(reading on_hold dropped plan_to_read re_reading completed),
|
|
133
|
-
converts:
|
|
135
|
+
converts: :to_s,
|
|
134
136
|
},
|
|
135
137
|
})
|
|
136
138
|
)
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
module Mangadex
|
|
2
|
+
module Storage
|
|
3
|
+
class Basic
|
|
4
|
+
def get(_scope, _key)
|
|
5
|
+
raise NotImplementedError
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def set(_scope, _key, _value)
|
|
9
|
+
raise NotImplementedError
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def clear(_scope)
|
|
13
|
+
warn("Don't know how to clear #{self.class} storage strategy! Skipping...")
|
|
14
|
+
nil
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
module Mangadex
|
|
2
|
+
module Storage
|
|
3
|
+
class Memory < BasicObject
|
|
4
|
+
def initialize
|
|
5
|
+
@storage = {}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def get(scope, key)
|
|
9
|
+
@storage.dig(scope.to_s, key.to_s)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def set(scope, key, value)
|
|
13
|
+
key = key.to_s
|
|
14
|
+
@storage[scope] = {} unless @storage.has_key?(scope)
|
|
15
|
+
@storage[scope][key] = value
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def clear(scope)
|
|
19
|
+
@storage.delete(scope)
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/mangadex/user.rb
CHANGED
|
@@ -60,7 +60,7 @@ module Mangadex
|
|
|
60
60
|
def self.follows_user(id)
|
|
61
61
|
Mangadex::Internal::Definition.must(id)
|
|
62
62
|
|
|
63
|
-
return if Mangadex
|
|
63
|
+
return if Mangadex.context.user.nil?
|
|
64
64
|
|
|
65
65
|
data = Mangadex::Internal::Request.get(
|
|
66
66
|
'/user/follows/user/%{id}' % {id: id},
|
|
@@ -90,7 +90,7 @@ module Mangadex
|
|
|
90
90
|
def self.follows_manga(id)
|
|
91
91
|
Mangadex::Internal::Definition.must(id)
|
|
92
92
|
|
|
93
|
-
return if Mangadex
|
|
93
|
+
return if Mangadex.context.user.nil?
|
|
94
94
|
|
|
95
95
|
data = Mangadex::Internal::Request.get(
|
|
96
96
|
'/user/follows/manga/%{id}' % {id: id},
|
data/lib/mangadex/version.rb
CHANGED
data/lib/mangadex.rb
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
# typed:
|
|
1
|
+
# typed: true
|
|
2
2
|
require 'sorbet-runtime'
|
|
3
3
|
|
|
4
4
|
require 'active_support'
|
|
@@ -14,22 +14,35 @@ require "mangadex/types"
|
|
|
14
14
|
# API, to interact with Mangadex
|
|
15
15
|
require "mangadex/api"
|
|
16
16
|
|
|
17
|
+
# Persist strategies
|
|
18
|
+
require "mangadex/storage"
|
|
19
|
+
|
|
20
|
+
require_relative "config"
|
|
21
|
+
require_relative "errors"
|
|
22
|
+
|
|
17
23
|
# Namespace for classes and modules for this gem.
|
|
18
24
|
# @since 5.3.0
|
|
19
25
|
|
|
20
26
|
module Mangadex
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
27
|
+
class << self
|
|
28
|
+
def configuration
|
|
29
|
+
@configuration ||= Config.new
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def context
|
|
33
|
+
@context ||= Internal::Context.new
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def configure(&block)
|
|
37
|
+
yield(configuration)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def storage
|
|
41
|
+
configuration.storage
|
|
42
|
+
end
|
|
28
43
|
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def message
|
|
32
|
-
"You are not logged in. Use [Mangadex::Auth.login] to log in."
|
|
44
|
+
def api_version
|
|
45
|
+
context.version
|
|
33
46
|
end
|
|
34
47
|
end
|
|
35
48
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: mangadex
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.3.3.
|
|
4
|
+
version: 5.3.3.2
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Akinyele Cafe-Febrissy
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: exe
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2021-09
|
|
11
|
+
date: 2021-10-09 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: psych
|
|
@@ -170,11 +170,14 @@ files:
|
|
|
170
170
|
- Rakefile
|
|
171
171
|
- bin/console
|
|
172
172
|
- bin/setup
|
|
173
|
+
- docs/authentication.md
|
|
174
|
+
- docs/context.md
|
|
175
|
+
- lib/config.rb
|
|
176
|
+
- lib/errors.rb
|
|
173
177
|
- lib/extensions.rb
|
|
174
178
|
- lib/mangadex.rb
|
|
175
179
|
- lib/mangadex/README.md
|
|
176
180
|
- lib/mangadex/api.rb
|
|
177
|
-
- lib/mangadex/api/context.rb
|
|
178
181
|
- lib/mangadex/api/response.rb
|
|
179
182
|
- lib/mangadex/api/user.rb
|
|
180
183
|
- lib/mangadex/api/version_checker.rb
|
|
@@ -186,6 +189,7 @@ files:
|
|
|
186
189
|
- lib/mangadex/cover_art.rb
|
|
187
190
|
- lib/mangadex/custom_list.rb
|
|
188
191
|
- lib/mangadex/internal.rb
|
|
192
|
+
- lib/mangadex/internal/context.rb
|
|
189
193
|
- lib/mangadex/internal/definition.rb
|
|
190
194
|
- lib/mangadex/internal/request.rb
|
|
191
195
|
- lib/mangadex/internal/with_attributes.rb
|
|
@@ -195,6 +199,10 @@ files:
|
|
|
195
199
|
- lib/mangadex/report_reason.rb
|
|
196
200
|
- lib/mangadex/scanlation_group.rb
|
|
197
201
|
- lib/mangadex/sorbet.rb
|
|
202
|
+
- lib/mangadex/storage.rb
|
|
203
|
+
- lib/mangadex/storage/basic.rb
|
|
204
|
+
- lib/mangadex/storage/memory.rb
|
|
205
|
+
- lib/mangadex/storage/none.rb
|
|
198
206
|
- lib/mangadex/tag.rb
|
|
199
207
|
- lib/mangadex/types.rb
|
|
200
208
|
- lib/mangadex/upload.rb
|
data/lib/mangadex/api/context.rb
DELETED
|
@@ -1,137 +0,0 @@
|
|
|
1
|
-
# typed: true
|
|
2
|
-
module Mangadex
|
|
3
|
-
module Api
|
|
4
|
-
class Context
|
|
5
|
-
extend T::Sig
|
|
6
|
-
|
|
7
|
-
DEFAULT_MANGADEX_CONTENT_RATING_VALUES = [
|
|
8
|
-
ContentRating::SAFE,
|
|
9
|
-
ContentRating::SUGGESTIVE,
|
|
10
|
-
ContentRating::EROTICA,
|
|
11
|
-
].freeze
|
|
12
|
-
|
|
13
|
-
@@user = nil
|
|
14
|
-
@@version = nil
|
|
15
|
-
@@force_raw_requests = nil
|
|
16
|
-
@@tags = nil
|
|
17
|
-
@@allowed_content_ratings = DEFAULT_MANGADEX_CONTENT_RATING_VALUES
|
|
18
|
-
|
|
19
|
-
sig { returns(T.nilable(String)) }
|
|
20
|
-
def self.version
|
|
21
|
-
return @@version unless @@version.nil?
|
|
22
|
-
|
|
23
|
-
@@version = Mangadex::Api::VersionChecker.check_mangadex_version
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
sig { returns(T.nilable(Mangadex::Api::User)) }
|
|
27
|
-
def self.user
|
|
28
|
-
@@user&.with_valid_session
|
|
29
|
-
end
|
|
30
|
-
|
|
31
|
-
sig { returns(T::Array[Mangadex::Tag]) }
|
|
32
|
-
def self.tags
|
|
33
|
-
return @@tags if @@tags
|
|
34
|
-
|
|
35
|
-
@@tags = Mangadex::Tag.list.data
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
sig { returns(T::Array[Mangadex::ContentRating]) }
|
|
39
|
-
def self.allowed_content_ratings
|
|
40
|
-
@@allowed_content_ratings.map { |value| ContentRating.new(value) }
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
sig { params(user: T.nilable(T.any(Hash, Mangadex::Api::User, Mangadex::User))).void }
|
|
44
|
-
def self.user=(user)
|
|
45
|
-
if user.is_a?(Mangadex::Api::User)
|
|
46
|
-
@@user = user
|
|
47
|
-
elsif user.is_a?(Mangadex::User)
|
|
48
|
-
@@user = Mangadex::Api::User.new(
|
|
49
|
-
user.id,
|
|
50
|
-
data: user,
|
|
51
|
-
)
|
|
52
|
-
elsif user.is_a?(Hash)
|
|
53
|
-
user = Mangadex::Internal::Definition.validate(user, {
|
|
54
|
-
mangadex_user_id: { accepts: String, required: true },
|
|
55
|
-
session: { accepts: String },
|
|
56
|
-
refresh: { accepts: String },
|
|
57
|
-
})
|
|
58
|
-
|
|
59
|
-
@@user = Mangadex::Api::User.new(
|
|
60
|
-
user[:mangadex_user_id],
|
|
61
|
-
session: user[:session],
|
|
62
|
-
refresh: user[:refresh],
|
|
63
|
-
)
|
|
64
|
-
elsif user.nil?
|
|
65
|
-
@@user = nil
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
sig { params(user: T.nilable(T.any(Hash, Mangadex::Api::User, Mangadex::User)), block: T.proc.returns(T.untyped)).returns(T.untyped) }
|
|
70
|
-
def self.with_user(user, &block)
|
|
71
|
-
temp_set_value("user", user) do
|
|
72
|
-
yield
|
|
73
|
-
end
|
|
74
|
-
end
|
|
75
|
-
|
|
76
|
-
sig { params(block: T.proc.returns(T.untyped)).returns(T.untyped) }
|
|
77
|
-
def self.without_user(&block)
|
|
78
|
-
with_user(nil) do
|
|
79
|
-
yield
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def self.force_raw_requests(&block)
|
|
84
|
-
if block_given?
|
|
85
|
-
temp_set_value("force_raw_requests", true) do
|
|
86
|
-
yield
|
|
87
|
-
end
|
|
88
|
-
else
|
|
89
|
-
!!@@force_raw_requests
|
|
90
|
-
end
|
|
91
|
-
end
|
|
92
|
-
|
|
93
|
-
def self.force_raw_requests=(value)
|
|
94
|
-
@@force_raw_requests = value
|
|
95
|
-
end
|
|
96
|
-
|
|
97
|
-
def self.allow_content_ratings(*content_ratings, &block)
|
|
98
|
-
content_ratings = if content_ratings.empty?
|
|
99
|
-
allowed_content_ratings
|
|
100
|
-
else
|
|
101
|
-
Mangadex::ContentRating.parse(content_ratings)
|
|
102
|
-
end
|
|
103
|
-
if block_given?
|
|
104
|
-
# set temporarily
|
|
105
|
-
temp_set_value("allowed_content_ratings", content_ratings) do
|
|
106
|
-
yield
|
|
107
|
-
end
|
|
108
|
-
elsif content_ratings.any?
|
|
109
|
-
# set "permanently"
|
|
110
|
-
@@allowed_content_ratings = content_ratings
|
|
111
|
-
else
|
|
112
|
-
# This is to throw an exception prompting to pass a block if there no params.
|
|
113
|
-
yield
|
|
114
|
-
end
|
|
115
|
-
end
|
|
116
|
-
|
|
117
|
-
def self.with_allowed_content_ratings(*other_content_ratings, &block)
|
|
118
|
-
T.unsafe(self).allow_content_ratings(*(allowed_content_ratings + other_content_ratings)) do
|
|
119
|
-
yield
|
|
120
|
-
end
|
|
121
|
-
end
|
|
122
|
-
|
|
123
|
-
private
|
|
124
|
-
|
|
125
|
-
def self.temp_set_value(name, value, &block)
|
|
126
|
-
var_name = "@@#{name}"
|
|
127
|
-
current_value = class_variable_get(var_name)
|
|
128
|
-
class_variable_set(var_name, value)
|
|
129
|
-
response = yield
|
|
130
|
-
class_variable_set(var_name, current_value)
|
|
131
|
-
response
|
|
132
|
-
ensure
|
|
133
|
-
class_variable_set(var_name, current_value) if current_value
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
end
|