Class | Autumn::Stem |
In: |
lib/autumn/stem.rb
|
Parent: | Object |
A connection to an IRC server. The stem acts as the IRC client on which a Leaf runs. It receives messages from the IRC server and sends messages to the server. Stem is compatible with many IRC daemons; details of the IRC protocol are handled by a Daemon instance. (See "Compatibility with Different Server Types," below).
Generally stems are initialized by the Foliater, but should you want to instantiate one yourself, a Stem is instantiated with a server to connect to and a nickname to acquire (see the initialize method docs). Once you initialize a Stem, you should call add_listener one or more times to indicate to the stem what objects are interested in working with it.
An object that functions as a listener should conform to an implicit protocol. See the add_listener docs for more infortmation on what methods you can implement to listen for IRC events. Duck typing is used — you need not implement every method of the protocol, only those you are concerned with.
Listeners can also act as plugins: Such listeners add functionality to other listeners (for example, a CTCP listener that adds CTCP support to other listeners, such as a Leaf instance). For more information, see the add_listener docs.
Once you have finished configuring your stem and you are ready to begin the IRC session, call the start method. This method blocks until the the socket has been closed, so it should be run in a thread. Once the connection has been made, you are free to send and receive IRC commands until you close the connection, which is done with the quit method.
Receiving events is explained in the add_listener docs. To send an IRC command, simply call a method named after the command name. For instance, if you wish to PRIVMSG another nick, call the privmsg method. If you wish to JOIN a channel, call the join method. The parameters should be specified in the same order as the IRC command expects.
For more information on what IRC commands are "method-ized", see the IRC_COMMANDS constant. For more information on the proper way to use these commands (and thus, the methods that call them), consult the Daemon class.
Many different IRC server daemons exist, and each one has a slightly different IRC implementation. To manage this, there is an option called server_type, which is set automatically by the stem if it can determine the IRC software that the server is running. Server types are instances of the Daemon class, and are associated with a name. A stem‘s server type affects things like response codes, user modes, and channel modes, as these vary from server to server.
If the stem is unsure what IRC daemon your server is running, it will use the default Daemon instance. This default server type will be compatible with nearly every server out there. You may not be able to leverage some of the more esoteric IRC features of your particular server, but for the most common uses of IRC (sending and receiving messages, for example), it will suffice.
If you‘d like to manually specify a server type, you can pass its name for the server_type initialization option. Consult the resources/daemons directory for valid Daemon names and hints on how to make your own Daemon specification, should you desire.
The convention for Autumn channel names is: When you specify a channel to an Autumn stem, you can (but don‘t have to) prefix it with the ’#’ character, if it‘s a normal IRC channel. When an Autumn stem gives a channel name to you, it will always start with the ’#’ character (assuming it‘s a normal IRC channel, of course). If your channel is prefixed with a different character (say, ’&’), you will need to include that prefix every time you pass a channel name to a stem method.
So, if you would like your stem to send a message to the "#kittens" channel, you can omit the ’#’ character; but if it‘s a server-local channel called "&kittens", you will have to provide the ’&’ character. Likewise, if you are overriding a hook method, you can be guaranteed that the channel given to you will always be called "#kittens", and not "kittens".
Because new messages are received and processed in separate threads, methods can sometimes receive messages out of order (for instance, if a first message takes a long time to process and a second message takes a short time to process). In the event that you require a guarantee that your method will receive messages in order, and that it will only be invoked in a single thread, annotate your method with the stem_sync property.
For instance, you might want to ensure that you are finished processing 353 messages (replies to NAMES commands) before you tackle 366 messages (end of NAMES list). To ensure these methods are invoked in the correct order:
class MyListener def irc_rpl_namreply_response(stem, sender, recipient, arguments, msg) [...] end def irc_rpl_endofnames_response(stem, sender, recipient, arguments, msg) [...] end ann :irc_rpl_namreply_response, :stem_sync => true ann :irc_rpl_endofnames_response, :stem_sync => true end
All such methods will be run in a single thread, and will receive server messages in order. Because of this, it is important that synchronized methods do not spend a lot of time processing a single message, as it forces all other synchronous methods to wait their turn.
This annotation is only relevant to "invoked" methods, those methods in listeners that are invoked by the stem‘s broadcast method. Methods that are marked with this annotation will also run faster, because they don‘t have the overhead of setting up a new thread.
Many of Stem‘s own internal methods are synchronized, to ensure internal data such as the channels list and channel members list stays consistent. Because of this, any method marked as synchronized can be guaranteed that the stem‘s channel data is consistent and "in sync" for the moment of time that the message was received.
If you send a message with the privmsg command, it will not be throttled. (Most IRC servers have some form of flood control that throttles rapid privmsg commands, however.)
If your IRC server does not have flood control, or you want to use client-side flood control, you can enable the throttling option. The stem will throttle large numbers of simultaneous messages, sending them with short pauses in between.
The privmsg command will still not be throttled (since it is a facade for the pure IRC command), but the StemFacade#message command will gain the ability to throttle its messages.
By default, the stem will begin throttling when there are five or more messages queued to be sent. It will continue throttling until the queue is emptied. When throttling, messages will be sent with a delay of one second between them. These options can be customized (see the initialize method options).
CHANNEL_REGEX | = | "[^\\s\\x7,:]+" | Describes all possible channel names. Omits the channel prefix, as that can vary from server to server. (See channel?) | |
NICK_REGEX | = | "[a-zA-Z0-9\\-_\\[\\]\\{\\}\\\\|`\\^]+" | The default regular expression for IRC nicknames. | |
IRC_COMMANDS | = | { :pass => [ param('password') ], :nick => [ param('nickname') ], :user => [ param('user'), param('host'), param('server'), param('name') ], :oper => [ param('user'), param('password') ], :quit => [ param('message', :required => false, :colonize => true) ], :join => [ param('channels', :list => true), param('keys', :list => true) ], :part => [ param('channels', :list => true) ], :mode => [ param('channel/nick'), param('mode'), param('limit', :required => false), param('user', :required => false), param('mask', :required => false) ], :topic => [ param('channel'), param('topic', :required => false, :colonize => true) ], :names => [ param('channels', :required => false, :list => true) ], :list => [ param('channels', :required => false, :list => true), param('server', :required => false) ], :invite => [ param('nick'), param('channel') ], :kick => [ param('channels', :list => true), param('users', :list => true), param('comment', :required => false, :colonize => true) ], :version => [ param('server', :required => false) ], :stats => [ param('query', :required => false), param('server', :required => false) ], :links => [ param('server/mask', :required => false), param('server/mask', :required => false) ], :time => [ param('server', :required => false) ], :connect => [ param('target server'), param('port', :required => false), param('remote server', :required => false) ], :trace => [ param('server', :required => false) ], :admin => [ param('server', :required => false) ], :info => [ param('server', :required => false) ], :privmsg => [ param('receivers', :list => true), param('message', :colonize => true) ], :notice => [ param('nick'), param('message', :colonize => true) ], :who => [ param('name', :required => false), param('is mask', :required => false) ], :whois => [ param('server/nicks', :list => true), param('nicks', :list => true, :required => false) ], :whowas => [ param('nick'), param('history count', :required => false), param('server', :required => false) ], :pong => [ param('code', :required => false, :colonize => true) ] | Valid IRC command names, mapped to information about their parameters. |
channel_members | [R] | A hash of channel members by channel name. |
channels | [R] | The channels that this stem is a member of. |
local_ip | [R] | The local IP to bind to (virtual hosting). |
logger | [R] | The LogFacade instance handling this stem. |
nick_generator | [R] | A Proc that will be called if a nickname is in use. It should take one argument, the nickname that was unavailable, and return a new nickname to try. The default Proc appends an underscore to the nickname to produce a new one, or GHOSTs the nick if possible. This block should return nil if you do not want another NICK attempt to be made. |
options | [R] | The global configuration options plus those for the current season and this stem. |
port | [R] | The remote port that this stem is connecting to. |
server | [R] | The address of the server this stem is connected to. |
server_type | [R] | The Daemon instance that describes the IRC server this client is connected to. |
Creates an instance that connects to a given IRC server and requests a given nick. Valid options:
port: | The port that the IRC client should connect on (default 6667). |
local_ip: | Set this if you want to bind to an IP other than your default (for virtual hosting). |
logger: | Specifies a logger instance to use. If none is specified, a new LogFacade instance is created for the current season. |
ssl: | If true, indicates that the connection will be made over SSL. |
user: | The username to transmit to the IRC server (by default it‘s the user‘s nick). |
name: | The real name to transmit to the IRC server (by default it‘s the user‘s nick). |
server_password: | The server password (not the nick password), if necessary. |
password: | The password to send to NickServ, if your leaf‘s nick is registered. |
channel: | The name of a channel to join. |
channels: | An array of channel names to join. |
sever_type: | The name of the server type. (See Daemon). If left blank, the default Daemon instance is used. |
rejoin: | If true, the stem will rejoin a channel it is kicked from. |
case_sensitive_channel_names: | If true, indicates to the IRC client that this IRC server uses case-sensitive channel names. |
dont_ghost: | If true, does not issue a /ghost command if the stem‘s nick is taken. (This is only relevant if the nick is registered and password is specified.) You should use this on IRC servers that don‘t use "NickServ" — otherwise someone may change their nick to NickServ and discover your password! |
ghost_without_password: | Set this to true if your IRC server uses hostname authentication instead of password authentication for GHOST commands. |
throttle: | If enabled, the stem will throttle large amounts of simultaneous messages. |
throttle_rate: | Sets the number of seconds that pass between consecutive PRIVMSG‘s when the leaf‘s output is throttled. |
throttle_threshold: | Sets the number of simultaneous messages that must be queued before the leaf begins throttling output. |
Any channel name can be a one-item hash, in which case it is taken to be a channel name-channel password association.
Adds an object that will receive notifications of incoming IRC messages. For each IRC event that the listener is interested in, the listener should implement a method in the form irc_[event]_event, where [event] is the name of the event, as taken from the IRC_COMMANDS hash. For example, to register interest in PRIVMSG events, implement the method:
irc_privmsg_event(stem, sender, arguments)
If you wish to perform an operation each time any IRC event is received, you can implement the method:
irc_event(stem, command, sender, arguments)
The parameters for both methods are as follows:
stem: | This Stem instance. |
sender: | A sender hash (see the Leaf docs). |
arguments: | A hash whose keys depend on the IRC command. Keys can be, for example, :recipient, :channel, :mode, or :message. Any can be nil. |
The irc_event method also receives the command name as a symbol.
In addition to events, the Stem will also pass IRC server responses along to its listeners. Known responses (those specified by the Daemon) are translated to programmer-friendly symbols using the Daemon.event hash. The rest are left in numerical form.
If you wish to register interest in a response code, implement a method of the form irc_[response]_response, where [response] is the symbol or numerical form of the response. For instance, to register interest in channel-full errors, you‘d implement:
irc_err_channelisfull_response(stem, sender, recipient, arguments, msg)
You can also register an interest in all server responses by implementing:
irc_response(stem, response, sender, recipient, arguments, msg)
This method is invoked when the server sends a response message. The parameters for both methods are:
sender: | The server‘s address. |
recipient: | The nick of the recipient (sometimes "*" if no nick has been assigned yet). |
arguments: | Array of response arguments, as strings. |
message: | An additional message attached to the end of the response. |
The irc_server_response method additionally receives the response code as a symbol or numerical parameter.
Please note that there are hundreds of possible responses, and IRC servers differ in what information they send along with each response code. I recommend inspecting the output of the specific IRC server you are working with, so you know what arguments to expect.
If your listener is interested in IRC server notices, implement the method:
irc_server_notice(stem, server, sender, msg)
This method will be invoked for notices from the IRC server. Its parameters are:
server: | The server‘s address. |
sender: | The message originator (e.g., "Auth" for authentication-related messages). |
msg: | The notice. |
If your listener is interested in IRC server errors, implement the method:
irc_server_error(stem, msg)
This method will be invoked whenever an IRC server reports an error, and is passed the error message. Server errors differ from normal server responses, which themselves can sometimes indicate errors.
Some listeners can act as listener plugins; see the broadcast method for more information.
If you‘d like your listener to perform actions after it‘s been added to a Stem, implement a method called added. This method will be called when the listener is added to a stem, and will be passed the Stem instance it was added to. You can use this method, for instance, to add additional methods to the stem.
Your listener can implement the stem_ready method, which will be called once the stem has started up, connected to the server, and joined all its channels. This method is passed the stem instance.
Sends the method with the name meth (a symbol) to all listeners that respond to that method. You can optionally specify one or more arguments. This method is meant for use by listener plugins: listeners that add features to other listeners by allowing them to implement optional methods.
For example, you might have a listener plugin that adds CTCP support to stems. Such a method would parse incoming messages for CTCP commands, and then use the broadcast method to call methods named after those commands. Other listeners who want to use CTCP support can implement the methods that your listener plugin broadcasts.
Note: Each method call will be executed in its own thread, and all exceptions will be caught and reported. This method will only invoke listener methods that have not been marked as synchronized. (See "Synchronous Methods" in the class docs.)
Same as the broadcast method, but only invokes listener methods that have been marked as synchronized.
Given a full channel name, returns the channel type as a symbol. Values can be found in the Daemons instance. Returns :unknown for unknown channel types.
Normalizes a channel name by placing a "#" character before the name if no channel prefix is otherwise present. Also converts the name to lowercase if the case_sensitive_channel_names option is false. You can suppress the automatic prefixing by passing false for add_prefix.
Returns true if this stem has started up completely, connected to the IRC server, and joined all its channels. A period of 10 seconds is allowed to join all channels, after which the stem will report ready even if some channels could not be joined.
Opens a connection to the IRC server and begins listening on it. This method runs until the socket is closed, and should be run in a thread. It will terminate when the connection is closed. No messages should be transmitted, nor will messages be received, until this method is called.
In the event that the nick is unavailable, the nick_generator proc will be called.