]> git.netwichtig.de Git - user/henk/code/ruby/rbot.git/blobdiff - data/rbot/plugins/games/quiz.rb
ensures the path reported by gems does exists
[user/henk/code/ruby/rbot.git] / data / rbot / plugins / games / quiz.rb
index 96e2ad402e520f3d30c552a890a184d4cd6edef4..b139c66a2fad25eb2ec8c9c0e31900ce2f0307a3 100644 (file)
@@ -175,6 +175,22 @@ class QuizPlugin < Plugin
     @ask_mutex = Mutex.new
   end
 
     @ask_mutex = Mutex.new
   end
 
+  def cleanup
+    @ask_mutex.synchronize do
+      # purge all waiting timers
+      @waiting.each do |chan, t|
+        @bot.timer.remove t.first
+        @bot.say chan, _("stopped quiz timer")
+      end
+      @waiting.clear
+    end
+    chans = @quizzes.keys
+    @quizzes.clear
+    chans.each do |chan|
+      @bot.say chan, _("quiz stopped")
+    end
+  end
+
   # Function that returns whether a char is a "separator", used for hints
   #
   def is_sep( ch )
   # Function that returns whether a char is a "separator", used for hints
   #
   def is_sep( ch )
@@ -300,7 +316,17 @@ class QuizPlugin < Plugin
 
   def help( plugin, topic="" )
     if topic == "admin"
 
   def help( plugin, topic="" )
     if topic == "admin"
-      "Quiz game aministration commands (requires authentication): 'quiz autoask <on/off>' => enable/disable autoask mode. 'quiz autoask delay <secs>' => delay next quiz by <secs> seconds when in autoask mode. 'quiz transfer <source> <dest> [score] [jokers]' => transfer [score] points and [jokers] jokers from <source> to <dest> (default is entire score and all jokers). 'quiz setscore <player> <score>' => set <player>'s score to <score>. 'quiz setjokers <player> <jokers>' => set <player>'s number of jokers to <jokers>. 'quiz deleteplayer <player>' => delete one player from the rank table (only works when score and jokers are set to 0). 'quiz cleanup' => remove players with no points and no jokers."
+      _("Quiz game aministration commands (requires authentication): ") + [
+        _("'quiz autoask <on/off>' => enable/disable autoask mode"),
+        _("'quiz autoask delay <time>' => delay next quiz by <time> when in autoask mode"),
+        _("'quiz autoskip <on/off>' => enable/disable autoskip mode (autoskip implies autoask)"),
+        _("'quiz autoskip delay <time>' => wait <time> before skipping to next quiz when in autoskip mode"),
+        _("'quiz transfer <source> <dest> [score] [jokers]' => transfer [score] points and [jokers] jokers from <source> to <dest> (default is entire score and all jokers)"),
+        _("'quiz setscore <player> <score>' => set <player>'s score to <score>"),
+        _("'quiz setjokers <player> <jokers>' => set <player>'s number of jokers to <jokers>"),
+        _("'quiz deleteplayer <player>' => delete one player from the rank table (only works when score and jokers are set to 0)"),
+        _("'quiz cleanup' => remove players with no points and no jokers")
+      ].join(". ")
     else
       urls = @bot.config['quiz.sources'].select { |p| p =~ /^https?:\/\// }
       "A multiplayer trivia quiz. 'quiz' => ask a question. 'quiz hint' => get a hint. 'quiz solve' => solve this question. 'quiz skip' => skip to next question. 'quiz joker' => draw a joker to win this round. 'quiz score [player]' => show score for [player] (default is yourself). 'quiz top5' => show top 5 players. 'quiz top <number>' => show top <number> players (max 50). 'quiz stats' => show some statistics. 'quiz fetch' => refetch questions from databases. 'quiz refresh' => refresh the question pool for this channel." + (urls.empty? ? "" : "\nYou can add new questions at #{urls.join(', ')}")
     else
       urls = @bot.config['quiz.sources'].select { |p| p =~ /^https?:\/\// }
       "A multiplayer trivia quiz. 'quiz' => ask a question. 'quiz hint' => get a hint. 'quiz solve' => solve this question. 'quiz skip' => skip to next question. 'quiz joker' => draw a joker to win this round. 'quiz score [player]' => show score for [player] (default is yourself). 'quiz top5' => show top 5 players. 'quiz top <number>' => show top <number> players (max 50). 'quiz stats' => show some statistics. 'quiz fetch' => refetch questions from databases. 'quiz refresh' => refresh the question pool for this channel." + (urls.empty? ? "" : "\nYou can add new questions at #{urls.join(', ')}")
@@ -359,6 +385,23 @@ class QuizPlugin < Plugin
   end
 
 
   end
 
 
+  def setup_ask_timer(m, q)
+    chan = m.channel
+    delay = q.registry_conf["autoask_delay"]
+    if delay > 0
+      m.reply "#{Bold}#{Color}03Next question in #{Bold}#{delay}#{Bold} seconds"
+      timer = @bot.timer.add_once(delay) {
+        @ask_mutex.synchronize do
+        @waiting.delete(chan)
+        end
+      cmd_quiz( m, nil)
+      }
+      @waiting[chan] = [timer, :ask]
+    else
+      cmd_quiz( m, nil )
+    end
+  end
+
   # Reimplemented from Plugin
   #
   def message(m)
   # Reimplemented from Plugin
   #
   def message(m)
@@ -375,6 +418,15 @@ class QuizPlugin < Plugin
     # Support multiple alternate answers and cores
     answer = q.answers.find { |ans| ans.valid?(message) }
     if answer
     # Support multiple alternate answers and cores
     answer = q.answers.find { |ans| ans.valid?(message) }
     if answer
+
+      # purge the autoskip timer
+      @ask_mutex.synchronize do
+        if @waiting.key? chan and @waiting[chan].last == :skip
+          @bot.timer.remove(@waiting[chan].first)
+          @waiting.delete(chan)
+        end
+      end
+
       # List canonical answer which the hint was based on, to avoid confusion
       # FIXME display this more friendly
       answer.info = " (hints were for alternate answer #{q.canonical_answer.core})" if answer != q.canonical_answer and q.hinted
       # List canonical answer which the hint was based on, to avoid confusion
       # FIXME display this more friendly
       answer.info = " (hints were for alternate answer #{q.canonical_answer.core})" if answer != q.canonical_answer and q.hinted
@@ -416,20 +468,9 @@ class QuizPlugin < Plugin
       calculate_ranks( m, q, nick)
 
       q.question = nil
       calculate_ranks( m, q, nick)
 
       q.question = nil
-      if q.registry_conf["autoask"]
-        delay = q.registry_conf["autoask_delay"]
-        if delay > 0
-          m.reply "#{Bold}#{Color}03Next question in #{Bold}#{delay}#{Bold} seconds"
-          timer = @bot.timer.add_once(delay) {
-            @ask_mutex.synchronize do
-              @waiting.delete(chan)
-            end
-            cmd_quiz( m, nil)
-          }
-          @waiting[chan] = timer
-        else
-          cmd_quiz( m, nil )
-        end
+
+      if q.registry_conf['autoskip'] or q.registry_conf["autoask"]
+        setup_ask_timer(m, q)
       end
     else
       # First try is used, and it wasn't the answer.
       end
     else
       # First try is used, and it wasn't the answer.
@@ -455,7 +496,7 @@ class QuizPlugin < Plugin
     chan = m.channel
 
     @ask_mutex.synchronize do
     chan = m.channel
 
     @ask_mutex.synchronize do
-      if @waiting.has_key?(chan)
+      if @waiting.has_key?(chan) and @waiting[chan].last == :ask
         m.reply "Next quiz question will be automatically asked soon, have patience"
         return
       end
         m.reply "Next quiz question will be automatically asked soon, have patience"
         return
       end
@@ -522,12 +563,34 @@ class QuizPlugin < Plugin
     q.hintrange = (0..q.hint.length-1).sort_by{ rand }
 
     m.reply "#{Bold}#{Color}03Question: #{Color}#{Bold}" + q.question
     q.hintrange = (0..q.hint.length-1).sort_by{ rand }
 
     m.reply "#{Bold}#{Color}03Question: #{Color}#{Bold}" + q.question
+
+    if q.registry_conf.key? 'autoskip'
+      delay = q.registry_conf['autoskip_delay']
+      timer = @bot.timer.add_once(delay) do
+        m.reply _("Nobody managed to answer in %{time}! Skipping to the next question ...") % {
+          :time => Utils.secs_to_string(delay)
+        }
+        q.question = nil
+        @ask_mutex.synchronize do
+          @waiting.delete(chan)
+        end
+        setup_ask_timer(m, q)
+      end
+      @waiting[chan] = [timer, :skip]
+    end
   end
 
 
   def cmd_solve( m, params )
     chan = m.channel
 
   end
 
 
   def cmd_solve( m, params )
     chan = m.channel
 
+    @ask_mutex.synchronize do
+      if @waiting.has_key?(chan) and @waiting[chan].last == :skip
+        m.reply _("you can't make me solve a quiz in autoskip mode, sorry")
+        return
+      end
+    end
+
     return unless @quizzes.has_key?( chan )
     q = @quizzes[chan]
 
     return unless @quizzes.has_key?( chan )
     q = @quizzes[chan]
 
@@ -535,7 +598,7 @@ class QuizPlugin < Plugin
 
     q.question = nil
 
 
     q.question = nil
 
-    cmd_quiz( m, nil ) if q.registry_conf["autoask"]
+    cmd_quiz( m, nil ) if q.registry_conf["autoask"] or q.registry_conf["autoskip"]
   end
 
 
   end
 
 
@@ -595,6 +658,14 @@ class QuizPlugin < Plugin
 
   def cmd_skip( m, params )
     chan = m.channel
 
   def cmd_skip( m, params )
     chan = m.channel
+
+    @ask_mutex.synchronize do
+      if @waiting.has_key?(chan) and @waiting[chan].last == :skip
+        m.reply _("I'll skip to the next question as soon as the timeout expires, not now")
+        return
+      end
+    end
+
     return unless @quizzes.has_key?(chan)
     q = @quizzes[chan]
 
     return unless @quizzes.has_key?(chan)
     q = @quizzes[chan]
 
@@ -723,19 +794,26 @@ class QuizPlugin < Plugin
 
     params[:enable] ||= 'status'
 
 
     params[:enable] ||= 'status'
 
+    reg = q.registry_conf
+
     case params[:enable].downcase
     when "on", "true"
     case params[:enable].downcase
     when "on", "true"
-      q.registry_conf["autoask"] = true
+      reg["autoask"] = true
       m.reply "Enabled autoask mode."
       m.reply "Enabled autoask mode."
+      reg["autoask_delay"] = 0 unless reg.has_key("autoask_delay")
       cmd_quiz( m, nil ) if q.question == nil
     when "off", "false"
       cmd_quiz( m, nil ) if q.question == nil
     when "off", "false"
-      q.registry_conf["autoask"] = false
+      reg["autoask"] = false
       m.reply "Disabled autoask mode."
     when "status"
       m.reply "Disabled autoask mode."
     when "status"
-      m.reply _("Autoask is %{status}, the delay is %{time}") % {
-        :status => q.registry_conf["autoask"],
-        :time => Utils.secs_to_string(q.registry_conf["autoask_delay"]),
-      }
+      if reg.has_key? "autoask"
+        m.reply _("autoask is %{status}, the delay is %{time}") % {
+          :status => reg["autoask"],
+          :time => Utils.secs_to_string(reg["autoask_delay"]),
+        }
+      else
+        m.reply _("autoask is not configured here")
+      end
     else
       m.reply "Invalid autoask parameter. Use 'on' or 'off' to set it, 'status' to check the current status."
     end
     else
       m.reply "Invalid autoask parameter. Use 'on' or 'off' to set it, 'status' to check the current status."
     end
@@ -746,11 +824,96 @@ class QuizPlugin < Plugin
     q = create_quiz( chan, m )
     return unless q
 
     q = create_quiz( chan, m )
     return unless q
 
-    delay = params[:time].to_i
+    time = params[:time].to_s
+    if time =~ /^-?\d+$/
+      delay = time.to_i
+    else
+      begin
+        delay = Utils.parse_time_offset(time)
+      rescue RuntimeError
+        m.reply _("I couldn't understand that delay expression, sorry")
+        return
+      end
+    end
+
+    if delay < 0
+      m.reply _("wait, you want me to ask the next question %{abs} BEFORE the previous one gets solved?") % {
+        :abs => Utils.secs_to_string(-delay)
+      }
+      return
+    end
+
     q.registry_conf["autoask_delay"] = delay
     q.registry_conf["autoask_delay"] = delay
-    m.reply "Autoask delay now #{q.registry_conf['autoask_delay']} seconds"
+    m.reply "autoask delay now #{q.registry_conf['autoask_delay']} seconds"
+  end
+
+
+  def cmd_autoskip( m, params )
+    chan = m.channel
+    q = create_quiz( chan, m )
+    return unless q
+
+    params[:enable] ||= 'status'
+
+    reg = q.registry_conf
+
+    case params[:enable].downcase
+    when "on", "true"
+      reg["autoskip"] = true
+      m.reply "Enabled autoskip mode."
+      # default: 1 minute (TODO customize with a global config key)
+      reg["autoskip_delay"] = 60 unless reg.has_key("autoskip_delay")
+      # also set a default autoask delay
+      reg["autoask_delay"] = 0 unless reg.has_key("autoask_delay")
+    when "off", "false"
+      reg["autoskip"] = false
+      m.reply "Disabled autoskip mode."
+    when "status"
+      if reg.has_key? "autoskip"
+        m.reply _("autoskip is %{status}, the delay is %{time}") % {
+          :status => reg["autoskip"],
+          :time => Utils.secs_to_string(reg["autoskip_delay"]),
+        }
+      else
+        m.reply _("autoskip is not configured here")
+      end
+    else
+      m.reply "Invalid autoskip parameter. Use 'on' or 'off' to set it, 'status' to check the current status."
+    end
   end
 
   end
 
+  def cmd_autoskip_delay( m, params )
+    chan = m.channel
+    q = create_quiz( chan, m )
+    return unless q
+
+    time = params[:time].to_s
+    if time =~ /^-?\d+$/
+      delay = time.to_i
+    else
+      begin
+        delay = Utils.parse_time_offset(time)
+      rescue RuntimeError
+        m.reply _("I couldn't understand that delay expression, sorry")
+        return
+      end
+    end
+
+    if delay < 0
+      m.reply _("wait, you want me to skip to the next question %{abs} BEFORE the previous one?") % {
+        :abs => Utils.secs_to_string(-delay)
+      }
+      return
+    elsif delay == 0
+      m.reply _("sure, I'll ask all the questions at the same time! </sarcasm>")
+      return
+    end
+
+    q.registry_conf["autoskip_delay"] = delay
+    m.reply "autoskip delay now #{q.registry_conf['autoskip_delay']} seconds"
+  end
+
+
   def cmd_transfer( m, params )
     chan = m.channel
     q = create_quiz( chan, m )
   def cmd_transfer( m, params )
     chan = m.channel
     q = create_quiz( chan, m )
@@ -914,7 +1077,7 @@ class QuizPlugin < Plugin
     if @quizzes.delete m.channel
       @ask_mutex.synchronize do
         t = @waiting.delete(m.channel)
     if @quizzes.delete m.channel
       @ask_mutex.synchronize do
         t = @waiting.delete(m.channel)
-        @bot.timer.remove t if t
+        @bot.timer.remove t.first if t
       end
       m.okay
     else
       end
       m.okay
     else
@@ -944,7 +1107,9 @@ plugin.map 'quiz stop', :action => :stop
 
 # Admin commands
 plugin.map 'quiz autoask [:enable]',  :action => 'cmd_autoask', :auth_path => 'edit'
 
 # Admin commands
 plugin.map 'quiz autoask [:enable]',  :action => 'cmd_autoask', :auth_path => 'edit'
-plugin.map 'quiz autoask delay :time',  :action => 'cmd_autoask_delay', :auth_path => 'edit', :requirements => {:time => /\d+/}
+plugin.map 'quiz autoask delay *time',  :action => 'cmd_autoask_delay', :auth_path => 'edit'
+plugin.map 'quiz autoskip [:enable]',  :action => 'cmd_autoskip', :auth_path => 'edit'
+plugin.map 'quiz autoskip delay *time',  :action => 'cmd_autoskip_delay', :auth_path => 'edit'
 plugin.map 'quiz transfer :source :dest :score :jokers', :action => 'cmd_transfer', :auth_path => 'edit', :defaults => {:score => '-1', :jokers => '-1'}
 plugin.map 'quiz deleteplayer :nick', :action => 'cmd_del_player', :auth_path => 'edit'
 plugin.map 'quiz setscore :nick :score', :action => 'cmd_set_score', :auth_path => 'edit'
 plugin.map 'quiz transfer :source :dest :score :jokers', :action => 'cmd_transfer', :auth_path => 'edit', :defaults => {:score => '-1', :jokers => '-1'}
 plugin.map 'quiz deleteplayer :nick', :action => 'cmd_del_player', :auth_path => 'edit'
 plugin.map 'quiz setscore :nick :score', :action => 'cmd_set_score', :auth_path => 'edit'