I am trying to create a Discord bot, complete with logging directly to the Discord server it is in, however the discordrb gem itself refuses to let me rescue the block itself.
begin
require 'discordrb'
phoenix = Discordrb::Bot.new token: 'TOKEN'
crashpass = rand(0..9999999)
puts "Crash password: #{crashpass}" #Prints to the terminal screen, not to the server
phoenix.message(with_text: "CP!crash #{crashpass}") do
raise "Admin initiated crash."
end
rescue Exception #I know, bad practice, but I wish for this to always execute on error.
ensure
phoenix.run :async #allows code to keep running after bot initialization
phoenix.dnd
phoenix.send_message(454137675944034314, "Something terrible has happened, and I can't recover!\n#{e}")
phoenix.send_message(454137675944034314, "Currently running in emergency mode!")
phoenix.sync
end
This results in this:
Using WSCS version: 0.3.0
libsodium not available! You can continue to use discordrb as normal but voice support won't work.
Read https://github.com/meew0/discordrb/wiki/Installing-libsodium for more details.
Crash password: 6736731
[INFO : websocket @ 2018-06-07 19:04:57.517] Discord using gateway protocol version: 6, requested: 6
[ERROR : et-1 @ 2018-06-07 19:05:33.326] Exception: #<RuntimeError: Admin initiated crash.>
[ERROR : et-1 @ 2018-06-07 19:05:33.330] C:/Users/nathan/Desktop/Cyan_Phoenix local/bot.rb:19:in `block in <main>'
[ERROR : et-1 @ 2018-06-07 19:05:33.330] C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/discordrb-3.2.1/lib/discordrb/events/generic.rb:98:in `call'
[ERROR : et-1 @ 2018-06-07 19:05:33.330] C:/Ruby23-x64/lib/ruby/gems/2.3.0/gems/discordrb-3.2.1/lib/discordrb/bot.rb:1227:in `block in call_event'
The bot does not stop or report the error to the server, ignoring the entire rescue, including ensure (Which I believe is guaranteed to at least start running).
Is there a way to force the script to us my error handling, instead of the gems inbuilt one?
It will only prevent you from rescuing exceptions inside the phoenix
.
require 'discordrb'
phoenix = Discordrb::Bot.new token: 'TOKEN'
phoenix.run :async
begin
raise "Error here!"
rescue Exception
puts "Got exception!"
end
Something like this will work just fine, but when you're doing something like:
phoenix.message(with_text: "CP!crash #{crashpass}") do
raise "Admin initiated crash."
end
The exception will be raised inside the asynchronously running phoenix
DiscorrRb::Bot
instance which has its' own error handling, so that exceptions raised while running in the background, such as reconnecting after any connection errors, will get handled there instead of crashing the rest of the application.
If you want to send the exception messages to discord, you would need to modify the Discordrb::Logger
. However, I don't think it's very useful, as most likely the exceptions raised inside the Discordrb::Bot
async code will be related to happen in a situation where the connection has stopped working and it wouldn't be able to send the exception message to discord, causing an infinite loop / stack overflow where sending the exception message to discord causes an exception because the discord connection has been disconnected.
If however you want any exceptions in your code (not the Discordrb::Bot
's code) there's nothing stopping you from writing something like:
phoenix.run :async
loop do
begin
score = calculate_score
phoenix.send_message(channel_id, "Score : #{score}")
rescue => ex
phoenix.send_message(
channel_id,
"crash while calulcating score! #{ex.class} : #{ex.message}"
)
sleep 10
retry
end
sleep 10
end
And if you want to rescue inside an event handler:
phoenix.message(with_text: "score?") do |event|
begin
score = ScoreCalc.calculate_score
event.respond("Score : #{score}")
rescue => ex
send_message(454137675944034314, "CRASHED! #{ex.class}: #{ex.message}")
send_message(454137675944034314, ex.backtrace.join("\n"))
event.respond "Sorry, there was a problem and it has been reported"
end
end
Exception handling in threaded/asynchronous code is a common problem in Ruby.