ruqqus 1.1.2 → 1.1.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 +4 -4
- data/CHANGELOG.md +14 -0
- data/lib/ruqqus.rb +151 -15
- data/lib/ruqqus/client.rb +4 -4
- data/lib/ruqqus/version.rb +7 -3
- data/ruqqus.gemspec +2 -1
- metadata +4 -17
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1274eade9799203bd720c94da255a81a0e0295da9e438519a687626c1db7f0b7
|
4
|
+
data.tar.gz: 96fe1b73d77d38004981f476ddca3acde81577755de6939f77c6c4ea65fdc693
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: da793dbcee6ae0cd28b6f5160f85a752804a44ceb00f295291209e2529a3ba39ed26300f43b8b9df95f4ce41b7c3c10b97f60550eed9b9ce5c27c50b4252a638
|
7
|
+
data.tar.gz: 92b77622dcaf2fa8129e013465a91186d1de731eb927bcd219fac8160c7afccceb2beecf4f509f7e3b9fe95c8b9431955d301566eeaa5608302a2331ce87743c
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,20 @@
|
|
2
2
|
|
3
3
|
Documentation for library API changes.
|
4
4
|
|
5
|
+
Versioning system:
|
6
|
+
|
7
|
+
`MAJOR.MINOR.REVISION`
|
8
|
+
|
9
|
+
* `MAJOR` Corresponds to the native Ruqqus API major version
|
10
|
+
* `MINOR` Indicates possible breaking API changes for existing code
|
11
|
+
* `REVISION` Added functionality, bug-fixes, and other non-breaking alterations
|
12
|
+
|
13
|
+
## Version 1.1.3
|
14
|
+
|
15
|
+
* Implemented browser-based confirmation process
|
16
|
+
* Implemented capturing confirmation code from `localhost` OAuth redirects
|
17
|
+
* Fixed bug in querying guild/username availability
|
18
|
+
|
5
19
|
## Version 1.1.2
|
6
20
|
|
7
21
|
* Implemented enumerating comments of guilds and posts
|
data/lib/ruqqus.rb
CHANGED
@@ -1,5 +1,9 @@
|
|
1
|
-
require '
|
1
|
+
require 'base64'
|
2
2
|
require 'json'
|
3
|
+
require 'rbconfig'
|
4
|
+
require 'rest-client'
|
5
|
+
require 'securerandom'
|
6
|
+
require 'socket'
|
3
7
|
|
4
8
|
require_relative 'ruqqus/token'
|
5
9
|
require_relative 'ruqqus/routes'
|
@@ -49,12 +53,14 @@ module Ruqqus
|
|
49
53
|
# @return [URI?] the URI of the proxy server in use, or `nil` if none has been set.
|
50
54
|
|
51
55
|
##
|
52
|
-
# Obtains a list of URIs of proxy servers that can be used to route network traffic through.
|
56
|
+
# Obtains a list of URIs of free proxy servers that can be used to route network traffic through.
|
53
57
|
#
|
54
58
|
# @param anon [Symbol] anonymity filter for the servers to return, either `:transparent`, `:anonymous`, or `:elite`.
|
55
59
|
# @param country [String,Symbol] country filter for servers to return, an ISO-3166 two digit county code.
|
56
60
|
#
|
57
61
|
# @return [Array<URI>] an array of proxy URIs that match the input filters.
|
62
|
+
# @note These proxies are free, keep that in mind. They are refreshed frequently, can go down unexpectedly, be slow,
|
63
|
+
# and other manners of inconvenience that can be expected with free services.
|
58
64
|
# @see https://www.nationsonline.org/oneworld/country_code_list.htm
|
59
65
|
def self.proxy_list(anon: :elite, country: nil)
|
60
66
|
raise(ArgumentError, 'invalid anonymity value') unless %i(transparent anonymous elite).include?(anon.to_sym)
|
@@ -83,7 +89,7 @@ module Ruqqus
|
|
83
89
|
#
|
84
90
|
# @param client_id [String] an Imgur client ID
|
85
91
|
# @param image_path [String] the path to an image file.
|
86
|
-
# @
|
92
|
+
# @param opts [Hash] the options hash.
|
87
93
|
# @option opts [String] :title a title to set on the Imgur post
|
88
94
|
# @option opts [String] :description a description to set on the Imgur post
|
89
95
|
#
|
@@ -111,7 +117,14 @@ module Ruqqus
|
|
111
117
|
#
|
112
118
|
# @return [Boolean] `true` is name is available, otherwise `false` if it has been reserved or is in use.
|
113
119
|
def self.guild_available?(guild_name)
|
114
|
-
|
120
|
+
begin
|
121
|
+
response = RestClient.get("#{Routes::GUILD_AVAILABLE}#{guild_name}")
|
122
|
+
json = JSON.parse(response.body, symbolize_names: true)
|
123
|
+
return json[:available]
|
124
|
+
rescue
|
125
|
+
puts 'err'
|
126
|
+
return false
|
127
|
+
end
|
115
128
|
end
|
116
129
|
|
117
130
|
##
|
@@ -121,22 +134,145 @@ module Ruqqus
|
|
121
134
|
#
|
122
135
|
# @return [Boolean] `true` is name is available, otherwise `false` if it has been reserved or is in use.
|
123
136
|
def self.username_available?(username)
|
124
|
-
|
137
|
+
begin
|
138
|
+
response = RestClient.get("#{Routes::USERNAME_AVAILABLE}#{username}")
|
139
|
+
json = JSON.parse(response.body)
|
140
|
+
return json[username]
|
141
|
+
rescue
|
142
|
+
return false
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
##
|
147
|
+
# Generates a URL for the user to navigate to that will allow them to authorize an application.
|
148
|
+
#
|
149
|
+
# @param client_id [String] the unique ID of the approved client to authorize.
|
150
|
+
# @param redirect [String] the redirect URL where the client sends the OAuth authorization code.
|
151
|
+
# @param scopes [Array<Symbol>] a collection of values indicating the permissions the application is requesting from
|
152
|
+
# the user. See {Ruqqus::Client::SCOPES} for valid values.
|
153
|
+
# @param permanent [Boolean] `true` if authorization should persist until user explicitly revokes it,
|
154
|
+
# otherwise `false`.
|
155
|
+
# @param csrf [String] a token to authenticate and prevent a cross-site request forgery (CSRF) attack, or `nil` if
|
156
|
+
# you do not plan to validate the presence of the cookie in the redirection.
|
157
|
+
#
|
158
|
+
# @see https://ruqqus.com/settings/apps
|
159
|
+
# @see https://owasp.org/www-community/attacks/csrf
|
160
|
+
def self.authorize_url(client_id, redirect, scopes, permanent = true, csrf = nil)
|
161
|
+
|
162
|
+
raise(ArgumentError, 'invalid redirect URI') unless URI.regexp =~ redirect
|
163
|
+
raise(ArgumentError, 'scopes cannot be empty') unless scopes && !scopes.empty?
|
164
|
+
|
165
|
+
scopes = scopes.map(&:to_sym)
|
166
|
+
raise(ArgumentError, "invalid scopes specified") unless scopes.all? { |s| Client::SCOPES.include?(s) }
|
167
|
+
if scopes.any? { |s| [:create, :update, :guildmaster].include?(s) } && !scopes.include?(:identity)
|
168
|
+
# Add identity permission if missing, which is obviously required for a few other permissions
|
169
|
+
scopes << :identity
|
170
|
+
end
|
171
|
+
|
172
|
+
url = 'https://ruqqus.com/oauth/authorize'
|
173
|
+
url << "?client_id=#{client_id || raise(ArgumentError, 'client ID cannot be nil')}"
|
174
|
+
url << "&redirect_uri=#{redirect}"
|
175
|
+
url << "&scope=#{scopes.join(',')}"
|
176
|
+
url << "&state=#{csrf || Base64.encode64(SecureRandom.uuid).chomp}"
|
177
|
+
url << "&permanent=#{permanent}"
|
178
|
+
url
|
125
179
|
end
|
126
180
|
|
127
|
-
private
|
128
181
|
|
129
182
|
##
|
130
|
-
#
|
183
|
+
# Opens a URL in the system's default web browser, using the appropriate command for the host platform.
|
131
184
|
#
|
132
|
-
# @param
|
133
|
-
# @param regex [Regex] a validation regex for the name.
|
134
|
-
# @param route [String] the API endpoint to invoke.
|
185
|
+
# @param [String] the URL to open.
|
135
186
|
#
|
136
|
-
# @return [
|
137
|
-
def self.
|
138
|
-
|
139
|
-
|
140
|
-
|
187
|
+
# @return [void]
|
188
|
+
def self.open_browser(url)
|
189
|
+
|
190
|
+
cmd = case RbConfig::CONFIG['host_os']
|
191
|
+
when /mswin|mingw|cygwin/ then "start \"\"#{url}\"\""
|
192
|
+
when /darwin/ then "open '#{url}'"
|
193
|
+
when /linux|bsd/ then "xdg-open '#{url}'"
|
194
|
+
else raise(Ruqqus::Error, 'unable to determine how to open URL for current platform')
|
195
|
+
end
|
196
|
+
|
197
|
+
system(cmd)
|
198
|
+
end
|
199
|
+
|
200
|
+
##
|
201
|
+
# If using a `localhost` address for your application's OAuth redirect, this method can be used to open a socket and
|
202
|
+
# listen for a request, returning the authorization code once it arrives.
|
203
|
+
#
|
204
|
+
# @param port [Integer] the port to listen on.
|
205
|
+
# @param timeout [Numeric] sets the number of seconds to wait before cancelling and returning `nil`.
|
206
|
+
#
|
207
|
+
# @return [String?] the authorization code, `nil` if an error occurred.
|
208
|
+
# @note This method is blocking, and will *not* return until a connection is made and data is received on the
|
209
|
+
# specified port, or the timeout is reached.
|
210
|
+
def self.wait_for_code(port, timeout = 30)
|
211
|
+
|
212
|
+
thread = Thread.new do
|
213
|
+
sleep(timeout)
|
214
|
+
TCPSocket.open('localhost', port) { |s| s.puts }
|
215
|
+
end
|
216
|
+
|
217
|
+
params = {}
|
218
|
+
TCPServer.open('localhost', port) do |server|
|
219
|
+
|
220
|
+
session = server.accept
|
221
|
+
request = session.gets
|
222
|
+
match = /^GET [\/?]+(.*) HTTP.*/.match(request)
|
223
|
+
|
224
|
+
Thread.kill(thread)
|
225
|
+
return nil unless match
|
226
|
+
|
227
|
+
$1.split('&').each do |str|
|
228
|
+
key, value = str.split('=')
|
229
|
+
next unless key && value
|
230
|
+
params[key.to_sym] = value
|
231
|
+
end
|
232
|
+
|
233
|
+
session.puts "HTTP/1.1 200\r\n"
|
234
|
+
session.puts "Content-Type: text/html\r\n"
|
235
|
+
session.puts "\r\n"
|
236
|
+
session.puts create_response(!!params[:code])
|
237
|
+
|
238
|
+
session.close
|
239
|
+
end
|
240
|
+
|
241
|
+
params[:code]
|
242
|
+
end
|
243
|
+
|
244
|
+
private
|
245
|
+
|
246
|
+
##
|
247
|
+
# @return [String] a generic confirmation page to display in the user's browser after confirming application access.
|
248
|
+
def self.create_response(success)
|
249
|
+
args = success ? ['#339966', 'Authorization Confirmed'] : ['#ff0000', 'Authorization Failed']
|
250
|
+
format ='<h1 style="text-align: center;"><span style="color: %s;"><strong>%s</strong></span></h1>'
|
251
|
+
message = sprintf(format, *args)
|
252
|
+
<<-EOS
|
253
|
+
<html>
|
254
|
+
<head>
|
255
|
+
<style>
|
256
|
+
.center {
|
257
|
+
margin: 0;
|
258
|
+
position: absolute;
|
259
|
+
top: 50%;
|
260
|
+
left: 50%;
|
261
|
+
-ms-transform: translate(-50%, -50%);
|
262
|
+
transform: translate(-50%, -50%);
|
263
|
+
}
|
264
|
+
</style>
|
265
|
+
</head>
|
266
|
+
<body>
|
267
|
+
<div class="center">
|
268
|
+
<div><img src="https://raw.githubusercontent.com/ruqqus/ruqqus/master/ruqqus/assets/images/logo/ruqqus_text_logo.png" alt="" width="365" height="92" /></div>
|
269
|
+
<p style="text-align: center;"> </p>
|
270
|
+
#{message}
|
271
|
+
<p style="text-align: center;"> </p>
|
272
|
+
<p style="text-align: center;"><span style="color: #808080;">You can safely close the tab/browser and return to the application.</span></p>
|
273
|
+
</div>
|
274
|
+
</body>
|
275
|
+
</html>
|
276
|
+
EOS
|
141
277
|
end
|
142
278
|
end
|
data/lib/ruqqus/client.rb
CHANGED
@@ -62,7 +62,7 @@ module Ruqqus
|
|
62
62
|
#
|
63
63
|
# @return [User] the requested {User}.
|
64
64
|
#
|
65
|
-
# @raise [ArgumentError] when `username` is `nil` or value does match the {VALID_USERNAME} regular expression.
|
65
|
+
# @raise [ArgumentError] when `username` is `nil` or value does match the {Ruqqus::VALID_USERNAME} regular expression.
|
66
66
|
# @raise [Error] thrown when user account does not exist.
|
67
67
|
def user(username)
|
68
68
|
raise(ArgumentError, 'username cannot be nil') unless username
|
@@ -77,7 +77,7 @@ module Ruqqus
|
|
77
77
|
#
|
78
78
|
# @return [Guild] the requested {Guild}.
|
79
79
|
#
|
80
|
-
# @raise [ArgumentError] when `guild_name` is `nil` or value does match the {VALID_GUILD} regular expression.
|
80
|
+
# @raise [ArgumentError] when `guild_name` is `nil` or value does match the {Ruqqus::VALID_GUILD} regular expression.
|
81
81
|
# @raise [Error] thrown when guild does not exist.
|
82
82
|
def guild(guild_name)
|
83
83
|
raise(ArgumentError, 'guild_name cannot be nil') unless guild_name
|
@@ -92,7 +92,7 @@ module Ruqqus
|
|
92
92
|
#
|
93
93
|
# @return [Post] the requested {Post}.
|
94
94
|
#
|
95
|
-
# @raise [ArgumentError] when `post_id` is `nil` or value does match the {VALID_POST} regular expression.
|
95
|
+
# @raise [ArgumentError] when `post_id` is `nil` or value does match the {Ruqqus::VALID_POST} regular expression.
|
96
96
|
# @raise [Error] thrown when a post with the specified ID does not exist.
|
97
97
|
def post(post_id)
|
98
98
|
raise(ArgumentError, 'post_id cannot be nil') unless post_id
|
@@ -107,7 +107,7 @@ module Ruqqus
|
|
107
107
|
#
|
108
108
|
# @return [Comment] the requested {Comment}.
|
109
109
|
#
|
110
|
-
# @raise [ArgumentError] when `comment_id` is `nil` or value does match the {VALID_POST} regular expression.
|
110
|
+
# @raise [ArgumentError] when `comment_id` is `nil` or value does match the {Ruqqus::VALID_POST} regular expression.
|
111
111
|
# @raise [Error] when a comment with the specified ID does not exist.
|
112
112
|
def comment(comment_id)
|
113
113
|
raise(ArgumentError, 'comment_id cannot be nil') unless comment_id
|
data/lib/ruqqus/version.rb
CHANGED
@@ -1,11 +1,15 @@
|
|
1
1
|
module Ruqqus
|
2
2
|
|
3
3
|
##
|
4
|
-
# The Ruqqus gem version.
|
5
|
-
|
4
|
+
# The Ruqqus gem version. Version changes implement the following versioning system:
|
5
|
+
#
|
6
|
+
# * `MAJOR` Corresponds to the native Ruqqus API major version
|
7
|
+
# * `MINOR` Indicates possible breaking API changes for existing code
|
8
|
+
# * `REVISION` Added functionality, bug-fixes, and other non-breaking alterations
|
9
|
+
VERSION = '1.1.3'.freeze
|
6
10
|
|
7
11
|
##
|
8
|
-
#
|
12
|
+
# Please listen to this song I wrote. The song is called "Endless Summer".
|
9
13
|
ENDLESS_SUMMER = 'https://youtu.be/o_LskiXQ73c'.freeze
|
10
14
|
|
11
15
|
private_constant(:ENDLESS_SUMMER)
|
data/ruqqus.gemspec
CHANGED
@@ -36,7 +36,8 @@ Gem::Specification.new do |spec|
|
|
36
36
|
spec.add_runtime_dependency('rest-client', '~> 2.1')
|
37
37
|
|
38
38
|
spec.add_development_dependency('mechanize', '~> 2.7')
|
39
|
-
spec.add_development_dependency('rake', '~> 13.0')
|
40
39
|
spec.add_development_dependency('tty-prompt', '~> 0.22')
|
41
40
|
spec.add_development_dependency('yard', '~> 0.9')
|
41
|
+
|
42
|
+
spec.post_install_message = 'Please listen to this song I wrote. The song is called "Endless Summer".'
|
42
43
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: ruqqus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- ForeverZer0
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-09-
|
11
|
+
date: 2020-09-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rest-client
|
@@ -38,20 +38,6 @@ dependencies:
|
|
38
38
|
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '2.7'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rake
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - "~>"
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '13.0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - "~>"
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '13.0'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: tty-prompt
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -126,7 +112,8 @@ metadata:
|
|
126
112
|
changelog_uri: https://github.com/ForeverZer0/ruqqus/CHANGELOG.md
|
127
113
|
documentation_uri: https://www.rubydoc.info/gems/ruqqus
|
128
114
|
bug_tracker_uri: https://github.com/ForeverZer0/ruqqus/issues
|
129
|
-
post_install_message:
|
115
|
+
post_install_message: Please listen to this song I wrote. The song is called "Endless
|
116
|
+
Summer".
|
130
117
|
rdoc_options: []
|
131
118
|
require_paths:
|
132
119
|
- lib
|