redd 0.6.0 → 0.6.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +141 -32
- data/RedditKit.LICENSE.md +2 -2
- data/lib/redd/client/authenticated/subreddits.rb +12 -0
- data/lib/redd/client/oauth2/authorization.rb +2 -2
- data/lib/redd/client/unauthenticated.rb +8 -8
- data/lib/redd/client/unauthenticated/moderation.rb +3 -3
- data/lib/redd/client/unauthenticated/utilities.rb +2 -2
- data/lib/redd/response/parse_json.rb +2 -0
- data/lib/redd/response/raise_error.rb +1 -0
- data/lib/redd/version.rb +1 -1
- data/redd.gemspec +1 -0
- metadata +16 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6760b565561bde890fe1595ebda87ada31bae326
|
4
|
+
data.tar.gz: 7bccd9ea39fb2fa9d8201648d2c6164cab2791f6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 24762a4de14370f0d6cb4b9a79150c8d8db6f86a290d06edb82034e664f2b878cb48c37bf4a046fe5f03b23623bd8c6af2008ca9687154596da56bf34d522072
|
7
|
+
data.tar.gz: ba5116ceaa492bdefb12dcd626597fdf9989b301dcc66154240f844b38163cc944eb682290f0282bd9118785dd7919a1d87c1b343ab5071a3f4edf4e7847cc88
|
data/README.md
CHANGED
@@ -1,16 +1,19 @@
|
|
1
1
|
<p align="center">
|
2
2
|
<img src="https://i.imgur.com/2JfE4M1.png" alt="redd"><br>
|
3
|
-
<a href="
|
4
|
-
<a href="https://travis-ci.org/avidw/redd"><img src="
|
3
|
+
<a href="https://rubygems.org/gems/redd"><img src="http://img.shields.io/gem/v/redd.svg?style=flat-square" alt="Gem Version"></a>
|
4
|
+
<a href="https://travis-ci.org/avidw/redd"><img src="http://img.shields.io/travis/avidw/redd.svg?style=flat-square" alt="Build Status"></a>
|
5
|
+
<a href="https://rubygems.org/gems/redd"><img src="http://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="MIT License"></a>
|
6
|
+
<a href="https://rubygems.org/gems/redd"><img src="http://img.shields.io/gem/dt/redd.svg?style=flat-square" alt="Gem Downloads"></a>
|
5
7
|
</p>
|
6
8
|
|
7
9
|
**redd** is an API wrapper for [reddit](http://reddit.com/dev/api) written in ruby that focuses on being *simple and extensible*.
|
8
|
-
Check out the latest documentation on [RubyDoc](http://rubydoc.info/github/avidw/redd/master/frames)
|
10
|
+
**Check out the latest documentation on [RubyDoc](http://rubydoc.info/github/avidw/redd/master/frames).**
|
9
11
|
|
10
12
|
---
|
11
13
|
|
12
14
|
<p align="center">
|
13
15
|
<a href="#getting-started">Getting Started</a> |
|
16
|
+
<a href="#oauth2">OAuth2</a> |
|
14
17
|
<a href="#extending-redd">Extending Redd</a> |
|
15
18
|
<a href="#supported-rubies">Supported Rubies</a> |
|
16
19
|
<a href="#copyright">Copyright</a>
|
@@ -23,6 +26,7 @@ Ruby and redd make creating reddit bots accessible and fun. To demonstrate, let'
|
|
23
26
|
|
24
27
|
1. **Installing**
|
25
28
|
You can either install the gem directly by running `gem install redd` or by placing the gem into your `Gemfile` and running `bundle install`.
|
29
|
+
|
26
30
|
```ruby
|
27
31
|
source "https://rubygems.org"
|
28
32
|
gem "redd"
|
@@ -33,84 +37,189 @@ Ruby and redd make creating reddit bots accessible and fun. To demonstrate, let'
|
|
33
37
|
|
34
38
|
2. **Setting Up**
|
35
39
|
Let's load up redd and create a client for us to work with. (The username and password aren't real!)
|
40
|
+
|
36
41
|
```ruby
|
37
42
|
require "redd"
|
38
43
|
#=> true
|
39
44
|
|
40
|
-
r = Redd.
|
41
|
-
user_agent: "HelloWorldBot v1.0 by /u/you"
|
45
|
+
r = Redd::Client::Authenticated.new_from_credentials "HelloWorldBot", "hunter2", user_agent: "HelloWorldBot v1.0 by /u/you"
|
42
46
|
# => #<Redd::Client::Authenticated:0xY4D4y4D4y4dA ...
|
43
47
|
```
|
44
48
|
|
45
49
|
3. **Scouting**
|
46
|
-
Redd has a really cool method similar to praw's `helpers.comment_stream` that "streams" comments to you while avoiding duplicates. You won't have to take care of rate-limiting either; Redd `sleep`s after requests to avoid ratelimit errors. If you want to write a rate limiting class yourself, take a look at `lib/redd/rate_limit.rb`
|
50
|
+
Redd has a really cool method similar to praw's `helpers.comment_stream` that "streams" comments to you while avoiding duplicates. You won't have to take care of rate-limiting either; Redd `sleep`s after requests to avoid ratelimit errors. If you want to write a rate limiting class yourself, take a look at [`lib/redd/rate_limit.rb`](https://github.com/avidw/redd/blob/master/lib/redd/rate_limit.rb#L2-L23)
|
51
|
+
|
47
52
|
```ruby
|
48
53
|
r.comment_stream "test" do |comment|
|
49
|
-
|
54
|
+
comment.reply "World!" if comment.body =~ /^Hello\?$/i
|
50
55
|
end
|
51
56
|
```
|
52
57
|
|
53
58
|
4. **Just in Case**
|
54
59
|
It's also a good idea to escape some common errors from reddit in case they happen:
|
60
|
+
|
55
61
|
```ruby
|
56
62
|
begin
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
63
|
+
r.comment_stream "test" do |comment|
|
64
|
+
comment.reply "World!" if comment.body =~ /^Hello\?$/i
|
65
|
+
end
|
66
|
+
rescue Redd::Error::RateLimited => e
|
67
|
+
time_left = e.time
|
68
|
+
sleep(time_left)
|
69
|
+
rescue Redd::Error => e
|
70
|
+
status = e.code
|
71
|
+
# 5-something errors are usually errors on reddit's end.
|
72
|
+
raise e unless (500...600).include?(status)
|
67
73
|
end
|
68
74
|
```
|
69
75
|
|
76
|
+
## OAuth2
|
77
|
+
Redd also provides a wrapper to connect to reddit via OAuth2. The client's methods are similar to the authenticated client, given that you have the required scopes. Refer to [reddit's api](https://www.reddit.com/dev/api/oauth) for the various scopes. Getting it running is really simple and can even be used to integrate reddit's features into a [**Rails**](https://github.com/rails/rails) application. Another plus is that logging in via OAuth2 lets you make twice as many requests without hitting a rate limit (1/second). Let's try logging into reddit with [**sinatra**](http://www.sinatrarb.com/).
|
78
|
+
|
79
|
+
The first thing you need to do is to create an OAuth2 application in reddit [**here**](https://ssl.reddit.com/prefs/apps). For more information, refer to [**"Getting Started"**](https://github.com/reddit/reddit/wiki/OAuth2#getting-started) on reddit's wiki.
|
80
|
+
|
81
|
+
Note: Although I enter the client id and secret in the code directly, I recommend you store them in environment variables or a **`.env`** file and load it with [**dotenv**](https://github.com/bkeepers/dotenv).
|
82
|
+
|
83
|
+
```ruby
|
84
|
+
# config.ru
|
85
|
+
|
86
|
+
require "./connect_to_reddit"
|
87
|
+
run ConnectToReddit
|
88
|
+
```
|
89
|
+
|
90
|
+
```ruby
|
91
|
+
# connect_to_reddit.rb
|
92
|
+
|
93
|
+
require "sinatra/base"
|
94
|
+
require "redd"
|
95
|
+
|
96
|
+
class ConnectToReddit < Sinatra::Base
|
97
|
+
configure do
|
98
|
+
enable :sessions
|
99
|
+
|
100
|
+
# If you're on Rails, you can replace the fixed url with a named one (e.g. redirect_url).
|
101
|
+
set :client, Redd::Client::OAuth2.new("sa_xTDcJ3dWz0w", "very-sensitive-secret", "http://localhost:8080/auth/reddit/redirect")
|
102
|
+
end
|
103
|
+
|
104
|
+
get "/auth/reddit" do
|
105
|
+
# Make use of the state!
|
106
|
+
# SecureRandom, which is included in Ruby, helps create a url-safe random string.
|
107
|
+
state = SecureRandom.urlsafe_base64
|
108
|
+
session[:state] = state
|
109
|
+
redirect settings.client.auth_url(["identity"], :temporary, state)
|
110
|
+
end
|
111
|
+
|
112
|
+
get "/auth/reddit/redirect" do
|
113
|
+
raise "Your state doesn't match!" unless session[:state] == params[:state]
|
114
|
+
|
115
|
+
# access is a Redd::OAuth2Access object.
|
116
|
+
access = settings.client.request_access(params[:code])
|
117
|
+
me = settings.client.with_access(access) { |client| client.me }
|
118
|
+
|
119
|
+
# Now use the Redd::Object::User object to create a user, maybe assign some
|
120
|
+
# sort of token to remember their session.
|
121
|
+
redirect to("/success")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
```
|
125
|
+
|
126
|
+
Now let's run the application:
|
127
|
+
|
128
|
+
```shell
|
129
|
+
$ rackup -p 8080
|
130
|
+
```
|
131
|
+
|
132
|
+
#### Remember Me
|
133
|
+
If you want longer control of users' accounts for background tasks like auto-saving to a users' account behind the scenes or not have to ask your user to go through reddit every time, you can choose to have a permanent access by changing the second parameter of `auth_url`.
|
134
|
+
|
135
|
+
```ruby
|
136
|
+
client = Redd::Client::OAuth2.new("sa_xTDcJ3dWz0w", "very-sensitive-secret", "http://localhost:8080/auth/reddit/redirect")
|
137
|
+
state = SecureRandom.urlsafe_base64
|
138
|
+
auth_url = client.auth_url(["identity"], :permanent, state)
|
139
|
+
```
|
140
|
+
|
141
|
+
The access will still only last one hour, but you can refresh the access whenever you want.
|
142
|
+
|
143
|
+
```ruby
|
144
|
+
access = client.request_access(params[:code])
|
145
|
+
|
146
|
+
# 1 hour or more later
|
147
|
+
client.refresh_access(access) if access.expired?
|
148
|
+
```
|
149
|
+
|
150
|
+
Now if you are running a web application, you can't just store access tokens in memory. `Redd::OAuth2Access` offers a couple of methods for serializing the access to JSON and retrieving it.
|
151
|
+
|
152
|
+
```ruby
|
153
|
+
json = access.to_json
|
154
|
+
current_user.update!(access: json) # Rails
|
155
|
+
redis.set("some-token", json) # Redis
|
156
|
+
|
157
|
+
# After some time
|
158
|
+
access = Redd::OAuth2Access.from_json(current_user.access)
|
159
|
+
client.with_access(access) do |authenticated_client|
|
160
|
+
authenticated_client.do_whatever_redd_client_can_do
|
161
|
+
end
|
162
|
+
```
|
163
|
+
|
164
|
+
#### Who, me?
|
165
|
+
You can also revoke access tokens after the user has logged out to make sure the tokens can't be used for malicious purposes.
|
166
|
+
|
167
|
+
```ruby
|
168
|
+
also_revoke_refresh_token = true
|
169
|
+
client.revoke_access(access, also_revoke_refresh_token)
|
170
|
+
```
|
171
|
+
|
70
172
|
## Extending Redd
|
71
173
|
Extending any ruby library, including redd is incredibly easy. Let's try this out by adding a gilding extension. Reddit provides an api to be able to gild posts and comments, given that you have "creddits".
|
72
174
|
|
73
175
|
1. Let's start by creating a module for the methods to live in.
|
176
|
+
|
74
177
|
```ruby
|
75
178
|
module MyGildingExtension
|
76
179
|
end
|
77
180
|
```
|
78
181
|
|
79
182
|
2. Let's add a method to gild a thing, using the [reddit api](http://www.reddit.com/dev/api#section_gold) and following the conventions.
|
183
|
+
|
80
184
|
```ruby
|
81
185
|
module MyGildingExtension
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
186
|
+
def gild(thing)
|
187
|
+
# Redd::Client::Unauthenticated::Utilities has some pretty helpful
|
188
|
+
# methods.
|
189
|
+
fullname = extract_fullname(thing)
|
190
|
+
|
191
|
+
# We're using post instead of object_from_response, because we don't
|
192
|
+
# expect any object from the response.
|
193
|
+
post "/api/v1/gold/gild/#{fullname}"
|
194
|
+
end
|
91
195
|
end
|
92
196
|
```
|
93
197
|
|
94
198
|
3. Let's add the method to the Authenticated client. You can also add it to the Unauthenticated client, but since unauthenticated users can't gild, there's no point.
|
199
|
+
|
95
200
|
```ruby
|
96
201
|
Redd::Client::Authenticated.include(MyGildingExtension)
|
97
202
|
```
|
98
203
|
|
99
204
|
4. You might also want to add the method to objects to make it easier to access.
|
205
|
+
|
100
206
|
```ruby
|
101
207
|
module Gildable
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
208
|
+
def gild
|
209
|
+
# Every Redd::Object is instantiated with the client that created
|
210
|
+
# it, so the method can be called on the client easily, similar to
|
211
|
+
# praw in python.
|
212
|
+
client.gild(self)
|
213
|
+
end
|
108
214
|
end
|
109
215
|
|
110
216
|
Redd::Object::Submission.include(Gildable)
|
111
217
|
Redd::Object::Comment.include(Gildable)
|
112
218
|
```
|
113
219
|
|
220
|
+
#### Contributing
|
221
|
+
Please do. If you would like to become a contributor, do ask.
|
222
|
+
|
114
223
|
## Supported Rubies
|
115
224
|
This gem aims to work on the following rubies:
|
116
225
|
|
data/RedditKit.LICENSE.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
The error and base files would have been impossible if it weren't for @samsymons :)
|
1
|
+
The error and base files would have been impossible if it weren't for [@samsymons](https://github.com/samsymons) :)
|
2
2
|
|
3
3
|
---
|
4
4
|
|
@@ -10,4 +10,4 @@ Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
10
10
|
|
11
11
|
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
12
12
|
|
13
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
@@ -71,6 +71,18 @@ module Redd
|
|
71
71
|
Redd::Object::Listing.new(data: {children: things})
|
72
72
|
end
|
73
73
|
|
74
|
+
def edit_stylesheet(subreddit, contents, reason = nil)
|
75
|
+
name = extract_attribute(subreddit, :display_name)
|
76
|
+
path = "/r/#{name}/api/subreddit_stylesheet"
|
77
|
+
params = {
|
78
|
+
api_type: "json",
|
79
|
+
op: "save",
|
80
|
+
stylesheet_contents: contents
|
81
|
+
}
|
82
|
+
params[:reason] = reason if reason
|
83
|
+
post path, params
|
84
|
+
end
|
85
|
+
|
74
86
|
private
|
75
87
|
|
76
88
|
# Subscribe or unsubscribe to a subreddit.
|
@@ -95,9 +95,9 @@ module Redd
|
|
95
95
|
params = {token: token}
|
96
96
|
|
97
97
|
if remove_refresh_token
|
98
|
-
params[:token_type_hint] =
|
98
|
+
params[:token_type_hint] = :refresh_token
|
99
99
|
elsif remove_refresh_token == false
|
100
|
-
params[:token_type_hint] =
|
100
|
+
params[:token_type_hint] = :access_token
|
101
101
|
end
|
102
102
|
|
103
103
|
auth_connection.post "/api/v1/revoke_token", params
|
@@ -64,7 +64,7 @@ module Redd
|
|
64
64
|
|
65
65
|
# Gets the Faraday connection or creates one if it doesn't exist yet.
|
66
66
|
#
|
67
|
-
|
67
|
+
# @return [Faraday] A new or existing Faraday connection.
|
68
68
|
def connection
|
69
69
|
@connection ||= Faraday.new(url: api_endpoint) do |faraday|
|
70
70
|
faraday.use Faraday::Request::UrlEncoded
|
@@ -80,36 +80,36 @@ module Redd
|
|
80
80
|
#
|
81
81
|
# @param [#to_sym] method The HTTP verb to use.
|
82
82
|
# @param [String] path The path under the api endpoint to request from.
|
83
|
-
# @param [Hash] params The additional parameters to send
|
84
|
-
|
83
|
+
# @param [Hash] params The additional parameters to send.
|
84
|
+
# @return [Faraday::Response] The faraday response.
|
85
85
|
def request(method, path, params = {})
|
86
86
|
rate_limit.after_limit do
|
87
|
-
connection.send(method.to_sym, path, params)
|
87
|
+
connection.send(method.to_sym, path, params)
|
88
88
|
end
|
89
89
|
end
|
90
90
|
|
91
91
|
# Performs a GET request via {#request}.
|
92
92
|
# @see #request
|
93
93
|
def get(*args)
|
94
|
-
request(:get, *args)
|
94
|
+
request(:get, *args).body
|
95
95
|
end
|
96
96
|
|
97
97
|
# Performs a POST request via {#request}.
|
98
98
|
# @see #request
|
99
99
|
def post(*args)
|
100
|
-
request(:post, *args)
|
100
|
+
request(:post, *args).body
|
101
101
|
end
|
102
102
|
|
103
103
|
# Performs a PUT request via {#request}.
|
104
104
|
# @see #request
|
105
105
|
def put(*args)
|
106
|
-
request(:put, *args)
|
106
|
+
request(:put, *args).body
|
107
107
|
end
|
108
108
|
|
109
109
|
# Performs a DELETE request via {#request}.
|
110
110
|
# @see #request
|
111
111
|
def delete(*args)
|
112
|
-
request(:delete, *args)
|
112
|
+
request(:delete, *args).body
|
113
113
|
end
|
114
114
|
end
|
115
115
|
end
|
@@ -7,11 +7,11 @@ module Redd
|
|
7
7
|
# query.
|
8
8
|
# @return [String] The url for the subreddit's css stylesheet.
|
9
9
|
def stylesheet_url(subreddit = nil)
|
10
|
-
name = extract_attribute(subreddit, :display_name)
|
10
|
+
name = extract_attribute(subreddit, :display_name) if subreddit
|
11
11
|
path = "/stylesheet"
|
12
12
|
path = path.prepend("/r/#{name}") if subreddit
|
13
13
|
|
14
|
-
|
14
|
+
request(:get, path).headers[:location]
|
15
15
|
end
|
16
16
|
|
17
17
|
# @param subreddit [Redd::Object::Subreddit, String] The subreddit to
|
@@ -23,4 +23,4 @@ module Redd
|
|
23
23
|
end
|
24
24
|
end
|
25
25
|
end
|
26
|
-
end
|
26
|
+
end
|
@@ -95,12 +95,12 @@ module Redd
|
|
95
95
|
end
|
96
96
|
|
97
97
|
def comments_from_response(*args)
|
98
|
-
|
98
|
+
body = request(*args).body[1]
|
99
99
|
object_from_body(body)
|
100
100
|
end
|
101
101
|
|
102
102
|
def object_from_response(*args)
|
103
|
-
|
103
|
+
body = request(*args).body
|
104
104
|
object_from_body(body)
|
105
105
|
end
|
106
106
|
end
|
data/lib/redd/version.rb
CHANGED
data/redd.gemspec
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: redd
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.6.
|
4
|
+
version: 0.6.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Avinash Dwarapu
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-09-11 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -122,6 +122,20 @@ dependencies:
|
|
122
122
|
- - ~>
|
123
123
|
- !ruby/object:Gem::Version
|
124
124
|
version: 0.9.0
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: faraday_middleware
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - ~>
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: 0.9.1
|
132
|
+
type: :runtime
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - ~>
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: 0.9.1
|
125
139
|
- !ruby/object:Gem::Dependency
|
126
140
|
name: multi_json
|
127
141
|
requirement: !ruby/object:Gem::Requirement
|