discorb 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +56 -0
  3. data/.yardopts +6 -0
  4. data/Changelog.md +5 -0
  5. data/Gemfile +23 -0
  6. data/Gemfile.lock +70 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +53 -0
  9. data/Rakefile +46 -0
  10. data/bin/console +15 -0
  11. data/bin/setup +8 -0
  12. data/discorb.gemspec +37 -0
  13. data/docs/Examples.md +26 -0
  14. data/docs/events.md +480 -0
  15. data/docs/voice_events.md +283 -0
  16. data/examples/components/authorization_button.rb +43 -0
  17. data/examples/components/select_menu.rb +61 -0
  18. data/examples/extension/main.rb +12 -0
  19. data/examples/extension/message_expander.rb +41 -0
  20. data/examples/simple/eval.rb +32 -0
  21. data/examples/simple/ping_pong.rb +16 -0
  22. data/examples/simple/rolepanel.rb +65 -0
  23. data/examples/simple/wait_for_message.rb +30 -0
  24. data/lib/discorb/application.rb +157 -0
  25. data/lib/discorb/asset.rb +57 -0
  26. data/lib/discorb/audit_logs.rb +323 -0
  27. data/lib/discorb/channel.rb +1101 -0
  28. data/lib/discorb/client.rb +363 -0
  29. data/lib/discorb/color.rb +173 -0
  30. data/lib/discorb/common.rb +123 -0
  31. data/lib/discorb/components.rb +290 -0
  32. data/lib/discorb/dictionary.rb +119 -0
  33. data/lib/discorb/embed.rb +345 -0
  34. data/lib/discorb/emoji.rb +218 -0
  35. data/lib/discorb/emoji_table.rb +3799 -0
  36. data/lib/discorb/error.rb +98 -0
  37. data/lib/discorb/event.rb +35 -0
  38. data/lib/discorb/extend.rb +18 -0
  39. data/lib/discorb/extension.rb +54 -0
  40. data/lib/discorb/file.rb +69 -0
  41. data/lib/discorb/flag.rb +109 -0
  42. data/lib/discorb/gateway.rb +967 -0
  43. data/lib/discorb/gateway_requests.rb +47 -0
  44. data/lib/discorb/guild.rb +1244 -0
  45. data/lib/discorb/guild_template.rb +211 -0
  46. data/lib/discorb/image.rb +43 -0
  47. data/lib/discorb/integration.rb +111 -0
  48. data/lib/discorb/intents.rb +137 -0
  49. data/lib/discorb/interaction.rb +333 -0
  50. data/lib/discorb/internet.rb +285 -0
  51. data/lib/discorb/invite.rb +145 -0
  52. data/lib/discorb/log.rb +70 -0
  53. data/lib/discorb/member.rb +232 -0
  54. data/lib/discorb/message.rb +583 -0
  55. data/lib/discorb/modules.rb +138 -0
  56. data/lib/discorb/permission.rb +270 -0
  57. data/lib/discorb/presence.rb +308 -0
  58. data/lib/discorb/reaction.rb +48 -0
  59. data/lib/discorb/role.rb +189 -0
  60. data/lib/discorb/sticker.rb +157 -0
  61. data/lib/discorb/user.rb +163 -0
  62. data/lib/discorb/utils.rb +16 -0
  63. data/lib/discorb/voice_state.rb +251 -0
  64. data/lib/discorb/webhook.rb +420 -0
  65. data/lib/discorb.rb +51 -0
  66. 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