chronicle_bot: An ambitious chat bot for Matrix

This commit is contained in:
Bill Niblock 2021-02-06 22:23:26 -05:00
parent 24bd7553fa
commit 1a422b65d0

130
lib/chronicle_bot.rb Executable file
View file

@ -0,0 +1,130 @@
# frozen_string_literal: true
require 'faraday'
require 'json'
require 'matrix_sdk'
# 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: [] },
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
@@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
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
include Matrix
def initialize(hs_url, access_token)
@hs_url = hs_url
@token = access_token
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
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