redd 0.7.10 → 0.8.0.pre.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +5 -30
  3. data/.rspec +1 -1
  4. data/.rubocop.yml +16 -3
  5. data/.travis.yml +13 -7
  6. data/Gemfile +3 -1
  7. data/LICENSE.txt +21 -0
  8. data/README.md +40 -126
  9. data/Rakefile +10 -3
  10. data/TODO.md +11 -0
  11. data/bin/console +84 -0
  12. data/bin/setup +8 -0
  13. data/lib/redd.rb +84 -46
  14. data/lib/redd/api_client.rb +109 -0
  15. data/lib/redd/auth_strategies/auth_strategy.rb +60 -0
  16. data/lib/redd/auth_strategies/installed.rb +22 -0
  17. data/lib/redd/auth_strategies/script.rb +23 -0
  18. data/lib/redd/auth_strategies/userless.rb +17 -0
  19. data/lib/redd/auth_strategies/web.rb +29 -0
  20. data/lib/redd/client.rb +88 -0
  21. data/lib/redd/error.rb +19 -142
  22. data/lib/redd/models/access.rb +20 -0
  23. data/lib/redd/models/basic_model.rb +124 -0
  24. data/lib/redd/models/comment.rb +51 -0
  25. data/lib/redd/models/front_page.rb +71 -0
  26. data/lib/redd/models/inboxable.rb +23 -0
  27. data/lib/redd/models/lazy_model.rb +63 -0
  28. data/lib/redd/models/listing.rb +26 -0
  29. data/lib/redd/models/messageable.rb +20 -0
  30. data/lib/redd/models/moderatable.rb +41 -0
  31. data/lib/redd/models/more_comments.rb +10 -0
  32. data/lib/redd/models/multireddit.rb +32 -0
  33. data/lib/redd/models/postable.rb +70 -0
  34. data/lib/redd/models/private_message.rb +29 -0
  35. data/lib/redd/models/replyable.rb +16 -0
  36. data/lib/redd/models/session.rb +86 -0
  37. data/lib/redd/models/submission.rb +40 -0
  38. data/lib/redd/models/subreddit.rb +201 -0
  39. data/lib/redd/models/user.rb +72 -0
  40. data/lib/redd/models/wiki_page.rb +24 -0
  41. data/lib/redd/utilities/error_handler.rb +35 -0
  42. data/lib/redd/utilities/rate_limiter.rb +21 -0
  43. data/lib/redd/utilities/stream.rb +63 -0
  44. data/lib/redd/utilities/unmarshaller.rb +39 -0
  45. data/lib/redd/version.rb +4 -3
  46. data/logo.png +0 -0
  47. data/redd.gemspec +26 -22
  48. metadata +73 -99
  49. data/LICENSE.md +0 -22
  50. data/RedditKit.LICENSE.md +0 -9
  51. data/lib/redd/access.rb +0 -76
  52. data/lib/redd/clients/base.rb +0 -188
  53. data/lib/redd/clients/base/account.rb +0 -20
  54. data/lib/redd/clients/base/identity.rb +0 -22
  55. data/lib/redd/clients/base/none.rb +0 -27
  56. data/lib/redd/clients/base/privatemessages.rb +0 -33
  57. data/lib/redd/clients/base/read.rb +0 -113
  58. data/lib/redd/clients/base/stream.rb +0 -81
  59. data/lib/redd/clients/base/submit.rb +0 -19
  60. data/lib/redd/clients/base/utilities.rb +0 -104
  61. data/lib/redd/clients/base/wikiread.rb +0 -33
  62. data/lib/redd/clients/installed.rb +0 -57
  63. data/lib/redd/clients/script.rb +0 -41
  64. data/lib/redd/clients/userless.rb +0 -32
  65. data/lib/redd/clients/web.rb +0 -58
  66. data/lib/redd/objects/base.rb +0 -39
  67. data/lib/redd/objects/comment.rb +0 -22
  68. data/lib/redd/objects/labeled_multi.rb +0 -13
  69. data/lib/redd/objects/listing.rb +0 -29
  70. data/lib/redd/objects/more_comments.rb +0 -11
  71. data/lib/redd/objects/private_message.rb +0 -28
  72. data/lib/redd/objects/submission.rb +0 -139
  73. data/lib/redd/objects/subreddit.rb +0 -330
  74. data/lib/redd/objects/thing.rb +0 -26
  75. data/lib/redd/objects/thing/editable.rb +0 -22
  76. data/lib/redd/objects/thing/hideable.rb +0 -18
  77. data/lib/redd/objects/thing/inboxable.rb +0 -25
  78. data/lib/redd/objects/thing/messageable.rb +0 -34
  79. data/lib/redd/objects/thing/moderatable.rb +0 -43
  80. data/lib/redd/objects/thing/refreshable.rb +0 -14
  81. data/lib/redd/objects/thing/saveable.rb +0 -21
  82. data/lib/redd/objects/thing/votable.rb +0 -33
  83. data/lib/redd/objects/user.rb +0 -52
  84. data/lib/redd/objects/wiki_page.rb +0 -15
  85. data/lib/redd/rate_limit.rb +0 -88
  86. data/lib/redd/response/parse_json.rb +0 -18
  87. data/lib/redd/response/raise_error.rb +0 -16
  88. data/spec/redd/objects/base_spec.rb +0 -1
  89. data/spec/redd/response/raise_error_spec.rb +0 -11
  90. data/spec/redd_spec.rb +0 -5
  91. data/spec/spec_helper.rb +0 -71
@@ -1,22 +0,0 @@
1
- require_relative "thing"
2
-
3
- module Redd
4
- module Objects
5
- # A comment that can be made on a link.
6
- class Comment < Thing
7
- include Thing::Editable
8
- include Thing::Inboxable
9
- include Thing::Moderatable
10
- include Thing::Refreshable
11
- include Thing::Saveable
12
- include Thing::Votable
13
-
14
- alias_property :reports_count, :num_reports
15
-
16
- # @return [Listing] The comment's replies.
17
- def replies
18
- @replies ||= (client.object_from_body(self[:replies]) || [])
19
- end
20
- end
21
- end
22
- end
@@ -1,13 +0,0 @@
1
- module Redd
2
- module Objects
3
- # A comment that can be made on a link.
4
- class LabeledMulti < Base
5
- # @see Objects::Base
6
- def initialize(client, attributes = {})
7
- attr_dup = attributes.dup
8
- attr_dup[:subreddits].map! { |sub| sub[:name] }
9
- super(client, attr_dup)
10
- end
11
- end
12
- end
13
- end
@@ -1,29 +0,0 @@
1
- module Redd
2
- module Objects
3
- # A collection of reddit things.
4
- # @see https://www.reddit.com/dev/api#listings
5
- class Listing < Array
6
- # @!attribute [r] before
7
- # @return [String] The id of the object before the listing.
8
- attr_reader :before
9
-
10
- # @!attribute [r] after
11
- # @return [String] The id of the object after the listing.
12
- attr_reader :after
13
-
14
- # @param [Clients::Base] client The client to expand the comments with.
15
- # @param [{:before => String, :after => String,
16
- # :children => Array<Hash>}] attributes The data to initialize the
17
- # class with.
18
- # @todo Only call Clients::Base#object_from_body when item is being
19
- # accessed.
20
- def initialize(client, attributes)
21
- @before = attributes[:before]
22
- @after = attributes[:after]
23
- attributes[:children].each do |child|
24
- self << (client.object_from_body(child) || child)
25
- end
26
- end
27
- end
28
- end
29
- end
@@ -1,11 +0,0 @@
1
- module Redd
2
- module Objects
3
- # The model for a morecomments object
4
- class MoreComments < Array
5
- def initialize(_, attributes)
6
- #Return an empty arrar if there are no children
7
- super(attributes[:children]) if attributes[:children]
8
- end
9
- end
10
- end
11
- end
@@ -1,28 +0,0 @@
1
- require_relative "thing"
2
-
3
- module Redd
4
- module Objects
5
- # The model for private messages
6
- class PrivateMessage < Thing
7
- include Thing::Inboxable
8
-
9
- alias_property :from, :author
10
- alias_property :to, :dest
11
-
12
- # Block the sender of the message from sending any more.
13
- def block_sender!
14
- post("/api/block", id: fullname)
15
- end
16
-
17
- # Mark the message as read.
18
- def mark_as_read
19
- post("/api/read_message", id: fullname)
20
- end
21
-
22
- # Mark the message as unread and add orangered to account.
23
- def mark_as_unread
24
- post("/api/unread_message", id: fullname)
25
- end
26
- end
27
- end
28
- end
@@ -1,139 +0,0 @@
1
- require_relative "thing"
2
-
3
- module Redd
4
- module Objects
5
- # A submission made in a subreddit.
6
- class Submission < Thing
7
- include Thing::Editable
8
- include Thing::Hideable
9
- include Thing::Moderatable
10
- include Thing::Refreshable
11
- include Thing::Saveable
12
- include Thing::Votable
13
-
14
- alias_property :nsfw?, :over_18
15
- alias_property :self?, :is_self
16
- alias_property :comments_count, :num_comments
17
-
18
- # The shorter url for sharing.
19
- def short_url
20
- "http://redd.it/#{self[:id]}"
21
- end
22
-
23
- # Whether the comment was gilded.
24
- def gilded?
25
- self[:gilded] > 0
26
- end
27
-
28
- # Mark the thing as Not Suitable For Work.
29
- def mark_as_nsfw
30
- get("/api/marknsfw", id: fullname)
31
- self[:over_18] = true
32
- end
33
-
34
- # No longer mark the thing as Not Suitable For Work.
35
- def unmark_as_nsfw
36
- get("/api/unmarknsfw", id: fullname)
37
- self[:over_18] = false
38
- end
39
- alias_method :mark_as_safe, :unmark_as_nsfw
40
-
41
- # Reply to the thing.
42
- # @param text [String] The text to comment.
43
- # @return [Objects::Comment] The reply.
44
- def add_comment(text)
45
- client.add_comment(self, text)
46
- end
47
-
48
- # Set the submission to "contest mode" (comments are randomly sorted)
49
- def set_contest_mode
50
- post("/api/set_contest_mode", id: fullname, state: true)
51
- end
52
-
53
- # Unset the "contest mode".
54
- def unset_contest_mode
55
- post("/api/set_contest_mode", id: fullname, state: false)
56
- end
57
-
58
- # Set the submission as the sticky post of the subreddit
59
- def set_sticky
60
- post("/api/set_subreddit_sticky", id: fullname, state: true)
61
- self[:stickied] = true
62
- end
63
-
64
- # Unsticky the post from the subreddit
65
- def unset_sticky
66
- post("/api/set_subreddit_sticky", id: fullname, state: false)
67
- self[:stickied] = false
68
- end
69
-
70
- # @return [Listing] The submission's comments.
71
- # @todo Allow for various depths and contexts and what not. Maybe a
72
- # get_comment method?
73
- def comments
74
- refresh! unless @comments
75
- @comments
76
- end
77
- alias_method :replies, :comments
78
-
79
- # Refresh the submission AND its comments.
80
- # @return [Submission] The updated submission.
81
- def refresh!
82
- body = get("/comments/#{id}.json").body
83
- @comments = client.object_from_body(body[1])
84
- deep_merge!(body[0])
85
- end
86
-
87
- # Take a MoreComments and return a listing of comments.
88
- # @param [MoreComments] more The object to expand.
89
- # @return [Listing] A listing of the expanded comments.
90
- def expand_more(more)
91
- response = client.get(
92
- "/api/morechildren",
93
- children: more.join(","),
94
- link_id: fullname
95
- )
96
-
97
- client.object_from_body(
98
- kind: "Listing",
99
- data: {
100
- before: "", after: "",
101
- children: response.body[:json][:data][:things]
102
- }
103
- )
104
- end
105
-
106
- # Get the related articles.
107
- # @param [Hash] params A list of params to send with the request.
108
- # @option params [String] :after Return results after the given
109
- # fullname.
110
- # @option params [String] :before Return results before the given
111
- # fullname.
112
- # @option params [Integer] :count The number of items already seen
113
- # in the listing.
114
- # @option params [1..100] :limit The maximum number of things to
115
- # return.
116
- # @return [Objects::Listing<Objects::Thing>]
117
- def get_related(**params)
118
- related = get("/related/#{id}.json", params).body[1]
119
- client.object_from_body(related)
120
- end
121
-
122
- # Get other articles with the same URL.
123
- # @param [Hash] params A list of params to send with the request.
124
- # @option params [String] :after Return results after the given
125
- # fullname.
126
- # @option params [String] :before Return results before the given
127
- # fullname.
128
- # @option params [Integer] :count The number of items already seen
129
- # in the listing.
130
- # @option params [1..100] :limit The maximum number of things to
131
- # return.
132
- # @return [Objects::Listing<Objects::Submission>]
133
- def get_duplicates(**params)
134
- duplicates = get("/duplicates/#{id}.json", params).body[1]
135
- client.object_from_body(duplicates)
136
- end
137
- end
138
- end
139
- end
@@ -1,330 +0,0 @@
1
- require "fastimage"
2
- require_relative "thing"
3
-
4
- module Redd
5
- module Objects
6
- # A comment made on links.
7
- # @todo #subscribe! and #unsubscribe!
8
- class Subreddit < Thing
9
- include Thing::Messageable
10
- include Thing::Refreshable
11
-
12
- alias_property :header_image, :header_img
13
- alias_property :nsfw?, :over18
14
- alias_property :users_online, :accounts_active
15
- alias_property :type, :subreddit_type
16
- alias_property :times_gilded, :gilded
17
-
18
- # @!group Submissions
19
-
20
- # Submit a link or a text post to the subreddit.
21
- #
22
- # @param [String] title The title of the submission.
23
- # @param [String] captcha A possible captcha result to send if one
24
- # is required.
25
- # @param [String] identifier The identifier for the captcha if one
26
- # is required.
27
- # @param [String] text The text of the self-post.
28
- # @param [String] url The URL of the link.
29
- # @param [Boolean] resubmit Whether to post a link to the subreddit
30
- # despite it having been posted there before (you monster).
31
- # @param [Boolean] sendreplies Whether to send the replies to your
32
- # inbox.
33
- # @return [Objects::Thing] The returned result (url, id and name).
34
- def submit(
35
- title, captcha = nil, identifier = nil, text: nil, url: nil,
36
- resubmit: false, sendreplies: true
37
- )
38
-
39
- params = {
40
- extension: "json", title: title, sr: display_name,
41
- resubmit: resubmit, sendreplies: sendreplies
42
- }
43
-
44
- params.merge!(captcha: captcha, iden: identifier) if captcha
45
- params[:kind], params[:text] = :self, text if text
46
- params[:kind], params[:url] = :link, url if url
47
-
48
- response = post("/api/submit", params)
49
- Objects::Thing.new(self, response.body[:json][:data])
50
- end
51
-
52
- # Add a comment to the submission.
53
- # @param text [String] The text to comment.
54
- # @return [Objects::Comment] The reply.
55
- def add_comment(text)
56
- client.add_comment(self, text)
57
- end
58
-
59
- # @!endgroup
60
-
61
- # @!group Stylesheets
62
-
63
- # @return [String] The url for the subreddit's stylesheet.
64
- def stylesheet_url
65
- get("/r/#{display_name}/stylesheet").headers["location"]
66
- end
67
-
68
- # @return [String] The css for the subreddit.
69
- def stylesheet
70
- Faraday.get(stylesheet_url).body
71
- end
72
-
73
- # Edit the subreddit's stylesheet
74
- #
75
- # @param [String] contents The new CSS.
76
- # @param [String] reason Why you modified the stylesheet.
77
- # @author Takashi M (@beatak) and Avinash Dwarapu (@avidw)
78
- # @note https://www.reddit.com/r/naut/about/stylesheet/ is a good place
79
- # to test if you have an error.
80
- def edit_stylesheet(contents, reason = nil)
81
- params = {op: "save", stylesheet_contents: contents}
82
- params[:reason] = reason if reason
83
- post("/r/#{display_name}/api/subreddit_stylesheet", params)
84
- end
85
-
86
- # @!endgroup
87
-
88
- # @!group Invites
89
-
90
- # Accept a moderator invite from a subreddit.
91
- def accept_moderator_invite!
92
- post("/r/#{display_name}/api/accept_moderator_invite")
93
- end
94
-
95
- # Stop being a contributor of the subreddit.
96
- def leave_contributor_status!
97
- post("/api/leavecontributor", id: fullname)
98
- end
99
-
100
- # Stop being a moderator of the subreddit.
101
- def leave_moderator_status!
102
- post("/api/leavemoderator", id: fullname)
103
- end
104
-
105
- # @!endgroup
106
-
107
- # @!group Flairs
108
-
109
- # Get a list of everbody on the subreddit with a user flair.
110
- #
111
- # @param [Hash] params A list of params to send with the request.
112
- # @option params [String] :after Return results after the given
113
- # fullname.
114
- # @option params [String] :before Return results before the given
115
- # fullname.
116
- # @option params [Integer] :count The number of items already seen in the
117
- # listing.
118
- # @option params [1..1000] :limit The maximum number of things to
119
- # return.
120
- # @option params [String] :name The username when getting the flair of
121
- # just one user.
122
- # @return [Objects::Listing<Hash>] A listing of flair hashes.
123
- def get_flairlist(**params)
124
- body = get("/r/#{display_name}/api/flairlist.json", params).body
125
- client.object_from_body(
126
- kind: "Listing",
127
- data: {
128
- children: body[:users],
129
- before: body[:prev],
130
- after: body[:next]
131
- }
132
- )
133
- end
134
-
135
- # Get the flair of a user.
136
- #
137
- # @param [Objects::User, String] user The user to find.
138
- # @return [Hash, nil] Flair info about the user or nil if nobody was
139
- # found.
140
- def get_flair(user)
141
- username = client.property(user, :name)
142
- flair = get_flairlist(user: username).first
143
- flair if flair[:user].casecmp(username) == 0
144
- end
145
-
146
- # Set the flair of a user or link.
147
- # @param [Objects::Subreddit, Objects::User] thing The user or link to
148
- # set the flair to.
149
- # @param [:user, :link] type The type of thing.
150
- # @param [String] text The text to set the flair to.
151
- # @param [String] css_class The css_class of the flair.
152
- def set_flair(thing, type = nil, text = nil, css_class = nil)
153
- params = {text: text, css_class: css_class}
154
- if thing.is_a?(Objects::User) || type == :user
155
- params[:name] = client.property(thing, :name)
156
- elsif thing.is_a?(Objects::Submission) || type == :link
157
- params[:link] = client.property(thing, :fullname)
158
- else
159
- fail "You should provide a proper type."
160
- end
161
-
162
- post("/r/#{display_name}/api/flair", params)
163
- end
164
-
165
- # @!endgroup
166
-
167
- # @!group Listings
168
-
169
- # @!method get_hot(**params)
170
- # @!method get_new(**params)
171
- # @!method get_top(**params)
172
- # @!method get_controversial(**params)
173
- # @!method get_comments(**params)
174
- #
175
- # Get the appropriate listing.
176
- # @param params [Hash] A list of params to send with the request.
177
- # @option params [String] :after Return results after the given
178
- # fullname.
179
- # @option params [String :before Return results before the given
180
- # fullname.
181
- # @option params [Integer] :count (0) The number of items already seen
182
- # in the listing.
183
- # @option params [1..100] :limit (25) The maximum number of things to
184
- # return.
185
- # @option params [:hour, :day, :week, :month, :year, :all] :t The
186
- # time period to consider when sorting.
187
- #
188
- # @note The option :t only applies to the top and controversial sorts.
189
- # @return [Objects::Listing<Objects::Thing>]
190
- %w(hot new top controversial comments).each do |sort|
191
- define_method :"get_#{sort}" do |**params|
192
- client.send(:"get_#{sort}", self, **params)
193
- end
194
- end
195
-
196
- # Search.
197
- # @param query [String] The query string.
198
- # @param params [Hash] A list of params to send with the request.
199
- # @option params [String] :after Return results after the given
200
- # fullname.
201
- # @option params [String :before Return results before the given
202
- # fullname.
203
- # @option params [Integer] :count The number of items already seen in
204
- # the listing.
205
- # @option params [1..100] :limit The maximum number of things to
206
- # return.
207
- # @option params [:cloudsearch, :lucene, :plain] :syntax The type of
208
- # syntax to use.
209
- # @option params [:relevance, :new, :hot, :top, :comments] :sort The
210
- # way to sort the results.
211
- # @option params [:hour, :day, :week, :month, :year, :all] :t The
212
- # time period to consider when sorting.
213
- #
214
- # @note The option :t only applies to the top and controversial sorts.
215
- # @return [Objects::Listing<Objects::Thing>]
216
- def search(query, **params)
217
- client.search(query, self, **params)
218
- end
219
-
220
- # @!endgroup
221
-
222
- # @!group Moderation
223
-
224
- # @!method get_reports(**params)
225
- # @!method get_spam(**params)
226
- # @!method get_modqueue(**params)
227
- # @!method get_unmoderated(**params)
228
- # @!method get_edited(**params)
229
- #
230
- # Get the appropriate moderator listing.
231
- # @param [Hash] params A list of params to send with the request.
232
- # @option params [String] :after Return results after the given
233
- # fullname.
234
- # @option params [String] :before Return results before the given
235
- # fullname.
236
- # @option params [Integer] :count The number of items already seen
237
- # in the listing.
238
- # @option params [1..100] :limit The maximum number of things to
239
- # return.
240
- # @option params :location No idea what this does.
241
- # @option params [:links, :comments] :only The type of things to show.
242
- #
243
- # @return [Objects::Listing<Objects::Thing>]
244
- # @see https://www.reddit.com/dev/api#GET_about_{location}
245
- %w(reports spam modqueue unmoderated edited).each do |sort|
246
- define_method :"get_#{sort}" do |**params|
247
- client.request_object(
248
- :get, "/r/#{display_name}/about/#{sort}", params
249
- )
250
- end
251
- end
252
-
253
- # @return [Objects::Base] The current settings of a subreddit.
254
- def admin_about
255
- client.request_object(:get, "/r/#{display_name}/about/edit.json")
256
- end
257
-
258
- # @return [Objects::Base] The current moderators of a subreddit.
259
- def moderator_about
260
- body = get("/r/#{display_name}/about/moderators.json").body
261
- client.object_from_body(
262
- kind: "Listing",
263
- data: {
264
- children: body[:data][:children]
265
- }
266
- )
267
- end
268
-
269
- # Edit the subreddit's settings
270
- # @param [Hash] attributes The subreddit's new settings.
271
- # @note This method may make additional requests if not all of the
272
- # required attributes are provided. Take a look at the source for the
273
- # required attributes required to avoid making the additional request.
274
- # @see https://github.com/alaycock/MeetCal-bot/blob/master/serverInfo.conf
275
- def admin_edit(attributes)
276
- params = {
277
- # Subreddit name
278
- sr: fullname,
279
- # Apparently useless options
280
- show_cname_sidebar: true,
281
- :"header-title" => title
282
- }
283
-
284
- required = %i(
285
- allow_top collapse_deleted_comments comment_score_hide_mins
286
- css_on_cname description exclude_banned_modqueue lang link_type name
287
- over_18 public_description public_traffic show_media spam_comments
288
- spam_links spam_selfposts submit_link_label submit_text
289
- submit_text_label title type wiki_edit_age wiki_edit_karma wikimode
290
- )
291
-
292
- if required.all? { |key| attributes.key?(key) }
293
- params.merge!(attributes)
294
- else
295
- about = admin_about
296
- final = about
297
- .select { |k, _| required.include?(k) }
298
- .merge(
299
- name: display_name,
300
- type: about[:subreddit_type],
301
- lang: about[:language],
302
- link_type: about[:content_options],
303
- allow_top: true,
304
- css_on_cname: true
305
- )
306
- .merge(attributes)
307
- params.merge!(final)
308
- end
309
-
310
- post("/api/site_admin", params)
311
- end
312
-
313
- # Add or replace the subreddit image or header logo.
314
- # @param [String, IO] file The path/url to the file or the file itself.
315
- # @param [String] name The name of the uploaded file.
316
- # @return [String] The url of the image on reddit's CDN.
317
- def upload_image(file, name = nil)
318
- io = (file.is_a?(IO) ? file : File.open(file, "r"))
319
- type = FastImage.type(io)
320
- payload = Faraday::UploadIO.new(io, "image/#{type}")
321
-
322
- params = {file: payload, header: (name ? 0 : 1), img_type: type}
323
- params[:name] = name if name
324
- post("/r/#{display_name}/api/upload_sr_img", params).body[:img_src]
325
- end
326
-
327
- # @!endgroup
328
- end
329
- end
330
- end