An IRC client library.
Install it through rubygems,
gem install ircbgb
or whatever, by now you know the drill.
To refresh my knowledge of RFC 1459 (et al) and to provide the first concrete use case for io_unblock.
client = Ircbgb::Client.new do |c|
c.servers = [ 'irc://irc.freenode.net' ]
c.nicks = [ 'nick1', 'nick2', 'nick3' ]
c.username = 'user1'
c.realname = 'I am a test client'
end
client.connect
This will open a socket to irc.freenode.net
on port 6667 (by default) and
send the required USER
, NICK
and PONG
commands to establish a connection.
The io_unblock
library handles IO through a separate thread, as a result
there is no guarantee that the client is actually connected immediately after
client.connect
is executed.
client.received :privmsg do |msg, me|
# `me` == `client`
$stdout.puts "<#{msg.source.nick}/#{msg.target}> #{msg.text}"
end
client.sent :privmsg do |msg, me|
$stdout.puts "#{msg.target}> #{msg.text}"
end
Ircbgb::Messages.define :privmsg do
target 0
text 1
targets { target.split(',') }
end
Ircbgb::Messages.define :ctcp_request, :privmsg do
match { |cmd, params, src|
params[1] =~ /\A\001.*\001\Z/
}
request { text[1..-2] }
ctcp_command { request.split(' ').first }
end
With those definitions in place, if the IRC server sent the following message:
some_dude!dude@my.host.name PRIVMSG #stuff :Testing is fun!
Ircbgb would parse it into an instance of the Privmsg
class we just
defined:
a_msg.target #=> "#stuff"
a_msg.text #=> "Testing is fun!"
a_msg.params #=> ["#stuf", "Testing is fun!"]
a_msg.command #=> "PRIVMSG"
a_msg.source #=> Ircbgb::User
The params
, command
, and source
attributes come from Ircbgb::Message
,
which all defined messages inherit from. Our custom defined messages can
do even more!
If you sent the following message:
PRIVMSG some_dude :\001VERSION\001
Ircbgb would parse it into an instance of the CtcpRequest
class. The
parser resolves the string to a class by first examining the command, PRIVMSG,
in this case. It finds a Privmsg
class, so now it gives subclasses of
Privmsg
a chance to claim ownership of the message. As it happens,
CtcpRequest
is a subclass of Privmsg
because we gave :privmsg
as the
second parameter to Ircbgb::Messages.define
. The parser then invokes
the match
block with the appropriate parameters, in this case "PRIVMSG",
"\001VERSION\001" and nil
as the client itself is the source. The match
block detects the surrounding "\001" characters, and returns true, thus
a CtcpRequest
is born (instead of a Privmsg
or generic Message
instance.)
Message definitions can leverage all kinds of neat stuff. I'll write more on that later, in the mean time have a look at specs/ircbgb/messages_spec.rb
The implementation is not quite what I want yet.
Currently, we have:
client.received(:privmsg) { ... }
which is equivalent to
client.received('PRIVMSG') { ... }
However,
client.received(:ctcp_request) { ... }
will never get triggered, because Ircbgb triggers based solely on the command name at the moment. Here's an example of what I ultimately want:
client.define(:no_such_nickname, :numeric => 401)
client.received(401) { ... }
client.received(:no_such_nickname) { ... }
I think the way to handle this is to match against the command when a string
or fixnum is given to the event binding helpers (sending
, sent
,
receiving
, received
), and filter by class name when a symbol is given.
Further, I need to implement the ability to base a message definition off of a
numeric command.
I also want to change how callback chains can be stopped. At the moment, if
a callback returns false
, no further processing on the chain is performed;
however, the :all
chain still gets executed. Returning false
isn't good
enough for me. I would rather have a couple explicit methods, like
prevent_default!
and halt!
where prevent_default!
stops the default chain
from being executed, and halt!
stops any further processing in the current
chain. If we wrap the callback evaluation within a kind of CallbackEvent
instance, we can provide these two methods, as well as some other goodies.