ruqqus 1.1.2 → 1.1.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|