rubyerror-handlingdiscordrescue

ruby gem stops me from rescuing


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?


Solution

  • 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.