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)
This commit is contained in:
Bill Niblock 2021-02-14 21:06:46 -05:00
parent faa62604ef
commit 6fe1a7a251
3 changed files with 158 additions and 110 deletions

View file

@ -1,30 +1,42 @@
# frozen_string_literal: true # frozen_string_literal: true
module Chronicle module Chronicle
module Matrix module Addon
# Roll dice and get the results # Roll dice and get the results
# class Roller
# @param message [hash] The message data from Matrix def self.register(bot)
def handle_roll(client, message) addon_instance = new(bot)
msgstr = message.content[:body] addon_command = ['roll']
.gsub(/!roll\s*/, '')
.strip
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) room = @bot.client.ensure_room(message.room_id)
end
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 # Solve an arithmatic forumla from a string
# #
# @param string [String] The string representation of the formula # @param string [String] The string representation of the formula
# @return Integer of the solution # @return Integer of the solution
def self.solve(string) def solve(string)
formatted = string.gsub(/\s+/, '') formatted = string.gsub(/\s+/, '')
formatted = formatted.gsub(/\[[\d,]*\]/) do |a| formatted = formatted.gsub(/\[[\d,]*\]/) do |a|
a.scan(/\d*/).reduce(0) { |x, y| x + y.to_i } a.scan(/\d*/).reduce(0) { |x, y| x + y.to_i }
@ -77,7 +89,7 @@ module Chronicle
# @param string [String] The processed request # @param string [String] The processed request
# @param res [String] The result of the process # @param res [String] The result of the process
# @return String re-formatted # @return String re-formatted
def self.pretty(func, orig, string, res) def pretty(func, orig, string, res)
orig.gsub!(/[\+\-*\/]/) { |s| " #{s} " } orig.gsub!(/[\+\-*\/]/) { |s| " #{s} " }
string.gsub!(/[\+\-*\/]/) { |s| " #{s} " } string.gsub!(/[\+\-*\/]/) { |s| " #{s} " }
"#{func.capitalize}: #{orig} (#{string}) ==> #{res}" "#{func.capitalize}: #{orig} (#{string}) ==> #{res}"
@ -88,7 +100,7 @@ module Chronicle
# @param string [String] The string representation of the dice roll # @param string [String] The string representation of the dice roll
# example: 2d4+6 # example: 2d4+6
# @return Array of the message and the result # @return Array of the message and the result
def self.roll(string) def roll(string)
results = [] results = []
string.gsub(/\s+/,'').split(',').each do |roll| string.gsub(/\s+/,'').split(',').each do |roll|
orig = roll orig = roll

View file

@ -1,56 +1,82 @@
# frozen_string_literal: true # frozen_string_literal: true
module Chronicle module Chronicle
module Matrix module Addon
# Ping - Pong. Useful for testing # Ping - Pong. Useful for testing
# class Ping
# @param client [Client object] The current Matrix client connection def self.register(bot)
# @param message [Message object] The relevant message object addon_instance = new(bot)
def handle_ping(client, message) addon_command = ['ping']
room = client.ensure_room message.room_id
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 end
# 8-Ball: Give a random, vague response to a question # 8-Ball: Give a random, vague response to a question
# class Eightball
# @param client [Client object] The current Matrix client connection def self.register(bot)
# @param message [Message object] The relevant message object addon_instance = new(bot)
def handle_8ball(client, message) addon_command = ['8ball']
msgstr = message.content[:body]
.gsub(/!8ball\s*/, '')
.strip
room = client.ensure_room(message.room_id) [addon_instance, addon_command]
end
fates = [ def initialize(bot)
'Resoundingly, yes.', @bot = bot
'Chances are good.', end
'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] == '?' # Handle a command from the Matrix protocol
fates.sample #
else # @param message [Message object] The relevant message object
'You must ask a question. Try again' def matrix_command(message)
end 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 end
end end

View file

@ -4,13 +4,13 @@ require 'faraday'
require 'json' require 'json'
require 'matrix_sdk' require 'matrix_sdk'
# Require any addons
Dir[File.join(__dir__, 'addons', '*.rb')].each do |file|
require file
end
# Chronicle Bot # Chronicle Bot
module Chronicle module Chronicle
# Require any addons
Dir[File.join(__dir__, 'addons', '*.rb')].each do |file|
require file
end
# A filter to simplify syncs # A filter to simplify syncs
BOT_FILTER = { BOT_FILTER = {
presence: { types: [] }, presence: { types: [] },
@ -30,23 +30,6 @@ module Chronicle
# Chronicle Bot for Matrix # Chronicle Bot for Matrix
module 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 # Begin the beast
def self.start(args) def self.start(args)
unless args.length >= 2 unless args.length >= 2
@ -65,11 +48,68 @@ module Chronicle
# The bot # The bot
class ChronicleBot class ChronicleBot
include Matrix attr_reader :all_commands, :cmd_prefix
def initialize(hs_url, access_token) def initialize(hs_url, access_token)
@hs_url = hs_url @hs_url = hs_url
@token = access_token @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 end
# Run Chronicle # Run Chronicle
@ -95,36 +135,6 @@ module Chronicle
end end
end 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 end
end end