Class: Ruqqus::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/ruqqus/client.rb

Overview

Implements interacting with the Ruqqus API as a user, such as login, posting, account management, etc. noinspection RubyTooManyMethodsInspection

Constant Summary collapse

USER_AGENT =

The user-agent the client identified itself as.

"ruqqus-ruby/#{Ruqqus::VERSION}".freeze
SCOPES =

A collection of valid scopes that can be authorized.

  • :identity - See your username.
  • :create - Save posts and comments as you
  • :read - View Ruqqus as you, including private or restricted content
  • :update - Edit your posts and comments
  • :delete - Delete your posts and comments
  • :vote - Cast votes as you
  • :guildmaster - Perform Guildmaster actions
%i(identity create read update delete vote guildmaster).freeze
DEFAULT_HEADERS =

A set of HTTP headers that will be included with every request.

{ 'User-Agent': USER_AGENT, 'Accept': 'application/json', 'Content-Type': 'application/json' }.freeze

Instance Attribute Summary collapse

Object Querying collapse

Commenting collapse

Posting collapse

Voting collapse

Object Enumeration collapse

Instance Method Summary collapse

Constructor Details

#initialize(client_id, client_secret, token) ⇒ Client #initialize(client_id, client_secret, code) ⇒ Client

Returns a new instance of Client.

Overloads:

  • #initialize(client_id, client_secret, token) ⇒ Client

    Creates a new instance of the Ruqqus::Client class with an existing token for authorization.

    Parameters:

    • client_id (String)

      the client ID of your of your application, issued after registration on Ruqqus.

    • client_secret (String)

      the client secret of your of your application, issued after registration on Ruqqus.

    • token (Token)

      a valid access token that has previously been granted access for the client.

  • #initialize(client_id, client_secret, code) ⇒ Client

    Creates a new instance of the Ruqqus::Client class with an existing token for authorization.

    Parameters:

    • client_id (String)

      the client ID of your of your application, issued after registration on Ruqqus.

    • client_secret (String)

      the client secret of your of your application, issued after registration on Ruqqus.

    • code (String)

      a the code from the Oauth2 redirect to create a new Token and grant access to it.



50
51
52
53
54
55
56
57
# File 'lib/ruqqus/client.rb', line 50

def initialize(client_id, client_secret, token)
  @client_id = client_id || raise(ArgumentError, 'client ID cannot be nil')
  @client_secret = client_secret || raise(ArgumentError, 'client secret cannot be nil')

  @token = token.is_a?(Token) ? token : Token.new(client_id, client_secret, token.to_s)
  @token.refresh(client_id, client_secret)
  @session = nil
end

Instance Attribute Details

#identityUser (readonly)

Returns the authenticated user this client is performing actions as.

Returns:

  • (User)

    the authenticated user this client is performing actions as.



# File 'lib/ruqqus/client.rb', line 34

#tokenToken

Returns the OAuth2 token that grants the client authentication.

Returns:

  • (Token)

    the OAuth2 token that grants the client authentication.



# File 'lib/ruqqus/client.rb', line 30

Instance Method Details

#comment(comment_id) ⇒ Comment

Retrieves the Ruqqus::Comment with the specified name.

Parameters:

  • comment_id (String)

    the ID of the comment to retrieve.

Returns:

Raises:

  • (ArgumentError)

    when comment_id is nil or value does match the VALID_POST regular expression.

  • (Error)

    when a comment with the specified ID does not exist.



121
122
123
124
125
# File 'lib/ruqqus/client.rb', line 121

def comment(comment_id)
  raise(ArgumentError, 'comment_id cannot be nil') unless comment_id
  raise(ArgumentError, 'invalid comment ID') unless VALID_POST.match?(comment_id)
  Comment.from_json(http_get("#{Routes::COMMENT}#{comment_id}"))
end

#comment_create(body, post, comment = nil) ⇒ Comment?

Note:

This method is restricted to 6/minute, and will fail when that limit is exceeded.

Submits a new comment on a post.

Parameters:

  • body (String)

    the text content of the post (supports Markdown)

  • post (Post, String)

    a Post instance or the unique ID of a post.

  • comment (Comment, String) (defaults to: nil)

    a Ruqqus::Comment with the post to reply under, or nil to reply directly to the post.

Returns:

  • (Comment?)

    the comment that was submitted, or nil if an error occurred.



141
142
143
144
145
# File 'lib/ruqqus/client.rb', line 141

def comment_create(body, post, comment = nil)
  pid = post.to_s
  parent = comment ? 't3_' + comment.to_s : 't2_' + pid
  comment_submit(parent, pid, body)
end

#comment_delete(comment) ⇒ Boolean

Deletes an existing comment.

Parameters:

Returns:

  • (Boolean)

    true if deletion completed without error, otherwise false.



171
172
173
174
175
# File 'lib/ruqqus/client.rb', line 171

def comment_delete(comment)
  id = comment.is_a?(Comment) ? comment.id : comment.sub(/^t3_/, '')
  url = "#{Routes::API_BASE}/delete/comment/#{id}"
  http_post(url).empty? rescue false
end

#comment_reply(body, comment) ⇒ Comment?

Note:

This method is restricted to 6/minute, and will fail when that limit is exceeded.

Submits a new comment on a post.

Parameters:

  • body (String)

    the text content of the comment (supports Markdown)

  • comment (Comment, String)

    a Ruqqus::Comment instance or the unique ID of a comment.

Returns:

  • (Comment?)

    the comment that was submitted, or nil if an error occurred.



156
157
158
159
160
161
162
163
# File 'lib/ruqqus/client.rb', line 156

def comment_reply(body, comment)
  if comment.is_a?(Comment)
    comment_submit(comment.fullname, comment.post_id, body)
  else
    comment = self.comment(comment.to_s)
    comment_submit(comment.fullname, comment.post_id, body)
  end
end

#each_guild(sort = :subs) {|guild| ... } ⇒ self

Note:

An API invocation is required for every 25 items that are yielded to the block, so observing brief pauses at these intervals is an expected behavior.

Enumerates through each post in the specified guild, and yields each one to a block.

Parameters:

  • sort (Symbol) (defaults to: :subs)

    a symbol to determine the sorting method, valid values include :trending, :subs, :new.

Yield Parameters:

Returns:

  • (self)

Raises:

  • (LocalJumpError)

    when a block is not supplied to the method.



297
298
299
300
301
302
303
304
305
306
307
308
309
310
# File 'lib/ruqqus/client.rb', line 297

def each_guild(sort = :subs)
  raise(LocalJumpError, 'block required') unless block_given?

  page = 1
  loop do
    params = { sort: sort, page: page }
    json = http_get(Routes::GUILDS, headers(params: params))
    break if json[:error]
    json[:data].each { |hash| yield Guild.from_json(hash) }
    break if json[:data].size < 25
    page += 1
  end
  self
end

#each_guild_comment(guild) {|yields| ... } ⇒ self

Enumerates through each comment in a guild, yielding each to a block.

Parameters:

  • guild (Guild, String)

    a Guild instance, or the name of the guild to query.

Yield Parameters:

Returns:

  • (self)

Raises:

  • (LocalJumpError)

    when a block is not supplied to the method.



355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/ruqqus/client.rb', line 355

def each_guild_comment(guild)
  raise(LocalJumpError, 'block required') unless block_given?
  name = guild.to_s
  raise(ArgumentError, 'invalid guild name') unless Ruqqus::VALID_GUILD.match?(name)

  page = 1
  loop do
    params = { page: page }
    json = http_get("#{Routes::GUILD}#{name}/comments", headers(params: params))
    break if json[:error]

    json[:data].each { |hash| yield Comment.from_json(hash) }
    break if json[:data].size < 25
    page += 1
  end

  self
end

#each_guild_post(guild, **opts) {|post| ... } ⇒ self

Note:

An API invocation is required for every 25 items that are yielded to the block, so observing brief pauses at these intervals is an expected behavior.

Enumerates through each post in a guild, yielding each to a block.

Parameters:

  • guild (Guild, String)

    a Guild instance, or the name of the guild to query.

  • opts (Hash)

    the options hash.

Options Hash (**opts):

  • :sort (Symbol) — default: :new

    Valid: :new, :top, :hot, :activity, :disputed

  • :filter (Symbol) — default: :all

    Valid: :all, :day, :week, :month, :year

Yield Parameters:

  • post (Post)

    yields a Post to the block.

Returns:

  • (self)

Raises:

  • (LocalJumpError)

    when a block is not supplied to the method.



325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/ruqqus/client.rb', line 325

def each_guild_post(guild, **opts)
  raise(LocalJumpError, 'block required') unless block_given?
  name = guild.to_s
  raise(ArgumentError, 'invalid guild name') unless Ruqqus::VALID_GUILD.match?(name)

  sort = opts[:sort] || :new
  filter = opts[:filter] || :all

  page = 1
  loop do
    params = { page: page, sort: sort, t: filter }
    json = http_get("#{Routes::GUILD}#{name}/listing", headers(params: params))
    break if json[:error]

    json[:data].each { |hash| yield Post.from_json(hash) }
    break if json[:data].size < 25
    page += 1
  end

  self
end

#each_home_post {|post| ... } ⇒ self

Note:

The front page uses a unique algorithm that is essentially "hot", but for guilds the user is subscribed to.

Enumerates through every post on the "front page", yielding each post to a block.

Yield Parameters:

  • post (Post)

    yields a Post to the block.

Returns:

  • (self)

Raises:

  • (LocalJumpError)


432
433
434
435
436
437
438
439
440
441
442
443
# File 'lib/ruqqus/client.rb', line 432

def each_home_post
  raise(LocalJumpError, 'block required') unless block_given?
  page = 1
  loop do
    json = http_get(Routes::FRONT_PAGE, headers(params: { page: page }))
    break if json[:error]
    json[:data].each { |hash| yield Post.from_json(hash) }
    break if json[:data].size < 25
    page += 1
  end
  self
end

#each_post(**opts) {|post| ... } ⇒ self

Note:

An API invocation is required for every 25 items that are yielded to the block, so observing brief pauses at these intervals is an expected behavior.

Enumerates through every post on Ruqqus, yielding each post to a block.

Parameters:

  • opts (Hash)

    the options hash.

Options Hash (**opts):

  • :sort (Symbol) — default: :new

    Valid: :new, :top, :hot, :activity, :disputed

  • :filter (Symbol) — default: :all

    Valid: :all, :day, :week, :month, :year

Yield Parameters:

  • post (Post)

    yields a post to the block.

Returns:

  • (self)

Raises:

  • (LocalJumpError)

    when a block is not supplied to the method.



407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
# File 'lib/ruqqus/client.rb', line 407

def each_post(**opts)
  raise(LocalJumpError, 'block required') unless block_given?

  sort = opts[:sort] || :new
  filter = opts[:filter] || :all

  page = 1
  loop do
    params = { page: page, sort: sort, t: filter }
    json = http_get(Routes::ALL_LISTINGS, headers(params: params))
    break if json[:error]
    json[:data].each { |hash| yield Post.from_json(hash) }
    break if json[:data].size < 25
    page += 1
  end
  self
end

#each_post_comment(post) {|yields| ... } ⇒ self

Note:

This method is very inefficient, as it the underlying API does not yet implement it, therefore each comment in the entire guild must be searched through.

Enumerates through each comment in a guild, yielding each to a block.

Parameters:

  • post (Post, String)

    a Post instance, or the unique ID of the post to query.

Yield Parameters:

Returns:

  • (self)

Raises:

  • (LocalJumpError)

    when a block is not supplied to the method.



384
385
386
387
388
389
390
391
392
393
# File 'lib/ruqqus/client.rb', line 384

def each_post_comment(post)
  # TODO: This is extremely inefficient, but will have to do until it gets implemented in the API
  raise(LocalJumpError, 'block required') unless block_given?
  post = self.post(post) unless post.is_a?(Post)
  each_guild_comment(post.guild_name) do |comment|
    next unless comment.post_id == post.id
    yield comment
  end
  self
end

#each_user_comment(user) {|comment| ... } ⇒ self

Note:

An API invocation is required for every 25 items that are yielded to the block, so observing brief pauses at these intervals is an expected behavior.

Enumerates through each comment of a user, yielding each to a block.

Parameters:

  • user (User, String)

    a User instance or the name of the account to query.

Yield Parameters:

Returns:

  • (self)

Raises:

  • (LocalJumpError)

    when a block is not supplied to the method.



283
284
285
286
# File 'lib/ruqqus/client.rb', line 283

def each_user_comment(user)
  raise(LocalJumpError, 'block required') unless block_given?
  each_submission(user, Comment, 'comments') { |obj| yield obj }
end

#each_user_post(user) {|post| ... } ⇒ self

Note:

An API invocation is required for every 25 items that are yielded to the block, so observing brief pauses at these intervals is an expected behavior.

Enumerates through each post of a user, yielding each to a block.

Parameters:

  • user (User, String)

    a User instance or the name of the account to query.

Yield Parameters:

  • post (Post)

    yields a Post to the block.

Returns:

  • (self)

Raises:

  • (LocalJumpError)

    when a block is not supplied to the method.



269
270
271
272
# File 'lib/ruqqus/client.rb', line 269

def each_user_post(user)
  raise(LocalJumpError, 'block required') unless block_given?
  each_submission(user, Post, 'listing') { |obj| yield obj }
end

#guild(guild_name) ⇒ Guild

Retrieves the Guild with the specified name.

Parameters:

  • guild_name (String)

    the name of the Ruqqus guild to retrieve.

Returns:

Raises:

  • (ArgumentError)

    when guild_name is nil or value does match the VALID_GUILD regular expression.

  • (Error)

    thrown when guild does not exist.



91
92
93
94
95
# File 'lib/ruqqus/client.rb', line 91

def guild(guild_name)
  raise(ArgumentError, 'guild_name cannot be nil') unless guild_name
  raise(ArgumentError, 'invalid guild name') unless VALID_GUILD.match?(guild_name)
  Guild.from_json(http_get("#{Routes::GUILD}#{guild_name}"))
end

#post(post_id) ⇒ Post

Retrieves the Post with the specified name.

Parameters:

  • post_id (String)

    the ID of the post to retrieve.

Returns:

Raises:

  • (ArgumentError)

    when post_id is nil or value does match the VALID_POST regular expression.

  • (Error)

    thrown when a post with the specified ID does not exist.



106
107
108
109
110
# File 'lib/ruqqus/client.rb', line 106

def post(post_id)
  raise(ArgumentError, 'post_id cannot be nil') unless post_id
  raise(ArgumentError, 'invalid post ID') unless VALID_POST.match?(post_id)
  Post.from_json(http_get("#{Routes::POST}#{post_id}"))
end

#post_create(guild, title, body = nil, **opts) ⇒ Post?

Note:

This method is restricted to 6/minute, and will fail when that limit is exceeded.

Creates a new post on Ruqqus as the current user.

Parameters:

  • guild (Guild, String)

    a Guild instance or the name of the guild to post to.

  • title (String)

    the title of the post to create.

  • body (String?) (defaults to: nil)

    the text body of the post, which can be nil if supplying URL or image upload.

  • opts (Hash)

    The options hash to specify a link or image to upload.

Options Hash (**opts):

  • :image (String) — default: nil

    the path to an image file to upload.

  • :url (String) — default: nil

    a URL to share with the post.

  • :imgur_client (String) — default: nil

    an Imgur client ID to automatically share images via Imgur instead of direct upload.

Returns:

  • (Post?)

    the newly created Post instance, or nil if an error occurred.

Raises:

  • (ArgumentError)


207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
# File 'lib/ruqqus/client.rb', line 207

def post_create(guild, title, body = nil, **opts)
  name = guild.is_a?(Guild) ? guild.name : guild.strip.sub(/^\+/, '')
  raise(ArgumentError, 'invalid guild name') unless Ruqqus::VALID_GUILD.match?(name)
  raise(ArgumentError, 'title cannot be nil or empty') unless title && !title.empty?
  params = { title: title, board: name, body: body }

  if opts[:image]
    if opts[:imgur_client]
      params[:url] = Ruqqus.imgur_upload(opts[:imgur_client], opts[:image])
    else
      params[:file] = File.new(opts[:image])
    end
  elsif opts[:url]
    raise(ArgumentError, 'invalid URI') unless URI.regexp =~ opts[:url]
    params[:url] = opts[:url]
  end

  if [params[:body], params[:image], params[:url]].none?
    raise(ArgumentError, 'text body cannot be nil or empty without URL or image') if body.nil? || body.empty?
  end
  Post.from_json(http_post(Routes::SUBMIT, params)) rescue nil
end

#post_delete(post) ⇒ Boolean

Deletes an existing post.

Parameters:

  • post (Comment, String)

    a Post instance, or the unique ID of the post to delete.

Returns:

  • (Boolean)

    true if deletion completed without error, otherwise false.



183
184
185
186
187
# File 'lib/ruqqus/client.rb', line 183

def post_delete(post)
  id = post.is_a?(Post) ? post.id : post.sub(/^t3_/, '')
  url = "#{Routes::API_BASE}/delete_post/#{id}"
  http_post(url).empty? rescue false
end

#token_refreshed {|token| ... } ⇒ void #token_refreshedvoid

This method returns an undefined value.

Overloads:

  • #token_refreshed {|token| ... } ⇒ void

    Sets a callback to be invoked when the token is refreshed, and a new access token is assigned.

    Yield Parameters:

    • token (Token)

      yields the newly refreshed Token to the block.

  • #token_refreshedvoid

    When called without a block, clears any callback that was previously assigned.



462
463
464
# File 'lib/ruqqus/client.rb', line 462

def token_refreshed(&block)
  @refreshed = block_given? ? block : nil
end

#user(username) ⇒ User

Retrieves the User with the specified username.

Parameters:

  • username (String)

    the username of the Ruqqus account to retrieve.

Returns:

Raises:

  • (ArgumentError)

    when username is nil or value does match the VALID_USERNAME regular expression.

  • (Error)

    thrown when user account does not exist.



76
77
78
79
80
# File 'lib/ruqqus/client.rb', line 76

def user(username)
  raise(ArgumentError, 'username cannot be nil') unless username
  raise(ArgumentError, 'invalid username') unless VALID_USERNAME.match?(username)
  User.from_json(http_get("#{Routes::USER}#{username}"))
end

#vote_comment(comment, value = 1) ⇒ Boolean

Places a vote on a comment.

Parameters:

  • comment (Comment, String)

    a Ruqqus::Comment instance, or the unique ID of a comment.

  • value (Integer) (defaults to: 1)

    the vote value to place, either -1, 0, or 1.

Returns:

  • (Boolean)

    true if vote was placed successfully, otherwise false.



252
253
254
# File 'lib/ruqqus/client.rb', line 252

def vote_comment(comment, value = 1)
  submit_vote(comment.to_s, value, 'https://ruqqus.com/api/v1/vote/comment/')
end

#vote_post(post, value = 1) ⇒ Boolean

Places a vote on a post.

Parameters:

  • post (Post, String)

    a Post instance, or the unique ID of a post.

  • value (Integer) (defaults to: 1)

    the vote value to place, either -1, 0, or 1.

Returns:

  • (Boolean)

    true if vote was placed successfully, otherwise false.



241
242
243
# File 'lib/ruqqus/client.rb', line 241

def vote_post(post, value = 1)
  submit_vote(post.to_s, value, 'https://ruqqus.com/api/v1/vote/post/')
end