Compare commits
5 Commits
f6839fe42a
...
0df41a7aae
Author | SHA1 | Date | |
---|---|---|---|
0df41a7aae | |||
8320d2dfcb | |||
5e18991d58 | |||
661d0f3f9a | |||
f72eeb3528 |
9
bin/ika_console
Executable file
9
bin/ika_console
Executable file
@ -0,0 +1,9 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'discordrb'
|
||||
require_relative '../lib/ika'
|
||||
require_relative '../lib/console_handler'
|
||||
|
||||
ika = Ika.for(type: ConsoleHandler)
|
||||
|
||||
ika.start!
|
13
bin/ika_discord
Executable file
13
bin/ika_discord
Executable file
@ -0,0 +1,13 @@
|
||||
#!/usr/bin/env ruby
|
||||
|
||||
require 'discordrb'
|
||||
require_relative '../lib/ika'
|
||||
require_relative '../lib/discord_handler'
|
||||
|
||||
token = File.read("./token")
|
||||
|
||||
bot = Discordrb::Bot.new(token:)
|
||||
|
||||
ika = Ika.for(type: DiscordHandler, bot:)
|
||||
|
||||
ika.start!
|
8
ika.rb
8
ika.rb
@ -1,8 +0,0 @@
|
||||
require 'discordrb'
|
||||
require_relative './lib/ika'
|
||||
|
||||
token = File.read("./token")
|
||||
|
||||
ika = Ika.new Discordrb::Bot.new(token:)
|
||||
|
||||
ika.start!
|
@ -1,4 +0,0 @@
|
||||
require_relative "commands/command"
|
||||
require_relative "commands/help_command"
|
||||
require_relative "commands/lesson_command"
|
||||
require_relative "commands/stop_lesson_command"
|
@ -1,46 +0,0 @@
|
||||
module Commands
|
||||
@@commands = []
|
||||
|
||||
def self.register_command(command_class)
|
||||
@@commands << command_class
|
||||
end
|
||||
|
||||
def self.registered_commands
|
||||
@@commands
|
||||
end
|
||||
|
||||
class Command
|
||||
def self.inherited(klass)
|
||||
Commands.register_command klass
|
||||
end
|
||||
|
||||
def self.for(ika, message)
|
||||
Commands.registered_commands.find do |command_class|
|
||||
command_class.matches? message.message.content
|
||||
end&.new(ika, message)
|
||||
end
|
||||
|
||||
def self.matches?(message_text)
|
||||
message_text == "!#{command_string}"
|
||||
end
|
||||
|
||||
def self.help
|
||||
"!#{command_string} - #{description}"
|
||||
end
|
||||
|
||||
def self.description
|
||||
"blah blah blah"
|
||||
end
|
||||
|
||||
attr_reader :ika, :message
|
||||
|
||||
def initialize(ika, message)
|
||||
@ika = ika
|
||||
@message = message
|
||||
end
|
||||
|
||||
def execute
|
||||
puts "This is a generic command! I can't execute anything!"
|
||||
end
|
||||
end
|
||||
end
|
@ -1,16 +0,0 @@
|
||||
module Commands
|
||||
class Help < Command
|
||||
def self.command_string
|
||||
"help"
|
||||
end
|
||||
|
||||
def self.description
|
||||
"Shows you a list of available commands"
|
||||
end
|
||||
|
||||
def execute
|
||||
message.respond ["I'm Ika! Here are the commands I know:",
|
||||
*Commands.registered_commands.map(&:help)].join("\n")
|
||||
end
|
||||
end
|
||||
end
|
@ -1,18 +0,0 @@
|
||||
module Commands
|
||||
class LessonCommand < Command
|
||||
def self.command_string
|
||||
"lesson"
|
||||
end
|
||||
|
||||
def self.description
|
||||
"Starts timers for a lesson"
|
||||
end
|
||||
|
||||
def execute
|
||||
return message.respond("There's already a lesson running!") if ika.current_lesson_running?
|
||||
|
||||
message.respond("Starting a lesson now!")
|
||||
ika.start_lesson! message
|
||||
end
|
||||
end
|
||||
end
|
@ -1,18 +0,0 @@
|
||||
module Commands
|
||||
class StopLessonCommand < Command
|
||||
def self.command_string
|
||||
"stoplesson"
|
||||
end
|
||||
|
||||
def self.description
|
||||
"Stops the current lesson, if one is running"
|
||||
end
|
||||
|
||||
def execute
|
||||
return message.respond("There's no lesson running") unless ika.current_lesson_running?
|
||||
|
||||
message.respond("Ending the current lesson!")
|
||||
ika.stop_current_lesson!
|
||||
end
|
||||
end
|
||||
end
|
19
lib/console_handler.rb
Normal file
19
lib/console_handler.rb
Normal file
@ -0,0 +1,19 @@
|
||||
require_relative './ika'
|
||||
|
||||
class ConsoleHandler < Ika
|
||||
def start!
|
||||
@handling_messages = true
|
||||
|
||||
while @handling_messages
|
||||
handle_message Message::Console.new(responder(nil), gets.chomp)
|
||||
end
|
||||
end
|
||||
|
||||
def stop!
|
||||
@handling_messages = false
|
||||
end
|
||||
|
||||
def responder(_)
|
||||
lambda { |response| puts "<Ika> #{response}" }
|
||||
end
|
||||
end
|
28
lib/discord_handler.rb
Normal file
28
lib/discord_handler.rb
Normal file
@ -0,0 +1,28 @@
|
||||
require_relative './ika'
|
||||
|
||||
class DiscordHandler < Ika
|
||||
attr_reader :bot
|
||||
|
||||
def initialize(bot:)
|
||||
super()
|
||||
@bot = bot
|
||||
end
|
||||
|
||||
def start!
|
||||
bot.remove_handler @message_handler if @message_handler
|
||||
|
||||
@message_handler = bot.message do |message_event|
|
||||
handle_message Message::Discord.new(responder(message_event.channel), message_event.message)
|
||||
end
|
||||
|
||||
bot.run # you can run this in the background, idk
|
||||
end
|
||||
|
||||
def stop!
|
||||
bot.remove_handler @message_handler if @message_handler
|
||||
end
|
||||
|
||||
def responder(channel)
|
||||
lambda { |response| channel.send_message(response) }
|
||||
end
|
||||
end
|
48
lib/drill.rb
Normal file
48
lib/drill.rb
Normal file
@ -0,0 +1,48 @@
|
||||
class Drill
|
||||
COMMANDS = %w[
|
||||
init
|
||||
start
|
||||
end
|
||||
]
|
||||
|
||||
attr_reader :channel
|
||||
|
||||
def initialize(channel)
|
||||
@channel = channel
|
||||
@exists = false
|
||||
@started = false
|
||||
end
|
||||
|
||||
def init!
|
||||
@exists = true
|
||||
end
|
||||
|
||||
def start!
|
||||
@started = true
|
||||
end
|
||||
|
||||
def end!
|
||||
@started = false
|
||||
end
|
||||
|
||||
def handle_message(message)
|
||||
message.command_message ? handle_command(message) : handle_normal_message(message)
|
||||
end
|
||||
|
||||
def handle_command(message)
|
||||
command = message.command
|
||||
if COMMANDS.includes?(command)
|
||||
send("handle_#{command}", message)
|
||||
else
|
||||
puts "[Drill] Unknown command #{command}"
|
||||
end
|
||||
end
|
||||
|
||||
def started?
|
||||
@started
|
||||
end
|
||||
|
||||
def exists?
|
||||
@exists
|
||||
end
|
||||
end
|
72
lib/drill_plugin.rb
Normal file
72
lib/drill_plugin.rb
Normal file
@ -0,0 +1,72 @@
|
||||
require_relative "drill"
|
||||
|
||||
class DrillPlugin
|
||||
COMMANDS = %w[
|
||||
help
|
||||
]
|
||||
|
||||
attr_reader :bot, :drills
|
||||
|
||||
def initialize(bot)
|
||||
@bot = bot
|
||||
@drills = Hash.new { Drill.new }
|
||||
end
|
||||
|
||||
def handle_message(message)
|
||||
puts "[#{self.class.name}] Handling \"#{message.content}\" from #{message.author.display_name}"
|
||||
command = strip_prefix(message.content).split.first
|
||||
p command
|
||||
if command.nil? || command == "" || command == "help" || command == "!drill"
|
||||
handle_help message
|
||||
elsif COMMANDS.include? command
|
||||
drill = drills[message.channel.id]
|
||||
send("handle_#{command}", message, drill)
|
||||
else
|
||||
drill = drills[message.channel.id]
|
||||
drill.handle_message(message)
|
||||
end
|
||||
end
|
||||
|
||||
def handle_init(message, drill)
|
||||
if drill.exists?
|
||||
message.respond "You've already got a drill running in this channel"
|
||||
else
|
||||
drills[message.channel.id] = Drill.new message.channel
|
||||
message.respond "New drill created!"
|
||||
end
|
||||
end
|
||||
|
||||
def handle_start(message, drill)
|
||||
if drill.started?
|
||||
message.respond "This drill has already started!"
|
||||
elsif drill.exists?
|
||||
drill.start!
|
||||
message.respond "Drill started!"
|
||||
else
|
||||
message.respond "You have to `!drill init` before you can start the drill"
|
||||
end
|
||||
end
|
||||
|
||||
def handle_end(message, drill)
|
||||
if !drill.exists?
|
||||
message.respond "There's no drill to end"
|
||||
elsif drill.started?
|
||||
drills.delete message.channel.id
|
||||
message.respond "Ending the drill now!"
|
||||
else
|
||||
message.respond "You have to `!drill start` before you can end the drill"
|
||||
end
|
||||
end
|
||||
|
||||
def handle_help(message)
|
||||
message.respond "Insert help message here"
|
||||
end
|
||||
|
||||
def strip_prefix(content)
|
||||
if content.start_with? "!drill"
|
||||
content.sub("!drill ", "")
|
||||
else
|
||||
content
|
||||
end
|
||||
end
|
||||
end
|
57
lib/ika.rb
57
lib/ika.rb
@ -1,51 +1,26 @@
|
||||
require_relative './session'
|
||||
require_relative './lesson'
|
||||
require_relative './commands'
|
||||
require_relative './message'
|
||||
require_relative './drill_plugin'
|
||||
require_relative './ika/plugin'
|
||||
|
||||
class Ika
|
||||
attr_reader :bot
|
||||
attr_reader :lessons, :sessions
|
||||
attr_accessor :current_lesson, :current_session
|
||||
attr_reader :plugins
|
||||
|
||||
def initialize(bot)
|
||||
@bot = bot
|
||||
@lessons = {}
|
||||
@sessions = {}
|
||||
PLUGIN_TYPES = [
|
||||
Plugin::HelpPlugin,
|
||||
Plugin::LessonPlugin
|
||||
]
|
||||
|
||||
def self.for(type:, **kwargs)
|
||||
type.new **kwargs
|
||||
end
|
||||
|
||||
def initialize
|
||||
@current_session = nil
|
||||
@current_lesson = Lesson.new
|
||||
end
|
||||
|
||||
def start!
|
||||
bot.remove_handler @message_handler if @message_handler
|
||||
|
||||
@message_handler = bot.message do |message|
|
||||
handle_message message
|
||||
end
|
||||
|
||||
bot.run # you can run this in the background, idk
|
||||
end
|
||||
|
||||
def stop!
|
||||
bot.remove_handler @message_handler if @message_handler
|
||||
end
|
||||
|
||||
def current_lesson_running?
|
||||
current_lesson.running?
|
||||
end
|
||||
|
||||
def start_lesson!(message)
|
||||
current_lesson.start!(message.channel)
|
||||
end
|
||||
|
||||
def stop_current_lesson!
|
||||
current_lesson.stop!
|
||||
@plugins = PLUGIN_TYPES.map { |plugin_type| plugin_type.new self }
|
||||
end
|
||||
|
||||
def handle_message(message)
|
||||
if command = Commands::Command.for(self, message)
|
||||
command.execute
|
||||
end
|
||||
|
||||
return current_session&.respond_to message
|
||||
plugins.find { |plugin| plugin.handle_message message }
|
||||
end
|
||||
end
|
||||
|
18
lib/ika/plugin.rb
Normal file
18
lib/ika/plugin.rb
Normal file
@ -0,0 +1,18 @@
|
||||
require_relative "./plugin/help_plugin"
|
||||
require_relative "./plugin/lesson_plugin"
|
||||
|
||||
class Plugin
|
||||
attr_reader :ika
|
||||
|
||||
def initialize(ika)
|
||||
@ika = ika
|
||||
end
|
||||
|
||||
def handle_message(message)
|
||||
can_handle_message? message
|
||||
end
|
||||
|
||||
def can_handle_message?(_)
|
||||
false
|
||||
end
|
||||
end
|
14
lib/ika/plugin/help_plugin.rb
Normal file
14
lib/ika/plugin/help_plugin.rb
Normal file
@ -0,0 +1,14 @@
|
||||
class Plugin
|
||||
class HelpPlugin < Plugin
|
||||
def handle_message(message)
|
||||
return false unless super
|
||||
|
||||
message.respond "I'm beyond help"
|
||||
end
|
||||
|
||||
def can_handle_message?(message)
|
||||
message.content == "!help"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
80
lib/ika/plugin/lesson_plugin.rb
Normal file
80
lib/ika/plugin/lesson_plugin.rb
Normal file
@ -0,0 +1,80 @@
|
||||
require_relative "../../lesson"
|
||||
|
||||
class Plugin
|
||||
class LessonPlugin < Plugin
|
||||
attr_reader :lessons
|
||||
|
||||
def initialize(ika)
|
||||
super
|
||||
@lessons = {}
|
||||
end
|
||||
|
||||
def handle_message(message)
|
||||
super
|
||||
Message.new(self, message).handle
|
||||
end
|
||||
|
||||
def can_handle_message?(message)
|
||||
return true if message.content == "!lesson"
|
||||
return true if message.content == "!stoplesson"
|
||||
|
||||
return false unless lessons[message.channel.id]
|
||||
|
||||
true
|
||||
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
|
||||
return handle_lesson if content == "!lesson"
|
||||
return handle_stoplesson if content == "!stoplesson"
|
||||
|
||||
lesson.handle_message(message)
|
||||
end
|
||||
|
||||
def lesson
|
||||
lsn = plugin.lessons[message.channel.id]
|
||||
return lsn if lsn
|
||||
|
||||
lsn = Lesson.new responder: responder
|
||||
plugin.lessons[message.channel.id] = lsn
|
||||
end
|
||||
|
||||
def handle_lesson
|
||||
if lesson.running?
|
||||
respond "There's already a lesson running!"
|
||||
else
|
||||
respond "Starting a lesson now!"
|
||||
lesson.start!
|
||||
end
|
||||
end
|
||||
|
||||
def handle_stoplesson
|
||||
if lesson.running?
|
||||
respond "Ending the current lesson"
|
||||
lesson.stop!
|
||||
else
|
||||
respond "There's no lesson running"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -8,30 +8,33 @@ DEFAULT_LESSON_PLAN = [
|
||||
]
|
||||
|
||||
class Lesson
|
||||
attr_reader :lesson_plan, :channel, :thread, :running, :scheduler
|
||||
attr_reader :lesson_plan, :channel, :running, :scheduler
|
||||
attr_accessor :responder
|
||||
|
||||
def initialize(lesson_plan=DEFAULT_LESSON_PLAN, scheduler: Rufus::Scheduler.new)
|
||||
def initialize(lesson_plan=DEFAULT_LESSON_PLAN, scheduler: Rufus::Scheduler.new, responder: nil)
|
||||
@lesson_plan = lesson_plan
|
||||
@running = false
|
||||
@scheduler = scheduler
|
||||
@responder = responder
|
||||
end
|
||||
|
||||
def stop!
|
||||
@scheduler.shutdown
|
||||
@running = false
|
||||
end
|
||||
|
||||
def start!(channel)
|
||||
def start!
|
||||
lesson_plan.each_with_index do |(time, message), index|
|
||||
offset = lesson_plan.map(&:first)[0..index - 1].sum(0)
|
||||
offset = 0 if index == 0
|
||||
scheduler.in "#{offset}m" do
|
||||
channel.send_message "#{time} minutes to #{message}"
|
||||
responder.call "#{time} minutes to #{message}"
|
||||
end
|
||||
end
|
||||
|
||||
end_offset = lesson_plan.map(&:first).sum(0)
|
||||
scheduler.in "#{end_offset}m" do
|
||||
channel.send_message "Lesson over!"
|
||||
responder.call "Lesson over!"
|
||||
@running = false
|
||||
end
|
||||
|
||||
@ -41,4 +44,8 @@ class Lesson
|
||||
def running?
|
||||
running
|
||||
end
|
||||
|
||||
def handle_message(_)
|
||||
# TODO: make it possible to advance the lesson or whatever
|
||||
end
|
||||
end
|
||||
|
109
lib/message.rb
Normal file
109
lib/message.rb
Normal file
@ -0,0 +1,109 @@
|
||||
class Message
|
||||
attr_reader :responder
|
||||
|
||||
def initialize(responder)
|
||||
@responder = responder
|
||||
end
|
||||
|
||||
def content
|
||||
# TODO: figure out how to delegate
|
||||
discord_message.content
|
||||
end
|
||||
|
||||
def author
|
||||
# TODO: figure out how to delegate
|
||||
discord_message.author
|
||||
end
|
||||
|
||||
def channel
|
||||
# TODO: figure out how to delegate
|
||||
discord_message.channel
|
||||
end
|
||||
|
||||
def respond(response)
|
||||
# TODO: figure out how to delegate
|
||||
discord_message.respond response
|
||||
end
|
||||
|
||||
def message
|
||||
discord_message
|
||||
end
|
||||
|
||||
def words
|
||||
content.split(" ")
|
||||
end
|
||||
|
||||
def first_word
|
||||
words.first
|
||||
end
|
||||
|
||||
def command_message?
|
||||
first_word.start_with? "!"
|
||||
end
|
||||
|
||||
def command
|
||||
command_message? ? first_word[1..] : nil
|
||||
end
|
||||
|
||||
def subcommand
|
||||
command_message? ? words[1] : nil
|
||||
end
|
||||
|
||||
def params
|
||||
command_message? ? words[2..] : []
|
||||
end
|
||||
|
||||
class Console < Message
|
||||
attr_reader :content
|
||||
|
||||
def initialize(responder, content)
|
||||
super(responder)
|
||||
@content = content
|
||||
end
|
||||
|
||||
def author
|
||||
OpenStruct.new(display_name: "CHANGE ME")
|
||||
end
|
||||
|
||||
def channel
|
||||
OpenStruct.new(id: nil)
|
||||
end
|
||||
|
||||
def respond(response)
|
||||
responder.call response
|
||||
end
|
||||
end
|
||||
|
||||
class Discord < Message
|
||||
attr_reader :discord_message
|
||||
|
||||
def initialize(responder, discord_message)
|
||||
super(responder)
|
||||
@discord_message = discord_message
|
||||
end
|
||||
|
||||
def content
|
||||
# TODO: figure out how to delegate
|
||||
discord_message.content
|
||||
end
|
||||
|
||||
def author
|
||||
# TODO: figure out how to delegate
|
||||
discord_message.author
|
||||
end
|
||||
|
||||
def channel
|
||||
# TODO: figure out how to delegate
|
||||
discord_message.channel
|
||||
end
|
||||
|
||||
def respond(response)
|
||||
# TODO: figure out how to delegate
|
||||
discord_message.respond response
|
||||
end
|
||||
|
||||
def message
|
||||
discord_message
|
||||
end
|
||||
end
|
||||
end
|
15
test/test_discord_handler.rb
Normal file
15
test/test_discord_handler.rb
Normal file
@ -0,0 +1,15 @@
|
||||
require_relative "../lib/discord_handler"
|
||||
require "minitest/autorun"
|
||||
|
||||
class TestDiscordHandler < Minitest::Test
|
||||
def setup
|
||||
@bot = Minitest::Mock.new
|
||||
@ika = DiscordHandler.new bot: @bot
|
||||
end
|
||||
|
||||
def test_start!
|
||||
@bot.expect(:message, nil)
|
||||
@bot.expect(:run, nil)
|
||||
@ika.start!
|
||||
end
|
||||
end
|
@ -1,16 +1,21 @@
|
||||
require_relative "../lib/ika"
|
||||
require "minitest/autorun"
|
||||
|
||||
class TestIka < Minitest::Test
|
||||
def setup
|
||||
@bot = Minitest::Mock.new
|
||||
@ika = Ika.new @bot
|
||||
class TestHandler < Ika
|
||||
attr_reader :responses
|
||||
|
||||
def initialize
|
||||
@responses = []
|
||||
end
|
||||
|
||||
def test_start
|
||||
@bot.expect(:message, nil)
|
||||
@bot.expect(:run, nil)
|
||||
@ika.start!
|
||||
def responder
|
||||
lambda { |response| @responses << response }
|
||||
end
|
||||
end
|
||||
|
||||
class TestIka < Minitest::Test
|
||||
def setup
|
||||
@ika = TestHandler.new
|
||||
end
|
||||
|
||||
def test_handle_command_message
|
||||
@ -19,6 +24,7 @@ class TestIka < Minitest::Test
|
||||
Commands::Command.stub(:for, @command) do |x|
|
||||
@ika.handle_message("msg")
|
||||
end
|
||||
assert_equal [], @ika.responses
|
||||
end
|
||||
|
||||
def test_handle_normal_message
|
||||
@ -30,5 +36,6 @@ class TestIka < Minitest::Test
|
||||
@ika.handle_message("msg")
|
||||
end
|
||||
end
|
||||
assert_equal [], @ika.responses
|
||||
end
|
||||
end
|
||||
|
@ -4,10 +4,12 @@ require "minitest/autorun"
|
||||
class TestLesson < Minitest::Test
|
||||
def setup
|
||||
@scheduler = Minitest::Mock.new
|
||||
@responses = []
|
||||
@responder = lambda { |rsp| @responses << rsp }
|
||||
end
|
||||
|
||||
def test_start
|
||||
@lesson = Lesson.new [[1, "test the thing"], [3, "finish testing"]], scheduler: @scheduler
|
||||
@lesson = Lesson.new [[1, "test the thing"], [3, "finish testing"]], scheduler: @scheduler, responder: @responder
|
||||
@scheduler.expect(:in, nil) { |time| time == "0m" }
|
||||
@scheduler.expect(:in, nil) { |time| time == "1m" }
|
||||
@scheduler.expect(:in, nil) { |time| time == "4m" }
|
||||
|
Loading…
Reference in New Issue
Block a user