Module: Ruqqus

Defined in:
lib/ruqqus.rb,
lib/ruqqus/token.rb,
lib/ruqqus/client.rb,
lib/ruqqus/routes.rb,
lib/ruqqus/version.rb,
lib/ruqqus/types/post.rb,
lib/ruqqus/types/user.rb,
lib/ruqqus/types/badge.rb,
lib/ruqqus/types/guild.rb,
lib/ruqqus/types/title.rb,
lib/ruqqus/types/comment.rb,
lib/ruqqus/types/item_base.rb,
lib/ruqqus/types/submission.rb

Overview

Top-level namespace of the Ruqqus gem.

Defined Under Namespace

Modules: Routes Classes: Badge, Client, Comment, Error, Guild, ItemBase, Post, Submission, Title, Token, User

Constant Summary collapse

HOME =

The base Ruqqus URL.

'https://ruqqus.com'.freeze
VALID_USERNAME =

A regular expression used for username validation.

/^[a-zA-Z0-9_]{5,25}$/.freeze
VALID_PASSWORD =

A regular expression used for password validation.

/^.{8,100}$/.freeze
VALID_GUILD =

A regular expression used for guild name validation.

/^[a-zA-Z0-9][a-zA-Z0-9_]{2,24}$/.freeze
VALID_POST =

A regular expression used for post/comment ID validation.

/[A-Za-z0-9]+/.freeze
POST_REGEX =

Captures the ID of a post from a Ruqqus URL

/\/post\/([A-Za-z0-9]+)\/?.*/.freeze
COMMENT_REGEX =

Captures the ID of a comment from a Ruqqus URL

/\/post\/.+\/.+\/([A-Za-z0-9]+)\/?/.freeze
VERSION =

The Ruqqus gem version. Version changes implement the following versioning system:

  • MAJOR Corresponds to the native Ruqqus API major version
  • MINOR Indicates possible breaking API changes for existing code
  • REVISION Added functionality, bug-fixes, and other non-breaking alterations
'1.1.5'.freeze

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.proxyURI?

Returns the URI of the proxy server in use, or nil if none has been set.

Returns:

  • (URI?)

    the URI of the proxy server in use, or nil if none has been set.

Raises:

  • (TypeError)


# File 'lib/ruqqus.rb', line 51

Class Method Details

.authorize_url(client_id, redirect, scopes, permanent = true, csrf = nil) ⇒ Object

Generates a URL for the user to navigate to that will allow them to authorize an application.

Parameters:

  • client_id (String)

    the unique ID of the approved client to authorize.

  • redirect (String)

    the redirect URL where the client sends the OAuth authorization code.

  • scopes (Array<Symbol>)

    a collection of values indicating the permissions the application is requesting from the user. See Ruqqus::Client::SCOPES for valid values.

  • permanent (Boolean) (defaults to: true)

    true if authorization should persist until user explicitly revokes it, otherwise false.

  • csrf (String) (defaults to: nil)

    a token to authenticate and prevent a cross-site request forgery (CSRF) attack, or nil if you do not plan to validate the presence of the cookie in the redirection.

Raises:

  • (ArgumentError)

See Also:



160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
# File 'lib/ruqqus.rb', line 160

def self.authorize_url(client_id, redirect, scopes, permanent = true, csrf = nil)

  raise(ArgumentError, 'invalid redirect URI') unless URI.regexp =~ redirect
  raise(ArgumentError, 'scopes cannot be empty') unless scopes && !scopes.empty?

  scopes = scopes.map(&:to_sym)
  raise(ArgumentError, "invalid scopes specified") unless scopes.all? { |s| Client::SCOPES.include?(s) }
  if scopes.any? { |s| [:create, :update, :guildmaster].include?(s) } && !scopes.include?(:identity)
    # Add identity permission if missing, which is obviously required for a few other permissions
    scopes << :identity
  end

  url = 'https://ruqqus.com/oauth/authorize'
  url << "?client_id=#{client_id || raise(ArgumentError, 'client ID cannot be nil')}"
  url << "&redirect_uri=#{redirect}"
  url << "&scope=#{scopes.join(',')}"
  url << "&state=#{csrf || Base64.encode64(SecureRandom.uuid).chomp}"
  url << "&permanent=#{permanent}"
  url
end

.guild_available?(guild_name) ⇒ Boolean

Checks if the specified guild name is available to be created.

Parameters:

  • guild_name (String)

    the name of a guild to query.

Returns:

  • (Boolean)

    true is name is available, otherwise false if it has been reserved or is in use.



119
120
121
122
123
124
125
126
127
128
# File 'lib/ruqqus.rb', line 119

def self.guild_available?(guild_name)
  begin
    response = RestClient.get("#{Routes::GUILD_AVAILABLE}#{guild_name}")
    json = JSON.parse(response.body, symbolize_names: true)
    return json[:available]
  rescue
    puts 'err'
    return false
  end
end

.imgur_upload(client_id, image_path, **opts) ⇒ String

Note:

To obtain a free Imgur client ID, visit https://api.imgur.com/oauth2/addclient

Note:

No authentication is required for anonymous image upload, though rate limiting (very generous) applies.

Helper function to automate uploading images to Imgur anonymously and returning the direct image link.

Parameters:

  • client_id (String)

    an Imgur client ID

  • image_path (String)

    the path to an image file.

  • opts (Hash)

    the options hash.

Options Hash (**opts):

  • :title (String)

    a title to set on the Imgur post

  • :description (String)

    a description to set on the Imgur post

Returns:

  • (String)

    the direct image link from Imgur.

Raises:

  • (Errno::ENOENT)

See Also:



100
101
102
103
104
105
106
107
108
109
110
111
# File 'lib/ruqqus.rb', line 100

def self.imgur_upload(client_id, image_path, **opts)
  #noinspection RubyResolve
  raise(Errno::ENOENT, image_path) unless File.exist?(image_path)
  raise(ArgumentError, 'client_id cannot be nil or empty') if client_id.nil? || client_id.empty?

  header = { 'Content-Type': 'application/json', 'Authorization': "Client-ID #{client_id}" }
  params = { image: File.new(image_path), type: 'file', title: opts[:title], description: opts[:description] }

  response = RestClient.post('https://api.imgur.com/3/upload', params, header)
  json = JSON.parse(response.body, symbolize_names: true)
  json[:data][:link]
end

.open_browser(url) ⇒ void

This method returns an undefined value.

Opens a URL in the system's default web browser, using the appropriate command for the host platform.

Parameters:

  • the (String)

    URL to open.



188
189
190
191
192
193
194
195
196
197
198
# File 'lib/ruqqus.rb', line 188

def self.open_browser(url)

  cmd = case RbConfig::CONFIG['host_os']
  when /mswin|mingw|cygwin/ then "start \"\"#{url}\"\""
  when /darwin/ then "open '#{url}'"
  when /linux|bsd/ then "xdg-open '#{url}'"
  else raise(Ruqqus::Error, 'unable to determine how to open URL for current platform')
  end

  system(cmd)
end

.proxy_list(anon: :elite, country: nil) ⇒ Array<URI>

Note:

These proxies are free, keep that in mind. They are refreshed frequently, can go down unexpectedly, be slow, and other manners of inconvenience that can be expected with free services.

Obtains a list of URIs of free proxy servers that can be used to route network traffic through.

Parameters:

  • anon (Symbol) (defaults to: :elite)

    anonymity filter for the servers to return, either :transparent, :anonymous, or :elite.

  • country (String, Symbol) (defaults to: nil)

    country filter for servers to return, an ISO-3166 two digit county code.

Returns:

  • (Array<URI>)

    an array of proxy URIs that match the input filters.

Raises:

  • (ArgumentError)

See Also:



65
66
67
68
69
70
71
72
73
74
75
76
# File 'lib/ruqqus.rb', line 65

def self.proxy_list(anon: :elite, country: nil)
  raise(ArgumentError, 'invalid anonymity value') unless %i(transparent anonymous elite).include?(anon.to_sym)

  url = "https://www.proxy-list.download/api/v1/get?type=https&anon=#{anon}"
  url << "&country=#{country}" if country

  RestClient.get(url) do |resp|
    break if resp.code != 200
    return resp.body.split.map { |proxy| URI.parse("https://#{proxy}") }
  end
  Array.new
end

.username_available?(username) ⇒ Boolean

Checks if the specified username is available to be created.

Parameters:

  • username (String)

    the name of a user to query.

Returns:

  • (Boolean)

    true is name is available, otherwise false if it has been reserved or is in use.



136
137
138
139
140
141
142
143
144
# File 'lib/ruqqus.rb', line 136

def self.username_available?(username)
  begin
    response = RestClient.get("#{Routes::USERNAME_AVAILABLE}#{username}")
    json = JSON.parse(response.body)
    return json[username]
  rescue
    return false
  end
end

.wait_for_code(port, timeout = 30) ⇒ String?

Note:

This method is blocking, and will not return until a connection is made and data is received on the specified port, or the timeout is reached.

If using a localhost address for your application's OAuth redirect, this method can be used to open a socket and listen for a request, returning the authorization code once it arrives.

Parameters:

  • port (Integer)

    the port to listen on.

  • timeout (Numeric) (defaults to: 30)

    sets the number of seconds to wait before cancelling and returning nil.

Returns:

  • (String?)

    the authorization code, nil if an error occurred.



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
# File 'lib/ruqqus.rb', line 210

def self.wait_for_code(port, timeout = 30)

  thread = Thread.new do
    sleep(timeout)
    TCPSocket.open('localhost', port) { |s| s.puts }
  end

  params = {}
  TCPServer.open('localhost', port) do |server|

    session = server.accept
    request = session.gets
    match = /^GET [\/?]+(.*) HTTP.*/.match(request)

    Thread.kill(thread)
    return nil unless match

    $1.split('&').each do |str|
      key, value = str.split('=')
      next unless key && value
      params[key.to_sym] = value
    end

    session.puts "HTTP/1.1 200\r\n"
    session.puts "Content-Type: text/html\r\n"
    session.puts "\r\n"
    session.puts create_response(!!params[:code])

    session.close
  end

  params[:code]
end