discorb 0.19.0 → 0.20.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.
- checksums.yaml +4 -4
- data/.github/workflows/build_version.yml +2 -2
- data/.rubocop.yml +12 -75
- data/Changelog.md +10 -0
- data/Rakefile +482 -454
- data/lib/discorb/allowed_mentions.rb +68 -72
- data/lib/discorb/app_command/command.rb +466 -398
- data/lib/discorb/app_command/common.rb +65 -25
- data/lib/discorb/app_command/handler.rb +304 -266
- data/lib/discorb/app_command.rb +5 -5
- data/lib/discorb/application.rb +198 -197
- data/lib/discorb/asset.rb +101 -101
- data/lib/discorb/attachment.rb +134 -119
- data/lib/discorb/audit_logs.rb +412 -385
- data/lib/discorb/automod.rb +279 -269
- data/lib/discorb/channel/base.rb +107 -108
- data/lib/discorb/channel/category.rb +32 -32
- data/lib/discorb/channel/container.rb +44 -44
- data/lib/discorb/channel/dm.rb +26 -28
- data/lib/discorb/channel/guild.rb +311 -246
- data/lib/discorb/channel/stage.rb +156 -140
- data/lib/discorb/channel/text.rb +430 -336
- data/lib/discorb/channel/thread.rb +374 -325
- data/lib/discorb/channel/voice.rb +85 -79
- data/lib/discorb/channel.rb +5 -5
- data/lib/discorb/client.rb +635 -621
- data/lib/discorb/color.rb +178 -182
- data/lib/discorb/common.rb +168 -164
- data/lib/discorb/components/button.rb +107 -106
- data/lib/discorb/components/select_menu.rb +157 -145
- data/lib/discorb/components/text_input.rb +103 -106
- data/lib/discorb/components.rb +68 -66
- data/lib/discorb/dictionary.rb +135 -135
- data/lib/discorb/embed.rb +404 -398
- data/lib/discorb/emoji.rb +309 -302
- data/lib/discorb/emoji_table.rb +16099 -8857
- data/lib/discorb/error.rb +131 -131
- data/lib/discorb/event.rb +360 -314
- data/lib/discorb/event_handler.rb +39 -39
- data/lib/discorb/exe/about.rb +17 -17
- data/lib/discorb/exe/irb.rb +72 -67
- data/lib/discorb/exe/new.rb +323 -315
- data/lib/discorb/exe/run.rb +69 -68
- data/lib/discorb/exe/setup.rb +57 -55
- data/lib/discorb/exe/show.rb +12 -12
- data/lib/discorb/extend.rb +25 -45
- data/lib/discorb/extension.rb +89 -83
- data/lib/discorb/flag.rb +126 -128
- data/lib/discorb/gateway.rb +984 -804
- data/lib/discorb/gateway_events.rb +670 -638
- data/lib/discorb/gateway_requests.rb +45 -48
- data/lib/discorb/guild.rb +2115 -1626
- data/lib/discorb/guild_template.rb +280 -241
- data/lib/discorb/http.rb +247 -232
- data/lib/discorb/image.rb +42 -42
- data/lib/discorb/integration.rb +169 -161
- data/lib/discorb/intents.rb +161 -163
- data/lib/discorb/interaction/autocomplete.rb +76 -62
- data/lib/discorb/interaction/command.rb +279 -224
- data/lib/discorb/interaction/components.rb +114 -104
- data/lib/discorb/interaction/modal.rb +36 -32
- data/lib/discorb/interaction/response.rb +379 -336
- data/lib/discorb/interaction/root.rb +271 -257
- data/lib/discorb/interaction.rb +5 -5
- data/lib/discorb/invite.rb +154 -153
- data/lib/discorb/member.rb +344 -311
- data/lib/discorb/message.rb +615 -544
- data/lib/discorb/message_meta.rb +197 -186
- data/lib/discorb/modules.rb +371 -290
- data/lib/discorb/permission.rb +305 -291
- data/lib/discorb/presence.rb +352 -346
- data/lib/discorb/rate_limit.rb +81 -76
- data/lib/discorb/reaction.rb +55 -54
- data/lib/discorb/role.rb +272 -240
- data/lib/discorb/shard.rb +76 -74
- data/lib/discorb/sticker.rb +193 -171
- data/lib/discorb/user.rb +205 -188
- data/lib/discorb/utils/colored_puts.rb +16 -16
- data/lib/discorb/utils.rb +12 -16
- data/lib/discorb/voice_state.rb +305 -281
- data/lib/discorb/webhook.rb +537 -507
- data/lib/discorb.rb +62 -56
- data/sig/discorb/application.rbs +2 -0
- data/sig/discorb/automod.rbs +10 -1
- data/sig/discorb/guild.rbs +2 -0
- data/sig/discorb/message.rbs +2 -0
- data/sig/discorb/user.rbs +22 -20
- metadata +2 -2
data/lib/discorb/http.rb
CHANGED
@@ -1,232 +1,247 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "net/https"
|
4
|
-
|
5
|
-
module Discorb
|
6
|
-
#
|
7
|
-
# A class to handle http requests.
|
8
|
-
# @private
|
9
|
-
#
|
10
|
-
class HTTP
|
11
|
-
@nil_body = nil
|
12
|
-
|
13
|
-
#
|
14
|
-
# Initializes the http client.
|
15
|
-
# @private
|
16
|
-
#
|
17
|
-
# @param [Discorb::Client] client The client.
|
18
|
-
#
|
19
|
-
def initialize(client)
|
20
|
-
@client = client
|
21
|
-
@ratelimit_handler = RatelimitHandler.new(client)
|
22
|
-
end
|
23
|
-
|
24
|
-
#
|
25
|
-
# Execute a request.
|
26
|
-
# @async
|
27
|
-
#
|
28
|
-
# @param [Discorb::Route] path The path to the resource.
|
29
|
-
# @param [String, Hash] body The body of the request. Defaults to an empty string.
|
30
|
-
# @param [String] audit_log_reason The audit log reason to send with the request.
|
31
|
-
# @param [Hash] kwargs The keyword arguments.
|
32
|
-
#
|
33
|
-
# @return [Async::Task<Array(Net::HTTPResponse, Hash)>] The response and as JSON.
|
34
|
-
# @return [Async::Task<Array(Net::HTTPResponse, nil)>] The response was 204.
|
35
|
-
#
|
36
|
-
# @raise [Discorb::HTTPError] The request was failed.
|
37
|
-
#
|
38
|
-
def request(path, body = "", audit_log_reason: nil, **kwargs)
|
39
|
-
Async do |_task|
|
40
|
-
@ratelimit_handler.wait(path)
|
41
|
-
resp =
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
#
|
65
|
-
#
|
66
|
-
#
|
67
|
-
#
|
68
|
-
# @param [
|
69
|
-
# @param [
|
70
|
-
# @param [
|
71
|
-
# @param [
|
72
|
-
#
|
73
|
-
#
|
74
|
-
# @return [Async::Task<Array(Net::HTTPResponse,
|
75
|
-
#
|
76
|
-
#
|
77
|
-
#
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
]
|
90
|
-
files&.each_with_index do |file, i|
|
91
|
-
next if file.nil?
|
92
|
-
|
93
|
-
if file.created_by == :discord
|
94
|
-
request_io =
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
"Content-Type" => nil,
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
else
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
end
|
206
|
-
|
207
|
-
def
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
end
|
231
|
-
|
232
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "net/https"
|
4
|
+
|
5
|
+
module Discorb
|
6
|
+
#
|
7
|
+
# A class to handle http requests.
|
8
|
+
# @private
|
9
|
+
#
|
10
|
+
class HTTP
|
11
|
+
@nil_body = nil
|
12
|
+
|
13
|
+
#
|
14
|
+
# Initializes the http client.
|
15
|
+
# @private
|
16
|
+
#
|
17
|
+
# @param [Discorb::Client] client The client.
|
18
|
+
#
|
19
|
+
def initialize(client)
|
20
|
+
@client = client
|
21
|
+
@ratelimit_handler = RatelimitHandler.new(client)
|
22
|
+
end
|
23
|
+
|
24
|
+
#
|
25
|
+
# Execute a request.
|
26
|
+
# @async
|
27
|
+
#
|
28
|
+
# @param [Discorb::Route] path The path to the resource.
|
29
|
+
# @param [String, Hash] body The body of the request. Defaults to an empty string.
|
30
|
+
# @param [String] audit_log_reason The audit log reason to send with the request.
|
31
|
+
# @param [Hash] kwargs The keyword arguments.
|
32
|
+
#
|
33
|
+
# @return [Async::Task<Array(Net::HTTPResponse, Hash)>] The response and as JSON.
|
34
|
+
# @return [Async::Task<Array(Net::HTTPResponse, nil)>] The response was 204.
|
35
|
+
#
|
36
|
+
# @raise [Discorb::HTTPError] The request was failed.
|
37
|
+
#
|
38
|
+
def request(path, body = "", audit_log_reason: nil, **kwargs)
|
39
|
+
Async do |_task|
|
40
|
+
@ratelimit_handler.wait(path)
|
41
|
+
resp =
|
42
|
+
if %i[post patch put].include? path.method
|
43
|
+
http.send(
|
44
|
+
path.method,
|
45
|
+
get_path(path),
|
46
|
+
get_body(body),
|
47
|
+
get_headers(body, audit_log_reason),
|
48
|
+
**kwargs
|
49
|
+
)
|
50
|
+
else
|
51
|
+
http.send(
|
52
|
+
path.method,
|
53
|
+
get_path(path),
|
54
|
+
get_headers(body, audit_log_reason),
|
55
|
+
**kwargs
|
56
|
+
)
|
57
|
+
end
|
58
|
+
data = get_response_data(resp)
|
59
|
+
@ratelimit_handler.save(path, resp)
|
60
|
+
handle_response(resp, data, path, body, nil, audit_log_reason, kwargs)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
#
|
65
|
+
# Execute a multipart request.
|
66
|
+
# @async
|
67
|
+
#
|
68
|
+
# @param [Discorb::Route] path The path to the resource.
|
69
|
+
# @param [String, Hash] body The body of the request.
|
70
|
+
# @param [Array<Discorb::Attachment>] files The files to upload.
|
71
|
+
# @param [String] audit_log_reason The audit log reason to send with the request.
|
72
|
+
# @param [Hash] kwargs The keyword arguments.
|
73
|
+
#
|
74
|
+
# @return [Async::Task<Array(Net::HTTPResponse, Hash)>] The response and as JSON.
|
75
|
+
# @return [Async::Task<Array(Net::HTTPResponse, nil)>] The response was 204.
|
76
|
+
#
|
77
|
+
# @raise [Discorb::HTTPError] The request was failed.
|
78
|
+
#
|
79
|
+
def multipart_request(path, body, files, audit_log_reason: nil, **kwargs)
|
80
|
+
Async do |_task|
|
81
|
+
@ratelimit_handler.wait(path)
|
82
|
+
req =
|
83
|
+
Net::HTTP.const_get(path.method.to_s.capitalize).new(
|
84
|
+
get_path(path),
|
85
|
+
get_headers(body, audit_log_reason),
|
86
|
+
**kwargs
|
87
|
+
)
|
88
|
+
# @type var data: Array[untyped]
|
89
|
+
data = [["payload_json", get_body(body)]]
|
90
|
+
files&.each_with_index do |file, i|
|
91
|
+
next if file.nil?
|
92
|
+
|
93
|
+
if file.created_by == :discord
|
94
|
+
request_io =
|
95
|
+
StringIO.new(
|
96
|
+
cdn_http.get(
|
97
|
+
(URI.parse(file.url).path || raise("Could not parse url")),
|
98
|
+
{ "Content-Type" => nil, "User-Agent" => Discorb::USER_AGENT }
|
99
|
+
).body
|
100
|
+
)
|
101
|
+
data << [
|
102
|
+
"files[#{i}]",
|
103
|
+
request_io,
|
104
|
+
{ filename: file.filename, content_type: file.content_type }
|
105
|
+
]
|
106
|
+
else
|
107
|
+
data << [
|
108
|
+
"files[#{i}]",
|
109
|
+
file.io,
|
110
|
+
{ filename: file.filename, content_type: file.content_type }
|
111
|
+
]
|
112
|
+
end
|
113
|
+
end
|
114
|
+
req.set_form(data, "multipart/form-data")
|
115
|
+
session = Net::HTTP.new("discord.com", 443)
|
116
|
+
session.use_ssl = true
|
117
|
+
resp = session.request(req)
|
118
|
+
resp_data = get_response_data(resp)
|
119
|
+
@ratelimit_handler.save(path, resp)
|
120
|
+
response =
|
121
|
+
handle_response(
|
122
|
+
resp,
|
123
|
+
resp_data,
|
124
|
+
path,
|
125
|
+
body,
|
126
|
+
files,
|
127
|
+
audit_log_reason,
|
128
|
+
kwargs
|
129
|
+
)
|
130
|
+
files&.then { _1.filter(&:will_close).each { |f| f.io.close } }
|
131
|
+
response
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def inspect
|
136
|
+
"#<#{self.class} client=#{@client}>"
|
137
|
+
end
|
138
|
+
|
139
|
+
private
|
140
|
+
|
141
|
+
def handle_response(resp, data, path, body, files, audit_log_reason, kwargs)
|
142
|
+
case resp.code
|
143
|
+
when "429"
|
144
|
+
@client.logger.info(
|
145
|
+
"Rate limit exceeded for #{path.method} #{path.url}, waiting #{data[:retry_after]} seconds"
|
146
|
+
)
|
147
|
+
sleep(data[:retry_after])
|
148
|
+
if files
|
149
|
+
multipart_request(
|
150
|
+
path,
|
151
|
+
body,
|
152
|
+
files,
|
153
|
+
audit_log_reason: audit_log_reason,
|
154
|
+
**kwargs
|
155
|
+
).wait
|
156
|
+
else
|
157
|
+
request(path, body, audit_log_reason: audit_log_reason, **kwargs).wait
|
158
|
+
end
|
159
|
+
when "400"
|
160
|
+
raise BadRequestError.new(resp, data)
|
161
|
+
when "401"
|
162
|
+
raise UnauthorizedError.new(resp, data)
|
163
|
+
when "403"
|
164
|
+
raise ForbiddenError.new(resp, data)
|
165
|
+
when "404"
|
166
|
+
raise NotFoundError.new(resp, data)
|
167
|
+
else
|
168
|
+
[resp, data]
|
169
|
+
end
|
170
|
+
end
|
171
|
+
|
172
|
+
def get_headers(body = "", audit_log_reason = nil)
|
173
|
+
ret =
|
174
|
+
if body.nil? || body == ""
|
175
|
+
{
|
176
|
+
"User-Agent" => USER_AGENT,
|
177
|
+
"authorization" => "Bot #{@client.token}"
|
178
|
+
}
|
179
|
+
else
|
180
|
+
{
|
181
|
+
"User-Agent" => USER_AGENT,
|
182
|
+
"authorization" => "Bot #{@client.token}",
|
183
|
+
"content-type" => "application/json"
|
184
|
+
}
|
185
|
+
end
|
186
|
+
ret["X-Audit-Log-Reason"] = audit_log_reason unless audit_log_reason.nil?
|
187
|
+
ret
|
188
|
+
end
|
189
|
+
|
190
|
+
def get_body(body)
|
191
|
+
if body.nil?
|
192
|
+
""
|
193
|
+
elsif body.is_a?(String)
|
194
|
+
body
|
195
|
+
else
|
196
|
+
recr_utf8(body).to_json
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
def get_path(path)
|
201
|
+
full_path =
|
202
|
+
(path.url.start_with?("https://") ? path.url : API_BASE_URL + path.url)
|
203
|
+
uri = URI(full_path)
|
204
|
+
full_path.sub("#{uri.scheme}://#{uri.host}", "")
|
205
|
+
end
|
206
|
+
|
207
|
+
def get_response_data(resp)
|
208
|
+
begin
|
209
|
+
data = JSON.parse(resp.body, symbolize_names: true)
|
210
|
+
rescue JSON::ParserError, TypeError
|
211
|
+
data = (resp.body.nil? || resp.body.empty? ? {} : resp.body)
|
212
|
+
end
|
213
|
+
if resp["Via"].nil? && resp.code == "429" && data.is_a?(String)
|
214
|
+
raise CloudFlareBanError.new(resp, @client)
|
215
|
+
end
|
216
|
+
|
217
|
+
data
|
218
|
+
end
|
219
|
+
|
220
|
+
def http
|
221
|
+
https = Net::HTTP.new("discord.com", 443)
|
222
|
+
https.use_ssl = true
|
223
|
+
https
|
224
|
+
end
|
225
|
+
|
226
|
+
def cdn_http
|
227
|
+
https = Net::HTTP.new("cdn.discordapp.com", 443)
|
228
|
+
https.use_ssl = true
|
229
|
+
https
|
230
|
+
end
|
231
|
+
|
232
|
+
def recr_utf8(data)
|
233
|
+
case data
|
234
|
+
when Hash
|
235
|
+
data.each { |k, v| data[k] = recr_utf8(v) }
|
236
|
+
data
|
237
|
+
when Array
|
238
|
+
data.each_index { |i| data[i] = recr_utf8(data[i]) }
|
239
|
+
data
|
240
|
+
when String
|
241
|
+
data.dup.force_encoding(Encoding::UTF_8)
|
242
|
+
else
|
243
|
+
data
|
244
|
+
end
|
245
|
+
end
|
246
|
+
end
|
247
|
+
end
|
data/lib/discorb/image.rb
CHANGED
@@ -1,42 +1,42 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "base64"
|
4
|
-
require "mime/types"
|
5
|
-
|
6
|
-
module Discorb
|
7
|
-
#
|
8
|
-
# Represents an image.
|
9
|
-
#
|
10
|
-
class Image
|
11
|
-
#
|
12
|
-
# Initializes a new Image.
|
13
|
-
#
|
14
|
-
# @param [#read, String] source The IO source or path of the image.
|
15
|
-
# @param [String] type The MIME type of the image.
|
16
|
-
#
|
17
|
-
def initialize(source, type = nil)
|
18
|
-
if source.respond_to?(:read)
|
19
|
-
@io = source
|
20
|
-
@type = type || MIME::Types.type_for(source.path).first.content_type
|
21
|
-
elsif ::File.exist?(source)
|
22
|
-
@io = ::File.open(source, "rb")
|
23
|
-
@type = MIME::Types.type_for(source).first.to_s
|
24
|
-
else
|
25
|
-
raise ArgumentError, "Couldn't read file."
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
#
|
30
|
-
# Formats the image as a Discord style.
|
31
|
-
#
|
32
|
-
# @return [String] The image as a Discord style.
|
33
|
-
#
|
34
|
-
def to_s
|
35
|
-
"data:#{@type};base64,#{Base64.strict_encode64(@io.read)}"
|
36
|
-
end
|
37
|
-
|
38
|
-
def inspect
|
39
|
-
"#<#{self.class} #{@type}>"
|
40
|
-
end
|
41
|
-
end
|
42
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "base64"
|
4
|
+
require "mime/types"
|
5
|
+
|
6
|
+
module Discorb
|
7
|
+
#
|
8
|
+
# Represents an image.
|
9
|
+
#
|
10
|
+
class Image
|
11
|
+
#
|
12
|
+
# Initializes a new Image.
|
13
|
+
#
|
14
|
+
# @param [#read, String] source The IO source or path of the image.
|
15
|
+
# @param [String] type The MIME type of the image.
|
16
|
+
#
|
17
|
+
def initialize(source, type = nil)
|
18
|
+
if source.respond_to?(:read)
|
19
|
+
@io = source
|
20
|
+
@type = type || MIME::Types.type_for(source.path).first.content_type
|
21
|
+
elsif ::File.exist?(source)
|
22
|
+
@io = ::File.open(source, "rb")
|
23
|
+
@type = MIME::Types.type_for(source).first.to_s
|
24
|
+
else
|
25
|
+
raise ArgumentError, "Couldn't read file."
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
#
|
30
|
+
# Formats the image as a Discord style.
|
31
|
+
#
|
32
|
+
# @return [String] The image as a Discord style.
|
33
|
+
#
|
34
|
+
def to_s
|
35
|
+
"data:#{@type};base64,#{Base64.strict_encode64(@io.read)}"
|
36
|
+
end
|
37
|
+
|
38
|
+
def inspect
|
39
|
+
"#<#{self.class} #{@type}>"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|