Class: Async::Matrix::Client

Inherits:
Object
  • Object
show all
Defined in:
lib/async/matrix/client.rb

Overview

Async HTTP client for the Matrix Client-Server API.

Every outbound request is authenticated with the appservice ‘as_token`. All methods are fiber-safe and run naturally inside Falcon’s async reactor.

client = Async::Matrix::Client.new(config)
client.send_text("!room:example.com", "Hello world")
client.join_room("!room:example.com")

Constant Summary collapse

CLIENT_PREFIX =
"/_matrix/client/v3"
DEFAULT_MAX_RETRIES =

Retry defaults

3
DEFAULT_RETRY_BASE =

max retry attempts (0 disables)

0.5
DEFAULT_MAX_RETRY_DELAY =

initial backoff in seconds

30
RATE_LIMIT_STATUS =

Status codes eligible for retry

429
GATEWAY_ERROR_STATUSES =
[502, 503, 504].freeze
DEFAULT_RESPONSE_SIZE_LIMIT =

Response size limits (bytes)

50 * 1024 * 1024
DEFAULT_ERROR_RESPONSE_SIZE_LIMIT =

50 MiB for JSON API responses

512 * 1024

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(config, max_retries: DEFAULT_MAX_RETRIES, retry_base_delay: DEFAULT_RETRY_BASE, max_retry_delay: DEFAULT_MAX_RETRY_DELAY, ignore_rate_limit: false, response_size_limit: DEFAULT_RESPONSE_SIZE_LIMIT, error_response_size_limit: DEFAULT_ERROR_RESPONSE_SIZE_LIMIT) ⇒ Client

Returns a new instance of Client.



44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
# File 'lib/async/matrix/client.rb', line 44

def initialize(config, max_retries: DEFAULT_MAX_RETRIES,
               retry_base_delay: DEFAULT_RETRY_BASE,
               max_retry_delay: DEFAULT_MAX_RETRY_DELAY,
               ignore_rate_limit: false,
               response_size_limit: DEFAULT_RESPONSE_SIZE_LIMIT,
               error_response_size_limit: DEFAULT_ERROR_RESPONSE_SIZE_LIMIT)
  @config                   = config
  @base                     = config.homeserver.address
  @max_retries              = max_retries
  @retry_base_delay         = retry_base_delay
  @max_retry_delay          = max_retry_delay
  @ignore_rate_limit        = ignore_rate_limit
  @response_size_limit      = response_size_limit
  @error_response_size_limit = error_response_size_limit
  @headers = [
    ["authorization", "Bearer #{config.appservice.as_token}"],
    ["content-type",  "application/json"],
    ["user-agent",    "AsyncMatrix/#{Async::Matrix::VERSION}"]
  ]
end

Instance Attribute Details

#configObject (readonly)

512 KiB for error bodies



42
43
44
# File 'lib/async/matrix/client.rb', line 42

def config
  @config
end

Instance Method Details

#apiObject

Returns a Gateway that provides method-chained access to every Matrix Client-Server API endpoint. Chains are validated against the official OpenAPI path tree and terminated by .get(), .post(), .put(), or .delete().

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


125
126
127
# File 'lib/async/matrix/client.rb', line 125

def api
  Api::Gateway.new(self)
end

#closeObject



167
168
169
170
171
172
# File 'lib/async/matrix/client.rb', line 167

def close
  @internet&.close
  @internet = nil
  @media_client&.close
  @media_client = nil
end

#get(path, max_retries: nil) ⇒ Object



155
156
157
# File 'lib/async/matrix/client.rb', line 155

def get(path, max_retries: nil)
  request("GET", path, nil, max_retries: max_retries)
end

#join_room(room_id) ⇒ Object

── Room actions ───────────────────────────────────────────



89
90
91
# File 'lib/async/matrix/client.rb', line 89

def join_room(room_id)
  post("#{CLIENT_PREFIX}/join/#{encode(room_id)}")
end

#leave_room(room_id) ⇒ Object



93
94
95
# File 'lib/async/matrix/client.rb', line 93

def leave_room(room_id)
  post("#{CLIENT_PREFIX}/rooms/#{encode(room_id)}/leave")
end

#mediaObject

Returns a Gateway rooted at /_matrix/media/v3 for media operations. Binary routes (upload/download/thumbnail) are automatically detected by the Chain and dispatched to the MediaClient.

client.media.upload.post(bytes, content_type: "image/png")
client.media.download("example.com", "abc123").get
client.media.thumbnail("example.com", "abc123").get(width: 64, height: 64)


137
138
139
# File 'lib/async/matrix/client.rb', line 137

def media
  Api::Gateway.new(self, prefix: %w[_matrix media v3])
end

#media_clientObject

Returns the binary media client used for upload/download operations. Lazily initialized, shares the same config as this client.



143
144
145
# File 'lib/async/matrix/client.rb', line 143

def media_client
  @media_client ||= MediaClient.new(@config)
end

#post(path, body = {}, max_retries: nil) ⇒ Object



163
164
165
# File 'lib/async/matrix/client.rb', line 163

def post(path, body = {}, max_retries: nil)
  request("POST", path, body, max_retries: max_retries)
end

#put(path, body = {}, max_retries: nil) ⇒ Object



159
160
161
# File 'lib/async/matrix/client.rb', line 159

def put(path, body = {}, max_retries: nil)
  request("PUT", path, body, max_retries: max_retries)
end

#send_html(room_id, html, plaintext = nil) ⇒ Object



72
73
74
75
76
77
78
79
80
# File 'lib/async/matrix/client.rb', line 72

def send_html(room_id, html, plaintext = nil)
  content = {
    msgtype:        "m.text",
    body:           plaintext || html.gsub(/<[^>]+>/, ""),
    format:         "org.matrix.custom.html",
    formatted_body: html
  }
  send_message_event(room_id, "m.room.message", content)
end

#send_message_event(room_id, event_type, content) ⇒ Object

── Low-level HTTP ─────────────────────────────────────────



149
150
151
152
153
# File 'lib/async/matrix/client.rb', line 149

def send_message_event(room_id, event_type, content)
  txn_id = SecureRandom.uuid
  path = "#{CLIENT_PREFIX}/rooms/#{encode(room_id)}/send/#{encode(event_type)}/#{txn_id}"
  put(path, content)
end

#send_notice(room_id, text) ⇒ Object



82
83
84
85
# File 'lib/async/matrix/client.rb', line 82

def send_notice(room_id, text)
  content = {msgtype: "m.notice", body: text}
  send_message_event(room_id, "m.room.message", content)
end

#send_text(room_id, text) ⇒ Object

── Messaging ──────────────────────────────────────────────



67
68
69
70
# File 'lib/async/matrix/client.rb', line 67

def send_text(room_id, text)
  content = {msgtype: "m.text", body: text}
  send_message_event(room_id, "m.room.message", content)
end

#set_display_name(name, user_id = nil) ⇒ Object

── Profile ────────────────────────────────────────────────



99
100
101
102
103
104
105
# File 'lib/async/matrix/client.rb', line 99

def set_display_name(name, user_id = nil)
  uid = user_id || @config.bot_mxid
  put(
    "#{CLIENT_PREFIX}/profile/#{encode(uid)}/displayname",
    {displayname: name}
  )
end

#whoamiObject

── Verification ───────────────────────────────────────────



109
110
111
# File 'lib/async/matrix/client.rb', line 109

def whoami
  get("#{CLIENT_PREFIX}/account/whoami")
end