ruqqus 1.0.0 → 1.1.4

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