ruqqus 1.0.0 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a01559e849bc401630ad22455035ae0d00891f34e294eb00ea9780b82c932620
4
- data.tar.gz: 56f99a8360fd71eb431dcf3d263f106412766f2afd51cdcda237aa643b9bb76d
3
+ metadata.gz: ddc434cd09ab2ab6f3c4460bd6d309e4311af4c6349e5fba9cdb89cc257133ab
4
+ data.tar.gz: 47f92d628a870e88ef893dea295c86e4bfd9b770b7fafa23caf988ba8bd3c798
5
5
  SHA512:
6
- metadata.gz: 1417f32ba0e67970c1305c0f2cf48873fd6c4a6ffe5c6df98cb799f1459bf0ec364762a78f3eff4fb756b367e13650eeea7b8a62b516623e476b4d8488b7a167
7
- data.tar.gz: 485fadd5ed20a3d6b1290ce709c9d5881f0f4a5ec890196fa16deff6b500ecfeb0f1f030a4dcc5eaeff853c9d2ac22a6180d080ed3cf583bf7679d2b9a10aa7c
6
+ metadata.gz: c01173a089510e8deb760dcab13144cfa5e5678ddfa5a2e136066c0de3d61cb192b30eadb461d28de588ba711c6ebe66d1c7b92fd83a8e2dff3694d51fb58611
7
+ data.tar.gz: 05ac950bf17799b45cfef60b3734f432c05906ab7caf8adf8b7e14898b53e46027284a671b9db32a70f45500382ff44dbec84e7b5fd12932ff76351e86d245ee
data/.gitignore CHANGED
@@ -164,4 +164,8 @@ modules.xml
164
164
  # Ignore all local history of files
165
165
  .history
166
166
 
167
- # End of https://www.toptal.com/developers/gitignore/api/ruby,rubymine+all,visualstudiocode
167
+ # End of https://www.toptal.com/developers/gitignore/api/ruby,rubymine+all,visualstudiocode
168
+
169
+ # JSON file containing the token for test user during development
170
+ /token.json
171
+ /test.rb
@@ -2,6 +2,53 @@
2
2
 
3
3
  Documentation for library API changes.
4
4
 
5
- ## Version 1.0
5
+ Versioning system:
6
+
7
+ `MAJOR.MINOR.REVISION`
8
+
9
+ * `MAJOR` Corresponds to the native Ruqqus API major version
10
+ * `MINOR` Indicates possible breaking API changes for existing code
11
+ * `REVISION` Added functionality, bug-fixes, and other non-breaking alterations
12
+
13
+ ## Version 1.1.3
14
+
15
+ * Implemented browser-based confirmation process
16
+ * Implemented capturing confirmation code from `localhost` OAuth redirects
17
+ * Fixed bug in querying guild/username availability
18
+
19
+ ## Version 1.1.2
20
+
21
+ * Implemented enumerating comments of guilds and posts
22
+
23
+ ## Version 1.1.1
24
+
25
+ * BUGFIX: Added acceptance of variable args to `Token#to_json`
26
+ * BUGFIX: Fixed regex validator in `ruqqus-oauth` for the client ID
27
+ * Separated the client ID/secret from the `Token` class, and placed within `Client` to now handle this logic
28
+
29
+ ## Version 1.1.0
30
+
31
+ * Implemented `Ruqqus::Token` class to handle OAuth2 authentication
32
+ * Added `Ruqqus::Client` class as the primary object for API usage
33
+ * Implemented post creation
34
+ * Implemented comment creation and deletion
35
+ * Implementing querying for reserved usernames/guilds
36
+ * Implemented retrieving posts of guilds
37
+ * Implemented retrieving posts and comments of users
38
+ * Implemented voting on posts and comments as a user
39
+ * Implemented enumerating all guilds
40
+ * Implemented enumerating through all posts
41
+ * Refactored `Ruqqus.user_info` to `Ruqqus::Client#user`
42
+ * Refactored `Ruqqus.guild_info` to `Ruqqus::Client#guild`
43
+ * Refactored `Ruqqus.post_info` to `Ruqqus::Client#post`
44
+ * Refactored `Ruqqus.comment_info` to `Ruqqus::Client#comment`
45
+ * Many improvements in code and better adhesion to DRY principles
46
+ * Added helper method to automate uploading and creating image posts on Ruqqus via Imgur
47
+
48
+ ## Version 1.0.1
49
+
50
+ * Changed validation for `fomr_url` methods in `Post` and `Comment` to also accept relative URIs.
51
+
52
+ ## Version 1.0.0
6
53
 
7
54
  * Initial release, 100% coverage of existing Ruqqus API
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
1
  source 'https://rubygems.org'
2
2
 
3
- # Specify your gem's dependencies in ruqqus.gemspec
3
+ # Dependencies specified in ruqqus.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -6,14 +6,16 @@
6
6
 
7
7
  # Ruqqus
8
8
 
9
- A Ruby API implementation for [Ruqqus](https://ruqqus.com/), an [open-source](https://github.com/ruqqus/ruqqus) platform for online communities, free of censorship and moderator abuse by design.
10
-
11
- While platform is still in Beta at this time and the public API for it is still quite limited, this gem will be actively updated as it continues to grow and is developed.
9
+ A Ruby API implementation for [Ruqqus](https://ruqqus.com/), an [open-source](https://github.com/ruqqus/ruqqus) platform for online communities, free of censorship and moderator abuse by design. [Sign up](https://ruqqus.com/signup?ref=foreverzer0
10
+ ) if you haven't yet!
12
11
 
13
12
  [![Build Status](https://travis-ci.org/ForeverZer0/ruqqus.svg?branch=master)](https://travis-ci.org/ForeverZer0/ruqqus)
14
- [![Gem Version](https://badge.fury.io/rb/ruqqus.svg)](https://badge.fury.io/rb/ruqqus)
13
+ [![Gem Version](https://badge.fury.io/rb/ruqqus.svg)](https://rubygems.org/gems/ruqqus)
15
14
  [![Inline docs](http://inch-ci.org/github/ForeverZer0/ruqqus.svg?branch=master)](http://inch-ci.org/github/ForeverZer0/ruqqus)
16
15
  [![Maintainability](https://api.codeclimate.com/v1/badges/c39f44a706302e4cd340/maintainability)](https://codeclimate.com/github/ForeverZer0/ruqqus/maintainability)
16
+ [![OpenIssues](https://img.shields.io/github/issues/ForeverZer0/ruqqus)](https://github.com/ForeverZer0/ruqqus/issues)
17
+ [![License](https://img.shields.io/github/license/ForeverZer0/ruqqus)](https://opensource.org/licenses/MIT)
18
+ [![Ruby](https://img.shields.io/badge/powered%20by-ruby-red)](https://www.ruby-lang.org/en/)
17
19
 
18
20
  ## Installation
19
21
 
@@ -31,23 +33,156 @@ Or install it yourself as:
31
33
 
32
34
  $ gem install ruqqus
33
35
 
36
+ To use the `ruqqus-oauth` helper to generate user tokens for desktop development:
37
+
38
+ $ gem install ruqqus --development
39
+
40
+ ## Authentication
41
+
42
+ Ruqqus enables 3rd-party client authorization using the [OAuth2 protocol](https://oauth.net/2/). Before it is possible
43
+ to interact with the API, you will need to first [register an application](https://ruqqus.com/settings/apps), which can
44
+ be supply you with an API key/secret pair. This key will allow you to authorize users and grant privileges with an
45
+ assortment of scopes to fit your needs.
46
+
47
+ ### Desktop Development
48
+
49
+ This gem includes a tool to automate obtaining a client code for users, primarily aimed for desktop developers who do
50
+ not have a server running to receive the redirect URL. The tool requires the `mechanize` and `tty-prompt` gems, which
51
+ are not included by default.
52
+
53
+ To install the tool with its dependencies, install this gem with the following flag.
54
+
55
+ $ gem insall ruqqus --development
56
+
57
+ Once installed, simply run `ruqqus-oauth`. You will be prompted to input the API key that was issued by Ruqqus to your
58
+ approved application, and the user credentials for the account you with to authorize, such as that for a bot. Once
59
+ executed, an authorization code will be displayed on-screen, which you can then use to create a token.
60
+
61
+ ```ruby
62
+ require 'ruqqus'
63
+
64
+ client_id = 'XXXXXX' # Received after registering application
65
+ client_secret = 'XXXXXX' # Received after registering application
66
+ code = 'XXXXXX' # The generated code (or the one you obtained via traditional means)
67
+
68
+ # You must implement a responsible way of storing this token for reuse.
69
+ token = Ruqqus::Token.new(client_id, client_secret, code)
70
+ client = Ruqqus::Client.new(client_id, client_secret, token)
71
+
72
+ # Alternatively, you can create a new token and a client with a single call.
73
+ # This will perform the "grant" action of the code while creating it automatically
74
+ client = Ruqqus::Client.new(client_id, client_secret, code)
75
+ ```
76
+
77
+ The token will automatically refresh itself as-needed, but you will need to handle storing its new value for repeated
78
+ uses. To facilitate this and make it easier, there is a callback that can be subscribed to which will be called each
79
+ time the access key is updated.
80
+
81
+ ```ruby
82
+ # Load an existing token that has already been authorized
83
+ token = Ruqqus::Token.load_json('./token.json')
84
+
85
+ # Create your client
86
+ client = Ruqqus::Client.new(client_id, client_secret, token)
87
+
88
+ # Set the callback block to automatically update the saved token when it refreshes
89
+ client.token_refreshed do |t|
90
+ t.save_json('./token.json')
91
+ end
92
+ ```
93
+
94
+ The token obtains sensitive material, and due to the security issues of storing it in plain text, this functionality is
95
+ left to the user. The token is essentially the equivalent of your user credentials for Ruqqus, so bear that in mind how
96
+ and where you store this information so that it is not compromised.
97
+
34
98
  ## Usage
35
99
 
36
- At this time, there are only four publicly exposed functions to utilize for acquiring information on the following items:
100
+ See the [documentation](https://www.rubydoc.info/gems/ruqqus) for a complete API reference (100% coverage).
101
+
102
+ ### Features
103
+
104
+ The bulk of the API is obviously related to performing actions as a user, such as posting, commenting, voting, etc.. The
105
+ following highlights some of these features.
106
+
107
+ * Vote on posts and comments
108
+ * Create posts (text/link/image)
109
+ * Automated image upload via anonymous upload to Imgur ([API Key](https://imgur.com/account/settings/apps) required)
110
+ * Create/edit/delete comments
111
+ * Enumerate all existing guilds
112
+ * Enumerate all posts in a guild
113
+ * Enumerate all posts on the "front page", "all", etc., with sorting and filtering
114
+ * Enumerate all posts/comments of users (excluding private/banned/blocked accounts)
115
+
116
+ #### Misc. Examples
117
+
118
+ Some random examples displaying the ease in performing various operations.
119
+
120
+ ##### Let's do some voting!
121
+ ```ruby
122
+ client.each_user_post('captainmeta4') do |post|
123
+ # Upvote our fearless leader's posts
124
+ client.vote_post(post, 1)
125
+ end
126
+
127
+ client.each_user_comment('captainmeta4') do |comment|
128
+ # ...and his comments.
129
+ client.vote_comment(comment, 1)
130
+ end
131
+ ```
132
+
133
+ ##### Monitor For New Posts
134
+ ```ruby
135
+ delay = 10
136
+ puts 'Watching for new posts, press Ctrl+C to quit'
137
+ loop do
138
+ time = Time.now
139
+ sleep(delay)
140
+
141
+ puts "Checking the front page for new posts since #{time}"
142
+ client.each_post(sort: :new, filter: :all) do |post|
143
+ # Stop checking post once we encounter one older than this iteration
144
+ break if post.created < time
145
+ # Do something about this new post showing up in All
146
+ puts "Found a new post: '#{post.title}'"
147
+ end
148
+ end
149
+ ```
150
+ ##### Download all images from a guild
151
+ ```ruby
152
+ require 'open-uri'
153
+ domains = %w[i.ruqqus.com i.imgur.com]
154
+
155
+ Dir.mkdir('./tree_pics') unless Dir.exist?('./tree_pics')
156
+ client.each_guild_post('Trees', sort: :new) do |post|
157
+ next unless domains.include?(post.domain)
158
+ ext = File.extname(post.url)
159
+ ext = '.jpg' if ext.empty? # We don't care, just an example
160
+ path = File.join('./tree_pics', post.id + ext)
161
+ URI.open(post.url) { |src| File.open(path, 'wb') { |dst| dst.write(src.read) } }
162
+ end
163
+ ```
164
+
165
+ ### Types
166
+
167
+ The Ruqqus API exposes four primary types:
37
168
 
38
169
  * Users
39
170
  * Guilds
40
171
  * Posts
41
172
  * Comments
42
173
 
43
- The full documentation can be found [here](https://www.rubydoc.info/gems/ruqqus), but the API is rather intuitive. Here is some basic examples to give a taste of its basic usage.
174
+ Nearly all client operations interact with one or more of these entities in some way. Each of these types also has an
175
+ `#id` property that can be used with other related API functions, such as voting, replying, deleting, etc. The full
176
+ documentation listing all properties they obtain can be found [here](https://www.rubydoc.info/gems/ruqqus), but the API
177
+ is rather intuitive. Here are some samples of their basic
178
+ usage.
44
179
 
45
- ### Users
180
+ #### Users
46
181
 
47
182
  Obtain information about users.
48
183
 
49
184
  ```ruby
50
- user = Ruqqus.user('foreverzer0')
185
+ user = client.user_info('foreverzer0')
51
186
 
52
187
  # Get user's total rep (as well as separate for comments/posts)
53
188
  user.total_rep
@@ -62,12 +197,12 @@ user.created
62
197
  #=> 2020-06-16 21:59:04 -0400
63
198
  ```
64
199
 
65
- ### Guilds
200
+ #### Guilds
66
201
 
67
202
  Obtain information about guilds.
68
203
 
69
204
  ```ruby
70
- guild = Ruqqus.guild('Ruby')
205
+ guild = client.guild('Ruby')
71
206
 
72
207
  # Query the number of members, description, accent color, etc.
73
208
  guild.member_count
@@ -82,14 +217,16 @@ guild.nsfw?
82
217
  #=> false
83
218
  ```
84
219
 
85
- ### Posts
220
+ #### Posts
86
221
 
87
- Obtain information about posts. The API functions for querying comments from a post are not yet implemented on the backend, so you will have to settle for web-scraping with `mechanize`, `nokogiri`, etc. if that information is needed.
222
+ Obtain information about posts.
88
223
 
89
224
  ```ruby
90
- post = Ruqqus.post('2e0x')
91
- # ...or alternatively
92
- post = Ruqqus::Post.from_url('https://ruqqus.com/post/2e0x/made-this-project-in-ruby-on')
225
+ # Post IDs can be found within any link to a post.
226
+ # https://ruqqus.com/post/<POST ID>/<POST TITLE>
227
+
228
+ post_id = '2e0x'
229
+ post = client.post(post_id)
93
230
 
94
231
  # Obtain relevant information pertaining the guilds on Ruqqus
95
232
 
@@ -106,27 +243,27 @@ post.score
106
243
  #=> 10
107
244
  ```
108
245
 
109
- ### Comments
246
+ #### Comments
110
247
 
111
248
  Obtain information about comments. Comments are very similar to posts, but have a few unique methods for obtaining their nesting level, parent post/comment, etc.
112
249
 
113
250
  ```ruby
114
- comment = Ruqqus.comment('67mt')
115
- # ...or alternatively
116
- comment = Ruqqus::Comment.from_url('https://ruqqus.com/post/1wbo/hi-im-josh-roehl-singer-and/67mt')
251
+ # Post IDs can be found within any link to a post.
252
+ # https://ruqqus.com/post/<POST ID>/<POST TITLE>/<COMMENT ID>
117
253
 
118
- comment.post.title
254
+ comment_id = '67mt'
255
+ comment = client.comment(comment_id)
256
+
257
+ client.post(comment.post).title
119
258
  #=> "Hi. I'm Josh Roehl, singer and songwriter of the hit song \"Endless Summer\". I am hosting an AMA here."
120
259
 
121
260
  comment.body
122
261
  #=> "I'm fully aware that I'm not a very good singer. Let's call it half-singing, half-rapping."
123
262
 
124
- comment.author.ban_reason
263
+ client.user(comment.author_name).ban_reason
125
264
  #=> "Spam"
126
265
  ```
127
266
 
128
- [Documentation](https://www.rubydoc.info/gems/ruqqus)
129
-
130
267
  ## Contributing
131
268
 
132
269
  Bug reports and pull requests are welcome on GitHub at https://github.com/ForeverZer0/ruqqus.
data/Rakefile CHANGED
@@ -1,6 +1,5 @@
1
1
  require "bundler/gem_tasks"
2
2
 
3
- # Tests not implemented yet...
4
- task(:test) { |_| true }
3
+ task(:test) { true }
5
4
 
6
5
  task default: :test
data/TODO.md ADDED
@@ -0,0 +1,25 @@
1
+ # TODO
2
+
3
+ A scratch pad for things to do and ideas to look into
4
+
5
+ * Create and centralize all API endpoints into `Routes` module
6
+ * Check for `json[:error]` in `http_get` and `http_post` for all calls?
7
+ * Create examples in documentation
8
+ * Update README with more examples
9
+ * Create wiki on GitHub
10
+ * Finish and cleanup and `ruqqus-oauth` app
11
+ * Embed comment/posts API
12
+
13
+ # Missing API features
14
+
15
+ Some API features that would be beneficial to have implemented
16
+
17
+ * Flagging
18
+ * Post deletion/edit (!)
19
+ * Notifications (!)
20
+ * NSFW/NSFW toggling
21
+ * Kicking/Yanking
22
+ * User Settings
23
+ * Guild Settings
24
+ * Guild join/leave
25
+ * Follow/Unfollow users
@@ -0,0 +1,98 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'mechanize'
4
+ require 'tty-prompt'
5
+
6
+ #noinspection RubyResolve
7
+ class RuqqusOAuth
8
+
9
+ def initialize
10
+ prompt = TTY::Prompt.new
11
+ agent = Mechanize.new
12
+
13
+ prompt.warn('After inputting your client information and credentials of user')
14
+ prompt.warn('to authorize, and a code for token creation will be generated.')
15
+ puts
16
+ url = authorization_url(prompt)
17
+ login(agent, prompt)
18
+ prompt.say('Generating code...')
19
+ puts
20
+ code = generate_code(url, agent)
21
+ STDOUT << 'CODE: '
22
+ prompt.ok(code)
23
+ end
24
+
25
+ def authorization_url(prompt)
26
+ url = 'https://ruqqus.com/oauth/authorize'
27
+ url << "?client_id=#{ask_client_id(prompt)}"
28
+ url << "&redirect_uri=#{ask_client_redirect(prompt)}"
29
+ url << "&scope=#{ask_scopes(prompt)}"
30
+ url << "&state=#{SecureRandom.uuid.gsub('-', '')}"
31
+ url << '&permanent=true'
32
+ url
33
+ end
34
+
35
+ def generate_code(url, agent)
36
+ agent.get(url) do |page|
37
+ page.form_with(action: '/oauth/authorize').submit
38
+ /.*\?code=([a-zA-Z0-9_-]+)&?/.match(agent.history.last.uri.to_s)
39
+ return $1
40
+ end
41
+ end
42
+
43
+ def ask_client_id(prompt)
44
+ prompt.ask('Client ID: ') { |q| q.validate(/^[A-Za-z0-9_-]+$/, "Invalid client ID") }
45
+ end
46
+
47
+ def ask_client_secret(prompt)
48
+ prompt.ask('Client Secret: ') { |q| q.validate(/^[A-Za-z0-9_-]+$/, "Invalid client secret") }
49
+ end
50
+
51
+ def ask_client_redirect(prompt)
52
+ prompt.ask('Redirect URI: ', default: 'https://www.google.com') do |q|
53
+ proc = Proc.new { |v| URI.regexp =~ v && URI(v).scheme == 'https' }
54
+ q.validate(proc, 'Invalid HTTPS URI/scheme (must be HTTPS, not localhost)')
55
+ end
56
+ end
57
+
58
+ def ask_scopes(prompt)
59
+ msg = 'Select scopes the client is authorized to perform:'
60
+ choices = %i(identity create read update delete vote guildmaster)
61
+ scopes = prompt.multi_select(msg, choices, per_page: choices.size) do |q|
62
+ defaults = (1..choices.size).to_a
63
+ q.default(*defaults)
64
+ end
65
+ scopes.join(',')
66
+ end
67
+
68
+ def login(agent, prompt)
69
+ agent.get('https://ruqqus.com/login') do |page|
70
+ page.form_with(action: '/login') do |form|
71
+ form['username'] = prompt.ask('Username: ') do |q|
72
+ q.validate(/^[a-zA-Z0-9_]{5,25}$/, 'Invalid username')
73
+ end
74
+ form['password'] = prompt.mask("Password: ") do |q|
75
+ q.validate(/^.{8,100}$/, 'Invalid password')
76
+ end
77
+ puts 'Logging into Ruqqus...'
78
+ end.submit
79
+ end
80
+ end
81
+ end
82
+
83
+ begin
84
+ RuqqusOAuth.new
85
+ rescue Interrupt
86
+ puts
87
+ # Ignored
88
+ end
89
+
90
+
91
+
92
+
93
+
94
+
95
+
96
+
97
+
98
+