Class: EM::IrcBot
- Inherits:
-
Object
- Object
- EM::IrcBot
- Defined in:
- lib/em/irc_bot.rb
Overview
A straightforward, if slightly bare-bones, EventMachine-based IRC bot framework.
IRC bots. We love 'em. Chatops wouldn't exist without 'em. Sometimes, when you're feeling a bit of self-loathing, you might decide you want to write one using EventMachine. Make life a bit easier on yourself, and use this class.
Basic usage is pretty simple: create a new instance, passing in all sorts of options to specify the server to talk to and the channels to join. Then specify what to respond to, and how to respond to it, by using the #on method to register callbacks in response to all lines which match a given regular expression. That's... pretty much it. The rest is up to you.
Instance Attribute Summary collapse
-
#nick ⇒ String
readonly
This bot's nick.
Instance Method Summary collapse
-
#command(cmd, &blk) {|The, The, The| ... } ⇒ Object
Register a callback to be executed on a given command.
-
#connect(host, port, tls = false) ⇒ Object
Connect to an IRC server.
-
#initialize(nick, opts = {}) ⇒ IrcBot
constructor
Create a new IRC bot.
-
#join(ch) ⇒ Object
Make the bot join a channel.
-
#listen_for(match, opts = {}, &blk) {|an, Any| ... } ⇒ Object
Register a new callback for a PRIVMSG seen by the bot.
-
#on(match, &blk) {|The, The| ... } ⇒ Object
Register a new callback to handle a line sent to the bot.
-
#on_once(match, opts = {}, &blk) ⇒ Object
Register a new "one-shot" callback to handle a single line sent to the bot.
-
#ready ⇒ Object
:nodoc:.
-
#ready? ⇒ Boolean
Tell whether the bot is ready to do things.
-
#receive_data(s) ⇒ Object
:nodoc:.
-
#say(target, msg) ⇒ Object
Send a message to someone (or a channel).
-
#send_line(s) ⇒ Object
Send a (raw) line to the server.
-
#unbind(*args) ⇒ Object
:nodoc:.
Constructor Details
#initialize(nick, opts = {}) ⇒ IrcBot
Create a new IRC bot.
If you want to have the bot connect to the server immediately upon
creation, pass the :server
and :port
(and optionally :tls
)
options. Otherwise, you can ask the bot to connect any later time with
#connect.
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 |
# File 'lib/em/irc_bot.rb', line 74 def initialize(nick, opts = {}) @nick = nick @ready = false @backlog = "" @line_handlers = {} @privmsg_handlers = {} @commands = {} @serverpass = opts[:serverpass] @username = opts[:username] || "em-irc-bot" @realname = opts[:realname] || "EM::IrcBot" @channels = opts[:channels] || [] @log = opts[:logger] || Logger.new($stderr).tap do |l| l.level = Logger::FATAL l.formatter = proc { |s, dt, p, m| "#{m}\n" } end @cmd_prefix = opts[:command_prefix] || "!" if opts[:server] and opts[:port] connect(opts[:server], opts[:port], opts[:tls]) end on(/^ping( |$)/i) do |s, _| s.send_line "PONG" end on(/^:[^\s]+ PRIVMSG /, &method(:do_privmsg)) listen_for(/./, &method(:command_handler)) end |
Instance Attribute Details
#nick ⇒ String (readonly)
This bot's nick.
25 26 27 |
# File 'lib/em/irc_bot.rb', line 25 def nick @nick end |
Instance Method Details
#command(cmd, &blk) {|The, The, The| ... } ⇒ Object
Register a callback to be executed on a given command.
A command is anything said in-channel which is prefixed by the
:command_prefix
option passed to the bot's constructor ("!"
by
default), or anything at all said to the bot privately.
Only one callback can be registered for a given command. If you register for the same command twice, only the last callback will be activated.
166 167 168 |
# File 'lib/em/irc_bot.rb', line 166 def command(cmd, &blk) @commands[cmd] = blk end |
#connect(host, port, tls = false) ⇒ Object
Connect to an IRC server.
If a connection is already established, the bot will be disconnect and then a new connection made to the specified server.
119 120 121 122 123 124 125 |
# File 'lib/em/irc_bot.rb', line 119 def connect(host, port, tls = false) @host = host @port = port @tls = tls reconnect end |
#join(ch) ⇒ Object
Make the bot join a channel.
134 135 136 |
# File 'lib/em/irc_bot.rb', line 134 def join(ch) send_line("JOIN #{ch}") end |
#listen_for(match, opts = {}, &blk) {|an, Any| ... } ⇒ Object
This method only matches against the message body -- what a user "normally" sees in their IRC client as conversation. While this is usually what you want, if you want your bot to respond to control data, like join/part, you'll want to use #on instead of this method.
Register a new callback for a PRIVMSG seen by the bot.
The fundamental way of causing the bot to interact with its environment is to watch for incoming messages, and run all callbacks associated with regular expressions which match the line that was received. This method registers those callbacks.
203 204 205 206 |
# File 'lib/em/irc_bot.rb', line 203 def listen_for(match, opts = {}, &blk) @log.debug { "Setting handler for #{match.inspect}, opts: #{opts.inspect}" } @privmsg_handlers[match] = blk end |
#on(match, &blk) {|The, The| ... } ⇒ Object
Register a new callback to handle a line sent to the bot.
This method matches against everything in the data line; you'll need to handle all of the protocol internal parts yourself. In general, you probably want to use #listen_for instead.
224 225 226 227 |
# File 'lib/em/irc_bot.rb', line 224 def on(match, &blk) @log.debug { "Setting 'on' handler for #{match.inspect}" } @line_handlers[match] = blk end |
#on_once(match, opts = {}, &blk) ⇒ Object
Register a new "one-shot" callback to handle a single line sent to the bot.
Sometimes, you want to respond to only the "next" message matching a particular regex. That's what this method is for. The first line that matches the regex will cause the callback to be called (in the same manner as #on), and then the handler will be deleted.
239 240 241 242 243 244 |
# File 'lib/em/irc_bot.rb', line 239 def on_once(match, opts = {}, &blk) on(match) do |*args| blk.call(*args) @line_handlers.delete(match) end end |
#ready ⇒ Object
:nodoc:
Callback used by ConnHandler to signal to the bot that the connection is established.
283 284 285 286 287 288 289 290 291 292 293 294 295 296 |
# File 'lib/em/irc_bot.rb', line 283 def ready @log.debug "Ready to rock and/or roll" send_line("PASS #{@serverpass}") if @serverpass send_line("NICK #{@nick}") send_line("USER #{@username} 0 * :#{@realname}") on_once(/^:#{@nick} /) do @channels.each do |ch| join(ch) end end @ready = true end |
#ready? ⇒ Boolean
Tell whether the bot is ready to do things.
302 303 304 |
# File 'lib/em/irc_bot.rb', line 302 def ready? @ready end |
#receive_data(s) ⇒ Object
:nodoc:
Callback used by ConnHandler to give us data that has come from the server.
311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 |
# File 'lib/em/irc_bot.rb', line 311 def receive_data(s) s = @backlog + s while (i = s.index("\r\n")) do l = s[0..i-1] s = s[i+2..-1] @log.info "<< #{l}" @line_handlers.each_pair do |re, blk| blk.call(self, l) if re =~ l end end @backlog = s end |
#say(target, msg) ⇒ Object
Send a message to someone (or a channel)
274 275 276 |
# File 'lib/em/irc_bot.rb', line 274 def say(target, msg) send_line("PRIVMSG #{target} :#{msg}") end |
#send_line(s) ⇒ Object
Send a (raw) line to the server.
This method does nothing to your line, except terminate it. In general, you should rarely, if ever, use this method yourself.
258 259 260 261 262 263 264 265 266 |
# File 'lib/em/irc_bot.rb', line 258 def send_line(s) if s =~ /[\r\n]/ raise ArgumentError, "Line contained NL or CR" end @log.info ">> #{s}" @conn.send_data("#{s}\r\n") end |
#unbind(*args) ⇒ Object
:nodoc:
Callback used by ConnHandler to tell us that our connection has gone away.
332 333 334 335 336 337 338 |
# File 'lib/em/irc_bot.rb', line 332 def unbind(*args) @log.debug "Unbind called: #{args.inspect}" @ready = false @conn = nil EM.add_timer(1) { reconnect } end |