From 6fe1a7a251b568eeb878566417a4813d3170ad93 Mon Sep 17 00:00:00 2001 From: Bill Niblock Date: Sun, 14 Feb 2021 21:06:46 -0500 Subject: [PATCH] Update how Addons are used After some more reading, update the logic for handling the addons. Leverage an "addons module" (Addon), and require that each addon be a class within that module. Within that class, optionally include a `self.register` method, which will be called when the bot is initialized. This method takes one parameter: the bot instance.Registering the addon adds the bot's instance to an instance of the addon, and vice versa, allowing the instances to communicate. Additionally, registering adds the addons commands. Each registered addon must also include a `matrix_command` method, which takes the message String from Matrix. The intention here is to include additional `*_command` methods for different protocols, without having to change much else. Adding additional protocols will determine how well that works. chronicle_bot.rb: Redo addon logic (details above) addons/utils.rb: Update for new addon logic (details above) addons/roller.rb: Update for new addon logic (details above) --- lib/addons/roller.rb | 44 ++++++++++------ lib/addons/utils.rb | 108 +++++++++++++++++++++++++--------------- lib/chronicle_bot.rb | 116 +++++++++++++++++++++++-------------------- 3 files changed, 158 insertions(+), 110 deletions(-) diff --git a/lib/addons/roller.rb b/lib/addons/roller.rb index 5a9bce7..1c522e9 100644 --- a/lib/addons/roller.rb +++ b/lib/addons/roller.rb @@ -1,30 +1,42 @@ # frozen_string_literal: true module Chronicle - module Matrix + module Addon # Roll dice and get the results - # - # @param message [hash] The message data from Matrix - def handle_roll(client, message) - msgstr = message.content[:body] - .gsub(/!roll\s*/, '') - .strip + class Roller + def self.register(bot) + addon_instance = new(bot) + addon_command = ['roll'] - room = client.ensure_room(message.room_id) + [addon_instance, addon_command] + end - res = Roller.roll(msgstr) + def initialize(bot) + @bot = bot + end - final_msg = res.reduce("") { |x,y| x+y+"\n" } + # Handle a command from the Matrix protocol + # + # @param message [hash] The message data from Matrix + def matrix_command(message) + msgstr = message.content[:body] + .gsub(/!roll\s*/, '') + .strip - room.send_notice(final_msg) - end + room = @bot.client.ensure_room(message.room_id) + + res = roll(msgstr) + + final_msg = res.reduce('') { |x, y| x + y + "\n" } + + room.send_notice(final_msg) + end - module Roller # Solve an arithmatic forumla from a string # # @param string [String] The string representation of the formula # @return Integer of the solution - def self.solve(string) + def solve(string) formatted = string.gsub(/\s+/, '') formatted = formatted.gsub(/\[[\d,]*\]/) do |a| a.scan(/\d*/).reduce(0) { |x, y| x + y.to_i } @@ -77,7 +89,7 @@ module Chronicle # @param string [String] The processed request # @param res [String] The result of the process # @return String re-formatted - def self.pretty(func, orig, string, res) + def pretty(func, orig, string, res) orig.gsub!(/[\+\-*\/]/) { |s| " #{s} " } string.gsub!(/[\+\-*\/]/) { |s| " #{s} " } "#{func.capitalize}: #{orig} (#{string}) ==> #{res}" @@ -88,7 +100,7 @@ module Chronicle # @param string [String] The string representation of the dice roll # example: 2d4+6 # @return Array of the message and the result - def self.roll(string) + def roll(string) results = [] string.gsub(/\s+/,'').split(',').each do |roll| orig = roll diff --git a/lib/addons/utils.rb b/lib/addons/utils.rb index e788d41..38bd545 100644 --- a/lib/addons/utils.rb +++ b/lib/addons/utils.rb @@ -1,56 +1,82 @@ # frozen_string_literal: true module Chronicle - module Matrix + module Addon # Ping - Pong. Useful for testing - # - # @param client [Client object] The current Matrix client connection - # @param message [Message object] The relevant message object - def handle_ping(client, message) - room = client.ensure_room message.room_id + class Ping + def self.register(bot) + addon_instance = new(bot) + addon_command = ['ping'] - room.send_notice('Pong!') + [addon_instance, addon_command] + end + + def initialize(bot) + @bot = bot + end + + # Handle a command from the Matrix protocol + # + # @param message [Message object] The relevant message object + def matrix_command(message) + room = @bot.client.ensure_room(message.room_id) + + room.send_notice('Pong!') + end end # 8-Ball: Give a random, vague response to a question - # - # @param client [Client object] The current Matrix client connection - # @param message [Message object] The relevant message object - def handle_8ball(client, message) - msgstr = message.content[:body] - .gsub(/!8ball\s*/, '') - .strip + class Eightball + def self.register(bot) + addon_instance = new(bot) + addon_command = ['8ball'] - room = client.ensure_room(message.room_id) + [addon_instance, addon_command] + end - fates = [ - 'Resoundingly, yes.', - 'Chances are good.', - 'Signs point to yes.', - 'Wheel.', - 'It is worth the attempt.', - 'Uncertainty clouds my sight.', - 'Wheel and woe.', - 'Neither wheel nor woe.', - 'Concentrate, and ask again.', - 'I cannot say for sure.', - 'The fates do not know.', - "Why are you asking me? I'm just a bot.", - 'Error: Fate API returned 404. Try again later.', - 'Woe.', - 'Chances are poor.', - 'Signs point to no.', - 'Very doubtful.', - 'Absolutely no.' - ] + def initialize(bot) + @bot = bot + end - res = if msgstr[-1] == '?' - fates.sample - else - 'You must ask a question. Try again' - end + # Handle a command from the Matrix protocol + # + # @param message [Message object] The relevant message object + def matrix_command(message) + msgstr = message.content[:body] + .gsub(/!8ball\s*/, '') + .strip - room.send_notice(res) + room = @bot.client.ensure_room(message.room_id) + + fates = [ + 'Resoundingly, yes.', + 'Chances are good.', + 'Signs point to yes.', + 'Wheel.', + 'It is worth the attempt.', + 'Uncertainty clouds my sight.', + 'Wheel and woe.', + 'Neither wheel nor woe.', + 'Concentrate, and ask again.', + 'I cannot say for sure.', + 'The fates do not know.', + "Why are you asking me? I'm just a bot.", + 'Error: Fate API returned 404. Try again later.', + 'Woe.', + 'Chances are poor.', + 'Signs point to no.', + 'Very doubtful.', + 'Absolutely no.' + ] + + res = if msgstr[-1] == '?' + fates.sample + else + 'You must ask a question. Try again' + end + + room.send_notice(res) + end end end end diff --git a/lib/chronicle_bot.rb b/lib/chronicle_bot.rb index a261bf8..db76a22 100755 --- a/lib/chronicle_bot.rb +++ b/lib/chronicle_bot.rb @@ -4,13 +4,13 @@ require 'faraday' require 'json' require 'matrix_sdk' +# Require any addons +Dir[File.join(__dir__, 'addons', '*.rb')].each do |file| + require file +end + # Chronicle Bot module Chronicle - # Require any addons - Dir[File.join(__dir__, 'addons', '*.rb')].each do |file| - require file - end - # A filter to simplify syncs BOT_FILTER = { presence: { types: [] }, @@ -30,23 +30,6 @@ module Chronicle # Chronicle Bot for Matrix module Matrix - @@default_allowed_commands = %w[ping] - @@room_allowed_commands = {} - - # Update allowed commands - def add_allowed_commands(cmds, msgid='') - if msgid - @@room_allowed_commands[msgid].concat(cmds) - else - @@room_allowed_commands.each { |_,v| v.concat(cmds) } - end - end - - # Establish or return allowed commands for a room - def allowed_commands(msgid) - @@room_allowed_commands[msgid] ||= @@default_allowed_commands - end - # Begin the beast def self.start(args) unless args.length >= 2 @@ -65,11 +48,68 @@ module Chronicle # The bot class ChronicleBot - include Matrix + attr_reader :all_commands, :cmd_prefix def initialize(hs_url, access_token) @hs_url = hs_url @token = access_token + + @cmd_prefix = '!' + @all_commands = {} + @allowed_commands = {} + + register_commands + end + + # All available commands + def available_commands(addon, commands) + commands.each do |command| + @all_commands[command] = addon + end + end + + def client + @client ||= MatrixSdk::Client.new( + @hs_url, + access_token: @token, + client_cache: :all + ) + end + + def deep_copy(hash) + Marshal.load(Marshal.dump(hash)) + end + + def disable_commands(*commands) + commands.each do |command| + @all_commands.delete(command) + end + end + + def on_message(message) + return unless message.content[:msgtype] == 'm.text' + + msgstr = message.content[:body] + roomid = message.room_id + cmds = @all_commands.keys.join('|') + + return unless msgstr =~ /^#{@cmd_prefix}#{cmds}\s*/ + + msgstr.match(/^#{@cmd_prefix}(#{cmds})\s*/) do |m| + @all_commands[m.to_s[1..-1].strip].matrix_command(message) + end + end + + def register_commands + Chronicle::Addon.constants.each do |addon| + cmd = Object.const_get("Chronicle::Addon::#{addon.to_s}") + + if cmd.methods.include?(:register) + instance, commands = cmd.send(:register, self) + + available_commands(instance, commands) + end + end end # Run Chronicle @@ -95,36 +135,6 @@ module Chronicle end end end - - def on_message(message) - return unless message.content[:msgtype] == 'm.text' - - msgstr = message.content[:body] - msgid = message.room_id - cmds = allowed_commands(msgid).join('|') - - return unless msgstr =~ /^!#{cmds}\s*/ - - msgstr.match(/^!(#{cmds})\s*/) do |m| - send( - "handle_#{m.to_s[1..-1].strip}", - client, - message - ) - end - end - - def client - @client ||= MatrixSdk::Client.new( - @hs_url, - access_token: @token, - client_cache: :none - ) - end - - def deep_copy(hash) - Marshal.load(Marshal.dump(hash)) - end end end end