discordrb 3.4.0 → 3.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.circleci/config.yml +44 -18
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
- data/.github/workflows/codeql.yml +65 -0
- data/.markdownlint.json +4 -0
- data/.rubocop.yml +8 -2
- data/CHANGELOG.md +419 -222
- data/LICENSE.txt +1 -1
- data/README.md +37 -25
- data/discordrb-webhooks.gemspec +4 -1
- data/discordrb.gemspec +9 -6
- data/lib/discordrb/api/application.rb +202 -0
- data/lib/discordrb/api/channel.rb +182 -11
- data/lib/discordrb/api/interaction.rb +54 -0
- data/lib/discordrb/api/invite.rb +2 -2
- data/lib/discordrb/api/server.rb +42 -19
- data/lib/discordrb/api/user.rb +9 -3
- data/lib/discordrb/api/webhook.rb +57 -0
- data/lib/discordrb/api.rb +19 -5
- data/lib/discordrb/bot.rb +328 -33
- data/lib/discordrb/cache.rb +27 -22
- data/lib/discordrb/commands/command_bot.rb +14 -7
- data/lib/discordrb/commands/container.rb +1 -1
- data/lib/discordrb/commands/parser.rb +2 -2
- data/lib/discordrb/commands/rate_limiter.rb +1 -1
- data/lib/discordrb/container.rb +132 -3
- data/lib/discordrb/data/activity.rb +8 -1
- data/lib/discordrb/data/attachment.rb +15 -0
- data/lib/discordrb/data/audit_logs.rb +3 -3
- data/lib/discordrb/data/channel.rb +167 -23
- data/lib/discordrb/data/component.rb +229 -0
- data/lib/discordrb/data/integration.rb +42 -3
- data/lib/discordrb/data/interaction.rb +800 -0
- data/lib/discordrb/data/invite.rb +2 -2
- data/lib/discordrb/data/member.rb +108 -33
- data/lib/discordrb/data/message.rb +100 -20
- data/lib/discordrb/data/overwrite.rb +13 -7
- data/lib/discordrb/data/role.rb +58 -1
- data/lib/discordrb/data/server.rb +82 -80
- data/lib/discordrb/data/user.rb +69 -9
- data/lib/discordrb/data/webhook.rb +97 -4
- data/lib/discordrb/data.rb +3 -0
- data/lib/discordrb/errors.rb +44 -3
- data/lib/discordrb/events/channels.rb +1 -1
- data/lib/discordrb/events/interactions.rb +482 -0
- data/lib/discordrb/events/message.rb +9 -6
- data/lib/discordrb/events/presence.rb +21 -14
- data/lib/discordrb/events/reactions.rb +0 -1
- data/lib/discordrb/events/threads.rb +96 -0
- data/lib/discordrb/gateway.rb +30 -17
- data/lib/discordrb/permissions.rb +59 -34
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +13 -4
- 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.rb +37 -4
- metadata +53 -19
- data/.codeclimate.yml +0 -16
- data/.travis.yml +0 -32
- data/bin/travis_build_docs.sh +0 -17
data/lib/discordrb/data/role.rb
CHANGED
@@ -32,6 +32,42 @@ module Discordrb
|
|
32
32
|
# @return [Integer] the position of this role in the hierarchy
|
33
33
|
attr_reader :position
|
34
34
|
|
35
|
+
# @return [String, nil] The icon hash for this role.
|
36
|
+
attr_reader :icon
|
37
|
+
|
38
|
+
# @return [Tags, nil] The role tags
|
39
|
+
attr_reader :tags
|
40
|
+
|
41
|
+
# Wrapper for the role tags
|
42
|
+
class Tags
|
43
|
+
# @return [Integer, nil] The ID of the bot this role belongs to
|
44
|
+
attr_reader :bot_id
|
45
|
+
|
46
|
+
# @return [Integer, nil] The ID of the integration this role belongs to
|
47
|
+
attr_reader :integration_id
|
48
|
+
|
49
|
+
# @return [true, false] Whether this is the guild's Booster role
|
50
|
+
attr_reader :premium_subscriber
|
51
|
+
|
52
|
+
# @return [Integer, nil] The id of this role's subscription sku and listing
|
53
|
+
attr_reader :subscription_listing_id
|
54
|
+
|
55
|
+
# @return [true, false] Whether this role is available for purchase
|
56
|
+
attr_reader :available_for_purchase
|
57
|
+
|
58
|
+
# @return [true, false] Whether this role is a guild's linked role
|
59
|
+
attr_reader :guild_connections
|
60
|
+
|
61
|
+
def initialize(data)
|
62
|
+
@bot_id = data['bot_id']&.resolve_id
|
63
|
+
@integration_id = data['integration_id']&.resolve_id
|
64
|
+
@premium_subscriber = data.key?('premium_subscriber')
|
65
|
+
@subscription_listing_id = data['subscription_listing_id']&.resolve_id
|
66
|
+
@available_for_purchase = data.key?('available_for_purchase')
|
67
|
+
@guild_connections = data.key?('guild_connections')
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
35
71
|
# This class is used internally as a wrapper to a Role object that allows easy writing of permission data.
|
36
72
|
class RoleWriter
|
37
73
|
# @!visibility private
|
@@ -67,6 +103,10 @@ module Discordrb
|
|
67
103
|
@managed = data['managed']
|
68
104
|
|
69
105
|
@colour = ColourRGB.new(data['color'])
|
106
|
+
|
107
|
+
@icon = data['icon']
|
108
|
+
|
109
|
+
@tags = Tags.new(data['tags']) if data['tags']
|
70
110
|
end
|
71
111
|
|
72
112
|
# @return [String] a string that will mention this role, if it is mentionable.
|
@@ -92,6 +132,7 @@ module Discordrb
|
|
92
132
|
@colour = other.colour
|
93
133
|
@position = other.position
|
94
134
|
@managed = other.managed
|
135
|
+
@icon = other.icon
|
95
136
|
end
|
96
137
|
|
97
138
|
# Updates the data cache from a hash containing data
|
@@ -128,6 +169,20 @@ module Discordrb
|
|
128
169
|
update_role_data(colour: colour)
|
129
170
|
end
|
130
171
|
|
172
|
+
# Upload a role icon for servers with the ROLE_ICONS feature.
|
173
|
+
# @param file [File]
|
174
|
+
def icon=(file)
|
175
|
+
update_role_data(icon: file)
|
176
|
+
end
|
177
|
+
|
178
|
+
# @param format ['webp', 'png', 'jpeg']
|
179
|
+
# @return [String] URL to the icon on Discord's CDN.
|
180
|
+
def icon_url(format = 'webp')
|
181
|
+
return nil unless @icon
|
182
|
+
|
183
|
+
Discordrb::API.role_icon_url(@id, @icon, format)
|
184
|
+
end
|
185
|
+
|
131
186
|
alias_method :color=, :colour=
|
132
187
|
|
133
188
|
# Changes this role's permissions to a fixed bitfield. This allows setting multiple permissions at once with just
|
@@ -184,7 +239,9 @@ module Discordrb
|
|
184
239
|
(new_data[:colour] || @colour).combined,
|
185
240
|
new_data[:hoist].nil? ? @hoist : new_data[:hoist],
|
186
241
|
new_data[:mentionable].nil? ? @mentionable : new_data[:mentionable],
|
187
|
-
new_data[:permissions] || @permissions.bits
|
242
|
+
new_data[:permissions] || @permissions.bits,
|
243
|
+
nil,
|
244
|
+
new_data.key?(:icon) ? new_data[:icon] : :undef)
|
188
245
|
update_data(new_data)
|
189
246
|
end
|
190
247
|
end
|
@@ -66,28 +66,15 @@ module Discordrb
|
|
66
66
|
@bot = bot
|
67
67
|
@owner_id = data['owner_id'].to_i
|
68
68
|
@id = data['id'].to_i
|
69
|
-
|
70
|
-
process_channels(data['channels'])
|
71
|
-
update_data(data)
|
72
|
-
|
73
|
-
@large = data['large']
|
74
|
-
@member_count = data['member_count']
|
75
|
-
@splash_id = nil
|
76
|
-
@banner_id = nil
|
77
|
-
@features = data['features'].map { |element| element.downcase.to_sym }
|
78
69
|
@members = {}
|
79
70
|
@voice_states = {}
|
80
71
|
@emoji = {}
|
81
72
|
|
82
|
-
|
83
|
-
|
84
|
-
process_members(data['members'])
|
85
|
-
process_presences(data['presences'])
|
86
|
-
process_voice_states(data['voice_states'])
|
73
|
+
process_channels(data['channels'])
|
74
|
+
update_data(data)
|
87
75
|
|
88
76
|
# Whether this server's members have been chunked (resolved using op 8 and GUILD_MEMBERS_CHUNK) yet
|
89
77
|
@chunked = false
|
90
|
-
@processed_chunk_members = 0
|
91
78
|
|
92
79
|
@booster_count = data['premium_subscription_count'] || 0
|
93
80
|
@boost_level = data['premium_tier']
|
@@ -143,10 +130,15 @@ module Discordrb
|
|
143
130
|
end
|
144
131
|
|
145
132
|
# @return [Array<Member>] an array of all the members on this server.
|
133
|
+
# @raise [RuntimeError] if the bot was not started with the :server_member intent
|
146
134
|
def members
|
147
135
|
return @members.values if @chunked
|
148
136
|
|
149
137
|
@bot.debug("Members for server #{@id} not chunked yet - initiating")
|
138
|
+
|
139
|
+
# If the SERVER_MEMBERS intent flag isn't set, the gateway won't respond when we ask for members.
|
140
|
+
raise 'The :server_members intent is required to get server members' if (@bot.gateway.intents & INTENTS[:server_members]).zero?
|
141
|
+
|
150
142
|
@bot.request_chunks(@id)
|
151
143
|
sleep 0.05 until @chunked
|
152
144
|
@members.values
|
@@ -189,78 +181,73 @@ module Discordrb
|
|
189
181
|
AuditLogs.new(self, @bot, JSON.parse(API::Server.audit_logs(@bot.token, @id, limit, user, action, before)))
|
190
182
|
end
|
191
183
|
|
192
|
-
# Cache @
|
184
|
+
# Cache @widget
|
193
185
|
# @note For internal use only
|
194
186
|
# @!visibility private
|
195
|
-
def
|
196
|
-
data = JSON.parse(API::Server.
|
197
|
-
@
|
198
|
-
@
|
187
|
+
def cache_widget_data
|
188
|
+
data = JSON.parse(API::Server.widget(@bot.token, @id))
|
189
|
+
@widget_enabled = data['enabled']
|
190
|
+
@widget_channel_id = data['channel_id']
|
199
191
|
end
|
200
192
|
|
201
193
|
# @return [true, false] whether or not the server has widget enabled
|
202
|
-
def
|
203
|
-
|
204
|
-
@
|
194
|
+
def widget_enabled?
|
195
|
+
cache_widget_data if @widget_enabled.nil?
|
196
|
+
@widget_enabled
|
205
197
|
end
|
206
|
-
alias_method :
|
207
|
-
alias_method :
|
208
|
-
alias_method :embed?, :
|
198
|
+
alias_method :widget?, :widget_enabled?
|
199
|
+
alias_method :embed_enabled, :widget_enabled?
|
200
|
+
alias_method :embed?, :widget_enabled?
|
209
201
|
|
210
|
-
# @return [Channel, nil] the channel the server
|
211
|
-
def
|
212
|
-
|
213
|
-
@bot.channel(@
|
202
|
+
# @return [Channel, nil] the channel the server widget will make an invite for.
|
203
|
+
def widget_channel
|
204
|
+
cache_widget_data if @widget_enabled.nil?
|
205
|
+
@bot.channel(@widget_channel_id) if @widget_channel_id
|
214
206
|
end
|
215
|
-
alias_method :
|
207
|
+
alias_method :embed_channel, :widget_channel
|
216
208
|
|
217
|
-
# Sets whether this server's
|
209
|
+
# Sets whether this server's widget is enabled
|
218
210
|
# @param value [true, false]
|
219
|
-
def
|
220
|
-
|
211
|
+
def widget_enabled=(value)
|
212
|
+
modify_widget(value, widget_channel)
|
221
213
|
end
|
214
|
+
alias_method :embed_enabled=, :widget_enabled=
|
222
215
|
|
223
|
-
|
224
|
-
|
225
|
-
# Sets whether this server's embed (widget) is enabled
|
216
|
+
# Sets whether this server's widget is enabled
|
226
217
|
# @param value [true, false]
|
227
218
|
# @param reason [String, nil] the reason to be shown in the audit log for this action
|
228
|
-
def
|
229
|
-
|
219
|
+
def set_widget_enabled(value, reason = nil)
|
220
|
+
modify_widget(value, widget_channel, reason)
|
230
221
|
end
|
222
|
+
alias_method :set_embed_enabled, :set_widget_enabled
|
231
223
|
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
def embed_channel=(channel)
|
237
|
-
modify_embed(embed?, channel)
|
224
|
+
# Changes the channel on the server's widget
|
225
|
+
# @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the widget
|
226
|
+
def widget_channel=(channel)
|
227
|
+
modify_widget(widget?, channel)
|
238
228
|
end
|
229
|
+
alias_method :embed_channel=, :widget_channel=
|
239
230
|
|
240
|
-
|
241
|
-
|
242
|
-
# Changes the channel on the server's embed (widget)
|
243
|
-
# @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the embed
|
231
|
+
# Changes the channel on the server's widget
|
232
|
+
# @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the widget
|
244
233
|
# @param reason [String, nil] the reason to be shown in the audit log for this action
|
245
|
-
def
|
246
|
-
|
234
|
+
def set_widget_channel(channel, reason = nil)
|
235
|
+
modify_widget(widget?, channel, reason)
|
247
236
|
end
|
237
|
+
alias_method :set_embed_channel, :set_widget_channel
|
248
238
|
|
249
|
-
|
250
|
-
|
251
|
-
#
|
252
|
-
# @param enabled [true, false] whether the embed (widget) is enabled
|
253
|
-
# @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the embed
|
239
|
+
# Changes the channel on the server's widget, and sets whether it is enabled.
|
240
|
+
# @param enabled [true, false] whether the widget is enabled
|
241
|
+
# @param channel [Channel, String, Integer] the channel, or its ID, to be referenced by the widget
|
254
242
|
# @param reason [String, nil] the reason to be shown in the audit log for this action
|
255
|
-
def
|
256
|
-
|
257
|
-
channel_id = channel ? channel.resolve_id : @
|
258
|
-
response = JSON.parse(API::Server.
|
259
|
-
@
|
260
|
-
@
|
243
|
+
def modify_widget(enabled, channel, reason = nil)
|
244
|
+
cache_widget_data if @widget_enabled.nil?
|
245
|
+
channel_id = channel ? channel.resolve_id : @widget_channel_id
|
246
|
+
response = JSON.parse(API::Server.modify_widget(@bot.token, @id, enabled, channel_id, reason))
|
247
|
+
@widget_enabled = response['enabled']
|
248
|
+
@widget_channel_id = response['channel_id']
|
261
249
|
end
|
262
|
-
|
263
|
-
alias_method :modify_widget, :modify_embed
|
250
|
+
alias_method :modify_embed, :modify_widget
|
264
251
|
|
265
252
|
# @param include_idle [true, false] Whether to count idle members as online.
|
266
253
|
# @param include_bots [true, false] Whether to include bot accounts in the count.
|
@@ -444,7 +431,7 @@ module Discordrb
|
|
444
431
|
# @!visibility private
|
445
432
|
def delete_member(user_id)
|
446
433
|
@members.delete(user_id)
|
447
|
-
@member_count -= 1
|
434
|
+
@member_count -= 1 unless @member_count <= 0
|
448
435
|
end
|
449
436
|
|
450
437
|
# Checks whether a member is cached
|
@@ -586,7 +573,7 @@ module Discordrb
|
|
586
573
|
# The amount of emoji the server can have, based on its current Nitro Boost Level.
|
587
574
|
# @return [Integer] the max amount of emoji
|
588
575
|
def max_emoji
|
589
|
-
case @
|
576
|
+
case @boost_level
|
590
577
|
when 1
|
591
578
|
100
|
592
579
|
when 2
|
@@ -598,9 +585,13 @@ module Discordrb
|
|
598
585
|
end
|
599
586
|
end
|
600
587
|
|
588
|
+
# Retrieve banned users from this server.
|
589
|
+
# @param limit [Integer] Number of users to return (up to maximum 1000, default 1000).
|
590
|
+
# @param before_id [Integer] Consider only users before given user id.
|
591
|
+
# @param after_id [Integer] Consider only users after given user id.
|
601
592
|
# @return [Array<ServerBan>] a list of banned users on this server and the reason they were banned.
|
602
|
-
def bans
|
603
|
-
response = JSON.parse(API::Server.bans(@bot.token, @id))
|
593
|
+
def bans(limit: nil, before_id: nil, after_id: nil)
|
594
|
+
response = JSON.parse(API::Server.bans(@bot.token, @id, limit, before_id, after_id))
|
604
595
|
response.map do |e|
|
605
596
|
ServerBan.new(self, User.new(e['user'], @bot), e['reason'])
|
606
597
|
end
|
@@ -628,11 +619,12 @@ module Discordrb
|
|
628
619
|
API::Server.remove_member(@bot.token, @id, user.resolve_id, reason)
|
629
620
|
end
|
630
621
|
|
631
|
-
# Forcibly moves a user into a different voice channel.
|
622
|
+
# Forcibly moves a user into a different voice channel.
|
623
|
+
# Only works if the bot has the permission needed and if the user is already connected to some voice channel on this server.
|
632
624
|
# @param user [User, String, Integer] The user to move.
|
633
|
-
# @param channel [Channel, String, Integer] The voice channel to move into.
|
625
|
+
# @param channel [Channel, String, Integer, nil] The voice channel to move into. (If nil, the user is disconnected from the voice channel)
|
634
626
|
def move(user, channel)
|
635
|
-
API::Server.update_member(@bot.token, @id, user.resolve_id, channel_id: channel
|
627
|
+
API::Server.update_member(@bot.token, @id, user.resolve_id, channel_id: channel&.resolve_id)
|
636
628
|
end
|
637
629
|
|
638
630
|
# Deletes this server. Be aware that this is permanent and impossible to undo, so be careful!
|
@@ -805,19 +797,16 @@ module Discordrb
|
|
805
797
|
# Processes a GUILD_MEMBERS_CHUNK packet, specifically the members field
|
806
798
|
# @note For internal use only
|
807
799
|
# @!visibility private
|
808
|
-
def process_chunk(members)
|
800
|
+
def process_chunk(members, chunk_index, chunk_count)
|
809
801
|
process_members(members)
|
810
|
-
@
|
811
|
-
LOGGER.debug("Processed one chunk on server #{@id} - length #{members.length}")
|
802
|
+
LOGGER.debug("Processed chunk #{chunk_index + 1}/#{chunk_count} server #{@id} - index #{chunk_index} - length #{members.length}")
|
812
803
|
|
813
|
-
|
814
|
-
return unless @processed_chunk_members == @member_count
|
804
|
+
return if chunk_index + 1 < chunk_count
|
815
805
|
|
816
806
|
LOGGER.debug("Finished chunking server #{@id}")
|
817
807
|
|
818
808
|
# Reset everything to normal
|
819
809
|
@chunked = true
|
820
|
-
@processed_chunk_members = 0
|
821
810
|
end
|
822
811
|
|
823
812
|
# @return [Channel, nil] the AFK voice channel of this server, or `nil` if none is set.
|
@@ -842,17 +831,30 @@ module Discordrb
|
|
842
831
|
|
843
832
|
afk_channel_id = new_data[:afk_channel_id] || new_data['afk_channel_id'] || @afk_channel
|
844
833
|
@afk_channel_id = afk_channel_id.nil? ? nil : afk_channel_id.resolve_id
|
845
|
-
|
846
|
-
@
|
834
|
+
widget_channel_id = new_data[:widget_channel_id] || new_data['widget_channel_id'] || @widget_channel
|
835
|
+
@widget_channel_id = widget_channel_id.nil? ? nil : widget_channel_id.resolve_id
|
847
836
|
system_channel_id = new_data[:system_channel_id] || new_data['system_channel_id'] || @system_channel
|
848
837
|
@system_channel_id = system_channel_id.nil? ? nil : system_channel_id.resolve_id
|
849
838
|
|
850
|
-
@
|
839
|
+
@widget_enabled = new_data[:widget_enabled] || new_data['widget_enabled']
|
851
840
|
@splash = new_data[:splash_id] || new_data['splash_id'] || @splash_id
|
852
841
|
|
853
842
|
@verification_level = new_data[:verification_level] || new_data['verification_level'] || @verification_level
|
854
843
|
@explicit_content_filter = new_data[:explicit_content_filter] || new_data['explicit_content_filter'] || @explicit_content_filter
|
855
844
|
@default_message_notifications = new_data[:default_message_notifications] || new_data['default_message_notifications'] || @default_message_notifications
|
845
|
+
|
846
|
+
@large = new_data.key?('large') ? new_data['large'] : @large
|
847
|
+
@member_count = new_data['member_count'] || @member_count || 0
|
848
|
+
@splash_id = new_data['splash'] || @splash_id
|
849
|
+
@banner_id = new_data['banner'] || @banner_id
|
850
|
+
@features = new_data['features'] ? new_data['features'].map { |element| element.downcase.to_sym } : @features || []
|
851
|
+
|
852
|
+
process_channels(new_data['channels']) if new_data['channels']
|
853
|
+
process_roles(new_data['roles']) if new_data['roles']
|
854
|
+
process_emoji(new_data['emojis']) if new_data['emojis']
|
855
|
+
process_members(new_data['members']) if new_data['members']
|
856
|
+
process_presences(new_data['presences']) if new_data['presences']
|
857
|
+
process_voice_states(new_data['voice_states']) if new_data['voice_states']
|
856
858
|
end
|
857
859
|
|
858
860
|
# Adds a channel to this server's cache
|
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,21 @@ 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
|
+
# Utility function to get Discord's display name of a user not in server
|
53
|
+
# @return [String] the name the user displays as (global_name if they have one, username otherwise)
|
54
|
+
def display_name
|
55
|
+
global_name || username
|
56
|
+
end
|
57
|
+
|
24
58
|
# Utility function to mention users in messages
|
25
59
|
# @return [String] the mention code in the form of <@id>
|
26
60
|
def mention
|
@@ -29,18 +63,37 @@ module Discordrb
|
|
29
63
|
|
30
64
|
# Utility function to get Discord's distinct representation of a user, i.e. username + discriminator
|
31
65
|
# @return [String] distinct representation of user
|
66
|
+
# TODO: Maybe change this method again after discriminator removal ?
|
32
67
|
def distinct
|
33
|
-
|
68
|
+
if @discriminator && @discriminator != '0'
|
69
|
+
"#{@username}##{@discriminator}"
|
70
|
+
else
|
71
|
+
@username.to_s
|
72
|
+
end
|
34
73
|
end
|
35
74
|
|
36
75
|
# Utility function to get a user's avatar URL.
|
37
76
|
# @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
77
|
# @return [String] the URL to the avatar image.
|
78
|
+
# TODO: Maybe change this method again after discriminator removal ?
|
39
79
|
def avatar_url(format = nil)
|
40
|
-
|
80
|
+
unless @avatar_id
|
81
|
+
return API::User.default_avatar(@discriminator, legacy: true) if @discriminator && @discriminator != '0'
|
82
|
+
|
83
|
+
return API::User.default_avatar(@id)
|
84
|
+
end
|
41
85
|
|
42
86
|
API::User.avatar_url(@id, @avatar_id, format)
|
43
87
|
end
|
88
|
+
|
89
|
+
# @return [Integer] the public flags on a user's account
|
90
|
+
attr_reader :public_flags
|
91
|
+
|
92
|
+
FLAGS.each do |name, value|
|
93
|
+
define_method("#{name}?") do
|
94
|
+
(@public_flags & value).positive?
|
95
|
+
end
|
96
|
+
end
|
44
97
|
end
|
45
98
|
|
46
99
|
# User on Discord, including internal data like discriminators
|
@@ -63,15 +116,20 @@ module Discordrb
|
|
63
116
|
@bot = bot
|
64
117
|
|
65
118
|
@username = data['username']
|
119
|
+
@global_name = data['global_name']
|
66
120
|
@id = data['id'].to_i
|
67
121
|
@discriminator = data['discriminator']
|
68
122
|
@avatar_id = data['avatar']
|
69
123
|
@roles = {}
|
70
124
|
@activities = Discordrb::ActivitySet.new
|
125
|
+
@public_flags = data['public_flags'] || 0
|
71
126
|
|
72
127
|
@bot_account = false
|
73
128
|
@bot_account = true if data['bot']
|
74
129
|
|
130
|
+
@webhook_account = false
|
131
|
+
@webhook_account = true if data['_webhook']
|
132
|
+
|
75
133
|
@status = :offline
|
76
134
|
@client_status = process_client_status(data['client_status'])
|
77
135
|
end
|
@@ -109,13 +167,20 @@ module Discordrb
|
|
109
167
|
pm.send_file(file, caption: caption, filename: filename, spoiler: spoiler)
|
110
168
|
end
|
111
169
|
|
112
|
-
# Set the user's
|
170
|
+
# Set the user's username
|
113
171
|
# @note for internal use only
|
114
172
|
# @!visibility private
|
115
173
|
def update_username(username)
|
116
174
|
@username = username
|
117
175
|
end
|
118
176
|
|
177
|
+
# Set the user's global_name
|
178
|
+
# @note For internal use only.
|
179
|
+
# @!visibility private
|
180
|
+
def update_global_name(global_name)
|
181
|
+
@global_name = global_name
|
182
|
+
end
|
183
|
+
|
119
184
|
# Set the user's presence data
|
120
185
|
# @note for internal use only
|
121
186
|
# @!visibility private
|
@@ -154,14 +219,9 @@ module Discordrb
|
|
154
219
|
@bot.profile.id == @id
|
155
220
|
end
|
156
221
|
|
157
|
-
# @return [true, false] whether this user is a fake user for a webhook message
|
158
|
-
def webhook?
|
159
|
-
@discriminator == Message::ZERO_DISCRIM
|
160
|
-
end
|
161
|
-
|
162
222
|
# @!visibility private
|
163
223
|
def process_client_status(client_status)
|
164
|
-
(client_status || {}).
|
224
|
+
(client_status || {}).to_h { |k, v| [k.to_sym, v.to_sym] }
|
165
225
|
end
|
166
226
|
|
167
227
|
# @!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
|
@@ -81,13 +84,13 @@ module Discordrb
|
|
81
84
|
def update(data)
|
82
85
|
# Only pass a value for avatar if the key is defined as sending nil will delete the
|
83
86
|
data[:avatar] = avatarise(data[:avatar]) if data.key?(:avatar)
|
84
|
-
data[:channel_id] = data[:channel]
|
87
|
+
data[:channel_id] = data[:channel]&.resolve_id
|
85
88
|
data.delete(:channel)
|
86
|
-
update_webhook(data)
|
89
|
+
update_webhook(**data)
|
87
90
|
end
|
88
91
|
|
89
92
|
# Deletes the webhook.
|
90
|
-
# @param reason [String] The reason the
|
93
|
+
# @param reason [String] The reason the webhook is being deleted.
|
91
94
|
def delete(reason = nil)
|
92
95
|
if token?
|
93
96
|
API::Webhook.token_delete_webhook(@token, @id, reason)
|
@@ -96,10 +99,100 @@ module Discordrb
|
|
96
99
|
end
|
97
100
|
end
|
98
101
|
|
102
|
+
# Execute a webhook.
|
103
|
+
# @param content [String] The content of the message. May be 2000 characters long at most.
|
104
|
+
# @param username [String] The username the webhook will display as. If this is not set, the default username set in the webhook's settings.
|
105
|
+
# @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
|
106
|
+
# @param tts [true, false] Whether this message should use TTS or not. By default, it doesn't.
|
107
|
+
# @param file [File] File to be sent together with the message. Mutually exclusive with embeds; a webhook message can contain
|
108
|
+
# either a file to be sent or embeds.
|
109
|
+
# @param embeds [Array<Webhooks::Embed, Hash>] Embeds to attach to this message.
|
110
|
+
# @param allowed_mentions [AllowedMentions, Hash] Mentions that are allowed to ping in the `content`.
|
111
|
+
# @param wait [true, false] Whether Discord should wait for the message to be successfully received by clients, or
|
112
|
+
# whether it should return immediately after sending the message. If `true` a {Message} object will be returned.
|
113
|
+
# @yield [builder] Gives the builder to the block to add additional steps, or to do the entire building process.
|
114
|
+
# @yieldparam builder [Builder] The builder given as a parameter which is used as the initial step to start from.
|
115
|
+
# @example Execute the webhook with kwargs
|
116
|
+
# client.execute(
|
117
|
+
# content: 'Testing',
|
118
|
+
# username: 'discordrb',
|
119
|
+
# embeds: [
|
120
|
+
# { timestamp: Time.now.iso8601, title: 'testing', image: { url: 'https://i.imgur.com/PcMltU7.jpg' } }
|
121
|
+
# ])
|
122
|
+
# @example Execute the webhook with an already existing builder
|
123
|
+
# builder = Discordrb::Webhooks::Builder.new # ...
|
124
|
+
# client.execute(builder)
|
125
|
+
# @example Execute the webhook by building a new message
|
126
|
+
# client.execute do |builder|
|
127
|
+
# builder.content = 'Testing'
|
128
|
+
# builder.username = 'discordrb'
|
129
|
+
# builder.add_embed do |embed|
|
130
|
+
# embed.timestamp = Time.now
|
131
|
+
# embed.title = 'Testing'
|
132
|
+
# embed.image = Discordrb::Webhooks::EmbedImage.new(url: 'https://i.imgur.com/PcMltU7.jpg')
|
133
|
+
# end
|
134
|
+
# end
|
135
|
+
# @return [Message, nil] If `wait` is `true`, a {Message} will be returned. Otherwise this method will return `nil`.
|
136
|
+
# @note This is only available to webhooks with publically exposed tokens. This excludes channel follow webhooks and webhooks retrieved
|
137
|
+
# via the audit log.
|
138
|
+
def execute(content: nil, username: nil, avatar_url: nil, tts: nil, file: nil, embeds: nil, allowed_mentions: nil, wait: true, builder: nil, components: nil)
|
139
|
+
raise Discordrb::Errors::UnauthorizedWebhook unless @token
|
140
|
+
|
141
|
+
params = { content: content, username: username, avatar_url: avatar_url, tts: tts, file: file, embeds: embeds, allowed_mentions: allowed_mentions }
|
142
|
+
|
143
|
+
builder ||= Webhooks::Builder.new
|
144
|
+
view = Webhooks::View.new
|
145
|
+
|
146
|
+
yield(builder, view) if block_given?
|
147
|
+
|
148
|
+
data = builder.to_json_hash.merge(params.compact)
|
149
|
+
components ||= view
|
150
|
+
|
151
|
+
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)
|
152
|
+
|
153
|
+
Message.new(JSON.parse(resp), @bot) if wait
|
154
|
+
end
|
155
|
+
|
156
|
+
# Delete a message created by this webhook.
|
157
|
+
# @param message [Message, String, Integer] The ID of the message to delete.
|
158
|
+
def delete_message(message)
|
159
|
+
raise Discordrb::Errors::UnauthorizedWebhook unless @token
|
160
|
+
|
161
|
+
API::Webhook.token_delete_message(@token, @id, message.resolve_id)
|
162
|
+
end
|
163
|
+
|
164
|
+
# Edit a message created by this webhook.
|
165
|
+
# @param message [Message, String, Integer] The ID of the message to edit.
|
166
|
+
# @param content [String] The content of the message. May be 2000 characters long at most.
|
167
|
+
# @param embeds [Array<Webhooks::Embed, Hash>] Embeds to be attached to the message.
|
168
|
+
# @param allowed_mentions [AllowedMentions, Hash] Mentions that are allowed to ping in the `content`.
|
169
|
+
# @param builder [Builder, nil] The builder to start out with, or nil if one should be created anew.
|
170
|
+
# @yield [builder] Gives the builder to the block to add additional steps, or to do the entire building process.
|
171
|
+
# @yieldparam builder [Webhooks::Builder] The builder given as a parameter which is used as the initial step to start from.
|
172
|
+
# @return [Message] The updated message.
|
173
|
+
# @param components [View, Array<Hash>] Interaction components to associate with this message.
|
174
|
+
# @note When editing `allowed_mentions`, it will update visually in the client but not alert the user with a notification.
|
175
|
+
def edit_message(message, content: nil, embeds: nil, allowed_mentions: nil, builder: nil, components: nil)
|
176
|
+
raise Discordrb::Errors::UnauthorizedWebhook unless @token
|
177
|
+
|
178
|
+
params = { content: content, embeds: embeds, allowed_mentions: allowed_mentions }.compact
|
179
|
+
|
180
|
+
builder ||= Webhooks::Builder.new
|
181
|
+
view ||= Webhooks::View.new
|
182
|
+
|
183
|
+
yield(builder, view) if block_given?
|
184
|
+
|
185
|
+
data = builder.to_json_hash.merge(params.compact)
|
186
|
+
components ||= view
|
187
|
+
|
188
|
+
resp = API::Webhook.token_edit_message(@token, @id, message.resolve_id, data[:content], data[:embeds], data[:allowed_mentions], components.to_a)
|
189
|
+
Message.new(JSON.parse(resp), @bot)
|
190
|
+
end
|
191
|
+
|
99
192
|
# Utility function to get a webhook's avatar URL.
|
100
193
|
# @return [String] the URL to the avatar image
|
101
194
|
def avatar_url
|
102
|
-
return API::User.default_avatar unless @avatar
|
195
|
+
return API::User.default_avatar(@id) unless @avatar
|
103
196
|
|
104
197
|
API::User.avatar_url(@id, @avatar)
|
105
198
|
end
|
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,5 @@ 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'
|