discorb 0.15.0 → 0.17.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (120) 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 +49 -8
  13. data/Changelog.md +32 -1
  14. data/Gemfile +14 -8
  15. data/Rakefile +46 -25
  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 +257 -193
  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 +2 -2
  39. data/examples/simple/shard.rb +17 -0
  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 +8 -0
  44. data/lib/discorb/app_command/command.rb +184 -60
  45. data/lib/discorb/app_command/common.rb +25 -0
  46. data/lib/discorb/app_command/handler.rb +116 -34
  47. data/lib/discorb/app_command.rb +2 -1
  48. data/lib/discorb/application.rb +17 -7
  49. data/lib/discorb/asset.rb +10 -2
  50. data/lib/discorb/attachment.rb +17 -2
  51. data/lib/discorb/audit_logs.rb +53 -12
  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 -1126
  62. data/lib/discorb/client.rb +160 -64
  63. data/lib/discorb/common.rb +18 -3
  64. data/lib/discorb/components/button.rb +7 -7
  65. data/lib/discorb/components/select_menu.rb +6 -18
  66. data/lib/discorb/components/text_input.rb +12 -2
  67. data/lib/discorb/components.rb +1 -1
  68. data/lib/discorb/dictionary.rb +2 -0
  69. data/lib/discorb/embed.rb +55 -14
  70. data/lib/discorb/emoji.rb +59 -5
  71. data/lib/discorb/emoji_table.rb +4970 -4
  72. data/lib/discorb/error.rb +7 -1
  73. data/lib/discorb/event.rb +56 -21
  74. data/lib/discorb/exe/about.rb +1 -0
  75. data/lib/discorb/exe/irb.rb +2 -4
  76. data/lib/discorb/exe/new.rb +95 -28
  77. data/lib/discorb/exe/run.rb +9 -37
  78. data/lib/discorb/exe/setup.rb +25 -12
  79. data/lib/discorb/exe/show.rb +4 -3
  80. data/lib/discorb/extend.rb +1 -0
  81. data/lib/discorb/extension.rb +6 -3
  82. data/lib/discorb/flag.rb +11 -0
  83. data/lib/discorb/gateway.rb +312 -169
  84. data/lib/discorb/gateway_requests.rb +4 -7
  85. data/lib/discorb/guild.rb +255 -89
  86. data/lib/discorb/guild_template.rb +34 -7
  87. data/lib/discorb/http.rb +23 -11
  88. data/lib/discorb/integration.rb +27 -9
  89. data/lib/discorb/intents.rb +8 -8
  90. data/lib/discorb/interaction/autocomplete.rb +31 -19
  91. data/lib/discorb/interaction/command.rb +70 -17
  92. data/lib/discorb/interaction/components.rb +20 -4
  93. data/lib/discorb/interaction/modal.rb +0 -1
  94. data/lib/discorb/interaction/response.rb +73 -22
  95. data/lib/discorb/interaction/root.rb +29 -14
  96. data/lib/discorb/interaction.rb +1 -0
  97. data/lib/discorb/invite.rb +16 -9
  98. data/lib/discorb/member.rb +46 -5
  99. data/lib/discorb/message.rb +56 -15
  100. data/lib/discorb/message_meta.rb +39 -9
  101. data/lib/discorb/modules.rb +56 -14
  102. data/lib/discorb/permission.rb +14 -5
  103. data/lib/discorb/presence.rb +43 -10
  104. data/lib/discorb/rate_limit.rb +13 -3
  105. data/lib/discorb/reaction.rb +10 -4
  106. data/lib/discorb/role.rb +31 -4
  107. data/lib/discorb/shard.rb +74 -0
  108. data/lib/discorb/sticker.rb +30 -21
  109. data/lib/discorb/user.rb +13 -1
  110. data/lib/discorb/utils/colored_puts.rb +1 -0
  111. data/lib/discorb/voice_state.rb +30 -8
  112. data/lib/discorb/webhook.rb +88 -25
  113. data/lib/discorb.rb +10 -6
  114. data/po/yard.pot +9 -9
  115. data/sig/discorb.rbs +7232 -5837
  116. metadata +23 -6
  117. data/examples/commands/bookmarker.rb +0 -42
  118. data/examples/commands/hello.rb +0 -10
  119. data/examples/commands/inspect.rb +0 -25
  120. data/lib/discorb/log.rb +0 -81
@@ -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