redd 0.8.8 → 0.9.0.pre.1
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.
- 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
|