discordrb 3.3.0 → 3.5.0

Sign up to get free protection for your applications and to get access to all the features.
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