disrb 0.1.3 → 0.1.4
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/CHANGELOG.md +13 -0
- data/LICENSE +1 -1
- data/README.md +19 -7
- data/lib/disrb/application_commands.rb +53 -45
- data/lib/disrb/guild.rb +162 -147
- data/lib/disrb/message.rb +87 -66
- data/lib/disrb/user.rb +36 -35
- data/lib/disrb.rb +183 -24
- data/lib/version.rb +5 -0
- metadata +30 -1
data/lib/disrb.rb
CHANGED
|
@@ -5,11 +5,14 @@ require 'async'
|
|
|
5
5
|
require 'async/http/endpoint'
|
|
6
6
|
require 'async/websocket/client'
|
|
7
7
|
require 'faraday'
|
|
8
|
+
require 'faraday/multipart'
|
|
9
|
+
require 'stringio'
|
|
8
10
|
require_relative 'disrb/guild'
|
|
9
11
|
require_relative 'disrb/logger'
|
|
10
12
|
require_relative 'disrb/user'
|
|
11
13
|
require_relative 'disrb/message'
|
|
12
14
|
require_relative 'disrb/application_commands'
|
|
15
|
+
require_relative 'version'
|
|
13
16
|
|
|
14
17
|
# Contains functions related to Discord snowflakes.
|
|
15
18
|
class Snowflake
|
|
@@ -47,7 +50,7 @@ class Snowflake
|
|
|
47
50
|
end
|
|
48
51
|
|
|
49
52
|
# Class that contains functions that allow interacting with the Discord API.
|
|
50
|
-
# @version 0.1.
|
|
53
|
+
# @version 0.1.4
|
|
51
54
|
class DiscordApi
|
|
52
55
|
# @!attribute [r] base_url
|
|
53
56
|
# @return [String] the base URL that is used to access the Discord API. ex: "https://discord.com/api/v10"
|
|
@@ -55,13 +58,16 @@ class DiscordApi
|
|
|
55
58
|
# @return [String] the authorization header that is used to authenticate requests to the Discord API.
|
|
56
59
|
# @!attribute [r] application_id
|
|
57
60
|
# @return [Integer] the application ID of the bot that has been assigned to the provided authorization token.
|
|
58
|
-
attr_accessor(:base_url, :authorization_header, :application_id, :logger)
|
|
61
|
+
attr_accessor(:base_url, :authorization_header, :application_id, :logger, :user_agent)
|
|
59
62
|
|
|
60
63
|
# Creates a new DiscordApi instance. (required to use most functions)
|
|
61
64
|
#
|
|
62
65
|
# @param authorization_token_type [String] The type of authorization token provided by Discord, 'Bot' or 'Bearer'.
|
|
63
66
|
# @param authorization_token [String] The value of the authorization token provided by Discord.
|
|
64
67
|
# @param verbosity_level [String, Integer, nil] The verbosity level of the logger.
|
|
68
|
+
# @param user_agent [String, nil] When sending a request to Discord's HTTP API, a valid User-Agent header must be set.
|
|
69
|
+
# By setting this parameter, the value of the User-Agent header sent will be equal to the value of this parameter.
|
|
70
|
+
# Defaults to 'discord.rb (https://github.com/hoovad/discord.rb, [discord.rb version])'
|
|
65
71
|
# Set verbosity_level to:
|
|
66
72
|
# - 'all' or 5 to log all of the below plus debug messages
|
|
67
73
|
# - 'info', 4 or nil to log all of the below plus info messages [DEFAULT]
|
|
@@ -70,7 +76,7 @@ class DiscordApi
|
|
|
70
76
|
# - 'fatal_error' or 1 to log only fatal errors
|
|
71
77
|
# - 'none' or 0 for no logging
|
|
72
78
|
# @return [DiscordApi] DiscordApi instance.
|
|
73
|
-
def initialize(authorization_token_type, authorization_token, verbosity_level = nil)
|
|
79
|
+
def initialize(authorization_token_type, authorization_token, verbosity_level = nil, user_agent = nil)
|
|
74
80
|
@api_version = '10'
|
|
75
81
|
@base_url = "https://discord.com/api/v#{@api_version}"
|
|
76
82
|
@authorization_token_type = authorization_token_type
|
|
@@ -108,13 +114,23 @@ class DiscordApi
|
|
|
108
114
|
@verbosity_level = 4
|
|
109
115
|
end
|
|
110
116
|
@logger = Logger2.new(@verbosity_level)
|
|
117
|
+
default_user_agent = "discord.rb (https://github.com/hoovad/discord.rb, #{DiscordApi::VERSION})"
|
|
118
|
+
if user_agent.is_a?(String) && !user_agent.empty?
|
|
119
|
+
@user_agent = user_agent
|
|
120
|
+
elsif user_agent.nil?
|
|
121
|
+
@user_agent = default_user_agent
|
|
122
|
+
else
|
|
123
|
+
@logger.warn("Invalid user_agent parameter. It must be a valid non-empty string. \
|
|
124
|
+
Defaulting to #{default_user_agent}.")
|
|
125
|
+
@user_agent = default_user_agent
|
|
126
|
+
end
|
|
111
127
|
url = "#{@base_url}/applications/@me"
|
|
112
128
|
headers = { 'Authorization': @authorization_header }
|
|
113
129
|
response = DiscordApi.get(url, headers)
|
|
114
|
-
if response.status == 200
|
|
130
|
+
if response.is_a?(Faraday::Response) && response.status == 200
|
|
115
131
|
@application_id = JSON.parse(response.body)['id']
|
|
116
132
|
else
|
|
117
|
-
@logger.fatal_error("Failed to get application ID with response: #{response
|
|
133
|
+
@logger.fatal_error("Failed to get application ID with response: #{response_error_body(response)}")
|
|
118
134
|
exit
|
|
119
135
|
end
|
|
120
136
|
end
|
|
@@ -238,10 +254,10 @@ class DiscordApi
|
|
|
238
254
|
recieved_ready = false
|
|
239
255
|
url = if rescue_connection.nil?
|
|
240
256
|
response = DiscordApi.get("#{@base_url}/gateway")
|
|
241
|
-
if response.status == 200
|
|
257
|
+
if response.is_a?(Faraday::Response) && response.status == 200
|
|
242
258
|
"#{JSON.parse(response.body)['url']}/?v=#{@api_version}&encoding=json"
|
|
243
259
|
else
|
|
244
|
-
@logger.fatal_error("Failed to get gateway URL. Response: #{response
|
|
260
|
+
@logger.fatal_error("Failed to get gateway URL. Response: #{response_error_body(response)}")
|
|
245
261
|
exit
|
|
246
262
|
end
|
|
247
263
|
else
|
|
@@ -362,9 +378,10 @@ class DiscordApi
|
|
|
362
378
|
data = JSON.generate(response)
|
|
363
379
|
headers = { 'content-type': 'application/json' }
|
|
364
380
|
response = DiscordApi.post(url, data, headers)
|
|
365
|
-
return response if
|
|
381
|
+
return response if response.is_a?(Faraday::Response) &&
|
|
382
|
+
((response.status == 204 && !with_response) || (response.status == 200 && with_response))
|
|
366
383
|
|
|
367
|
-
@logger.error("Failed to respond to interaction. Response: #{response
|
|
384
|
+
@logger.error("Failed to respond to interaction. Response: #{response_error_body(response)}")
|
|
368
385
|
response
|
|
369
386
|
end
|
|
370
387
|
|
|
@@ -480,15 +497,37 @@ class DiscordApi
|
|
|
480
497
|
intents.reduce(0) { |acc, n| acc | n }
|
|
481
498
|
end
|
|
482
499
|
|
|
500
|
+
private
|
|
501
|
+
|
|
502
|
+
# If 'response' is a Faraday::Response object, returns response.body, else, returns 'Empty'
|
|
503
|
+
# @param response [Object] Any object
|
|
504
|
+
# @return [String] response.body if response is a Faraday::Response object, else 'Empty'
|
|
505
|
+
def response_error_body(response)
|
|
506
|
+
return response.body if response.is_a?(Faraday::Response)
|
|
507
|
+
|
|
508
|
+
'Empty'
|
|
509
|
+
end
|
|
510
|
+
|
|
483
511
|
# Performs an HTTP GET request using Faraday.
|
|
484
512
|
# @param url [String] Full URL including scheme and host; path may be included.
|
|
485
513
|
# @param headers [Hash, nil] Optional request headers.
|
|
486
|
-
# @return [Faraday::Response] The Faraday response object.
|
|
487
|
-
def
|
|
514
|
+
# @return [Faraday::Response, nil] The Faraday response object, or nil if an error was encountered.
|
|
515
|
+
def get(url, headers = nil)
|
|
488
516
|
split_url = url.split(%r{(http[^/]+)(/.*)}).reject(&:empty?)
|
|
489
|
-
|
|
517
|
+
if split_url.empty?
|
|
518
|
+
@logger.error("Empty/invalid URL provided: #{url}. Cannot perform GET request.")
|
|
519
|
+
return
|
|
520
|
+
end
|
|
490
521
|
host = split_url[0]
|
|
491
522
|
path = split_url[1] if split_url[1]
|
|
523
|
+
if headers.is_a?(Hash)
|
|
524
|
+
headers['User-Agent'] = @user_agent
|
|
525
|
+
elsif headers.nil?
|
|
526
|
+
headers = { 'User-Agent' => @user_agent }
|
|
527
|
+
else
|
|
528
|
+
@logger.warn('Invalid headers parameter. It will be discarded.')
|
|
529
|
+
headers = { 'User-Agent' => @user_agent }
|
|
530
|
+
end
|
|
492
531
|
conn = Faraday.new(url: host, headers: headers)
|
|
493
532
|
if path
|
|
494
533
|
conn.get(path)
|
|
@@ -500,12 +539,23 @@ class DiscordApi
|
|
|
500
539
|
# Performs an HTTP DELETE request using Faraday.
|
|
501
540
|
# @param url [String] Full URL including scheme and host; path may be included.
|
|
502
541
|
# @param headers [Hash, nil] Optional request headers.
|
|
503
|
-
# @return [Faraday::Response] The Faraday response object.
|
|
504
|
-
def
|
|
542
|
+
# @return [Faraday::Response, nil] The Faraday response object, or nil if an error was encountered.
|
|
543
|
+
def delete(url, headers = nil)
|
|
505
544
|
split_url = url.split(%r{(http[^/]+)(/.*)}).reject(&:empty?)
|
|
506
|
-
|
|
545
|
+
if split_url.empty?
|
|
546
|
+
@logger.error("Empty/invalid URL provided: #{url}. Cannot perform DELETE request.")
|
|
547
|
+
return
|
|
548
|
+
end
|
|
507
549
|
host = split_url[0]
|
|
508
550
|
path = split_url[1] if split_url[1]
|
|
551
|
+
if headers.is_a?(Hash)
|
|
552
|
+
headers['User-Agent'] = @user_agent
|
|
553
|
+
elsif headers.nil?
|
|
554
|
+
headers = { 'User-Agent' => @user_agent }
|
|
555
|
+
else
|
|
556
|
+
@logger.warn('Invalid headers parameter. It will be discarded.')
|
|
557
|
+
headers = { 'User-Agent' => @user_agent }
|
|
558
|
+
end
|
|
509
559
|
conn = Faraday.new(url: host, headers: headers)
|
|
510
560
|
if path
|
|
511
561
|
conn.delete(path)
|
|
@@ -518,12 +568,23 @@ class DiscordApi
|
|
|
518
568
|
# @param url [String] Full URL including scheme and host; path may be included.
|
|
519
569
|
# @param data [String] Serialized request body (e.g., JSON string).
|
|
520
570
|
# @param headers [Hash, nil] Optional request headers.
|
|
521
|
-
# @return [Faraday::Response] The Faraday response object.
|
|
522
|
-
def
|
|
571
|
+
# @return [Faraday::Response, nil] The Faraday response object, or nil if an error was encountered.
|
|
572
|
+
def post(url, data, headers = nil)
|
|
523
573
|
split_url = url.split(%r{(http[^/]+)(/.*)}).reject(&:empty?)
|
|
524
|
-
|
|
574
|
+
if split_url.empty?
|
|
575
|
+
@logger.error("Empty/invalid URL provided: #{url}. Cannot perform POST request.")
|
|
576
|
+
return
|
|
577
|
+
end
|
|
525
578
|
host = split_url[0]
|
|
526
579
|
path = split_url[1] if split_url[1]
|
|
580
|
+
if headers.is_a?(Hash)
|
|
581
|
+
headers['User-Agent'] = @user_agent
|
|
582
|
+
elsif headers.nil?
|
|
583
|
+
headers = { 'User-Agent' => @user_agent }
|
|
584
|
+
else
|
|
585
|
+
@logger.warn('Invalid headers parameter. It will be discarded.')
|
|
586
|
+
headers = { 'User-Agent' => @user_agent }
|
|
587
|
+
end
|
|
527
588
|
conn = Faraday.new(url: host, headers: headers)
|
|
528
589
|
if path
|
|
529
590
|
conn.post(path, data)
|
|
@@ -536,12 +597,23 @@ class DiscordApi
|
|
|
536
597
|
# @param url [String] Full URL including scheme and host; path may be included.
|
|
537
598
|
# @param data [String] Serialized request body (e.g., JSON string).
|
|
538
599
|
# @param headers [Hash, nil] Optional request headers.
|
|
539
|
-
# @return [Faraday::Response] The Faraday response object.
|
|
540
|
-
def
|
|
600
|
+
# @return [Faraday::Response, nil] The Faraday response object, or nil if an error was encountered.
|
|
601
|
+
def patch(url, data, headers = nil)
|
|
541
602
|
split_url = url.split(%r{(http[^/]+)(/.*)}).reject(&:empty?)
|
|
542
|
-
|
|
603
|
+
if split_url.empty?
|
|
604
|
+
@logger.error("Empty/invalid URL provided: #{url}. Cannot perform PATCH request.")
|
|
605
|
+
return
|
|
606
|
+
end
|
|
543
607
|
host = split_url[0]
|
|
544
608
|
path = split_url[1] if split_url[1]
|
|
609
|
+
if headers.is_a?(Hash)
|
|
610
|
+
headers['User-Agent'] = @user_agent
|
|
611
|
+
elsif headers.nil?
|
|
612
|
+
headers = { 'User-Agent' => @user_agent }
|
|
613
|
+
else
|
|
614
|
+
@logger.warn('Invalid headers parameter. It will be discarded.')
|
|
615
|
+
headers = { 'User-Agent' => @user_agent }
|
|
616
|
+
end
|
|
545
617
|
conn = Faraday.new(url: host, headers: headers)
|
|
546
618
|
if path
|
|
547
619
|
conn.patch(path, data)
|
|
@@ -554,12 +626,23 @@ class DiscordApi
|
|
|
554
626
|
# @param url [String] Full URL including scheme and host; path may be included.
|
|
555
627
|
# @param data [String] Serialized request body (e.g., JSON string).
|
|
556
628
|
# @param headers [Hash, nil] Optional request headers.
|
|
557
|
-
# @return [Faraday::Response] The Faraday response object.
|
|
558
|
-
def
|
|
629
|
+
# @return [Faraday::Response, nil] The Faraday response object, or nil if an error was encountered.
|
|
630
|
+
def put(url, data, headers = nil)
|
|
559
631
|
split_url = url.split(%r{(http[^/]+)(/.*)}).reject(&:empty?)
|
|
560
|
-
|
|
632
|
+
if split_url.empty?
|
|
633
|
+
@logger.error("Empty/invalid URL provided: #{url}. Cannot perform PUT request.")
|
|
634
|
+
return
|
|
635
|
+
end
|
|
561
636
|
host = split_url[0]
|
|
562
637
|
path = split_url[1] if split_url[1]
|
|
638
|
+
if headers.is_a?(Hash)
|
|
639
|
+
headers['User-Agent'] = @user_agent
|
|
640
|
+
elsif headers.nil?
|
|
641
|
+
headers = { 'User-Agent' => @user_agent }
|
|
642
|
+
else
|
|
643
|
+
@logger.warn('Invalid headers parameter. It will be discarded.')
|
|
644
|
+
headers = { 'User-Agent' => @user_agent }
|
|
645
|
+
end
|
|
563
646
|
conn = Faraday.new(url: host, headers: headers)
|
|
564
647
|
if path
|
|
565
648
|
conn.put(path, data)
|
|
@@ -567,4 +650,80 @@ class DiscordApi
|
|
|
567
650
|
conn.put('', data)
|
|
568
651
|
end
|
|
569
652
|
end
|
|
653
|
+
|
|
654
|
+
# Sends a HTTP POST request to the specified URL, containing multipart/form-data data structured
|
|
655
|
+
# according to Discord documentation.
|
|
656
|
+
# See https://docs.discord.com/developers/reference#uploading-files
|
|
657
|
+
# @param url [String] Full URL including scheme and host; path may be included.
|
|
658
|
+
# @param payload_json [String] JSON data which will be included in the request under the 'payload_json'
|
|
659
|
+
# Content-Disposition.
|
|
660
|
+
# @param files [Array] An array of arrays, each inner-array first has its filename (index 0),
|
|
661
|
+
# raw file data as a string (index 1), and then the MIME type of the file (index 2).
|
|
662
|
+
# @param headers [Hash, nil] Optional request headers.
|
|
663
|
+
# @return [Faraday::Response, nil] The Faraday response object, or nil if an error was encountered.
|
|
664
|
+
def file_upload(url, files, payload_json: nil, headers: nil)
|
|
665
|
+
split_url = url.split(%r{(http[^/]+)(/.*)}).reject(&:empty?)
|
|
666
|
+
if split_url.empty?
|
|
667
|
+
@logger.error("Empty/invalid URL provided: #{url}. Cannot perform Discord multipart/form-data POST request.")
|
|
668
|
+
return
|
|
669
|
+
end
|
|
670
|
+
host = split_url[0]
|
|
671
|
+
path = split_url[1] if split_url[1]
|
|
672
|
+
if headers.is_a?(Hash)
|
|
673
|
+
headers['User-Agent'] = @user_agent
|
|
674
|
+
elsif headers.nil?
|
|
675
|
+
headers = { 'User-Agent' => @user_agent }
|
|
676
|
+
else
|
|
677
|
+
@logger.warn('Invalid headers parameter. It will be discarded.')
|
|
678
|
+
headers = { 'User-Agent' => @user_agent }
|
|
679
|
+
end
|
|
680
|
+
conn = Faraday.new(url: host, headers: headers, request: :multipart, content_type: 'multipart/form-data')
|
|
681
|
+
payload = {}
|
|
682
|
+
# FilePart expects File/IO objects as the first argument.
|
|
683
|
+
# However, since the function is being given raw data instead of File/IO objects, we should be using ParamPart
|
|
684
|
+
# But, ParamPart doesn't let us use a customized Content-Disposition, which is what we need
|
|
685
|
+
# So we will just have to wrap the raw data in an IO class with StringIO
|
|
686
|
+
if payload_json
|
|
687
|
+
payload[:payload_json] = Faraday::Multipart::FilePart.new(
|
|
688
|
+
StringIO.new(payload_json),
|
|
689
|
+
'application/json',
|
|
690
|
+
nil,
|
|
691
|
+
'Content-Disposition' => 'form-data; name="payload_json"'
|
|
692
|
+
)
|
|
693
|
+
end
|
|
694
|
+
files.each_with_index do |(filename, raw_bytes, mime_type), i|
|
|
695
|
+
payload[:"file_#{i}"] = Faraday::Multipart::FilePart.new(
|
|
696
|
+
StringIO.new(raw_bytes),
|
|
697
|
+
mime_type,
|
|
698
|
+
filename,
|
|
699
|
+
'Content-Disposition' => "form-data; name=\"files[#{i}]\"; filename=\"#{filename}\""
|
|
700
|
+
)
|
|
701
|
+
end
|
|
702
|
+
if payload.empty?
|
|
703
|
+
@logger.warn("Payload empty, not sending Discord multipart/form-data POST request to #{url}.")
|
|
704
|
+
nil
|
|
705
|
+
elsif path
|
|
706
|
+
conn.post(path, payload)
|
|
707
|
+
else
|
|
708
|
+
conn.post('', payload)
|
|
709
|
+
end
|
|
710
|
+
end
|
|
711
|
+
|
|
712
|
+
# Generates an array of attachments objects (hashes) according to
|
|
713
|
+
# https://docs.discord.com/developers/resources/message#attachment-object.
|
|
714
|
+
# @param attachments_array [Array] An array of arrays, each inner-array first has its filename (index 0),
|
|
715
|
+
# raw file data as a string (index 1), and then the MIME type of the file (index 2).
|
|
716
|
+
# @return [Array] An array formed of Discord attachment objects (hashes)
|
|
717
|
+
def generate_attachment_object_array(attachments_array)
|
|
718
|
+
final_array = []
|
|
719
|
+
attachments_array.each_with_index do |(filename, raw_bytes, mime_type), i|
|
|
720
|
+
final_array << {
|
|
721
|
+
'id' => i,
|
|
722
|
+
'filename' => filename,
|
|
723
|
+
'content_type' => mime_type,
|
|
724
|
+
'size' => raw_bytes.bytesize
|
|
725
|
+
}
|
|
726
|
+
end
|
|
727
|
+
final_array
|
|
728
|
+
end
|
|
570
729
|
end
|
data/lib/version.rb
ADDED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: disrb
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.4
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- hoovad
|
|
@@ -65,6 +65,34 @@ dependencies:
|
|
|
65
65
|
- - ">="
|
|
66
66
|
- !ruby/object:Gem::Version
|
|
67
67
|
version: 2.13.3
|
|
68
|
+
- !ruby/object:Gem::Dependency
|
|
69
|
+
name: faraday-multipart
|
|
70
|
+
requirement: !ruby/object:Gem::Requirement
|
|
71
|
+
requirements:
|
|
72
|
+
- - ">="
|
|
73
|
+
- !ruby/object:Gem::Version
|
|
74
|
+
version: 1.2.0
|
|
75
|
+
type: :runtime
|
|
76
|
+
prerelease: false
|
|
77
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
78
|
+
requirements:
|
|
79
|
+
- - ">="
|
|
80
|
+
- !ruby/object:Gem::Version
|
|
81
|
+
version: 1.2.0
|
|
82
|
+
- !ruby/object:Gem::Dependency
|
|
83
|
+
name: stringio
|
|
84
|
+
requirement: !ruby/object:Gem::Requirement
|
|
85
|
+
requirements:
|
|
86
|
+
- - ">="
|
|
87
|
+
- !ruby/object:Gem::Version
|
|
88
|
+
version: 3.2.0
|
|
89
|
+
type: :runtime
|
|
90
|
+
prerelease: false
|
|
91
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
92
|
+
requirements:
|
|
93
|
+
- - ">="
|
|
94
|
+
- !ruby/object:Gem::Version
|
|
95
|
+
version: 3.2.0
|
|
68
96
|
- !ruby/object:Gem::Dependency
|
|
69
97
|
name: rubocop
|
|
70
98
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -110,6 +138,7 @@ files:
|
|
|
110
138
|
- lib/disrb/logger.rb
|
|
111
139
|
- lib/disrb/message.rb
|
|
112
140
|
- lib/disrb/user.rb
|
|
141
|
+
- lib/version.rb
|
|
113
142
|
homepage: https://github.com/hoovad/discord.rb
|
|
114
143
|
licenses:
|
|
115
144
|
- MIT
|