discorb 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/.gitignore +56 -0
- data/.yardopts +6 -0
- data/Changelog.md +5 -0
- data/Gemfile +23 -0
- data/Gemfile.lock +70 -0
- data/LICENSE.txt +21 -0
- data/README.md +53 -0
- data/Rakefile +46 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/discorb.gemspec +37 -0
- data/docs/Examples.md +26 -0
- data/docs/events.md +480 -0
- data/docs/voice_events.md +283 -0
- data/examples/components/authorization_button.rb +43 -0
- data/examples/components/select_menu.rb +61 -0
- data/examples/extension/main.rb +12 -0
- data/examples/extension/message_expander.rb +41 -0
- data/examples/simple/eval.rb +32 -0
- data/examples/simple/ping_pong.rb +16 -0
- data/examples/simple/rolepanel.rb +65 -0
- data/examples/simple/wait_for_message.rb +30 -0
- data/lib/discorb/application.rb +157 -0
- data/lib/discorb/asset.rb +57 -0
- data/lib/discorb/audit_logs.rb +323 -0
- data/lib/discorb/channel.rb +1101 -0
- data/lib/discorb/client.rb +363 -0
- data/lib/discorb/color.rb +173 -0
- data/lib/discorb/common.rb +123 -0
- data/lib/discorb/components.rb +290 -0
- data/lib/discorb/dictionary.rb +119 -0
- data/lib/discorb/embed.rb +345 -0
- data/lib/discorb/emoji.rb +218 -0
- data/lib/discorb/emoji_table.rb +3799 -0
- data/lib/discorb/error.rb +98 -0
- data/lib/discorb/event.rb +35 -0
- data/lib/discorb/extend.rb +18 -0
- data/lib/discorb/extension.rb +54 -0
- data/lib/discorb/file.rb +69 -0
- data/lib/discorb/flag.rb +109 -0
- data/lib/discorb/gateway.rb +967 -0
- data/lib/discorb/gateway_requests.rb +47 -0
- data/lib/discorb/guild.rb +1244 -0
- data/lib/discorb/guild_template.rb +211 -0
- data/lib/discorb/image.rb +43 -0
- data/lib/discorb/integration.rb +111 -0
- data/lib/discorb/intents.rb +137 -0
- data/lib/discorb/interaction.rb +333 -0
- data/lib/discorb/internet.rb +285 -0
- data/lib/discorb/invite.rb +145 -0
- data/lib/discorb/log.rb +70 -0
- data/lib/discorb/member.rb +232 -0
- data/lib/discorb/message.rb +583 -0
- data/lib/discorb/modules.rb +138 -0
- data/lib/discorb/permission.rb +270 -0
- data/lib/discorb/presence.rb +308 -0
- data/lib/discorb/reaction.rb +48 -0
- data/lib/discorb/role.rb +189 -0
- data/lib/discorb/sticker.rb +157 -0
- data/lib/discorb/user.rb +163 -0
- data/lib/discorb/utils.rb +16 -0
- data/lib/discorb/voice_state.rb +251 -0
- data/lib/discorb/webhook.rb +420 -0
- data/lib/discorb.rb +51 -0
- metadata +120 -0
@@ -0,0 +1,333 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Discorb
|
4
|
+
#
|
5
|
+
# Represents a user interaction with the bot.
|
6
|
+
#
|
7
|
+
class Interaction < DiscordModel
|
8
|
+
# @return [Discorb::Snowflake] The ID of the interaction.
|
9
|
+
attr_reader :id
|
10
|
+
# @return [Discorb::Snowflake] The ID of the application that created the interaction.
|
11
|
+
attr_reader :application_id
|
12
|
+
# @return [Symbol] The type of interaction.
|
13
|
+
attr_reader :type
|
14
|
+
# @return [Discorb::Member] The member that created the interaction.
|
15
|
+
attr_reader :member
|
16
|
+
# @return [Discorb::User] The user that created the interaction.
|
17
|
+
attr_reader :user
|
18
|
+
# @return [Integer] The type of interaction.
|
19
|
+
# @note This is always `1` for now.
|
20
|
+
attr_reader :version
|
21
|
+
# @return [String] The token for the interaction.
|
22
|
+
attr_reader :token
|
23
|
+
|
24
|
+
# @!attribute [r] guild
|
25
|
+
# @macro client_cache
|
26
|
+
# @return [Discorb::Guild] The guild the interaction took place in.
|
27
|
+
# @!attribute [r] channel
|
28
|
+
# @macro client_cache
|
29
|
+
# @return [Discorb::Channel] The channel the interaction took place in.
|
30
|
+
# @!attribute [r] target
|
31
|
+
# @return [Discorb::User, Discorb::Member] The user or member the interaction took place with.
|
32
|
+
|
33
|
+
@interaction_type = nil
|
34
|
+
@interaction_name = nil
|
35
|
+
|
36
|
+
# @!visibility private
|
37
|
+
def initialize(client, data)
|
38
|
+
@client = client
|
39
|
+
@id = Snowflake.new(data[:id])
|
40
|
+
@application_id = Snowflake.new(data[:application_id])
|
41
|
+
@type = self.class.interaction_name
|
42
|
+
@type_id = self.class.interaction_type
|
43
|
+
@guild_id = data[:guild_id] && Snowflake.new(data[:guild_id])
|
44
|
+
@channel_id = data[:channel_id] && Snowflake.new(data[:channel_id])
|
45
|
+
@member = guild.members[data[:member][:id]] || Member.new(@client, @guild_id, data[:member][:user], data[:member]) if data[:member]
|
46
|
+
@user = @client.users[data[:user][:id]] || User.new(@client, data[:user]) if data[:user]
|
47
|
+
@token = data[:token]
|
48
|
+
@version = data[:version]
|
49
|
+
@defered = false
|
50
|
+
@responded = false
|
51
|
+
_set_data(data[:data])
|
52
|
+
end
|
53
|
+
|
54
|
+
def guild
|
55
|
+
@client.guilds[@guild_id]
|
56
|
+
end
|
57
|
+
|
58
|
+
def channel
|
59
|
+
@client.channels[@channel_id]
|
60
|
+
end
|
61
|
+
|
62
|
+
def target
|
63
|
+
@member || @user
|
64
|
+
end
|
65
|
+
|
66
|
+
alias fired_by target
|
67
|
+
|
68
|
+
def inspect
|
69
|
+
"#<#{self.class} id=#{@id}>"
|
70
|
+
end
|
71
|
+
|
72
|
+
class << self
|
73
|
+
# @!visibility private
|
74
|
+
attr_reader :interaction_type, :interaction_name, :event_name
|
75
|
+
|
76
|
+
# @!visibility private
|
77
|
+
def make_interaction(client, data)
|
78
|
+
descendants.each do |klass|
|
79
|
+
return klass.make_interaction(client, data) if !klass.interaction_type.nil? && klass.interaction_type == data[:type]
|
80
|
+
end
|
81
|
+
client.log.warn("Unknown interaction type #{data[:type]}, initialized Interaction")
|
82
|
+
Interaction.new(client, data)
|
83
|
+
end
|
84
|
+
|
85
|
+
# @!visibility private
|
86
|
+
def descendants
|
87
|
+
ObjectSpace.each_object(Class).select { |klass| klass < self }
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
#
|
92
|
+
# A module for response with source.
|
93
|
+
#
|
94
|
+
module SourceResponse
|
95
|
+
#
|
96
|
+
# Response with `DEFERRED_CHANNEL_MESSAGE_WITH_SOURCE`(`5`).
|
97
|
+
#
|
98
|
+
# @param [Boolean] hide Whether to hide the response (ephemeral).
|
99
|
+
#
|
100
|
+
def defer_source(hide: false)
|
101
|
+
Async do
|
102
|
+
@client.internet.post("/interactions/#{@id}/#{@token}/callback", {
|
103
|
+
type: 5,
|
104
|
+
data: {
|
105
|
+
flags: (hide ? 1 << 6 : 0),
|
106
|
+
},
|
107
|
+
}).wait
|
108
|
+
@defered = true
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
#
|
113
|
+
# Response with `CHANNEL_MESSAGE_WITH_SOURCE`(`4`).
|
114
|
+
#
|
115
|
+
# @param [String] content The content of the response.
|
116
|
+
# @param [Boolean] tts Whether to send the message as text-to-speech.
|
117
|
+
# @param [Discorb::Embed] embed The embed to send.
|
118
|
+
# @param [Array<Discorb::Embed>] embeds The embeds to send. (max: 10)
|
119
|
+
# @param [Discorb::AllowedMentions] allowed_mentions The allowed mentions to send.
|
120
|
+
# @param [Array<Discorb::Components>, Array<Array<Discorb::Components>>] components The components to send.
|
121
|
+
# @param [Boolean] hide Whether to hide the response (ephemeral).
|
122
|
+
#
|
123
|
+
def post(content, tts: false, embed: nil, embeds: nil, allowed_mentions: nil, components: nil, hide: false)
|
124
|
+
payload = {}
|
125
|
+
payload[:content] = content if content
|
126
|
+
payload[:tts] = tts
|
127
|
+
tmp_embed = if embed
|
128
|
+
[embed]
|
129
|
+
elsif embeds
|
130
|
+
embeds
|
131
|
+
end
|
132
|
+
payload[:embeds] = tmp_embed.map(&:to_hash) if tmp_embed
|
133
|
+
payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
|
134
|
+
if components
|
135
|
+
tmp_components = []
|
136
|
+
tmp_row = []
|
137
|
+
components.each do |c|
|
138
|
+
case c
|
139
|
+
when Array
|
140
|
+
tmp_components << tmp_row
|
141
|
+
tmp_row = []
|
142
|
+
tmp_components << c
|
143
|
+
when SelectMenu
|
144
|
+
tmp_components << tmp_row
|
145
|
+
tmp_row = []
|
146
|
+
tmp_components << [c]
|
147
|
+
else
|
148
|
+
tmp_row << c
|
149
|
+
end
|
150
|
+
end
|
151
|
+
tmp_components << tmp_row
|
152
|
+
payload[:components] = tmp_components.filter { |c| c.length.positive? }.map { |c| { type: 1, components: c.map(&:to_hash) } }
|
153
|
+
end
|
154
|
+
payload[:flags] = (hide ? 1 << 6 : 0)
|
155
|
+
if @responded
|
156
|
+
@client.internet.post("/webhooks/#{@id}/#{@token}", { type: 4, data: payload }).wait
|
157
|
+
elsif @defered
|
158
|
+
@client.internet.post("/interactions/#{@id}/#{@token}/@original/edit", { type: 4, data: payload }).wait
|
159
|
+
else
|
160
|
+
@client.internet.post("/interactions/#{@id}/#{@token}/callback", { type: 4, data: payload }).wait
|
161
|
+
end
|
162
|
+
@responded = true
|
163
|
+
end
|
164
|
+
end
|
165
|
+
|
166
|
+
#
|
167
|
+
# A module for response with update.
|
168
|
+
#
|
169
|
+
module UpdateResponse
|
170
|
+
#
|
171
|
+
# Response with `DEFERRED_UPDATE_MESSAGE`(`6`).
|
172
|
+
#
|
173
|
+
# @param [Boolean] hide Whether to hide the response (ephemeral).
|
174
|
+
#
|
175
|
+
def defer_update(hide: false)
|
176
|
+
Async do
|
177
|
+
@client.internet.post("/interactions/#{@id}/#{@token}/callback", {
|
178
|
+
type: 7,
|
179
|
+
data: {
|
180
|
+
flags: (hide ? 1 << 6 : 0),
|
181
|
+
},
|
182
|
+
}).wait
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
#
|
187
|
+
# Response with `UPDATE_MESSAGE`(`7`).
|
188
|
+
#
|
189
|
+
# @param [String] content The content of the response.
|
190
|
+
# @param [Boolean] tts Whether to send the message as text-to-speech.
|
191
|
+
# @param [Discorb::Embed] embed The embed to send.
|
192
|
+
# @param [Array<Discorb::Embed>] embeds The embeds to send. (max: 10)
|
193
|
+
# @param [Discorb::AllowedMentions] allowed_mentions The allowed mentions to send.
|
194
|
+
# @param [Array<Discorb::Components>, Array<Array<Discorb::Components>>] components The components to send.
|
195
|
+
# @param [Boolean] hide Whether to hide the response (ephemeral).
|
196
|
+
#
|
197
|
+
def edit(content, tts: false, embed: nil, embeds: nil, allowed_mentions: nil, components: nil, hide: false)
|
198
|
+
payload = {}
|
199
|
+
payload[:content] = content if content
|
200
|
+
payload[:tts] = tts
|
201
|
+
tmp_embed = if embed
|
202
|
+
[embed]
|
203
|
+
elsif embeds
|
204
|
+
embeds
|
205
|
+
end
|
206
|
+
payload[:embeds] = tmp_embed.map(&:to_hash) if tmp_embed
|
207
|
+
payload[:allowed_mentions] = allowed_mentions ? allowed_mentions.to_hash(@client.allowed_mentions) : @client.allowed_mentions.to_hash
|
208
|
+
if components
|
209
|
+
tmp_components = []
|
210
|
+
tmp_row = []
|
211
|
+
components.each do |c|
|
212
|
+
case c
|
213
|
+
when Array
|
214
|
+
tmp_components << tmp_row
|
215
|
+
tmp_row = []
|
216
|
+
tmp_components << c
|
217
|
+
when SelectMenu
|
218
|
+
tmp_components << tmp_row
|
219
|
+
tmp_row = []
|
220
|
+
tmp_components << [c]
|
221
|
+
else
|
222
|
+
tmp_row << c
|
223
|
+
end
|
224
|
+
end
|
225
|
+
tmp_components << tmp_row
|
226
|
+
payload[:components] = tmp_components.filter { |c| c.length.positive? }.map { |c| { type: 1, components: c.map(&:to_hash) } }
|
227
|
+
end
|
228
|
+
payload[:flags] = (hide ? 1 << 6 : 0)
|
229
|
+
@client.internet.post("/interactions/#{@id}/#{@token}/callback", { type: 6, data: payload }).wait
|
230
|
+
end
|
231
|
+
end
|
232
|
+
|
233
|
+
private
|
234
|
+
|
235
|
+
def _set_data(*)
|
236
|
+
nil
|
237
|
+
end
|
238
|
+
end
|
239
|
+
|
240
|
+
#
|
241
|
+
# Represents a slash command interaction.
|
242
|
+
# @todo Implement this.
|
243
|
+
#
|
244
|
+
class SlashCommandInteraction < Interaction
|
245
|
+
@interaction_type = 2
|
246
|
+
@interaction_name = :slash_command
|
247
|
+
|
248
|
+
def _set_data(data)
|
249
|
+
p data
|
250
|
+
end
|
251
|
+
end
|
252
|
+
|
253
|
+
#
|
254
|
+
# Represents a message component interaction.
|
255
|
+
# @abstract
|
256
|
+
#
|
257
|
+
class MessageComponentInteraction < Interaction
|
258
|
+
include Interaction::SourceResponse
|
259
|
+
include Interaction::UpdateResponse
|
260
|
+
# @return [String] The content of the response.
|
261
|
+
attr_reader :custom_id
|
262
|
+
|
263
|
+
@interaction_type = 3
|
264
|
+
@interaction_name = :message_component
|
265
|
+
|
266
|
+
# @!visibility private
|
267
|
+
def initialize(client, data)
|
268
|
+
super
|
269
|
+
@message = Message.new(@client, data[:message].merge({ member: data[:member] }))
|
270
|
+
end
|
271
|
+
|
272
|
+
class << self
|
273
|
+
# @!visibility private
|
274
|
+
attr_reader :component_type
|
275
|
+
|
276
|
+
# @!visibility private
|
277
|
+
def make_interaction(client, data)
|
278
|
+
nested_classes.each do |klass|
|
279
|
+
return klass.new(client, data) if !klass.component_type.nil? && klass.component_type == data[:type]
|
280
|
+
end
|
281
|
+
client.log.warn("Unknown component type #{data[:component_type]}, initialized Interaction")
|
282
|
+
MessageComponentInteraction.new(client, data)
|
283
|
+
end
|
284
|
+
|
285
|
+
# @!visibility private
|
286
|
+
def nested_classes
|
287
|
+
constants.select { |c| const_get(c).is_a? Class }.map { |c| const_get(c) }
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
#
|
292
|
+
# Represents a button interaction.
|
293
|
+
#
|
294
|
+
class Button < MessageComponentInteraction
|
295
|
+
@component_type = 2
|
296
|
+
@event_name = :button_click
|
297
|
+
# @return [String] The custom id of the button.
|
298
|
+
attr_reader :custom_id
|
299
|
+
|
300
|
+
private
|
301
|
+
|
302
|
+
def _set_data(data)
|
303
|
+
@custom_id = data[:custom_id]
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
#
|
308
|
+
# Represents a select menu interaction.
|
309
|
+
#
|
310
|
+
class SelectMenu < MessageComponentInteraction
|
311
|
+
@component_type = 3
|
312
|
+
@event_name = :select_menu_select
|
313
|
+
# @return [String] The custom id of the select menu.
|
314
|
+
attr_reader :custom_id
|
315
|
+
# @return [Array<String>] The selected options.
|
316
|
+
attr_reader :values
|
317
|
+
|
318
|
+
# @!attribute [r] value
|
319
|
+
# @return [String] The first selected value.
|
320
|
+
|
321
|
+
def value
|
322
|
+
@values[0]
|
323
|
+
end
|
324
|
+
|
325
|
+
private
|
326
|
+
|
327
|
+
def _set_data(data)
|
328
|
+
@custom_id = data[:custom_id]
|
329
|
+
@values = data[:values]
|
330
|
+
end
|
331
|
+
end
|
332
|
+
end
|
333
|
+
end
|
@@ -0,0 +1,285 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/https"
|
4
|
+
|
5
|
+
module Discorb
|
6
|
+
#
|
7
|
+
# A class to handle internet requests.
|
8
|
+
# This class is internal use only.
|
9
|
+
#
|
10
|
+
class Internet
|
11
|
+
@nil_body = nil
|
12
|
+
|
13
|
+
# @!visibility private
|
14
|
+
def initialize(client)
|
15
|
+
@client = client
|
16
|
+
end
|
17
|
+
|
18
|
+
#
|
19
|
+
# Execute a GET request.
|
20
|
+
# @macro async
|
21
|
+
#
|
22
|
+
# @param [String] path The path to the resource.
|
23
|
+
# @param [Hash] headers The headers to send with the request.
|
24
|
+
# @param [String] audit_log_reason The audit log reason to send with the request.
|
25
|
+
# @param [Hash] kwargs The keyword arguments.
|
26
|
+
#
|
27
|
+
# @return [Array[Net::HTTPResponse, Hash]] The response and as JSON.
|
28
|
+
# @return [Array[Net::HTTPResponse, nil]] The response was 204.
|
29
|
+
#
|
30
|
+
# @raise [Discorb::HTTPError] The request was failed.
|
31
|
+
#
|
32
|
+
def get(path, headers: nil, audit_log_reason: nil, **kwargs)
|
33
|
+
Async do |task|
|
34
|
+
resp = http.get(get_path(path), get_headers(headers, "", audit_log_reason), **kwargs)
|
35
|
+
rd = resp.body
|
36
|
+
data = if rd.nil? || rd.empty?
|
37
|
+
nil
|
38
|
+
else
|
39
|
+
JSON.parse(rd, symbolize_names: true)
|
40
|
+
end
|
41
|
+
test_error(if resp.code == "429"
|
42
|
+
@client.log.warn "Ratelimit exceeded for #{path}, trying again in #{data[:retry_after]} seconds."
|
43
|
+
task.sleep(data[:retry_after])
|
44
|
+
get(path, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
|
45
|
+
else
|
46
|
+
[resp, data]
|
47
|
+
end)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Execute a POST request.
|
53
|
+
# @macro async
|
54
|
+
#
|
55
|
+
# @param [String] path The path to the resource.
|
56
|
+
# @param [String, Hash] body The body of the request.
|
57
|
+
# @param [Hash] headers The headers to send with the request.
|
58
|
+
# @param [String] audit_log_reason The audit log reason to send with the request.
|
59
|
+
# @param [Hash] kwargs The keyword arguments.
|
60
|
+
#
|
61
|
+
# @return [Array[Net::HTTPResponse, Hash]] The response and as JSON.
|
62
|
+
# @return [Array[Net::HTTPResponse, nil]] The response was 204.
|
63
|
+
#
|
64
|
+
# @raise [Discorb::HTTPError] The request was failed.
|
65
|
+
#
|
66
|
+
def post(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
|
67
|
+
Async do |task|
|
68
|
+
resp = http.post(get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
|
69
|
+
rd = resp.body
|
70
|
+
data = if rd.nil? || rd.empty?
|
71
|
+
nil
|
72
|
+
else
|
73
|
+
JSON.parse(rd, symbolize_names: true)
|
74
|
+
end
|
75
|
+
test_error(if resp.code == "429"
|
76
|
+
task.sleep(data[:retry_after])
|
77
|
+
post(path, body, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
|
78
|
+
else
|
79
|
+
[resp, data]
|
80
|
+
end)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
#
|
85
|
+
# Execute a PATCH request.
|
86
|
+
# @macro async
|
87
|
+
#
|
88
|
+
# @param [String] path The path to the resource.
|
89
|
+
# @param [String, Hash] body The body of the request.
|
90
|
+
# @param [Hash] headers The headers to send with the request.
|
91
|
+
# @param [String] audit_log_reason The audit log reason to send with the request.
|
92
|
+
# @param [Hash] kwargs The keyword arguments.
|
93
|
+
#
|
94
|
+
# @return [Array[Net::HTTPResponse, Hash]] The response and as JSON.
|
95
|
+
# @return [Array[Net::HTTPResponse, nil]] The response was 204.
|
96
|
+
#
|
97
|
+
# @raise [Discorb::HTTPError] The request was failed.
|
98
|
+
#
|
99
|
+
def patch(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
|
100
|
+
Async do |task|
|
101
|
+
resp = http.patch(get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
|
102
|
+
rd = resp.body
|
103
|
+
data = if rd.nil? || rd.empty?
|
104
|
+
nil
|
105
|
+
else
|
106
|
+
JSON.parse(rd, symbolize_names: true)
|
107
|
+
end
|
108
|
+
test_error(if resp.code == "429"
|
109
|
+
task.sleep(data[:retry_after])
|
110
|
+
patch(path, body, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
|
111
|
+
else
|
112
|
+
[resp, data]
|
113
|
+
end)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
#
|
118
|
+
# Execute a PUT request.
|
119
|
+
# @macro async
|
120
|
+
#
|
121
|
+
# @param [String] path The path to the resource.
|
122
|
+
# @param [String, Hash] body The body of the request.
|
123
|
+
# @param [Hash] headers The headers to send with the request.
|
124
|
+
# @param [String] audit_log_reason The audit log reason to send with the request.
|
125
|
+
# @param [Hash] kwargs The keyword arguments.
|
126
|
+
#
|
127
|
+
# @return [Array[Net::HTTPResponse, Hash]] The response and as JSON.
|
128
|
+
# @return [Array[Net::HTTPResponse, nil]] The response was 204.
|
129
|
+
#
|
130
|
+
# @raise [Discorb::HTTPError] The request was failed.
|
131
|
+
#
|
132
|
+
def put(path, body = "", headers: nil, audit_log_reason: nil, **kwargs)
|
133
|
+
Async do |task|
|
134
|
+
resp = http.put(get_path(path), get_body(body), get_headers(headers, body, audit_log_reason), **kwargs)
|
135
|
+
rd = resp.body
|
136
|
+
data = if rd.nil? || rd.empty?
|
137
|
+
nil
|
138
|
+
else
|
139
|
+
JSON.parse(rd, symbolize_names: true)
|
140
|
+
end
|
141
|
+
test_error(if resp.code == "429"
|
142
|
+
task.sleep(data[:retry_after])
|
143
|
+
put(path, body, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
|
144
|
+
else
|
145
|
+
[resp, data]
|
146
|
+
end)
|
147
|
+
end
|
148
|
+
end
|
149
|
+
|
150
|
+
#
|
151
|
+
# Execute a DELETE request.
|
152
|
+
# @macro async
|
153
|
+
#
|
154
|
+
# @param [String] path The path to the resource.
|
155
|
+
# @param [Hash] headers The headers to send with the request.
|
156
|
+
# @param [String] audit_log_reason The audit log reason to send with the request.
|
157
|
+
# @param [Hash] kwargs The keyword arguments.
|
158
|
+
#
|
159
|
+
# @return [Array[Net::HTTPResponse, Hash]] The response and as JSON.
|
160
|
+
# @return [Array[Net::HTTPResponse, nil]] The response was 204.
|
161
|
+
#
|
162
|
+
# @raise [Discorb::HTTPError] The request was failed.
|
163
|
+
#
|
164
|
+
def delete(path, headers: nil, audit_log_reason: nil, **kwargs)
|
165
|
+
Async do |task|
|
166
|
+
resp = http.delete(get_path(path), get_headers(headers, "", audit_log_reason))
|
167
|
+
rd = resp.body
|
168
|
+
data = if rd.nil? || rd.empty?
|
169
|
+
nil
|
170
|
+
else
|
171
|
+
JSON.parse(rd, symbolize_names: true)
|
172
|
+
end
|
173
|
+
test_error(if resp.code == "429"
|
174
|
+
task.sleep(data[:retry_after])
|
175
|
+
delete(path, headers: headers, audit_log_reason: audit_log_reason, **kwargs).wait
|
176
|
+
else
|
177
|
+
[resp, data]
|
178
|
+
end)
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
def inspect
|
183
|
+
"#<#{self.class} client=#{@client}>"
|
184
|
+
end
|
185
|
+
|
186
|
+
#
|
187
|
+
# A helper method to send multipart/form-data requests.
|
188
|
+
#
|
189
|
+
# @param [Hash] payload The payload to send.
|
190
|
+
# @param [Array<Discorb::File>] files The files to send.
|
191
|
+
#
|
192
|
+
# @return [Array[String, String]] The boundary and body.
|
193
|
+
#
|
194
|
+
def self.multipart(payload, files)
|
195
|
+
boundary = "DiscorbBySevenC7CMultipartFormData#{Time.now.to_f}"
|
196
|
+
str_payloads = [<<~HTTP]
|
197
|
+
Content-Disposition: form-data; name="payload_json"
|
198
|
+
Content-Type: application/json
|
199
|
+
|
200
|
+
#{payload.to_json}
|
201
|
+
HTTP
|
202
|
+
files.each do |single_file|
|
203
|
+
str_payloads << <<~HTTP
|
204
|
+
Content-Disposition: form-data; name="file"; filename="#{single_file.filename}"
|
205
|
+
Content-Type: #{single_file.content_type}
|
206
|
+
|
207
|
+
#{single_file.io.read}
|
208
|
+
HTTP
|
209
|
+
end
|
210
|
+
[boundary, "--#{boundary}\n#{str_payloads.join("\n--#{boundary}\n")}\n--#{boundary}--"]
|
211
|
+
end
|
212
|
+
|
213
|
+
private
|
214
|
+
|
215
|
+
def test_error(ary)
|
216
|
+
resp, data = *ary
|
217
|
+
case resp.code
|
218
|
+
when "400"
|
219
|
+
raise BadRequestError.new(resp, data)
|
220
|
+
when "403"
|
221
|
+
raise ForbiddenError.new(resp, data)
|
222
|
+
when "404"
|
223
|
+
raise NotFoundError.new(resp, data)
|
224
|
+
else
|
225
|
+
[resp, data]
|
226
|
+
end
|
227
|
+
end
|
228
|
+
|
229
|
+
def get_headers(headers, body = "", audit_log_reason = nil)
|
230
|
+
ret = if body.nil? || body.empty?
|
231
|
+
{ "User-Agent" => USER_AGENT, "authorization" => "Bot #{@client.token}" }
|
232
|
+
else
|
233
|
+
{ "User-Agent" => USER_AGENT, "authorization" => "Bot #{@client.token}",
|
234
|
+
"content-type" => "application/json" }
|
235
|
+
end
|
236
|
+
ret.merge(headers) if !headers.nil? && headers.length.positive?
|
237
|
+
ret["X-Audit-Log-Reason"] = audit_log_reason unless audit_log_reason.nil?
|
238
|
+
ret
|
239
|
+
end
|
240
|
+
|
241
|
+
def get_body(body)
|
242
|
+
if body.nil?
|
243
|
+
""
|
244
|
+
elsif body.is_a?(String)
|
245
|
+
body
|
246
|
+
else
|
247
|
+
recr_utf8(body).to_json
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def get_path(path)
|
252
|
+
full_path = if path.start_with?("https://")
|
253
|
+
path
|
254
|
+
else
|
255
|
+
API_BASE_URL + path
|
256
|
+
end
|
257
|
+
URI(full_path).path
|
258
|
+
end
|
259
|
+
|
260
|
+
def http
|
261
|
+
https = Net::HTTP.new("discord.com", 443)
|
262
|
+
https.use_ssl = true
|
263
|
+
https
|
264
|
+
end
|
265
|
+
|
266
|
+
def recr_utf8(data)
|
267
|
+
case data
|
268
|
+
when Hash
|
269
|
+
data.each do |k, v|
|
270
|
+
data[k] = recr_utf8(v)
|
271
|
+
end
|
272
|
+
data
|
273
|
+
when Array
|
274
|
+
data.each_index do |i|
|
275
|
+
data[i] = recr_utf8(data[i])
|
276
|
+
end
|
277
|
+
data
|
278
|
+
when String
|
279
|
+
data.dup.force_encoding(Encoding::UTF_8)
|
280
|
+
else
|
281
|
+
data
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|