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