class Kybus::Bot::Base

Base class for bot implementation. It wraps the threads execution, the provider and the state storage inside an object.

Base class for bot implementation. It wraps the threads execution, the provider and the state storage inside an object.

Attributes

provider[R]

Public Class Methods

new(configs) click to toggle source

Configurations needed:

  • pool_size: number of threads created in execution

  • provider: a configuration for a thread provider. See supported adapters

  • name: The bot name

  • repository: Configurations about the state storage

# File lib/kybus/bot/base.rb, line 41
def initialize(configs)
  @pool_size = configs['pool_size']
  @provider = Kybus::Bot::Adapter.from_config(configs['provider'])
  @commands = Kybus::Bot::CommandDefinition.new

  # TODO: move this to config
  @repository = Kybus::Storage::Repository.from_config(
    nil,
    configs['state_repository']
      .merge('primary_key' => 'channel_id',
             'table' => 'bot_sessions'),
    {}
  )
  @factory = Kybus::Storage::Factory.new(EmptyModel)
  @factory.register(:default, :json)
  @factory.register(:json, @repository)
end

Public Instance Methods

add_param(value) click to toggle source

Stores a parameter into the status

# File lib/kybus/bot/base.rb, line 183
def add_param(value)
  return if @state[:requested_param].nil?

  log_debug('Received new param',
            param: @state[:requested_param].to_sym,
            value: value)

  @state[:params][@state[:requested_param].to_sym] = value
end
ask_param(param) click to toggle source

Sends a message to get the next parameter from the user

# File lib/kybus/bot/base.rb, line 175
def ask_param(param)
  log_debug('I\'m going to ask the next param', param: param)
  @provider.send_message(current_channel,
                         "I need you to tell me #{param}")
  @state[:requested_param] = param.to_s
end
clear_command() click to toggle source
# File lib/kybus/bot/base.rb, line 119
def clear_command
  @state[:cmd] = nil
end
command=(cmd) click to toggle source

stores the command into state

# File lib/kybus/bot/base.rb, line 162
def command=(cmd)
  log_debug('Message set as command', command: cmd)

  @state[:cmd] = cmd.split(' ').first
  @state[:params] = {}
end
command_ready?() click to toggle source

Checks if the command is ready to be executed

# File lib/kybus/bot/base.rb, line 124
def command_ready?
  cmd = current_command_object
  cmd.ready?(current_params)
end
current_channel() click to toggle source

returns the current_channel from where the message was sent

# File lib/kybus/bot/base.rb, line 149
def current_channel
  @state[:channel_id]
end
current_command_object() click to toggle source

Loads command from state

# File lib/kybus/bot/base.rb, line 143
def current_command_object
  command = @state[:cmd]
  @commands[command] || @commands['default']
end
current_params() click to toggle source

loads parameters from state

# File lib/kybus/bot/base.rb, line 130
def current_params
  @state[:params] || {}
end
current_user() click to toggle source
# File lib/kybus/bot/base.rb, line 153
def current_user
  @last_message.user
end
is_private?() click to toggle source
# File lib/kybus/bot/base.rb, line 157
def is_private?
  @last_message.is_private?
end
load_state(channel) click to toggle source

Private implementation for load message

# File lib/kybus/bot/base.rb, line 199
def load_state(channel)
  data = @factory.get(channel)
  data[:params] = JSON.parse(data[:params], symbolize_names: true)
  data
rescue Kybus::Storage::Exceptions::ObjectNotFound
  @factory.create(channel_id: channel, params: {}.to_json)
end
load_state!(channel) click to toggle source

Loads the state from storage

# File lib/kybus/bot/base.rb, line 194
def load_state!(channel)
  @state = load_state(channel)
end
mention(name) click to toggle source
# File lib/kybus/bot/base.rb, line 138
def mention(name)
  @provider.mention(name)
end
next_missing_param() click to toggle source

validates which is the following parameter required

# File lib/kybus/bot/base.rb, line 170
def next_missing_param
  current_command_object.next_missing_param(current_params)
end
params() click to toggle source
# File lib/kybus/bot/base.rb, line 134
def params
  current_params
end
process_message(message) click to toggle source

Process a single message, this method can be overwriten to enable more complex implementations of commands. It receives a message object.

# File lib/kybus/bot/base.rb, line 78
def process_message(message)
  run_simple_command!(message)
end
register_command(name, params = [], &block) click to toggle source

DSL method for adding simple commands

# File lib/kybus/bot/base.rb, line 109
def register_command(name, params = [], &block)
  @commands.register_command(name, params, block)
end
rescue_from(klass, &block) click to toggle source
# File lib/kybus/bot/base.rb, line 23
def rescue_from(klass, &block)
  @commands.register_command(klass, [], block)
end
run() click to toggle source

Starts the bot execution, this is a blocking call.

# File lib/kybus/bot/base.rb, line 60
def run
  @pool = Array.new(@pool_size) do
    # TODO: Create a subclass with the context execution
    Kybus::DRY::Daemon.new(@pool_size, true) do
      message = @provider.read_message
      @last_message = message
      process_message(message)
    end
  end
  # TODO: Implement an interface for killing the process
  @pool.each(&:run)
  # :nocov: #
  @pool.each(&:await)
  # :nocov: #
end
run_command!() click to toggle source

Method for triggering command

# File lib/kybus/bot/base.rb, line 114
def run_command!
  instance_eval(&current_command_object.block)
  clear_command
end
run_simple_command!(message) click to toggle source

Executes a command with the easiest definition. It runs a state machine:

  • If the message is a command, set the status to asking params

  • If the message is a param, stores it

  • If the command is ready to be executed, trigger it.

# File lib/kybus/bot/base.rb, line 86
def run_simple_command!(message)
  load_state!(message.channel_id)
  log_debug('loaded state', message: message.to_h, state: @state.to_h)
  if message.command?
    self.command = message.raw_message
  else
    add_param(message.raw_message)
  end
  if command_ready?
    run_command!
  else
    ask_param(next_missing_param)
  end
  save_state!
rescue StandardError => e
  catch = @commands[e.class]
  raise if catch.nil?

  instance_eval(&catch.block)
  clear_command
end
run_test() click to toggle source
# File lib/kybus/bot/testing.rb, line 20
def run_test
  run
rescue Debug::NoMoreMessageException
  true
end
save_state!() click to toggle source

Saves the state into storage

# File lib/kybus/bot/base.rb, line 208
def save_state!
  json = @state[:params]
  @state[:params] = json.to_json
  @state.store
  @state[:params] = json
end
send_audio(content, channel = nil) click to toggle source
# File lib/kybus/bot/base.rb, line 31
def send_audio(content, channel = nil)
  @provider.send_audio(channel || current_channel, content)
end
send_image(content, channel = nil) click to toggle source
# File lib/kybus/bot/base.rb, line 27
def send_image(content, channel = nil)
  @provider.send_image(channel || current_channel, content)
end
send_message(content, channel = nil) click to toggle source
# File lib/kybus/bot/base.rb, line 19
def send_message(content, channel = nil)
  @provider.send_message(channel || current_channel, content)
end
session() click to toggle source
# File lib/kybus/bot/base.rb, line 215
def session
  @repository
end
stub_channels(messages) click to toggle source
# File lib/kybus/bot/testing.rb, line 14
def stub_channels(messages)
  raise(NonDebugAdapterInTesting) unless @provider.is_a?(Debug)

  @provider = Debug.new('channels' => messages)
end