discordrb 3.1.1 → 3.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of discordrb might be problematic. Click here for more details.
- checksums.yaml +5 -5
- data/.circleci/config.yml +126 -0
- data/.codeclimate.yml +16 -0
- data/.github/CONTRIBUTING.md +13 -0
- data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
- data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
- data/.github/pull_request_template.md +37 -0
- data/.gitignore +5 -0
- data/.rubocop.yml +39 -33
- data/.travis.yml +27 -2
- data/.yardopts +1 -1
- data/CHANGELOG.md +808 -208
- data/Gemfile +4 -1
- data/LICENSE.txt +1 -1
- data/README.md +108 -53
- data/Rakefile +14 -1
- data/bin/console +1 -0
- data/bin/travis_build_docs.sh +17 -0
- data/discordrb-webhooks.gemspec +26 -0
- data/discordrb.gemspec +24 -15
- data/lib/discordrb.rb +75 -2
- data/lib/discordrb/allowed_mentions.rb +36 -0
- data/lib/discordrb/api.rb +126 -27
- data/lib/discordrb/api/channel.rb +165 -43
- data/lib/discordrb/api/invite.rb +10 -7
- data/lib/discordrb/api/server.rb +240 -61
- data/lib/discordrb/api/user.rb +26 -24
- data/lib/discordrb/api/webhook.rb +83 -0
- data/lib/discordrb/await.rb +1 -2
- data/lib/discordrb/bot.rb +417 -149
- data/lib/discordrb/cache.rb +42 -10
- data/lib/discordrb/colour_rgb.rb +43 -0
- data/lib/discordrb/commands/command_bot.rb +186 -31
- data/lib/discordrb/commands/container.rb +30 -16
- data/lib/discordrb/commands/parser.rb +102 -47
- data/lib/discordrb/commands/rate_limiter.rb +18 -17
- data/lib/discordrb/container.rb +245 -41
- data/lib/discordrb/data.rb +27 -2511
- data/lib/discordrb/data/activity.rb +264 -0
- data/lib/discordrb/data/application.rb +50 -0
- data/lib/discordrb/data/attachment.rb +56 -0
- data/lib/discordrb/data/audit_logs.rb +345 -0
- data/lib/discordrb/data/channel.rb +849 -0
- data/lib/discordrb/data/embed.rb +251 -0
- data/lib/discordrb/data/emoji.rb +82 -0
- data/lib/discordrb/data/integration.rb +83 -0
- data/lib/discordrb/data/invite.rb +137 -0
- data/lib/discordrb/data/member.rb +297 -0
- data/lib/discordrb/data/message.rb +334 -0
- data/lib/discordrb/data/overwrite.rb +102 -0
- data/lib/discordrb/data/profile.rb +91 -0
- data/lib/discordrb/data/reaction.rb +33 -0
- data/lib/discordrb/data/recipient.rb +34 -0
- data/lib/discordrb/data/role.rb +191 -0
- data/lib/discordrb/data/server.rb +1002 -0
- data/lib/discordrb/data/user.rb +204 -0
- data/lib/discordrb/data/voice_region.rb +45 -0
- data/lib/discordrb/data/voice_state.rb +41 -0
- data/lib/discordrb/data/webhook.rb +145 -0
- data/lib/discordrb/errors.rb +36 -2
- data/lib/discordrb/events/bans.rb +7 -5
- data/lib/discordrb/events/channels.rb +2 -0
- data/lib/discordrb/events/generic.rb +19 -3
- data/lib/discordrb/events/guilds.rb +129 -6
- data/lib/discordrb/events/invites.rb +125 -0
- data/lib/discordrb/events/members.rb +6 -2
- data/lib/discordrb/events/message.rb +86 -36
- data/lib/discordrb/events/presence.rb +23 -16
- data/lib/discordrb/events/raw.rb +47 -0
- data/lib/discordrb/events/reactions.rb +159 -0
- data/lib/discordrb/events/roles.rb +7 -6
- data/lib/discordrb/events/typing.rb +9 -5
- data/lib/discordrb/events/voice_server_update.rb +47 -0
- data/lib/discordrb/events/voice_state_update.rb +29 -9
- data/lib/discordrb/events/webhooks.rb +64 -0
- data/lib/discordrb/gateway.rb +219 -88
- data/lib/discordrb/id_object.rb +39 -0
- data/lib/discordrb/light.rb +1 -1
- data/lib/discordrb/light/integrations.rb +1 -1
- data/lib/discordrb/light/light_bot.rb +1 -1
- data/lib/discordrb/logger.rb +12 -11
- data/lib/discordrb/paginator.rb +57 -0
- data/lib/discordrb/permissions.rb +148 -14
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +14 -15
- data/lib/discordrb/voice/network.rb +86 -45
- data/lib/discordrb/voice/sodium.rb +96 -0
- data/lib/discordrb/voice/voice_bot.rb +52 -40
- data/lib/discordrb/webhooks.rb +12 -0
- data/lib/discordrb/websocket.rb +2 -2
- metadata +137 -34
@@ -0,0 +1,297 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Discordrb
|
4
|
+
# Mixin for the attributes members and private members should have
|
5
|
+
module MemberAttributes
|
6
|
+
# @return [Time] when this member joined the server.
|
7
|
+
attr_reader :joined_at
|
8
|
+
|
9
|
+
# @return [Time, nil] when this member boosted this server, `nil` if they haven't.
|
10
|
+
attr_reader :boosting_since
|
11
|
+
|
12
|
+
# @return [String, nil] the nickname this member has, or `nil` if it has none.
|
13
|
+
attr_reader :nick
|
14
|
+
alias_method :nickname, :nick
|
15
|
+
|
16
|
+
# @return [Array<Role>] the roles this member has.
|
17
|
+
attr_reader :roles
|
18
|
+
|
19
|
+
# @return [Server] the server this member is on.
|
20
|
+
attr_reader :server
|
21
|
+
end
|
22
|
+
|
23
|
+
# A member is a user on a server. It differs from regular users in that it has roles, voice statuses and things like
|
24
|
+
# that.
|
25
|
+
class Member < DelegateClass(User)
|
26
|
+
# @return [true, false] whether this member is muted server-wide.
|
27
|
+
def mute
|
28
|
+
voice_state_attribute(:mute)
|
29
|
+
end
|
30
|
+
|
31
|
+
# @return [true, false] whether this member is deafened server-wide.
|
32
|
+
def deaf
|
33
|
+
voice_state_attribute(:deaf)
|
34
|
+
end
|
35
|
+
|
36
|
+
# @return [true, false] whether this member has muted themselves.
|
37
|
+
def self_mute
|
38
|
+
voice_state_attribute(:self_mute)
|
39
|
+
end
|
40
|
+
|
41
|
+
# @return [true, false] whether this member has deafened themselves.
|
42
|
+
def self_deaf
|
43
|
+
voice_state_attribute(:self_deaf)
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Channel] the voice channel this member is in.
|
47
|
+
def voice_channel
|
48
|
+
voice_state_attribute(:voice_channel)
|
49
|
+
end
|
50
|
+
|
51
|
+
alias_method :muted?, :mute
|
52
|
+
alias_method :deafened?, :deaf
|
53
|
+
alias_method :self_muted?, :self_mute
|
54
|
+
alias_method :self_deafened?, :self_deaf
|
55
|
+
|
56
|
+
include MemberAttributes
|
57
|
+
|
58
|
+
# @!visibility private
|
59
|
+
def initialize(data, server, bot)
|
60
|
+
@bot = bot
|
61
|
+
|
62
|
+
@user = bot.ensure_user(data['user'])
|
63
|
+
super @user # Initialize the delegate class
|
64
|
+
|
65
|
+
# Somehow, Discord doesn't send the server ID in the standard member format...
|
66
|
+
raise ArgumentError, 'Cannot create a member without any information about the server!' if server.nil? && data['guild_id'].nil?
|
67
|
+
|
68
|
+
@server = server || bot.server(data['guild_id'].to_i)
|
69
|
+
|
70
|
+
# Initialize the roles by getting the roles from the server one-by-one
|
71
|
+
update_roles(data['roles'])
|
72
|
+
|
73
|
+
@nick = data['nick']
|
74
|
+
@joined_at = data['joined_at'] ? Time.parse(data['joined_at']) : nil
|
75
|
+
@boosting_since = data['premium_since'] ? Time.parse(data['premium_since']) : nil
|
76
|
+
end
|
77
|
+
|
78
|
+
# @return [true, false] if this user is a Nitro Booster of this server.
|
79
|
+
def boosting?
|
80
|
+
!@boosting_since.nil?
|
81
|
+
end
|
82
|
+
|
83
|
+
# @return [true, false] whether this member is the server owner.
|
84
|
+
def owner?
|
85
|
+
@server.owner == self
|
86
|
+
end
|
87
|
+
|
88
|
+
# @param role [Role, String, Integer] the role to check or its ID.
|
89
|
+
# @return [true, false] whether this member has the specified role.
|
90
|
+
def role?(role)
|
91
|
+
role = role.resolve_id
|
92
|
+
@roles.any? { |e| e.id == role }
|
93
|
+
end
|
94
|
+
|
95
|
+
# @see Member#set_roles
|
96
|
+
def roles=(role)
|
97
|
+
set_roles(role)
|
98
|
+
end
|
99
|
+
|
100
|
+
# Bulk sets a member's roles.
|
101
|
+
# @param role [Role, Array<Role>] The role(s) to set.
|
102
|
+
# @param reason [String] The reason the user's roles are being changed.
|
103
|
+
def set_roles(role, reason = nil)
|
104
|
+
role_ids = role_id_array(role)
|
105
|
+
API::Server.update_member(@bot.token, @server.id, @user.id, roles: role_ids, reason: reason)
|
106
|
+
end
|
107
|
+
|
108
|
+
# Adds and removes roles from a member.
|
109
|
+
# @param add [Role, Array<Role>] The role(s) to add.
|
110
|
+
# @param remove [Role, Array<Role>] The role(s) to remove.
|
111
|
+
# @param reason [String] The reason the user's roles are being changed.
|
112
|
+
# @example Remove the 'Member' role from a user, and add the 'Muted' role to them.
|
113
|
+
# to_add = server.roles.find {|role| role.name == 'Muted'}
|
114
|
+
# to_remove = server.roles.find {|role| role.name == 'Member'}
|
115
|
+
# member.modify_roles(to_add, to_remove)
|
116
|
+
def modify_roles(add, remove, reason = nil)
|
117
|
+
add_role_ids = role_id_array(add)
|
118
|
+
remove_role_ids = role_id_array(remove)
|
119
|
+
old_role_ids = @roles.map(&:id)
|
120
|
+
new_role_ids = (old_role_ids - remove_role_ids + add_role_ids).uniq
|
121
|
+
|
122
|
+
API::Server.update_member(@bot.token, @server.id, @user.id, roles: new_role_ids, reason: reason)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Adds one or more roles to this member.
|
126
|
+
# @param role [Role, Array<Role, String, Integer>, String, Integer] The role(s), or their ID(s), to add.
|
127
|
+
# @param reason [String] The reason the user's roles are being changed.
|
128
|
+
def add_role(role, reason = nil)
|
129
|
+
role_ids = role_id_array(role)
|
130
|
+
|
131
|
+
if role_ids.count == 1
|
132
|
+
API::Server.add_member_role(@bot.token, @server.id, @user.id, role_ids[0], reason)
|
133
|
+
else
|
134
|
+
old_role_ids = @roles.map(&:id)
|
135
|
+
new_role_ids = (old_role_ids + role_ids).uniq
|
136
|
+
API::Server.update_member(@bot.token, @server.id, @user.id, roles: new_role_ids, reason: reason)
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
# Removes one or more roles from this member.
|
141
|
+
# @param role [Role, Array<Role>] The role(s) to remove.
|
142
|
+
# @param reason [String] The reason the user's roles are being changed.
|
143
|
+
def remove_role(role, reason = nil)
|
144
|
+
role_ids = role_id_array(role)
|
145
|
+
|
146
|
+
if role_ids.count == 1
|
147
|
+
API::Server.remove_member_role(@bot.token, @server.id, @user.id, role_ids[0], reason)
|
148
|
+
else
|
149
|
+
old_role_ids = @roles.map(&:id)
|
150
|
+
new_role_ids = old_role_ids.reject { |i| role_ids.include?(i) }
|
151
|
+
API::Server.update_member(@bot.token, @server.id, @user.id, roles: new_role_ids, reason: reason)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# @return [Role] the highest role this member has.
|
156
|
+
def highest_role
|
157
|
+
@roles.max_by(&:position)
|
158
|
+
end
|
159
|
+
|
160
|
+
# @return [Role, nil] the role this member is being hoisted with.
|
161
|
+
def hoist_role
|
162
|
+
hoisted_roles = @roles.select(&:hoist)
|
163
|
+
return nil if hoisted_roles.empty?
|
164
|
+
|
165
|
+
hoisted_roles.max_by(&:position)
|
166
|
+
end
|
167
|
+
|
168
|
+
# @return [Role, nil] the role this member is basing their colour on.
|
169
|
+
def colour_role
|
170
|
+
coloured_roles = @roles.select { |v| v.colour.combined.nonzero? }
|
171
|
+
return nil if coloured_roles.empty?
|
172
|
+
|
173
|
+
coloured_roles.max_by(&:position)
|
174
|
+
end
|
175
|
+
alias_method :color_role, :colour_role
|
176
|
+
|
177
|
+
# @return [ColourRGB, nil] the colour this member has.
|
178
|
+
def colour
|
179
|
+
return nil unless colour_role
|
180
|
+
|
181
|
+
colour_role.color
|
182
|
+
end
|
183
|
+
alias_method :color, :colour
|
184
|
+
|
185
|
+
# Server deafens this member.
|
186
|
+
def server_deafen
|
187
|
+
API::Server.update_member(@bot.token, @server.id, @user.id, deaf: true)
|
188
|
+
end
|
189
|
+
|
190
|
+
# Server undeafens this member.
|
191
|
+
def server_undeafen
|
192
|
+
API::Server.update_member(@bot.token, @server.id, @user.id, deaf: false)
|
193
|
+
end
|
194
|
+
|
195
|
+
# Server mutes this member.
|
196
|
+
def server_mute
|
197
|
+
API::Server.update_member(@bot.token, @server.id, @user.id, mute: true)
|
198
|
+
end
|
199
|
+
|
200
|
+
# Server unmutes this member.
|
201
|
+
def server_unmute
|
202
|
+
API::Server.update_member(@bot.token, @server.id, @user.id, mute: false)
|
203
|
+
end
|
204
|
+
|
205
|
+
# @see Member#set_nick
|
206
|
+
def nick=(nick)
|
207
|
+
set_nick(nick)
|
208
|
+
end
|
209
|
+
|
210
|
+
alias_method :nickname=, :nick=
|
211
|
+
|
212
|
+
# Sets or resets this member's nickname. Requires the Change Nickname permission for the bot itself and Manage
|
213
|
+
# Nicknames for other users.
|
214
|
+
# @param nick [String, nil] The string to set the nickname to, or nil if it should be reset.
|
215
|
+
# @param reason [String] The reason the user's nickname is being changed.
|
216
|
+
def set_nick(nick, reason = nil)
|
217
|
+
# Discord uses the empty string to signify 'no nickname' so we convert nil into that
|
218
|
+
nick ||= ''
|
219
|
+
|
220
|
+
if @user.current_bot?
|
221
|
+
API::User.change_own_nickname(@bot.token, @server.id, nick, reason)
|
222
|
+
else
|
223
|
+
API::Server.update_member(@bot.token, @server.id, @user.id, nick: nick, reason: nil)
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
alias_method :set_nickname, :set_nick
|
228
|
+
|
229
|
+
# @return [String] the name the user displays as (nickname if they have one, username otherwise)
|
230
|
+
def display_name
|
231
|
+
nickname || username
|
232
|
+
end
|
233
|
+
|
234
|
+
# Update this member's roles
|
235
|
+
# @note For internal use only.
|
236
|
+
# @!visibility private
|
237
|
+
def update_roles(role_ids)
|
238
|
+
@roles = [@server.role(@server.id)]
|
239
|
+
role_ids.each do |id|
|
240
|
+
# It is possible for members to have roles that do not exist
|
241
|
+
# on the server any longer. See https://github.com/shardlab/discordrb/issues/371
|
242
|
+
role = @server.role(id)
|
243
|
+
@roles << role if role
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
# Update this member's nick
|
248
|
+
# @note For internal use only.
|
249
|
+
# @!visibility private
|
250
|
+
def update_nick(nick)
|
251
|
+
@nick = nick
|
252
|
+
end
|
253
|
+
|
254
|
+
# Update this member's boosting timestamp
|
255
|
+
# @note For internal user only.
|
256
|
+
# @!visibility private
|
257
|
+
def update_boosting_since(time)
|
258
|
+
@boosting_since = time
|
259
|
+
end
|
260
|
+
|
261
|
+
# Update this member
|
262
|
+
# @note For internal use only.
|
263
|
+
# @!visibility private
|
264
|
+
def update_data(data)
|
265
|
+
update_roles(data['roles']) if data['roles']
|
266
|
+
update_nick(data['nick']) if data.key?('nick')
|
267
|
+
@mute = data['mute'] if data.key?('mute')
|
268
|
+
@deaf = data['deaf'] if data.key?('deaf')
|
269
|
+
|
270
|
+
@joined_at = Time.parse(data['joined_at']) if data['joined_at']
|
271
|
+
end
|
272
|
+
|
273
|
+
include PermissionCalculator
|
274
|
+
|
275
|
+
# Overwriting inspect for debug purposes
|
276
|
+
def inspect
|
277
|
+
"<Member user=#{@user.inspect} server=#{@server.inspect} joined_at=#{@joined_at} roles=#{@roles.inspect} voice_channel=#{@voice_channel.inspect} mute=#{@mute} deaf=#{@deaf} self_mute=#{@self_mute} self_deaf=#{@self_deaf}>"
|
278
|
+
end
|
279
|
+
|
280
|
+
private
|
281
|
+
|
282
|
+
# Utility method to get a list of role IDs from one role or an array of roles
|
283
|
+
def role_id_array(role)
|
284
|
+
if role.is_a? Array
|
285
|
+
role.map(&:resolve_id)
|
286
|
+
else
|
287
|
+
[role.resolve_id]
|
288
|
+
end
|
289
|
+
end
|
290
|
+
|
291
|
+
# Utility method to get data out of this member's voice state
|
292
|
+
def voice_state_attribute(name)
|
293
|
+
voice_state = @server.voice_states[@user.id]
|
294
|
+
voice_state&.send name
|
295
|
+
end
|
296
|
+
end
|
297
|
+
end
|
@@ -0,0 +1,334 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Discordrb
|
4
|
+
# A message on Discord that was sent to a text channel
|
5
|
+
class Message
|
6
|
+
include IDObject
|
7
|
+
|
8
|
+
# @return [String] the content of this message.
|
9
|
+
attr_reader :content
|
10
|
+
alias_method :text, :content
|
11
|
+
alias_method :to_s, :content
|
12
|
+
|
13
|
+
# @return [Member, User] the user that sent this message. (Will be a {Member} most of the time, it should only be a
|
14
|
+
# {User} for old messages when the author has left the server since then)
|
15
|
+
attr_reader :author
|
16
|
+
alias_method :user, :author
|
17
|
+
alias_method :writer, :author
|
18
|
+
|
19
|
+
# @return [Channel] the channel in which this message was sent.
|
20
|
+
attr_reader :channel
|
21
|
+
|
22
|
+
# @return [Time] the timestamp at which this message was sent.
|
23
|
+
attr_reader :timestamp
|
24
|
+
|
25
|
+
# @return [Time] the timestamp at which this message was edited. `nil` if the message was never edited.
|
26
|
+
attr_reader :edited_timestamp
|
27
|
+
alias_method :edit_timestamp, :edited_timestamp
|
28
|
+
|
29
|
+
# @return [Array<User>] the users that were mentioned in this message.
|
30
|
+
attr_reader :mentions
|
31
|
+
|
32
|
+
# @return [Array<Role>] the roles that were mentioned in this message.
|
33
|
+
attr_reader :role_mentions
|
34
|
+
|
35
|
+
# @return [Array<Attachment>] the files attached to this message.
|
36
|
+
attr_reader :attachments
|
37
|
+
|
38
|
+
# @return [Array<Embed>] the embed objects contained in this message.
|
39
|
+
attr_reader :embeds
|
40
|
+
|
41
|
+
# @return [Array<Reaction>] the reaction objects contained in this message.
|
42
|
+
attr_reader :reactions
|
43
|
+
|
44
|
+
# @return [true, false] whether the message used Text-To-Speech (TTS) or not.
|
45
|
+
attr_reader :tts
|
46
|
+
alias_method :tts?, :tts
|
47
|
+
|
48
|
+
# @return [String] used for validating a message was sent.
|
49
|
+
attr_reader :nonce
|
50
|
+
|
51
|
+
# @return [true, false] whether the message was edited or not.
|
52
|
+
attr_reader :edited
|
53
|
+
alias_method :edited?, :edited
|
54
|
+
|
55
|
+
# @return [true, false] whether the message mentioned everyone or not.
|
56
|
+
attr_reader :mention_everyone
|
57
|
+
alias_method :mention_everyone?, :mention_everyone
|
58
|
+
alias_method :mentions_everyone?, :mention_everyone
|
59
|
+
|
60
|
+
# @return [true, false] whether the message is pinned or not.
|
61
|
+
attr_reader :pinned
|
62
|
+
alias_method :pinned?, :pinned
|
63
|
+
|
64
|
+
# @return [Server, nil] the server in which this message was sent.
|
65
|
+
attr_reader :server
|
66
|
+
|
67
|
+
# @return [Integer, nil] the webhook ID that sent this message, or `nil` if it wasn't sent through a webhook.
|
68
|
+
attr_reader :webhook_id
|
69
|
+
|
70
|
+
# The discriminator that webhook user accounts have.
|
71
|
+
ZERO_DISCRIM = '0000'
|
72
|
+
|
73
|
+
# @!visibility private
|
74
|
+
def initialize(data, bot)
|
75
|
+
@bot = bot
|
76
|
+
@content = data['content']
|
77
|
+
@channel = bot.channel(data['channel_id'].to_i)
|
78
|
+
@pinned = data['pinned']
|
79
|
+
@tts = data['tts']
|
80
|
+
@nonce = data['nonce']
|
81
|
+
@mention_everyone = data['mention_everyone']
|
82
|
+
|
83
|
+
@referenced_message = Message.new(data['referenced_message'], bot) if data['referenced_message']
|
84
|
+
@message_reference = data['message_reference']
|
85
|
+
|
86
|
+
@server = bot.server(data['guild_id'].to_i) if data['guild_id']
|
87
|
+
|
88
|
+
@author = if data['author']
|
89
|
+
if data['author']['discriminator'] == ZERO_DISCRIM
|
90
|
+
# This is a webhook user! It would be pointless to try to resolve a member here, so we just create
|
91
|
+
# a User and return that instead.
|
92
|
+
Discordrb::LOGGER.debug("Webhook user: #{data['author']['id']}")
|
93
|
+
User.new(data['author'], @bot)
|
94
|
+
elsif @channel.private?
|
95
|
+
# Turn the message user into a recipient - we can't use the channel recipient
|
96
|
+
# directly because the bot may also send messages to the channel
|
97
|
+
Recipient.new(bot.user(data['author']['id'].to_i), @channel, bot)
|
98
|
+
else
|
99
|
+
member = @channel.server.member(data['author']['id'].to_i)
|
100
|
+
|
101
|
+
if member
|
102
|
+
member.update_data(data['member']) if data['member']
|
103
|
+
else
|
104
|
+
Discordrb::LOGGER.debug("Member with ID #{data['author']['id']} not cached (possibly left the server).")
|
105
|
+
member = if data['member']
|
106
|
+
member_data = data['author'].merge(data['member'])
|
107
|
+
Member.new(member_data, bot)
|
108
|
+
else
|
109
|
+
@bot.ensure_user(data['author'])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
member
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
@webhook_id = data['webhook_id'].to_i if data['webhook_id']
|
118
|
+
|
119
|
+
@timestamp = Time.parse(data['timestamp']) if data['timestamp']
|
120
|
+
@edited_timestamp = data['edited_timestamp'].nil? ? nil : Time.parse(data['edited_timestamp'])
|
121
|
+
@edited = !@edited_timestamp.nil?
|
122
|
+
@id = data['id'].to_i
|
123
|
+
|
124
|
+
@emoji = []
|
125
|
+
|
126
|
+
@reactions = []
|
127
|
+
|
128
|
+
data['reactions']&.each do |element|
|
129
|
+
@reactions << Reaction.new(element)
|
130
|
+
end
|
131
|
+
|
132
|
+
@mentions = []
|
133
|
+
|
134
|
+
data['mentions']&.each do |element|
|
135
|
+
@mentions << bot.ensure_user(element)
|
136
|
+
end
|
137
|
+
|
138
|
+
@role_mentions = []
|
139
|
+
|
140
|
+
# Role mentions can only happen on public servers so make sure we only parse them there
|
141
|
+
if @channel.text?
|
142
|
+
data['mention_roles']&.each do |element|
|
143
|
+
@role_mentions << @channel.server.role(element.to_i)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
147
|
+
@attachments = []
|
148
|
+
@attachments = data['attachments'].map { |e| Attachment.new(e, self, @bot) } if data['attachments']
|
149
|
+
|
150
|
+
@embeds = []
|
151
|
+
@embeds = data['embeds'].map { |e| Embed.new(e, self) } if data['embeds']
|
152
|
+
end
|
153
|
+
|
154
|
+
# Replies to this message with the specified content.
|
155
|
+
# @deprecated Please use {#respond}.
|
156
|
+
# @see Channel#send_message
|
157
|
+
def reply(content)
|
158
|
+
@channel.send_message(content)
|
159
|
+
end
|
160
|
+
|
161
|
+
# Sends a message to this channel.
|
162
|
+
# @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
|
163
|
+
# @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
|
164
|
+
# @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
|
165
|
+
# @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
|
166
|
+
# @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
|
167
|
+
# @param mention_user [true, false] Whether the user that is being replied to should be pinged by the reply.
|
168
|
+
# @return [Message] the message that was sent.
|
169
|
+
def reply!(content, tts: false, embed: nil, attachments: nil, allowed_mentions: {}, mention_user: false)
|
170
|
+
allowed_mentions = { parse: [] } if allowed_mentions == false
|
171
|
+
allowed_mentions = allowed_mentions.to_hash.transform_keys(&:to_sym)
|
172
|
+
allowed_mentions[:replied_user] = mention_user
|
173
|
+
|
174
|
+
respond(content, tts, embed, attachments, allowed_mentions, self)
|
175
|
+
end
|
176
|
+
|
177
|
+
# (see Channel#send_message)
|
178
|
+
def respond(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil)
|
179
|
+
@channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Edits this message to have the specified content instead.
|
183
|
+
# You can only edit your own messages.
|
184
|
+
# @param new_content [String] the new content the message should have.
|
185
|
+
# @param new_embed [Hash, Discordrb::Webhooks::Embed, nil] The new embed the message should have. If `nil` the message will be changed to have no embed.
|
186
|
+
# @return [Message] the resulting message.
|
187
|
+
def edit(new_content, new_embed = nil)
|
188
|
+
response = API::Channel.edit_message(@bot.token, @channel.id, @id, new_content, [], new_embed ? new_embed.to_hash : nil)
|
189
|
+
Message.new(JSON.parse(response), @bot)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Deletes this message.
|
193
|
+
def delete(reason = nil)
|
194
|
+
API::Channel.delete_message(@bot.token, @channel.id, @id, reason)
|
195
|
+
nil
|
196
|
+
end
|
197
|
+
|
198
|
+
# Pins this message
|
199
|
+
def pin(reason = nil)
|
200
|
+
API::Channel.pin_message(@bot.token, @channel.id, @id, reason)
|
201
|
+
@pinned = true
|
202
|
+
nil
|
203
|
+
end
|
204
|
+
|
205
|
+
# Unpins this message
|
206
|
+
def unpin(reason = nil)
|
207
|
+
API::Channel.unpin_message(@bot.token, @channel.id, @id, reason)
|
208
|
+
@pinned = false
|
209
|
+
nil
|
210
|
+
end
|
211
|
+
|
212
|
+
# Add an {Await} for a message with the same user and channel.
|
213
|
+
# @see Bot#add_await
|
214
|
+
# @deprecated Will be changed to blocking behavior in v4.0. Use {#await!} instead.
|
215
|
+
def await(key, attributes = {}, &block)
|
216
|
+
@bot.add_await(key, Discordrb::Events::MessageEvent, { from: @author.id, in: @channel.id }.merge(attributes), &block)
|
217
|
+
end
|
218
|
+
|
219
|
+
# Add a blocking {Await} for a message with the same user and channel.
|
220
|
+
# @see Bot#add_await!
|
221
|
+
def await!(attributes = {}, &block)
|
222
|
+
@bot.add_await!(Discordrb::Events::MessageEvent, { from: @author.id, in: @channel.id }.merge(attributes), &block)
|
223
|
+
end
|
224
|
+
|
225
|
+
# @return [true, false] whether this message was sent by the current {Bot}.
|
226
|
+
def from_bot?
|
227
|
+
@author&.current_bot?
|
228
|
+
end
|
229
|
+
|
230
|
+
# @return [true, false] whether this message has been sent over a webhook.
|
231
|
+
def webhook?
|
232
|
+
!@webhook_id.nil?
|
233
|
+
end
|
234
|
+
|
235
|
+
# @return [Array<Emoji>] the emotes that were used/mentioned in this message.
|
236
|
+
def emoji
|
237
|
+
return if @content.nil?
|
238
|
+
return @emoji unless @emoji.empty?
|
239
|
+
|
240
|
+
@emoji = @bot.parse_mentions(@content).select { |el| el.is_a? Discordrb::Emoji }
|
241
|
+
end
|
242
|
+
|
243
|
+
# Check if any emoji were used in this message.
|
244
|
+
# @return [true, false] whether or not any emoji were used
|
245
|
+
def emoji?
|
246
|
+
emoji&.empty?
|
247
|
+
end
|
248
|
+
|
249
|
+
# Check if any reactions were used in this message.
|
250
|
+
# @return [true, false] whether or not this message has reactions
|
251
|
+
def reactions?
|
252
|
+
!@reactions.empty?
|
253
|
+
end
|
254
|
+
|
255
|
+
# Returns the reactions made by the current bot or user.
|
256
|
+
# @return [Array<Reaction>] the reactions
|
257
|
+
def my_reactions
|
258
|
+
@reactions.select(&:me)
|
259
|
+
end
|
260
|
+
|
261
|
+
# Reacts to a message.
|
262
|
+
# @param reaction [String, #to_reaction] the unicode emoji or {Emoji}
|
263
|
+
def create_reaction(reaction)
|
264
|
+
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
265
|
+
API::Channel.create_reaction(@bot.token, @channel.id, @id, reaction)
|
266
|
+
nil
|
267
|
+
end
|
268
|
+
|
269
|
+
alias_method :react, :create_reaction
|
270
|
+
|
271
|
+
# Returns the list of users who reacted with a certain reaction.
|
272
|
+
# @param reaction [String, #to_reaction] the unicode emoji or {Emoji}
|
273
|
+
# @param limit [Integer] the limit of how many users to retrieve. `nil` will return all users
|
274
|
+
# @example Get all the users that reacted with a thumbs up.
|
275
|
+
# thumbs_up_reactions = message.reacted_with("\u{1F44D}")
|
276
|
+
# @return [Array<User>] the users who used this reaction
|
277
|
+
def reacted_with(reaction, limit: 100)
|
278
|
+
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
279
|
+
paginator = Paginator.new(limit, :down) do |last_page|
|
280
|
+
after_id = last_page.last.id if last_page
|
281
|
+
last_page = JSON.parse(API::Channel.get_reactions(@bot.token, @channel.id, @id, reaction, nil, after_id, limit))
|
282
|
+
last_page.map { |d| User.new(d, @bot) }
|
283
|
+
end
|
284
|
+
paginator.to_a
|
285
|
+
end
|
286
|
+
|
287
|
+
# Deletes a reaction made by a user on this message.
|
288
|
+
# @param user [User, String, Integer] the user or user ID who used this reaction
|
289
|
+
# @param reaction [String, #to_reaction] the reaction to remove
|
290
|
+
def delete_reaction(user, reaction)
|
291
|
+
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
292
|
+
API::Channel.delete_user_reaction(@bot.token, @channel.id, @id, reaction, user.resolve_id)
|
293
|
+
end
|
294
|
+
|
295
|
+
# Deletes this client's reaction on this message.
|
296
|
+
# @param reaction [String, #to_reaction] the reaction to remove
|
297
|
+
def delete_own_reaction(reaction)
|
298
|
+
reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
|
299
|
+
API::Channel.delete_own_reaction(@bot.token, @channel.id, @id, reaction)
|
300
|
+
end
|
301
|
+
|
302
|
+
# Removes all reactions from this message.
|
303
|
+
def delete_all_reactions
|
304
|
+
API::Channel.delete_all_reactions(@bot.token, @channel.id, @id)
|
305
|
+
end
|
306
|
+
|
307
|
+
# The inspect method is overwritten to give more useful output
|
308
|
+
def inspect
|
309
|
+
"<Message content=\"#{@content}\" id=#{@id} timestamp=#{@timestamp} author=#{@author} channel=#{@channel}>"
|
310
|
+
end
|
311
|
+
|
312
|
+
# @return [String] a URL that a user can use to navigate to this message in the client
|
313
|
+
def link
|
314
|
+
"https://discord.com/channels/#{@server&.id || '@me'}/#{@channel.id}/#{@id}"
|
315
|
+
end
|
316
|
+
|
317
|
+
alias_method :jump_link, :link
|
318
|
+
|
319
|
+
# Whether or not this message was sent in reply to another message
|
320
|
+
# @return [true, false]
|
321
|
+
def reply?
|
322
|
+
!@referenced_message.nil?
|
323
|
+
end
|
324
|
+
|
325
|
+
# @return [Message, nil] the Message this Message was sent in reply to.
|
326
|
+
def referenced_message
|
327
|
+
return @referenced_message if @referenced_message
|
328
|
+
return nil unless @message_reference
|
329
|
+
|
330
|
+
referenced_channel = @bot.channel(@message_reference['channel_id'])
|
331
|
+
@referenced_message = referenced_channel.message(@message_reference['message_id'])
|
332
|
+
end
|
333
|
+
end
|
334
|
+
end
|