irc-connect-ctcp v0.1.1
irc-connect-ctcp
CTCP plugin for irc-connect
Basic CTCP support, emits 'ctcp_request' and 'ctcp_response' events, among
others.
PROTIP: Run with
DEBUG=irc:ctcp*to see all the events and data it throws, it's almost self-documenting!
Currently includes responders to PING, TIME, and VERSION requests but is
easily extensible.
DCC CHAT and DCC SEND are in development and work well, currently only
incoming requests (aka outgoing sockets) are supported. DCC SEND will unkindly
shove received files in your os.tmpdir() (unless you set the targetPath)
but won't overwrite (or resume) anything. There is an acceptance phase so this
won't just happen unless you wire an event to call the accept method.
Documentation
All of the following assumes this init stub:
var irc = require('irc-connect') var client = irc.connect(...) var ircCtcp = require('irc-connect-ctcp')
CTCP Plugin client.use(ircCtcp)
Setup and configurables
Access internal plugin options using methods
client.ctcpSetOption(key, value)
and
client.ctcpGetOption(key)
All settings are optional, there is a default provided for each one.
Set any of the autoresponses to literally false to disable the feature,
such as to install your own handler on ctcp_response, or go "steath".
NOTE: associative
falsesuch as''or0ornullorundefinedare all otherwise valid
For example, with the VERSION responder:
client.ctcpSetOption('versionRaw',false) // preferred client.ctcpSetOption('version',false) // courtesyWhen set
falseand someone sends aVERSIONrequest, there will be no response at all
CTCP VERSION responder
Default response is similar to
irc-connect-ctcp:UNKNOWN:NodeJS=0.10.30+(V8=3.14.5.9)
with your environments actual versions. Per some random official-looking protocol documentation I read somewhere (maybe the RFC?) the proper format is three colon-separated elements.
Set 'version' with a string and it will use the standard, with your string in
the middle element
client.ctcpSetOption('version','SomeApp v4.2')result:
'irc-connect-ctcp:SomeApp v4.2:NodeJS=0.10.30+(V8=3.14.5.9)'
Set 'versionRaw' with a string and it will take you literally
(RFC be darned!)
NOTE: if you set this to
''it will still respond, with empty paramsclient.ctcpSetOption('versionRaw','SomeApp v4.2')result:
'SomeApp v4.2'
CTCP TIME responder
Default response is the current date/time formatted like
Tue Aug 19 13:22:02 2014 -0600
THESE SETTINGS ARE PRELIMINARY AND ARE NOT IN THE CODE YET
Set 'timeSkew' with an offset in seconds to adjust the advertised time.
client.ctcpSetOption('timeSkew',3600)
CTCP TIMEresponse will be current date minus one hour
Set 'time' with a Date to force the response to then and not the current time.
client.ctcpSetOption('time',new Date('Mon, 5 Nov 1955 02:20:00 -0800'))
CTCP TIMEresponse:Tue Nov 5 04:20:00 1955 -0600no matter what time it really is, although note it is in your default timezone regardless the zone on what you set
CTCP PING responder
Default response is whatever the other end sent us (usually a timestamp number) and then whatever their current time on reception of the response minus the reflected stamp, yields a reasonable idea of latency.
THE FOLLOWING SETTINGS ARE PRELIMINARY AND ARE NOT IN THE CODE YET
As there are really no settables for this service, only the disable works, via 'ping'
client.ctcpSetOption('ping',false)
CTCP PINGwill no longer respond at all
Events
This plugin emits the events
'ctcp_request'
and
'ctcp_response'
on incoming CTCP messages. Consume them as usual:
client.on('ctcp_request',function(event){...}) client.on('ctcp_response',function(event){...})Where
origis the original'PRIVMSG'or'NOTICE'event...
member content event.typefrom orig.params[0](ex:'DCC','FINGER','VERSION')event.paramsfrom orig.params[1..n](ex:['SEND','Funny.mp3','12945673','7654','54321'],[])event.messageraw message, unsplit into params (in case you want to use your own encoding)
Methods
client.isCtcp(event)
Event test for CTCP formatted params. Returns true or false based on
detection of a CTCP formatted message in the event.
This is of use in any PRIVMSG or NOTICE event handlers, as they will still
propagate, showing up with params wrapped in \u0001 characters. So, to solve
this, use this to bail out of your handler and skip anything that will have
already emitted a ctcp_request or ctcp_response. Such as:
client.on('PRIVMSG', function(event){ if(client.isCtcp(event)) return; //... process PRIVMSG as normal ... } )
Formatting methods which proxy to client.send():
client.ctcpRequestSend(target, type, params)
sends a CTCP-encoded PRIVMSG
client.ctcpResponseSend(target, type, params)
sends a CTCP-encoded NOTICE
Usage is the same as main irc-connect methods, add your listener and then send
off the request.
You could use client.once() to connect the listener, however if any other
ctcp_response comes in out-of-order it will unbind the listener even though it
wasn't a matching event. So, the example below shows a method for handling the
.once() style manually.
With a little more you can add a setTimeout() to unbind your listener which is
decent practice, if not best practice. Usually the target will respond within a
second, or won't respond at all.
var target = 'someNickName' var type = 'VERSION' var resHandler = function(event){ //this is our fire escape (not the response we want) if((target !== event.nick) || type !== event.nick)) return //disconnect the emitter (this function, omg wormholes!) client.removeListener('ctcp_response',resHandler) //do whatever, in this case just bark the message to the console console.log('someNickName is using ' + event.message) } client.on('ctcp_response',resHandler) //string literals used for better illustration //could have just passed target/type vars client.ctcpRequestSend('someNickName','VERSION')
DCC Plugin client.use(ircCtcp.dcc)
You need to client.use(ircCtcp) before this, it won't load by itself as it
needs the CTCP ctcp_request event and the above two methods.
Setup and configurables
Access internal plugin options using methods
client.dccSetOption(key, value)
and
client.dccGetOption(key)
All settings are optional, there is a default provided for each one.
Banner support for new DCC CHAT connections
Default is 'DCC CHAT ready'
Set 'banner' with a string to auto-respond on the opening of a DCC CHAT.
Include '\n' for multiple line support. A trailing '\n' is assumed, do not
include one (it gets trimmed anyway!). You can also set this to false to
disable any outgoing greeting.
client.dccSetOption('banner','Welcome to DCC CHAT!\nPlease type back at me.')FutureDCC CHATconnections will receive:Welcome to DCC CHAT! Please type back at me.
File placement for incoming DCC SEND requests
Default is whatever 'os.tmpdir()' says on your system
Set 'targetPath' with a string to the place you'd like DCC SEND to put files.
client.dccSetOption('targetPath','/some/place/safe')
FutureDCC SENDrequests will have for example'/some/place/safe/Funny.mp3'inevent.filename
Events
This plugin consumes a 'ctcp_request' event, and emits events based on
event.type (chat or send). It assigns a "handle" (random string) to each new
session request, use this handle to identify the session to various methods.
Other DCC methods are not supported.
Events from 'CHAT' type requests
client.on('ctcp_dcc_chat_request',function(event){...})
A new CTCP DCC CHAT request has been received.
Where
origis the original'ctcp_request'event...
member content event.handlerandomly assigned internally, as a reference string/tag to this DCC request (ex: 'Z1LD02EWM')event.typefrom orig.params[0]always'CHAT'event.argumentfrom orig.params[1]generally always'chat'(ignored internally)event.addressfrom orig.params[2]target IP, converted from integer (ex:'127.3.2.1')event.portfrom orig.params[3]target port, as number (ex:4273)Call the
client.dccRequestAccept(handle)method with theevent.handleto confirm the connection.
In the rest of the events, event format is the same as the
'ctcp_dcc_chat_request' event, as the entire structure is emitted from the
sessions for every specific event, with possible additions.
client.on('ctcp_dcc_chat_error',function(event){...})
There has been an error, reason is in
event.message.
client.on('ctcp_dcc_chat_connecting',function(event){...})
The socket is connecting.
client.on('ctcp_dcc_chat_connect',function(event){...})
The socket has connected successfully.
client.on('ctcp_dcc_chat_message',function(event){...})
The remote side sent a line, which is in
event.message.
client.on('ctcp_dcc_chat_close',function(event){...})
The socket has closed. This can be either on purpose (other end closed the chat) or may be emitted alongside a
'ctcp_dcc_chat_error'event.
Events from 'SEND' type requests
client.on('ctcp_dcc_send_request',function(event){...})
A new CTCP DCC SEND request has been received.
Where
origis the original'ctcp_request'event...
member content event.handlerandomly assigned internally, as a reference string/tag to this DCC request (ex: 'byBfoSG')event.typefrom orig.params[0]always'SEND'event.argumentfrom orig.params[1]the source basename (ex:'Funny.mp3')event.addressfrom orig.params[2]target IP, converted from integer (ex:'127.3.2.1')event.portfrom orig.params[3]target port, as number (ex:4273)event.sizeoptional from orig.params[4]file size, as number (ex:1048576)event.filenamegenerated local target with full path (ex: '/home/lolcats/Documents/Funny.mp3'or'C:\\Users\\LOLcats\\Documents\\Funny.mp3')event.wrotehow many bytes have been received/written so far, always 0on the request eventCall the
client.dccRequestAccept(handle)method with theevent.handleto confirm the connection.
In the rest of the events, event format is the same as the
'ctcp_dcc_send_request' event, as the entire structure is emitted from the
sessions for every specific event, with possible additions.
client.on('ctcp_dcc_send_error',function(event){...})
There has been an error, reason is in
event.message.
client.on('ctcp_dcc_send_open',function(event){...})
The destination file (
event.filename) has been successfully created for writing.
client.on('ctcp_dcc_send_connecting',function(event){...})
The socket is connecting.
client.on('ctcp_dcc_send_connect',function(event){...})
The socket has connected successfully.
client.on('ctcp_dcc_send_progress',function(event){...})
Progress reports are a snapshot of the session sent every second on a timer, basically a snapshot of the session. Therefore
event.wroteshould be rising each time. Useevent.sizeif provided, to figure out and display progress or speed/completion estimations. There will always be at least one of these emitted.
client.on('ctcp_dcc_send_close',function(event){...})
The socket and file have closed. This can be either on purpose (other end closed the send) or may be emitted alongside a
'ctcp_dcc_send_error'event. Ifevent.wrotedoes not equalevent.size(if given) then something failed. Nobody knows what, there are no checksums (and sometimes not even expected size!) in this protocol. Some clients will send you a'NOTICE'event to tell you what happened, others don't.
Methods
client.dccRequestAccept(handle)
Signal that the connection session (referenced by 'event.handle') should be
accepted. If you do not want to connect just don't call this in the
'ctcp_dcc_*_request' handler. The request (and session) will expire 1 minute
later.
client.dccChatWrite(handle,message)
Send message as a line (auto-appends the newline) to the DCC CHAT session
identified by handle. If the session does not exist it will return false.
THE FOLLOWING METHODS ARE PRELIMINARY AND ARE NOT IN THE CODE YET
client.dccChatRequest(target)
client.dccChatRequest(target)client.dccSendRequest(target,filename)
client.dccSendRequest(target,filename)Both of these initiate an outgoing
DCC CHAT or DCC SEND request to the nick provided in target.The IP used is autoselected based on the one the IRC server says we have (which should bypass
NAT issues), and port is randomly selected by the system as port 0 is given to the listener.