Class: Discordrb::Bot
Overview
Represents a Discord bot, including servers, users, etc.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#bot_user ⇒ User
readonly
The user that represents the bot itself.
-
#event_threads ⇒ Array<Thread>
readonly
The list of currently running threads used to parse and call events.
-
#profile ⇒ Profile
readonly
The bot's user profile.
-
#servers ⇒ Array<Server>
readonly
The list of servers the bot is currently in.
-
#should_parse_self ⇒ Object
Whether or not the bot should parse its own messages.
-
#token ⇒ String
readonly
The Discord API token received when logging in.
-
#users ⇒ Array<User>
readonly
The list of users the bot shares a server with.
-
#voice ⇒ Object
readonly
Returns the value of attribute voice.
Instance Method Summary collapse
-
#add_await(key, type, attributes = {}) {|event| ... } ⇒ Await
Add an await the bot should listen to.
- #add_handler(handler) ⇒ Object (also: #<<)
-
#await(attributes = {}) {|event| ... } ⇒ AwaitEventHandler
This event is raised when an Await is triggered.
-
#channel(id) ⇒ Channel
Gets a channel given its ID.
-
#channel_create(attributes = {}, &block) ⇒ Object
Handle channel creation Attributes: * type: Channel type ('text' or 'voice') * name: Channel name.
-
#channel_delete(attributes = {}, &block) ⇒ Object
Handle channel deletion Attributes: * type: Channel type ('text' or 'voice') * name: Channel name.
-
#channel_update(attributes = {}, &block) ⇒ Object
Handle channel update Attributes: * type: Channel type ('text' or 'voice') * name: Channel name.
-
#create_server(name, region = :london) ⇒ Server
Creates a server on Discord with a specified name and a region.
- #debug(message, important = false) ⇒ Object
-
#debug=(new_debug) ⇒ Object
Sets debug mode.
-
#delete_invite(code) ⇒ Object
Revokes an invite to a server.
- #disconnected(attributes = {}, &block) ⇒ Object
-
#find(channel_name, server_name = nil, threshold = 0) ⇒ Array<Channel>
Finds a channel given its name and optionally the name of the server it is in.
-
#find_user(username, threshold = 0) ⇒ Array<User>
Finds a user given its username.
-
#game=(name) ⇒ String
Sets the currently playing game to the specified game.
- #handler_class(event_class) ⇒ Object
-
#initialize(email, password, debug = false) ⇒ Bot
constructor
Makes a new bot with the given email and password.
-
#join(invite) ⇒ Object
Makes the bot join an invite to a server.
- #log_exception(e) ⇒ Object
- #member_join(attributes = {}, &block) ⇒ Object
- #member_leave(attributes = {}, &block) ⇒ Object
- #member_update(attributes = {}, &block) ⇒ Object
- #mention(attributes = {}, &block) ⇒ Object
-
#message(attributes = {}) {|event| ... } ⇒ MessageEventHandler
This event is raised when a message is sent to a text channel the bot is currently in.
-
#parse_mention(mention) ⇒ User
Gets the user from a mention of the user.
- #playing(attributes = {}, &block) ⇒ Object
- #pm(attributes = {}, &block) ⇒ Object (also: #private_message)
- #presence(attributes = {}, &block) ⇒ Object
-
#private_channel(id) ⇒ Channel
Creates a private channel for the given user ID, or if one exists already, returns that one.
- #ready(attributes = {}, &block) ⇒ Object
- #remove_handler(handler) ⇒ Object
-
#resolve_invite_code(invite) ⇒ String
Gets the code for an invite.
-
#run(async = false) ⇒ Object
Runs the bot, which logs into Discord and connects the WebSocket.
- #run_async ⇒ Object
-
#send_file(channel_id, file) ⇒ Object
Sends a file to a channel.
-
#send_message(channel_id, content) ⇒ Message
Sends a text message to a channel given its ID and the message's content.
-
#server(id) ⇒ Server?
Gets a server by its ID.
- #server_create(attributes = {}, &block) ⇒ Object
- #server_delete(attributes = {}, &block) ⇒ Object
- #server_update(attributes = {}, &block) ⇒ Object
-
#stop ⇒ Object
Kills the websocket thread, stopping all connections to Discord.
-
#sync ⇒ Object
Prevents all further execution until the websocket thread stops (e. g. through a closed connection).
- #typing(attributes = {}, &block) ⇒ Object
-
#user(id) ⇒ User?
Gets a user by its ID.
- #voice_connect(chan) ⇒ Object
-
#voice_state_update(attributes = {}, &block) ⇒ Object
Handle a change to a voice state.
Methods included from Events
Constructor Details
#initialize(email, password, debug = false) ⇒ Bot
Makes a new bot with the given email and password. It will be ready to be added event handlers to and can eventually be run with #run.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 |
# File 'lib/discordrb/bot.rb', line 70 def initialize(email, password, debug = false) # Make sure people replace the login details in the example files... if email.end_with? 'example.com' puts 'You have to replace the login details in the example files with your own!' exit end LOGGER.debug = debug @should_parse_self = false @email = email @password = password debug('Creating token cache') @token_cache = Discordrb::TokenCache.new debug('Token cache created successfully') @token = login @event_handlers = {} @channels = {} @users = {} @awaits = {} @event_threads = [] @current_thread = 0 end |
Instance Attribute Details
#bot_user ⇒ User (readonly)
The user that represents the bot itself. This version will always be identical to the user determined by #user called with the bot's ID.
36 37 38 |
# File 'lib/discordrb/bot.rb', line 36 def bot_user @bot_user end |
#event_threads ⇒ Array<Thread> (readonly)
The list of currently running threads used to parse and call events.
The threads will have a local variable :discordrb_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.
56 57 58 |
# File 'lib/discordrb/bot.rb', line 56 def event_threads @event_threads end |
#profile ⇒ Profile (readonly)
The bot's user profile. This special user object can be used to edit user data like the current username (see Profile#username=).
61 62 63 |
# File 'lib/discordrb/bot.rb', line 61 def profile @profile end |
#servers ⇒ Array<Server> (readonly)
The list of servers the bot is currently in.
49 50 51 |
# File 'lib/discordrb/bot.rb', line 49 def servers @servers end |
#should_parse_self ⇒ Object
Whether or not the bot should parse its own messages. Off by default.
64 65 66 |
# File 'lib/discordrb/bot.rb', line 64 def should_parse_self @should_parse_self end |
#token ⇒ String (readonly)
The Discord API token received when logging in. Useful to explicitly call API methods.
41 42 43 |
# File 'lib/discordrb/bot.rb', line 41 def token @token end |
#users ⇒ Array<User> (readonly)
The list of users the bot shares a server with.
45 46 47 |
# File 'lib/discordrb/bot.rb', line 45 def users @users end |
#voice ⇒ Object (readonly)
Returns the value of attribute voice.
201 202 203 |
# File 'lib/discordrb/bot.rb', line 201 def voice @voice end |
Instance Method Details
#add_await(key, type, attributes = {}) {|event| ... } ⇒ Await
Add an await the bot should listen to. For information on awaits, see Await.
337 338 339 340 341 |
# File 'lib/discordrb/bot.rb', line 337 def add_await(key, type, attributes = {}, &block) fail "You can't await an AwaitEvent!" if type == Discordrb::Events::AwaitEvent await = Await.new(self, key, type, attributes, block) @awaits[key] = await end |
#add_handler(handler) ⇒ Object Also known as: <<
533 534 535 536 |
# File 'lib/discordrb/bot.rb', line 533 def add_handler(handler) clazz = event_class(handler.class) @event_handlers[clazz] << handler end |
#await(attributes = {}) {|event| ... } ⇒ AwaitEventHandler
This event is raised when an Await is triggered. It provides an easy way to execute code on an await without having to rely on the await's block.
518 519 520 |
# File 'lib/discordrb/bot.rb', line 518 def await(attributes = {}, &block) register_event(AwaitEvent, attributes, block) end |
#channel(id) ⇒ Channel
Gets a channel given its ID. This queries the internal channel cache, and if the channel doesn't exist in there, it will get the data from Discord.
155 156 157 158 159 160 161 162 |
# File 'lib/discordrb/bot.rb', line 155 def channel(id) debug("Obtaining data for channel with id #{id}") return @channels[id] if @channels[id] response = API.channel(@token, id) channel = Channel.new(JSON.parse(response), self) @channels[id] = channel end |
#channel_create(attributes = {}, &block) ⇒ Object
Handle channel creation Attributes:
- type: Channel type ('text' or 'voice')
- name: Channel name
453 454 455 |
# File 'lib/discordrb/bot.rb', line 453 def channel_create(attributes = {}, &block) register_event(ChannelCreateEvent, attributes, block) end |
#channel_delete(attributes = {}, &block) ⇒ Object
Handle channel deletion Attributes:
- type: Channel type ('text' or 'voice')
- name: Channel name
469 470 471 |
# File 'lib/discordrb/bot.rb', line 469 def channel_delete(attributes = {}, &block) register_event(ChannelDeleteEvent, attributes, block) end |
#channel_update(attributes = {}, &block) ⇒ Object
Handle channel update Attributes:
- type: Channel type ('text' or 'voice')
- name: Channel name
461 462 463 |
# File 'lib/discordrb/bot.rb', line 461 def channel_update(attributes = {}, &block) register_event(ChannelUpdateEvent, attributes, block) end |
#create_server(name, region = :london) ⇒ Server
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.
357 358 359 360 361 362 363 364 |
# File 'lib/discordrb/bot.rb', line 357 def create_server(name, region = :london) response = API.create_server(@token, name, region) id = JSON.parse(response)['id'].to_i sleep 0.1 until @servers[id] server = @servers[id] debug "Successfully created server #{server.id} with name #{server.name}" server end |
#debug(message, important = false) ⇒ Object
538 539 540 |
# File 'lib/discordrb/bot.rb', line 538 def debug(, important = false) LOGGER.debug(, important) end |
#debug=(new_debug) ⇒ Object
Sets debug mode. If debug mode is on, many things will be outputted to STDOUT.
394 395 396 |
# File 'lib/discordrb/bot.rb', line 394 def debug=(new_debug) LOGGER.debug = new_debug 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.
237 238 239 240 |
# File 'lib/discordrb/bot.rb', line 237 def delete_invite(code) invite = resolve_invite_code(code) API.delete_invite(@token, invite) end |
#disconnected(attributes = {}, &block) ⇒ Object
429 430 431 |
# File 'lib/discordrb/bot.rb', line 429 def disconnected(attributes = {}, &block) register_event(DisconnectEvent, attributes, block) end |
#find(channel_name, server_name = nil, threshold = 0) ⇒ Array<Channel>
Finds a channel given its name and optionally the name of the server it is in. If the threshold is not 0, it will use a Levenshtein distance function to find the channel in a fuzzy way, which allows slight misspellings.
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 |
# File 'lib/discordrb/bot.rb', line 266 def find(channel_name, server_name = nil, threshold = 0) require 'levenshtein' results = [] @servers.values.each do |server| server.channels.each do |channel| distance = Levenshtein.distance(channel.name, channel_name) distance += Levenshtein.distance(server_name || server.name, server.name) next if distance > threshold # Make a singleton accessor "distance" channel.instance_variable_set(:@distance, distance) class << channel attr_reader :distance end results << channel end end results end |
#find_user(username, threshold = 0) ⇒ Array<User>
Finds a user given its username. This allows fuzzy finding using Levenshtein distances, see #find
293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 |
# File 'lib/discordrb/bot.rb', line 293 def find_user(username, threshold = 0) require 'levenshtein' results = [] @users.values.each do |user| distance = Levenshtein.distance(user.username, username) next if distance > threshold # Make a singleton accessor "distance" user.instance_variable_set(:@distance, distance) class << user attr_reader :distance end results << user end results end |
#game=(name) ⇒ String
Sets the currently playing game to the specified game.
378 379 380 381 382 383 384 385 386 387 388 389 390 391 |
# File 'lib/discordrb/bot.rb', line 378 def game=(name) @game = name data = { 'op' => 3, 'd' => { 'idle_since' => nil, 'game' => name ? { 'name' => name } : nil } } @ws.send(data.to_json) name end |
#handler_class(event_class) ⇒ Object
546 547 548 |
# File 'lib/discordrb/bot.rb', line 546 def handler_class(event_class) class_from_string(event_class.to_s + 'Handler') end |
#join(invite) ⇒ Object
Makes the bot join an invite to a server.
195 196 197 198 199 |
# File 'lib/discordrb/bot.rb', line 195 def join(invite) invite = resolve_invite_code(invite) resolved = JSON.parse(API.resolve_invite(@token, invite))['code'] API.join_server(@token, resolved) end |
#log_exception(e) ⇒ Object
542 543 544 |
# File 'lib/discordrb/bot.rb', line 542 def log_exception(e) LOGGER.log_exception(e) end |
#member_join(attributes = {}, &block) ⇒ Object
486 487 488 |
# File 'lib/discordrb/bot.rb', line 486 def member_join(attributes = {}, &block) register_event(GuildMemberAddEvent, attributes, block) end |
#member_leave(attributes = {}, &block) ⇒ Object
494 495 496 |
# File 'lib/discordrb/bot.rb', line 494 def member_leave(attributes = {}, &block) register_event(GuildMemberDeleteEvent, attributes, block) end |
#member_update(attributes = {}, &block) ⇒ Object
490 491 492 |
# File 'lib/discordrb/bot.rb', line 490 def member_update(attributes = {}, &block) register_event(GuildMemberUpdateEvent, attributes, block) end |
#mention(attributes = {}, &block) ⇒ Object
445 446 447 |
# File 'lib/discordrb/bot.rb', line 445 def mention(attributes = {}, &block) register_event(MentionEvent, attributes, block) end |
#message(attributes = {}) {|event| ... } ⇒ MessageEventHandler
This event is raised when a message is sent to a text channel the bot is currently in.
421 422 423 |
# File 'lib/discordrb/bot.rb', line 421 def (attributes = {}, &block) register_event(MessageEvent, attributes, block) end |
#parse_mention(mention) ⇒ User
Gets the user from a mention of the user.
369 370 371 372 373 |
# File 'lib/discordrb/bot.rb', line 369 def parse_mention(mention) # Mention format: <@id> return nil unless /\<@(?<id>\d+)\>?/ =~ mention user(id.to_i) end |
#playing(attributes = {}, &block) ⇒ Object
441 442 443 |
# File 'lib/discordrb/bot.rb', line 441 def (attributes = {}, &block) register_event(PlayingEvent, attributes, block) end |
#pm(attributes = {}, &block) ⇒ Object Also known as: private_message
522 523 524 |
# File 'lib/discordrb/bot.rb', line 522 def pm(attributes = {}, &block) register_event(PrivateMessageEvent, attributes, block) end |
#presence(attributes = {}, &block) ⇒ Object
437 438 439 |
# File 'lib/discordrb/bot.rb', line 437 def presence(attributes = {}, &block) register_event(PresenceEvent, attributes, block) end |
#private_channel(id) ⇒ Channel
Creates a private channel for the given user ID, or if one exists already, returns that one. It is recommended that you use User#pm instead, as this is mainly for internal use. However, usage of this method may be unavoidable if only the user ID is known.
169 170 171 172 173 174 175 176 |
# File 'lib/discordrb/bot.rb', line 169 def private_channel(id) debug("Creating private channel with user id #{id}") return @private_channels[id] if @private_channels[id] response = API.create_private(@token, @bot_user.id, id) channel = Channel.new(JSON.parse(response), self) @private_channels[id] = channel end |
#ready(attributes = {}, &block) ⇒ Object
425 426 427 |
# File 'lib/discordrb/bot.rb', line 425 def ready(attributes = {}, &block) register_event(ReadyEvent, attributes, block) end |
#remove_handler(handler) ⇒ Object
528 529 530 531 |
# File 'lib/discordrb/bot.rb', line 528 def remove_handler(handler) clazz = event_class(handler.class) @event_handlers[clazz].delete(handler) end |
#resolve_invite_code(invite) ⇒ String
Gets the code for an invite.
187 188 189 190 191 |
# File 'lib/discordrb/bot.rb', line 187 def resolve_invite_code(invite) invite = invite.code if invite.is_a? Discordrb::Invite invite = invite[invite.rindex('/') + 1..-1] if invite.start_with?('http') || invite.start_with?('discord.gg') invite end |
#run(async = false) ⇒ Object
Runs the bot, which logs into Discord and connects the WebSocket. This prevents all further execution unless it is executed with async = :async.
105 106 107 108 109 110 111 |
# File 'lib/discordrb/bot.rb', line 105 def run(async = false) run_async return if async debug('Oh wait! Not exiting yet as run was run synchronously.') sync end |
#run_async ⇒ Object
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 |
# File 'lib/discordrb/bot.rb', line 113 def run_async # Handle heartbeats @heartbeat_interval = 1 @heartbeat_active = false @heartbeat_thread = Thread.new do Thread.current[:discordrb_name] = 'heartbeat' loop do sleep @heartbeat_interval send_heartbeat if @heartbeat_active end end @ws_thread = Thread.new do Thread.current[:discordrb_name] = 'websocket' loop do websocket_connect debug('Disconnected! Attempting to reconnect in 5 seconds.') sleep 5 @token = login end end debug('WS thread created! Now waiting for confirmation that everything worked') @ws_success = false sleep(0.5) until @ws_success debug('Confirmation received! Exiting run.') end |
#send_file(channel_id, file) ⇒ Object
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.
326 327 328 |
# File 'lib/discordrb/bot.rb', line 326 def send_file(channel_id, file) API.send_file(@token, channel_id, file) end |
#send_message(channel_id, content) ⇒ Message
Sends a text message to a channel given its ID and the message's content.
315 316 317 318 319 320 |
# File 'lib/discordrb/bot.rb', line 315 def (channel_id, content) debug("Sending message to #{channel_id} with content '#{content}'") response = API.(@token, channel_id, content) Message.new(JSON.parse(response), self) end |
#server(id) ⇒ Server?
This can only resolve servers the bot is currently in.
Gets a server by its ID.
254 255 256 |
# File 'lib/discordrb/bot.rb', line 254 def server(id) @servers[id] end |
#server_create(attributes = {}, &block) ⇒ Object
498 499 500 |
# File 'lib/discordrb/bot.rb', line 498 def server_create(attributes = {}, &block) register_event(GuildCreateEvent, attributes, block) end |
#server_delete(attributes = {}, &block) ⇒ Object
506 507 508 |
# File 'lib/discordrb/bot.rb', line 506 def server_delete(attributes = {}, &block) register_event(GuildDeleteEvent, attributes, block) end |
#server_update(attributes = {}, &block) ⇒ Object
502 503 504 |
# File 'lib/discordrb/bot.rb', line 502 def server_update(attributes = {}, &block) register_event(GuildUpdateEvent, attributes, block) end |
#stop ⇒ Object
Kills the websocket thread, stopping all connections to Discord.
147 148 149 |
# File 'lib/discordrb/bot.rb', line 147 def stop @ws_thread.kill end |
#sync ⇒ Object
Prevents all further execution until the websocket thread stops (e. g. through a closed connection).
142 143 144 |
# File 'lib/discordrb/bot.rb', line 142 def sync @ws_thread.join end |
#typing(attributes = {}, &block) ⇒ Object
433 434 435 |
# File 'lib/discordrb/bot.rb', line 433 def typing(attributes = {}, &block) register_event(TypingEvent, attributes, block) end |
#user(id) ⇒ User?
This can only resolve users known by the bot (i.e. that share a server with the bot).
Gets a user by its ID.
246 247 248 |
# File 'lib/discordrb/bot.rb', line 246 def user(id) @users[id] end |
#voice_connect(chan) ⇒ Object
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
# File 'lib/discordrb/bot.rb', line 203 def voice_connect(chan) if @voice debug('Voice bot exists already! Destroying it') @voice.destroy @voice = nil end chan = channel(chan) if chan.is_a? Integer @voice_channel = chan debug("Got voice channel: #{@voice_channel}") data = { op: 4, d: { guild_id: @voice_channel.server.id.to_s, channel_id: @voice_channel.id.to_s, self_mute: false, self_deaf: false } } debug("Voice channel init packet is: #{data.to_json}") @should_connect_to_voice = true @ws.send(data.to_json) debug('Voice channel init packet sent! Now waiting.') sleep(0.05) until @voice debug('Voice connect succeeded!') @voice end |
#voice_state_update(attributes = {}, &block) ⇒ Object
Handle a change to a voice state. This includes joining a voice channel or changing mute or deaf state. Attributes:
- from: User whose voice state changed
- mute: server mute status
- deaf: server deaf status
- self_mute: self mute status
- self_deaf: self deaf status
- channel: channel the user joined
482 483 484 |
# File 'lib/discordrb/bot.rb', line 482 def voice_state_update(attributes = {}, &block) register_event(VoiceStateUpdateEvent, attributes, block) end |