discordrb 3.3.0 → 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.

Files changed (82) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +126 -0
  3. data/{CONTRIBUTING.md → .github/CONTRIBUTING.md} +0 -0
  4. data/.github/ISSUE_TEMPLATE/bug_report.md +39 -0
  5. data/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
  6. data/.github/pull_request_template.md +37 -0
  7. data/.rubocop.yml +34 -37
  8. data/.travis.yml +5 -6
  9. data/CHANGELOG.md +472 -347
  10. data/Gemfile +2 -0
  11. data/LICENSE.txt +1 -1
  12. data/README.md +61 -79
  13. data/Rakefile +2 -0
  14. data/bin/console +1 -0
  15. data/discordrb-webhooks.gemspec +6 -6
  16. data/discordrb.gemspec +17 -17
  17. data/lib/discordrb.rb +73 -0
  18. data/lib/discordrb/allowed_mentions.rb +36 -0
  19. data/lib/discordrb/api.rb +40 -15
  20. data/lib/discordrb/api/channel.rb +57 -39
  21. data/lib/discordrb/api/invite.rb +3 -3
  22. data/lib/discordrb/api/server.rb +55 -50
  23. data/lib/discordrb/api/user.rb +8 -8
  24. data/lib/discordrb/api/webhook.rb +6 -6
  25. data/lib/discordrb/await.rb +0 -1
  26. data/lib/discordrb/bot.rb +164 -72
  27. data/lib/discordrb/cache.rb +4 -2
  28. data/lib/discordrb/colour_rgb.rb +43 -0
  29. data/lib/discordrb/commands/command_bot.rb +22 -6
  30. data/lib/discordrb/commands/container.rb +20 -23
  31. data/lib/discordrb/commands/parser.rb +18 -18
  32. data/lib/discordrb/commands/rate_limiter.rb +3 -2
  33. data/lib/discordrb/container.rb +77 -17
  34. data/lib/discordrb/data.rb +25 -4180
  35. data/lib/discordrb/data/activity.rb +264 -0
  36. data/lib/discordrb/data/application.rb +50 -0
  37. data/lib/discordrb/data/attachment.rb +56 -0
  38. data/lib/discordrb/data/audit_logs.rb +345 -0
  39. data/lib/discordrb/data/channel.rb +849 -0
  40. data/lib/discordrb/data/embed.rb +251 -0
  41. data/lib/discordrb/data/emoji.rb +82 -0
  42. data/lib/discordrb/data/integration.rb +83 -0
  43. data/lib/discordrb/data/invite.rb +137 -0
  44. data/lib/discordrb/data/member.rb +297 -0
  45. data/lib/discordrb/data/message.rb +334 -0
  46. data/lib/discordrb/data/overwrite.rb +102 -0
  47. data/lib/discordrb/data/profile.rb +91 -0
  48. data/lib/discordrb/data/reaction.rb +33 -0
  49. data/lib/discordrb/data/recipient.rb +34 -0
  50. data/lib/discordrb/data/role.rb +191 -0
  51. data/lib/discordrb/data/server.rb +1002 -0
  52. data/lib/discordrb/data/user.rb +204 -0
  53. data/lib/discordrb/data/voice_region.rb +45 -0
  54. data/lib/discordrb/data/voice_state.rb +41 -0
  55. data/lib/discordrb/data/webhook.rb +145 -0
  56. data/lib/discordrb/errors.rb +2 -1
  57. data/lib/discordrb/events/bans.rb +7 -5
  58. data/lib/discordrb/events/channels.rb +2 -0
  59. data/lib/discordrb/events/guilds.rb +16 -9
  60. data/lib/discordrb/events/invites.rb +125 -0
  61. data/lib/discordrb/events/members.rb +6 -2
  62. data/lib/discordrb/events/message.rb +69 -27
  63. data/lib/discordrb/events/presence.rb +14 -4
  64. data/lib/discordrb/events/raw.rb +1 -3
  65. data/lib/discordrb/events/reactions.rb +49 -3
  66. data/lib/discordrb/events/typing.rb +6 -4
  67. data/lib/discordrb/events/voice_server_update.rb +47 -0
  68. data/lib/discordrb/events/voice_state_update.rb +15 -10
  69. data/lib/discordrb/events/webhooks.rb +9 -6
  70. data/lib/discordrb/gateway.rb +72 -57
  71. data/lib/discordrb/id_object.rb +39 -0
  72. data/lib/discordrb/light/integrations.rb +1 -1
  73. data/lib/discordrb/light/light_bot.rb +1 -1
  74. data/lib/discordrb/logger.rb +4 -4
  75. data/lib/discordrb/paginator.rb +57 -0
  76. data/lib/discordrb/permissions.rb +103 -8
  77. data/lib/discordrb/version.rb +1 -1
  78. data/lib/discordrb/voice/encoder.rb +3 -3
  79. data/lib/discordrb/voice/network.rb +84 -43
  80. data/lib/discordrb/voice/sodium.rb +96 -0
  81. data/lib/discordrb/voice/voice_bot.rb +34 -26
  82. metadata +93 -55
@@ -0,0 +1,264 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Discordrb
4
+ # Contains information about user activities such as the game they are playing,
5
+ # music they are listening to, or their live stream.
6
+ class Activity
7
+ # Values corresponding to the flags bitmask
8
+ FLAGS = {
9
+ instance: 1 << 0, # this activity is an instanced game session
10
+ join: 1 << 1, # this activity is joinable
11
+ spectate: 1 << 2, # this activity can be spectated
12
+ join_request: 1 << 3, # this activity allows asking to join
13
+ sync: 1 << 4, # this activity is a spotify track
14
+ play: 1 << 5 # this game can be played or opened from discord
15
+ }.freeze
16
+
17
+ # @return [String] the activity's name
18
+ attr_reader :name
19
+
20
+ # @return [Integer, nil] activity type. Can be {GAME}, {STREAMING}, {LISTENING}, {CUSTOM}
21
+ attr_reader :type
22
+
23
+ # @return [String, nil] stream URL, when the activity type is {STREAMING}
24
+ attr_reader :url
25
+
26
+ # @return [String, nil] the application ID for the game
27
+ attr_reader :application_id
28
+
29
+ # @return [String, nil] details about what the player is currently doing
30
+ attr_reader :details
31
+
32
+ # @return [String, nil] the user's current party status
33
+ attr_reader :state
34
+
35
+ # @return [true, false] whether or not the activity is an instanced game session
36
+ attr_reader :instance
37
+
38
+ # @return [Integer] a bitmask of activity flags
39
+ # @see FLAGS
40
+ attr_reader :flags
41
+
42
+ # @return [Timestamps, nil] times for the start and/or end of the activity
43
+ attr_reader :timestamps
44
+
45
+ # @return [Secrets, nil] secrets for rich presence, joining, and spectating
46
+ attr_reader :secrets
47
+
48
+ # @return [Assets, nil] images for the presence and their texts
49
+ attr_reader :assets
50
+
51
+ # @return [Party, nil] information about the player's current party
52
+ attr_reader :party
53
+
54
+ # @return [Emoji, nil] emoji data for custom statuses
55
+ attr_reader :emoji
56
+
57
+ # @return [Time] the time when the activity was added to the user's session
58
+ attr_reader :created_at
59
+
60
+ # Type indicating the activity is for a game
61
+ GAME = 0
62
+ # Type indicating the activity is a stream
63
+ STREAMING = 1
64
+ # Type indicating the activity is for music
65
+ LISTENING = 2
66
+ # This type is currently unused in the client but can be reported by bots
67
+ WATCHING = 3
68
+ # Type indicating the activity is a custom status
69
+ CUSTOM = 4
70
+
71
+ # @!visibility private
72
+ def initialize(data, bot)
73
+ @name = data['name']
74
+ @type = data['type']
75
+ @url = data['url']
76
+ @application_id = data['application_id']
77
+ @details = data['details']
78
+ @state = data['state']
79
+ @instance = data['instance']
80
+ @flags = data['flags'] || 0
81
+ @created_at = Time.at(data['created_at'].to_i)
82
+
83
+ @timestamps = Timestamps.new(data['timestamps']) if data['timestamps']
84
+ @secrets = Secret.new(data['secrets']) if data['secrets']
85
+ @assets = Assets.new(data['assets'], @application_id) if data['assets']
86
+ @party = Party.new(data['party']) if data['party']
87
+ @emoji = Emoji.new(data['emoji'], bot, nil) if data['emoji']
88
+ end
89
+
90
+ # @return [true, false] Whether or not the `join` flag is set for this activity
91
+ def join?
92
+ flag_set? :join
93
+ end
94
+
95
+ # @return [true, false] Whether or not the `spectate` flag is set for this activity
96
+ def spectate?
97
+ flag_set? :spectate
98
+ end
99
+
100
+ # @return [true, false] Whether or not the `join_request` flag is set for this activity
101
+ def join_request?
102
+ flag_set? :join_request
103
+ end
104
+
105
+ # @return [true, false] Whether or not the `sync` flag is set for this activity
106
+ def sync?
107
+ flag_set? :sync
108
+ end
109
+
110
+ # @return [true, false] Whether or not the `play` flag is set for this activity
111
+ def play?
112
+ flag_set? :play
113
+ end
114
+
115
+ # @return [true, false] Whether or not the `instance` flag is set for this activity
116
+ def instance?
117
+ @instance || flag_set?(:instance)
118
+ end
119
+
120
+ # @!visibility private
121
+ def flag_set?(sym)
122
+ !(@flags & FLAGS[sym]).zero?
123
+ end
124
+
125
+ # Timestamps for the start and end of instanced activities
126
+ class Timestamps
127
+ # @return [Time, nil]
128
+ attr_reader :start
129
+
130
+ # @return [Time, nil]
131
+ attr_reader :end
132
+
133
+ # @!visibility private
134
+ def initialize(data)
135
+ @start = Time.at(data['start'] / 1000) if data['start']
136
+ @end = Time.at(data['end'] / 1000) if data['end']
137
+ end
138
+ end
139
+
140
+ # Contains secrets used for rich presence
141
+ class Secrets
142
+ # @return [String, nil] secret for joining a party
143
+ attr_reader :join
144
+
145
+ # @return [String, nil] secret for spectating
146
+ attr_reader :spectate
147
+
148
+ # @return [String, nil] secret for a specific instanced match
149
+ attr_reader :match
150
+
151
+ # @!visibility private
152
+ def initialize(data)
153
+ @join = data['join']
154
+ @spectate = data['spectate']
155
+ @match = data['match']
156
+ end
157
+ end
158
+
159
+ # Assets for rich presence images and hover text
160
+ class Assets
161
+ # @return [String, nil] the asset ID for the large image of this activity
162
+ attr_reader :large_image_id
163
+
164
+ # @return [String, nil] text displayed when hovering over the large iamge
165
+ attr_reader :large_text
166
+
167
+ # @return [String, nil] the asset ID for the small image of this activity
168
+ attr_reader :small_image_id
169
+
170
+ # @return [String, nil]
171
+ attr_reader :small_text
172
+
173
+ # @return [String, nil] the application ID for these assets.
174
+ attr_reader :application_id
175
+
176
+ # @!visibility private
177
+ def initialize(data, application_id)
178
+ @application_id = application_id
179
+ @large_image_id = data['large_image']
180
+ @large_text = data['large_text']
181
+ @small_image_id = data['small_image']
182
+ @small_text = data['small_text']
183
+ end
184
+
185
+ # Utility function to get an Asset's large image URL.
186
+ # @param format [String, nil] If `nil`, the URL will default to `webp`. You can otherwise specify one of `webp`, `jpg`, or `png`.
187
+ # @return [String] the URL to the large image asset.
188
+ def large_image_url(format = 'webp')
189
+ API.asset_url(@application_id, @large_image_id, format)
190
+ end
191
+
192
+ # Utility function to get an Asset's large image URL.
193
+ # @param format [String, nil] If `nil`, the URL will default to `webp`. You can otherwise specify one of `webp`, `jpg`, or `png`.
194
+ # @return [String] the URL to the small image asset.
195
+ def small_image_url(format = 'webp')
196
+ API.asset_url(@application_id, @small_image_id, format)
197
+ end
198
+ end
199
+
200
+ # Contains information about an activity's party
201
+ class Party
202
+ # @return [String, nil]
203
+ attr_reader :id
204
+
205
+ # @return [Integer, nil]
206
+ attr_reader :current_size
207
+
208
+ # @return [Integer, nil]
209
+ attr_reader :max_size
210
+
211
+ # @!visibility private
212
+ def initialize(data)
213
+ @id = data['id']
214
+ @current_size, @max_size = data['size']
215
+ end
216
+ end
217
+ end
218
+
219
+ # A collection of the user's activities.
220
+ class ActivitySet
221
+ include Enumerable
222
+
223
+ # @!visibility private
224
+ def initialize(activities = [])
225
+ @activities = activities
226
+ end
227
+
228
+ # @!visibility private
229
+ # Implement each for Enumerable
230
+ def each(&block)
231
+ @activities.each(&block)
232
+ end
233
+
234
+ # @return [Array<Activity>] all activities
235
+ def to_a
236
+ @activities
237
+ end
238
+
239
+ # @return [Array<Activity>] all activities of type {Activity::GAME}
240
+ def games
241
+ @activities.select { |act| act.type == Activity::GAME }
242
+ end
243
+
244
+ # @return [Array<Activity>] all activities of type {Activity::STREAMING}
245
+ def streaming
246
+ @activities.select { |act| act.type == Activity::STREAMING }
247
+ end
248
+
249
+ # @return [Array<Activity>] all activities of type {Activity::LISTENING}
250
+ def listening
251
+ @activities.select { |act| act.type == Activity::LISTENING }
252
+ end
253
+
254
+ # @return [Array<Activity>] all activities of type {Activity::WATCHING}
255
+ def watching
256
+ @activities.select { |act| act.type == Activity::WATCHING }
257
+ end
258
+
259
+ # @return [Array<Activity>] all activities of type {Activity::CUSTOM}
260
+ def custom_status
261
+ @activities.select { |act| act.type == Activity::CUSTOM }
262
+ end
263
+ end
264
+ end
@@ -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,56 @@
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
+ # @!visibility private
31
+ def initialize(data, message, bot)
32
+ @bot = bot
33
+ @message = message
34
+
35
+ @id = data['id'].to_i
36
+ @url = data['url']
37
+ @proxy_url = data['proxy_url']
38
+ @filename = data['filename']
39
+
40
+ @size = data['size']
41
+
42
+ @width = data['width']
43
+ @height = data['height']
44
+ end
45
+
46
+ # @return [true, false] whether this file is an image file.
47
+ def image?
48
+ !(@width.nil? || @height.nil?)
49
+ end
50
+
51
+ # @return [true, false] whether this file is tagged as a spoiler.
52
+ def spoiler?
53
+ @filename.start_with? 'SPOILER_'
54
+ end
55
+ end
56
+ 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} 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'].to_sym, allow: o['allow'], deny: o['deny']) } if @old && @key == 'permission_overwrites'
223
+ @new = @new.map { |o| Overwrite.new(o['id'], type: o['type'].to_sym, 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