discordrb 3.3.0 → 3.5.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.
Files changed (93) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +152 -0
  3. data/.github/ISSUE_TEMPLATE/bug_report.md +38 -0
  4. data/.github/ISSUE_TEMPLATE/feature_request.md +24 -0
  5. data/.github/pull_request_template.md +37 -0
  6. data/.github/workflows/codeql.yml +65 -0
  7. data/.markdownlint.json +4 -0
  8. data/.rubocop.yml +39 -36
  9. data/CHANGELOG.md +874 -552
  10. data/Gemfile +2 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +80 -86
  13. data/Rakefile +2 -0
  14. data/bin/console +1 -0
  15. data/discordrb-webhooks.gemspec +9 -6
  16. data/discordrb.gemspec +21 -18
  17. data/lib/discordrb/allowed_mentions.rb +36 -0
  18. data/lib/discordrb/api/application.rb +202 -0
  19. data/lib/discordrb/api/channel.rb +236 -47
  20. data/lib/discordrb/api/interaction.rb +54 -0
  21. data/lib/discordrb/api/invite.rb +5 -5
  22. data/lib/discordrb/api/server.rb +94 -66
  23. data/lib/discordrb/api/user.rb +17 -11
  24. data/lib/discordrb/api/webhook.rb +63 -6
  25. data/lib/discordrb/api.rb +55 -16
  26. data/lib/discordrb/await.rb +0 -1
  27. data/lib/discordrb/bot.rb +480 -93
  28. data/lib/discordrb/cache.rb +31 -24
  29. data/lib/discordrb/colour_rgb.rb +43 -0
  30. data/lib/discordrb/commands/command_bot.rb +35 -12
  31. data/lib/discordrb/commands/container.rb +21 -24
  32. data/lib/discordrb/commands/parser.rb +20 -20
  33. data/lib/discordrb/commands/rate_limiter.rb +4 -3
  34. data/lib/discordrb/container.rb +209 -20
  35. data/lib/discordrb/data/activity.rb +271 -0
  36. data/lib/discordrb/data/application.rb +50 -0
  37. data/lib/discordrb/data/attachment.rb +71 -0
  38. data/lib/discordrb/data/audit_logs.rb +345 -0
  39. data/lib/discordrb/data/channel.rb +993 -0
  40. data/lib/discordrb/data/component.rb +229 -0
  41. data/lib/discordrb/data/embed.rb +251 -0
  42. data/lib/discordrb/data/emoji.rb +82 -0
  43. data/lib/discordrb/data/integration.rb +122 -0
  44. data/lib/discordrb/data/interaction.rb +800 -0
  45. data/lib/discordrb/data/invite.rb +137 -0
  46. data/lib/discordrb/data/member.rb +372 -0
  47. data/lib/discordrb/data/message.rb +414 -0
  48. data/lib/discordrb/data/overwrite.rb +108 -0
  49. data/lib/discordrb/data/profile.rb +91 -0
  50. data/lib/discordrb/data/reaction.rb +33 -0
  51. data/lib/discordrb/data/recipient.rb +34 -0
  52. data/lib/discordrb/data/role.rb +248 -0
  53. data/lib/discordrb/data/server.rb +1004 -0
  54. data/lib/discordrb/data/user.rb +264 -0
  55. data/lib/discordrb/data/voice_region.rb +45 -0
  56. data/lib/discordrb/data/voice_state.rb +41 -0
  57. data/lib/discordrb/data/webhook.rb +238 -0
  58. data/lib/discordrb/data.rb +28 -4180
  59. data/lib/discordrb/errors.rb +46 -4
  60. data/lib/discordrb/events/bans.rb +7 -5
  61. data/lib/discordrb/events/channels.rb +3 -1
  62. data/lib/discordrb/events/guilds.rb +16 -9
  63. data/lib/discordrb/events/interactions.rb +482 -0
  64. data/lib/discordrb/events/invites.rb +125 -0
  65. data/lib/discordrb/events/members.rb +6 -2
  66. data/lib/discordrb/events/message.rb +72 -27
  67. data/lib/discordrb/events/presence.rb +35 -18
  68. data/lib/discordrb/events/raw.rb +1 -3
  69. data/lib/discordrb/events/reactions.rb +49 -4
  70. data/lib/discordrb/events/threads.rb +96 -0
  71. data/lib/discordrb/events/typing.rb +6 -4
  72. data/lib/discordrb/events/voice_server_update.rb +47 -0
  73. data/lib/discordrb/events/voice_state_update.rb +15 -10
  74. data/lib/discordrb/events/webhooks.rb +9 -6
  75. data/lib/discordrb/gateway.rb +99 -71
  76. data/lib/discordrb/id_object.rb +39 -0
  77. data/lib/discordrb/light/integrations.rb +1 -1
  78. data/lib/discordrb/light/light_bot.rb +1 -1
  79. data/lib/discordrb/logger.rb +4 -4
  80. data/lib/discordrb/paginator.rb +57 -0
  81. data/lib/discordrb/permissions.rb +159 -39
  82. data/lib/discordrb/version.rb +1 -1
  83. data/lib/discordrb/voice/encoder.rb +16 -7
  84. data/lib/discordrb/voice/network.rb +99 -47
  85. data/lib/discordrb/voice/sodium.rb +98 -0
  86. data/lib/discordrb/voice/voice_bot.rb +33 -25
  87. data/lib/discordrb/webhooks.rb +2 -0
  88. data/lib/discordrb.rb +107 -1
  89. metadata +126 -54
  90. data/.codeclimate.yml +0 -16
  91. data/.travis.yml +0 -33
  92. data/bin/travis_build_docs.sh +0 -17
  93. /data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
@@ -0,0 +1,50 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # OAuth Application information
5
+ class Application
6
+ include IDObject
7
+
8
+ # @return [String] the application name
9
+ attr_reader :name
10
+
11
+ # @return [String] the application description
12
+ attr_reader :description
13
+
14
+ # @return [Array<String>] the application's origins permitted to use RPC
15
+ attr_reader :rpc_origins
16
+
17
+ # @return [Integer]
18
+ attr_reader :flags
19
+
20
+ # Gets the user object of the owner. May be limited to username, discriminator,
21
+ # ID, and avatar if the bot cannot reach the owner.
22
+ # @return [User] the user object of the owner
23
+ attr_reader :owner
24
+
25
+ def initialize(data, bot)
26
+ @bot = bot
27
+
28
+ @name = data['name']
29
+ @id = data['id'].to_i
30
+ @description = data['description']
31
+ @icon_id = data['icon']
32
+ @rpc_origins = data['rpc_origins']
33
+ @flags = data['flags']
34
+ @owner = @bot.ensure_user(data['owner'])
35
+ end
36
+
37
+ # Utility function to get a application's icon URL.
38
+ # @return [String, nil] the URL of the icon image (nil if no image is set).
39
+ def icon_url
40
+ return nil if @icon_id.nil?
41
+
42
+ API.app_icon_url(@id, @icon_id)
43
+ end
44
+
45
+ # The inspect method is overwritten to give more useful output
46
+ def inspect
47
+ "<Application name=#{@name} id=#{@id}>"
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # An attachment to a message
5
+ class Attachment
6
+ include IDObject
7
+
8
+ # @return [Message] the message this attachment belongs to.
9
+ attr_reader :message
10
+
11
+ # @return [String] the CDN URL this attachment can be downloaded at.
12
+ attr_reader :url
13
+
14
+ # @return [String] the attachment's proxy URL - I'm not sure what exactly this does, but I think it has something to
15
+ # do with CDNs.
16
+ attr_reader :proxy_url
17
+
18
+ # @return [String] the attachment's filename.
19
+ attr_reader :filename
20
+
21
+ # @return [Integer] the attachment's file size in bytes.
22
+ attr_reader :size
23
+
24
+ # @return [Integer, nil] the width of an image file, in pixels, or `nil` if the file is not an image.
25
+ attr_reader :width
26
+
27
+ # @return [Integer, nil] the height of an image file, in pixels, or `nil` if the file is not an image.
28
+ attr_reader :height
29
+
30
+ # @return [String, nil] the attachment's description.
31
+ attr_reader :description
32
+
33
+ # @return [String, nil] the attachment's media type.
34
+ attr_reader :content_type
35
+
36
+ # @return [true, false] whether this attachment is ephemeral.
37
+ attr_reader :ephemeral
38
+ alias_method :ephemeral?, :ephemeral
39
+
40
+ # @!visibility private
41
+ def initialize(data, message, bot)
42
+ @bot = bot
43
+ @message = message
44
+
45
+ @id = data['id'].to_i
46
+ @url = data['url']
47
+ @proxy_url = data['proxy_url']
48
+ @filename = data['filename']
49
+
50
+ @size = data['size']
51
+
52
+ @width = data['width']
53
+ @height = data['height']
54
+
55
+ @description = data['description']
56
+ @content_type = data['content_type']
57
+
58
+ @ephemeral = data['ephemeral']
59
+ end
60
+
61
+ # @return [true, false] whether this file is an image file.
62
+ def image?
63
+ !(@width.nil? || @height.nil?)
64
+ end
65
+
66
+ # @return [true, false] whether this file is tagged as a spoiler.
67
+ def spoiler?
68
+ @filename.start_with? 'SPOILER_'
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,345 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # A server's audit logs
5
+ class AuditLogs
6
+ # The numbers associated with the type of action.
7
+ ACTIONS = {
8
+ 1 => :server_update,
9
+ 10 => :channel_create,
10
+ 11 => :channel_update,
11
+ 12 => :channel_delete,
12
+ 13 => :channel_overwrite_create,
13
+ 14 => :channel_overwrite_update,
14
+ 15 => :channel_overwrite_delete,
15
+ 20 => :member_kick,
16
+ 21 => :member_prune,
17
+ 22 => :member_ban_add,
18
+ 23 => :member_ban_remove,
19
+ 24 => :member_update,
20
+ 25 => :member_role_update,
21
+ 26 => :member_move,
22
+ 27 => :member_disconnect,
23
+ 28 => :bot_add,
24
+ 30 => :role_create,
25
+ 31 => :role_update,
26
+ 32 => :role_delete,
27
+ 40 => :invite_create,
28
+ 41 => :invite_update,
29
+ 42 => :invite_delete,
30
+ 50 => :webhook_create,
31
+ 51 => :webhook_update,
32
+ 52 => :webhook_delete,
33
+ 60 => :emoji_create,
34
+ 61 => :emoji_update,
35
+ 62 => :emoji_delete,
36
+ # 70
37
+ # 71
38
+ 72 => :message_delete,
39
+ 73 => :message_bulk_delete,
40
+ 74 => :message_pin,
41
+ 75 => :message_unpin,
42
+ 80 => :integration_create,
43
+ 81 => :integration_update,
44
+ 82 => :integration_delete
45
+ }.freeze
46
+
47
+ # @!visibility private
48
+ CREATE_ACTIONS = %i[
49
+ channel_create channel_overwrite_create member_ban_add role_create
50
+ invite_create webhook_create emoji_create integration_create
51
+ ].freeze
52
+
53
+ # @!visibility private
54
+ DELETE_ACTIONS = %i[
55
+ channel_delete channel_overwrite_delete member_kick member_prune
56
+ member_ban_remove role_delete invite_delete webhook_delete
57
+ emoji_delete message_delete message_bulk_delete integration_delete
58
+ ].freeze
59
+
60
+ # @!visibility private
61
+ UPDATE_ACTIONS = %i[
62
+ server_update channel_update channel_overwrite_update member_update
63
+ member_role_update role_update invite_update webhook_update
64
+ emoji_update integration_update
65
+ ].freeze
66
+
67
+ # @return [Hash<String => User>] the users included in the audit logs.
68
+ attr_reader :users
69
+
70
+ # @return [Hash<String => Webhook>] the webhooks included in the audit logs.
71
+ attr_reader :webhooks
72
+
73
+ # @return [Array<Entry>] the entries listed in the audit logs.
74
+ attr_reader :entries
75
+
76
+ # @!visibility private
77
+ def initialize(server, bot, data)
78
+ @bot = bot
79
+ @server = server
80
+ @users = {}
81
+ @webhooks = {}
82
+ @entries = data['audit_log_entries'].map { |entry| Entry.new(self, @server, @bot, entry) }
83
+
84
+ process_users(data['users'])
85
+ process_webhooks(data['webhooks'])
86
+ end
87
+
88
+ # An entry in a server's audit logs.
89
+ class Entry
90
+ include IDObject
91
+
92
+ # @return [Symbol] the action that was performed.
93
+ attr_reader :action
94
+
95
+ # @return [Symbol] the type action that was performed. (:create, :delete, :update, :unknown)
96
+ attr_reader :action_type
97
+
98
+ # @return [Symbol] the type of target being performed on. (:server, :channel, :user, :role, :invite, :webhook, :emoji, :unknown)
99
+ attr_reader :target_type
100
+
101
+ # @return [Integer, nil] the amount of messages deleted. Only present if the action is `:message_delete`.
102
+ attr_reader :count
103
+ alias_method :amount, :count
104
+
105
+ # @return [Integer, nil] the amount of days the members were inactive for. Only present if the action is `:member_prune`.
106
+ attr_reader :days
107
+
108
+ # @return [Integer, nil] the amount of members removed. Only present if the action is `:member_prune`.
109
+ attr_reader :members_removed
110
+
111
+ # @return [String, nil] the reason for this action occurring.
112
+ attr_reader :reason
113
+
114
+ # @return [Hash<String => Change>, RoleChange, nil] the changes from this log, listing the key as the key changed. Will be a RoleChange object if the action is `:member_role_update`. Will be nil if the action is either `:message_delete` or `:member_prune`.
115
+ attr_reader :changes
116
+
117
+ # @!visibility private
118
+ def initialize(logs, server, bot, data)
119
+ @bot = bot
120
+ @id = data['id'].resolve_id
121
+ @logs = logs
122
+ @server = server
123
+ @data = data
124
+ @action = ACTIONS[data['action_type']]
125
+ @reason = data['reason']
126
+ @action_type = AuditLogs.action_type_for(data['action_type'])
127
+ @target_type = AuditLogs.target_type_for(data['action_type'])
128
+
129
+ # Sets the 'changes' variable to a empty hash if there are no special actions.
130
+ @changes = {} unless @action == :message_delete || @action == :member_prune || @action == :member_role_update
131
+
132
+ # Sets the 'changes' variable to a RoleChange class if there's a role update.
133
+ @changes = RoleChange.new(data['changes'][0], @server) if @action == :member_role_update
134
+
135
+ process_changes(data['changes']) unless @action == :member_role_update
136
+ return unless data.include?('options')
137
+
138
+ # Checks and sets variables for special action options.
139
+ @count = data['options']['count'].to_i unless data['options']['count'].nil?
140
+ @channel_id = data['options']['channel'].to_i unless data['options']['channel'].nil?
141
+ @days = data['options']['delete_member_days'].to_i unless data['options']['delete_member_days'].nil?
142
+ @members_removed = data['options']['members_removed'].to_i unless data['options']['members_removed'].nil?
143
+ end
144
+
145
+ # @return [Server, Channel, Member, User, Role, Invite, Webhook, Emoji, nil] the target being performed on.
146
+ def target
147
+ @target ||= process_target(@data['target_id'], @target_type)
148
+ end
149
+
150
+ # @return [Member, User] the user that authored this action. Can be a User object if the user no longer exists in the server.
151
+ def user
152
+ @user ||= @server.member(@data['user_id'].to_i) || @bot.user(@data['user_id'].to_i) || @logs.user(@data['user_id'].to_i)
153
+ end
154
+ alias_method :author, :user
155
+
156
+ # @return [Channel, nil] the amount of messages deleted. Won't be nil if the action is `:message_delete`.
157
+ def channel
158
+ return nil unless @channel_id
159
+
160
+ @channel ||= @bot.channel(@channel_id, @server, bot, self)
161
+ end
162
+
163
+ # @!visibility private
164
+ def process_target(id, type)
165
+ id = id.resolve_id unless id.nil?
166
+ case type
167
+ when :server then @server # Since it won't be anything else
168
+ when :channel then @bot.channel(id, @server)
169
+ when :user, :message then @server.member(id) || @bot.user(id) || @logs.user(id)
170
+ when :role then @server.role(id)
171
+ when :invite then @bot.invite(@data['changes'].find { |change| change['key'] == 'code' }.values.delete_if { |v| v == 'code' }.first)
172
+ when :webhook then @server.webhooks.find { |webhook| webhook.id == id } || @logs.webhook(id)
173
+ when :emoji then @server.emoji[id]
174
+ when :integration then @server.integrations.find { |integration| integration.id == id }
175
+ end
176
+ end
177
+
178
+ # The inspect method is overwritten to give more useful output
179
+ def inspect
180
+ "<AuditLogs::Entry id=#{@id} key=#{@key} action=#{@action} reason=#{@reason} action_type=#{@action_type} target_type=#{@target_type} count=#{@count} days=#{@days} members_removed=#{@members_removed}>"
181
+ end
182
+
183
+ # Process action changes
184
+ # @note For internal use only
185
+ # @!visibility private
186
+ def process_changes(changes)
187
+ return unless changes
188
+
189
+ changes.each do |element|
190
+ change = Change.new(element, @server, @bot, self)
191
+ @changes[change.key] = change
192
+ end
193
+ end
194
+ end
195
+
196
+ # A change in a audit log entry.
197
+ class Change
198
+ # @return [String] the key that was changed.
199
+ # @note You should check with the Discord API Documentation on what key gives out what value.
200
+ attr_reader :key
201
+
202
+ # @return [String, Integer, true, false, Permissions, Overwrite, nil] the value that was changed from.
203
+ attr_reader :old
204
+ alias_method :old_value, :old
205
+
206
+ # @return [String, Integer, true, false, Permissions, Overwrite, nil] the value that was changed to.
207
+ attr_reader :new
208
+ alias_method :new_value, :new
209
+
210
+ # @!visibility private
211
+ def initialize(data, server, bot, logs)
212
+ @key = data['key']
213
+ @old = data['old_value']
214
+ @new = data['new_value']
215
+ @server = server
216
+ @bot = bot
217
+ @logs = logs
218
+
219
+ @old = Permissions.new(@old) if @old && @key == 'permissions'
220
+ @new = Permissions.new(@new) if @new && @key == 'permissions'
221
+
222
+ @old = @old.map { |o| Overwrite.new(o['id'], type: o['type'], allow: o['allow'], deny: o['deny']) } if @old && @key == 'permission_overwrites'
223
+ @new = @new.map { |o| Overwrite.new(o['id'], type: o['type'], allow: o['allow'], deny: o['deny']) } if @new && @key == 'permission_overwrites'
224
+ end
225
+
226
+ # @return [Channel, nil] the channel that was previously used in the server widget. Only present if the key for this change is `widget_channel_id`.
227
+ def old_widget_channel
228
+ @bot.channel(@old, @server) if @old && @key == 'widget_channel_id'
229
+ end
230
+
231
+ # @return [Channel, nil] the channel that is used in the server widget prior to this change. Only present if the key for this change is `widget_channel_id`.
232
+ def new_widget_channel
233
+ @bot.channel(@new, @server) if @new && @key == 'widget_channel_id'
234
+ end
235
+
236
+ # @return [Channel, nil] the channel that was previously used in the server as an AFK channel. Only present if the key for this change is `afk_channel_id`.
237
+ def old_afk_channel
238
+ @bot.channel(@old, @server) if @old && @key == 'afk_channel_id'
239
+ end
240
+
241
+ # @return [Channel, nil] the channel that is used in the server as an AFK channel prior to this change. Only present if the key for this change is `afk_channel_id`.
242
+ def new_afk_channel
243
+ @bot.channel(@new, @server) if @new && @key == 'afk_channel_id'
244
+ end
245
+
246
+ # @return [Member, User, nil] the member that used to be the owner of the server. Only present if the for key for this change is `owner_id`.
247
+ def old_owner
248
+ @server.member(@old) || @bot.user(@old) || @logs.user(@old) if @old && @key == 'owner_id'
249
+ end
250
+
251
+ # @return [Member, User, nil] the member that is now the owner of the server prior to this change. Only present if the key for this change is `owner_id`.
252
+ def new_owner
253
+ @server.member(@new) || @bot.user(@new) || @logs.user(@new) if @new && @key == 'owner_id'
254
+ end
255
+ end
256
+
257
+ # A change that includes roles.
258
+ class RoleChange
259
+ # @return [Symbol] what type of change this is: (:add, :remove)
260
+ attr_reader :type
261
+
262
+ # @!visibility private
263
+ def initialize(data, server)
264
+ @type = data['key'].delete('$').to_sym
265
+ @role_id = data['new_value'][0]['id'].to_i
266
+ @server = server
267
+ end
268
+
269
+ # @return [Role] the role being used.
270
+ def role
271
+ @role ||= @server.role(@role_id)
272
+ end
273
+ end
274
+
275
+ # @return [Entry] the latest entry in the audit logs.
276
+ def latest
277
+ @entries.first
278
+ end
279
+ alias_method :first, :latest
280
+
281
+ # Gets a user in the audit logs data based on user ID
282
+ # @note This only uses data given by the audit logs request
283
+ # @param id [String, Integer] The user ID to look for
284
+ def user(id)
285
+ @users[id.resolve_id]
286
+ end
287
+
288
+ # Gets a webhook in the audit logs data based on webhook ID
289
+ # @note This only uses data given by the audit logs request
290
+ # @param id [String, Integer] The webhook ID to look for
291
+ def webhook(id)
292
+ @webhooks[id.resolve_id]
293
+ end
294
+
295
+ # Process user objects given by the request
296
+ # @note For internal use only
297
+ # @!visibility private
298
+ def process_users(users)
299
+ users.each do |element|
300
+ user = User.new(element, @bot)
301
+ @users[user.id] = user
302
+ end
303
+ end
304
+
305
+ # Process webhook objects given by the request
306
+ # @note For internal use only
307
+ # @!visibility private
308
+ def process_webhooks(webhooks)
309
+ webhooks.each do |element|
310
+ webhook = Webhook.new(element, @bot)
311
+ @webhooks[webhook.id] = webhook
312
+ end
313
+ end
314
+
315
+ # Find the type of target by it's action number
316
+ # @note For internal use only
317
+ # @!visibility private
318
+ def self.target_type_for(action)
319
+ case action
320
+ when 1..9 then :server
321
+ when 10..19 then :channel
322
+ when 20..29 then :user
323
+ when 30..39 then :role
324
+ when 40..49 then :invite
325
+ when 50..59 then :webhook
326
+ when 60..69 then :emoji
327
+ when 70..79 then :message
328
+ when 80..89 then :integration
329
+ else :unknown
330
+ end
331
+ end
332
+
333
+ # Find the type of action by its action number
334
+ # @note For internal use only
335
+ # @!visibility private
336
+ def self.action_type_for(action)
337
+ action = ACTIONS[action]
338
+ return :create if CREATE_ACTIONS.include?(action)
339
+ return :delete if DELETE_ACTIONS.include?(action)
340
+ return :update if UPDATE_ACTIONS.include?(action)
341
+
342
+ :unknown
343
+ end
344
+ end
345
+ end