Correctly using /quit command without reconnecting #567
[KiwiIRC.git] / server / clientcommands.js
1 var _ = require('lodash');
2
3
4
5
6 var ClientCommands = function (client) {
7 this.client = client;
8 };
9 module.exports = ClientCommands;
10
11 ClientCommands.prototype.run = function (command, args, irc_connection, callback) {
12 // Do we have a function to handle this command?
13 if (!listeners[command.toUpperCase()]) {
14 return;
15 }
16
17 return listeners[command.toUpperCase()](args, irc_connection, callback);
18 };
19
20 ClientCommands.prototype.addRpcEvents = function(client, rpc) {
21 // Called for each RPC call
22 // addRpcMethod() below prepends the incoming RPC call with the method name and
23 // the listener that handles this call, and passes that argument list to moduleEventWrap().
24 // This gives us the chance to wrap all calls with connection_id checks and passing
25 // them off to the module system.
26
27 var moduleEventWrap = function(rpc_method, the_fn, callback, connection_id) {
28 var connection, rpc_args, fn_args;
29
30 // Make sure we have a connection_id specified
31 if (!connection_id && connection_id !== 0) {
32 return callback('server not specified');
33
34 } else if (!client.state.irc_connections[connection_id]) {
35 return callback('not connected to server');
36 }
37
38 // The server this command is directed to
39 connection = client.state.irc_connections[connection_id];
40
41 // Get the arguments for the RPC call only (starts at 4)
42 rpc_args = Array.prototype.slice.call(arguments, 4);
43
44 global.modules.emit('rpc ' + rpc_method, {
45 arguments: rpc_args,
46 client: client,
47 connection: connection
48 })
49 .done(function() {
50 // Listeners expect arguments in a (connection, callback, args..n) format, so preppend
51 // the connection + callback
52 fn_args = rpc_args.slice(0);
53 fn_args.unshift(connection, callback);
54
55 the_fn.apply(client, fn_args);
56 })
57 .prevented(function() {
58 // The RPC call was prevented from running by a module
59 });
60 };
61
62 // Quick + easier way to call the above function
63 var addRpcMethod = function(rpc_method, fn) {
64 rpc.on(rpc_method, _.partial(moduleEventWrap, rpc_method, fn));
65 };
66
67 addRpcMethod('irc.privmsg', listeners.privmsg);
68 addRpcMethod('irc.ctcp', listeners.ctcp);
69 addRpcMethod('irc.raw', listeners.raw);
70 addRpcMethod('irc.join', listeners.join);
71 addRpcMethod('irc.channel_info', listeners.channel_info);
72 addRpcMethod('irc.part', listeners.part);
73 addRpcMethod('irc.topic', listeners.topic);
74 addRpcMethod('irc.kick', listeners.kick);
75 addRpcMethod('irc.quit', listeners.quit);
76 addRpcMethod('irc.notice', listeners.notice);
77 addRpcMethod('irc.mode', listeners.mode);
78 addRpcMethod('irc.nick', listeners.nick);
79 addRpcMethod('irc.kiwi', listeners.kiwi);
80 addRpcMethod('irc.encoding', listeners.encoding);
81 };
82
83
84
85
86 /**
87 * Truncate a string into blocks of a set size
88 */
89 function truncateString(str, block_size) {
90 block_size = block_size || 350;
91
92 var blocks = [],
93 current_pos;
94
95 for (current_pos = 0; current_pos < str.length; current_pos = current_pos + block_size) {
96 blocks.push(str.substr(current_pos, block_size));
97 }
98
99 return blocks;
100 }
101
102
103
104
105 var listeners = {
106 privmsg: function (irc_connection, callback, args) {
107 // Maximum length of target + message we can send to the IRC server is 500 characters
108 // but we need to leave extra room for the sender prefix so the entire message can
109 // be sent from the IRCd to the target without being truncated.
110 var blocks = truncateString(args.msg, 350);
111
112 blocks.forEach(function (block, idx) {
113 // Apply the callback on the last message only
114 var cb = (idx === blocks.length - 1) ?
115 callback :
116 undefined;
117
118 irc_connection.write('PRIVMSG ' + args.target + ' :' + block, cb);
119 });
120 },
121
122
123 ctcp: function (irc_connection, callback, args) {
124 if ((args.target) && (args.type)) {
125 if (args.is_request) {
126 irc_connection.write('PRIVMSG ' + args.target + ' :' + String.fromCharCode(1) + args.type.toUpperCase() + ' ' + args.params + String.fromCharCode(1), callback);
127 } else {
128 irc_connection.write('NOTICE ' + args.target + ' :' + String.fromCharCode(1) + args.type.toUpperCase() + ' ' + args.params + String.fromCharCode(1), callback);
129 }
130 }
131 },
132
133
134 raw: function (irc_connection, callback, args) {
135 irc_connection.write(args.data, callback);
136 },
137
138
139 join: function (irc_connection, callback, args) {
140 var channels, keys;
141 if (args.channel) {
142 channels = args.channel.split(",");
143 keys = (args.key) ? args.key.split(",") : [];
144 _.each(channels, function (chan, index) {
145 irc_connection.write('JOIN ' + chan + ' ' + (keys[index] || ''), callback);
146 });
147 }
148 },
149
150
151 channel_info: function (irc_connection, callback, args) {
152 if (args.channel) {
153 irc_connection.write('MODE ' + args.channel, callback);
154 }
155 },
156
157
158 part: function (irc_connection, callback, args) {
159 if (args.channel) {
160 _.each(args.channel.split(","), function (chan) {
161 irc_connection.write('PART ' + chan + (args.message ? ' :' + args.message : ''), callback);
162 });
163 }
164 },
165
166
167 topic: function (irc_connection, callback, args) {
168 if (args.channel) {
169 if (args.topic) {
170 irc_connection.write('TOPIC ' + args.channel + ' :' + args.topic, callback);
171 } else {
172 irc_connection.write('TOPIC ' + args.channel, callback);
173 }
174 }
175 },
176
177
178 kick: function (irc_connection, callback, args) {
179 if ((args.channel) && (args.nick)) {
180 irc_connection.write('KICK ' + args.channel + ' ' + args.nick + ' :' + args.reason, callback);
181 }
182 },
183
184
185 quit: function (irc_connection, callback, args) {
186 irc_connection.end('QUIT :' + (args.message||''));
187 },
188
189
190 notice: function (irc_connection, callback, args) {
191 // Maximum length of target + message we can send to the IRC server is 500 characters
192 // but we need to leave extra room for the sender prefix so the entire message can
193 // be sent from the IRCd to the target without being truncated.
194
195 var blocks = truncateString(args.msg, 350);
196 blocks.forEach(function (block, idx) {
197 // Apply the callback on the last message only
198 var cb = (idx === blocks.length - 1) ?
199 callback :
200 undefined;
201
202 irc_connection.write('NOTICE ' + args.target + ' :' + block, cb);
203 });
204 },
205
206
207 mode: function (irc_connection, callback, args) {
208 if ((args.target) && (args.mode)) {
209 irc_connection.write('MODE ' + args.target + ' ' + args.mode + ' ' + args.params, callback);
210 }
211 },
212
213
214 nick: function (irc_connection, callback, args) {
215 if (args.nick) {
216 irc_connection.write('NICK ' + args.nick, callback);
217 }
218 },
219
220
221 kiwi: function (irc_connection, callback, args) {
222 if ((args.target) && (args.data)) {
223 irc_connection.write('PRIVMSG ' + args.target + ': ' + String.fromCharCode(1) + 'KIWI ' + args.data + String.fromCharCode(1), callback);
224 }
225 },
226
227 encoding: function (irc_connection, callback, args) {
228 if (args.encoding) {
229 return callback(irc_connection.setEncoding(args.encoding));
230 }
231 }
232 };