From 597084178478ba1b5734e8645f16daa191e19ff9 Mon Sep 17 00:00:00 2001 From: Bill Rossi Date: Thu, 28 Aug 2025 21:02:26 -0400 Subject: [PATCH] Drill baby drill --- lib/drill.rb | 137 +++++++++++++++++++++++++------- lib/ika.rb | 4 +- lib/ika/plugin.rb | 1 + lib/ika/plugin/drill_plugin.rb | 57 +++++++++++++ lib/ika/plugin/lesson_plugin.rb | 3 +- 5 files changed, 171 insertions(+), 31 deletions(-) create mode 100644 lib/ika/plugin/drill_plugin.rb diff --git a/lib/drill.rb b/lib/drill.rb index 70a0668..0d4da0d 100644 --- a/lib/drill.rb +++ b/lib/drill.rb @@ -1,48 +1,129 @@ +require "rufus-scheduler" + class Drill - COMMANDS = %w[ - init - start - end - ] + attr_reader :responder, :current_question, :scheduler + attr_accessor :length, :participants, :timer_job_id - attr_reader :channel - - def initialize(channel) - @channel = channel - @exists = false - @started = false + def initialize(responder: nil, scheduler: Rufus::Scheduler.new) + @responder = responder + @participants = [] + @length = "10m" + @running = false + @scheduler = scheduler + @time_is_up = false end - def init! - @exists = true + def respond(response) + responder.call response end - def start! - @started = true + def can_handle_message?(message) + true end - def end! - @started = false - end + ACTIONS = %w[join leave start stop] + CUSTOMIZATIONS = %w[length] def handle_message(message) - message.command_message ? handle_command(message) : handle_normal_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_command(message) - command = message.command - if COMMANDS.includes?(command) - send("handle_#{command}", message) + def handle_join(message) + if participants.include? message.author + respond "You've already joined" else - puts "[Drill] Unknown command #{command}" + self.participants << message.author + respond "#{message.author.display_name} has joined" end end - def started? - @started + def handle_leave(message) + 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 exists? - @exists + def handle_start(message) + return respond "The drill's already started!" if running? + + @running = true + @time_is_up = false + self.timer_job_id = scheduler.in(length){ @time_is_up = true } + respond "Drill started!" + participants.shuffle! + next_question! + end + + def handle_stop(message) + return respond "The drill's not running yet" unless running? + + @running = false + scheduler.unschedule(timer_job_id) if timer_job_id + respond "Drill stopped!" + 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.correct_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 "Time's up! Everyone did great!" + @running = false + end + + def generate_question + value = (1..100).to_a.sample + @current_question = OpenStruct.new( + question_text: "Type the number #{value}", + correct_answer: value.to_s + ) + end + + def current_question_target + participants.first + end + + def running? + @running + end + + def time_is_up? + @time_is_up end end diff --git a/lib/ika.rb b/lib/ika.rb index 3371282..93dbbce 100644 --- a/lib/ika.rb +++ b/lib/ika.rb @@ -1,6 +1,5 @@ require_relative './session' require_relative './message' -require_relative './drill_plugin' require_relative './ika/plugin' class Ika @@ -8,7 +7,8 @@ class Ika PLUGIN_TYPES = [ Plugin::HelpPlugin, - Plugin::LessonPlugin + Plugin::LessonPlugin, + Plugin::DrillPlugin ] def self.for(type:, **kwargs) diff --git a/lib/ika/plugin.rb b/lib/ika/plugin.rb index c3f2cb6..f785e6e 100644 --- a/lib/ika/plugin.rb +++ b/lib/ika/plugin.rb @@ -1,3 +1,4 @@ +require_relative "./plugin/drill_plugin" require_relative "./plugin/help_plugin" require_relative "./plugin/lesson_plugin" diff --git a/lib/ika/plugin/drill_plugin.rb b/lib/ika/plugin/drill_plugin.rb new file mode 100644 index 0000000..af15d71 --- /dev/null +++ b/lib/ika/plugin/drill_plugin.rb @@ -0,0 +1,57 @@ +require_relative "../../drill" + +class Plugin + class DrillPlugin < Plugin + attr_reader :drills + + def initialize(ika) + super + @drills = {} + end + + def handle_message(message) + return false unless super + + Message.new(self, message).handle + end + + def can_handle_message?(message) + return true if message.content == "!drill" + + drills[message.channel.id]&.can_handle_message?(message) + end + + class Message + attr_reader :plugin, :message + + def initialize(plugin, message) + @plugin = plugin + @message = message + end + + def content + message.content + end + + def responder + message.responder + end + + def respond(response) + responder.call response + end + + def handle + drill.handle_message(message) + end + + def drill + drl = plugin.drills[message.channel.id] + return drl if drl + + drl = Drill.new responder: responder + plugin.drills[message.channel.id] = drl + end + end + end +end diff --git a/lib/ika/plugin/lesson_plugin.rb b/lib/ika/plugin/lesson_plugin.rb index 6a5619d..6b8fcfe 100644 --- a/lib/ika/plugin/lesson_plugin.rb +++ b/lib/ika/plugin/lesson_plugin.rb @@ -10,7 +10,8 @@ class Plugin end def handle_message(message) - super + return false unless super + Message.new(self, message).handle end