Class: MatrixSdk::Bot::Base

Inherits:
Object show all
Extended by:
Extensions
Defined in:
lib/matrix_sdk/bot/base.rb

Direct Known Subclasses

Instance

Defined Under Namespace

Classes: RequestHandler

Constant Summary collapse

CALLERS_TO_IGNORE =
[
  /\/matrix_sdk\/.+\.rb$/,                            # all MatrixSdk code
  /^\(.*\)$/,                                         # generated code
  /rubygems\/(custom|core_ext\/kernel)_require\.rb$/, # rubygems require hacks
  /bundler(\/(?:runtime|inline))?\.rb/,               # bundler require hacks
  /<internal:/                                        # internal in ruby >= 1.9.2
].freeze
EMPTY_BOT_FILTER =

A filter that should only result in a valid sync token and no other data

{
  account_data: { types: [] },
  event_fields: [],
  presence: { types: [] },
  room: {
    account_data: { types: [] },
    ephemeral: { types: [] },
    state: {
      types: [],
      lazy_load_members: true
    },
    timeline: {
      types: []
    }
  }
}.freeze

Class Attribute Summary collapse

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Extensions

events, ignore_inspect

Constructor Details

#initialize(hs_url, **params) ⇒ Base

Returns a new instance of Base.



30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# File 'lib/matrix_sdk/bot/base.rb', line 30

def initialize(hs_url, **params)
  @client = case hs_url
            when MatrixSdk::Api
              MatrixSdk::Client.new hs_url
            when MatrixSdk::Client
              hs_url
            when %r{^https?://.*}
              MatrixSdk::Client.new hs_url, **params
            else
              MatrixSdk::Client.new_for_domain hs_url, **params
            end

  @client.on_event.add_handler { |ev| _handle_event(ev) }
  @client.on_invite_event.add_handler do |ev|
    break unless settings.accept_invites?

    logger.info "Received invite to #{ev[:room_id]}, joining."
    client.join_room(ev[:room_id])
  end

  @event = nil

  logger.warn 'The bot abstraction is not fully finalized and can be expected to change.'
end

Class Attribute Details

.handlersObject (readonly)

Returns the value of attribute handlers.



149
150
151
# File 'lib/matrix_sdk/bot/base.rb', line 149

def handlers
  @handlers
end

Instance Attribute Details

#clientObject (readonly)

Returns the value of attribute client.



25
26
27
# File 'lib/matrix_sdk/bot/base.rb', line 25

def client
  @client
end

#eventObject (readonly)

Returns the value of attribute event.



25
26
27
# File 'lib/matrix_sdk/bot/base.rb', line 25

def event
  @event
end

#loggerObject



55
56
57
58
59
# File 'lib/matrix_sdk/bot/base.rb', line 55

def logger
  return @logger if instance_variable_defined?(:@logger) && @logger

  self.class.logger
end

Class Method Details

.all_handlers(type: :command) ⇒ Array[RequestHandler]

Retrieves all registered - including inherited - handlers for the bot

Parameters:

  • type (:command, :event, :all) (defaults to: :command)

    Which handler type to return, or :all to return all handlers regardless of type

Returns:

  • (Array[RequestHandler])

    The registered handlers for the bot and parents



187
188
189
190
# File 'lib/matrix_sdk/bot/base.rb', line 187

def all_handlers(type: :command)
  parent = superclass&.all_handlers(type: type) if superclass.respond_to? :all_handlers
  (parent || {}).merge(@handlers.select { |_, h| type == :all || h.type == type }).compact
end

.client(&block) ⇒ Object

Registers a block to be run when configuring the client, before starting the sync



307
308
309
# File 'lib/matrix_sdk/bot/base.rb', line 307

def client(&block)
  @client_handler = block
end

.command(command, desc: nil, notes: nil, only: nil, **params, &block) ⇒ Object



263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
# File 'lib/matrix_sdk/bot/base.rb', line 263

def command(command, desc: nil, notes: nil, only: nil, **params, &block)
  args = params[:args] || convert_to_lambda(&block).parameters.map do |type, name|
    case type
    when :req
      name.to_s.upcase
    when :opt
      "[#{name.to_s.upcase}]"
    when :rest
      "[#{name.to_s.upcase}...]"
    end
  end.compact.join(' ')

  logger.debug "Registering command #{command} with args #{args}"

  add_handler(
    command.to_s.downcase,
    type: :command,
    args: args,
    desc: desc,
    notes: notes,
    only: [only].flatten.compact,
    &block
  )
end

.command?(command, ignore_inherited: false) ⇒ Boolean

Check if a command is registered

Parameters:

  • command (String)

    The command to check

  • ignore_inherited (Booleen) (defaults to: false)

    Should the check ignore any inherited commands and only check local registrations

Returns:

  • (Boolean)


315
316
317
318
319
# File 'lib/matrix_sdk/bot/base.rb', line 315

def command?(command, ignore_inherited: false)
  return @handlers[command.to_s.downcase]&.command? if ignore_inherited

  all_handlers[command.to_s.downcase]&.command? || false
end

.disable(*opts) ⇒ Object

Same as calling ‘set :option, false` for each of the given options.

Parameters:

  • opts (Array[Symbol])

    The options to set to false



246
247
248
# File 'lib/matrix_sdk/bot/base.rb', line 246

def disable(*opts)
  opts.each { |key| set(key, false) }
end

.enable(*opts) ⇒ Object

Same as calling ‘set :option, true` for each of the given options.

Parameters:

  • opts (Array[Symbol])

    The options to set to true



239
240
241
# File 'lib/matrix_sdk/bot/base.rb', line 239

def enable(*opts)
  opts.each { |key| set(key, true) }
end

.event(event, only: nil, **_params, &block) ⇒ Object



295
296
297
298
299
300
301
302
303
304
# File 'lib/matrix_sdk/bot/base.rb', line 295

def event(event, only: nil, **_params, &block)
  logger.debug "Registering event #{event}"

  add_handler(
    event.to_s,
    type: :event,
    only: [only].flatten.compact,
    &block
  )
end

.event?(event, ignore_inherited: false) ⇒ Boolean

Check if an event is registered

Parameters:

  • event (String)

    The event type to check

  • ignore_inherited (Booleen) (defaults to: false)

    Should the check ignore any inherited events and only check local registrations

Returns:

  • (Boolean)


325
326
327
328
329
# File 'lib/matrix_sdk/bot/base.rb', line 325

def event?(event, ignore_inherited: false)
  return @handlers[event]&.event? if ignore_inherited

  all_handlers(type: :event)[event]&.event? || false
end

.get_command(command, ignore_inherited: false) ⇒ RequestHandler?

Retrieves the RequestHandler for a given command

Parameters:

  • command (String)

    The command to retrieve

  • ignore_inherited (Booleen) (defaults to: false)

    Should the retrieval ignore any inherited commands and only check local registrations

Returns:

  • (RequestHandler, nil)

    The registered handler for the command if any



336
337
338
339
340
341
342
# File 'lib/matrix_sdk/bot/base.rb', line 336

def get_command(command, ignore_inherited: false)
  if ignore_inherited && @handlers[command]&.command?
    @handlers[command]
  elsif !ignore_inherited && all_handlers[command]&.command?
    all_handlers[command]
  end
end

.get_event(event, ignore_inherited: false) ⇒ RequestHandler?

Retrieves the RequestHandler for a given event

Parameters:

  • event (String)

    The event type to retrieve

  • ignore_inherited (Booleen) (defaults to: false)

    Should the retrieval ignore any inherited events and only check local registrations

Returns:

  • (RequestHandler, nil)

    The registered handler for the event if any



349
350
351
352
353
354
355
# File 'lib/matrix_sdk/bot/base.rb', line 349

def get_event(event, ignore_inherited: false)
  if ignore_inherited && @handlers[event]&.event?
    @handlers[event]
  elsif !ignore_inherited && all_handlers(type: :event)[event]&.event?
    all_handlers(type: :event)[event]
  end
end

.loggerObject



61
62
63
64
65
66
67
68
69
70
# File 'lib/matrix_sdk/bot/base.rb', line 61

def self.logger
  Logging.logger[self].tap do |l|
    begin
      l.level = :debug if MatrixSdk::Bot::PARAMS_CONFIG[:logging]
    rescue NameError
      # Not running as instance
    end
    l.level = settings.log_level unless settings.logging?
  end
end

.quit!Object

Stops any running instance of the bot



380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
# File 'lib/matrix_sdk/bot/base.rb', line 380

def quit!
  return unless running?

  active_bot.logger.info "Stopping #{settings.bot_name}..."

  if settings.store_sync_token
    begin
      active_bot.client.api.(
        active_bot.client.mxid, "dev.ananace.ruby-sdk.#{settings.bot_name}",
        { sync_token: active_bot.client.sync_token }
      )
    rescue StandardError => e
      active_bot.logger.error "Failed to save sync token, #{e.class}: #{e}"
    end
  end

  active_bot.client.logout if login?

  active_bot.client.api.stop_inflight
  active_bot.client.stop_listener_thread

  set :active_bot, nil
end

.remove_command(command) ⇒ Object

Note:

This will only affect local commands, not ones inherited

Removes a registered command from the bot

Parameters:

  • command (String)

    The command to remove



361
362
363
364
365
366
# File 'lib/matrix_sdk/bot/base.rb', line 361

def remove_command(command)
  return false unless @handlers[command]&.command?

  @handers.delete command
  true
end

.remove_event(event) ⇒ Object

Note:

This will only affect local event, not ones inherited

Removes a registered event from the bot

Parameters:

  • event (String)

    The event to remove



372
373
374
375
376
377
# File 'lib/matrix_sdk/bot/base.rb', line 372

def remove_event(event)
  return false unless @handlers[event]&.event?

  @handers.delete event
  true
end

.reset!Object

Reset the bot class, removing any local handlers that have been registered



178
179
180
181
# File 'lib/matrix_sdk/bot/base.rb', line 178

def reset!
  @handlers = {}
  @client_handler = nil
end

.run!(options = {}, &block) ⇒ Object

Starts the bot up

Parameters:

  • options (Hash) (defaults to: {})

    Settings to apply using Base.set



407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
# File 'lib/matrix_sdk/bot/base.rb', line 407

def run!(options = {}, &block)
  return if running?

  set options

  bot_settings = settings.respond_to?(:bot_settings) ? settings.bot_settings : {}
  bot_settings.merge!(
    threadsafe: settings.threadsafe,
    client_cache: settings.client_cache,
    sync_filter: settings.sync_filter
  )

  bot_settings[:auth] = if settings.access_token?
                          { access_token: settings.access_token }
                        else
                          { username: settings.username, password: settings.password }
                        end

  begin
    start_bot(bot_settings, &block)
  ensure
    quit!
  end
end

.running?Boolean

Check whether the self-hosted server is running or not.

Returns:

  • (Boolean)


433
434
435
# File 'lib/matrix_sdk/bot/base.rb', line 433

def running?
  active_bot?
end

.set(option, value = (not_set = true), ignore_setter = false, &block) ⇒ Object

Set a class-wide option for the bot

Parameters:

  • option (Symbol, Hash)

    The option/options to set

  • value (Proc, Symbol, Integer, Boolean, Hash, nil) (defaults to: (not_set = true))

    The value to set for the option, should be ignored if option is a Hash

  • ignore_setter (Boolean) (defaults to: false)

    Should any existing setter method be ignored during assigning of the option

Yield Returns:

  • The value that the option should return when requested, as an alternative to passing the Proc as value

Raises:

  • (ArgumentError)


198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
# File 'lib/matrix_sdk/bot/base.rb', line 198

def set(option, value = (not_set = true), ignore_setter = false, &block) # rubocop:disable Style/OptionalBooleanParameter
  raise ArgumentError if block && !not_set

  if block
    value = block
    not_set = false
  end

  if not_set
    raise ArgumentError unless option.respond_to?(:each)

    option.each { |k, v| set(k, v) }
    return self
  end

  return send("#{option}=", value) if respond_to?("#{option}=") && !ignore_setter

  setter = proc { |val| set option, val, true }
  getter = proc { value }

  case value
  when Proc
    getter = value
  when Symbol, Integer, FalseClass, TrueClass, NilClass
    getter = value.inspect
  when Hash
    setter = proc do |val|
      val = value.merge val if val.is_a? Hash
      set option, val, true
    end
  end

  define_singleton("#{option}=", setter)
  define_singleton(option, getter)
  define_singleton("#{option}?", "!!#{option}") unless method_defined? "#{option}?"
  self
end

.settingsObject

Access settings defined with Base.set



144
145
146
# File 'lib/matrix_sdk/bot/base.rb', line 144

def self.settings
  self
end

Instance Method Details

#botObject



601
602
603
# File 'lib/matrix_sdk/bot/base.rb', line 601

def bot
  self
end

#command?(command, **params) ⇒ Boolean

Checks for the existence of a command

Parameters:

  • command (String)

    The command to check

Returns:

  • (Boolean)

See Also:



126
127
128
# File 'lib/matrix_sdk/bot/base.rb', line 126

def command?(command, **params)
  self.class.command?(command, **params)
end

#command_allowed?(command, event) ⇒ Boolean

Returns:

  • (Boolean)


533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
# File 'lib/matrix_sdk/bot/base.rb', line 533

def command_allowed?(command, event)
  pre_event = @event

  return false unless command? command

  handler = get_command(command)
  return true if (handler.data[:only] || []).empty?

  # Avoid modifying input data for a checking method
  @event = MatrixSdk::Response.new(client.api, event.dup)
  return false if [handler.data[:only]].flatten.compact.any? do |only|
    if only.is_a? Proc
      !instance_exec(&only)
    else
      case only.to_s.downcase.to_sym
      when :dm
        !room.dm?(members_only: true)
      when :admin
        !sender_admin?
      when :mod
        !sender_moderator?
      end
    end
  end

  true
ensure
  @event = pre_event
end

#event?(event, **params) ⇒ Boolean

Checks for the existence of a handled event

Parameters:

  • event (String)

    The event to check

Returns:

  • (Boolean)

See Also:



134
135
136
# File 'lib/matrix_sdk/bot/base.rb', line 134

def event?(event, **params)
  self.class.event?(event, **params)
end

#event_allowed?(event) ⇒ Boolean

Returns:

  • (Boolean)


563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
# File 'lib/matrix_sdk/bot/base.rb', line 563

def event_allowed?(event)
  pre_event = @event

  return false unless event? event[:type]

  handler = get_event(event[:type])
  return true if (handler.data[:only] || []).empty?

  # Avoid modifying input data for a checking method
  @event = MatrixSdk::Response.new(client.api, event.dup)
  return false if [handler.data[:only]].flatten.compact.any? do |only|
    if only.is_a? Proc
      instance_exec(&only)
    else
      case only.to_s.downcase.to_sym
      when :dm
        !room.dm?(members_only: true)
      when :admin
        !sender_admin?
      when :mod
        !sender_moderator?
      end
    end
  end

  true
ensure
  @event = pre_event
end

#expanded_prefixObject

Helpers



626
627
628
629
630
# File 'lib/matrix_sdk/bot/base.rb', line 626

def expanded_prefix
  return "#{settings.command_prefix}#{settings.bot_name} " if settings.bot_name?

  settings.command_prefix
end

#get_command(command, **params) ⇒ RequestHandler

Gets the handler for a command

Parameters:

  • command (String)

    The command to retrieve

Returns:

See Also:



109
110
111
# File 'lib/matrix_sdk/bot/base.rb', line 109

def get_command(command, **params)
  self.class.get_command(command, **params)
end

#get_event(event, **params) ⇒ RequestHandler

Gets the handler for an event

Parameters:

  • event (String)

    The event to retrieve

Returns:

See Also:



118
119
120
# File 'lib/matrix_sdk/bot/base.rb', line 118

def get_event(event, **params)
  self.class.get_event(event, **params)
end

#in_event?Boolean

Helpers for handling events

Returns:

  • (Boolean)


597
598
599
# File 'lib/matrix_sdk/bot/base.rb', line 597

def in_event?
  !@event.nil?
end

#register_command(command, **params, &block) ⇒ Object

Register a command during runtime

Parameters:

  • command (String)

    The command to register

See Also:



76
77
78
# File 'lib/matrix_sdk/bot/base.rb', line 76

def register_command(command, **params, &block)
  self.class.command(command, **params, &block)
end

#register_event(event, **params, &block) ⇒ Object

Register an event during runtime

Parameters:

  • event (String)

    The event to register

See Also:



84
85
86
# File 'lib/matrix_sdk/bot/base.rb', line 84

def register_event(event, **params, &block)
  self.class.event(event, **params, &block)
end

#roomObject



605
606
607
# File 'lib/matrix_sdk/bot/base.rb', line 605

def room
  client.ensure_room(event[:room_id]) if in_event?
end

#senderObject



609
610
611
# File 'lib/matrix_sdk/bot/base.rb', line 609

def sender
  client.get_user(event[:sender]) if in_event?
end

#sender_admin?Boolean

Helpers for checking power levels

Returns:

  • (Boolean)


614
615
616
# File 'lib/matrix_sdk/bot/base.rb', line 614

def sender_admin?
  sender&.admin? room
end

#sender_moderator?Boolean

Returns:

  • (Boolean)


618
619
620
# File 'lib/matrix_sdk/bot/base.rb', line 618

def sender_moderator?
  sender&.moderator? room
end

#settingsObject

Access settings defined with Base.set



139
140
141
# File 'lib/matrix_sdk/bot/base.rb', line 139

def settings
  self.class.settings
end

#unregister_command(command) ⇒ Object

Removes a registered command during runtime

Parameters:

  • command (String)

    The command to remove

See Also:



92
93
94
# File 'lib/matrix_sdk/bot/base.rb', line 92

def unregister_command(command)
  self.class.remove_command(command)
end

#unregister_event(command) ⇒ Object

Removes a registered event during runtime

Parameters:

  • event (String)

    The event to remove

See Also:



100
101
102
# File 'lib/matrix_sdk/bot/base.rb', line 100

def unregister_event(command)
  self.class.remove_event(command)
end