ruqqus 1.0.0 → 1.1.4

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.
@@ -0,0 +1,68 @@
1
+ module Ruqqus
2
+
3
+ ##
4
+ # A module containing constants that define the method routes for the Ruqqus REST API.
5
+ module Routes
6
+
7
+ ##
8
+ # The Ruqqus API version.
9
+ API_VERSION = 1
10
+
11
+ ##
12
+ # The top-level site URL.
13
+ HOME = 'https://ruqqus.com'.freeze
14
+
15
+ ##
16
+ # The base URL for the Ruqqus REST API.
17
+ API_BASE = "#{HOME}/api/v#{API_VERSION}".freeze
18
+
19
+ ##
20
+ # The endpoint for the GET method to obtain user information.
21
+ USER = "#{API_BASE}/user/".freeze
22
+
23
+ ##
24
+ # The endpoint for the GET method to obtain guild information.
25
+ GUILD = "#{API_BASE}/guild/".freeze
26
+
27
+ ##
28
+ # The endpoint for the GET method to obtain post information.
29
+ POST = "#{API_BASE}/post/".freeze
30
+
31
+ ##
32
+ # The endpoint for the GET method to obtain comment information.
33
+ COMMENT = "#{API_BASE}/comment/".freeze
34
+
35
+ ##
36
+ # The endpoint for the POST method to place a vote on a post.
37
+ POST_VOTE = "#{API_BASE}/vote/post/".freeze
38
+
39
+ ##
40
+ # The endpoint for the GET method to query guild availability.
41
+ GUILD_AVAILABLE = "#{HOME}/api/board_available/".freeze
42
+
43
+ ##
44
+ # The endpoint for the GET method to query username availability.
45
+ USERNAME_AVAILABLE = "#{HOME}/api/is_available/".freeze
46
+
47
+ ##
48
+ # The endpoint for the POST method to submit a post.
49
+ SUBMIT = "#{Routes::API_BASE}/submit/".freeze
50
+
51
+ ##
52
+ # The endpoint for the GET method to get the current user.
53
+ IDENTITY = "#{Routes::API_BASE}/identity".freeze
54
+
55
+ ##
56
+ # The endpoint for the GET method to get the guild listings.
57
+ GUILDS = "#{Routes::API_BASE}/guilds".freeze
58
+
59
+ ##
60
+ # The endpoint for the GET method to get the front page listings.
61
+ FRONT_PAGE = "#{Routes::API_BASE}/front/listing".freeze
62
+
63
+ ##
64
+ # The endpoint for the GET method to get all post listings.
65
+ ALL_LISTINGS = "#{Routes::API_BASE}/all/listing".freeze
66
+
67
+ end
68
+ end
@@ -0,0 +1,124 @@
1
+ module Ruqqus
2
+
3
+ ##
4
+ # Represents a Ruqqus [OAuth2](https://oauth.net/2/) access token.
5
+ class Token
6
+
7
+ ##
8
+ # @!attribute [r] access_token
9
+ # @return [String] the access token value.
10
+
11
+ ##
12
+ # @!attribute [r] refresh_token
13
+ # @return [String] the refresh token value.
14
+
15
+ ##
16
+ # @!attribute [r] expires
17
+ # @return [Time] the time the token expires and will require a refresh.
18
+
19
+ ##
20
+ # @!attribute [r] type
21
+ # @return [String] the token type to specify in the HTTP header.
22
+
23
+ ##
24
+ # @!attribute [r] scopes
25
+ # @return [Array<Symbol>] an array of scopes this token authorizes.
26
+
27
+ ##
28
+ # Grants access to a user account and returns an a newly created {Token} to use as authentication for it.
29
+ #
30
+ # @param client_id [String] the ID of client application.
31
+ # @param client_secret [String] the secret of the client application.
32
+ # @param code [String] the code received in the redirect response when the user requested API access.
33
+ # @param persist [Boolean] `true` if token will be reusable, otherwise `false`.
34
+ #
35
+ # @return [Token] a newly created {Token} object.
36
+ def initialize(client_id, client_secret, code, persist = true)
37
+ headers = { 'User-Agent': Client::USER_AGENT, 'Accept': 'application/json', 'Content-Type': 'application/json' }
38
+ params = { code: code, client_id: client_id, client_secret: client_secret, grant_type: 'code', permanent: persist }
39
+ resp = RestClient.post('https://ruqqus.com/oauth/grant', params, headers )
40
+ @data = JSON.parse(resp.body, symbolize_names: true)
41
+
42
+ raise(Ruqqus::Error, 'failed to grant access for token') if @data[:oauth_error]
43
+ end
44
+
45
+ def access_token
46
+ @data[:access_token]
47
+ end
48
+
49
+ def refresh_token
50
+ @data[:refresh_token]
51
+ end
52
+
53
+ def type
54
+ @data[:token_type]
55
+ end
56
+
57
+ def expires
58
+ Time.at(@data[:expires_at])
59
+ end
60
+
61
+ def scopes
62
+ @data[:scopes].split(',').map(&:to_sym)
63
+ end
64
+
65
+ ##
66
+ # Refreshes the access token and resets its time of expiration.
67
+ #
68
+ # @return [void]
69
+ def refresh(client_id, client_secret)
70
+ headers = { 'User-Agent': Client::USER_AGENT, Authorization: "Bearer #{access_token}" }
71
+ params = { client_id: client_id, client_secret: client_secret, refresh_token: refresh_token, grant_type: 'refresh' }
72
+ resp = RestClient.post('https://ruqqus.com/oauth/grant', params, headers )
73
+
74
+ data = JSON.parse(resp.body, symbolize_names: true)
75
+ raise(Ruqqus::Error, 'failed to refresh authentication token') unless resp.code == 200 || data[:oauth_error]
76
+ @data.merge!(data)
77
+ end
78
+
79
+ ##
80
+ # @return [Boolean] `true` if token is expired, otherwise `false`.
81
+ def expired?
82
+ expires <= Time.now
83
+ end
84
+
85
+ ##
86
+ # @return [String] the object as a JSON-formatted string.
87
+ def to_json(*_unused_)
88
+ @data.to_json
89
+ end
90
+
91
+ ##
92
+ # Saves this token in JSON format to the specified file.
93
+ #
94
+ # @param filename [String] the path to a file where the token will be written to.
95
+ # @return [Integer] the number of bytes written.
96
+ # @note **Security Alert:** The token is essentially the equivalent to login credentials in regards to security,
97
+ # so it is important to not share or store it somewhere that it can be easily compromised.
98
+ def save_json(filename)
99
+ File.open(filename, 'wb') { |io| io.write(to_json) }
100
+ end
101
+
102
+ ##
103
+ # Loads a token in JSON format from a file.
104
+ #
105
+ # @param filename [String] the path to a file where the token is written to.
106
+ # @return [Token] a newly created {Token} instance.
107
+ def self.load_json(filename)
108
+ from_json(File.read(filename))
109
+ end
110
+
111
+ ##
112
+ # Loads the object from a JSON-formatted string.
113
+ #
114
+ # @param json [String,Hash] a JSON string representing the object, or the parsed Hash of the JSON (symbol keys).
115
+ #
116
+ # @return [Object] the loaded object.
117
+ def self.from_json(payload)
118
+ data = payload.is_a?(Hash) ? payload: JSON.parse(payload, symbolize_names: true)
119
+ token = allocate
120
+ token.instance_variable_set(:@data, data)
121
+ token
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,11 @@
1
+
2
+ # Bootstrap script to load all concrete Ruqqus types
3
+
4
+ require_relative 'types/item_base'
5
+ require_relative 'types/submission'
6
+ require_relative 'types/comment'
7
+ require_relative 'types/guild'
8
+ require_relative 'types/post'
9
+ require_relative 'types/badge'
10
+ require_relative 'types/title'
11
+ require_relative 'types/user'
File without changes
@@ -0,0 +1,44 @@
1
+
2
+ module Ruqqus
3
+
4
+ ##
5
+ # Describes a comment in a post.
6
+ class Comment < Submission
7
+
8
+ ##
9
+ # @!attribute [r] level
10
+ # @return [Integer] the level of "nesting" in the comment tree, starting at `1` when in direct reply to the post.
11
+
12
+ ##
13
+ # @!attribute parent_id
14
+ # @return [String] the unique ID of the parent for this comment.
15
+
16
+ ##
17
+ # @!attribute [r] post_id
18
+ # @return [String] the ID of the post this comment belongs to.
19
+
20
+ ##
21
+ # @return [Boolean] `true` if the comment's parent is comment, otherwise `false` if it is a post.
22
+ def parent_comment?
23
+ level > 1
24
+ end
25
+
26
+ ##
27
+ # @return [Boolean] `true` if the comment's parent is post, otherwise `false` if it is a comment.
28
+ def parent_post?
29
+ level == 1
30
+ end
31
+
32
+ def level
33
+ @data[:level]
34
+ end
35
+
36
+ def parent_id
37
+ @data[:parent]
38
+ end
39
+
40
+ def post_id
41
+ @data[:post]
42
+ end
43
+ end
44
+ end
@@ -5,28 +5,40 @@ module Ruqqus
5
5
  class Guild < ItemBase
6
6
 
7
7
  ##
8
- # @return [String] the name of the guild.
9
- def name
10
- @data[:name]
11
- end
8
+ # @!attribute [r] name
9
+ # @return [String] the name of the guild.
12
10
 
13
11
  ##
14
- # @return [Integer] the number of members subscribed to the guild.
15
- def member_count
16
- @data[:subscriber_count]&.to_i || 0
17
- end
12
+ # @!attribute [r] member_count
13
+ # @return [Integer] the number of members subscribed to the guild.
18
14
 
19
15
  ##
20
- # @return [Integer] the number of guild masters who moderate this guild.
21
- def gm_count
22
- @data[:mods_count]&.to_i || 0
23
- end
16
+ # @!attribute [r] fullname
17
+ # @return [String] the full ID of the guild.
24
18
 
25
19
  ##
26
- # @return [String] the full ID of the guild.
27
- def full_name
28
- @data[:fullname]
29
- end
20
+ # @!attribute [r] guildmaster_count
21
+ # @return [Integer] the number of guild masters who moderate this guild.
22
+
23
+ ##
24
+ # @!attribute [r] profile_url
25
+ # @return [String] the URL for the profile image associated with the guild.
26
+
27
+ ##
28
+ # @!attribute [r] color
29
+ # @return [String] the accent color used for the guild, in HTML format.
30
+
31
+ ##
32
+ # @!attribute [r] description
33
+ # @return [String] the description of the guild.
34
+
35
+ ##
36
+ # @!attribute [r] description_html
37
+ # @return [String] the description of the guild in HTML format.
38
+
39
+ ##
40
+ # @!attribute [r] banner_url
41
+ # @return [String] the URL for the banner image associated with the guild.
30
42
 
31
43
  ##
32
44
  # @return [Boolean] `true` if the guild contains adult content and flagged as NSFW, otherwise `false`.
@@ -47,39 +59,45 @@ module Ruqqus
47
59
  end
48
60
 
49
61
  ##
50
- # @return [String] the description of the guild.
51
- def description
52
- @data[:description]
62
+ # @return [String] the string representation of the object.
63
+ def to_s
64
+ @data[:name] || inspect
53
65
  end
54
66
 
55
- ##
56
- # @return [String] the description of the guild in HTML format.
57
- def description_html
58
- @data[:description_html]
67
+ def description
68
+ @data[:description]
59
69
  end
60
70
 
61
- ##
62
- # @return [String] the URL for the banner image associated with the guild.
63
71
  def banner_url
64
72
  @data[:banner_url]
65
73
  end
66
74
 
67
- ##
68
- # @return [String] the URL for the profile image associated with the guild.
75
+ def description_html
76
+ @data[:description_html]
77
+ end
78
+
69
79
  def profile_url
70
80
  @data[:profile_url]
71
81
  end
72
82
 
73
- ##
74
- # @return [String] the accent color used for the guild, in HTML format.
75
83
  def color
76
84
  @data[:color]
77
85
  end
78
86
 
79
- ##
80
- # @return [String] the string representation of the object.
81
- def to_s
82
- @data[:name] || inspect
87
+ def name
88
+ @data[:name]
89
+ end
90
+
91
+ def member_count
92
+ @data[:subscriber_count]&.to_i || 0
93
+ end
94
+
95
+ def guildmaster_count
96
+ @data[:mods_count]&.to_i || 0
97
+ end
98
+
99
+ def fullname
100
+ @data[:fullname]
83
101
  end
84
102
  end
85
103
  end
@@ -1,4 +1,3 @@
1
- require 'json'
2
1
 
3
2
  module Ruqqus
4
3
 
@@ -7,6 +6,22 @@ module Ruqqus
7
6
  # Base class for all all major API types.
8
7
  class ItemBase
9
8
 
9
+ ##
10
+ # @!attribute [r] permalink
11
+ # @return [String] a relative link to this item.
12
+
13
+ ##
14
+ # @!attribute [r] created_utc
15
+ # @return [Integer] the time the item was created, in seconds since the Unix epoch.
16
+
17
+ ##
18
+ # @!attribute [r] created
19
+ # @return [Time] the time the item was created.
20
+
21
+ ##
22
+ # @!attribute [r] id
23
+ # @return [String] a unique ID for this item.
24
+
10
25
  ##
11
26
  # @return [Boolean] `true` if item has been banned, otherwise `false`.
12
27
  def banned?
@@ -14,31 +29,23 @@ module Ruqqus
14
29
  end
15
30
 
16
31
  ##
17
- # @return [Integer] the time the item was created, in seconds since the Unix epoch.
32
+ # @return [Boolean] `true` if this object is equal to another, otherwise `false`.
33
+ def ==(other)
34
+ self.class == other.class && id == other.id
35
+ end
36
+
18
37
  def created_utc
19
38
  @data[:created_utc]
20
39
  end
21
40
 
22
- ##
23
- # @return [Time] the time the item was created.
24
41
  def created
25
42
  Time.at(created_utc)
26
43
  end
27
44
 
28
- ##
29
- # @return [String] a unique ID for this item.
30
45
  def id
31
46
  @data[:id]
32
47
  end
33
48
 
34
- ##
35
- # @return [Boolean] `true` if this object is equal to another, otherwise `false`.
36
- def ==(other)
37
- self.class == other.class && id == other.id
38
- end
39
-
40
- ##
41
- # @return [String] a relative link to this item.
42
49
  def permalink
43
50
  @data[:permalink]
44
51
  end
@@ -46,10 +53,13 @@ module Ruqqus
46
53
  ##
47
54
  # Loads the object from a JSON-formatted string.
48
55
  #
56
+ # @param json [String,Hash] a JSON string representing the object.
57
+ #
49
58
  # @return [Object] the loaded object.
50
59
  def self.from_json(json)
51
60
  obj = allocate
52
- obj.instance_variable_set(:@data, JSON.parse(json, symbolize_names: true))
61
+ data = json.is_a?(Hash) ? json : JSON.parse(json, symbolize_names: true)
62
+ obj.instance_variable_set(:@data, data)
53
63
  obj
54
64
  end
55
65