Class: BeerBot::Bot

Inherits:
Array
  • Object
show all
Defined in:
lib/beerbot/01.bot/Bot.rb

Overview

Represents a sequence of BotModule instances.

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeBot

Returns a new instance of Bot.



19
20
21
# File 'lib/beerbot/01.bot/Bot.rb', line 19

def initialize
  super()
end

Instance Attribute Details

#module_namesObject

Returns the value of attribute module_names.



17
18
19
# File 'lib/beerbot/01.bot/Bot.rb', line 17

def module_names
  @module_names
end

#module_pathObject

Returns the value of attribute module_path.



17
18
19
# File 'lib/beerbot/01.bot/Bot.rb', line 17

def module_path
  @module_path
end

Instance Method Details

#action(action, from: nil, to: nil, me: false, config: nil) ⇒ Object



158
159
160
# File 'lib/beerbot/01.bot/Bot.rb', line 158

def action action,from:nil,to:nil,me:false,config:nil
  self.run(:action,action,from:from,to:to,me:me,config:config)
end

#cmd(msg, from: nil, to: nil, me: false, config: nil) ⇒ Object

Process messages addressed directly to the bot.



140
141
142
143
144
145
146
# File 'lib/beerbot/01.bot/Bot.rb', line 140

def cmd msg,from:nil,to:nil,me:false,config:nil
  if @cmd then
    @cmd.call(msg,from:from,to:to,config:config,me:me)
  else
    self.run(:cmd,msg,from:from,to:to,config:config,me:me)
  end
end

#event(event, **kargs) ⇒ Object

Handle events other than being messaged.

IRC events like joining channels, changing nicks etc.

Both event and kargs is dependent on the dispatcher which in turn is dependent on how the protocol (eg irc) is parsed.



169
170
171
# File 'lib/beerbot/01.bot/Bot.rb', line 169

def event event,**kargs
  self.run(:event,event,**kargs)
end

#has_errorsObject



91
92
93
94
95
96
97
# File 'lib/beerbot/01.bot/Bot.rb', line 91

def has_errors
  if self.find{|bot_module| !bot_module[:status]} then
    true
  else
    false
  end
end

#hear(msg, from: nil, to: nil, me: false, config: nil) ⇒ Object

Process messages the bot overhears.



150
151
152
153
154
155
156
# File 'lib/beerbot/01.bot/Bot.rb', line 150

def hear msg,from:nil,to:nil,me:false,config:nil
  if @hear then
    @hear.call(msg,from:from,to:to,me:me,config:config)
  else
    self.run(:hear,msg,from:from,to:to,me:me,config:config)
  end
end

#help(arr, from: nil, to: nil, config: nil, me: false) ⇒ Object



173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
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
235
236
237
238
239
240
# File 'lib/beerbot/01.bot/Bot.rb', line 173

def help arr,from:nil,to:nil,config:nil,me:false
  m = []
  modname,*topics = arr

  # "help"

  if arr.empty? then
    helplist = self.valid_modules.select {|bot_module|
      bot_module[:mod].respond_to?(:help)
    }.map{|bot_module|
      bot_module[:name]
    }
    reply = [
      {
        to:from,
        msg:"To issue commands to the bot over a channel, you need to start with a command prefix like ','."
      },
      {
        to:from,
        msg:"Modules (type: help <module-name>): "+helplist.join('; ')
      }
    ]

  # "help modname [subtopic [... [... etc]]]"

  else

    bot_module = self.valid_modules.find{|bot_module|
      bot_module[:name]==modname
    }

    # Can't find module...
    if bot_module.nil? then
      reply = [to:to,msg:"Don't know this topic #{from}"]

    # Can find module...
    else

      mod = bot_module[:mod]
      reply = []


      # Module has help...
      if mod.respond_to?(:help) then
        arr = mod.help(topics)

        if !arr || arr.empty? then
          reply += [to:from,msg:"hmmm, the module didn't say anything..."]
        else
          if topics.empty? then
            reply += [to:from,msg:"Note: modules should list topics which you can access like: help #{modname} <topic>"]
          end
          reply += arr.map{|a| {to:from,msg:a}}
        end

      # Module doesn't have help...
      else
        reply += [to:from,msg:"type: #{modname} doesn't seem to have any help"]
      end
    end

  end

  if not me then
    reply += [to:to,msg:"pm'ing you #{from}"]
  end
  return reply
end

#init(config) ⇒ Object

Call all init methods on bot modules that have them.

Should only be called once.



27
28
29
30
31
32
33
# File 'lib/beerbot/01.bot/Bot.rb', line 27

def init config
  self.valid_modules.each {|botmodule|
    if botmodule[:mod].respond_to?(:init) then
      botmodule[:mod].init(config)
    end
  }
end

#load!(module_names, module_path) ⇒ Object

Purge existing modules from this array and load modules with names in module_names in module_path on disk into memory.

MODULE NAMES and MODULE PATH:

Best understood like this:

"#{module_path}/#{name}/*" = location of modules
modname = "::BeerBot::Modules::#{name}"


54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/beerbot/01.bot/Bot.rb', line 54

def load! module_names, module_path
  @module_path = module_path
  @module_names = module_names
  self.reject!{true} unless self.empty?  # ick :)
  Dir.chdir(module_path) {
    module_names.each {|name|
      initfile = "#{name}/init.rb"
      modname = "::BeerBot::Modules::#{name}"
      mod = nil
      err = [
        [:nodir,!File.directory?(name)],
        [:badname,name !~ /^[A-Z]/  ],
        [:noinit,!File.exists?(initfile)],
      ].select{|e| e[1]}
      ok = (err.size == 0)

      if ok then
        puts "loading #{initfile}..."
        load(initfile)
        mod = Object.const_get(modname)
        if mod.respond_to?(:instance) then
          mod = mod.instance
        end
      else
        p [initfile,err]
        raise "Can't load (some) modules."
      end

      bm = BotModule.new(
        name,status:ok,mod:mod,
        modname:modname,errors:err
      )
      self.push(bm)
    }
  }
end

#run(meth, *args, **kargs) ⇒ Object

Call :meth on valid (loaded) modules and maybe accumulate result or return first valid response…

Converts return value to ARRAY FORMAT if not already.

The array could be empty, which means nothing was returned (or we couldn’t interpret the output of the bot modules).

We expect each bot_module to return nil or a botmsg (Hash, Array or Proc that returns the first two).

At the moment, we use inject and break on first valid response…



119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
# File 'lib/beerbot/01.bot/Bot.rb', line 119

def run meth,*args,**kargs
  self.valid_modules.inject([]) {|arr,bot_module|
    name,mod = bot_module.values_at(:name,:mod)
    unless mod.respond_to?(meth) then
      next arr
    end
    reply = mod.send(meth,*args,**kargs)
    suppress,botmsg = BotMsg.to_reply_format(reply)
    if botmsg then
      arr += botmsg
    end
    if suppress then
      break arr
    else
      arr
    end
  }
end

#set_cmd(&block) ⇒ Object

Override normal cmd processing with Proc.

You might want to do this to temporarily stop normal bot command behaviour in order for the bot to go into some sort of exclusive mode.

To disable normal response behaviour do:

bot.set_cmd {|msg,**kargs| nil }
bot.set_hear {|msg,**kargs| nil }

To unset,

bot.set_cmd
bot.set_hear


258
259
260
# File 'lib/beerbot/01.bot/Bot.rb', line 258

def set_cmd &block
  @cmd = block
end

#set_hear(&block) ⇒ Object

Override normal hear-processing with Proc.

See set_cmd.



266
267
268
# File 'lib/beerbot/01.bot/Bot.rb', line 266

def set_hear &block
  @hear = block
end

#update_config(config) ⇒ Object

Call #config on all valid bot modules.



37
38
39
40
41
42
43
# File 'lib/beerbot/01.bot/Bot.rb', line 37

def update_config config
  self.valid_modules.each {|botmodule|
    if botmodule[:mod].respond_to?(:config) then
      botmodule[:mod].config(config)
    end
  }
end

#valid_modulesObject

Return list of valid (loaded) modules.



101
102
103
# File 'lib/beerbot/01.bot/Bot.rb', line 101

def valid_modules
  self.select{|bot_module| bot_module[:status]}
end