module Discordrb::PermissionCalculator

Mixin to calculate resulting permissions from overrides etc.

Public Instance Methods

defined_permission?(action, channel = nil) click to toggle source

Checks whether this user has a particular permission defined (i.e. not implicit, through for example Manage Roles) @param action [Symbol] The permission that should be checked. See also {Permissions::FLAGS} for a list. @param channel [Channel, nil] If channel overrides should be checked too, this channel specifies where the overrides should be checked. @example Check if a member has the Manage Channels permission defined in the server.

has_manage_channels = member.defined_permission?(:manage_channels)

@return [true, false] whether or not this user has the permission defined.

# File lib/discordrb/permissions.rb, line 175
def defined_permission?(action, channel = nil)
  # For slash commands we may not have access to the server or role
  # permissions. In this case we use the permissions given to us by the
  # interaction. If attempting to check against a specific channel the check
  # is skipped.
  return @permissions.__send__(action) if @permissions && channel.nil?

  # Get the permission the user's roles have
  role_permission = defined_role_permission?(action, channel)

  # Once we have checked the role permission, we have to check the channel overrides for the
  # specific user
  user_specific_override = permission_overwrite(action, channel, id) # Use the ID reader as members have no ID instance variable

  # Merge the two permissions - if an override is defined, it has to be allow, otherwise we only care about the role
  return role_permission unless user_specific_override

  user_specific_override == :allow
end
permission?(action, channel = nil) click to toggle source

Checks whether this user can do the particular action, regardless of whether it has the permission defined, through for example being the server owner or having the Manage Roles permission @param action [Symbol] The permission that should be checked. See also {Permissions::FLAGS} for a list. @param channel [Channel, nil] If channel overrides should be checked too, this channel specifies where the overrides should be checked. @example Check if the bot can send messages to a specific channel in a server.

bot_profile = bot.profile.on(event.server)
can_send_messages = bot_profile.permission?(:send_messages, channel)

@return [true, false] whether or not this user has the permission.

# File lib/discordrb/permissions.rb, line 154
def permission?(action, channel = nil)
  # If the member is the server owner, it irrevocably has all permissions.
  return true if owner?

  # First, check whether the user has Manage Roles defined.
  # (Coincidentally, Manage Permissions is the same permission as Manage Roles, and a
  # Manage Permissions deny overwrite will override Manage Roles, so we can just check for
  # Manage Roles once and call it a day.)
  return true if defined_permission?(:administrator, channel)

  # Otherwise, defer to defined_permission
  defined_permission?(action, channel)
end

Private Instance Methods

defined_role_permission?(action, channel) click to toggle source
# File lib/discordrb/permissions.rb, line 206
def defined_role_permission?(action, channel)
  roles_to_check = [@server.everyone_role] + roles

  # For each role, check if
  #   (1) the channel explicitly allows or permits an action for the role and
  #   (2) if the user is allowed to do the action if the channel doesn't specify
  roles_to_check.sort_by(&:position).reduce(false) do |can_act, role|
    # Get the override defined for the role on the channel
    channel_allow = permission_overwrite(action, channel, role.id)
    if channel_allow
      # If the channel has an override, check whether it is an allow - if yes,
      # the user can act, if not, it can't
      break true if channel_allow == :allow

      false
    else
      # Otherwise defer to the role
      role.permissions.instance_variable_get("@#{action}") || can_act
    end
  end
end
permission_overwrite(action, channel, id) click to toggle source
# File lib/discordrb/permissions.rb, line 228
def permission_overwrite(action, channel, id)
  # If no overwrites are defined, or no channel is set, no overwrite will be present
  return nil unless channel && channel.permission_overwrites[id]

  # Otherwise, check the allow and deny objects
  allow = channel.permission_overwrites[id].allow
  deny = channel.permission_overwrites[id].deny
  if allow.instance_variable_get("@#{action}")
    :allow
  elsif deny.instance_variable_get("@#{action}")
    :deny
  end

  # If there's no variable defined, nil will implicitly be returned
end