redd 0.8.8 → 0.9.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.rubocop.yml +4 -1
- data/CONTRIBUTING.md +63 -0
- data/Guardfile +7 -0
- data/README.md +6 -5
- data/Rakefile +1 -1
- data/TODO.md +423 -0
- data/bin/console +91 -77
- data/bin/guard +2 -0
- data/lib/redd.rb +7 -5
- data/lib/redd/api_client.rb +2 -3
- data/lib/redd/auth_strategies/auth_strategy.rb +7 -2
- data/lib/redd/auth_strategies/script.rb +7 -0
- data/lib/redd/auth_strategies/userless.rb +7 -0
- data/lib/redd/auth_strategies/web.rb +6 -1
- data/lib/redd/client.rb +0 -3
- data/lib/redd/errors.rb +56 -0
- data/lib/redd/middleware.rb +10 -8
- data/lib/redd/models/access.rb +30 -18
- data/lib/redd/models/comment.rb +185 -27
- data/lib/redd/models/front_page.rb +16 -36
- data/lib/redd/models/gildable.rb +1 -1
- data/lib/redd/models/inboxable.rb +13 -3
- data/lib/redd/models/listing.rb +27 -6
- data/lib/redd/models/live_thread.rb +76 -23
- data/lib/redd/models/live_update.rb +46 -0
- data/lib/redd/models/messageable.rb +1 -1
- data/lib/redd/models/mod_action.rb +59 -0
- data/lib/redd/models/model.rb +23 -0
- data/lib/redd/models/moderatable.rb +6 -6
- data/lib/redd/models/modmail.rb +61 -0
- data/lib/redd/models/modmail_conversation.rb +154 -0
- data/lib/redd/models/modmail_message.rb +35 -0
- data/lib/redd/models/more_comments.rb +29 -5
- data/lib/redd/models/multireddit.rb +63 -20
- data/lib/redd/models/paginated_listing.rb +113 -0
- data/lib/redd/models/postable.rb +11 -13
- data/lib/redd/models/private_message.rb +78 -11
- data/lib/redd/models/replyable.rb +2 -2
- data/lib/redd/models/reportable.rb +14 -0
- data/lib/redd/models/searchable.rb +2 -2
- data/lib/redd/models/self.rb +17 -0
- data/lib/redd/models/session.rb +75 -31
- data/lib/redd/models/submission.rb +309 -56
- data/lib/redd/models/subreddit.rb +330 -103
- data/lib/redd/models/trophy.rb +34 -0
- data/lib/redd/models/user.rb +185 -46
- data/lib/redd/models/wiki_page.rb +37 -16
- data/lib/redd/utilities/error_handler.rb +13 -13
- data/lib/redd/utilities/unmarshaller.rb +7 -5
- data/lib/redd/version.rb +1 -1
- data/redd.gemspec +18 -15
- metadata +82 -16
- data/lib/redd/error.rb +0 -53
- data/lib/redd/models/basic_model.rb +0 -80
- data/lib/redd/models/lazy_model.rb +0 -75
- data/lib/redd/models/mod_mail.rb +0 -142
- data/lib/redd/utilities/stream.rb +0 -61
@@ -0,0 +1,35 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'time'
|
4
|
+
require_relative 'model'
|
5
|
+
|
6
|
+
module Redd
|
7
|
+
module Models
|
8
|
+
# Represents a message in the new modmail.
|
9
|
+
class ModmailMessage < Model
|
10
|
+
# @!attribute [r] id
|
11
|
+
# @return [String] the message id
|
12
|
+
property :id
|
13
|
+
|
14
|
+
# @!attribute [r] body
|
15
|
+
# @return [String] the html conversation body
|
16
|
+
property :body
|
17
|
+
|
18
|
+
# @!attribute [r] markdown_body
|
19
|
+
# @return [String] the body in markdown form
|
20
|
+
property :markdown_body, from: :bodyMarkdown
|
21
|
+
|
22
|
+
# @!attribute [r] author
|
23
|
+
# @return [Object] FIXME: do shit
|
24
|
+
property :author
|
25
|
+
|
26
|
+
# @!attribute [r] internal?
|
27
|
+
# @return [Boolean] whether the message is internal
|
28
|
+
property :internal?, from: :isInternal
|
29
|
+
|
30
|
+
# @!attribute [r] date
|
31
|
+
# @return [Time] the message date
|
32
|
+
property :date, with: ->(t) { Time.parse(t) }
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -1,20 +1,20 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'model'
|
4
4
|
|
5
5
|
module Redd
|
6
6
|
module Models
|
7
7
|
# An object that represents a bunch of comments that need to be expanded.
|
8
|
-
class MoreComments <
|
8
|
+
class MoreComments < Model
|
9
9
|
# Expand the object's children into a listing of Comments and MoreComments.
|
10
10
|
# @param link [Submission] the submission the object belongs to
|
11
11
|
# @param sort [String] the sort order of the submission
|
12
12
|
# @return [Listing<Comment, MoreComments>] the expanded children
|
13
13
|
def expand(link:, sort: nil)
|
14
|
-
params = { link_id: link.name, children:
|
14
|
+
params = { link_id: link.name, children: read_attribute(:children).join(',') }
|
15
15
|
params[:sort] = sort if sort
|
16
16
|
params[:sort] = link.sort_order if link.sort_order
|
17
|
-
|
17
|
+
client.model(:get, '/api/morechildren', params)
|
18
18
|
end
|
19
19
|
|
20
20
|
# Keep expanding until all top-level MoreComments are converted to comments.
|
@@ -44,8 +44,32 @@ module Redd
|
|
44
44
|
|
45
45
|
# @return [Array<String>] an array representation of self
|
46
46
|
def to_ary
|
47
|
-
|
47
|
+
read_attribute(:children)
|
48
48
|
end
|
49
|
+
|
50
|
+
# @!attribute [r] count
|
51
|
+
# @return [Integer] the comments under this object
|
52
|
+
property :count
|
53
|
+
|
54
|
+
# @!attribute [r] name
|
55
|
+
# @return [String] the object fullname
|
56
|
+
property :name
|
57
|
+
|
58
|
+
# @!attribute [r] id
|
59
|
+
# @return [String] the object id
|
60
|
+
property :id
|
61
|
+
|
62
|
+
# @!attribute [r] parent_id
|
63
|
+
# @return [String] the parent fullname
|
64
|
+
property :parent_id
|
65
|
+
|
66
|
+
# @!attribute [r] depth
|
67
|
+
# @return [Integer] the depth
|
68
|
+
property :depth
|
69
|
+
|
70
|
+
# @!attribute [r] children
|
71
|
+
# @return [Array<String>] the unexpanded comments
|
72
|
+
property :children
|
49
73
|
end
|
50
74
|
end
|
51
75
|
end
|
@@ -1,19 +1,11 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'model'
|
4
4
|
|
5
5
|
module Redd
|
6
6
|
module Models
|
7
7
|
# A multi.
|
8
|
-
class Multireddit <
|
9
|
-
# Create a Multireddit from its path.
|
10
|
-
# @param client [APIClient] the api client to initialize the object with
|
11
|
-
# @param id [String] the multi's path (with a leading and trailing slash)
|
12
|
-
# @return [Multireddit]
|
13
|
-
def self.from_id(client, id)
|
14
|
-
new(client, path: id)
|
15
|
-
end
|
16
|
-
|
8
|
+
class Multireddit < Model
|
17
9
|
# Get the appropriate listing.
|
18
10
|
# @param sort [:hot, :new, :top, :controversial, :comments, :rising, :gilded] the type of
|
19
11
|
# listing
|
@@ -29,7 +21,7 @@ module Redd
|
|
29
21
|
# @return [Listing<Submission>]
|
30
22
|
def listing(sort, **params)
|
31
23
|
params[:t] = params.delete(:time) if params.key?(:time)
|
32
|
-
|
24
|
+
client.model(:get, "#{read_attribute(:path)}#{sort}", params)
|
33
25
|
end
|
34
26
|
|
35
27
|
# @!method hot(**params)
|
@@ -41,20 +33,71 @@ module Redd
|
|
41
33
|
# @!method gilded(**params)
|
42
34
|
#
|
43
35
|
# @see #listing
|
44
|
-
%i
|
36
|
+
%i[hot new top controversial comments rising gilded].each do |sort|
|
45
37
|
define_method(sort) { |**params| listing(sort, **params) }
|
46
38
|
end
|
47
39
|
|
48
|
-
|
40
|
+
# @!attribute [r] can_edit?
|
41
|
+
# @return [Boolean] whether the user can edit the multireddit
|
42
|
+
property :can_edit?, from: :can_edit
|
49
43
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
44
|
+
# @!attribute [r] display_name
|
45
|
+
# @return [String] the multi's display name
|
46
|
+
property :display_name
|
47
|
+
|
48
|
+
# @!attribute [r] name
|
49
|
+
# @return [String] the multireddit name
|
50
|
+
property :name
|
51
|
+
|
52
|
+
# @!attribute [r] description_md
|
53
|
+
# @return [String] the markdown verion of the description
|
54
|
+
property :description_md
|
55
|
+
|
56
|
+
# @!attribute [r] description_html
|
57
|
+
# @return [String] the html-rendered description
|
58
|
+
property :description_html
|
59
|
+
|
60
|
+
# @!attribute [r] copied_from
|
61
|
+
# @return [Multireddit, nil] the multi this one was copied from
|
62
|
+
property :copied_from, with: ->(n) { Multireddit.new(client, path: n) if n }
|
63
|
+
|
64
|
+
# @!attribute [r] icon_url
|
65
|
+
# @return [String, nil] the icon url
|
66
|
+
property :icon_url
|
67
|
+
|
68
|
+
# @!attribute [r] subreddits
|
69
|
+
# @return [Array<Subreddit>] the subreddits in this multi
|
70
|
+
property :subreddits,
|
71
|
+
with: ->(a) { a.map { |n| Subreddit.new(client, display_name: n.fetch(:name)) } }
|
72
|
+
|
73
|
+
# @!attribute [r] created_at
|
74
|
+
# @return [Time] the creation time
|
75
|
+
property :created_at, from: :created_utc, with: ->(t) { Time.at(t) }
|
76
|
+
|
77
|
+
# @!attribute [r] key_color
|
78
|
+
# @return [String] a hex color
|
79
|
+
property :key_color
|
80
|
+
|
81
|
+
# @!attribute [r] visibility
|
82
|
+
# @return [String] the multi visibility, either "public" or "private"
|
83
|
+
property :visibility
|
84
|
+
|
85
|
+
# @!attribute [r] icon_name
|
86
|
+
# @return [String] the icon name
|
87
|
+
property :icon_name
|
88
|
+
|
89
|
+
# @!attribute [r] weighting_scheme
|
90
|
+
# @return [String]
|
91
|
+
property :weighting_scheme
|
92
|
+
|
93
|
+
# @!attribute [r] path
|
94
|
+
# @return [String] the multi path
|
95
|
+
property :path, :required
|
96
|
+
|
97
|
+
private
|
55
98
|
|
56
|
-
def
|
57
|
-
|
99
|
+
def lazer_reload
|
100
|
+
client.get("/api/multi#{read_attribute(:path)}").body[:data]
|
58
101
|
end
|
59
102
|
end
|
60
103
|
end
|
@@ -0,0 +1,113 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative 'model'
|
4
|
+
|
5
|
+
module Redd
|
6
|
+
module Models
|
7
|
+
# An enumerable type that covers listings and expands forwards.
|
8
|
+
class PaginatedListing
|
9
|
+
include Enumerable
|
10
|
+
|
11
|
+
# A simple fixed-size ring buffer.
|
12
|
+
# @api private
|
13
|
+
class RingBuffer
|
14
|
+
def initialize(size)
|
15
|
+
@size = size
|
16
|
+
@backing_array = Array.new(size)
|
17
|
+
@pointer = 0
|
18
|
+
end
|
19
|
+
|
20
|
+
def include?(el)
|
21
|
+
@backing_array.include?(el)
|
22
|
+
end
|
23
|
+
|
24
|
+
def add(el)
|
25
|
+
@backing_array[@pointer] = el
|
26
|
+
@pointer = (@pointer + 1) % @size
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
# Create an expandable listing.
|
31
|
+
# @param client [APIClient] the caller to use for streams
|
32
|
+
# @param options [Hash]
|
33
|
+
# @option options [String] :before the listing's before parameter
|
34
|
+
# @option options [String] :after the listing's after parameter
|
35
|
+
# @option options [Integer] :limit the maximum number of items to fetch
|
36
|
+
# @yieldparam after [String] the fullname of the item to fetch after
|
37
|
+
# @yieldparam limit [Integer] the number of items to fetch (max 100)
|
38
|
+
# @yieldreturn [Listing] the listing to return
|
39
|
+
def initialize(client, **options, &block)
|
40
|
+
raise ArgumentError, 'block must be provided' unless block_given?
|
41
|
+
|
42
|
+
@client = client
|
43
|
+
@caller = block
|
44
|
+
@before = options[:before]
|
45
|
+
@after = options[:after]
|
46
|
+
@limit = options[:limit] || 1000
|
47
|
+
end
|
48
|
+
|
49
|
+
# Go forward through the listing.
|
50
|
+
# @yield [Model] the object returned in the listings
|
51
|
+
# @return [Enumerator] if a block wasn't provided
|
52
|
+
def each(&block)
|
53
|
+
return _each(&block) if block_given?
|
54
|
+
enum_for(:_each)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Stream through the listing.
|
58
|
+
# @note If you iterate through the stream, you'll loop forever.
|
59
|
+
# This may or may not be desirable.
|
60
|
+
# @yield [Model] the object returned in the listings
|
61
|
+
# @return [Enumerator] if a block wasn't provided
|
62
|
+
def stream(&block)
|
63
|
+
return _stream(&block) if block_given?
|
64
|
+
enum_for(:_stream)
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
# Go backward through the listing.
|
70
|
+
# @yield [Object] the object returned in the listings
|
71
|
+
def _stream(&block)
|
72
|
+
reverse_each(&block) if @limit > 0
|
73
|
+
buffer = RingBuffer.new(100)
|
74
|
+
loop do
|
75
|
+
remaining = fetch_prev_listing
|
76
|
+
remaining.reverse_each do |o|
|
77
|
+
next if buffer.include?(o)
|
78
|
+
yield o
|
79
|
+
buffer.add(o)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Go forward through the listing.
|
85
|
+
# @yield [Object] the object returned in the listings
|
86
|
+
def _each(&block)
|
87
|
+
loop do
|
88
|
+
return if @limit == 0
|
89
|
+
remaining = fetch_next_listing
|
90
|
+
return if remaining.children.empty? # if the fetched listing is empty
|
91
|
+
remaining.each(&block)
|
92
|
+
return if remaining.after.nil? # if there's no link to the next item
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
# Fetch the next listing with @caller and update @after and @limit.
|
97
|
+
def fetch_next_listing
|
98
|
+
caller_limit = [@limit, 100].min
|
99
|
+
listing = @caller.call(before: nil, after: @after, limit: caller_limit)
|
100
|
+
@after = listing.after
|
101
|
+
@limit -= caller_limit
|
102
|
+
listing
|
103
|
+
end
|
104
|
+
|
105
|
+
# Fetch the previous listing with @caller and update @before.
|
106
|
+
def fetch_prev_listing
|
107
|
+
listing = @caller.call(before: @before, after: nil, limit: 100)
|
108
|
+
@before = listing.first.name unless listing.empty?
|
109
|
+
listing
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
data/lib/redd/models/postable.rb
CHANGED
@@ -8,37 +8,36 @@ module Redd
|
|
8
8
|
# @param text [String] The new text.
|
9
9
|
# @return [self] the edited thing
|
10
10
|
def edit(text)
|
11
|
-
|
12
|
-
@attributes[is_a?(Submission) ? :selftext : :body] = text
|
11
|
+
client.post('/api/editusertext', thing_id: read_attribute(:name), text: text)
|
13
12
|
self
|
14
13
|
end
|
15
14
|
|
16
15
|
# Delete the thing.
|
17
16
|
def delete
|
18
|
-
|
17
|
+
client.post('/api/del', id: read_attribute(:name))
|
19
18
|
end
|
20
19
|
|
21
20
|
# Save a link or comment to the user's account.
|
22
21
|
# @param category [String] a category to save to
|
23
22
|
def save(category = nil)
|
24
|
-
params = { id:
|
23
|
+
params = { id: read_attribute(:name) }
|
25
24
|
params[:category] = category if category
|
26
|
-
|
25
|
+
client.post('/api/save', params)
|
27
26
|
end
|
28
27
|
|
29
28
|
# Remove the link or comment from the user's saved links.
|
30
29
|
def unsave
|
31
|
-
|
30
|
+
client.post('/api/unsave', id: read_attribute(:name))
|
32
31
|
end
|
33
32
|
|
34
33
|
# Hide a link from the user.
|
35
34
|
def hide
|
36
|
-
|
35
|
+
client.post('/api/hide', id: read_attribute(:name))
|
37
36
|
end
|
38
37
|
|
39
38
|
# Unhide a previously hidden link.
|
40
39
|
def unhide
|
41
|
-
|
40
|
+
client.post('/api/unhide', id: read_attribute(:name))
|
42
41
|
end
|
43
42
|
|
44
43
|
# Upvote the model.
|
@@ -58,12 +57,12 @@ module Redd
|
|
58
57
|
|
59
58
|
# Send replies to this thing to the user's inbox.
|
60
59
|
def enable_inbox_replies
|
61
|
-
|
60
|
+
client.post('/api/sendreplies', id: read_attribute(:name), state: true)
|
62
61
|
end
|
63
62
|
|
64
63
|
# Stop sending replies to this thing to the user's inbox.
|
65
64
|
def disable_inbox_replies
|
66
|
-
|
65
|
+
client.post('/api/sendreplies', id: read_attribute(:name), state: false)
|
67
66
|
end
|
68
67
|
|
69
68
|
private
|
@@ -71,9 +70,8 @@ module Redd
|
|
71
70
|
# Send a vote.
|
72
71
|
# @param direction [-1, 0, 1] the direction to vote in
|
73
72
|
def vote(direction)
|
74
|
-
fullname =
|
75
|
-
|
76
|
-
@attributes[:ups] += direction
|
73
|
+
fullname = read_attribute(:name)
|
74
|
+
client.post('/api/vote', id: fullname, dir: direction)
|
77
75
|
end
|
78
76
|
end
|
79
77
|
end
|
@@ -1,38 +1,105 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require_relative '
|
3
|
+
require_relative 'model'
|
4
4
|
require_relative 'inboxable'
|
5
5
|
require_relative 'replyable'
|
6
|
+
require_relative 'reportable'
|
6
7
|
|
7
8
|
module Redd
|
8
9
|
module Models
|
9
10
|
# A private message
|
10
|
-
class PrivateMessage <
|
11
|
+
class PrivateMessage < Model
|
11
12
|
include Inboxable
|
12
13
|
include Replyable
|
14
|
+
include Reportable
|
13
15
|
|
14
16
|
# Delete the message from the user's inbox.
|
15
17
|
def delete
|
16
|
-
|
18
|
+
client.post('/api/del_msg', id: read_attribute(:name))
|
17
19
|
end
|
18
20
|
|
19
21
|
# Mute the author of the message.
|
20
22
|
def mute_author
|
21
|
-
|
23
|
+
client.post('/api/mute_message_author', id: read_attribute(:name))
|
22
24
|
end
|
23
25
|
|
24
26
|
# Unmute the author of the message.
|
25
27
|
def unmute_author
|
26
|
-
|
28
|
+
client.post('/api/unmute_message_author', id: read_attribute(:name))
|
27
29
|
end
|
28
30
|
|
29
|
-
|
31
|
+
# @!attribute [r] first_message
|
32
|
+
# @return [Integer] not sure what this does
|
33
|
+
property :first_message
|
30
34
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
35
|
+
# @!attribute [r] first_message_name
|
36
|
+
# @return [String] the fullname of the first message
|
37
|
+
property :first_message_name
|
38
|
+
|
39
|
+
# @!attribute [r] subreddit
|
40
|
+
# @return [Subreddit, nil] the subreddit that sent the message
|
41
|
+
property :subreddit, with: ->(s) { Subreddit.new(client, display_name: s) if s }
|
42
|
+
|
43
|
+
# @!attribute [r] replies
|
44
|
+
# @return [Listing<PrivateMessage>]
|
45
|
+
property :replies, with: ->(l) { Listing.new(client, l[:data]) if l.is_a?(Hash) }
|
46
|
+
|
47
|
+
# @!attribute [r] id
|
48
|
+
# @return [String] the message id
|
49
|
+
property :id
|
50
|
+
|
51
|
+
# @!attribute [r] subject
|
52
|
+
# @return [String] the message subject
|
53
|
+
property :subject
|
54
|
+
|
55
|
+
# @!attribute [r] was_comment?
|
56
|
+
# @return [Boolean]
|
57
|
+
property :was_comment?, from: :was_comment
|
58
|
+
|
59
|
+
# @!attribute [r] author
|
60
|
+
# @return [User] the message author
|
61
|
+
property :author, with: ->(n) { User.new(client, name: n) if n }
|
62
|
+
|
63
|
+
# @!attribute [r] num_comments
|
64
|
+
# @return [Integer] huh?
|
65
|
+
property :num_comments
|
66
|
+
|
67
|
+
# @!attribute [r] parent_id
|
68
|
+
# @return [String, nil] the parent id
|
69
|
+
property :parent_id
|
70
|
+
|
71
|
+
# @!attribute [r] subreddit_name_prefixed
|
72
|
+
# @return [String] the subreddit name, prefixed with "r/"
|
73
|
+
property :subreddit_name_prefixed
|
74
|
+
|
75
|
+
# @!attribute [r] new?
|
76
|
+
# @return [Boolean] whether the message is new
|
77
|
+
property :new?, from: :new
|
78
|
+
|
79
|
+
# @!attribute [r] body
|
80
|
+
# @return [String] the message body
|
81
|
+
property :body
|
82
|
+
|
83
|
+
# @!attribute [r] body_html
|
84
|
+
# @return [String] the html-rendered version of the body
|
85
|
+
property :body_html
|
86
|
+
|
87
|
+
# @!attribute [r] dest
|
88
|
+
# @return [String] the recipient of the message
|
89
|
+
# @todo maybe convert the object to Subreddit/User?
|
90
|
+
property :dest
|
91
|
+
|
92
|
+
# @!attribute [r] name
|
93
|
+
# @return [String] the message fullname
|
94
|
+
property :name
|
95
|
+
|
96
|
+
# @!attribute [r] created
|
97
|
+
# @return [Time] the time the message was created
|
98
|
+
property :created_utc, with: ->(t) { Time.at(t) }
|
99
|
+
|
100
|
+
# @!attribute [r] distinguished
|
101
|
+
# @return [String] the level the message is distinguished
|
102
|
+
property :distinguished
|
36
103
|
end
|
37
104
|
end
|
38
105
|
end
|