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)
140 lines
3.3 KiB
Ruby
Executable file
140 lines
3.3 KiB
Ruby
Executable file
# frozen_string_literal: true
|
|
|
|
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
|
|
# A filter to simplify syncs
|
|
BOT_FILTER = {
|
|
presence: { types: [] },
|
|
account_data: { types: [] },
|
|
room: {
|
|
ephemeral: { types: [] },
|
|
state: {
|
|
types: ['m.room.*'],
|
|
lazy_load_members: true
|
|
},
|
|
timeline: {
|
|
types: ['m.room.message']
|
|
},
|
|
account_data: { types: [] }
|
|
}
|
|
}.freeze
|
|
|
|
# Chronicle Bot for Matrix
|
|
module Matrix
|
|
# Begin the beast
|
|
def self.start(args)
|
|
unless args.length >= 2
|
|
raise "Usage: #{$PROGRAM_NAME} [-d] homeserver_url access_token"
|
|
end
|
|
|
|
if args.first == '-d'
|
|
Thread.abort_on_exception = true
|
|
MatrixSdk.debug!
|
|
args.shift
|
|
end
|
|
|
|
bot = ChronicleBot.new args[0], args[1]
|
|
bot.run
|
|
end
|
|
|
|
# The bot
|
|
class ChronicleBot
|
|
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
|
|
def run
|
|
# Join all invited rooms
|
|
client.on_invite_event.add_handler { |ev| client.join_room(ev[:room_id]) }
|
|
|
|
# Run an empty sync to get to a `since` token without old data.
|
|
# Storing the `since` token is also valid for bot use-cases, but in the
|
|
# case of ping responses there's never any need to reply to old data.
|
|
empty_sync = deep_copy(BOT_FILTER)
|
|
empty_sync[:room].map { |_k, v| v[:types] = [] }
|
|
client.sync filter: empty_sync
|
|
|
|
# Read all message events
|
|
client.on_event.add_handler('m.room.message') { |ev| on_message(ev) }
|
|
|
|
loop do
|
|
begin
|
|
client.sync filter: BOT_FILTER
|
|
rescue MatrixSdk::MatrixError => e
|
|
puts e
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|