async-matrix

Gem Version CI License Ruby

Async-native Matrix Application Service SDK for Ruby. Built on the Socketry ecosystem (async, async-http, Falcon). No threads, no callbacks -- just fibers.

Usage

Please see the project documentation for more details.

Install

gem "async-matrix"

Quick Start

# config.ru
require "async/matrix"

config = Async::Matrix::ApplicationService::Config.load("config/appservice.yml")
client = Async::Matrix::Client.new(config)

bot = Async::Matrix::ApplicationService::Bot.new(client) do
  on "m.room.member" do |event|
    join_room(event.room_id) if event.content.membership == "invite"
  end

  on "m.room.message", msgtype: "m.text", not_from: :self do |event|
    send_notice event.room_id, "Echo: #{event.content.body}"
  end
end

server = Async::Matrix::ApplicationService::Server.new(hs_token: config.appservice.hs_token)
server.register(bot)

run server
falcon serve --bind http://0.0.0.0:9292

A complete working example with Docker Compose and Synapse lives in examples/echo_bot/.

Handlers

Any object that responds to #event_types and #call(event) is a handler. Use this when you need more control than the Bot DSL provides.

class Echo
  def initialize(client) = @client = client

  def event_types = ["m.room.message"]

  def call(event)
    return unless event.content&.msgtype == "m.text"
    return unless event.sender != @client.config.bot_mxid
    @client.send_notice(event.room_id, "Echo: #{event.content.body}")
  end
end

server.register(Echo.new(client))

Dispatch is fault-tolerant -- one handler raising won't take down the rest.

Client

client.send_text(room_id, "Hello world")
client.send_html(room_id, "<b>bold</b>")
client.send_notice(room_id, "Bot says hi")
client.join_room(room_id)
client.leave_room(room_id)
client.set_display_name("My Bot")
client.whoami

For anything beyond the convenience methods, client.api provides method-chained access to the full Matrix Client-Server API, validated at runtime against the official OpenAPI specs:

client.api.createRoom.post(name: "Pub")
client.api.rooms("!room:ex.com").messages.get(dir: "b", limit: 10)

All methods are fiber-safe with automatic connection pooling.

Configuration

Create config/appservice.yml for your bot:

homeserver:
  address: "http://synapse:8008"
  domain: "localhost"

appservice:
  as_token: "your-appservice-token"
  hs_token: "your-homeserver-token"
  bot:
    username: "bot"

You'll also need a registration.yml registered with your homeserver. See the echo bot example for a working template.

Override the config path at runtime:

APPSERVICE_CONFIG=/etc/bot/appservice.yml falcon serve

Built With

  • async -- fiber-based concurrency framework
  • async-http -- HTTP client/server with connection pooling
  • falcon -- async Rack-compatible web server
  • json_schemer -- JSON Schema validation
  • scampi -- inline co-located test framework
  • string_builder -- method-chain string builder

License

Apache 2.0