ruqqus 0.1.0 → 1.1.3

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'
@@ -0,0 +1,61 @@
1
+ module Ruqqus
2
+ ##
3
+ # Describes a trophy that can be earned/issued to an account for specific accomplishments.
4
+ class Badge
5
+
6
+ ##
7
+ # @!attribute [r] name
8
+ # @return [String?] the name of the badge.
9
+
10
+ ##
11
+ # @!attribute [r] text
12
+ # @return [String?] a brief description of the badge.
13
+
14
+ ##
15
+ # @!attribute [r] url
16
+ # @return [String?] the URL associated with the badge, or `nil` if not defined.
17
+
18
+ ##
19
+ # @!attribute [r] created_utc
20
+ # @return [Integer?] the time the badge was earned in seconds since the Unix epoch, or `0` if not defined.
21
+
22
+ ##
23
+ # @!attribute [r] created
24
+ # @return [Time?] the time the badge was earned, or `nil` if not defined.
25
+
26
+ ##
27
+ # Creates a new instance of the {Badge} class.
28
+ #
29
+ # @param data [Hash] the parsed JSON payload defining this instance.
30
+ def initialize(data)
31
+ @data = data || raise(ArgumentError, 'data cannot be nil')
32
+ end
33
+
34
+ def name
35
+ @data[:name]
36
+ end
37
+
38
+ def text
39
+ @data[:text]
40
+ end
41
+
42
+ def url
43
+ @data[:url]
44
+ end
45
+
46
+ def created_utc
47
+ @data[:created_utc]
48
+ end
49
+
50
+ def created
51
+ #noinspection RubyYardReturnMatch
52
+ @data[:created_utc] ? Time.at(@data[:created_utc]) : nil
53
+ end
54
+
55
+ ##
56
+ # @return [String] the string representation of the object.
57
+ def to_s
58
+ @data[:text] || inspect
59
+ end
60
+ end
61
+ end
@@ -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
@@ -0,0 +1,103 @@
1
+ module Ruqqus
2
+
3
+ ##
4
+ # Represents a Ruqqus guild.
5
+ class Guild < ItemBase
6
+
7
+ ##
8
+ # @!attribute [r] name
9
+ # @return [String] the name of the guild.
10
+
11
+ ##
12
+ # @!attribute [r] member_count
13
+ # @return [Integer] the number of members subscribed to the guild.
14
+
15
+ ##
16
+ # @!attribute [r] fullname
17
+ # @return [String] the full ID of the guild.
18
+
19
+ ##
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.
42
+
43
+ ##
44
+ # @return [Boolean] `true` if the guild contains adult content and flagged as NSFW, otherwise `false`.
45
+ def nsfw?
46
+ @data[:over_18]
47
+ end
48
+
49
+ ##
50
+ # @return [Boolean] `true` if guild is private and required membership to view content, otherwise `false`.
51
+ def private?
52
+ !!@data[:is_private]
53
+ end
54
+
55
+ ##
56
+ # @return [Boolean] `true` if posting is restricted byy guild masters, otherwise `false`.
57
+ def restricted?
58
+ !!@data[:is_restricted]
59
+ end
60
+
61
+ ##
62
+ # @return [String] the string representation of the object.
63
+ def to_s
64
+ @data[:name] || inspect
65
+ end
66
+
67
+ def description
68
+ @data[:description]
69
+ end
70
+
71
+ def banner_url
72
+ @data[:banner_url]
73
+ end
74
+
75
+ def description_html
76
+ @data[:description_html]
77
+ end
78
+
79
+ def profile_url
80
+ @data[:profile_url]
81
+ end
82
+
83
+ def color
84
+ @data[:color]
85
+ end
86
+
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]
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,68 @@
1
+
2
+ module Ruqqus
3
+
4
+ ##
5
+ # @abstract
6
+ # Base class for all all major API types.
7
+ class ItemBase
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
+
25
+ ##
26
+ # @return [Boolean] `true` if item has been banned, otherwise `false`.
27
+ def banned?
28
+ !!@data[:is_banned]
29
+ end
30
+
31
+ ##
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
+
37
+ def created_utc
38
+ @data[:created_utc]
39
+ end
40
+
41
+ def created
42
+ Time.at(created_utc)
43
+ end
44
+
45
+ def id
46
+ @data[:id]
47
+ end
48
+
49
+ def permalink
50
+ @data[:permalink]
51
+ end
52
+
53
+ ##
54
+ # Loads the object from a JSON-formatted string.
55
+ #
56
+ # @param json [String,Hash] a JSON string representing the object.
57
+ #
58
+ # @return [Object] the loaded object.
59
+ def self.from_json(json)
60
+ obj = allocate
61
+ data = json.is_a?(Hash) ? json : JSON.parse(json, symbolize_names: true)
62
+ obj.instance_variable_set(:@data, data)
63
+ obj
64
+ end
65
+
66
+ private_class_method :new
67
+ end
68
+ end