Class: Ant::Bot::Base

Inherits:
Object
  • Object
show all
Includes:
Logger, Storage::Datasource
Defined in:
lib/ant/bot/base.rb

Overview

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

Instance Method Summary collapse

Constructor Details

#initialize(configs) ⇒ Base

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



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# File 'lib/ant/bot/base.rb', line 23

def initialize(configs)
  @pool_size = configs['pool_size']
  @provider = Ant::Bot::Adapter.from_config(configs['provider'])
  @commands = Ant::Bot::CommandDefinition.new

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

Instance Method Details

#add_param(value) ⇒ Object

Stores a parameter into the status



141
142
143
144
145
146
147
# File 'lib/ant/bot/base.rb', line 141

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

  @state[:params][@state[:requested_param].to_sym] = value.raw_message
end

#ask_param(param) ⇒ Object

Sends a message to get the next parameter from the user



133
134
135
136
137
138
# File 'lib/ant/bot/base.rb', line 133

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

#command=(cmd) ⇒ Object

stores the command into state



120
121
122
123
124
125
# File 'lib/ant/bot/base.rb', line 120

def command=(cmd)
  log_debug('Message set as command', command: cmd)

  @state[:cmd] = cmd.raw_message
  @state[:params] = {}
end

#command_ready?Boolean

Checks if the command is ready to be executed

Returns:

  • (Boolean)


98
99
100
101
# File 'lib/ant/bot/base.rb', line 98

def command_ready?
  cmd = current_command_object
  cmd.ready?(current_params)
end

#current_channelObject

returns the current_channel from where the message was sent



115
116
117
# File 'lib/ant/bot/base.rb', line 115

def current_channel
  @state[:channel_id]
end

#current_command_objectObject

Loads command from state



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

def current_command_object
  command = @state[:cmd]
  @commands[command]
end

#current_paramsObject

loads parameters from state



104
105
106
# File 'lib/ant/bot/base.rb', line 104

def current_params
  @state[:params] || {}
end

#load_state(channel) ⇒ Object

Private implementation for load message



155
156
157
158
159
160
161
# File 'lib/ant/bot/base.rb', line 155

def load_state(channel)
  data = @factory.get(channel)
  data[:params] = JSON.parse(data[:params], symbolize_names: true)
  data
rescue Ant::Storage::Exceptions::ObjectNotFound
  @factory.create(channel_id: channel, params: {}.to_json)
end

#load_state!(channel) ⇒ Object

Loads the state from storage



150
151
152
# File 'lib/ant/bot/base.rb', line 150

def load_state!(channel)
  @state = load_state(channel)
end

#next_missing_paramObject

validates which is the following parameter required



128
129
130
# File 'lib/ant/bot/base.rb', line 128

def next_missing_param
  current_command_object.next_missing_param(current_params)
end

#process_message(message) ⇒ Object

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



63
64
65
# File 'lib/ant/bot/base.rb', line 63

def process_message(message)
  run_simple_command!(message)
end

#register_command(name, params, &block) ⇒ Object

DSL method for adding simple commands



88
89
90
# File 'lib/ant/bot/base.rb', line 88

def register_command(name, params, &block)
  @commands.register_command(name, params, block)
end

#runObject

Starts the bot execution, this is a blocking call.



46
47
48
49
50
51
52
53
54
55
56
57
58
59
# File 'lib/ant/bot/base.rb', line 46

def run
  @pool = Array.new(@pool_size) do
    # TODO: Create a subclass with the context execution
    Ant::DRY::Daemon.new(@pool_size, true) do
      message = @provider.read_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!Object

Method for triggering command



93
94
95
# File 'lib/ant/bot/base.rb', line 93

def run_command!
  current_command_object.execute(current_params)
end

#run_simple_command!(message) ⇒ Object

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.



71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
# File 'lib/ant/bot/base.rb', line 71

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
  else
    add_param(message)
  end
  if command_ready?
    run_command!
  else
    ask_param(next_missing_param)
  end
  save_state!
end

#save_state!Object

Saves the state into storage



164
165
166
167
168
169
# File 'lib/ant/bot/base.rb', line 164

def save_state!
  json = @state[:params]
  @state[:params] = json.to_json
  @state.store
  @state[:params] = json
end

#sessionObject



41
42
43
# File 'lib/ant/bot/base.rb', line 41

def session
  @repository.connection
end