class Discorb::Client

Class for connecting to the Discord server.

Attributes

allowed_mentions[R]

@return [Discorb::AllowedMentions] The allowed mentions that the client is using.

api_version[R]

@return [Integer] The API version of the Discord gateway. @return [nil] If not connected to the gateway.

application[R]

@return [Discorb::Application] The application that the client is using. @return [nil] If never fetched application by {#fetch_application}.

bottom_commands[R]

@private

channels[R]

@return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Channel}] A dictionary of channels.

commands[R]

@return [Array<Discorb::Command::Command>] The commands that the client is using.

emojis[R]

@return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Emoji}] A dictionary of emojis.

guilds[R]

@return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Guild}] A dictionary of guilds.

heartbeat_interval[R]

@return [Integer] The heartbeat interval.

http[R]

@return [Discorb::HTTP] The http client.

intents[RW]

@return [Discorb::Intents] The intents that the client is currently using.

log[R]

@return [Discorb::Logger] The logger.

messages[R]

@return [Discorb::Dictionary{Discorb::Snowflake => Discorb::Message}] A dictionary of messages.

ping[R]

@return [Float] The ping of the client.

@note This will be calculated from heartbeat and heartbeat_ack.

@return [nil] If not connected to the gateway.

session_id[R]

@return [Integer] The session ID of connection.

status[R]

@return [:initialized, :running, :closed] The status of the client.

token[R]

@return [String] The token of the client.

user[R]

@return [Discorb::ClientUser] The client user.

users[R]

@return [Discorb::Dictionary{Discorb::Snowflake => Discorb::User}] A dictionary of users.

Public Class Methods

new( allowed_mentions: nil, intents: nil, message_caches: 1000, log: nil, colorize_log: false, log_level: :info, wait_until_ready: true ) click to toggle source

Initializes a new client.

@param [Discorb::AllowedMentions] allowed_mentions The allowed mentions that the client is using. @param [Discorb::Intents] intents The intents that the client is currently using. @param [Integer] message_caches The number of messages to cache. @param [#puts] log The IO object to use for logging. @param [Boolean] colorize_log Whether to colorize the log. @param [:debug, :info, :warn, :error, :critical] log_level The log level. @param [Boolean] wait_until_ready Whether to delay event dispatch until ready.

# File lib/discorb/client.rb, line 68
def initialize(
  allowed_mentions: nil, intents: nil, message_caches: 1000,
  log: nil, colorize_log: false, log_level: :info,
  wait_until_ready: true
)
  @allowed_mentions = allowed_mentions || AllowedMentions.new(everyone: true, roles: true, users: true)
  @intents = (intents or Intents.default)
  @events = {}
  @api_version = nil
  @log = Logger.new(log, colorize_log, log_level)
  @user = nil
  @users = Discorb::Dictionary.new
  @channels = Discorb::Dictionary.new
  @guilds = Discorb::Dictionary.new(sort: ->(k) { k[0].to_i })
  @emojis = Discorb::Dictionary.new
  @messages = Discorb::Dictionary.new(limit: message_caches)
  @application = nil
  @last_s = nil
  @identify_presence = nil
  @wait_until_ready = wait_until_ready
  @ready = false
  @tasks = []
  @conditions = {}
  @commands = []
  @bottom_commands = []
  @status = :initialized
end

Public Instance Methods

await(event, timeout = nil, &check)
Alias for: event_lock
change_presence(activity = nil, status: nil)
Alias for: update_presence
close!() click to toggle source

Stops the client.

# File lib/discorb/client.rb, line 446
def close!
  @connection.send_close
  @tasks.each(&:stop)
  @status = :closed
  @close_condition.signal
end
dispatch(event_name, *args) click to toggle source

Dispatch an event.

@param [Symbol] event_name The name of the event. @param [Object] args The arguments to pass to the event.

# File lib/discorb/client.rb, line 145
def dispatch(event_name, *args)
  Async do
    if (conditions = @conditions[event_name])
      ids = Set[*conditions.map(&:first).map(&:object_id)]
      conditions.delete_if do |condition|
        next unless ids.include?(condition.first.object_id)

        check_result = condition[1].nil? || condition[1].call(*args)
        if check_result
          condition.first.signal(args)
          true
        else
          false
        end
      end
    end
    events = @events[event_name].dup || []
    if respond_to?("on_" + event_name.to_s)
      event_method = method("on_" + event_name.to_s)
      class << event_method
        def id
          "method"
        end
      end
      events << event_method
    end
    if events.nil?
      @log.debug "Event #{event_name} doesn't have any proc, skipping"
      next
    end
    @log.debug "Dispatching event #{event_name}"
    events.each do |block|
      lambda { |event_args|
        Async(annotation: "Discorb event: #{event_name}") do |task|
          if block.is_a?(Discorb::Event)
            @events[event_name].delete(block) if block.discriminator[:once]
          end
          block.call(*event_args)
          @log.debug "Dispatched proc with ID #{block.id.inspect}"
        rescue StandardError, ScriptError => e
          message = "An error occurred while dispatching proc with ID #{block.id.inspect}\n#{e.full_message}"
          dispatch(:error, event_name, event_args, e)
          if @log.out
            @log.error message
          else
            warn message
          end
        end
      }.call(args)
    end
  end
end
event_lock(event, timeout = nil, &check) click to toggle source

Method to wait for a event.

@param [Symbol] event The name of the event. @param [Integer] timeout The timeout in seconds. @param [Proc] check The check to use.

@return [Object] The result of the event.

@raise [Discorb::TimeoutError] If the event didn't occur in time.

# File lib/discorb/client.rb, line 338
def event_lock(event, timeout = nil, &check)
  Async do |task|
    condition = Async::Condition.new
    @conditions[event] ||= []
    @conditions[event] << [condition, check]
    if timeout.nil?
      value = condition.wait
    else
      timeout_task = task.with_timeout(timeout) do
        condition.wait
      rescue Async::TimeoutError
        @conditions[event].delete_if { |c| c.first == condition }
        raise Discorb::TimeoutError, "Timeout waiting for event #{event}", cause: nil
      end
      value = timeout_task
    end
    value.length <= 1 ? value.first : value
  end
end
Also aliased as: await
extend(mod) click to toggle source

Load the extension.

@param [Module] mod The extension to load.

Calls superclass method
# File lib/discorb/client.rb, line 369
def extend(mod)
  if mod.respond_to?(:events)
    @events.each_value do |event|
      event.delete_if { |c| c.discriminator[:extension] == mod.name }
    end
    mod.events.each do |name, events|
      @events[name] = [] if @events[name].nil?
      events.each do |event|
        @events[name] << event
      end
    end
    @commands.delete_if do |cmd|
      cmd.respond_to? :extension and cmd.extension == mod.name
    end
    mod.commands.each do |cmd|
      cmd.define_singleton_method(:extension) { mod.name }
      @commands << cmd
    end
    @bottom_commands += mod.bottom_commands
    mod.client = self
  end
  super(mod)
end
fetch_application(force: false) click to toggle source

Fetch webhook from ID. If application was cached, it will be used. @macro async @macro http

@param [Boolean] force Whether to force the fetch.

@return [Discorb::Application] The application.

# File lib/discorb/client.rb, line 280
def fetch_application(force: false)
  Async do
    next @application if @application && !force

    _resp, data = http.get("/oauth2/applications/@me").wait
    @application = Application.new(self, data)
    @application
  end
end
fetch_channel(id) click to toggle source

Fetch channel from ID. @macro async @macro http

@param [#to_s] id The ID of the channel.

@return [Discorb::Channel] The channel.

@raise [Discorb::NotFoundError] If the channel doesn't exist.

# File lib/discorb/client.rb, line 227
def fetch_channel(id)
  Async do
    _resp, data = http.get("/channels/#{id}").wait
    Channel.make_channel(self, data)
  end
end
fetch_guild(id) click to toggle source

Fetch guild from ID. @macro async @macro http

@param [#to_s] id <description>

@return [Discorb::Guild] The guild.

@raise [Discorb::NotFoundError] If the guild doesn't exist.

# File lib/discorb/client.rb, line 245
def fetch_guild(id)
  Async do
    _resp, data = http.get("/guilds/#{id}").wait
    Guild.new(self, data, false)
  end
end
fetch_invite(code, with_count: false, with_expiration: false) click to toggle source

Fetch invite from code. @macro async @macro http

@param [String] code The code of the invite. @param [Boolean] with_count Whether to include the count of the invite. @param [Boolean] with_expiration Whether to include the expiration of the invite.

@return [Discorb::Invite] The invite.

# File lib/discorb/client.rb, line 263
def fetch_invite(code, with_count: false, with_expiration: false)
  Async do
    _resp, data = http.get("/invites/#{code}?with_count=#{with_count}&with_expiration=#{with_expiration}").wait
    Invite.new(self, data, false)
  end
end
fetch_nitro_sticker_packs() click to toggle source

Fetch nitro sticker pack from ID. @macro async @macro http

@return [Array<Discorb::Sticker::Pack>] The packs.

# File lib/discorb/client.rb, line 297
def fetch_nitro_sticker_packs
  Async do
    _resp, data = http.get("/stickers-packs").wait
    data.map { |pack| Sticker::Pack.new(self, pack) }
  end
end
fetch_user(id) click to toggle source

Fetch user from ID. @macro async @macro http

@param [#to_s] id <description>

@return [Discorb::User] The user.

@raise [Discorb::NotFoundError] If the user doesn't exist.

# File lib/discorb/client.rb, line 209
def fetch_user(id)
  Async do
    _resp, data = http.get("/users/#{id}").wait
    User.new(self, data)
  end
end
inspect() click to toggle source
# File lib/discorb/client.rb, line 360
def inspect
  "#<#{self.class} user=\"#{user}\">"
end
on(event_name, id: nil, **discriminator, &block) click to toggle source

Registers an event handler. @see file:docs/Events.md

@param [Symbol] event_name The name of the event. @param [Symbol] id Custom ID of the event. @param [Hash] discriminator The discriminator of the event. @param [Proc] block The block to execute when the event is triggered.

@return [Discorb::Event] The event.

# File lib/discorb/client.rb, line 107
def on(event_name, id: nil, **discriminator, &block)
  ne = Event.new(block, id, discriminator)
  @events[event_name] ||= []
  @events[event_name] << ne
  ne
end
once(event_name, id: nil, **discriminator, &block) click to toggle source

Almost same as {#on}, but only triggers the event once.

@param (see on)

@return [Discorb::Event] The event.

# File lib/discorb/client.rb, line 121
def once(event_name, id: nil, **discriminator, &block)
  discriminator[:once] = true
  ne = Event.new(block, id, discriminator)
  @events[event_name] ||= []
  @events[event_name] << ne
  ne
end
remove_event(event_name, id) click to toggle source

Remove event by ID.

@param [Symbol] event_name The name of the event. @param [Symbol] id The ID of the event.

# File lib/discorb/client.rb, line 135
def remove_event(event_name, id)
  @events[event_name].delete_if { |e| e.id == id }
end
run(token) click to toggle source

Starts the client. @note This method behavior will change by CLI. @see file:docs/cli.md

@param [String] token The token to use.

# File lib/discorb/client.rb, line 403
def run(token)
  case ENV["DISCORB_CLI_FLAG"]
  when nil
    start_client(token)
  when "run"
    require "json"
    options = JSON.parse(ENV["DISCORB_CLI_OPTIONS"], symbolize_names: true)
    Process.daemon if options[:daemon]
    setup_commands(token) if options[:setup]
    if options[:log_level]
      if options[:log_level] == "none"
        @log.out = nil
      else
        @log.out = case options[:log_file]
          when nil, "stderr"
            $stderr
          when "stdout"
            $stdout
          else
            ::File.open(options[:log_file], "a")
          end
        @log.level = options[:log_level].to_sym
        @log.colorize_log = case options[:log_color]
          when nil
            if @log.out == $stdout || @log.out == $stderr
              true
            else
              false
            end
          when true, false
            options[:log_color]
          end
      end
    end
    start_client(token)
  when "setup"
    setup_commands(token)
  end
end
update_presence(activity = nil, status: nil) click to toggle source

Update presence of the client.

@param [Discorb::Activity] activity The activity to update. @param [:online, :idle, :dnd, :invisible] status The status to update.

# File lib/discorb/client.rb, line 310
def update_presence(activity = nil, status: nil)
  payload = {}
  if !activity.nil?
    payload[:activities] = [activity.to_hash]
  end
  payload[:status] = status unless status.nil?
  if @connection
    Async do
      send_gateway(3, **payload)
    end
  else
    @identify_presence = payload
  end
end
Also aliased as: change_presence

Private Instance Methods

start_client(token) click to toggle source
# File lib/discorb/client.rb, line 455
def start_client(token)
  Async do |task|
    trap(:SIGINT) {
      @log.info "SIGINT received, closing..."
      close!
    }
    @token = token.to_s
    @close_condition = Async::Condition.new
    @main_task = Async do
      @status = :running
      connect_gateway(true).wait
    rescue
      @status = :stopped
      @close_condition.signal
      raise
    end
    @close_condition.wait
    @main_task.stop
  end
end