*/\r
this.makeIrcConnection = function(connection_info, callback_fn) {\r
var server_info = {\r
- command: 'connect',\r
nick: connection_info.nick,\r
hostname: connection_info.host,\r
port: connection_info.port,\r
if (connection_info.options.encoding)\r
server_info.encoding = connection_info.options.encoding;\r
\r
- this.rpc.call('kiwi', server_info, function (err, server_num) {\r
+ this.rpc.call('kiwi.connect', server_info, function (err, server_num) {\r
if (!err) {\r
callback_fn && callback_fn(err, server_num);\r
\r
\r
\r
this.parseKiwi = function (command, data) {\r
- var client_info_data;\r
+ var args;\r
\r
this.trigger('kiwi:' + command, data);\r
this.trigger('kiwi', data);\r
switch (command) {\r
case 'connected':\r
// Send some info on this client to the server\r
- client_info_data = {\r
- command: 'client_info',\r
+ args = {\r
build_version: _kiwi.global.build_version\r
};\r
- this.rpc.call('kiwi', client_info_data);\r
+ this.rpc.call('kiwi.client_info', args);\r
\r
this.connect_callback && this.connect_callback();\r
delete this.connect_callback;\r
break;\r
}\r
};\r
- /*\r
- Events:\r
- msg\r
- action\r
- server_connect\r
- options\r
- motd\r
- notice\r
- userlist\r
- nick\r
- join\r
- topic\r
- part\r
- kick\r
- quit\r
- whois\r
- syncchannel_redirect\r
- debug\r
- */\r
+\r
/**\r
* Parses the response from the server\r
*/\r
this.parse = function (command, data) {\r
- //console.log('gateway event', command, data);\r
-\r
- if (command !== undefined) {\r
- switch (command) {\r
- case 'options':\r
- $.each(data.options, function (name, value) {\r
- switch (name) {\r
- case 'CHANTYPES':\r
- that.set('channel_prefix', value.join(''));\r
- break;\r
- case 'NETWORK':\r
- that.set('name', value);\r
- break;\r
- case 'PREFIX':\r
- that.set('user_prefixes', value);\r
- break;\r
- }\r
- });\r
- that.set('cap', data.cap);\r
- break;\r
-\r
- /*\r
- case 'sync':\r
- if (_kiwi.gateway.onSync && _kiwi.gateway.syncing) {\r
- _kiwi.gateway.syncing = false;\r
- _kiwi.gateway.onSync(item);\r
- }\r
- break;\r
- */\r
-\r
- case 'kiwi':\r
- this.emit('_kiwi.' + data.namespace, data.data);\r
- break;\r
- }\r
- }\r
-\r
\r
// Trigger the connection specific events (used by Network objects)\r
if (typeof data.connection_id !== 'undefined') {\r
that.trigger(command, data);\r
};\r
\r
- /**\r
- * Sends data to the server\r
- * @private\r
- * @param {Object} data The data to send\r
- * @param {Function} callback A callback function\r
- */\r
- this.sendData = function (connection_id, data, callback) {\r
- if (typeof connection_id === 'undefined' || connection_id === null)\r
- connection_id = _kiwi.app.connections.active_connection.get('connection_id');\r
+ this.rpcCall = function(method, connection_id) {\r
+ var args = Array.prototype.slice.call(arguments, 0);\r
\r
- var data_buffer = {\r
- connection_id: connection_id,\r
- data: JSON.stringify(data)\r
- };\r
- this.rpc.call('irc', data_buffer, callback);\r
+ if (typeof args[1] === 'undefined' || args[1] === null)\r
+ args[1] = _kiwi.app.connections.active_connection.get('connection_id');\r
+\r
+ return this.rpc.call.apply(this.rpc, args);\r
};\r
\r
/**\r
* @param {Function} callback A callback function\r
*/\r
this.privmsg = function (connection_id, target, msg, callback) {\r
- var data = {\r
- method: 'privmsg',\r
- args: {\r
- target: target,\r
- msg: msg\r
- }\r
+ var args = {\r
+ target: target,\r
+ msg: msg\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.privmsg', connection_id, args, callback);\r
};\r
\r
/**\r
* @param {Function} callback A callback function\r
*/\r
this.notice = function (connection_id, target, msg, callback) {\r
- var data = {\r
- method: 'notice',\r
- args: {\r
- target: target,\r
- msg: msg\r
- }\r
+ var args = {\r
+ target: target,\r
+ msg: msg\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.notice', connection_id, args, callback);\r
};\r
\r
/**\r
* @param {Function} callback A callback function\r
*/\r
this.ctcp = function (connection_id, request, type, target, params, callback) {\r
- var data = {\r
- method: 'ctcp',\r
- args: {\r
- request: request,\r
- type: type,\r
- target: target,\r
- params: params\r
- }\r
+ var args = {\r
+ request: request,\r
+ type: type,\r
+ target: target,\r
+ params: params\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.ctcp', connection_id, args, callback);\r
};\r
\r
/**\r
* @param {Function} callback A callback function\r
*/\r
this.join = function (connection_id, channel, key, callback) {\r
- var data = {\r
- method: 'join',\r
- args: {\r
- channel: channel,\r
- key: key\r
- }\r
+ var args = {\r
+ channel: channel,\r
+ key: key\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.join', connection_id, args, callback);\r
};\r
\r
/**\r
* Retrieves channel information\r
*/\r
this.channelInfo = function (connection_id, channel, callback) {\r
- var data = {\r
- method: 'channel_info',\r
- args: {\r
- channel: channel\r
- }\r
+ var args = {\r
+ channel: channel\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.channel_info', connection_id, args, callback);\r
};\r
\r
/**\r
* @param {Function} callback A callback function\r
*/\r
this.part = function (connection_id, channel, callback) {\r
- var data = {\r
- method: 'part',\r
- args: {\r
- channel: channel\r
- }\r
+ var args = {\r
+ channel: channel\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.part', connection_id, args, callback);\r
};\r
\r
/**\r
* @param {Function} callback A callback function\r
*/\r
this.topic = function (connection_id, channel, new_topic, callback) {\r
- var data = {\r
- method: 'topic',\r
- args: {\r
- channel: channel,\r
- topic: new_topic\r
- }\r
+ var args = {\r
+ channel: channel,\r
+ topic: new_topic\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.topic', connection_id, args, callback);\r
};\r
\r
/**\r
* @param {Function} callback A callback function\r
*/\r
this.kick = function (connection_id, channel, nick, reason, callback) {\r
- var data = {\r
- method: 'kick',\r
- args: {\r
- channel: channel,\r
- nick: nick,\r
- reason: reason\r
- }\r
+ var args = {\r
+ channel: channel,\r
+ nick: nick,\r
+ reason: reason\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.kick', connection_id, args, callback);\r
};\r
\r
/**\r
*/\r
this.quit = function (connection_id, msg, callback) {\r
msg = msg || "";\r
- var data = {\r
- method: 'quit',\r
- args: {\r
- message: msg\r
- }\r
+\r
+ var args = {\r
+ message: msg\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.quit', connection_id, args, callback);\r
};\r
\r
/**\r
* @param {Function} callback A callback function\r
*/\r
this.raw = function (connection_id, data, callback) {\r
- data = {\r
- method: 'raw',\r
- args: {\r
- data: data\r
- }\r
+ var args = {\r
+ data: data\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.raw', connection_id, args, callback);\r
};\r
\r
/**\r
* @param {Function} callback A callback function\r
*/\r
this.changeNick = function (connection_id, new_nick, callback) {\r
- var data = {\r
- method: 'nick',\r
- args: {\r
- nick: new_nick\r
- }\r
+ var args = {\r
+ nick: new_nick\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.nick', connection_id, args, callback);\r
};\r
\r
/**\r
* Sets a mode for a target\r
*/\r
this.mode = function (connection_id, target, mode_string, callback) {\r
- data = {\r
- method: 'raw',\r
- args: {\r
- data: 'MODE ' + target + ' ' + mode_string\r
- }\r
+ var args = {\r
+ data: 'MODE ' + target + ' ' + mode_string\r
};\r
\r
- this.sendData(connection_id, data, callback);\r
+ this.rpcCall('irc.raw', connection_id, args, callback);\r
};\r
\r
\r
* @param {Fucntion} callback A callback function\r
*/\r
this.setEncoding = function (connection_id, new_encoding, callback) {\r
- var data = {\r
- method: 'encoding',\r
- args: {\r
- encoding: new_encoding\r
- }\r
- };\r
- this.sendData(connection_id, data, callback);\r
- };\r
-\r
- /**\r
- * Sends data to a fellow Kiwi IRC user\r
- * @param {String} target The nick of the Kiwi IRC user to send to\r
- * @param {String} data The data to send\r
- * @param {Function} callback A callback function\r
- */\r
- this.kiwi = function (target, data, callback) {\r
- data = {\r
- method: 'kiwi',\r
- args: {\r
- target: target,\r
- data: data\r
- }\r
+ var args = {\r
+ encoding: new_encoding\r
};\r
\r
- this.sendData(data, callback);\r
+ this.rpcCall('irc.encoding', connection_id, args, callback);\r
};\r
\r
\r
// Handler for any commands sent from the client
this.client_commands = new ClientCommands(this);
+ this.client_commands.addRpcEvents(this, this.rpc);
+
+ // Handles the kiwi.* RPC functions
+ this.attachKiwiCommands();
- this.rpc.on('irc', function (response, data) {
- handleClientMessage.call(that, data, response);
- });
- this.rpc.on('kiwi', function (response, data) {
- kiwiCommand.call(that, data, response);
- });
websocket.on('close', function () {
websocketDisconnect.apply(that, arguments);
});
this.removeAllListeners();
};
-function handleClientMessage(msg, callback) {
- var that = this,
- server;
-
- // Make sure we have a server number specified
- if ((msg.connection_id === null) || (typeof msg.connection_id !== 'number')) {
- return (typeof callback === 'function') ? callback('server not specified') : undefined;
- } else if (!this.state.irc_connections[msg.connection_id]) {
- return (typeof callback === 'function') ? callback('not connected to server') : undefined;
- }
-
- // The server this command is directed to
- server = this.state.irc_connections[msg.connection_id];
-
- if (typeof callback !== 'function') {
- callback = null;
- }
-
- try {
- msg.data = JSON.parse(msg.data);
- } catch (e) {
- kiwi.log('[handleClientMessage] JSON parsing error ' + msg.data);
- return;
- }
-
- // Run the client command
- global.modules.emit('client command', {
- command: msg.data,
- server: server
- })
- .done(function() {
- that.client_commands.run(msg.data.method, msg.data.args, server, callback);
- });
-}
-
-
-
-function kiwiCommand(command, callback) {
- if (typeof callback !== 'function') {
- callback = function () {};
- }
- switch (command.command) {
- case 'connect':
- if (command.hostname && command.port && command.nick) {
- var options = {};
-
- // Get any optional parameters that may have been passed
- if (command.encoding)
- options.encoding = command.encoding;
-
- options.password = global.config.restrict_server_password || command.password;
-
- this.state.connect(
- (global.config.restrict_server || command.hostname),
- (global.config.restrict_server_port || command.port),
- (typeof global.config.restrict_server_ssl !== 'undefined' ?
- global.config.restrict_server_ssl :
- command.ssl),
- command.nick,
- {hostname: this.websocket.meta.revdns, address: this.websocket.meta.real_address},
- options,
- callback);
- } else {
- return callback('Hostname, port and nickname must be specified');
- }
+Client.prototype.attachKiwiCommands = function() {
+ var that = this;
- break;
+ this.rpc.on('kiwi.connect', function(callback, command) {
+ if (command.hostname && command.port && command.nick) {
+ var options = {};
+
+ // Get any optional parameters that may have been passed
+ if (command.encoding)
+ options.encoding = command.encoding;
+
+ options.password = global.config.restrict_server_password || command.password;
+
+ that.state.connect(
+ (global.config.restrict_server || command.hostname),
+ (global.config.restrict_server_port || command.port),
+ (typeof global.config.restrict_server_ssl !== 'undefined' ?
+ global.config.restrict_server_ssl :
+ command.ssl),
+ command.nick,
+ {hostname: that.websocket.meta.revdns, address: that.websocket.meta.real_address},
+ options,
+ callback);
+ } else {
+ return callback('Hostname, port and nickname must be specified');
+ }
+ });
- case 'client_info':
- // keep hold of selected parts of the client_info
- this.client_info = {
- build_version: command.build_version.toString() || undefined
- };
- break;
+ this.rpc.on('kiwi.client_info', function(callback, args) {
+ // keep hold of selected parts of the client_info
+ that.client_info = {
+ build_version: args.build_version.toString() || undefined
+ };
+ });
+};
- default:
- callback();
- }
-}
// Websocket has disconnected, so quit all the IRC connections
return listeners[command.toUpperCase()](args, irc_connection, callback);\r
};\r
\r
+ClientCommands.prototype.addRpcEvents = function(client, rpc) {\r
+ // Called for each RPC call\r
+ // addRpcMethod() below prepends the incoming RPC call with the method name and\r
+ // the listener that handles this call, and passes that argument list to moduleEventWrap().\r
+ // This gives us the chance to wrap all calls with connection_id checks and passing\r
+ // them off to the module system.\r
+\r
+ var moduleEventWrap = function(rpc_method, the_fn, callback, connection_id) {\r
+ var connection, rpc_args, fn_args;\r
+\r
+ // Make sure we have a connection_id specified\r
+ if (!connection_id && connection_id !== 0) {\r
+ return callback('server not specified');\r
+\r
+ } else if (!client.state.irc_connections[connection_id]) {\r
+ return callback('not connected to server');\r
+ }\r
+\r
+ // The server this command is directed to\r
+ connection = client.state.irc_connections[connection_id];\r
+\r
+ // Get the arguments for the RPC call only (starts at 4)\r
+ rpc_args = Array.prototype.slice.call(arguments, 4);\r
+\r
+ global.modules.emit('rpc ' + rpc_method, {\r
+ arguments: rpc_args,\r
+ client: client,\r
+ connection: connection\r
+ })\r
+ .done(function() {\r
+ // Listeners expect arguments in a (connection, callback, args..n) format, so preppend\r
+ // the connection + callback\r
+ fn_args = rpc_args.slice(0);\r
+ fn_args.unshift(connection, callback);\r
+\r
+ the_fn.apply(client, fn_args);\r
+ })\r
+ .prevented(function() {\r
+ // The RPC call was prevented from running by a module\r
+ });\r
+ };\r
+\r
+ // Quick + easier way to call the above function\r
+ var addRpcMethod = function(rpc_method, fn) {\r
+ rpc.on(rpc_method, _.partial(moduleEventWrap, rpc_method, fn));\r
+ };\r
+\r
+ addRpcMethod('irc.privmsg', listeners.privmsg);\r
+ addRpcMethod('irc.ctcp', listeners.ctcp);\r
+ addRpcMethod('irc.raw', listeners.raw);\r
+ addRpcMethod('irc.join', listeners.join);\r
+ addRpcMethod('irc.channel_info', listeners.channel_info);\r
+ addRpcMethod('irc.part', listeners.part);\r
+ addRpcMethod('irc.topic', listeners.topic);\r
+ addRpcMethod('irc.kick', listeners.kick);\r
+ addRpcMethod('irc.quit', listeners.quit);\r
+ addRpcMethod('irc.notice', listeners.notice);\r
+ addRpcMethod('irc.mode', listeners.mode);\r
+ addRpcMethod('irc.nick', listeners.nick);\r
+ addRpcMethod('irc.kiwi', listeners.kiwi);\r
+ addRpcMethod('irc.encoding', listeners.encoding);\r
+};\r
+\r
\r
\r
\r
\r
\r
var listeners = {\r
- PRIVMSG: function (args, irc_connection, callback) {\r
+ privmsg: function (irc_connection, callback, args) {\r
// Maximum length of target + message we can send to the IRC server is 500 characters\r
// but we need to leave extra room for the sender prefix so the entire message can\r
// be sent from the IRCd to the target without being truncated.\r
-\r
var blocks = truncateString(args.msg, 350);\r
+\r
blocks.forEach(function (block, idx) {\r
// Apply the callback on the last message only\r
var cb = (idx === blocks.length - 1) ?\r
},\r
\r
\r
- CTCP: function (args, irc_connection, callback) {\r
+ ctcp: function (irc_connection, callback, args) {\r
if ((args.target) && (args.type)) {\r
if (args.request) {\r
irc_connection.write('PRIVMSG ' + args.target + ' :' + String.fromCharCode(1) + args.type.toUpperCase() + ' ' + args.params + String.fromCharCode(1), callback);\r
},\r
\r
\r
- RAW: function (args, irc_connection, callback) {\r
+ raw: function (irc_connection, callback, args) {\r
irc_connection.write(args.data, callback);\r
},\r
\r
\r
- JOIN: function (args, irc_connection, callback) {\r
+ join: function (irc_connection, callback, args) {\r
+ var channels, keys;\r
if (args.channel) {\r
channels = args.channel.split(",");\r
keys = (args.key) ? args.key.split(",") : [];\r
},\r
\r
\r
- CHANNEL_INFO: function (args, irc_connection, callback) {\r
+ channel_info: function (irc_connection, callback, args) {\r
if (args.channel) {\r
irc_connection.write('MODE ' + args.channel, callback);\r
}\r
},\r
\r
\r
- PART: function (args, irc_connection, callback) {\r
+ part: function (irc_connection, callback, args) {\r
if (args.channel) {\r
_.each(args.channel.split(","), function (chan) {\r
irc_connection.write('PART ' + chan, callback);\r
},\r
\r
\r
- TOPIC: function (args, irc_connection, callback) {\r
+ topic: function (irc_connection, callback, args) {\r
if (args.channel) {\r
if (args.topic) {\r
irc_connection.write('TOPIC ' + args.channel + ' :' + args.topic, callback);\r
},\r
\r
\r
- KICK: function (args, irc_connection, callback) {\r
+ kick: function (irc_connection, callback, args) {\r
if ((args.channel) && (args.nick)) {\r
irc_connection.write('KICK ' + args.channel + ' ' + args.nick + ' :' + args.reason, callback);\r
}\r
},\r
\r
\r
- QUIT: function (args, irc_connection, callback) {\r
+ quit: function (irc_connection, callback, args) {\r
websocket.ircConnection.end('QUIT :' + args.message + '\r\n');\r
websocket.sentQUIT = true;\r
websocket.ircConnection.destroySoon();\r
},\r
\r
\r
- NOTICE: function (args, irc_connection, callback) {\r
+ notice: function (irc_connection, callback, args) {\r
// Maximum length of target + message we can send to the IRC server is 500 characters\r
// but we need to leave extra room for the sender prefix so the entire message can\r
// be sent from the IRCd to the target without being truncated.\r
},\r
\r
\r
- MODE: function (args, irc_connection, callback) {\r
+ mode: function (irc_connection, callback, args) {\r
if ((args.target) && (args.mode)) {\r
irc_connection.write('MODE ' + args.target + ' ' + args.mode + ' ' + args.params, callback);\r
}\r
},\r
\r
\r
- NICK: function (args, irc_connection, callback) {\r
+ nick: function (irc_connection, callback, args) {\r
if (args.nick) {\r
irc_connection.write('NICK ' + args.nick, callback);\r
}\r
},\r
\r
\r
- KIWI: function (args, irc_connection, callback) {\r
+ kiwi: function (irc_connection, callback, args) {\r
if ((args.target) && (args.data)) {\r
irc_connection.write('PRIVMSG ' + args.target + ': ' + String.fromCharCode(1) + 'KIWI ' + args.data + String.fromCharCode(1), callback);\r
}\r
},\r
\r
- ENCODING: function (args, irc_connection, callback) {\r
+ encoding: function (irc_connection, callback, args) {\r
if (args.encoding) {\r
return callback(irc_connection.setEncoding(args.encoding));\r
}\r