Class: Rubycord::Bot

Inherits:
Object
  • Object
show all
Includes:
Cache, EventContainer
Defined in:
lib/rubycord/bot.rb

Overview

Represents a Discord bot, including servers, users, etc.

Direct Known Subclasses

Commands::CommandBot

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Cache

#channel, #ensure_channel, #ensure_server, #ensure_thread_member, #ensure_user, #find_channel, #find_user, #init_cache, #invite, #member, #pm_channel, #request_chunks, #resolve_invite_code, #server, #user, #voice_regions

Methods included from EventContainer

#add_handler, #application_command, #await, #button, #channel_create, #channel_delete, #channel_recipient_add, #channel_recipient_remove, #channel_select, #channel_update, class_from_string, #clear!, #disconnected, event_class, handler_class, #heartbeat, #include_events, #interaction_create, #invite_create, #invite_delete, #member_join, #member_leave, #member_update, #mention, #mentionable_select, #message, #message_delete, #message_edit, #message_update, #modal_submit, #playing, #pm, #presence, #raw, #reaction_add, #reaction_remove, #reaction_remove_all, #ready, #remove_application_command_handler, #remove_handler, #role_select, #server_create, #server_delete, #server_emoji, #server_emoji_create, #server_emoji_delete, #server_emoji_update, #server_role_create, #server_role_delete, #server_role_update, #server_update, #string_select, #typing, #unknown, #user_ban, #user_select, #user_unban, #voice_server_update, #voice_state_update, #webhook_update

Methods included from Events

matches_all

Constructor Details

#initialize(log_mode: :normal, token: nil, client_id: nil, type: nil, name: "", fancy_log: false, suppress_ready: false, parse_self: false, shard_id: nil, num_shards: nil, redact_token: true, ignore_bots: false, compress_mode: :large, intents: :all) ⇒ Bot

Makes a new bot with the given authentication data. It will be ready to be added event handlers to and can eventually be run with #run.

As support for logging in using username and password has been removed in version 3.0.0, only a token login is possible. Be sure to specify the type parameter as :user if you're logging in as a user.

Simply creating a bot won't be enough to start sending messages etc. with, only a limited set of methods can be used after logging in. If you want to do something when the bot has connected successfully, either do it in the EventContainer#ready event, or use the #run method with the :async parameter and do the processing after that.

Parameters:

  • log_mode (Symbol) (defaults to: :normal)

    The mode this bot should use for logging. See Logger#mode= for a list of modes.

  • token (String) (defaults to: nil)

    The token that should be used to log in. If your bot is a bot account, you have to specify this. If you're logging in as a user, make sure to also set the account type to :user so rubycord doesn't think you're trying to log in as a bot.

  • client_id (Integer) (defaults to: nil)

    If you're logging in as a bot, the bot's client ID. This is optional, and may be fetched from the API by calling #bot_application (see Application).

  • type (Symbol) (defaults to: nil)

    This parameter lets you manually overwrite the account type. This needs to be set when logging in as a user, otherwise rubycord will treat you as a bot account. Valid values are :user and :bot.

  • name (String) (defaults to: "")

    Your bot's name. This will be sent to Discord with any API requests, who will use this to trace the source of excessive API requests; it's recommended to set this to something if you make bots that many people will host on their servers separately.

  • fancy_log (true, false) (defaults to: false)

    Whether the output log should be made extra fancy using ANSI escape codes. (Your terminal may not support this.)

  • suppress_ready (true, false) (defaults to: false)

    Whether the READY packet should be exempt from being printed to console. Useful for very large bots running in debug or verbose log_mode.

  • parse_self (true, false) (defaults to: false)

    Whether the bot should react on its own messages. It's best to turn this off unless you really need this so you don't inadvertently create infinite loops.

  • shard_id (Integer) (defaults to: nil)

    The number of the shard this bot should handle. See https://github.com/discord/discord-api-docs/issues/17 for how to do sharding.

  • num_shards (Integer) (defaults to: nil)

    The total number of shards that should be running. See https://github.com/discord/discord-api-docs/issues/17 for how to do sharding.

  • redact_token (true, false) (defaults to: true)

    Whether the bot should redact the token in logs. Default is true.

  • ignore_bots (true, false) (defaults to: false)

    Whether the bot should ignore bot accounts or not. Default is false.

  • compress_mode (:none, :large, :stream) (defaults to: :large)

    Sets which compression mode should be used when connecting to Discord's gateway. :none will request that no payloads are received compressed (not recommended for production bots). :large will request that large payloads are received compressed. :stream will request that all data be received in a continuous compressed stream.

  • intents (:all, :unprivileged, Array<Symbol>, :none) (defaults to: :all)

    Gateway intents that this bot requires. :all will request all intents. :unprivileged will request only intents that are not defined as "Privileged". :none will request no intents. An array of symbols will request only those intents specified.

See Also:



111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/rubycord/bot.rb', line 111

def initialize(
  log_mode: :normal,
  token: nil, client_id: nil,
  type: nil, name: "", fancy_log: false, suppress_ready: false, parse_self: false,
  shard_id: nil, num_shards: nil, redact_token: true, ignore_bots: false,
  compress_mode: :large, intents: :all
)
  LOGGER.mode = log_mode
  LOGGER.token = token if redact_token

  @should_parse_self = parse_self

  @client_id = client_id

  @type = type || :bot
  @name = name

  @shard_key = num_shards ? [shard_id, num_shards] : nil

  LOGGER.fancy = fancy_log
  @prevent_ready = suppress_ready

  @compress_mode = compress_mode

  raise "Token string is empty or nil" if token.nil? || token.empty?

  @intents = case intents
  when :all
    ALL_INTENTS
  when :unprivileged
    UNPRIVILEGED_INTENTS
  when :none
    NO_INTENTS
  else
    calculate_intents(intents)
  end

  @token = process_token(@type, token)
  @gateway = Gateway.new(self, @token, @shard_key, @compress_mode, @intents)

  init_cache

  @voices = {}
  @should_connect_to_voice = {}

  @ignored_ids = Set.new
  @ignore_bots = ignore_bots

  @event_threads = []
  @current_thread = 0

  @status = :online

  @application_commands = {}
end

Instance Attribute Details

#awaitsHash<Symbol => Await> (readonly)

Returns the list of registered Awaits.

Returns:

  • (Hash<Symbol => Await>)

    the list of registered Awaits.



61
62
63
# File 'lib/rubycord/bot.rb', line 61

def awaits
  @awaits
end

#event_threadsArray<Thread> (readonly)

The list of currently running threads used to parse and call events. The threads will have a local variable :rubycord_name in the format of et-1234, where "et" stands for "event thread" and the number is a continually incrementing number representing how many events were executed before.

Returns:

  • (Array<Thread>)

    The threads.



47
48
49
# File 'lib/rubycord/bot.rb', line 47

def event_threads
  @event_threads
end

#gatewayGateway (readonly)

The gateway connection is an internal detail that is useless to most people. It is however essential while debugging or developing rubycord itself, or while writing very custom bots.

Returns:



66
67
68
# File 'lib/rubycord/bot.rb', line 66

def gateway
  @gateway
end

#nameString

The bot's name which rubycord sends to Discord when making any request, so Discord can identify bots with the same codebase. Not required but I recommend setting it anyway.

Returns:

  • (String)

    The bot's name.



55
56
57
# File 'lib/rubycord/bot.rb', line 55

def name
  @name
end

#shard_keyArray(Integer, Integer) (readonly)

Returns the current shard key.

Returns:



58
59
60
# File 'lib/rubycord/bot.rb', line 58

def shard_key
  @shard_key
end

#should_parse_selftrue, false

Returns whether or not the bot should parse its own messages. Off by default.

Returns:

  • (true, false)

    whether or not the bot should parse its own messages. Off by default.



50
51
52
# File 'lib/rubycord/bot.rb', line 50

def should_parse_self
  @should_parse_self
end

#voicesHash<Integer => VoiceBot> (readonly)

Returns the voice connections this bot currently has, by the server ID to which they are connected.

Returns:

  • (Hash<Integer => VoiceBot>)

    the voice connections this bot currently has, by the server ID to which they are connected.



325
326
327
# File 'lib/rubycord/bot.rb', line 325

def voices
  @voices
end

Instance Method Details

#accept_invite(invite) ⇒ Object

Makes the bot join an invite to a server.

Parameters:



299
300
301
302
# File 'lib/rubycord/bot.rb', line 299

def accept_invite(invite)
  resolved = invite(invite).code
  API::Invite.accept(token, resolved)
end

#add_await(key, type, attributes = {}) {|event| ... } ⇒ Await

Deprecated.

Will be changed to blocking behavior in v4.0. Use #add_await! instead.

Add an await the bot should listen to. For information on awaits, see Await.

Parameters:

  • key (Symbol)

    The key that uniquely identifies the await for Events::AwaitEvents to listen to (see EventContainer#await).

  • type (Class)

    The event class that should be listened for.

  • attributes (Hash) (defaults to: {})

    The attributes the event should check for. The block will only be executed if all attributes match.

Yields:

  • Is executed when the await is triggered.

Yield Parameters:

  • event (Event)

    The event object that was triggered.

Returns:

  • (Await)

    The await that was created.



701
702
703
704
705
706
707
# File 'lib/rubycord/bot.rb', line 701

def add_await(key, type, attributes = {}, &block)
  raise "You can't await an AwaitEvent!" if type == Rubycord::Events::AwaitEvent

  await = Await.new(self, key, type, attributes, block)
  @awaits ||= {}
  @awaits[key] = await
end

#add_await!(type, attributes = {}) {|event| ... } ⇒ Event?

Awaits an event, blocking the current thread until a response is received.

Parameters:

  • type (Class)

    The event class that should be listened for.

  • attributes (Hash) (defaults to: {})

    a customizable set of options

Options Hash (attributes):

  • :timeout (Numeric)

    the amount of time (in seconds) to wait for a response before returning nil. Waits forever if omitted.

Yields:

  • Executed when a matching event is received.

Yield Parameters:

  • event (Event)

    The event object that was triggered.

Yield Returns:

  • (true, false)

    Whether the event matches extra await criteria described by the block

Returns:

  • (Event, nil)

    The event object that was triggered, or nil if a timeout was set and no event was raised in time.

Raises:

  • (ArgumentError)

    if timeout is given and is not a positive numeric value



717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
# File 'lib/rubycord/bot.rb', line 717

def add_await!(type, attributes = {})
  raise "You can't await an AwaitEvent!" if type == Rubycord::Events::AwaitEvent

  timeout = attributes[:timeout]
  raise ArgumentError, "Timeout must be a number > 0" if timeout.is_a?(Numeric) && !timeout.positive?

  mutex = Mutex.new
  cv = ConditionVariable.new
  response = nil
  block = lambda do |event|
    mutex.synchronize do
      response = event
      if block_given?
        result = yield(event)
        cv.signal if result.is_a?(TrueClass)
      else
        cv.signal
      end
    end
  end

  handler = register_event(type, attributes, block)

  if timeout
    Thread.new do
      sleep timeout
      mutex.synchronize { cv.signal }
    end
  end

  mutex.synchronize { cv.wait(mutex) }

  remove_handler(handler)
  raise "ConditionVariable was signaled without returning an event!" if response.nil? && timeout.nil?

  response
end

#add_thread_member(channel, member) ⇒ Object

Add a member to a thread

Parameters:



664
665
666
667
# File 'lib/rubycord/bot.rb', line 664

def add_thread_member(channel, member)
  API::Channel.add_thread_member(@token, channel.resolve_id, member.resolve_id)
  nil
end

#bot_applicationApplication? Also known as: bot_app

The bot's OAuth application.

Returns:

  • (Application, nil)

    The bot's application info. Returns nil if bot is not a bot account.



233
234
235
236
237
238
# File 'lib/rubycord/bot.rb', line 233

def bot_application
  return unless @type == :bot

  response = API.oauth_application(token)
  Application.new(JSON.parse(response), self)
end

#competing=(name) ⇒ String

Sets the currently competing status to the specified name.

Parameters:

  • name (String)

    The name of the game to be competing in.

Returns:

  • (String)

    The game that is being competed in now.



606
607
608
609
# File 'lib/rubycord/bot.rb', line 606

def competing=(name)
  gateway_check
  update_status(@status, name, nil, nil, nil, 5)
end

#connected?true, false

Returns whether or not the bot is currently connected to Discord.

Returns:

  • (true, false)

    whether or not the bot is currently connected to Discord.



293
294
295
# File 'lib/rubycord/bot.rb', line 293

def connected?
  @gateway.open?
end

#create_oauth_application(name, redirect_uris) ⇒ Array(String, String)

Creates a new application to do OAuth authorization with. This allows you to use OAuth to authorize users using Discord. For information how to use this, see the docs: https://discord.com/developers/docs/topics/oauth2

Parameters:

  • name (String)

    What your application should be called.

  • redirect_uris (Array<String>)

    URIs that Discord should redirect your users to after authorizing.

Returns:

  • (Array(String, String))

    your applications' client ID and client secret to be used in OAuth authorization.



482
483
484
485
# File 'lib/rubycord/bot.rb', line 482

def create_oauth_application(name, redirect_uris)
  response = JSON.parse(API.create_oauth_application(@token, name, redirect_uris))
  [response["id"], response["secret"]]
end

#create_server(name, region = :"eu-central") ⇒ Server

Note:

Discord's API doesn't directly return the server when creating it, so this method waits until the data has been received via the websocket. This may make the execution take a while.

Creates a server on Discord with a specified name and a region.

Parameters:

  • name (String)

    The name the new server should have. Doesn't have to be alphanumeric.

  • region (Symbol) (defaults to: :"eu-central")

    The region where the server should be created, for example 'eu-central' or 'hongkong'.

Returns:

  • (Server)

    The server that was created.



469
470
471
472
473
474
475
# File 'lib/rubycord/bot.rb', line 469

def create_server(name, region = :"eu-central")
  response = API::Server.create(token, name, region)
  id = JSON.parse(response)["id"].to_i
  sleep 0.1 until (server = @servers[id])
  debug "Successfully created server #{server.id} with name #{server.name}"
  server
end

#custom_status=(name) ⇒ String

Sets the currently custom status to the specified name.

Parameters:

  • name (String)

    The custom status.

Returns:

  • (String)

    The custom status that is being used now.



614
615
616
617
# File 'lib/rubycord/bot.rb', line 614

def custom_status=(name)
  gateway_check
  update_status(@status, name, nil, nil, nil, 4)
end

#debug(message) ⇒ Object

See Also:

  • Logger#debug


777
778
779
# File 'lib/rubycord/bot.rb', line 777

def debug(message)
  LOGGER.debug(message)
end

#debug=(new_debug) ⇒ Object

Sets debug mode. If debug mode is on, many things will be outputted to STDOUT.



678
679
680
# File 'lib/rubycord/bot.rb', line 678

def debug=(new_debug)
  LOGGER.debug = new_debug
end

#delete_application_command(command_id, server_id: nil) ⇒ Object

Remove an application command from the commands registered with discord.

Parameters:

  • command_id (String, Integer)

    The ID of the command to remove.

  • server_id (String, Integer) (defaults to: nil)

    The ID of the server to delete this command from, global if nil.



896
897
898
899
900
901
902
# File 'lib/rubycord/bot.rb', line 896

def delete_application_command(command_id, server_id: nil)
  if server_id
    API::Application.delete_guild_command(@token, profile.id, server_id, command_id)
  else
    API::Application.delete_global_command(@token, profile.id, command_id)
  end
end

#delete_invite(code) ⇒ Object

Revokes an invite to a server. Will fail unless you have the Manage Server permission. It is recommended that you use Invite#delete instead.

Parameters:



389
390
391
392
# File 'lib/rubycord/bot.rb', line 389

def delete_invite(code)
  invite = resolve_invite_code(code)
  API::Invite.delete(token, invite)
end

#dispatch(type, data) ⇒ Object

Dispatches an event to this bot. Called by the gateway connection handler used internally.



787
788
789
# File 'lib/rubycord/bot.rb', line 787

def dispatch(type, data)
  handle_dispatch(type, data)
end

#dndObject

Sets the bot's status to DnD (red icon).



636
637
638
639
# File 'lib/rubycord/bot.rb', line 636

def dnd
  gateway_check
  update_status(:dnd, @activity, nil)
end

#edit_application_command(command_id, server_id: nil, name: nil, description: nil, default_permission: nil, type: :chat_input) {|, | ... } ⇒ Object

Yield Parameters:

  • (OptionBuilder)
  • (PermissionBuilder)


869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
# File 'lib/rubycord/bot.rb', line 869

def edit_application_command(command_id, server_id: nil, name: nil, description: nil, default_permission: nil, type: :chat_input)
  type = ApplicationCommand::TYPES[type] || type

  builder = Interactions::OptionBuilder.new
  permission_builder = Interactions::PermissionBuilder.new

  yield(builder, permission_builder) if block_given?

  resp = if server_id
    API::Application.edit_guild_command(@token, profile.id, server_id, command_id, name, description, builder.to_a, default_permission, type)
  else
    API::Application.edit_guild_command(@token, profile.id, command_id, name, description, builder.to_a, default_permission.type)
  end
  cmd = ApplicationCommand.new(JSON.parse(resp), self, server_id)

  if permission_builder.to_a.any?
    raise ArgumentError, "Permissions can only be set for guild commands" unless server_id

    edit_application_command_permissions(cmd.id, server_id, permission_builder.to_a)
  end

  cmd
end

#edit_application_command_permissions(command_id, server_id, permissions = []) {|builder| ... } ⇒ Object

Parameters:

  • command_id (Integer, String)
  • server_id (Integer, String)
  • permissions (Array<Hash>) (defaults to: [])

    An array of objects formatted as { id: ENTITY_ID, type: 1 or 2, permission: true or false }

Yields:

  • (builder)


907
908
909
910
911
912
913
# File 'lib/rubycord/bot.rb', line 907

def edit_application_command_permissions(command_id, server_id, permissions = [])
  builder = Interactions::PermissionBuilder.new
  yield builder if block_given?

  permissions += builder.to_a
  API::Application.edit_guild_command_permissions(@token, profile.id, server_id, command_id, permissions)
end

#emoji(id) ⇒ Emoji? #emojiArray<Emoji> Also known as: emojis, all_emoji

Overloads:

  • #emoji(id) ⇒ Emoji?

    Return an emoji by its ID

    Parameters:

    Returns:

    • (Emoji, nil)

      the emoji object. nil if the emoji was not found.

  • #emojiArray<Emoji>

    The list of emoji the bot can use.

    Returns:

    • (Array<Emoji>)

      the emoji available.



198
199
200
201
202
203
204
205
206
# File 'lib/rubycord/bot.rb', line 198

def emoji(id = nil)
  emoji_hash = servers.values.map(&:emoji).reduce(&:merge)
  if id
    id = id.resolve_id
    emoji_hash[id]
  else
    emoji_hash.values
  end
end

#find_emoji(name) ⇒ GlobalEmoji?

Finds an emoji by its name.

Parameters:

  • name (String)

    The emoji name that should be resolved.

Returns:

  • (GlobalEmoji, nil)

    the emoji identified by the name, or nil if it couldn't be found.



214
215
216
217
# File 'lib/rubycord/bot.rb', line 214

def find_emoji(name)
  LOGGER.out("Resolving emoji #{name}")
  emoji.find { |element| element.name == name }
end

#game=(name) ⇒ String Also known as: playing=

Sets the currently playing game to the specified game.

Parameters:

  • name (String)

    The name of the game to be played.

Returns:

  • (String)

    The game that is being played now.



570
571
572
573
# File 'lib/rubycord/bot.rb', line 570

def game=(name)
  gateway_check
  update_status(@status, name, nil)
end

#get_application_command(command_id, server_id: nil) ⇒ Object

Get an application command by ID.

Parameters:

  • command_id (String, Integer)
  • server_id (String, Integer, nil) (defaults to: nil)

    The ID of the server to get the command from. Global if nil.



821
822
823
824
825
826
827
828
# File 'lib/rubycord/bot.rb', line 821

def get_application_command(command_id, server_id: nil)
  resp = if server_id
    API::Application.get_guild_command(@token, profile.id, server_id, command_id)
  else
    API::Application.get_global_command(@token, profile.id, command_id)
  end
  ApplicationCommand.new(JSON.parse(resp), self, server_id)
end

#get_application_commands(server_id: nil) ⇒ Array<ApplicationCommand>

Get all application commands.

Parameters:

  • server_id (String, Integer, nil) (defaults to: nil)

    The ID of the server to get the commands from. Global if nil.

Returns:



806
807
808
809
810
811
812
813
814
815
816
# File 'lib/rubycord/bot.rb', line 806

def get_application_commands(server_id: nil)
  resp = if server_id
    API::Application.get_guild_commands(@token, profile.id, server_id)
  else
    API::Application.get_global_commands(@token, profile.id)
  end

  JSON.parse(resp).map do |command_data|
    ApplicationCommand.new(command_data, self, server_id)
  end
end

#idleObject Also known as: away

Sets status to idle.



628
629
630
631
# File 'lib/rubycord/bot.rb', line 628

def idle
  gateway_check
  update_status(:idle, @activity, nil)
end

#ignore_user(user) ⇒ Object

Note:

Ignoring a user only prevents any message events (including mentions, commands etc.) from them! Typing and presence and any other events will still be received.

Add a user to the list of ignored users. Those users will be ignored in message events at event processing level.

Parameters:



759
760
761
# File 'lib/rubycord/bot.rb', line 759

def ignore_user(user)
  @ignored_ids << user.resolve_id
end

#ignored?(user) ⇒ true, false

Checks whether a user is being ignored.

Parameters:

Returns:

  • (true, false)

    whether or not the user is ignored.



772
773
774
# File 'lib/rubycord/bot.rb', line 772

def ignored?(user)
  @ignored_ids.include?(user.resolve_id)
end

#inspectObject

The inspect method is overwritten to reduce superfluous data



916
917
918
# File 'lib/rubycord/bot.rb', line 916

def inspect
  "#<Bot @name=#{@name.inspect} @intents=#{@intents.inspect} @shard_key=#{@shard_key.inspect}>"
end

#invisibleObject

Sets the bot's status to invisible (appears offline).



642
643
644
645
# File 'lib/rubycord/bot.rb', line 642

def invisible
  gateway_check
  update_status(:invisible, @activity, nil)
end

#invite_url(server: nil, permission_bits: nil, redirect_uri: nil, scopes: ["bot"]) ⇒ String

Creates an OAuth invite URL that can be used to invite this bot to a particular server.

Parameters:

  • server (Server, nil) (defaults to: nil)

    The server the bot should be invited to, or nil if a general invite should be created.

  • permission_bits (String, Integer) (defaults to: nil)

    Permission bits that should be appended to invite url.

  • redirect_uri (String) (defaults to: nil)

    Redirect URI that should be appended to invite url.

  • scopes (Array<String>) (defaults to: ["bot"])

    Scopes that should be appended to invite url.

Returns:

  • (String)

    the OAuth invite URL.



310
311
312
313
314
315
316
317
318
319
320
321
322
# File 'lib/rubycord/bot.rb', line 310

def invite_url(server: nil, permission_bits: nil, redirect_uri: nil, scopes: ["bot"])
  @client_id ||= bot_application.id

  query = URI.encode_www_form({
    client_id: @client_id,
    guild_id: server&.id,
    permissions: permission_bits,
    redirect_uri: redirect_uri,
    scope: scopes.join(" ")
  }.compact)

  "https://discord.com/oauth2/authorize?#{query}"
end

#joinObject Also known as: sync

Joins the bot's connection thread with the current thread. This blocks execution until the websocket stops, which should only happen manually triggered. or due to an error. This is necessary to have a continuously running bot.



280
281
282
# File 'lib/rubycord/bot.rb', line 280

def join
  @gateway.sync
end

#join_thread(channel) ⇒ Object

Join a thread

Parameters:



649
650
651
652
# File 'lib/rubycord/bot.rb', line 649

def join_thread(channel)
  API::Channel.join_thread(@token, channel.resolve_id)
  nil
end

#leave_thread(channel) ⇒ Object

Leave a thread

Parameters:



656
657
658
659
# File 'lib/rubycord/bot.rb', line 656

def leave_thread(channel)
  API::Channel.leave_thread(@token, channel.resolve_id)
  nil
end

#listening=(name) ⇒ String

Sets the current listening status to the specified name.

Parameters:

  • name (String)

    The thing to be listened to.

Returns:

  • (String)

    The thing that is now being listened to.



580
581
582
583
# File 'lib/rubycord/bot.rb', line 580

def listening=(name)
  gateway_check
  update_status(@status, name, nil, nil, nil, 2)
end

#log_exception(e) ⇒ Object



782
783
784
# File 'lib/rubycord/bot.rb', line 782

def log_exception(e)
  LOGGER.log_exception(e)
end

#mode=(new_mode) ⇒ Object

Sets the logging mode

See Also:



684
685
686
# File 'lib/rubycord/bot.rb', line 684

def mode=(new_mode)
  LOGGER.mode = new_mode
end

#onlineObject Also known as: on

Sets status to online.



620
621
622
623
# File 'lib/rubycord/bot.rb', line 620

def online
  gateway_check
  update_status(:online, @activity, @streamurl)
end

#parse_mention(mention, server = nil) ⇒ User, ...

Gets the user, channel, role or emoji from a string.

Parameters:

  • mention (String)

    The mention, which should look like <@12314873129>, <#123456789>, <@&123456789> or <:name:126328:>.

  • server (Server, nil) (defaults to: nil)

    The server of the associated mention. (recommended for role parsing, to speed things up)

Returns:

  • (User, Channel, Role, Emoji)

    The user, channel, role or emoji identified by the mention, or nil if none exists.



535
536
537
# File 'lib/rubycord/bot.rb', line 535

def parse_mention(mention, server = nil)
  parse_mentions(mention, server).first
end

#parse_mentions(mentions, server = nil) ⇒ Array<User, Channel, Role, Emoji>

Gets the users, channels, roles and emoji from a string.

Parameters:

  • mentions (String)

    The mentions, which should look like <@12314873129>, <#123456789>, <@&123456789> or <:name:126328:>.

  • server (Server, nil) (defaults to: nil)

    The server of the associated mentions. (recommended for role parsing, to speed things up)

Returns:

  • (Array<User, Channel, Role, Emoji>)

    The array of users, channels, roles and emoji identified by the mentions, or nil if none exists.



501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
# File 'lib/rubycord/bot.rb', line 501

def parse_mentions(mentions, server = nil)
  array_to_return = []
  # While possible mentions may be in message
  while mentions.include?("<") && mentions.include?(">")
    # Removing all content before the next possible mention
    mentions = mentions.split("<", 2)[1]
    # Locate the first valid mention enclosed in `<...>`, otherwise advance to the next open `<`
    next unless mentions.split(">", 2).first.length < mentions.split("<", 2).first.length

    # Store the possible mention value to be validated with RegEx
    mention = mentions.split(">", 2).first
    if /@!?(?<id>\d+)/ =~ mention
      array_to_return << user(id) unless user(id).nil?
    elsif /#(?<id>\d+)/ =~ mention
      array_to_return << channel(id, server) unless channel(id, server).nil?
    elsif /@&(?<id>\d+)/ =~ mention
      if server
        array_to_return << server.role(id) unless server.role(id).nil?
      else
        @servers.each_value do |element|
          array_to_return << element.role(id) unless element.role(id).nil?
        end
      end
    elsif /(?<animated>^a|^${0}):(?<name>\w+):(?<id>\d+)/ =~ mention
      array_to_return << (emoji(id) || Emoji.new({"animated" => !animated.nil?, "name" => name, "id" => id}, self, nil))
    end
  end
  array_to_return
end

#profileProfile Also known as: bot_user

The bot's user profile. This special user object can be used to edit user data like the current username (see Profile#username=).

Returns:

  • (Profile)

    The bot's profile that can be used to edit data.



222
223
224
225
226
227
# File 'lib/rubycord/bot.rb', line 222

def profile
  return @profile if @profile

  response = Rubycord::API::User.profile(@token)
  @profile = Profile.new(JSON.parse(response), self)
end

#prune_empty_groupsObject

Makes the bot leave any groups with no recipients remaining



797
798
799
800
801
# File 'lib/rubycord/bot.rb', line 797

def prune_empty_groups
  @channels.each_value do |channel|
    channel.leave_group if channel.group? && channel.recipients.empty?
  end
end

#raise_heartbeat_eventObject

Raises a heartbeat event. Called by the gateway connection handler used internally.



792
793
794
# File 'lib/rubycord/bot.rb', line 792

def raise_heartbeat_event
  raise_event(HeartbeatEvent.new(self))
end

#raw_tokenString

Returns the raw token, without any prefix.

Returns:

  • (String)

    the raw token, without any prefix

See Also:



252
253
254
# File 'lib/rubycord/bot.rb', line 252

def raw_token
  @token.split(" ").last
end

#register_application_command(name, description, server_id: nil, default_permission: nil, type: :chat_input) {|, | ... } ⇒ Object

Examples:

bot.register_application_command(:reddit, 'Reddit Commands') do |cmd|
  cmd.subcommand_group(:subreddit, 'Subreddit Commands') do |group|
    group.subcommand(:hot, "What's trending") do |sub|
      sub.string(:subreddit, 'Subreddit to search')
    end
    group.subcommand(:new, "What's new") do |sub|
      sub.string(:since, 'How long ago', choices: ['this hour', 'today', 'this week', 'this month', 'this year', 'all time'])
      sub.string(:subreddit, 'Subreddit to search')
    end
  end
end

Yield Parameters:

  • (OptionBuilder)
  • (PermissionBuilder)


844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
# File 'lib/rubycord/bot.rb', line 844

def register_application_command(name, description, server_id: nil, default_permission: nil, type: :chat_input)
  type = ApplicationCommand::TYPES[type] || type

  builder = Interactions::OptionBuilder.new
  permission_builder = Interactions::PermissionBuilder.new
  yield(builder, permission_builder) if block_given?

  resp = if server_id
    API::Application.create_guild_command(@token, profile.id, server_id, name, description, builder.to_a, default_permission, type)
  else
    API::Application.create_global_command(@token, profile.id, name, description, builder.to_a, default_permission, type)
  end
  cmd = ApplicationCommand.new(JSON.parse(resp), self, server_id)

  if permission_builder.to_a.any?
    raise ArgumentError, "Permissions can only be set for guild commands" unless server_id

    edit_application_command_permissions(cmd.id, server_id, permission_builder.to_a)
  end

  cmd
end

#remove_thread_member(channel, member) ⇒ Object

Remove a member from a thread

Parameters:



672
673
674
675
# File 'lib/rubycord/bot.rb', line 672

def remove_thread_member(channel, member)
  API::Channel.remove_thread_member(@token, channel.resolve_id, member.resolve_id)
  nil
end

#run(background = false) ⇒ Object

Note:

Running the bot in the background means that you can call some methods that require a gateway connection before that connection is established. In most cases an exception will be raised if you try to do this. If you need a way to safely run code after the bot is fully connected, use a EventContainer#ready event handler instead.

Runs the bot, which logs into Discord and connects the WebSocket. This prevents all further execution unless it is executed with background = true.

Parameters:

  • background (true, false) (defaults to: false)

    If it is true, then the bot will run in another thread to allow further execution. If it is false, this method will block until #stop is called. If the bot is run with true, make sure to eventually call #join so the script doesn't stop prematurely.



268
269
270
271
272
273
274
# File 'lib/rubycord/bot.rb', line 268

def run(background = false)
  @gateway.run_async
  return if background

  debug("Oh wait! Not exiting yet as run was run synchronously.")
  @gateway.sync
end

#send_file(channel, file, caption: nil, tts: false, filename: nil, spoiler: nil) ⇒ Object

Note:

This executes in a blocking way, so if you're sending long files, be wary of delays.

Sends a file to a channel. If it is an image, it will automatically be embedded.

Examples:

Send a file from disk

bot.send_file(83281822225530880, File.open('rubytaco.png', 'r'))

Parameters:

  • channel (Channel, String, Integer)

    The channel, or its ID, to send something to.

  • file (File)

    The file that should be sent.

  • caption (string) (defaults to: nil)

    The caption for the file.

  • tts (true, false) (defaults to: false)

    Whether or not this file's caption should be sent using Discord text-to-speech.

  • filename (String) (defaults to: nil)

    Overrides the filename of the uploaded file

  • spoiler (true, false) (defaults to: nil)

    Whether or not this file should appear as a spoiler.



447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
# File 'lib/rubycord/bot.rb', line 447

def send_file(channel, file, caption: nil, tts: false, filename: nil, spoiler: nil)
  if file.respond_to?(:read)
    if spoiler
      filename ||= File.basename(file.path)
      filename = "SPOILER_#{filename}" unless filename.start_with? "SPOILER_"
    end
    # https://github.com/rest-client/rest-client/blob/v2.0.2/lib/restclient/payload.rb#L160
    file.define_singleton_method(:original_filename) { filename } if filename
    file.define_singleton_method(:path) { filename } if filename
  end

  channel = channel.resolve_id
  response = API::Channel.upload_file(token, channel, file, caption: caption, tts: tts)
  Message.new(JSON.parse(response), self)
end

#send_message(channel, content, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil) ⇒ Message

Sends a text message to a channel given its ID and the message's content.

Parameters:

  • channel (Channel, String, Integer)

    The channel, or its ID, to send something to.

  • content (String)

    The text that should be sent as a message. It is limited to 2000 characters (Discord imposed).

  • tts (true, false) (defaults to: false)

    Whether or not this message should be sent using Discord text-to-speech.

  • embeds (Hash, Rubycord::Webhooks::Embed, Array<Hash>, Array<Rubycord::Webhooks::Embed> nil) (defaults to: nil)

    The rich embed(s) to append to this message.

  • allowed_mentions (Hash, Rubycord::AllowedMentions, false, nil) (defaults to: nil)

    Mentions that are allowed to ping on this message. false disables all pings

  • message_reference (Message, String, Integer, nil) (defaults to: nil)

    The message, or message ID, to reply to if any.

  • components (View, Array<Hash>) (defaults to: nil)

    Interaction components to associate with this message.

Returns:

  • (Message)

    The message that was sent.



403
404
405
406
407
408
409
410
411
412
# File 'lib/rubycord/bot.rb', line 403

def send_message(channel, content, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
  channel = channel.resolve_id
  debug("Sending message to #{channel} with content '#{content}'")
  allowed_mentions = {parse: []} if allowed_mentions == false
  message_reference = {message_id: message_reference.id} if message_reference.respond_to?(:id)
  embeds = (embeds.instance_of?(Array) ? embeds.map(&:to_hash) : [embeds&.to_hash]).compact

  response = API::Channel.create_message(token, channel, content, tts, embeds, nil, attachments, allowed_mentions&.to_hash, message_reference, components)
  Message.new(JSON.parse(response), self)
end

#send_temporary_message(channel, content, timeout, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil) ⇒ Object

Sends a text message to a channel given its ID and the message's content, then deletes it after the specified timeout in seconds.

Parameters:

  • channel (Channel, String, Integer)

    The channel, or its ID, to send something to.

  • content (String)

    The text that should be sent as a message. It is limited to 2000 characters (Discord imposed).

  • timeout (Float)

    The amount of time in seconds after which the message sent will be deleted.

  • tts (true, false) (defaults to: false)

    Whether or not this message should be sent using Discord text-to-speech.

  • embeds (Hash, Rubycord::Webhooks::Embed, Array<Hash>, Array<Rubycord::Webhooks::Embed> nil) (defaults to: nil)

    The rich embed(s) to append to this message.

  • attachments (Array<File>) (defaults to: nil)

    Files that can be referenced in embeds via attachment://file.png

  • allowed_mentions (Hash, Rubycord::AllowedMentions, false, nil) (defaults to: nil)

    Mentions that are allowed to ping on this message. false disables all pings

  • message_reference (Message, String, Integer, nil) (defaults to: nil)

    The message, or message ID, to reply to if any.

  • components (View, Array<Hash>) (defaults to: nil)

    Interaction components to associate with this message.



425
426
427
428
429
430
431
432
433
434
435
# File 'lib/rubycord/bot.rb', line 425

def send_temporary_message(channel, content, timeout, tts = false, embeds = nil, attachments = nil, allowed_mentions = nil, message_reference = nil, components = nil)
  Thread.new do
    Thread.current[:rubycord_name] = "#{@current_thread}-temp-msg"

    message = send_message(channel, content, tts, embeds, attachments, allowed_mentions, message_reference, components)
    sleep(timeout)
    message.delete
  end

  nil
end

#serversHash<Integer => Server>

The list of servers the bot is currently in.

Returns:



177
178
179
180
181
# File 'lib/rubycord/bot.rb', line 177

def servers
  gateway_check
  unavailable_servers_check
  @servers
end

#stop(_no_sync = nil) ⇒ Object

Note:

This method no longer takes an argument as of 3.4.0

Stops the bot gracefully, disconnecting the websocket without immediately killing the thread. This means that Discord is immediately aware of the closed connection and makes the bot appear offline instantly.



288
289
290
# File 'lib/rubycord/bot.rb', line 288

def stop(_no_sync = nil)
  @gateway.stop
end

#stream(name, url) ⇒ String

Sets the currently online stream to the specified name and Twitch URL.

Parameters:

  • name (String)

    The name of the stream to display.

  • url (String)

    The url of the current Twitch stream.

Returns:

  • (String)

    The stream name that is being displayed now.



597
598
599
600
601
# File 'lib/rubycord/bot.rb', line 597

def stream(name, url)
  gateway_check
  update_status(@status, name, url)
  name
end

#suppress_ready_debugObject

Prevents the READY packet from being printed regardless of debug mode.



689
690
691
# File 'lib/rubycord/bot.rb', line 689

def suppress_ready_debug
  @prevent_ready = true
end

#thread_membersHash<Integer => Hash<Integer => Hash<String => Object>>]

The list of members in threads the bot can see.

Returns:



185
186
187
188
189
# File 'lib/rubycord/bot.rb', line 185

def thread_members
  gateway_check
  unavailable_servers_check
  @thread_members
end

#tokenString

The Discord API token received when logging in. Useful to explicitly call API methods.

Returns:



245
246
247
248
# File 'lib/rubycord/bot.rb', line 245

def token
  API.bot_name = @name
  @token
end

#unignore_user(user) ⇒ Object

Remove a user from the ignore list.

Parameters:



765
766
767
# File 'lib/rubycord/bot.rb', line 765

def unignore_user(user)
  @ignored_ids.delete(user.resolve_id)
end

#update_oauth_application(name, redirect_uris, description = "", icon = nil) ⇒ Object

Changes information about your OAuth application

Parameters:

  • name (String)

    What your application should be called.

  • redirect_uris (Array<String>)

    URIs that Discord should redirect your users to after authorizing.

  • description (String) (defaults to: "")

    A string that describes what your application does.

  • icon (String, nil) (defaults to: nil)

    A data URI for your icon image (for example a base 64 encoded image), or nil if no icon should be set or changed.



493
494
495
# File 'lib/rubycord/bot.rb', line 493

def update_oauth_application(name, redirect_uris, description = "", icon = nil)
  API.update_oauth_application(@token, name, redirect_uris, description, icon)
end

#update_status(status, activity, url, since = 0, afk = false, activity_type = 0) ⇒ Object

Updates presence status.

Parameters:

  • status (String)

    The status the bot should show up as. Can be online, dnd, idle, or invisible

  • activity (String, nil)

    The name of the activity to be played/watched/listened to/stream name on the stream.

  • url (String, nil)

    The Twitch URL to display as a stream. nil for no stream.

  • since (Integer) (defaults to: 0)

    When this status was set.

  • afk (true, false) (defaults to: false)

    Whether the bot is AFK.

  • activity_type (Integer) (defaults to: 0)

    The type of activity status to display. Can be 0 (Playing), 1 (Streaming), 2 (Listening), 3 (Watching), 4 (Custom), or 5 (Competing).

See Also:



548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
# File 'lib/rubycord/bot.rb', line 548

def update_status(status, activity, url, since = 0, afk = false, activity_type = 0)
  gateway_check

  @activity = activity
  @status = status
  @streamurl = url
  type = url ? 1 : activity_type

  activity_obj = if type == 4
    {"name" => activity, "type" => type, "state" => activity}
  else
    (activity || url) ? {"name" => activity, "url" => url, "type" => type} : nil
  end
  @gateway.send_status_update(status, since, activity_obj, afk)

  # Update the status in the cache
  profile.update_presence("status" => status.to_s, "activities" => [activity_obj].compact)
end

#usersHash<Integer => User>

The list of users the bot shares a server with.

Returns:



169
170
171
172
173
# File 'lib/rubycord/bot.rb', line 169

def users
  gateway_check
  unavailable_servers_check
  @users
end

#voice(thing) ⇒ Voice::VoiceBot?

Gets the voice bot for a particular server or channel. You can connect to a new channel using the #voice_connect method.

Parameters:

  • thing (Channel, Server, Integer)

    the server or channel you want to get the voice bot for, or its ID.

Returns:

  • (Voice::VoiceBot, nil)

    the VoiceBot for the thing you specified, or nil if there is no connection yet



331
332
333
334
335
336
337
338
339
340
# File 'lib/rubycord/bot.rb', line 331

def voice(thing)
  id = thing.resolve_id
  return @voices[id] if @voices[id]

  channel = channel(id)
  return nil unless channel

  server_id = channel.server.id
  @voices[server_id]
end

#voice_connect(chan, encrypted = true) ⇒ Voice::VoiceBot

Connects to a voice channel, initializes network connections and returns the Voice::VoiceBot over which audio data can then be sent. After connecting, the bot can also be accessed using #voice. If the bot is already connected to voice, the existing connection will be terminated - you don't have to call Voice::VoiceBot#destroy before calling this method.

Parameters:

  • chan (Channel, String, Integer)

    The voice channel, or its ID, to connect to.

  • encrypted (true, false) (defaults to: true)

    Whether voice communication should be encrypted using (uses an XSalsa20 stream cipher for encryption and Poly1305 for authentication)

Returns:

  • (Voice::VoiceBot)

    the initialized bot over which audio data can then be sent.

Raises:

  • (ArgumentError)


350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
# File 'lib/rubycord/bot.rb', line 350

def voice_connect(chan, encrypted = true)
  raise ArgumentError, "Unencrypted voice connections are no longer supported." unless encrypted

  chan = channel(chan.resolve_id)
  server_id = chan.server.id

  if @voices[chan.id]
    debug("Voice bot exists already! Destroying it")
    @voices[chan.id].destroy
    @voices.delete(chan.id)
  end

  debug("Got voice channel: #{chan}")

  @should_connect_to_voice[server_id] = chan
  @gateway.send_voice_state_update(server_id.to_s, chan.id.to_s, false, false)

  debug("Voice channel init packet sent! Now waiting.")

  sleep(0.05) until @voices[server_id]
  debug("Voice connect succeeded!")
  @voices[server_id]
end

#voice_destroy(server, destroy_vws = true) ⇒ Object

Disconnects the client from a specific voice connection given the server ID. Usually it's more convenient to use Voice::VoiceBot#destroy rather than this.

Parameters:

  • server (Server, String, Integer)

    The server, or server ID, the voice connection is on.

  • destroy_vws (true, false) (defaults to: true)

    Whether or not the VWS should also be destroyed. If you're calling this method directly, you should leave it as true.



379
380
381
382
383
384
# File 'lib/rubycord/bot.rb', line 379

def voice_destroy(server, destroy_vws = true)
  server = server.resolve_id
  @gateway.send_voice_state_update(server.to_s, nil, false, false)
  @voices[server].destroy if @voices[server] && destroy_vws
  @voices.delete(server)
end

#watching=(name) ⇒ String

Sets the current watching status to the specified name.

Parameters:

  • name (String)

    The thing to be watched.

Returns:

  • (String)

    The thing that is now being watched.



588
589
590
591
# File 'lib/rubycord/bot.rb', line 588

def watching=(name)
  gateway_check
  update_status(@status, name, nil, nil, nil, 3)
end