spinels-redd 0.9.0

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.
Files changed (69) hide show
  1. checksums.yaml +7 -0
  2. data/.github/dependabot.yml +7 -0
  3. data/.github/workflows/ci.yml +52 -0
  4. data/.gitignore +10 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +29 -0
  7. data/CONTRIBUTING.md +63 -0
  8. data/Gemfile +6 -0
  9. data/Guardfile +7 -0
  10. data/LICENSE.txt +21 -0
  11. data/README.md +119 -0
  12. data/Rakefile +12 -0
  13. data/TODO.md +423 -0
  14. data/bin/console +127 -0
  15. data/bin/guard +2 -0
  16. data/bin/setup +8 -0
  17. data/docs/guides/.keep +0 -0
  18. data/docs/tutorials/creating-bots-with-redd.md +101 -0
  19. data/docs/tutorials/creating-webapps-with-redd.md +124 -0
  20. data/docs/tutorials/make-a-grammar-bot.md +5 -0
  21. data/docs/tutorials.md +7 -0
  22. data/lib/redd/api_client.rb +116 -0
  23. data/lib/redd/assist/delete_badly_scoring.rb +64 -0
  24. data/lib/redd/auth_strategies/auth_strategy.rb +68 -0
  25. data/lib/redd/auth_strategies/script.rb +35 -0
  26. data/lib/redd/auth_strategies/userless.rb +29 -0
  27. data/lib/redd/auth_strategies/web.rb +36 -0
  28. data/lib/redd/client.rb +91 -0
  29. data/lib/redd/errors.rb +65 -0
  30. data/lib/redd/middleware.rb +125 -0
  31. data/lib/redd/models/access.rb +54 -0
  32. data/lib/redd/models/comment.rb +229 -0
  33. data/lib/redd/models/front_page.rb +55 -0
  34. data/lib/redd/models/gildable.rb +13 -0
  35. data/lib/redd/models/inboxable.rb +33 -0
  36. data/lib/redd/models/listing.rb +52 -0
  37. data/lib/redd/models/live_thread.rb +133 -0
  38. data/lib/redd/models/live_update.rb +46 -0
  39. data/lib/redd/models/messageable.rb +20 -0
  40. data/lib/redd/models/mod_action.rb +59 -0
  41. data/lib/redd/models/model.rb +23 -0
  42. data/lib/redd/models/moderatable.rb +46 -0
  43. data/lib/redd/models/modmail.rb +61 -0
  44. data/lib/redd/models/modmail_conversation.rb +154 -0
  45. data/lib/redd/models/modmail_message.rb +35 -0
  46. data/lib/redd/models/more_comments.rb +96 -0
  47. data/lib/redd/models/multireddit.rb +104 -0
  48. data/lib/redd/models/paginated_listing.rb +124 -0
  49. data/lib/redd/models/postable.rb +83 -0
  50. data/lib/redd/models/private_message.rb +105 -0
  51. data/lib/redd/models/replyable.rb +16 -0
  52. data/lib/redd/models/reportable.rb +14 -0
  53. data/lib/redd/models/searchable.rb +35 -0
  54. data/lib/redd/models/self.rb +17 -0
  55. data/lib/redd/models/session.rb +198 -0
  56. data/lib/redd/models/submission.rb +405 -0
  57. data/lib/redd/models/subreddit.rb +670 -0
  58. data/lib/redd/models/trophy.rb +34 -0
  59. data/lib/redd/models/user.rb +239 -0
  60. data/lib/redd/models/wiki_page.rb +56 -0
  61. data/lib/redd/utilities/error_handler.rb +73 -0
  62. data/lib/redd/utilities/rate_limiter.rb +21 -0
  63. data/lib/redd/utilities/unmarshaller.rb +70 -0
  64. data/lib/redd/version.rb +5 -0
  65. data/lib/redd.rb +129 -0
  66. data/lib/spinels-redd.rb +3 -0
  67. data/logo.png +0 -0
  68. data/spinels-redd.gemspec +39 -0
  69. metadata +298 -0
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'model'
4
+
5
+ module Redd
6
+ module Models
7
+ # A backward-expading listing of items.
8
+ # @see Stream
9
+ class Listing < Model
10
+ include Enumerable
11
+
12
+ # Create an empty listing with a client.
13
+ # @param client [APIClient] the client to create the listing with
14
+ # @return [Listing] the empty listing
15
+ def self.empty(client)
16
+ Listing.new(client, children: [])
17
+ end
18
+
19
+ # Create a fully initialized listing.
20
+ # @param client [APIClient] the api client
21
+ # @param attributes [Hash] the attribute hash
22
+ def initialize(client, attributes = {})
23
+ super
24
+ fully_loaded!
25
+ end
26
+
27
+ # @return [Array<Comment, Submission, PrivateMessage>] an array representation of self
28
+ def to_a
29
+ read_attribute(:children)
30
+ end
31
+ alias to_ary to_a
32
+
33
+ %i[[] each empty? first last].each do |method_name|
34
+ define_method(method_name) do |*args, &block|
35
+ read_attribute(:children).public_send(method_name, *args, &block)
36
+ end
37
+ end
38
+
39
+ # @!attribute [r] before
40
+ # @return [String] the fullname of the item before this listing
41
+ property :before, :nil
42
+
43
+ # @!attribute [r] after
44
+ # @return [String] the fullname of the item that the next listing will start from
45
+ property :after, :nil
46
+
47
+ # @!attribute [r] children
48
+ # @return [Array<Model>] the listing's children
49
+ property :children, :required, with: ->(a) { a.map { |m| client.unmarshal(m) } }
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,133 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'model'
4
+
5
+ module Redd
6
+ module Models
7
+ # Represents a live thread.
8
+ class LiveThread < Model
9
+ # Get the updates from the thread.
10
+ # @param params [Hash] a list of params to send with the request
11
+ # @option params [String] :after return results after the given fullname
12
+ # @option params [String] :before return results before the given fullname
13
+ # @option params [Integer] :count the number of items already seen in the listing
14
+ # @option params [1..100] :limit the maximum number of things to return
15
+ # @return [Listing]
16
+ def updates(**params)
17
+ client.model(:get, "/live/#{read_attribute(:id)}", params)
18
+ end
19
+
20
+ # Configure the settings of this live thread
21
+ # @param params [Hash] a list of params to send with the request
22
+ # @option params [String] :description the new description
23
+ # @option params [Boolean] :nsfw whether the thread is for users 18 and above
24
+ # @option params [String] :resources the new resources
25
+ # @option params [String] :title the thread title
26
+ def configure(**params)
27
+ client.post("/api/live/#{read_attribute(:id)}/edit", params)
28
+ end
29
+
30
+ # Add an update to this live event.
31
+ # @param body [String] the update text
32
+ def update(body)
33
+ client.post("/api/live/#{read_attribute(:id)}/update", body: body)
34
+ end
35
+
36
+ # Strike out a live thread update.
37
+ # @param live_update [LiveUpdate] the update to strike out
38
+ def strike_update(live_update)
39
+ client.post("/api/live/#{read_attribute(:id)}/strike_update", id: live_update.name)
40
+ end
41
+
42
+ # Delete a live thread update.
43
+ # @param live_update [LiveUpdate] the update to strike out
44
+ def delete_update(live_update)
45
+ client.post("/api/live/#{read_attribute(:id)}/delete_update", id: live_update.name)
46
+ end
47
+
48
+ # @return [Array<User>] the contributors to this thread
49
+ def contributors
50
+ client.get("/live/#{read_attribute(:id)}/contributors").body[0][:data].map do |user|
51
+ User.new(client, user)
52
+ end
53
+ end
54
+
55
+ # @return [Array<User>] users invited to contribute to this thread
56
+ def invited_contributors
57
+ client.get("/live/#{read_attribute(:id)}/contributors").body[1][:data].map do |user|
58
+ User.new(client, user)
59
+ end
60
+ end
61
+
62
+ # Returns all discussions that link to this live thread.
63
+ # @param params [Hash] a list of params to send with the request
64
+ # @option params [String] :after return results after the given fullname
65
+ # @option params [String] :before return results before the given fullname
66
+ # @option params [Integer] :count the number of items already seen in the listing
67
+ # @option params [1..100] :limit the maximum number of things to return
68
+ #
69
+ # @return [Listing<Submission>]
70
+ def discussions(**params)
71
+ client.model(:get, "/live/#{read_attribute(:id)}/discussions", params)
72
+ end
73
+
74
+ # @!attribute [r] id
75
+ # @return [String] the thread id
76
+ property :id, :required
77
+
78
+ # @!attribute [r] name
79
+ # @return [String] the thread fullname
80
+ property :name, default: -> { "LiveUpdateEvent_#{read_attribute(:id)}" }
81
+
82
+ # @!attribute [r] description
83
+ # @return [String] the live thread description
84
+ property :description
85
+
86
+ # @!attribute [r] description_html
87
+ # @return [String] the html-rendered thread description
88
+ property :description_html
89
+
90
+ # @!attribute [r] title
91
+ # @return [String] the live thread title
92
+ property :title
93
+
94
+ # @!attribute [r] created_at
95
+ # @return [String] the live thread creation time
96
+ property :created_at, from: :created_utc, with: ->(t) { Time.at(t) }
97
+
98
+ # @!attribute [r] websocket_url
99
+ # @return [String] the websocket url for listening to updates
100
+ property :websocket_url
101
+
102
+ # @!attribute [r] state
103
+ # @return [String] the thread state (e.g. "live")
104
+ property :state
105
+
106
+ # @!attribute [r] nsfw?
107
+ # @return [Boolean] whether the thread is nsfw
108
+ property :nsfw?, from: :nsfw
109
+
110
+ # @!attribute [r] viewer_count
111
+ # @return [Integer] the thread viewer count
112
+ property :viewer_count
113
+
114
+ # @!attribute [r] viewer_count_fuzzed?
115
+ # @return [Boolean] whether the viewer count is fuzzed
116
+ property :viewer_count_fuzzed?, from: :viewer_count_fuzzed
117
+
118
+ # @!attribute [r] resources
119
+ # @return [String] the thread's resources section
120
+ property :resources
121
+
122
+ # @!attribute [r] resources_html
123
+ # @return [String] the html-rendered thread resources
124
+ property :resources_html
125
+
126
+ private
127
+
128
+ def lazer_reload
129
+ client.get("/live/#{read_attribute(:id)}/about").body[:data]
130
+ end
131
+ end
132
+ end
133
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'model'
4
+
5
+ module Redd
6
+ module Models
7
+ # A live thread update.
8
+ class LiveUpdate < Model
9
+ # @!attribute [r] id
10
+ # @return [String] the update id
11
+ property :id, :required
12
+
13
+ # @!attribute [r] name
14
+ # @return [String] the update fullname
15
+ property :name, default: -> { "LiveUpdate_#{read_attribute(:id)}" }
16
+
17
+ # @!attribute [r] body
18
+ # @return [String] the update body
19
+ property :body
20
+
21
+ # @!attribute [r] body_html
22
+ # @return [String] the html-rendered update body
23
+ property :body_html
24
+
25
+ # @!attribute [r] embeds
26
+ # @return [Array]
27
+ property :embeds
28
+
29
+ # @!attribute [r] mobile_embeds
30
+ # @return [Array]
31
+ property :mobile_embeds
32
+
33
+ # @!attribute [r] author
34
+ # @return [User] the poster of the update
35
+ property :author, with: ->(n) { User.new(client, name: n) }
36
+
37
+ # @!attribute [r] created_at
38
+ # @return [Time] the post time
39
+ property :created_at, from: :created_utc, with: ->(t) { Time.at(t) }
40
+
41
+ # @!attribute [r] stricken?
42
+ # @return [Boolean] whether the update is stricken
43
+ property :stricken?, from: :stricken
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Redd
4
+ module Models
5
+ # A model that can be messaged (i.e. Users and Subreddits).
6
+ module Messageable
7
+ # Compose a message to a person or the moderators of a subreddit.
8
+ #
9
+ # @param to [String] the thing to send the message to (overriden by User and Subreddit)
10
+ # @param subject [String] the subject of the message
11
+ # @param text [String] the message text
12
+ # @param from [Subreddit, nil] the subreddit to send the message on behalf of
13
+ def send_message(to:, subject:, text:, from: nil)
14
+ params = { to: to, subject: subject, text: text }
15
+ params[:from_sr] = from.display_name if from
16
+ client.post('/api/compose', params)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,59 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'model'
4
+
5
+ module Redd
6
+ module Models
7
+ # Represents a moderator action, part of a moderation log.
8
+ # @see Subreddit#mod_log
9
+ class ModAction < Model
10
+ # @!attribute [r] description
11
+ # @return [String] the action description
12
+ property :description
13
+
14
+ # @!attribute [r] target_title
15
+ # @return [String] the title of the item that was targeted
16
+ property :target_title
17
+
18
+ # @!attribute [r] target_body
19
+ # @return [String] the body of the item that was targeted
20
+ property :target_body
21
+
22
+ # @!attribute [r] target_permalink
23
+ # @return [String] the **relative** permalink to the item
24
+ property :target_permalink
25
+
26
+ # @!attribute [r] target_author
27
+ # @return [User] the target user
28
+ property :target_author, with: ->(n) { User.new(client, name: n) }
29
+
30
+ # @!attribute [r] mod_id36
31
+ # @return [String] the id of the moderator that performed this action
32
+ property :mod_id36
33
+
34
+ # @!attribute [r] created_at
35
+ # @return [Time] the time when the action was done
36
+ property :created_at, from: :created_utc, with: ->(t) { Time.at(t) }
37
+
38
+ # @!attribute [r] subreddit
39
+ # @return [Subreddit] the subreddit the action was performed on
40
+ property :subreddit, with: ->(n) { Subreddit.new(client, display_name: n) }
41
+
42
+ # @!attribute [r] subreddit_name_prefixed
43
+ # @return [String] the subreddit name, prefixed with a "r/"
44
+ property :subreddit_name_prefixed
45
+
46
+ # @!attribute [r] subreddit_id36
47
+ # @return [String] the subreddit's id
48
+ property :subreddit_id36, from: :sr_id36
49
+
50
+ # @!attribute [r] details
51
+ # @return [String] the action details
52
+ property :details
53
+
54
+ # @!attribute [r] action
55
+ # @return [String] the action type
56
+ property :action
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,23 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'lazy_lazer'
4
+
5
+ module Redd
6
+ module Models
7
+ # A base model class.
8
+ class Model
9
+ include LazyLazer
10
+
11
+ # @return [Client] the model's client
12
+ attr_reader :client
13
+
14
+ # Create a new Model.
15
+ # @param client [Client] the model's client
16
+ # @param attributes [Hash] the model's attributes
17
+ def initialize(client, attributes = {})
18
+ super(attributes)
19
+ @client = client
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Redd
4
+ module Models
5
+ # A model that can be managed by a moderator (i.e. Submissions and Comments).
6
+ module Moderatable
7
+ # Approve a submission.
8
+ def approve
9
+ client.post('/api/approve', id: read_attribute(:name))
10
+ end
11
+
12
+ # Remove a submission.
13
+ # @param spam [Boolean] whether or not this item is removed due to it being spam
14
+ def remove(spam: false)
15
+ client.post('/api/remove', id: read_attribute(:name), spam: spam)
16
+ end
17
+
18
+ # Distinguish a link or comment with a sigil to show that it has been created by a moderator.
19
+ # @param how [:yes, :no, :admin, :special, :sticky] how to distinguish the thing
20
+ # @note :sticky is for comments. see {Submission#make_sticky} for posts.
21
+ def distinguish(how = :yes)
22
+ params = { id: read_attribute(:name), how: how }
23
+ if how == :sticky
24
+ params[:how] = :yes
25
+ params[:sticky] = true
26
+ end
27
+ client.post('/api/distinguish', params)
28
+ end
29
+
30
+ # Remove the sigil that shows a thing was created by a moderator.
31
+ def undistinguish
32
+ distinguish(:no)
33
+ end
34
+
35
+ # Stop getting any moderator-related reports on the thing.
36
+ def ignore_reports
37
+ client.post('/api/ignore_reports', id: read_attribute(:name))
38
+ end
39
+
40
+ # Start getting moderator-related reports on the thing again.
41
+ def unignore_reports
42
+ client.post('/api/unignore_reports', id: read_attribute(:name))
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'model'
4
+ require_relative 'subreddit'
5
+
6
+ module Redd
7
+ module Models
8
+ # A container for the new modmail.
9
+ class Modmail < Model
10
+ # @return [Hash<Symbol, Integer>] the number of unread messages in each category
11
+ def unread_count
12
+ client.get('/api/mod/conversations/unread/count').body
13
+ end
14
+
15
+ # @return [Array<Subreddit>] moderated subreddits that are enrolled in the new modmail
16
+ def enrolled
17
+ client.get('/api/mod/conversations/subreddits').body[:subreddits].map do |_, s|
18
+ Subreddit.new(client, s.merge(last_updated: s.delete(:lastUpdated)))
19
+ end
20
+ end
21
+
22
+ # Get the conversations
23
+ # @param subreddits [Subreddit, Array<Subreddit>] the subreddits to limit to
24
+ # @param params [Hash] additional request parameters
25
+ # @option params [String] :after base36 modmail conversation id
26
+ # @option params [Integer] :limit an integer (default: 25)
27
+ # @option params [:recent, :mod, :user, :unread] :sort the sort order
28
+ # @option params [:new, :inprogress, :mod, :notifications, :archived, :highlighted, :all]
29
+ # :state the state to limit the conversations by
30
+ # @return [Array<ModmailConversation>] the conversations
31
+ def conversations(subreddits: nil, **params)
32
+ params[:entity] = Array(subreddits).map(&:display_name).join(',') if subreddits
33
+ client.get('/api/mod/conversations', **params)
34
+ .body[:conversations]
35
+ .values
36
+ .map { |conv| ModmailConversation.new(client, conv) }
37
+ end
38
+
39
+ # Create a new conversation.
40
+ # @param from [Subreddit] the subreddit to send the conversation from
41
+ # @param to [User] the person to send the message to
42
+ # @param subject [String] the message subject
43
+ # @param body [String] the message body
44
+ # @return [ModmailConversation] the created conversation
45
+ def create(from:, to:, subject:, body:, hidden: false)
46
+ ModmailConversation.new(client, client.post(
47
+ '/api/mod/conversations',
48
+ srName: from.display_name, to: to.name,
49
+ subject: subject, body: body, isAuthorHidden: hidden
50
+ ).body[:conversation])
51
+ end
52
+
53
+ # Get a conversation from its base36 id.
54
+ # @param id [String] the conversation's id
55
+ # @return [ModmailConversation]
56
+ def get(id)
57
+ ModmailConversation.new(client, id: id)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,154 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'time'
4
+ require_relative 'model'
5
+
6
+ module Redd
7
+ module Models
8
+ # Represents a conversation in the new modmail.
9
+ class ModmailConversation < Model
10
+ # Add a reply to the ongoing conversation.
11
+ # @param body [String] the message body (probably markdown)
12
+ # @param hidden [Boolean] whether the message is hidden
13
+ # @param internal [Boolean] whether the message is internal
14
+ def reply(body, hidden: false, internal: false)
15
+ # TODO: merge response into the conversation
16
+ client.post(
17
+ "/api/mod/conversations/#{read_attribute(:id)}",
18
+ body: body, isAuthorHidden: hidden, isInternal: internal
19
+ ).body
20
+ end
21
+
22
+ # Mark this conversation as read.
23
+ def mark_as_read
24
+ client.post('/api/mod/conversations/read', conversationIds: [read_attribute(:id)])
25
+ end
26
+
27
+ # Mark this conversation as unread.
28
+ def mark_as_unread
29
+ client.post('/api/mod/conversations/unread', conversationIds: [read_attribute(:id)])
30
+ end
31
+
32
+ # Mark this conversation as archived.
33
+ def archive
34
+ perform_action(:post, 'archive')
35
+ end
36
+
37
+ # Removed this conversation from archived.
38
+ def unarchive
39
+ perform_action(:post, 'unarchive')
40
+ end
41
+
42
+ # Highlight this conversation.
43
+ def highlight
44
+ perform_action(:post, 'highlight')
45
+ end
46
+
47
+ # Remove the highlight on this conversation.
48
+ def unhighlight
49
+ perform_action(:delete, 'highlight')
50
+ end
51
+
52
+ # Mute this conversation.
53
+ def mute
54
+ perform_action(:post, 'mute')
55
+ end
56
+
57
+ # Unmute this conversation.
58
+ def unmute
59
+ perform_action(:post, 'unmute')
60
+ end
61
+
62
+ # @!attribute [r] id
63
+ # @return [String] the conversation id
64
+ property :id, :required
65
+
66
+ # @!attribute [r] messages
67
+ # @return [Array<ModmailMessage>] the modmail messages
68
+ property :messages, with: ->(hsh) { hsh.values.map { |m| ModmailMessage.new(client, m) } }
69
+
70
+ # @!attribute [r] user
71
+ # @return [Object] FIXME: details about the user the conversation deals with
72
+ property :user
73
+
74
+ # @!attribute [r] auto?
75
+ # @return [Boolean]
76
+ property :auto?, from: :isAuto
77
+
78
+ # @!attribute [r] message_ids
79
+ # @return [Array<String>] the conversation's message ids
80
+ property :message_ids, from: :objIds
81
+
82
+ # @!attribute [r] replyable?
83
+ # @return [Boolean] whether you can reply to this conversation
84
+ property :replyable?, from: :isRepliable
85
+
86
+ # @!attribute [r] last_user_update
87
+ # @return [Time] the time of last user update
88
+ property :last_user_update, from: :lastUserUpdate, with: ->(t) { Time.parse(t) }
89
+
90
+ # @!attribute [r] internal?
91
+ # @return [Boolean]
92
+ property :internal?, from: :isInternal
93
+
94
+ # @!attribute [r] last_mod_update
95
+ # @return [Time] the time of last mod update
96
+ property :last_mod_update, from: :lastModUpdate, with: ->(t) { Time.parse(t) }
97
+
98
+ # @!attribute [r] last_updated
99
+ # @return [Time] the time of last update
100
+ property :last_updated, from: :lastUpdated, with: ->(t) { Time.parse(t) }
101
+
102
+ # @!attribute [r] authors
103
+ # @return [Array<Hash>] FIXME: apply conversions
104
+ property :authors
105
+
106
+ # @!attribute [r] owner
107
+ # @return [Hash] FIXME: do shit
108
+ property :owner
109
+
110
+ # @!attribute [r] highlighted?
111
+ # @return [Boolean] whether the conversation is highlighted
112
+ property :highlighted?, from: :isHighlighted
113
+
114
+ # @!attribute [r] subject
115
+ # @return [String] the conversation subject
116
+ property :subject
117
+
118
+ # @!attribute [r] participant
119
+ # @return [Hash] FIXME: do shit
120
+ property :participant
121
+
122
+ # @!attribute [r] state
123
+ # @return [Integer]
124
+ property :state
125
+
126
+ # @!attribute [r] last_unread
127
+ # @return [Object]
128
+ property :last_unread, from: :lastUnread
129
+
130
+ # @!attribute [r] message_count
131
+ # @return [Integer] the message count
132
+ property :message_count, from: :numMessages
133
+
134
+ private
135
+
136
+ def lazer_reload
137
+ fully_loaded!
138
+ response = client.get("/api/mod/conversations/#{read_attribute(:id)}").body
139
+ response[:conversation].merge(
140
+ messages: response[:messages],
141
+ user: response[:user],
142
+ modActions: response[:modActions]
143
+ )
144
+ end
145
+
146
+ # Perform an action on a conversation.
147
+ # @param method [:post, :delete] the method to use
148
+ # @param action [String] the name of the action
149
+ def perform_action(method, action)
150
+ client.send(method, "/api/mod/conversations/#{read_attribute(:id)}/#{action}")
151
+ end
152
+ end
153
+ end
154
+ end
@@ -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