require "rufus-scheduler" class Drill attr_reader :responder, :current_question, :scheduler, :questions attr_accessor :length, :participants, :timer_job_id def initialize(responder: nil, scheduler: Rufus::Scheduler.new) @responder = responder @participants = [] @length = "10m" @running = false @scheduler = scheduler @time_is_up = false @questions = [] end def respond(response) responder.call response end def can_handle_message?(message) true end ACTIONS = %w[join leave start stop] CUSTOMIZATIONS = %w[length] def handle_message(message) if ACTIONS.include? message.content return send("handle_#{message.content.downcase}", message) elsif customization = CUSTOMIZATIONS.find { |c| message.content.start_with?("#{c} ") } return send("handle_customize_#{customization}", message) end return unless running? return unless message.author == current_question_target handle_question_response(message) end def handle_join(message) return respond "The drill's already started!" if running? if participants.include? message.author respond "You've already joined" else self.participants << message.author respond "#{message.author.display_name} has joined" end end def handle_leave(message) return respond "The drill's already started!" if running? if participants.include? message.author self.participants -= [message.author] respond "#{message.author.display_name} has left" else respond "You aren't a participant" end end def handle_start(message) return respond "The drill's already started!" if running? return respond "Nobody has joined the drill! Type `join` to join it!" if participants.empty? @running = true @time_is_up = false self.timer_job_id = scheduler.in(length){ @time_is_up = true } respond "Drill started!" participants.shuffle! @questions = [] next_question! end def handle_stop(message) return respond "The drill's not running yet" unless running? scheduler.unschedule(timer_job_id) if timer_job_id end_drill! end def handle_customize_length(message) return respond "The drill's already started!" if running? cmd, desired_length = message.content.split return respond "Please specify a length" unless desired_length self.length = desired_length respond "Drill length set to #{desired_length}" end def handle_question_response(message) if current_question.answer message.content respond "Correct!" else respond "Incorrect: #{current_question.correct_answer}" end next_question! end def next_question! return end_drill! if time_is_up? self.participants = participants.rotate generate_question respond "#{current_question_target.display_name}: #{current_question.question_text}" end def end_drill! respond "Lesson's over!" questions.group_by(&:target).each do |target, target_questions| correct_count = target_questions.select(&:correct?).count total_count = target_questions.count percentage = "#{((correct_count.to_f / total_count) * 100).to_i}%" respond "#{target.display_name} got #{correct_count} out of #{total_count} correct (#{percentage})" end @running = false @participants = [] end def generate_question q = Question.new current_question_target questions << q @current_question = q end def current_question_target participants.first end def running? @running end def time_is_up? @time_is_up end class Question attr_reader :question_text, :correct_answer, :target attr_accessor :given_answer def initialize(target) # some kind of config here @target = target value = (1..100).to_a.sample @question_text = "Type the number #{value}" @correct_answer = value.to_s end def answer(response) @given_answer = response correct? end def correct? given_answer == correct_answer end end end