discordrb 3.4.3 → 3.6.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/.devcontainer/Dockerfile +13 -0
- data/.devcontainer/devcontainer.json +29 -0
- data/.devcontainer/postcreate.sh +4 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
- data/.github/workflows/ci.yml +78 -0
- data/.github/workflows/codeql.yml +65 -0
- data/.github/workflows/deploy.yml +54 -0
- data/.github/workflows/release.yml +45 -0
- data/.markdownlint.json +4 -0
- data/.rubocop.yml +58 -2
- data/CHANGELOG.md +485 -225
- data/LICENSE.txt +1 -1
- data/README.md +38 -26
- data/discordrb-webhooks.gemspec +4 -1
- data/discordrb.gemspec +18 -10
- data/lib/discordrb/api/application.rb +278 -0
- data/lib/discordrb/api/channel.rb +222 -18
- data/lib/discordrb/api/interaction.rb +63 -0
- data/lib/discordrb/api/invite.rb +2 -2
- data/lib/discordrb/api/server.rb +123 -66
- data/lib/discordrb/api/user.rb +20 -5
- data/lib/discordrb/api/webhook.rb +72 -0
- data/lib/discordrb/api.rb +35 -25
- data/lib/discordrb/bot.rb +437 -66
- data/lib/discordrb/cache.rb +41 -22
- data/lib/discordrb/commands/command_bot.rb +13 -21
- data/lib/discordrb/commands/container.rb +1 -1
- data/lib/discordrb/commands/parser.rb +7 -7
- data/lib/discordrb/commands/rate_limiter.rb +1 -1
- data/lib/discordrb/container.rb +178 -3
- data/lib/discordrb/data/activity.rb +1 -1
- data/lib/discordrb/data/application.rb +1 -0
- data/lib/discordrb/data/attachment.rb +38 -3
- data/lib/discordrb/data/audit_logs.rb +3 -3
- data/lib/discordrb/data/avatar_decoration.rb +26 -0
- data/lib/discordrb/data/call.rb +22 -0
- data/lib/discordrb/data/channel.rb +299 -30
- data/lib/discordrb/data/collectibles.rb +45 -0
- data/lib/discordrb/data/component.rb +229 -0
- data/lib/discordrb/data/embed.rb +10 -3
- data/lib/discordrb/data/emoji.rb +20 -1
- data/lib/discordrb/data/integration.rb +45 -3
- data/lib/discordrb/data/interaction.rb +937 -0
- data/lib/discordrb/data/invite.rb +1 -1
- data/lib/discordrb/data/member.rb +236 -44
- data/lib/discordrb/data/message.rb +278 -51
- data/lib/discordrb/data/overwrite.rb +15 -7
- data/lib/discordrb/data/primary_server.rb +60 -0
- data/lib/discordrb/data/profile.rb +2 -7
- data/lib/discordrb/data/reaction.rb +2 -1
- data/lib/discordrb/data/recipient.rb +1 -1
- data/lib/discordrb/data/role.rb +204 -18
- data/lib/discordrb/data/server.rb +194 -118
- data/lib/discordrb/data/server_preview.rb +68 -0
- data/lib/discordrb/data/snapshot.rb +110 -0
- data/lib/discordrb/data/user.rb +132 -12
- data/lib/discordrb/data/voice_region.rb +1 -0
- data/lib/discordrb/data/webhook.rb +99 -9
- data/lib/discordrb/data.rb +9 -0
- data/lib/discordrb/errors.rb +47 -3
- data/lib/discordrb/events/await.rb +1 -1
- data/lib/discordrb/events/channels.rb +38 -1
- data/lib/discordrb/events/generic.rb +2 -0
- data/lib/discordrb/events/guilds.rb +6 -1
- data/lib/discordrb/events/interactions.rb +575 -0
- data/lib/discordrb/events/invites.rb +2 -0
- data/lib/discordrb/events/members.rb +19 -2
- data/lib/discordrb/events/message.rb +42 -8
- data/lib/discordrb/events/presence.rb +23 -14
- data/lib/discordrb/events/raw.rb +1 -0
- data/lib/discordrb/events/reactions.rb +2 -1
- data/lib/discordrb/events/roles.rb +2 -0
- data/lib/discordrb/events/threads.rb +100 -0
- data/lib/discordrb/events/typing.rb +1 -0
- data/lib/discordrb/events/voice_server_update.rb +1 -0
- data/lib/discordrb/events/voice_state_update.rb +1 -0
- data/lib/discordrb/events/webhooks.rb +1 -0
- data/lib/discordrb/gateway.rb +57 -28
- data/lib/discordrb/paginator.rb +3 -3
- data/lib/discordrb/permissions.rb +71 -35
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +2 -2
- data/lib/discordrb/voice/network.rb +18 -7
- data/lib/discordrb/voice/sodium.rb +3 -1
- data/lib/discordrb/voice/voice_bot.rb +3 -3
- data/lib/discordrb/webhooks.rb +2 -0
- data/lib/discordrb/websocket.rb +0 -10
- data/lib/discordrb.rb +54 -5
- metadata +87 -25
- data/.circleci/config.yml +0 -126
- data/.codeclimate.yml +0 -16
- data/.travis.yml +0 -32
- data/bin/travis_build_docs.sh +0 -17
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Discordrb
|
|
4
|
+
# A partial and immutable copy of a message that has been forwarded.
|
|
5
|
+
class Snapshot
|
|
6
|
+
# @return [Integer] the message type of the message snapshot.
|
|
7
|
+
attr_reader :type
|
|
8
|
+
|
|
9
|
+
# @return [String] the text content of the message snapshot.
|
|
10
|
+
attr_reader :content
|
|
11
|
+
|
|
12
|
+
# @return [Array<Embed>] the embeds attached to the message snapshot.
|
|
13
|
+
attr_reader :embeds
|
|
14
|
+
|
|
15
|
+
# @return [Array<Attachment>] the files attached to the message snapshot.
|
|
16
|
+
attr_reader :attachments
|
|
17
|
+
|
|
18
|
+
# @return [Time] the time at which the message snapshot was created.
|
|
19
|
+
attr_reader :created_at
|
|
20
|
+
|
|
21
|
+
# @return [Time, nil] the time at which the message snapshot was edited.
|
|
22
|
+
attr_reader :edited_at
|
|
23
|
+
|
|
24
|
+
# @return [Integer] the flags that have been set on the message snapshot.
|
|
25
|
+
attr_reader :flags
|
|
26
|
+
|
|
27
|
+
# @return [Array<User>] the users that were mentioned in the message snapshot.
|
|
28
|
+
attr_reader :mentions
|
|
29
|
+
|
|
30
|
+
# @return [Array<Component>] the interaction components associated with the message snapshot.
|
|
31
|
+
attr_reader :components
|
|
32
|
+
|
|
33
|
+
# @!visibility private
|
|
34
|
+
def initialize(data, bot)
|
|
35
|
+
@bot = bot
|
|
36
|
+
@type = data['type']
|
|
37
|
+
@flags = data['flags'] || 0
|
|
38
|
+
@content = data['content']
|
|
39
|
+
@mention_roles = data['mention_roles']&.map(&:resolve_id) || []
|
|
40
|
+
@embeds = data['embeds']&.map { |embed| Embed.new(embed, self) } || []
|
|
41
|
+
@attachments = data['attachments']&.map { |file| Attachment.new(file, self, bot) } || []
|
|
42
|
+
@created_at = data['timestamp'] ? Time.parse(data['timestamp']) : nil
|
|
43
|
+
@edited_at = data['edited_timestamp'] ? Time.parse(data['edited_timestamp']) : nil
|
|
44
|
+
@mentions = data['mentions']&.map { |mention| bot.ensure_user(mention) } || []
|
|
45
|
+
@components = data['components']&.map { |component| Components.from_data(component, bot) } || []
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
# Check whether the message snapshot has been edited.
|
|
49
|
+
# @return [true, false] whether the snapshot was edited or not.
|
|
50
|
+
def edited?
|
|
51
|
+
!@edited_at.nil?
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
# Check whether the message snapshot contains any custom emojis.
|
|
55
|
+
# @return [true, false] whether or not any emoji were used in the snapshot.
|
|
56
|
+
def emojis?
|
|
57
|
+
emojis.any?
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# Get the custom emojis that were used in the message snapshot.
|
|
61
|
+
# @return [Array<Emoji>] the emojis used in the message snapshot.
|
|
62
|
+
def emojis
|
|
63
|
+
return [] if @content.nil? || @content.empty?
|
|
64
|
+
|
|
65
|
+
@emojis ||= @bot.parse_mentions(@content).select { |parsed| parsed.is_a?(Emoji) }
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Get the roles that were mentioned in the message snapshot.
|
|
69
|
+
# @return [Array<Role>] the roles that were mentioned in the message snapshot.
|
|
70
|
+
# @note this can only resolve roles in servers that the bot has access to via {Bot#servers}.
|
|
71
|
+
def role_mentions
|
|
72
|
+
return [] if @mention_roles.empty?
|
|
73
|
+
|
|
74
|
+
return @role_mentions if @role_mentions
|
|
75
|
+
|
|
76
|
+
roles = @bot.servers.values.flat_map(&:roles)
|
|
77
|
+
|
|
78
|
+
@role_mentions = @mention_roles.filter_map { |id| roles.find { |r| r.id == id } }
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Get the buttons that were used in the message snapshot.
|
|
82
|
+
# @return [Array<Components::Button>] the button components used in the message snapshot.
|
|
83
|
+
def buttons
|
|
84
|
+
buttons = @components.flat_map do |component|
|
|
85
|
+
case component
|
|
86
|
+
when Components::Button
|
|
87
|
+
component
|
|
88
|
+
when Components::ActionRow
|
|
89
|
+
component.buttons
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
buttons.compact
|
|
94
|
+
end
|
|
95
|
+
|
|
96
|
+
# @see Discordrb::Message::FLAGS
|
|
97
|
+
Message::FLAGS.each do |name, value|
|
|
98
|
+
define_method("#{name}?") do
|
|
99
|
+
@flags.anybits?(value)
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# @see Discordrb::Message::TYPES
|
|
104
|
+
Message::TYPES.each do |name, value|
|
|
105
|
+
define_method("#{name}?") do
|
|
106
|
+
@type == value
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
end
|
data/lib/discordrb/data/user.rb
CHANGED
|
@@ -3,10 +3,33 @@
|
|
|
3
3
|
module Discordrb
|
|
4
4
|
# Mixin for the attributes users should have
|
|
5
5
|
module UserAttributes
|
|
6
|
+
# rubocop:disable Naming/VariableNumber
|
|
7
|
+
FLAGS = {
|
|
8
|
+
staff: 1 << 0,
|
|
9
|
+
partner: 1 << 1,
|
|
10
|
+
hypesquad: 1 << 2,
|
|
11
|
+
bug_hunter_level_1: 1 << 3,
|
|
12
|
+
hypesquad_online_house_1: 1 << 6,
|
|
13
|
+
hypesquad_online_house_2: 1 << 7,
|
|
14
|
+
hypesquad_online_house_3: 1 << 8,
|
|
15
|
+
premium_early_supporter: 1 << 9,
|
|
16
|
+
team_pseudo_user: 1 << 10,
|
|
17
|
+
bug_hunter_level_2: 1 << 14,
|
|
18
|
+
verified_bot: 1 << 16,
|
|
19
|
+
verified_developer: 1 << 17,
|
|
20
|
+
certified_moderator: 1 << 18,
|
|
21
|
+
bot_http_interactions: 1 << 19,
|
|
22
|
+
active_developer: 1 << 22
|
|
23
|
+
}.freeze
|
|
24
|
+
# rubocop:enable Naming/VariableNumber
|
|
25
|
+
|
|
6
26
|
# @return [String] this user's username
|
|
7
27
|
attr_reader :username
|
|
8
28
|
alias_method :name, :username
|
|
9
29
|
|
|
30
|
+
# @return [String, nil] this user's global name
|
|
31
|
+
attr_reader :global_name
|
|
32
|
+
|
|
10
33
|
# @return [String] this user's discriminator which is used internally to identify users with identical usernames.
|
|
11
34
|
attr_reader :discriminator
|
|
12
35
|
alias_method :discrim, :discriminator
|
|
@@ -17,10 +40,35 @@ module Discordrb
|
|
|
17
40
|
attr_reader :bot_account
|
|
18
41
|
alias_method :bot_account?, :bot_account
|
|
19
42
|
|
|
43
|
+
# @return [true, false] whether this is fake user for a webhook message
|
|
44
|
+
attr_reader :webhook_account
|
|
45
|
+
alias_method :webhook_account?, :webhook_account
|
|
46
|
+
alias_method :webhook?, :webhook_account
|
|
47
|
+
|
|
20
48
|
# @return [String] the ID of this user's current avatar, can be used to generate an avatar URL.
|
|
21
49
|
# @see #avatar_url
|
|
22
50
|
attr_accessor :avatar_id
|
|
23
51
|
|
|
52
|
+
# @return [true, false] whether the user is an offical Discord System user (part of the urgent message system).
|
|
53
|
+
attr_reader :system_account
|
|
54
|
+
alias_method :system_account?, :system_account
|
|
55
|
+
|
|
56
|
+
# @return [AvatarDecoration, nil] the current user's avatar decoration, or nil if the user doesn't have one.
|
|
57
|
+
attr_reader :avatar_decoration
|
|
58
|
+
|
|
59
|
+
# @return [Collectibles] the collectibles that this user has collected.
|
|
60
|
+
attr_reader :collectibles
|
|
61
|
+
|
|
62
|
+
# @return [PrimaryServer, nil] the server tag the user has adopted, or nil if the user doesn't have one displayed.
|
|
63
|
+
attr_reader :primary_server
|
|
64
|
+
alias_method :server_tag, :primary_server
|
|
65
|
+
|
|
66
|
+
# Utility function to get Discord's display name of a user not in server
|
|
67
|
+
# @return [String] the name the user displays as (global_name if they have one, username otherwise)
|
|
68
|
+
def display_name
|
|
69
|
+
global_name || username
|
|
70
|
+
end
|
|
71
|
+
|
|
24
72
|
# Utility function to mention users in messages
|
|
25
73
|
# @return [String] the mention code in the form of <@id>
|
|
26
74
|
def mention
|
|
@@ -29,18 +77,45 @@ module Discordrb
|
|
|
29
77
|
|
|
30
78
|
# Utility function to get Discord's distinct representation of a user, i.e. username + discriminator
|
|
31
79
|
# @return [String] distinct representation of user
|
|
80
|
+
# TODO: Maybe change this method again after discriminator removal ?
|
|
32
81
|
def distinct
|
|
33
|
-
|
|
82
|
+
if @discriminator && @discriminator != '0'
|
|
83
|
+
"#{@username}##{@discriminator}"
|
|
84
|
+
else
|
|
85
|
+
@username.to_s
|
|
86
|
+
end
|
|
34
87
|
end
|
|
35
88
|
|
|
36
89
|
# Utility function to get a user's avatar URL.
|
|
37
90
|
# @param format [String, nil] If `nil`, the URL will default to `webp` for static avatars, and will detect if the user has a `gif` avatar. You can otherwise specify one of `webp`, `jpg`, `png`, or `gif` to override this. Will always be PNG for default avatars.
|
|
38
91
|
# @return [String] the URL to the avatar image.
|
|
92
|
+
# TODO: Maybe change this method again after discriminator removal ?
|
|
39
93
|
def avatar_url(format = nil)
|
|
40
|
-
|
|
94
|
+
unless @avatar_id
|
|
95
|
+
return API::User.default_avatar(@discriminator, legacy: true) if @discriminator && @discriminator != '0'
|
|
96
|
+
|
|
97
|
+
return API::User.default_avatar(@id)
|
|
98
|
+
end
|
|
41
99
|
|
|
42
100
|
API::User.avatar_url(@id, @avatar_id, format)
|
|
43
101
|
end
|
|
102
|
+
|
|
103
|
+
# @return [Integer] the public flags on a user's account
|
|
104
|
+
attr_reader :public_flags
|
|
105
|
+
|
|
106
|
+
FLAGS.each do |name, value|
|
|
107
|
+
define_method("#{name}?") do
|
|
108
|
+
@public_flags.anybits?(value)
|
|
109
|
+
end
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
# Utility function to get a user's banner URL.
|
|
113
|
+
# @param format [String, nil] If `nil`, the URL will default to `png` for static banners and will detect if the user has a `gif` banner.
|
|
114
|
+
# You can otherwise specify one of `webp`, `jpg`, `png`, or `gif` to override this.
|
|
115
|
+
# @return [String, nil] the URL to the banner image or nil if the user doesn't have one.
|
|
116
|
+
def banner_url(format = nil)
|
|
117
|
+
API::User.banner_url(@id, banner_id, format) if banner_id
|
|
118
|
+
end
|
|
44
119
|
end
|
|
45
120
|
|
|
46
121
|
# User on Discord, including internal data like discriminators
|
|
@@ -63,17 +138,23 @@ module Discordrb
|
|
|
63
138
|
@bot = bot
|
|
64
139
|
|
|
65
140
|
@username = data['username']
|
|
141
|
+
@global_name = data['global_name']
|
|
66
142
|
@id = data['id'].to_i
|
|
67
143
|
@discriminator = data['discriminator']
|
|
68
144
|
@avatar_id = data['avatar']
|
|
69
|
-
@roles = {}
|
|
70
145
|
@activities = Discordrb::ActivitySet.new
|
|
71
|
-
|
|
72
|
-
@bot_account = false
|
|
73
|
-
@
|
|
146
|
+
@public_flags = data['public_flags'] || 0
|
|
147
|
+
@bot_account = data['bot'] || false
|
|
148
|
+
@webhook_account = data['_webhook'] || false
|
|
74
149
|
|
|
75
150
|
@status = :offline
|
|
76
151
|
@client_status = process_client_status(data['client_status'])
|
|
152
|
+
@banner_id = data['banner']
|
|
153
|
+
@system_account = data['system'] || false
|
|
154
|
+
@avatar_decoration = process_avatar_decoration(data['avatar_decoration_data'])
|
|
155
|
+
@collectibles = Collectibles.new(data['collectibles'] || {}, bot)
|
|
156
|
+
|
|
157
|
+
@primary_server = process_primary_server(data['primary_guild'] || {})
|
|
77
158
|
end
|
|
78
159
|
|
|
79
160
|
# Get a user's PM channel or send them a PM
|
|
@@ -109,13 +190,47 @@ module Discordrb
|
|
|
109
190
|
pm.send_file(file, caption: caption, filename: filename, spoiler: spoiler)
|
|
110
191
|
end
|
|
111
192
|
|
|
112
|
-
#
|
|
193
|
+
# @return [String, nil] the ID of this user's current banner, can be used to generate a banner URL.
|
|
194
|
+
# @see #banner_url
|
|
195
|
+
def banner_id
|
|
196
|
+
@banner_id ||= JSON.parse(API::User.resolve(@bot.token, @id))['banner']
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Set the user's username
|
|
113
200
|
# @note for internal use only
|
|
114
201
|
# @!visibility private
|
|
115
202
|
def update_username(username)
|
|
116
203
|
@username = username
|
|
117
204
|
end
|
|
118
205
|
|
|
206
|
+
# Set the user's global_name
|
|
207
|
+
# @note For internal use only.
|
|
208
|
+
# @!visibility private
|
|
209
|
+
def update_global_name(global_name)
|
|
210
|
+
@global_name = global_name
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Set the user's avatar_decoration
|
|
214
|
+
# @note For internal use only.
|
|
215
|
+
# @!visibility private
|
|
216
|
+
def update_avatar_decoration(decoration)
|
|
217
|
+
@avatar_decoration = process_avatar_decoration(decoration)
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
# Set the user's collectibles
|
|
221
|
+
# @note For internal use only.
|
|
222
|
+
# @!visibility private
|
|
223
|
+
def update_collectibles(collectibles)
|
|
224
|
+
@collectibles = Collectibles.new(collectibles || {}, @bot)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Set the user's primary server
|
|
228
|
+
# @note For internal use only.
|
|
229
|
+
# @!visibility private
|
|
230
|
+
def update_primary_server(server)
|
|
231
|
+
@primary_server = process_primary_server(server || {})
|
|
232
|
+
end
|
|
233
|
+
|
|
119
234
|
# Set the user's presence data
|
|
120
235
|
# @note for internal use only
|
|
121
236
|
# @!visibility private
|
|
@@ -154,14 +269,19 @@ module Discordrb
|
|
|
154
269
|
@bot.profile.id == @id
|
|
155
270
|
end
|
|
156
271
|
|
|
157
|
-
#
|
|
158
|
-
def
|
|
159
|
-
|
|
272
|
+
# @!visibility private
|
|
273
|
+
def process_client_status(client_status)
|
|
274
|
+
(client_status || {}).to_h { |k, v| [k.to_sym, v.to_sym] }
|
|
275
|
+
end
|
|
276
|
+
|
|
277
|
+
# @!visibility private
|
|
278
|
+
def process_avatar_decoration(decoration)
|
|
279
|
+
decoration ? AvatarDecoration.new(decoration, @bot) : nil
|
|
160
280
|
end
|
|
161
281
|
|
|
162
282
|
# @!visibility private
|
|
163
|
-
def
|
|
164
|
-
(
|
|
283
|
+
def process_primary_server(server)
|
|
284
|
+
PrimaryServer.new(server, @bot) if server['identity_enabled']
|
|
165
285
|
end
|
|
166
286
|
|
|
167
287
|
# @!method offline?
|
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# frozen_string_literal: true
|
|
2
2
|
|
|
3
|
+
require 'discordrb/webhooks/builder'
|
|
4
|
+
require 'discordrb/webhooks/view'
|
|
5
|
+
|
|
3
6
|
module Discordrb
|
|
4
7
|
# A webhook on a server channel
|
|
5
8
|
class Webhook
|
|
@@ -28,6 +31,7 @@ module Discordrb
|
|
|
28
31
|
# @return [Member, User, nil] the user object of the owner or nil if the webhook was requested using the token.
|
|
29
32
|
attr_reader :owner
|
|
30
33
|
|
|
34
|
+
# @!visibility private
|
|
31
35
|
def initialize(data, bot)
|
|
32
36
|
@bot = bot
|
|
33
37
|
|
|
@@ -81,13 +85,13 @@ module Discordrb
|
|
|
81
85
|
def update(data)
|
|
82
86
|
# Only pass a value for avatar if the key is defined as sending nil will delete the
|
|
83
87
|
data[:avatar] = avatarise(data[:avatar]) if data.key?(:avatar)
|
|
84
|
-
data[:channel_id] = data[:channel]
|
|
88
|
+
data[:channel_id] = data[:channel]&.resolve_id
|
|
85
89
|
data.delete(:channel)
|
|
86
|
-
update_webhook(data)
|
|
90
|
+
update_webhook(**data)
|
|
87
91
|
end
|
|
88
92
|
|
|
89
93
|
# Deletes the webhook.
|
|
90
|
-
# @param reason [String] The reason the
|
|
94
|
+
# @param reason [String] The reason the webhook is being deleted.
|
|
91
95
|
def delete(reason = nil)
|
|
92
96
|
if token?
|
|
93
97
|
API::Webhook.token_delete_webhook(@token, @id, reason)
|
|
@@ -96,10 +100,100 @@ module Discordrb
|
|
|
96
100
|
end
|
|
97
101
|
end
|
|
98
102
|
|
|
103
|
+
# Execute a webhook.
|
|
104
|
+
# @param content [String] The content of the message. May be 2000 characters long at most.
|
|
105
|
+
# @param username [String] The username the webhook will display as. If this is not set, the default username set in the webhook's settings.
|
|
106
|
+
# @param avatar_url [String] The URL of an image file to be used as an avatar. If this is not set, the default avatar from the webhook's
|
|
107
|
+
# @param tts [true, false] Whether this message should use TTS or not. By default, it doesn't.
|
|
108
|
+
# @param file [File] File to be sent together with the message. Mutually exclusive with embeds; a webhook message can contain
|
|
109
|
+
# either a file to be sent or embeds.
|
|
110
|
+
# @param embeds [Array<Webhooks::Embed, Hash>] Embeds to attach to this message.
|
|
111
|
+
# @param allowed_mentions [AllowedMentions, Hash] Mentions that are allowed to ping in the `content`.
|
|
112
|
+
# @param wait [true, false] Whether Discord should wait for the message to be successfully received by clients, or
|
|
113
|
+
# whether it should return immediately after sending the message. If `true` a {Message} object will be returned.
|
|
114
|
+
# @yield [builder] Gives the builder to the block to add additional steps, or to do the entire building process.
|
|
115
|
+
# @yieldparam builder [Builder] The builder given as a parameter which is used as the initial step to start from.
|
|
116
|
+
# @example Execute the webhook with kwargs
|
|
117
|
+
# client.execute(
|
|
118
|
+
# content: 'Testing',
|
|
119
|
+
# username: 'discordrb',
|
|
120
|
+
# embeds: [
|
|
121
|
+
# { timestamp: Time.now.iso8601, title: 'testing', image: { url: 'https://i.imgur.com/PcMltU7.jpg' } }
|
|
122
|
+
# ])
|
|
123
|
+
# @example Execute the webhook with an already existing builder
|
|
124
|
+
# builder = Discordrb::Webhooks::Builder.new # ...
|
|
125
|
+
# client.execute(builder)
|
|
126
|
+
# @example Execute the webhook by building a new message
|
|
127
|
+
# client.execute do |builder|
|
|
128
|
+
# builder.content = 'Testing'
|
|
129
|
+
# builder.username = 'discordrb'
|
|
130
|
+
# builder.add_embed do |embed|
|
|
131
|
+
# embed.timestamp = Time.now
|
|
132
|
+
# embed.title = 'Testing'
|
|
133
|
+
# embed.image = Discordrb::Webhooks::EmbedImage.new(url: 'https://i.imgur.com/PcMltU7.jpg')
|
|
134
|
+
# end
|
|
135
|
+
# end
|
|
136
|
+
# @return [Message, nil] If `wait` is `true`, a {Message} will be returned. Otherwise this method will return `nil`.
|
|
137
|
+
# @note This is only available to webhooks with publically exposed tokens. This excludes channel follow webhooks and webhooks retrieved
|
|
138
|
+
# via the audit log.
|
|
139
|
+
def execute(content: nil, username: nil, avatar_url: nil, tts: nil, file: nil, embeds: nil, allowed_mentions: nil, wait: true, builder: nil, components: nil)
|
|
140
|
+
raise Discordrb::Errors::UnauthorizedWebhook unless @token
|
|
141
|
+
|
|
142
|
+
params = { content: content, username: username, avatar_url: avatar_url, tts: tts, file: file, embeds: embeds, allowed_mentions: allowed_mentions }
|
|
143
|
+
|
|
144
|
+
builder ||= Webhooks::Builder.new
|
|
145
|
+
view = Webhooks::View.new
|
|
146
|
+
|
|
147
|
+
yield(builder, view) if block_given?
|
|
148
|
+
|
|
149
|
+
data = builder.to_json_hash.merge(params.compact)
|
|
150
|
+
components ||= view
|
|
151
|
+
|
|
152
|
+
resp = API::Webhook.token_execute_webhook(@token, @id, wait, data[:content], data[:username], data[:avatar_url], data[:tts], data[:file], data[:embeds], data[:allowed_mentions], nil, components.to_a)
|
|
153
|
+
|
|
154
|
+
Message.new(JSON.parse(resp), @bot) if wait
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# Delete a message created by this webhook.
|
|
158
|
+
# @param message [Message, String, Integer] The ID of the message to delete.
|
|
159
|
+
def delete_message(message)
|
|
160
|
+
raise Discordrb::Errors::UnauthorizedWebhook unless @token
|
|
161
|
+
|
|
162
|
+
API::Webhook.token_delete_message(@token, @id, message.resolve_id)
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
# Edit a message created by this webhook.
|
|
166
|
+
# @param message [Message, String, Integer] The ID of the message to edit.
|
|
167
|
+
# @param content [String] The content of the message. May be 2000 characters long at most.
|
|
168
|
+
# @param embeds [Array<Webhooks::Embed, Hash>] Embeds to be attached to the message.
|
|
169
|
+
# @param allowed_mentions [AllowedMentions, Hash] Mentions that are allowed to ping in the `content`.
|
|
170
|
+
# @param builder [Builder, nil] The builder to start out with, or nil if one should be created anew.
|
|
171
|
+
# @yield [builder] Gives the builder to the block to add additional steps, or to do the entire building process.
|
|
172
|
+
# @yieldparam builder [Webhooks::Builder] The builder given as a parameter which is used as the initial step to start from.
|
|
173
|
+
# @return [Message] The updated message.
|
|
174
|
+
# @param components [View, Array<Hash>] Interaction components to associate with this message.
|
|
175
|
+
# @note When editing `allowed_mentions`, it will update visually in the client but not alert the user with a notification.
|
|
176
|
+
def edit_message(message, content: nil, embeds: nil, allowed_mentions: nil, builder: nil, components: nil)
|
|
177
|
+
raise Discordrb::Errors::UnauthorizedWebhook unless @token
|
|
178
|
+
|
|
179
|
+
params = { content: content, embeds: embeds, allowed_mentions: allowed_mentions }.compact
|
|
180
|
+
|
|
181
|
+
builder ||= Webhooks::Builder.new
|
|
182
|
+
view ||= Webhooks::View.new
|
|
183
|
+
|
|
184
|
+
yield(builder, view) if block_given?
|
|
185
|
+
|
|
186
|
+
data = builder.to_json_hash.merge(params.compact)
|
|
187
|
+
components ||= view
|
|
188
|
+
|
|
189
|
+
resp = API::Webhook.token_edit_message(@token, @id, message.resolve_id, data[:content], data[:embeds], data[:allowed_mentions], components.to_a)
|
|
190
|
+
Message.new(JSON.parse(resp), @bot)
|
|
191
|
+
end
|
|
192
|
+
|
|
99
193
|
# Utility function to get a webhook's avatar URL.
|
|
100
194
|
# @return [String] the URL to the avatar image
|
|
101
195
|
def avatar_url
|
|
102
|
-
return API::User.default_avatar unless @avatar
|
|
196
|
+
return API::User.default_avatar(@id) unless @avatar
|
|
103
197
|
|
|
104
198
|
API::User.avatar_url(@id, @avatar)
|
|
105
199
|
end
|
|
@@ -118,11 +212,7 @@ module Discordrb
|
|
|
118
212
|
private
|
|
119
213
|
|
|
120
214
|
def avatarise(avatar)
|
|
121
|
-
|
|
122
|
-
"data:image/jpg;base64,#{Base64.strict_encode64(avatar.read)}"
|
|
123
|
-
else
|
|
124
|
-
avatar
|
|
125
|
-
end
|
|
215
|
+
avatar.respond_to?(:read) ? Discordrb.encode64(avatar) : avatar
|
|
126
216
|
end
|
|
127
217
|
|
|
128
218
|
def update_internal(data)
|
data/lib/discordrb/data.rb
CHANGED
|
@@ -12,6 +12,7 @@ require 'discordrb/api/invite'
|
|
|
12
12
|
require 'discordrb/api/user'
|
|
13
13
|
require 'discordrb/api/webhook'
|
|
14
14
|
require 'discordrb/webhooks/embeds'
|
|
15
|
+
require 'discordrb/webhooks/view'
|
|
15
16
|
require 'discordrb/paginator'
|
|
16
17
|
require 'time'
|
|
17
18
|
require 'base64'
|
|
@@ -37,3 +38,11 @@ require 'discordrb/data/integration'
|
|
|
37
38
|
require 'discordrb/data/server'
|
|
38
39
|
require 'discordrb/data/webhook'
|
|
39
40
|
require 'discordrb/data/audit_logs'
|
|
41
|
+
require 'discordrb/data/interaction'
|
|
42
|
+
require 'discordrb/data/component'
|
|
43
|
+
require 'discordrb/data/avatar_decoration'
|
|
44
|
+
require 'discordrb/data/collectibles'
|
|
45
|
+
require 'discordrb/data/primary_server'
|
|
46
|
+
require 'discordrb/data/server_preview'
|
|
47
|
+
require 'discordrb/data/call'
|
|
48
|
+
require 'discordrb/data/snapshot'
|
data/lib/discordrb/errors.rb
CHANGED
|
@@ -20,6 +20,9 @@ module Discordrb
|
|
|
20
20
|
# Raised when the bot gets a HTTP 502 error, which is usually caused by Cloudflare.
|
|
21
21
|
class CloudflareError < RuntimeError; end
|
|
22
22
|
|
|
23
|
+
# Raised when using a webhook method without an associated token.
|
|
24
|
+
class UnauthorizedWebhook < RuntimeError; end
|
|
25
|
+
|
|
23
26
|
# Generic class for errors denoted by API error codes
|
|
24
27
|
class CodeError < RuntimeError
|
|
25
28
|
class << self
|
|
@@ -29,8 +32,11 @@ module Discordrb
|
|
|
29
32
|
|
|
30
33
|
# Create a new error with a particular message (the code should be defined by the class instance variable)
|
|
31
34
|
# @param message [String] the message to use
|
|
32
|
-
|
|
35
|
+
# @param errors [Hash] API errors
|
|
36
|
+
def initialize(message, errors = nil)
|
|
33
37
|
@message = message
|
|
38
|
+
|
|
39
|
+
@errors = errors ? flatten_errors(errors) : []
|
|
34
40
|
end
|
|
35
41
|
|
|
36
42
|
# @return [Integer] The error code represented by this error.
|
|
@@ -38,15 +44,50 @@ module Discordrb
|
|
|
38
44
|
self.class.code
|
|
39
45
|
end
|
|
40
46
|
|
|
47
|
+
# @return [String] A message including the message and flattened errors.
|
|
48
|
+
def full_message(*)
|
|
49
|
+
error_list = @errors.collect { |err| "\t- #{err}" }
|
|
50
|
+
|
|
51
|
+
"#{@message}\n#{error_list.join("\n")}"
|
|
52
|
+
end
|
|
53
|
+
|
|
41
54
|
# @return [String] This error's represented message
|
|
42
55
|
attr_reader :message
|
|
56
|
+
|
|
57
|
+
# @return [Hash] More precise errors
|
|
58
|
+
attr_reader :errors
|
|
59
|
+
|
|
60
|
+
private
|
|
61
|
+
|
|
62
|
+
# @!visibility hidden
|
|
63
|
+
# Flattens errors into a more easily read format.
|
|
64
|
+
# @example Flattening errors of a bad field
|
|
65
|
+
# flatten_errors(data['errors'])
|
|
66
|
+
# # => ["embed.fields[0].name: This field is required", "embed.fields[0].value: This field is required"]
|
|
67
|
+
def flatten_errors(err, prev_key = nil)
|
|
68
|
+
err.collect do |key, sub_err|
|
|
69
|
+
if prev_key
|
|
70
|
+
key = /\A\d+\Z/.match?(key) ? "#{prev_key}[#{key}]" : "#{prev_key}.#{key}"
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
if (errs = sub_err['_errors'])
|
|
74
|
+
"#{key}: #{errs.map { |e| e['message'] }.join(' ')}"
|
|
75
|
+
elsif sub_err['message'] || sub_err['code']
|
|
76
|
+
"#{"#{sub_err['code']}: " if sub_err['code']}#{err_msg}"
|
|
77
|
+
elsif sub_err.is_a? String
|
|
78
|
+
sub_err
|
|
79
|
+
else
|
|
80
|
+
flatten_errors(sub_err, key)
|
|
81
|
+
end
|
|
82
|
+
end.flatten
|
|
83
|
+
end
|
|
43
84
|
end
|
|
44
85
|
|
|
45
86
|
# Create a new code error class
|
|
46
87
|
# rubocop:disable Naming/MethodName
|
|
47
88
|
def self.Code(code)
|
|
48
89
|
classy = Class.new(CodeError)
|
|
49
|
-
classy.instance_variable_set(
|
|
90
|
+
classy.instance_variable_set(:@code, code)
|
|
50
91
|
|
|
51
92
|
@code_classes ||= {}
|
|
52
93
|
@code_classes[code] = classy
|
|
@@ -58,7 +99,7 @@ module Discordrb
|
|
|
58
99
|
# @param code [Integer] The code to check
|
|
59
100
|
# @return [Class] the error class for the given code
|
|
60
101
|
def self.error_class_for(code)
|
|
61
|
-
@code_classes[code]
|
|
102
|
+
@code_classes[code] || Code(code)
|
|
62
103
|
end
|
|
63
104
|
|
|
64
105
|
# Used when Discord doesn't provide a more specific code
|
|
@@ -133,6 +174,9 @@ module Discordrb
|
|
|
133
174
|
# Unauthorized
|
|
134
175
|
Unauthorized = Unauthorised = Code(40_001)
|
|
135
176
|
|
|
177
|
+
# Unable to bulk ban any users
|
|
178
|
+
UnableToBulkBanUsers = Code(500_000)
|
|
179
|
+
|
|
136
180
|
# Missing Access
|
|
137
181
|
MissingAccess = Code(50_001)
|
|
138
182
|
|
|
@@ -29,9 +29,10 @@ module Discordrb::Events
|
|
|
29
29
|
# @see Channel#server
|
|
30
30
|
delegate :name, :server, :type, :owner_id, :recipients, :topic, :user_limit, :position, :permission_overwrites, to: :channel
|
|
31
31
|
|
|
32
|
+
# @!visibility private
|
|
32
33
|
def initialize(data, bot)
|
|
33
34
|
@bot = bot
|
|
34
|
-
@channel = bot.channel(data['id'].to_i)
|
|
35
|
+
@channel = data.is_a?(Discordrb::Channel) ? data : bot.channel(data['id'].to_i)
|
|
35
36
|
end
|
|
36
37
|
end
|
|
37
38
|
|
|
@@ -83,6 +84,7 @@ module Discordrb::Events
|
|
|
83
84
|
# @return [Integer, nil] the channel's owner ID if this is a group channel
|
|
84
85
|
attr_reader :owner_id
|
|
85
86
|
|
|
87
|
+
# @!visibility private
|
|
86
88
|
def initialize(data, bot)
|
|
87
89
|
@bot = bot
|
|
88
90
|
|
|
@@ -134,6 +136,7 @@ module Discordrb::Events
|
|
|
134
136
|
|
|
135
137
|
delegate :id, to: :recipient
|
|
136
138
|
|
|
139
|
+
# @!visibility private
|
|
137
140
|
def initialize(data, bot)
|
|
138
141
|
@bot = bot
|
|
139
142
|
|
|
@@ -168,6 +171,40 @@ module Discordrb::Events
|
|
|
168
171
|
end
|
|
169
172
|
end
|
|
170
173
|
|
|
174
|
+
# Raised when a message is pinned or unpinned.
|
|
175
|
+
class ChannelPinsUpdateEvent < Event
|
|
176
|
+
# @return [Time, nil] Time at which the most recent pinned message was pinned.
|
|
177
|
+
attr_reader :last_pin_timestamp
|
|
178
|
+
|
|
179
|
+
# @return [Channel] The channel this event originates from.
|
|
180
|
+
attr_reader :channel
|
|
181
|
+
|
|
182
|
+
# @return [Server, nil] The server this event originates from.
|
|
183
|
+
attr_reader :server
|
|
184
|
+
|
|
185
|
+
# @!visibility private
|
|
186
|
+
def initialize(data, bot)
|
|
187
|
+
@bot = bot
|
|
188
|
+
|
|
189
|
+
@server = bot.server(data['guild_id']) if data['guild_id']
|
|
190
|
+
@channel = bot.channel(data['channel_id'])
|
|
191
|
+
@last_pin_timestamp = Time.iso8601(data['last_pin_timestamp']) if data['last_pin_timestamp']
|
|
192
|
+
end
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
# Event handler for ChannelPinsUpdateEvent.
|
|
196
|
+
class ChannelPinsUpdateEventHandler < EventHandler
|
|
197
|
+
def matches?(event)
|
|
198
|
+
# Check for the proper event type.
|
|
199
|
+
return false unless event.is_a? ChannelPinsUpdateEvent
|
|
200
|
+
|
|
201
|
+
[
|
|
202
|
+
matches_all(@attributes[:server], event.server) { |a, e| a.resolve_id == e&.id },
|
|
203
|
+
matches_all(@attributes[:channel], event.channel) { |a, e| a.resolve_id == e.id }
|
|
204
|
+
].reduce(true, &:&)
|
|
205
|
+
end
|
|
206
|
+
end
|
|
207
|
+
|
|
171
208
|
# Raised when a user is added to a private channel
|
|
172
209
|
class ChannelRecipientAddEvent < ChannelRecipientEvent; end
|
|
173
210
|
|