Class: Autumn::CTCP
- Inherits:
-
Object
- Object
- Autumn::CTCP
- Defined in:
- lib/autumn/ctcp.rb
Overview
A listener for a Stem that listens for and handles CTCP requests. You can add CTCP support for your IRC client by instantiating this object and passing it to the Stem#add_listener method.
CTCP stands for Client-to-Client Protocol and is a way that IRC clients and servers can request and transmit more information about each other. Modern IRC clients all have CTCP support, and many servers expect or assume that their clients support CTCP. CTCP is also used as a basis for further extensions to IRC, such as DCC and XDCC.
This class implements the spec defined at www.invlogic.com/irc/ctcp.html
Because some IRC servers will disconnect clients that send a large number of messages in a short period of time, this listener will only send one CTCP reply per second, with up to a maximum of 10 replies waiting in the queue (after which new requests are ignored). These values can be adjusted in the initialization options.
This class acts as a listener plugin: Any of the methods specified below can be implemented by any other listener, and will be invoked by this listener when appropriate.
To respond to incoming CTCP requests, you should implement methods of the form ctcp_*_request
, where “*” is replaced with the lowercase name of the CTCP command. (For example, to handle VERSION requests, implement ctcp_version_request
). This method will be invoked whenever a request is received by the IRC client. It will be given the following parameters:
-
the CTCP instance that parsed the request,
-
the Stem instance that received the request,
-
the person who sent the request (a hash in the form of that used by Stem; see Stem#add_listener for more information), and
-
an array of arguments passed along with the request.
In addition, you can implement ctcp_request_received
, which will then be invoked for any and all incoming CTCP requests. It is passed the following arguments:
-
the name of the request, as a lowercase symbol,
-
the CTCP instance that parsed the request,
-
the Stem instance that received the request,
-
the person who sent the request (a sender hash – see the Leaf docs), and
-
an array of arguments passed along with the request.
This class will by default respond to some incoming CTCP requests and generate appropriate replies; however, it does not implement any specific behavior for parsing incoming replies. If you wish to parse replies, you should implement methods in your listener of the form ctcp_*_response
, with the “*” character replaced as above. This method will be invoked whenever a reply is received by this listener. You can also implement ctcp_response_received
just as above. The parameters for these methods are the same as those listed above.
Responses are assumed to be any CTCP messages that are sent as a NOTICE (as opposed to a PRIVMSG). Because they are NOTICEs, your program should not send a message in response.
In addition to responding to incoming CTCP requests and replies, your listener can use its stem to send CTCP requests and replies. See the added method for more detail.
Constant Summary collapse
- CTCP_REQUEST =
Format of an embedded CTCP request.
/\x01(.+?)\x01/
- ENCODED_COMMANDS =
CTCP commands whose arguments are encoded according to the CTCP spec (as opposed to other commands, whose arguments are plaintext).
[ 'VERSION', 'PING' ]
Instance Method Summary collapse
-
#added(stem) ⇒ Object
When this listener is added to a stem, the stem gains the ability to send CTCP messages directly.
-
#ctcp_ping_request(handler, stem, sender, arguments) ⇒ Object
Replies to a CTCP PING request by sending back the same arguments as a PONG reply.
-
#ctcp_time_request(handler, stem, sender, arguments) ⇒ Object
Replies to a CTCP TIME request by sending back the local time in RFC-822 format.
-
#ctcp_version_request(handler, stem, sender, arguments) ⇒ Object
Replies to a CTCP VERSION request by sending:.
-
#initialize(options = {}) ⇒ CTCP
constructor
Creates a new CTCP parser.
-
#irc_notice_event(stem, sender, arguments) ⇒ Object
Parses CTCP responses in a NOTICE event.
-
#irc_privmsg_event(stem, sender, arguments) ⇒ Object
Parses CTCP requests in a PRIVMSG event.
-
#make_ctcp_message(command, *arguments) ⇒ Object
Creates a CTCP-formatted message with the given command (uppercase string) and arguments (strings).
-
#send_ctcp_reply(stem, recipient, command, *arguments) ⇒ Object
Adds a CTCP reply to the queue.
Constructor Details
#initialize(options = {}) ⇒ CTCP
Creates a new CTCP parser. Options are:
reply_queue_size
-
The maximum number of pending replies to store in the queue, after which new CTCP requests are ignored.
reply_rate
-
The minimum time, in seconds, between consecutive CTCP replies.
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 |
# File 'lib/autumn/ctcp.rb', line 82 def initialize(={}) @options = @options[:reply_queue_size] ||= 10 @options[:reply_rate] ||= 0.25 @reply_thread = Hash.new @reply_queue = Hash.new do |hsh, key| hsh[key] = ForgetfulQueue.new(@options[:reply_queue_size]) @reply_thread[key] = Thread.new(key) do |stem| loop do #TODO wake thread when stem is quitting so this thread can terminate? reply = @reply_queue[stem].pop stem.notice reply[:recipient], reply[:message] sleep @options[:reply_rate] end end hsh[key] end end |
Instance Method Details
#added(stem) ⇒ Object
When this listener is added to a stem, the stem gains the ability to send CTCP messages directly. Methods of the form ctcp_*
, where “*” is the lowercase name of a CTCP action, will be forwarded to this listener, which will send the CTCP message. The first parameter of the method is the nick of one or more recipients; all other parameters are parameters for the CTCP command. See the CTCP spec for more information on the different commands and parameters available.
For example, to send an action (such as “/me is cold”) to a channel:
stem.ctcp_action "#channel", "is cold"
In addition, the stem gains the ability to send CTCP replies. Replies are messages that are added to this class’s reply queue, adding flood abuse prevention. To send a reply, call a Stem method of the form ctcp_reply_*
, where “*” is the command name you are replying to, in lowercase. Pass first the nick or sender hash of the recipient, then any parameters as specified by the CTCP spec. For example, to respond to a CTCP VERSION request:
stem.ctcp_reply_version sender, 'Bot Name', 'Computer Name', 'Other Info'
(Note that responding to VERSION requests is already handled by this class so you’ll need to either override or delete the ctcp_version_request method to do this.)
204 205 206 207 208 209 210 211 212 213 214 215 216 217 |
# File 'lib/autumn/ctcp.rb', line 204 def added(stem) stem.instance_variable_set :@ctcp, self class << stem def method_missing(meth, *args) if meth.to_s =~ /^ctcp_reply_([a-z]+)$/ then @ctcp.send_ctcp_reply self, args.shift, $1.to_s.upcase, *args elsif meth.to_s =~ /^ctcp_([a-z]+)$/ then privmsg args.shift, @ctcp.($1.to_s.upcase, *args) else super end end end end |
#ctcp_ping_request(handler, stem, sender, arguments) ⇒ Object
Replies to a CTCP PING request by sending back the same arguments as a PONG reply.
150 151 152 153 |
# File 'lib/autumn/ctcp.rb', line 150 def ctcp_ping_request(handler, stem, sender, arguments) return unless handler == self send_ctcp_reply stem, sender[:nick], 'PING', *arguments end |
#ctcp_time_request(handler, stem, sender, arguments) ⇒ Object
Replies to a CTCP TIME request by sending back the local time in RFC-822 format.
158 159 160 161 |
# File 'lib/autumn/ctcp.rb', line 158 def ctcp_time_request(handler, stem, sender, arguments) return unless handler == self send_ctcp_reply stem, sender[:nick], 'TIME', Time.now.rfc822 end |
#ctcp_version_request(handler, stem, sender, arguments) ⇒ Object
Replies to a CTCP VERSION request by sending:
-
the name of the IRC client (“Autumn, a Ruby IRC framework”),
-
the operating system name and version, and
-
the home page URL for Autumn.
To determine the OS name and version, this method runs the uname -sr
command; if your operating system does not support this command, you should override this method.
Although the CTCP spec states that the VERSION response should be three encoded strings (as shown above), many modern clients expect one plaintext string. If you’d prefer compatibility with those clients, you should override this method to return a single plaintext string and remove the VERSION command from ENCODED_COMMANDS
.
142 143 144 145 |
# File 'lib/autumn/ctcp.rb', line 142 def ctcp_version_request(handler, stem, sender, arguments) return unless handler == self send_ctcp_reply stem, sender[:nick], 'VERSION', "Autumn #{AUTUMN_VERSION}, a Ruby IRC framework", `uname -sr`, "http://github.com/RISCfuture/autumn" end |
#irc_notice_event(stem, sender, arguments) ⇒ Object
Parses CTCP responses in a NOTICE event.
115 116 117 118 119 120 121 122 123 124 |
# File 'lib/autumn/ctcp.rb', line 115 def irc_notice_event(stem, sender, arguments) # :nodoc: arguments[:message].scan(CTCP_REQUEST).flatten.each do |ctcp| ctcp_args = ctcp.split(' ') request = ctcp_args.shift ctcp_args = ctcp_args.map { |arg| unquote arg } if ENCODED_COMMANDS.include? request meth = "ctcp_#{request.downcase}_response".to_sym stem.broadcast meth, self, stem, sender, ctcp_args stem.broadcast :ctcp_response_received, request.downcase.to_sym, self, stem, sender, ctcp_args end end |
#irc_privmsg_event(stem, sender, arguments) ⇒ Object
Parses CTCP requests in a PRIVMSG event.
102 103 104 105 106 107 108 109 110 111 |
# File 'lib/autumn/ctcp.rb', line 102 def irc_privmsg_event(stem, sender, arguments) # :nodoc: arguments[:message].scan(CTCP_REQUEST).flatten.each do |ctcp| ctcp_args = ctcp.split(' ') request = ctcp_args.shift ctcp_args = ctcp_args.map { |arg| unquote arg } if ENCODED_COMMANDS.include? request meth = "ctcp_#{request.downcase}_request".to_sym stem.broadcast meth, self, stem, sender, ctcp_args stem.broadcast :ctcp_request_received, request.downcase.to_sym, self, stem, sender, ctcp_args end end |
#make_ctcp_message(command, *arguments) ⇒ Object
Creates a CTCP-formatted message with the given command (uppercase string) and arguments (strings). The string returned is suitable for transmission over IRC using the PRIVMSG command.
223 224 225 226 |
# File 'lib/autumn/ctcp.rb', line 223 def (command, *arguments) arguments = arguments.map { |arg| quote arg } if ENCODED_COMMANDS.include? command "\01#{arguments.unshift(command).join(' ')}\01" end |
#send_ctcp_reply(stem, recipient, command, *arguments) ⇒ Object
Adds a CTCP reply to the queue. You must pass the Stem instance that will be sending this reply, the recipient (channel or nick), and the name of the CTCP command (as an uppercase string). Any additional arguments are taken to be arguments of the CTCP reply, and are thus encoded and joined by space characters, as specified in the CTCP white paper. The arguments should all be strings.
recipient
can be a nick, a channel name, or a sender hash, as necessary. Encoding of arguments is only done for commands in ENCODED_COMMANDS
.
173 174 175 176 |
# File 'lib/autumn/ctcp.rb', line 173 def send_ctcp_reply(stem, recipient, command, *arguments) recipient = recipient[:nick] if recipient.kind_of? Hash @reply_queue[stem] << { :recipient => recipient, :message => (command, *arguments) } end |