discordrb 3.4.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.
- checksums.yaml +4 -4
- data/.circleci/config.yml +44 -18
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -1
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -1
- data/.github/workflows/codeql.yml +65 -0
- data/.markdownlint.json +4 -0
- data/.rubocop.yml +8 -2
- data/CHANGELOG.md +419 -222
- data/LICENSE.txt +1 -1
- data/README.md +37 -25
- data/discordrb-webhooks.gemspec +4 -1
- data/discordrb.gemspec +9 -6
- data/lib/discordrb/api/application.rb +202 -0
- data/lib/discordrb/api/channel.rb +182 -11
- data/lib/discordrb/api/interaction.rb +54 -0
- data/lib/discordrb/api/invite.rb +2 -2
- data/lib/discordrb/api/server.rb +42 -19
- data/lib/discordrb/api/user.rb +9 -3
- data/lib/discordrb/api/webhook.rb +57 -0
- data/lib/discordrb/api.rb +19 -5
- data/lib/discordrb/bot.rb +328 -33
- data/lib/discordrb/cache.rb +27 -22
- data/lib/discordrb/commands/command_bot.rb +14 -7
- data/lib/discordrb/commands/container.rb +1 -1
- data/lib/discordrb/commands/parser.rb +2 -2
- data/lib/discordrb/commands/rate_limiter.rb +1 -1
- data/lib/discordrb/container.rb +132 -3
- data/lib/discordrb/data/activity.rb +8 -1
- data/lib/discordrb/data/attachment.rb +15 -0
- data/lib/discordrb/data/audit_logs.rb +3 -3
- data/lib/discordrb/data/channel.rb +167 -23
- data/lib/discordrb/data/component.rb +229 -0
- data/lib/discordrb/data/integration.rb +42 -3
- data/lib/discordrb/data/interaction.rb +800 -0
- data/lib/discordrb/data/invite.rb +2 -2
- data/lib/discordrb/data/member.rb +108 -33
- data/lib/discordrb/data/message.rb +100 -20
- data/lib/discordrb/data/overwrite.rb +13 -7
- data/lib/discordrb/data/role.rb +58 -1
- data/lib/discordrb/data/server.rb +82 -80
- data/lib/discordrb/data/user.rb +69 -9
- data/lib/discordrb/data/webhook.rb +97 -4
- data/lib/discordrb/data.rb +3 -0
- data/lib/discordrb/errors.rb +44 -3
- data/lib/discordrb/events/channels.rb +1 -1
- data/lib/discordrb/events/interactions.rb +482 -0
- data/lib/discordrb/events/message.rb +9 -6
- data/lib/discordrb/events/presence.rb +21 -14
- data/lib/discordrb/events/reactions.rb +0 -1
- data/lib/discordrb/events/threads.rb +96 -0
- data/lib/discordrb/gateway.rb +30 -17
- data/lib/discordrb/permissions.rb +59 -34
- data/lib/discordrb/version.rb +1 -1
- data/lib/discordrb/voice/encoder.rb +13 -4
- data/lib/discordrb/voice/network.rb +18 -7
- data/lib/discordrb/voice/sodium.rb +3 -1
- data/lib/discordrb/voice/voice_bot.rb +3 -3
- data/lib/discordrb/webhooks.rb +2 -0
- data/lib/discordrb.rb +37 -4
- metadata +53 -19
- data/.codeclimate.yml +0 -16
- data/.travis.yml +0 -32
- data/bin/travis_build_docs.sh +0 -17
| @@ -87,7 +87,7 @@ module Discordrb | |
| 87 87 | 
             
                def initialize(data, bot)
         | 
| 88 88 | 
             
                  @bot = bot
         | 
| 89 89 |  | 
| 90 | 
            -
                  @channel = if data['channel_id'] | 
| 90 | 
            +
                  @channel = if data['channel_id']
         | 
| 91 91 | 
             
                               bot.channel(data['channel_id'])
         | 
| 92 92 | 
             
                             else
         | 
| 93 93 | 
             
                               InviteChannel.new(data['channel'], bot)
         | 
| @@ -100,7 +100,7 @@ module Discordrb | |
| 100 100 | 
             
                            end
         | 
| 101 101 |  | 
| 102 102 | 
             
                  @uses = data['uses']
         | 
| 103 | 
            -
                  @inviter = data['inviter'] ?  | 
| 103 | 
            +
                  @inviter = data['inviter'] ? bot.ensure_user(data['inviter']) : nil
         | 
| 104 104 | 
             
                  @temporary = data['temporary']
         | 
| 105 105 | 
             
                  @revoked = data['revoked']
         | 
| 106 106 | 
             
                  @online_member_count = data['approximate_presence_count']
         | 
| @@ -18,6 +18,10 @@ module Discordrb | |
| 18 18 |  | 
| 19 19 | 
             
                # @return [Server] the server this member is on.
         | 
| 20 20 | 
             
                attr_reader :server
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                # @return [Time] When the user's timeout will expire.
         | 
| 23 | 
            +
                attr_reader :communication_disabled_until
         | 
| 24 | 
            +
                alias_method :timeout, :communication_disabled_until
         | 
| 21 25 | 
             
              end
         | 
| 22 26 |  | 
| 23 27 | 
             
              # A member is a user on a server. It differs from regular users in that it has roles, voice statuses and things like
         | 
| @@ -62,17 +66,39 @@ module Discordrb | |
| 62 66 | 
             
                  @user = bot.ensure_user(data['user'])
         | 
| 63 67 | 
             
                  super @user # Initialize the delegate class
         | 
| 64 68 |  | 
| 65 | 
            -
                   | 
| 66 | 
            -
                   | 
| 67 | 
            -
             | 
| 68 | 
            -
                  @server = server || bot.server(data['guild_id'].to_i)
         | 
| 69 | 
            +
                  @server = server
         | 
| 70 | 
            +
                  @server_id = server&.id || data['guild_id'].to_i
         | 
| 69 71 |  | 
| 70 | 
            -
                   | 
| 71 | 
            -
                  update_roles(data['roles'])
         | 
| 72 | 
            +
                  @role_ids = data['roles']&.map(&:to_i) || []
         | 
| 72 73 |  | 
| 73 74 | 
             
                  @nick = data['nick']
         | 
| 74 75 | 
             
                  @joined_at = data['joined_at'] ? Time.parse(data['joined_at']) : nil
         | 
| 75 76 | 
             
                  @boosting_since = data['premium_since'] ? Time.parse(data['premium_since']) : nil
         | 
| 77 | 
            +
                  timeout_until = data['communication_disabled_until']
         | 
| 78 | 
            +
                  @communication_disabled_until = timeout_until ? Time.parse(timeout_until) : nil
         | 
| 79 | 
            +
                  @permissions = Permissions.new(data['permissions']) if data['permissions']
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                # @return [Server] the server this member is on.
         | 
| 83 | 
            +
                # @raise [Discordrb::Errors::NoPermission] This can happen when receiving interactions for servers in which the bot is not
         | 
| 84 | 
            +
                #   authorized with the `bot` scope.
         | 
| 85 | 
            +
                def server
         | 
| 86 | 
            +
                  return @server if @server
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  @server = @bot.server(@server_id)
         | 
| 89 | 
            +
                  raise Discordrb::Errors::NoPermission, 'The bot does not have access to this server' unless @server
         | 
| 90 | 
            +
             | 
| 91 | 
            +
                  @server
         | 
| 92 | 
            +
                end
         | 
| 93 | 
            +
             | 
| 94 | 
            +
                # @return [Array<Role>] the roles this member has.
         | 
| 95 | 
            +
                # @raise [Discordrb::Errors::NoPermission] This can happen when receiving interactions for servers in which the bot is not
         | 
| 96 | 
            +
                #   authorized with the `bot` scope.
         | 
| 97 | 
            +
                def roles
         | 
| 98 | 
            +
                  return @roles if @roles
         | 
| 99 | 
            +
             | 
| 100 | 
            +
                  update_roles(@role_ids)
         | 
| 101 | 
            +
                  @roles
         | 
| 76 102 | 
             
                end
         | 
| 77 103 |  | 
| 78 104 | 
             
                # @return [true, false] if this user is a Nitro Booster of this server.
         | 
| @@ -82,14 +108,14 @@ module Discordrb | |
| 82 108 |  | 
| 83 109 | 
             
                # @return [true, false] whether this member is the server owner.
         | 
| 84 110 | 
             
                def owner?
         | 
| 85 | 
            -
                   | 
| 111 | 
            +
                  server.owner == self
         | 
| 86 112 | 
             
                end
         | 
| 87 113 |  | 
| 88 114 | 
             
                # @param role [Role, String, Integer] the role to check or its ID.
         | 
| 89 115 | 
             
                # @return [true, false] whether this member has the specified role.
         | 
| 90 116 | 
             
                def role?(role)
         | 
| 91 117 | 
             
                  role = role.resolve_id
         | 
| 92 | 
            -
                   | 
| 118 | 
            +
                  roles.any?(role)
         | 
| 93 119 | 
             
                end
         | 
| 94 120 |  | 
| 95 121 | 
             
                # @see Member#set_roles
         | 
| @@ -97,12 +123,30 @@ module Discordrb | |
| 97 123 | 
             
                  set_roles(role)
         | 
| 98 124 | 
             
                end
         | 
| 99 125 |  | 
| 126 | 
            +
                # Check if the current user has communication disabled.
         | 
| 127 | 
            +
                # @return [true, false]
         | 
| 128 | 
            +
                def communication_disabled?
         | 
| 129 | 
            +
                  !@communication_disabled_until.nil? && @communication_disabled_until > Time.now
         | 
| 130 | 
            +
                end
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                alias_method :timeout?, :communication_disabled?
         | 
| 133 | 
            +
             | 
| 134 | 
            +
                # Set a user's timeout duration, or remove it by setting the timeout to `nil`.
         | 
| 135 | 
            +
                # @param timeout_until [Time, nil] When the timeout will end.
         | 
| 136 | 
            +
                def communication_disabled_until=(timeout_until)
         | 
| 137 | 
            +
                  raise ArgumentError, 'A time out cannot exceed 28 days' if timeout_until && timeout_until > (Time.now + 2_419_200)
         | 
| 138 | 
            +
             | 
| 139 | 
            +
                  API::Server.update_member(@bot.token, @server_id, @user.id, communication_disabled_until: timeout_until.iso8601)
         | 
| 140 | 
            +
                end
         | 
| 141 | 
            +
             | 
| 142 | 
            +
                alias_method :timeout=, :communication_disabled_until=
         | 
| 143 | 
            +
             | 
| 100 144 | 
             
                # Bulk sets a member's roles.
         | 
| 101 145 | 
             
                # @param role [Role, Array<Role>] The role(s) to set.
         | 
| 102 146 | 
             
                # @param reason [String] The reason the user's roles are being changed.
         | 
| 103 147 | 
             
                def set_roles(role, reason = nil)
         | 
| 104 148 | 
             
                  role_ids = role_id_array(role)
         | 
| 105 | 
            -
                  API::Server.update_member(@bot.token, @ | 
| 149 | 
            +
                  API::Server.update_member(@bot.token, @server_id, @user.id, roles: role_ids, reason: reason)
         | 
| 106 150 | 
             
                end
         | 
| 107 151 |  | 
| 108 152 | 
             
                # Adds and removes roles from a member.
         | 
| @@ -116,10 +160,10 @@ module Discordrb | |
| 116 160 | 
             
                def modify_roles(add, remove, reason = nil)
         | 
| 117 161 | 
             
                  add_role_ids = role_id_array(add)
         | 
| 118 162 | 
             
                  remove_role_ids = role_id_array(remove)
         | 
| 119 | 
            -
                  old_role_ids =  | 
| 163 | 
            +
                  old_role_ids = resolve_role_ids
         | 
| 120 164 | 
             
                  new_role_ids = (old_role_ids - remove_role_ids + add_role_ids).uniq
         | 
| 121 165 |  | 
| 122 | 
            -
                  API::Server.update_member(@bot.token, @ | 
| 166 | 
            +
                  API::Server.update_member(@bot.token, @server_id, @user.id, roles: new_role_ids, reason: reason)
         | 
| 123 167 | 
             
                end
         | 
| 124 168 |  | 
| 125 169 | 
             
                # Adds one or more roles to this member.
         | 
| @@ -129,11 +173,11 @@ module Discordrb | |
| 129 173 | 
             
                  role_ids = role_id_array(role)
         | 
| 130 174 |  | 
| 131 175 | 
             
                  if role_ids.count == 1
         | 
| 132 | 
            -
                    API::Server.add_member_role(@bot.token, @ | 
| 176 | 
            +
                    API::Server.add_member_role(@bot.token, @server_id, @user.id, role_ids[0], reason)
         | 
| 133 177 | 
             
                  else
         | 
| 134 | 
            -
                    old_role_ids =  | 
| 178 | 
            +
                    old_role_ids = resolve_role_ids
         | 
| 135 179 | 
             
                    new_role_ids = (old_role_ids + role_ids).uniq
         | 
| 136 | 
            -
                    API::Server.update_member(@bot.token, @ | 
| 180 | 
            +
                    API::Server.update_member(@bot.token, @server_id, @user.id, roles: new_role_ids, reason: reason)
         | 
| 137 181 | 
             
                  end
         | 
| 138 182 | 
             
                end
         | 
| 139 183 |  | 
| @@ -144,22 +188,22 @@ module Discordrb | |
| 144 188 | 
             
                  role_ids = role_id_array(role)
         | 
| 145 189 |  | 
| 146 190 | 
             
                  if role_ids.count == 1
         | 
| 147 | 
            -
                    API::Server.remove_member_role(@bot.token, @ | 
| 191 | 
            +
                    API::Server.remove_member_role(@bot.token, @server_id, @user.id, role_ids[0], reason)
         | 
| 148 192 | 
             
                  else
         | 
| 149 | 
            -
                    old_role_ids =  | 
| 193 | 
            +
                    old_role_ids = resolve_role_ids
         | 
| 150 194 | 
             
                    new_role_ids = old_role_ids.reject { |i| role_ids.include?(i) }
         | 
| 151 | 
            -
                    API::Server.update_member(@bot.token, @ | 
| 195 | 
            +
                    API::Server.update_member(@bot.token, @server_id, @user.id, roles: new_role_ids, reason: reason)
         | 
| 152 196 | 
             
                  end
         | 
| 153 197 | 
             
                end
         | 
| 154 198 |  | 
| 155 199 | 
             
                # @return [Role] the highest role this member has.
         | 
| 156 200 | 
             
                def highest_role
         | 
| 157 | 
            -
                   | 
| 201 | 
            +
                  roles.max_by(&:position)
         | 
| 158 202 | 
             
                end
         | 
| 159 203 |  | 
| 160 204 | 
             
                # @return [Role, nil] the role this member is being hoisted with.
         | 
| 161 205 | 
             
                def hoist_role
         | 
| 162 | 
            -
                  hoisted_roles =  | 
| 206 | 
            +
                  hoisted_roles = roles.select(&:hoist)
         | 
| 163 207 | 
             
                  return nil if hoisted_roles.empty?
         | 
| 164 208 |  | 
| 165 209 | 
             
                  hoisted_roles.max_by(&:position)
         | 
| @@ -167,7 +211,7 @@ module Discordrb | |
| 167 211 |  | 
| 168 212 | 
             
                # @return [Role, nil] the role this member is basing their colour on.
         | 
| 169 213 | 
             
                def colour_role
         | 
| 170 | 
            -
                  coloured_roles =  | 
| 214 | 
            +
                  coloured_roles = roles.select { |v| v.colour.combined.nonzero? }
         | 
| 171 215 | 
             
                  return nil if coloured_roles.empty?
         | 
| 172 216 |  | 
| 173 217 | 
             
                  coloured_roles.max_by(&:position)
         | 
| @@ -184,22 +228,41 @@ module Discordrb | |
| 184 228 |  | 
| 185 229 | 
             
                # Server deafens this member.
         | 
| 186 230 | 
             
                def server_deafen
         | 
| 187 | 
            -
                  API::Server.update_member(@bot.token, @ | 
| 231 | 
            +
                  API::Server.update_member(@bot.token, @server_id, @user.id, deaf: true)
         | 
| 188 232 | 
             
                end
         | 
| 189 233 |  | 
| 190 234 | 
             
                # Server undeafens this member.
         | 
| 191 235 | 
             
                def server_undeafen
         | 
| 192 | 
            -
                  API::Server.update_member(@bot.token, @ | 
| 236 | 
            +
                  API::Server.update_member(@bot.token, @server_id, @user.id, deaf: false)
         | 
| 193 237 | 
             
                end
         | 
| 194 238 |  | 
| 195 239 | 
             
                # Server mutes this member.
         | 
| 196 240 | 
             
                def server_mute
         | 
| 197 | 
            -
                  API::Server.update_member(@bot.token, @ | 
| 241 | 
            +
                  API::Server.update_member(@bot.token, @server_id, @user.id, mute: true)
         | 
| 198 242 | 
             
                end
         | 
| 199 243 |  | 
| 200 244 | 
             
                # Server unmutes this member.
         | 
| 201 245 | 
             
                def server_unmute
         | 
| 202 | 
            -
                  API::Server.update_member(@bot.token, @ | 
| 246 | 
            +
                  API::Server.update_member(@bot.token, @server_id, @user.id, mute: false)
         | 
| 247 | 
            +
                end
         | 
| 248 | 
            +
             | 
| 249 | 
            +
                # Bans this member from the server.
         | 
| 250 | 
            +
                # @param message_days [Integer] How many days worth of messages sent by the member should be deleted.
         | 
| 251 | 
            +
                # @param reason [String] The reason this member is being banned.
         | 
| 252 | 
            +
                def ban(message_days = 0, reason: nil)
         | 
| 253 | 
            +
                  server.ban(@user, message_days, reason: reason)
         | 
| 254 | 
            +
                end
         | 
| 255 | 
            +
             | 
| 256 | 
            +
                # Unbans this member from the server.
         | 
| 257 | 
            +
                # @param reason [String] The reason this member is being unbanned.
         | 
| 258 | 
            +
                def unban(reason = nil)
         | 
| 259 | 
            +
                  server.unban(@user, reason)
         | 
| 260 | 
            +
                end
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                # Kicks this member from the server.
         | 
| 263 | 
            +
                # @param reason [String] The reason this member is being kicked.
         | 
| 264 | 
            +
                def kick(reason = nil)
         | 
| 265 | 
            +
                  server.kick(@user, reason)
         | 
| 203 266 | 
             
                end
         | 
| 204 267 |  | 
| 205 268 | 
             
                # @see Member#set_nick
         | 
| @@ -218,28 +281,28 @@ module Discordrb | |
| 218 281 | 
             
                  nick ||= ''
         | 
| 219 282 |  | 
| 220 283 | 
             
                  if @user.current_bot?
         | 
| 221 | 
            -
                    API::User.change_own_nickname(@bot.token, @ | 
| 284 | 
            +
                    API::User.change_own_nickname(@bot.token, @server_id, nick, reason)
         | 
| 222 285 | 
             
                  else
         | 
| 223 | 
            -
                    API::Server.update_member(@bot.token, @ | 
| 286 | 
            +
                    API::Server.update_member(@bot.token, @server_id, @user.id, nick: nick, reason: nil)
         | 
| 224 287 | 
             
                  end
         | 
| 225 288 | 
             
                end
         | 
| 226 289 |  | 
| 227 290 | 
             
                alias_method :set_nickname, :set_nick
         | 
| 228 291 |  | 
| 229 | 
            -
                # @return [String] the name the user displays as (nickname if they have one, username otherwise)
         | 
| 292 | 
            +
                # @return [String] the name the user displays as (nickname if they have one, global_name if they have one, username otherwise)
         | 
| 230 293 | 
             
                def display_name
         | 
| 231 | 
            -
                  nickname || username
         | 
| 294 | 
            +
                  nickname || global_name || username
         | 
| 232 295 | 
             
                end
         | 
| 233 296 |  | 
| 234 297 | 
             
                # Update this member's roles
         | 
| 235 298 | 
             
                # @note For internal use only.
         | 
| 236 299 | 
             
                # @!visibility private
         | 
| 237 300 | 
             
                def update_roles(role_ids)
         | 
| 238 | 
            -
                  @roles = [ | 
| 301 | 
            +
                  @roles = [server.role(@server_id)]
         | 
| 239 302 | 
             
                  role_ids.each do |id|
         | 
| 240 303 | 
             
                    # It is possible for members to have roles that do not exist
         | 
| 241 | 
            -
                    # on the server any longer. See https://github.com/ | 
| 242 | 
            -
                    role =  | 
| 304 | 
            +
                    # on the server any longer. See https://github.com/discordrb/discordrb/issues/371
         | 
| 305 | 
            +
                    role = server.role(id)
         | 
| 243 306 | 
             
                    @roles << role if role
         | 
| 244 307 | 
             
                  end
         | 
| 245 308 | 
             
                end
         | 
| @@ -258,6 +321,12 @@ module Discordrb | |
| 258 321 | 
             
                  @boosting_since = time
         | 
| 259 322 | 
             
                end
         | 
| 260 323 |  | 
| 324 | 
            +
                # @!visibility private
         | 
| 325 | 
            +
                def update_communication_disabled_until(time)
         | 
| 326 | 
            +
                  time = time ? Time.parse(time) : nil
         | 
| 327 | 
            +
                  @communication_disabled_until = time
         | 
| 328 | 
            +
                end
         | 
| 329 | 
            +
             | 
| 261 330 | 
             
                # Update this member
         | 
| 262 331 | 
             
                # @note For internal use only.
         | 
| 263 332 | 
             
                # @!visibility private
         | 
| @@ -268,13 +337,15 @@ module Discordrb | |
| 268 337 | 
             
                  @deaf = data['deaf'] if data.key?('deaf')
         | 
| 269 338 |  | 
| 270 339 | 
             
                  @joined_at = Time.parse(data['joined_at']) if data['joined_at']
         | 
| 340 | 
            +
                  timeout_until = data['communication_disabled_until']
         | 
| 341 | 
            +
                  @communication_disabled_until = timeout_until ? Time.parse(timeout_until) : nil
         | 
| 271 342 | 
             
                end
         | 
| 272 343 |  | 
| 273 344 | 
             
                include PermissionCalculator
         | 
| 274 345 |  | 
| 275 346 | 
             
                # Overwriting inspect for debug purposes
         | 
| 276 347 | 
             
                def inspect
         | 
| 277 | 
            -
                  "<Member user=#{@user.inspect} server=#{@server | 
| 348 | 
            +
                  "<Member user=#{@user.inspect} server=#{@server&.inspect || @server_id} joined_at=#{@joined_at} roles=#{@roles&.inspect || @role_ids} voice_channel=#{@voice_channel.inspect} mute=#{@mute} deaf=#{@deaf} self_mute=#{@self_mute} self_deaf=#{@self_deaf}>"
         | 
| 278 349 | 
             
                end
         | 
| 279 350 |  | 
| 280 351 | 
             
                private
         | 
| @@ -290,8 +361,12 @@ module Discordrb | |
| 290 361 |  | 
| 291 362 | 
             
                # Utility method to get data out of this member's voice state
         | 
| 292 363 | 
             
                def voice_state_attribute(name)
         | 
| 293 | 
            -
                  voice_state =  | 
| 364 | 
            +
                  voice_state = server.voice_states[@user.id]
         | 
| 294 365 | 
             
                  voice_state&.send name
         | 
| 295 366 | 
             
                end
         | 
| 367 | 
            +
             | 
| 368 | 
            +
                def resolve_role_ids
         | 
| 369 | 
            +
                  @roles ? @roles.collect(&:id) : @role_ids
         | 
| 370 | 
            +
                end
         | 
| 296 371 | 
             
              end
         | 
| 297 372 | 
             
            end
         | 
| @@ -61,14 +61,17 @@ module Discordrb | |
| 61 61 | 
             
                attr_reader :pinned
         | 
| 62 62 | 
             
                alias_method :pinned?, :pinned
         | 
| 63 63 |  | 
| 64 | 
            +
                # @return [Integer] what the type of the message is
         | 
| 65 | 
            +
                attr_reader :type
         | 
| 66 | 
            +
             | 
| 64 67 | 
             
                # @return [Server, nil] the server in which this message was sent.
         | 
| 65 68 | 
             
                attr_reader :server
         | 
| 66 69 |  | 
| 67 70 | 
             
                # @return [Integer, nil] the webhook ID that sent this message, or `nil` if it wasn't sent through a webhook.
         | 
| 68 71 | 
             
                attr_reader :webhook_id
         | 
| 69 72 |  | 
| 70 | 
            -
                #  | 
| 71 | 
            -
                 | 
| 73 | 
            +
                # @return [Array<Component>]
         | 
| 74 | 
            +
                attr_reader :components
         | 
| 72 75 |  | 
| 73 76 | 
             
                # @!visibility private
         | 
| 74 77 | 
             
                def initialize(data, bot)
         | 
| @@ -76,6 +79,7 @@ module Discordrb | |
| 76 79 | 
             
                  @content = data['content']
         | 
| 77 80 | 
             
                  @channel = bot.channel(data['channel_id'].to_i)
         | 
| 78 81 | 
             
                  @pinned = data['pinned']
         | 
| 82 | 
            +
                  @type = data['type']
         | 
| 79 83 | 
             
                  @tts = data['tts']
         | 
| 80 84 | 
             
                  @nonce = data['nonce']
         | 
| 81 85 | 
             
                  @mention_everyone = data['mention_everyone']
         | 
| @@ -83,14 +87,16 @@ module Discordrb | |
| 83 87 | 
             
                  @referenced_message = Message.new(data['referenced_message'], bot) if data['referenced_message']
         | 
| 84 88 | 
             
                  @message_reference = data['message_reference']
         | 
| 85 89 |  | 
| 86 | 
            -
                  @server =  | 
| 90 | 
            +
                  @server = @channel.server
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                  @webhook_id = data['webhook_id']&.to_i
         | 
| 87 93 |  | 
| 88 94 | 
             
                  @author = if data['author']
         | 
| 89 | 
            -
                              if  | 
| 95 | 
            +
                              if @webhook_id
         | 
| 90 96 | 
             
                                # This is a webhook user! It would be pointless to try to resolve a member here, so we just create
         | 
| 91 97 | 
             
                                # a User and return that instead.
         | 
| 92 98 | 
             
                                Discordrb::LOGGER.debug("Webhook user: #{data['author']['id']}")
         | 
| 93 | 
            -
                                User.new(data['author'], @bot)
         | 
| 99 | 
            +
                                User.new(data['author'].merge({ '_webhook' => true }), @bot)
         | 
| 94 100 | 
             
                              elsif @channel.private?
         | 
| 95 101 | 
             
                                # Turn the message user into a recipient - we can't use the channel recipient
         | 
| 96 102 | 
             
                                # directly because the bot may also send messages to the channel
         | 
| @@ -100,11 +106,12 @@ module Discordrb | |
| 100 106 |  | 
| 101 107 | 
             
                                if member
         | 
| 102 108 | 
             
                                  member.update_data(data['member']) if data['member']
         | 
| 109 | 
            +
                                  member.update_global_name(data['author']['global_name']) if data['author']['global_name']
         | 
| 103 110 | 
             
                                else
         | 
| 104 111 | 
             
                                  Discordrb::LOGGER.debug("Member with ID #{data['author']['id']} not cached (possibly left the server).")
         | 
| 105 112 | 
             
                                  member = if data['member']
         | 
| 106 113 | 
             
                                             member_data = data['author'].merge(data['member'])
         | 
| 107 | 
            -
                                             Member.new(member_data, bot)
         | 
| 114 | 
            +
                                             Member.new(member_data, @server, bot)
         | 
| 108 115 | 
             
                                           else
         | 
| 109 116 | 
             
                                             @bot.ensure_user(data['author'])
         | 
| 110 117 | 
             
                                           end
         | 
| @@ -114,8 +121,6 @@ module Discordrb | |
| 114 121 | 
             
                              end
         | 
| 115 122 | 
             
                            end
         | 
| 116 123 |  | 
| 117 | 
            -
                  @webhook_id = data['webhook_id'].to_i if data['webhook_id']
         | 
| 118 | 
            -
             | 
| 119 124 | 
             
                  @timestamp = Time.parse(data['timestamp']) if data['timestamp']
         | 
| 120 125 | 
             
                  @edited_timestamp = data['edited_timestamp'].nil? ? nil : Time.parse(data['edited_timestamp'])
         | 
| 121 126 | 
             
                  @edited = !@edited_timestamp.nil?
         | 
| @@ -149,43 +154,53 @@ module Discordrb | |
| 149 154 |  | 
| 150 155 | 
             
                  @embeds = []
         | 
| 151 156 | 
             
                  @embeds = data['embeds'].map { |e| Embed.new(e, self) } if data['embeds']
         | 
| 157 | 
            +
             | 
| 158 | 
            +
                  @components = []
         | 
| 159 | 
            +
                  @components = data['components'].map { |component_data| Components.from_data(component_data, @bot) } if data['components']
         | 
| 152 160 | 
             
                end
         | 
| 153 161 |  | 
| 154 162 | 
             
                # Replies to this message with the specified content.
         | 
| 155 163 | 
             
                # @deprecated Please use {#respond}.
         | 
| 164 | 
            +
                # @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
         | 
| 165 | 
            +
                # @return (see #respond)
         | 
| 156 166 | 
             
                # @see Channel#send_message
         | 
| 157 167 | 
             
                def reply(content)
         | 
| 158 168 | 
             
                  @channel.send_message(content)
         | 
| 159 169 | 
             
                end
         | 
| 160 170 |  | 
| 161 | 
            -
                #  | 
| 171 | 
            +
                # Responds to this message as an inline reply.
         | 
| 162 172 | 
             
                # @param content [String] The content to send. Should not be longer than 2000 characters or it will result in an error.
         | 
| 163 173 | 
             
                # @param tts [true, false] Whether or not this message should be sent using Discord text-to-speech.
         | 
| 164 174 | 
             
                # @param embed [Hash, Discordrb::Webhooks::Embed, nil] The rich embed to append to this message.
         | 
| 165 175 | 
             
                # @param attachments [Array<File>] Files that can be referenced in embeds via `attachment://file.png`
         | 
| 166 176 | 
             
                # @param allowed_mentions [Hash, Discordrb::AllowedMentions, false, nil] Mentions that are allowed to ping on this message. `false` disables all pings
         | 
| 167 177 | 
             
                # @param mention_user [true, false] Whether the user that is being replied to should be pinged by the reply.
         | 
| 168 | 
            -
                # @ | 
| 169 | 
            -
                 | 
| 178 | 
            +
                # @param components [View, Array<Hash>] Interaction components to associate with this message.
         | 
| 179 | 
            +
                # @return (see #respond)
         | 
| 180 | 
            +
                def reply!(content, tts: false, embed: nil, attachments: nil, allowed_mentions: {}, mention_user: false, components: nil)
         | 
| 170 181 | 
             
                  allowed_mentions = { parse: [] } if allowed_mentions == false
         | 
| 171 182 | 
             
                  allowed_mentions = allowed_mentions.to_hash.transform_keys(&:to_sym)
         | 
| 172 183 | 
             
                  allowed_mentions[:replied_user] = mention_user
         | 
| 173 184 |  | 
| 174 | 
            -
                  respond(content, tts, embed, attachments, allowed_mentions, self)
         | 
| 185 | 
            +
                  respond(content, tts, embed, attachments, allowed_mentions, self, components)
         | 
| 175 186 | 
             
                end
         | 
| 176 187 |  | 
| 177 188 | 
             
                # (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)
         | 
| 189 | 
            +
                def respond(content, tts = false, embed = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
         | 
| 190 | 
            +
                  @channel.send_message(content, tts, embed, attachments, allowed_mentions, message_reference, components)
         | 
| 180 191 | 
             
                end
         | 
| 181 192 |  | 
| 182 193 | 
             
                # Edits this message to have the specified content instead.
         | 
| 183 194 | 
             
                # You can only edit your own messages.
         | 
| 184 195 | 
             
                # @param new_content [String] the new content the message should have.
         | 
| 185 | 
            -
                # @param  | 
| 196 | 
            +
                # @param new_embeds [Hash, Discordrb::Webhooks::Embed, Array<Hash>, Array<Discordrb::Webhooks::Embed>, nil] The new embeds the message should have. If `nil` the message will be changed to have no embeds.
         | 
| 197 | 
            +
                # @param new_components [View, Array<Hash>] The new components the message should have. If `nil` the message will be changed to have no components.
         | 
| 186 198 | 
             
                # @return [Message] the resulting message.
         | 
| 187 | 
            -
                def edit(new_content,  | 
| 188 | 
            -
                   | 
| 199 | 
            +
                def edit(new_content, new_embeds = nil, new_components = nil)
         | 
| 200 | 
            +
                  new_embeds = (new_embeds.instance_of?(Array) ? new_embeds.map(&:to_hash) : [new_embeds&.to_hash]).compact
         | 
| 201 | 
            +
                  new_components = new_components&.to_a || []
         | 
| 202 | 
            +
             | 
| 203 | 
            +
                  response = API::Channel.edit_message(@bot.token, @channel.id, @id, new_content, [], new_embeds, new_components)
         | 
| 189 204 | 
             
                  Message.new(JSON.parse(response), @bot)
         | 
| 190 205 | 
             
                end
         | 
| 191 206 |  | 
| @@ -222,6 +237,19 @@ module Discordrb | |
| 222 237 | 
             
                  @bot.add_await!(Discordrb::Events::MessageEvent, { from: @author.id, in: @channel.id }.merge(attributes), &block)
         | 
| 223 238 | 
             
                end
         | 
| 224 239 |  | 
| 240 | 
            +
                # Add an {Await} for a reaction to be added on this message.
         | 
| 241 | 
            +
                # @see Bot#add_await
         | 
| 242 | 
            +
                # @deprecated Will be changed to blocking behavior in v4.0. Use {#await_reaction!} instead.
         | 
| 243 | 
            +
                def await_reaction(key, attributes = {}, &block)
         | 
| 244 | 
            +
                  @bot.add_await(key, Discordrb::Events::ReactionAddEvent, { message: @id }.merge(attributes), &block)
         | 
| 245 | 
            +
                end
         | 
| 246 | 
            +
             | 
| 247 | 
            +
                # Add a blocking {Await} for a reaction to be added on this message.
         | 
| 248 | 
            +
                # @see Bot#add_await!
         | 
| 249 | 
            +
                def await_reaction!(attributes = {}, &block)
         | 
| 250 | 
            +
                  @bot.add_await!(Discordrb::Events::ReactionAddEvent, { message: @id }.merge(attributes), &block)
         | 
| 251 | 
            +
                end
         | 
| 252 | 
            +
             | 
| 225 253 | 
             
                # @return [true, false] whether this message was sent by the current {Bot}.
         | 
| 226 254 | 
             
                def from_bot?
         | 
| 227 255 | 
             
                  @author&.current_bot?
         | 
| @@ -276,14 +304,38 @@ module Discordrb | |
| 276 304 | 
             
                # @return [Array<User>] the users who used this reaction
         | 
| 277 305 | 
             
                def reacted_with(reaction, limit: 100)
         | 
| 278 306 | 
             
                  reaction = reaction.to_reaction if reaction.respond_to?(:to_reaction)
         | 
| 307 | 
            +
                  reaction = reaction.to_s if reaction.respond_to?(:to_s)
         | 
| 308 | 
            +
             | 
| 309 | 
            +
                  get_reactions = proc do |fetch_limit, after_id = nil|
         | 
| 310 | 
            +
                    resp = API::Channel.get_reactions(@bot.token, @channel.id, @id, reaction, nil, after_id, fetch_limit)
         | 
| 311 | 
            +
                    return JSON.parse(resp).map { |d| User.new(d, @bot) }
         | 
| 312 | 
            +
                  end
         | 
| 313 | 
            +
             | 
| 314 | 
            +
                  # Can be done without pagination
         | 
| 315 | 
            +
                  return get_reactions.call(limit) if limit && limit <= 100
         | 
| 316 | 
            +
             | 
| 279 317 | 
             
                  paginator = Paginator.new(limit, :down) do |last_page|
         | 
| 280 | 
            -
                     | 
| 281 | 
            -
             | 
| 282 | 
            -
                     | 
| 318 | 
            +
                    if last_page && last_page.count < 100
         | 
| 319 | 
            +
                      []
         | 
| 320 | 
            +
                    else
         | 
| 321 | 
            +
                      get_reactions.call(100, last_page&.last&.id)
         | 
| 322 | 
            +
                    end
         | 
| 283 323 | 
             
                  end
         | 
| 324 | 
            +
             | 
| 284 325 | 
             
                  paginator.to_a
         | 
| 285 326 | 
             
                end
         | 
| 286 327 |  | 
| 328 | 
            +
                # Returns a hash of all reactions to a message as keys and the users that reacted to it as values.
         | 
| 329 | 
            +
                # @param limit [Integer] the limit of how many users to retrieve per distinct reaction emoji. `nil` will return all users
         | 
| 330 | 
            +
                # @example Get all the users that reacted to a message for a giveaway.
         | 
| 331 | 
            +
                #   giveaway_participants = message.all_reaction_users
         | 
| 332 | 
            +
                # @return [Hash<String => Array<User>>] A hash mapping the string representation of a
         | 
| 333 | 
            +
                #   reaction to an array of users.
         | 
| 334 | 
            +
                def all_reaction_users(limit: 100)
         | 
| 335 | 
            +
                  all_reactions = @reactions.map { |r| { r.to_s => reacted_with(r, limit: limit) } }
         | 
| 336 | 
            +
                  all_reactions.reduce({}, :merge)
         | 
| 337 | 
            +
                end
         | 
| 338 | 
            +
             | 
| 287 339 | 
             
                # Deletes a reaction made by a user on this message.
         | 
| 288 340 | 
             
                # @param user [User, String, Integer] the user or user ID who used this reaction
         | 
| 289 341 | 
             
                # @param reaction [String, #to_reaction] the reaction to remove
         | 
| @@ -322,6 +374,12 @@ module Discordrb | |
| 322 374 | 
             
                  !@referenced_message.nil?
         | 
| 323 375 | 
             
                end
         | 
| 324 376 |  | 
| 377 | 
            +
                # Whether or not this message was of type "CHAT_INPUT_COMMAND"
         | 
| 378 | 
            +
                # @return [true, false]
         | 
| 379 | 
            +
                def chat_input_command?
         | 
| 380 | 
            +
                  @type == 20
         | 
| 381 | 
            +
                end
         | 
| 382 | 
            +
             | 
| 325 383 | 
             
                # @return [Message, nil] the Message this Message was sent in reply to.
         | 
| 326 384 | 
             
                def referenced_message
         | 
| 327 385 | 
             
                  return @referenced_message if @referenced_message
         | 
| @@ -330,5 +388,27 @@ module Discordrb | |
| 330 388 | 
             
                  referenced_channel = @bot.channel(@message_reference['channel_id'])
         | 
| 331 389 | 
             
                  @referenced_message = referenced_channel.message(@message_reference['message_id'])
         | 
| 332 390 | 
             
                end
         | 
| 391 | 
            +
             | 
| 392 | 
            +
                # @return [Array<Components::Button>]
         | 
| 393 | 
            +
                def buttons
         | 
| 394 | 
            +
                  results = @components.collect do |component|
         | 
| 395 | 
            +
                    case component
         | 
| 396 | 
            +
                    when Components::Button
         | 
| 397 | 
            +
                      component
         | 
| 398 | 
            +
                    when Components::ActionRow
         | 
| 399 | 
            +
                      component.buttons
         | 
| 400 | 
            +
                    end
         | 
| 401 | 
            +
                  end
         | 
| 402 | 
            +
             | 
| 403 | 
            +
                  results.flatten.compact
         | 
| 404 | 
            +
                end
         | 
| 405 | 
            +
             | 
| 406 | 
            +
                # to_message -> self or message
         | 
| 407 | 
            +
                # @return [Discordrb::Message]
         | 
| 408 | 
            +
                def to_message
         | 
| 409 | 
            +
                  self
         | 
| 410 | 
            +
                end
         | 
| 411 | 
            +
             | 
| 412 | 
            +
                alias_method :message, :to_message
         | 
| 333 413 | 
             
              end
         | 
| 334 414 | 
             
            end
         | 
| @@ -4,6 +4,12 @@ module Discordrb | |
| 4 4 | 
             
              # A permissions overwrite, when applied to channels describes additional
         | 
| 5 5 | 
             
              # permissions a member needs to perform certain actions in context.
         | 
| 6 6 | 
             
              class Overwrite
         | 
| 7 | 
            +
                # Types of overwrites mapped to their API value.
         | 
| 8 | 
            +
                TYPES = {
         | 
| 9 | 
            +
                  role: 0,
         | 
| 10 | 
            +
                  member: 1
         | 
| 11 | 
            +
                }.freeze
         | 
| 12 | 
            +
             | 
| 7 13 | 
             
                # @return [Integer] ID of the thing associated with this overwrite type
         | 
| 8 14 | 
             
                attr_accessor :id
         | 
| 9 15 |  | 
| @@ -32,14 +38,14 @@ module Discordrb | |
| 32 38 | 
             
                # @example Create an overwrite by ID and permissions bits
         | 
| 33 39 | 
             
                #   Overwrite.new(120571255635181568, type: 'member', allow: 1024, deny: 0)
         | 
| 34 40 | 
             
                # @param object [Integer, #id] the ID or object this overwrite is for
         | 
| 35 | 
            -
                # @param type [String] the type of object this overwrite is for (only required if object is an Integer)
         | 
| 36 | 
            -
                # @param allow [Integer, Permissions] allowed permissions for this overwrite, by bits or a Permissions object
         | 
| 37 | 
            -
                # @param deny [Integer, Permissions] denied permissions for this overwrite, by bits or a Permissions object
         | 
| 41 | 
            +
                # @param type [String, Symbol, Integer] the type of object this overwrite is for (only required if object is an Integer)
         | 
| 42 | 
            +
                # @param allow [String, Integer, Permissions] allowed permissions for this overwrite, by bits or a Permissions object
         | 
| 43 | 
            +
                # @param deny [String, Integer, Permissions] denied permissions for this overwrite, by bits or a Permissions object
         | 
| 38 44 | 
             
                # @raise [ArgumentError] if type is not :member or :role
         | 
| 39 45 | 
             
                def initialize(object = nil, type: nil, allow: 0, deny: 0)
         | 
| 40 46 | 
             
                  if type
         | 
| 41 | 
            -
                    type = type.to_sym
         | 
| 42 | 
            -
                    raise ArgumentError, 'Overwrite type must be :member or :role' unless  | 
| 47 | 
            +
                    type = TYPES.value?(type) ? TYPES.key(type) : type.to_sym
         | 
| 48 | 
            +
                    raise ArgumentError, 'Overwrite type must be :member or :role' unless type
         | 
| 43 49 | 
             
                  end
         | 
| 44 50 |  | 
| 45 51 | 
             
                  @id = object.respond_to?(:id) ? object.id : object
         | 
| @@ -71,7 +77,7 @@ module Discordrb | |
| 71 77 | 
             
                def self.from_hash(data)
         | 
| 72 78 | 
             
                  new(
         | 
| 73 79 | 
             
                    data['id'].to_i,
         | 
| 74 | 
            -
                    type: data['type'],
         | 
| 80 | 
            +
                    type: TYPES.key(data['type']),
         | 
| 75 81 | 
             
                    allow: Permissions.new(data['allow']),
         | 
| 76 82 | 
             
                    deny: Permissions.new(data['deny'])
         | 
| 77 83 | 
             
                  )
         | 
| @@ -93,7 +99,7 @@ module Discordrb | |
| 93 99 | 
             
                def to_hash
         | 
| 94 100 | 
             
                  {
         | 
| 95 101 | 
             
                    id: id,
         | 
| 96 | 
            -
                    type: type,
         | 
| 102 | 
            +
                    type: TYPES[type],
         | 
| 97 103 | 
             
                    allow: allow.bits,
         | 
| 98 104 | 
             
                    deny: deny.bits
         | 
| 99 105 | 
             
                  }
         |