discorb 0.16.0 → 0.17.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 (115) hide show
  1. checksums.yaml +4 -4
  2. data/.gitattributes +1 -0
  3. data/.github/workflows/build_main.yml +2 -2
  4. data/.github/workflows/build_version.yml +1 -1
  5. data/.github/workflows/codeql-analysis.yml +1 -1
  6. data/.github/workflows/lint-push.yml +3 -5
  7. data/.github/workflows/lint.yml +1 -1
  8. data/.github/workflows/spec.yml +30 -0
  9. data/.lefthook/commit-msg/validator.rb +5 -0
  10. data/.rspec +2 -0
  11. data/.rspec_parallel +2 -0
  12. data/.rubocop.yml +43 -6
  13. data/Changelog.md +14 -1
  14. data/Gemfile +14 -8
  15. data/Rakefile +41 -26
  16. data/bin/console +3 -3
  17. data/docs/Examples.md +1 -1
  18. data/docs/application_command.md +138 -46
  19. data/docs/cli/irb.md +2 -2
  20. data/docs/cli/new.md +14 -9
  21. data/docs/cli/run.md +7 -11
  22. data/docs/cli.md +17 -10
  23. data/docs/events.md +209 -211
  24. data/docs/extension.md +1 -2
  25. data/docs/faq.md +0 -1
  26. data/docs/tutorial.md +12 -12
  27. data/docs/voice_events.md +106 -106
  28. data/examples/commands/message.rb +63 -0
  29. data/examples/commands/permission.rb +18 -0
  30. data/examples/commands/slash.rb +44 -0
  31. data/examples/commands/user.rb +51 -0
  32. data/examples/components/authorization_button.rb +2 -2
  33. data/examples/components/select_menu.rb +2 -2
  34. data/examples/extension/main.rb +1 -1
  35. data/examples/extension/message_expander.rb +5 -2
  36. data/examples/simple/eval.rb +2 -2
  37. data/examples/simple/ping_pong.rb +1 -1
  38. data/examples/simple/rolepanel.rb +1 -1
  39. data/examples/simple/shard.rb +1 -1
  40. data/examples/simple/wait_for_message.rb +1 -1
  41. data/exe/discorb +31 -16
  42. data/lefthook.yml +45 -0
  43. data/lib/discorb/allowed_mentions.rb +1 -0
  44. data/lib/discorb/app_command/command.rb +127 -65
  45. data/lib/discorb/app_command/common.rb +25 -0
  46. data/lib/discorb/app_command/handler.rb +115 -33
  47. data/lib/discorb/app_command.rb +2 -1
  48. data/lib/discorb/application.rb +1 -0
  49. data/lib/discorb/asset.rb +1 -2
  50. data/lib/discorb/attachment.rb +1 -1
  51. data/lib/discorb/audit_logs.rb +11 -8
  52. data/lib/discorb/channel/base.rb +108 -0
  53. data/lib/discorb/channel/category.rb +32 -0
  54. data/lib/discorb/channel/container.rb +44 -0
  55. data/lib/discorb/channel/dm.rb +28 -0
  56. data/lib/discorb/channel/guild.rb +245 -0
  57. data/lib/discorb/channel/stage.rb +140 -0
  58. data/lib/discorb/channel/text.rb +345 -0
  59. data/lib/discorb/channel/thread.rb +321 -0
  60. data/lib/discorb/channel/voice.rb +79 -0
  61. data/lib/discorb/channel.rb +2 -1165
  62. data/lib/discorb/client.rb +38 -26
  63. data/lib/discorb/common.rb +2 -1
  64. data/lib/discorb/components/button.rb +2 -1
  65. data/lib/discorb/components/select_menu.rb +4 -2
  66. data/lib/discorb/components/text_input.rb +12 -2
  67. data/lib/discorb/components.rb +1 -1
  68. data/lib/discorb/embed.rb +22 -7
  69. data/lib/discorb/emoji.rb +30 -3
  70. data/lib/discorb/emoji_table.rb +4969 -3
  71. data/lib/discorb/event.rb +29 -4
  72. data/lib/discorb/exe/about.rb +1 -0
  73. data/lib/discorb/exe/irb.rb +2 -4
  74. data/lib/discorb/exe/new.rb +90 -23
  75. data/lib/discorb/exe/run.rb +8 -22
  76. data/lib/discorb/exe/setup.rb +25 -12
  77. data/lib/discorb/exe/show.rb +4 -3
  78. data/lib/discorb/extend.rb +1 -0
  79. data/lib/discorb/extension.rb +6 -3
  80. data/lib/discorb/flag.rb +11 -0
  81. data/lib/discorb/gateway.rb +67 -19
  82. data/lib/discorb/guild.rb +188 -56
  83. data/lib/discorb/guild_template.rb +10 -4
  84. data/lib/discorb/http.rb +16 -9
  85. data/lib/discorb/integration.rb +4 -1
  86. data/lib/discorb/intents.rb +1 -1
  87. data/lib/discorb/interaction/autocomplete.rb +28 -16
  88. data/lib/discorb/interaction/command.rb +36 -12
  89. data/lib/discorb/interaction/components.rb +5 -2
  90. data/lib/discorb/interaction/modal.rb +0 -1
  91. data/lib/discorb/interaction/response.rb +61 -22
  92. data/lib/discorb/interaction/root.rb +13 -13
  93. data/lib/discorb/interaction.rb +1 -0
  94. data/lib/discorb/invite.rb +5 -2
  95. data/lib/discorb/member.rb +25 -5
  96. data/lib/discorb/message.rb +47 -14
  97. data/lib/discorb/message_meta.rb +1 -0
  98. data/lib/discorb/modules.rb +56 -14
  99. data/lib/discorb/presence.rb +2 -2
  100. data/lib/discorb/rate_limit.rb +7 -2
  101. data/lib/discorb/reaction.rb +4 -4
  102. data/lib/discorb/role.rb +19 -4
  103. data/lib/discorb/shard.rb +1 -1
  104. data/lib/discorb/sticker.rb +8 -7
  105. data/lib/discorb/user.rb +2 -1
  106. data/lib/discorb/utils/colored_puts.rb +1 -0
  107. data/lib/discorb/voice_state.rb +10 -6
  108. data/lib/discorb/webhook.rb +36 -24
  109. data/lib/discorb.rb +5 -3
  110. data/po/yard.pot +9 -9
  111. data/sig/discorb.rbs +7232 -7235
  112. metadata +21 -5
  113. data/examples/commands/bookmarker.rb +0 -42
  114. data/examples/commands/hello.rb +0 -10
  115. data/examples/commands/inspect.rb +0 -25
@@ -0,0 +1,345 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Represents a text channel.
6
+ #
7
+ class TextChannel < GuildChannel
8
+ # @return [String] The topic of the channel.
9
+ attr_reader :topic
10
+ # @return [Boolean] Whether the channel is nsfw.
11
+ attr_reader :nsfw
12
+ # @return [Discorb::Snowflake] The id of the last message.
13
+ attr_reader :last_message_id
14
+ # @return [Integer] The rate limit per user (Slowmode) in the channel.
15
+ attr_reader :rate_limit_per_user
16
+ alias slowmode rate_limit_per_user
17
+ # @return [Time] The time when the last pinned message was pinned.
18
+ attr_reader :last_pin_timestamp
19
+ alias last_pinned_at last_pin_timestamp
20
+ # @return [Integer] The default value of duration of auto archive.
21
+ attr_reader :default_auto_archive_duration
22
+
23
+ include Messageable
24
+
25
+ # @return [{Integer => Symbol}] The auto archive duration map.
26
+ # @private
27
+ DEFAULT_AUTO_ARCHIVE_DURATION = {
28
+ 60 => :hour,
29
+ 1440 => :day,
30
+ 4320 => :three_days,
31
+ 10080 => :week,
32
+ }.freeze
33
+
34
+ @channel_type = 0
35
+
36
+ # @!attribute [r] threads
37
+ # @return [Array<Discorb::ThreadChannel>] The threads in the channel.
38
+ def threads
39
+ guild.threads.select { |thread| thread.parent == self }
40
+ end
41
+
42
+ #
43
+ # Edits the channel.
44
+ # @async
45
+ # @macro edit
46
+ #
47
+ # @param [String] name The name of the channel.
48
+ # @param [Integer] position The position of the channel.
49
+ # @param [Discorb::CategoryChannel, nil] category The parent of channel. Specify `nil` to remove the parent.
50
+ # @param [Discorb::CategoryChannel, nil] parent Alias of `category`.
51
+ # @param [String] topic The topic of the channel.
52
+ # @param [Boolean] nsfw Whether the channel is nsfw.
53
+ # @param [Boolean] announce Whether the channel is announce channel.
54
+ # @param [Integer] rate_limit_per_user The rate limit per user (Slowmode) in the channel.
55
+ # @param [Integer] slowmode Alias of `rate_limit_per_user`.
56
+ # @param [Integer] default_auto_archive_duration The default auto archive duration of the channel.
57
+ # @param [Integer] archive_in Alias of `default_auto_archive_duration`.
58
+ # @param [String] reason The reason of editing the channel.
59
+ #
60
+ # @return [Async::Task<self>] The edited channel.
61
+ #
62
+ def edit(
63
+ name: Discorb::Unset,
64
+ position: Discorb::Unset,
65
+ category: Discorb::Unset,
66
+ parent: Discorb::Unset,
67
+ topic: Discorb::Unset,
68
+ nsfw: Discorb::Unset,
69
+ announce: Discorb::Unset,
70
+ rate_limit_per_user: Discorb::Unset,
71
+ slowmode: Discorb::Unset,
72
+ default_auto_archive_duration: Discorb::Unset,
73
+ archive_in: Discorb::Unset,
74
+ reason: nil
75
+ )
76
+ Async do
77
+ payload = {}
78
+ payload[:name] = name if name != Discorb::Unset
79
+ payload[:announce] = announce ? 5 : 0 if announce != Discorb::Unset
80
+ payload[:position] = position if position != Discorb::Unset
81
+ payload[:topic] = topic || "" if topic != Discorb::Unset
82
+ payload[:nsfw] = nsfw if nsfw != Discorb::Unset
83
+
84
+ slowmode = rate_limit_per_user if slowmode == Discorb::Unset
85
+ payload[:rate_limit_per_user] = slowmode || 0 if slowmode != Discorb::Unset
86
+ parent = category if parent == Discorb::Unset
87
+ payload[:parent_id] = parent&.id if parent != Discorb::Unset
88
+
89
+ default_auto_archive_duration ||= archive_in
90
+ if default_auto_archive_duration != Discorb::Unset
91
+ payload[:default_auto_archive_duration] =
92
+ default_auto_archive_duration
93
+ end
94
+
95
+ @client.http.request(Route.new("/channels/#{@id}", "//channels/:channel_id", :patch), payload,
96
+ audit_log_reason: reason).wait
97
+ self
98
+ end
99
+ end
100
+
101
+ alias modify edit
102
+
103
+ #
104
+ # Create webhook in the channel.
105
+ # @async
106
+ #
107
+ # @param [String] name The name of the webhook.
108
+ # @param [Discorb::Image] avatar The avatar of the webhook.
109
+ #
110
+ # @return [Async::Task<Discorb::Webhook::IncomingWebhook>] The created webhook.
111
+ #
112
+ def create_webhook(name, avatar: nil)
113
+ Async do
114
+ payload = {}
115
+ payload[:name] = name
116
+ payload[:avatar] = avatar.to_s if avatar
117
+ _resp, data = @client.http.request(
118
+ Route.new("/channels/#{@id}/webhooks", "//channels/:channel_id/webhooks", :post), payload
119
+ ).wait
120
+ Webhook.from_data(@client, data)
121
+ end
122
+ end
123
+
124
+ #
125
+ # Fetch webhooks in the channel.
126
+ # @async
127
+ #
128
+ # @return [Async::Task<Array<Discorb::Webhook>>] The webhooks in the channel.
129
+ #
130
+ def fetch_webhooks
131
+ Async do
132
+ _resp, data = @client.http.request(Route.new("/channels/#{@id}/webhooks", "//channels/:channel_id/webhooks",
133
+ :get)).wait
134
+ data.map { |webhook| Webhook.new([@client, webhook]) }
135
+ end
136
+ end
137
+
138
+ #
139
+ # Bulk delete messages in the channel.
140
+ # @async
141
+ #
142
+ # @param [Discorb::Message] messages The messages to delete.
143
+ # @param [Boolean] force Whether to ignore the validation for message (14 days limit).
144
+ #
145
+ # @return [Async::Task<void>] The task.
146
+ #
147
+ def delete_messages!(*messages, force: false)
148
+ Async do
149
+ messages = messages.first if messages.length == 1 && messages.first.is_a?(Array)
150
+ unless force
151
+ time = Time.now
152
+ messages.delete_if do |message|
153
+ next false unless message.is_a?(Message)
154
+
155
+ time - message.created_at > 60 * 60 * 24 * 14
156
+ end
157
+ end
158
+
159
+ message_ids = messages.map { |m| Discorb::Utils.try(m, :id).to_s }
160
+
161
+ @client.http.request(
162
+ Route.new("/channels/#{@id}/messages/bulk-delete", "//channels/:channel_id/messages/bulk-delete",
163
+ :post), { messages: message_ids }
164
+ ).wait
165
+ end
166
+ end
167
+
168
+ alias bulk_delete! delete_messages!
169
+ alias destroy_messages! delete_messages!
170
+
171
+ #
172
+ # Follow the existing announcement channel.
173
+ # @async
174
+ #
175
+ # @param [Discorb::NewsChannel] target The channel to follow.
176
+ # @param [String] reason The reason of following the channel.
177
+ #
178
+ # @return [Async::Task<void>] The task.
179
+ #
180
+ def follow_from(target, reason: nil)
181
+ Async do
182
+ @client.http.request(Route.new("/channels/#{target.id}/followers", "//channels/:channel_id/followers", :post),
183
+ { webhook_channel_id: @id }, audit_log_reason: reason).wait
184
+ end
185
+ end
186
+
187
+ #
188
+ # Start thread in the channel.
189
+ # @async
190
+ #
191
+ # @param [String] name The name of the thread.
192
+ # @param [Discorb::Message] message The message to start the thread.
193
+ # @param [:hour, :day, :three_days, :week] auto_archive_duration The duration of auto-archiving.
194
+ # @param [Boolean] public Whether the thread is public.
195
+ # @param [Integer] rate_limit_per_user The rate limit per user.
196
+ # @param [Integer] slowmode Alias of `rate_limit_per_user`.
197
+ # @param [String] reason The reason of starting the thread.
198
+ #
199
+ # @return [Async::Task<Discorb::ThreadChannel>] The started thread.
200
+ #
201
+ def start_thread(
202
+ name,
203
+ message: nil,
204
+ auto_archive_duration: nil,
205
+ public: true,
206
+ rate_limit_per_user: nil,
207
+ slowmode: nil,
208
+ reason: nil
209
+ )
210
+ auto_archive_duration ||= @default_auto_archive_duration
211
+ auto_archive_duration_value = DEFAULT_AUTO_ARCHIVE_DURATION.key(auto_archive_duration)
212
+ Async do
213
+ _resp, data = if message.nil?
214
+ @client.http.request(
215
+ Route.new("/channels/#{@id}/threads", "//channels/:channel_id/threads", :post),
216
+ {
217
+ name: name,
218
+ auto_archive_duration: auto_archive_duration_value,
219
+ type: public ? 11 : 10,
220
+ rate_limit_per_user: rate_limit_per_user || slowmode,
221
+ },
222
+ audit_log_reason: reason,
223
+ ).wait
224
+ else
225
+ @client.http.request(
226
+ Route.new("/channels/#{@id}/messages/#{Utils.try(message, :id)}/threads",
227
+ "//channels/:channel_id/messages/:message_id/threads", :post),
228
+ {
229
+ name: name,
230
+ auto_archive_duration: auto_archive_duration_value,
231
+ },
232
+ audit_log_reason: reason,
233
+ ).wait
234
+ end
235
+ Channel.make_channel(@client, data)
236
+ end
237
+ end
238
+
239
+ alias create_thread start_thread
240
+
241
+ #
242
+ # Fetch archived threads in the channel.
243
+ # @async
244
+ #
245
+ # @return [Async::Task<Array<Discorb::ThreadChannel>>] The archived threads in the channel.
246
+ #
247
+ def fetch_archived_public_threads
248
+ Async do
249
+ _resp, data = @client.http.request(Route.new("/channels/#{@id}/threads/archived/public",
250
+ "//channels/:channel_id/threads/archived/public", :get)).wait
251
+ data.map { |thread| Channel.make_channel(@client, thread) }
252
+ end
253
+ end
254
+
255
+ #
256
+ # Fetch archived private threads in the channel.
257
+ # @async
258
+ #
259
+ # @return [Async::Task<Array<Discorb::ThreadChannel>>] The archived private threads in the channel.
260
+ #
261
+ def fetch_archived_private_threads
262
+ Async do
263
+ _resp, data = @client.http.request(Route.new("/channels/#{@id}/threads/archived/private",
264
+ "//channels/:channel_id/threads/archived/private", :get)).wait
265
+ data.map { |thread| Channel.make_channel(@client, thread) }
266
+ end
267
+ end
268
+
269
+ #
270
+ # Fetch joined archived private threads in the channel.
271
+ # @async
272
+ #
273
+ # @param [Integer] limit The limit of threads to fetch.
274
+ # @param [Time] before <description>
275
+ #
276
+ # @return [Async::Task<Array<Discorb::ThreadChannel>>] The joined archived private threads in the channel.
277
+ #
278
+ def fetch_joined_archived_private_threads(limit: nil, before: nil)
279
+ Async do
280
+ if limit.nil?
281
+ before = 0
282
+ threads = []
283
+ loop do
284
+ _resp, data = @client.http.request(
285
+ Route.new(
286
+ "/channels/#{@id}/users/@me/threads/archived/private?before=#{before}",
287
+ "//channels/:channel_id/users/@me/threads/archived/private",
288
+ :get
289
+ )
290
+ ).wait
291
+ threads += data[:threads].map { |thread| Channel.make_channel(@client, thread) }
292
+ before = data[:threads][-1][:id]
293
+
294
+ break unless data[:has_more]
295
+ end
296
+ threads
297
+ else
298
+ _resp, data = @client.http.request(
299
+ Route.new(
300
+ "/channels/#{@id}/users/@me/threads/archived/private?limit=#{limit}&before=#{before}",
301
+ "//channels/:channel_id/users/@me/threads/archived/private",
302
+ :get
303
+ )
304
+ ).wait
305
+ data.map { |thread| Channel.make_channel(@client, thread) }
306
+ end
307
+ end
308
+ end
309
+
310
+ private
311
+
312
+ def _set_data(data)
313
+ @topic = data[:topic]
314
+ @nsfw = data[:nsfw]
315
+ @last_message_id = data[:last_message_id]
316
+ @rate_limit_per_user = data[:rate_limit_per_user]
317
+ @last_pin_timestamp = data[:last_pin_timestamp] && Time.iso8601(data[:last_pin_timestamp])
318
+ @default_auto_archive_duration = DEFAULT_AUTO_ARCHIVE_DURATION[data[:default_auto_archive_duration]]
319
+ super
320
+ end
321
+ end
322
+
323
+ #
324
+ # Represents a news channel (announcement channel).
325
+ #
326
+ class NewsChannel < TextChannel
327
+ @channel_type = 5
328
+
329
+ #
330
+ # Follow the existing announcement channel from self.
331
+ # @async
332
+ #
333
+ # @param [Discorb::TextChannel] target The channel to follow to.
334
+ # @param [String] reason The reason of following the channel.
335
+ #
336
+ # @return [Async::Task<void>] The task.
337
+ #
338
+ def follow_to(target, reason: nil)
339
+ Async do
340
+ @client.http.request(Route.new("/channels/#{@id}/followers", "//channels/:channel_id/followers", :post),
341
+ { webhook_channel_id: target.id }, audit_log_reason: reason).wait
342
+ end
343
+ end
344
+ end
345
+ end
@@ -0,0 +1,321 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discorb
4
+ #
5
+ # Represents a thread.
6
+ # @abstract
7
+ #
8
+ class ThreadChannel < Channel
9
+ # @return [Discorb::Snowflake] The ID of the channel.
10
+ # @note This ID is same as the starter message's ID
11
+ attr_reader :id
12
+ # @return [String] The name of the thread.
13
+ attr_reader :name
14
+ # @return [Integer] The number of messages in the thread.
15
+ # @note This will stop counting at 50.
16
+ attr_reader :message_count
17
+ # @return [Integer] The number of recipients in the thread.
18
+ # @note This will stop counting at 50.
19
+ attr_reader :member_count
20
+ alias recipient_count member_count
21
+ # @return [Integer] The rate limit per user (slowmode) in the thread.
22
+ attr_reader :rate_limit_per_user
23
+ alias slowmode rate_limit_per_user
24
+ # @return [Array<Discorb::ThreadChannel::Member>] The members of the thread.
25
+ attr_reader :members
26
+ # @return [Time] The time the thread was archived.
27
+ # @return [nil] If the thread is not archived.
28
+ attr_reader :archived_timestamp
29
+ alias archived_at archived_timestamp
30
+ # @return [Integer] Auto archive duration in seconds.
31
+ attr_reader :auto_archive_duration
32
+ alias archive_in auto_archive_duration
33
+ # @return [Boolean] Whether the thread is archived or not.
34
+ attr_reader :archived
35
+ alias archived? archived
36
+
37
+ # @!attribute [r] parent
38
+ # @macro client_cache
39
+ # @return [Discorb::GuildChannel] The parent channel of the thread.
40
+ # @!attribute [r] me
41
+ # @return [Discorb::ThreadChannel::Member] The bot's member in the thread.
42
+ # @return [nil] If the bot is not in the thread.
43
+ # @!attribute [r] joined?
44
+ # @return [Boolean] Whether the bot is in the thread or not.
45
+ # @!attribute [r] guild
46
+ # @macro client_cache
47
+ # @return [Discorb::Guild] The guild of the thread.
48
+ # @!attribute [r] owner
49
+ # @macro client_cache
50
+ # @macro members_intent
51
+ # @return [Discorb::Member] The owner of the thread.
52
+
53
+ include Messageable
54
+ @channel_type = nil
55
+
56
+ #
57
+ # Initialize a new thread channel.
58
+ # @private
59
+ #
60
+ # @param [Discorb::Client] client The client.
61
+ # @param [Hash] data The data of the thread channel.
62
+ # @param [Boolean] no_cache Whether to disable the cache.
63
+ #
64
+ def initialize(client, data, no_cache: false)
65
+ @members = Dictionary.new
66
+ super
67
+ @client.channels[@id] = self unless no_cache
68
+ end
69
+
70
+ #
71
+ # Edit the thread.
72
+ # @async
73
+ # @macro edit
74
+ #
75
+ # @param [String] name The name of the thread.
76
+ # @param [Boolean] archived Whether the thread is archived or not.
77
+ # @param [Integer] auto_archive_duration The auto archive duration in seconds.
78
+ # @param [Integer] archive_in Alias of `auto_archive_duration`.
79
+ # @param [Boolean] locked Whether the thread is locked or not.
80
+ # @param [String] reason The reason of editing the thread.
81
+ #
82
+ # @return [Async::Task<self>] The edited thread.
83
+ #
84
+ # @see #archive
85
+ # @see #lock
86
+ # @see #unarchive
87
+ # @see #unlock
88
+ #
89
+ def edit(
90
+ name: Discorb::Unset,
91
+ archived: Discorb::Unset,
92
+ auto_archive_duration: Discorb::Unset,
93
+ archive_in: Discorb::Unset,
94
+ locked: Discorb::Unset,
95
+ reason: nil
96
+ )
97
+ Async do
98
+ payload = {}
99
+ payload[:name] = name if name != Discorb::Unset
100
+ payload[:archived] = archived if archived != Discorb::Unset
101
+ auto_archive_duration ||= archive_in
102
+ payload[:auto_archive_duration] = auto_archive_duration if auto_archive_duration != Discorb::Unset
103
+ payload[:locked] = locked if locked != Discorb::Unset
104
+ @client.http.request(Route.new("/channels/#{@id}", "//channels/:channel_id", :patch), payload,
105
+ audit_log_reason: reason).wait
106
+ self
107
+ end
108
+ end
109
+
110
+ #
111
+ # Helper method to archive the thread.
112
+ #
113
+ # @param [String] reason The reason of archiving the thread.
114
+ #
115
+ # @return [self] The archived thread.
116
+ #
117
+ def archive(reason: nil)
118
+ edit(archived: true, reason: reason)
119
+ end
120
+
121
+ #
122
+ # Helper method to lock the thread.
123
+ #
124
+ # @param [String] reason The reason of locking the thread.
125
+ #
126
+ # @return [self] The locked thread.
127
+ #
128
+ def lock(reason: nil)
129
+ edit(archived: true, locked: true, reason: reason)
130
+ end
131
+
132
+ #
133
+ # Helper method to unarchive the thread.
134
+ #
135
+ # @param [String] reason The reason of unarchiving the thread.
136
+ #
137
+ # @return [self] The unarchived thread.
138
+ #
139
+ def unarchive(reason: nil)
140
+ edit(archived: false, reason: reason)
141
+ end
142
+
143
+ #
144
+ # Helper method to unlock the thread.
145
+ #
146
+ # @param [String] reason The reason of unlocking the thread.
147
+ #
148
+ # @return [self] The unlocked thread.
149
+ #
150
+ # @note This method won't unarchive the thread. Use {#unarchive} instead.
151
+ #
152
+ def unlock(reason: nil)
153
+ edit(archived: !unarchive, locked: false, reason: reason)
154
+ end
155
+
156
+ def parent
157
+ return nil unless @parent_id
158
+
159
+ @client.channels[@parent_id]
160
+ end
161
+
162
+ alias channel parent
163
+
164
+ def me
165
+ @members[@client.user.id]
166
+ end
167
+
168
+ def joined?
169
+ !!me
170
+ end
171
+
172
+ def guild
173
+ @client.guilds[@guild]
174
+ end
175
+
176
+ def owner
177
+ guild.members[@owner_id]
178
+ end
179
+
180
+ def inspect
181
+ "#<#{self.class} \"##{@name}\" id=#{@id}>"
182
+ end
183
+
184
+ #
185
+ # Add a member to the thread.
186
+ #
187
+ # @param [Discorb::Member, :me] member The member to add. If `:me` is given, the bot will be added.
188
+ #
189
+ # @return [Async::Task<void>] The task.
190
+ #
191
+ def add_member(member = :me)
192
+ Async do
193
+ if member == :me
194
+ @client.http.request(Route.new("/channels/#{@id}/thread-members/@me",
195
+ "//channels/:channel_id/thread-members/@me", :post)).wait
196
+ else
197
+ @client.http.request(Route.new("/channels/#{@id}/thread-members/#{Utils.try(member, :id)}",
198
+ "//channels/:channel_id/thread-members/:user_id", :post)).wait
199
+ end
200
+ end
201
+ end
202
+
203
+ alias join add_member
204
+
205
+ #
206
+ # Remove a member from the thread.
207
+ #
208
+ # @param [Discorb::Member, :me] member The member to remove. If `:me` is given, the bot will be removed.
209
+ #
210
+ # @return [Async::Task<void>] The task.
211
+ #
212
+ def remove_member(member = :me)
213
+ Async do
214
+ if member == :me
215
+ @client.http.request(Route.new("/channels/#{@id}/thread-members/@me",
216
+ "//channels/:channel_id/thread-members/@me", :delete)).wait
217
+ else
218
+ @client.http.request(Route.new("/channels/#{@id}/thread-members/#{Utils.try(member, :id)}",
219
+ "//channels/:channel_id/thread-members/:user_id", :delete)).wait
220
+ end
221
+ end
222
+ end
223
+
224
+ alias leave remove_member
225
+
226
+ #
227
+ # Fetch members in the thread.
228
+ #
229
+ # @return [Array<Discorb::ThreadChannel::Member>] The members in the thread.
230
+ #
231
+ def fetch_members
232
+ Async do
233
+ _resp, data = @client.http.request(Route.new("/channels/#{@id}/thread-members",
234
+ "//channels/:channel_id/thread-members", :get)).wait
235
+ data.map { |d| @members[d[:id]] = Member.new(@client, d) }
236
+ end
237
+ end
238
+
239
+ #
240
+ # Represents a thread in news channel(aka announcement channel).
241
+ #
242
+ class News < ThreadChannel
243
+ @channel_type = 10
244
+ end
245
+
246
+ #
247
+ # Represents a public thread in text channel.
248
+ #
249
+ class Public < ThreadChannel
250
+ @channel_type = 11
251
+ end
252
+
253
+ #
254
+ # Represents a private thread in text channel.
255
+ #
256
+ class Private < ThreadChannel
257
+ @channel_type = 12
258
+ end
259
+
260
+ class << self
261
+ attr_reader :channel_type
262
+ end
263
+
264
+ #
265
+ # Represents a member in a thread.
266
+ #
267
+ class Member < DiscordModel
268
+ attr_reader :joined_at
269
+
270
+ def initialize(cilent, data)
271
+ @cilent = cilent
272
+ @thread_id = data[:id]
273
+ @user_id = data[:user_id]
274
+ @joined_at = Time.iso8601(data[:join_timestamp])
275
+ end
276
+
277
+ def thread
278
+ @client.channels[@thread_id]
279
+ end
280
+
281
+ def member
282
+ thread && thread.members[@user_id]
283
+ end
284
+
285
+ def id
286
+ @user_id
287
+ end
288
+
289
+ def user
290
+ @cilent.users[@user_id]
291
+ end
292
+
293
+ def inspect
294
+ "#<#{self.class} id=#{@id.inspect}>"
295
+ end
296
+ end
297
+
298
+ private
299
+
300
+ def _set_data(data)
301
+ @id = Snowflake.new(data[:id])
302
+ @name = data[:name]
303
+ @guild_id = data[:guild_id]
304
+ @parent_id = data[:parent_id]
305
+ @archived = data[:thread_metadata][:archived]
306
+ @owner_id = data[:owner_id]
307
+ @archived_timestamp =
308
+ data[:thread_metadata][:archived_timestamp] && Time.iso8601(data[:thread_metadata][:archived_timestamp])
309
+ @auto_archive_duration = data[:thread_metadata][:auto_archive_duration]
310
+ @locked = data[:thread_metadata][:locked]
311
+ @member_count = data[:member_count]
312
+ @message_count = data[:message_count]
313
+ if data[:member]
314
+ @members[@client.user.id] =
315
+ ThreadChannel::Member.new(@client,
316
+ data[:member].merge({ id: data[:id], user_id: @client.user.id }))
317
+ end
318
+ @data.merge!(data)
319
+ end
320
+ end
321
+ end