-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathircClient.jsmod
More file actions
330 lines (249 loc) · 11.8 KB
/
Copy pathircClient.jsmod
File metadata and controls
330 lines (249 loc) · 11.8 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
/* ***** BEGIN LICENSE BLOCK *****
* Version: GNU GPL 3.0
*
* The contents of this file are subject to the
* GNU General Public License Version 3.0; you may not use this file except
* in compliance with the License. You may obtain a copy of the License at
* http://www.gnu.org/licenses/gpl.html
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
* ***** END LICENSE BLOCK ***** */
(function IrcBotModule($D, $A, $S) {
var _name = this.name = this.constructor.name;
var _numericCommand = Exec('NumericCommand.js');
var _connection;
// this.hasFinished = function() !_connection;
function Disconnect() {
_connection.Disconnect(); // make a Gracefully disconnect ( disconnect != close )
}
function Connect() {
var _receiveBuffer = new Buffer();
function OnData(connection) {
_receiveBuffer.Write(connection.Read());
var message;
while ( (message = _receiveBuffer.ReadUntil(CRLF)) ) {
log.Write( LOG_IRCMSG, 'in', message);
try {
// message = [ ":" prefix SPACE ] command [ params ] crlf
// prefix = servername / ( nickname [ [ "!" user ] "@" host ] )
// command = 1*letter / 3digit
// params = *14( SPACE middle ) [ SPACE ":" trailing ]
// =/ 14( SPACE middle ) [ SPACE [ ":" ] trailing ]
// nospcrlfcl = %x01-09 / %x0B-0C / %x0E-1F / %x21-39 / %x3B-FF
// ; any octet except NUL, CR, LF, " " and ":"
// middle = nospcrlfcl *( ":" / nospcrlfcl )
// trailing = *( ":" / " " / nospcrlfcl )
// The prefix is used by servers to indicate the true origin of the message.
var prefix = message.indexOf( ':' );
var trailing = message.indexOf( ':', 1 );
var args = message.substring( prefix ? 0 : 1, (trailing > 0) ? trailing-1 : message.length ).split(' ');
if ( prefix != 0 ) // If the prefix is missing from the message, it is assumed to have originated from the connection from which it was received from.
args.unshift( undefined );
if ( trailing != 0 )
args.push( message.substring( trailing + 1 ) );
if ( !isNaN(args[1]) )
args[1] = _numericCommand[parseInt(args[1])] || parseInt(args[1]);
args.splice(1, 0, args.shift()); // move the command name to the first place.
args.unshift('ircMsg'); // add the module message type at the first place
$A.FireModuleListener.apply( null, args );
if ( args[1] == 'RPL_WELCOME' )
$S.Enter('STATE_IRC_INTERACTIVE');
} catch (ex if ex == ERR) {
ReportError('Invalid IRC server message', message);
}
}
}
function OnDisconnected( remotelyDisconnected ) {
remotelyDisconnected && ReportWarning( 'Remotely disconnected' );
!remotelyDisconnected && ReportNotice( 'Locally disconnected' );
$S.Leave('STATE_IRC_INTERACTIVE');
$S.Leave('STATE_IRC_CONNECTED');
_connection.Close();
// _connection = undefined; // this is the end
// (TBD) retry if remotelyDisconnected ?
}
function OnConnected() {
ReportNotice( 'Connection established' );
$S.Enter('STATE_IRC_CONNECTED');
setData( $D.sockName, _connection.sockName );
setData( $D.sockPort, _connection.sockPort );
setData( $D.peerPort, _connection.peerPort );
setData( $D.peerName, _connection.peerName );
_connection.OnData = OnData;
_connection.OnDisconnected = OnDisconnected;
getData($D.ident) && Ident( io, function(identRequest) identRequest + ' : '+getData($D.ident.userid)+' : '+getData($D.ident.opsys)+' : '+getData($D.nick)+CRLF, 2*SECOND ); // let 2 seconds to the server to make the IDENT request
}
var getServer = new function() {
for (;;) {
ReportError('starting/repeating IRC server list.');
for ( var j = 0; j < getData($D.serverRetry); j++ )
for each ( let serverInfo in getData($D.serverList) ) {
var [server, ports] = serverInfo.split(':', 2);
for each ( let port in ExpandStringRanges(ports) )
for ( var i = 0; i < getData($D.serverRetry); i++ )
yield [server, port];
}
}
}
function TryNextServer() {
var [host, port] = getServer.next();
ReportNotice( 'Trying to connect to ' + host + ':' + port );
_connection = new TCPConnection(host, port);
_connection.OnFailed = function() {
ReportError('Failed to connect to ' + host + ':' + port );
io.AddTimeout( getData($D.serverRetryPause), TryNextServer );
}
_connection.OnConnected = OnConnected;
_connection.Connect( getData($D.connectTimeout) );
setData( $D.connectTime, IntervalNow() );
}
TryNextServer();
}
function RawDataSender(buf) {
log.Write( LOG_IRCMSG, 'out', buf );
_connection.Write(buf); // && Failed('Unable to send (more) data.'); (TBD) This failure must be detected elsewhere
setData( $D.lastMessageTime, IntervalNow() );
}
// this.Send = MakeFloodSafeMessageSender( getData($D.antiflood.maxMessage), getData($D.antiflood.maxBytes), getData($D.antiflood.interval), RawDataSender, $S );
function SyncWait() function(callback) {
var events, sync = RandomNumber(5), time = Now()
events = { ircMsg: { PONG: function( tmp, command, from, server, data ) {
if ( data != sync )
return;
$A.RemoveModuleListener(events);
callback(Now()-time);
}}};
$A.AddModuleListener(events);
RawDataSender( 'PING '+sync+CRLF );
}
var _messageQueue = [];
var _messageEvent = new Event();
var sendProcedure = new AsyncProcHelper( function() {
var data, interval, length = 0, time = 0, sentNodifyList = [];
for (;;) {
if ( _messageQueue.length == 0 ) // no need to wait if messages are pending
yield AsyncEventWait(_messageEvent);
interval = Now() - time;
time = Now();
data = '';
while ( _messageQueue.length ) {
var [message, highPriorityMessage, OnSent] = _messageQueue[0]; // peek
if ( message.length >= getData($D.maxMessageLength) )
Failed('Message too long');
if ( data.length + message.length >= getData($D.maxMessageLength) ) // we cannot append this message
break;
data += message;
sentNodifyList.push(OnSent);
_messageQueue.shift();
}
var _monitorPeriod = getData($D.antiflood.monitorPeriod);
var _messageOverload = getData($D.antiflood.messageOverload);
length = Math.floor(length * (interval < _monitorPeriod ? 1 - interval / _monitorPeriod : 0)) + _messageOverload + data.length;
if ( length > getData($D.antiflood.maxLength) && !highPriorityMessage ) { // if the rate is too high, test if we are flooding
length += _messageOverload + 12; // PING message length
let [syncTime] = yield SyncWait();
if ( syncTime > 5000 && length > 0 )
yield AsyncSleep(5000);
}
RawDataSender(data);
while ( sentNodifyList.length )
sentNodifyList.shift()();
}
} );
// // // // // // //
this.moduleApi = {
Send:function(message, highPriorityMessage, OnSent) {
if ( message instanceof Array )
message = message.join(CRLF);
message += CRLF;
if ( message.length >= getData($D.maxMessageLength) ) {
//Failed('Message too long');
ReportError('Message too long');
return false;
}
OnSent = OnSent||Noop;
if ( highPriorityMessage )
_messageQueue.unshift([message, highPriorityMessage, OnSent]);
else
_messageQueue.push([message, highPriorityMessage, OnSent]);
_messageEvent.Fire();
return true;
}
};
this.stateListener = [
{
set: function(s) s[STATE_RUNNING] && s[_name],
reset: function(s) !s[STATE_RUNNING] || !s[_name],
trigger: function(polarity) {
if ( polarity )
Connect();
else
Disconnect();
sendProcedure.Toggle(polarity);
}
}
];
})
/*
http://www.irchelp.org/irchelp/misc/ccosmos.html
DCC protocol (Direct Client Connection):
http://www.irchelp.org/irchelp/rfc/dccspec.html
CTCP Protocol (Client-To-Client Protocol)
http://www.irchelp.org/irchelp/rfc/ctcpspec.html
http://mathieu-lemoine.developpez.com/tutoriels/irc/ctcp/
misc links:
modes infos/servers: http://www.alien.net.au/irc/chanmodes.html
http://www.irchelp.org/irchelp/rfc/rfc2811.txt
http://www.croczilla.com/~alex/reference/javascript_ref/object.html
http://www.js-examples.com/javascript/core_js15/obj.php
CTCP: http://www.irchelp.org/irchelp/rfc/ctcpspec.html
6.9 Characters on IRC
For chatting in channels, anything that can be translated to ASCII gets through. (ASCII is a standard way to express characters) Note however,
that since the parts of the ASCII table may be country-specific,
your ASCII-art may not turn out as well for others. Fancy fonts will only show up on your own computer.
You can use character map (charmap.exe) in windows to view the ASCII table.
Channelnames: After the initial #, & or +, all characters except NUL (\0), BELL (\007), CR (\r), LF (\n) a space or a comma
Colons in channelnames are valid for ircu but may be reserved for other purposes on other nets.
Nicks: The allowed characters are a to z, A to Z, 0 to 9 and [ ] { } [ ] { } \ | ^ ` - _
This is the same as saying that - and the characters 0 to 9 and A to } in the ASCII table are allowed.
The first character in a nick cannot be a - or a number.
The characters { } | ^ are considered the lower case equivalents of [ ] \ ~ respectively. This is said to be because of IRCs scandinavian origin,
but while scandinavians will notice that Æ,Ø,Å is forced lowercase in channelnames, Å and Ä is not equivalent with each other or
with any of { } | ^. This bear the mark of a backward-compatible ircu feature, and how non-american letters are treated on IRC may vary between nets.
A weird side-effect happens when trying to ban people with these characters in the name.
Trying to ban the nick ac\dc, *|*!*@* will work where *\*!*@* fails. *ac\dc*!*@* works just fine too.
*/
/* Character codes
...
Because of IRC's Scandinavian origin, the characters {}|^ are
considered to be the lower case equivalents of the characters []\~,
respectively. This is a critical issue when determining the
equivalence of two nicknames or channel names.
*/
/* IRC Messages
Each IRC message may consist of up to three main parts: the prefix
(OPTIONAL), the command, and the command parameters (maximum of
fifteen (15)). The prefix, command, and all parameters are separated
by one ASCII space character (0x20) each.
The presence of a prefix is indicated with a single leading ASCII
colon character (':', 0x3b), which MUST be the first character of the
message itself. There MUST be NO gap (whitespace) between the colon
and the prefix. The prefix is used by servers to indicate the true
origin of the message. If the prefix is missing from the message, it
is assumed to have originated from the connection from which it was
received from. Clients SHOULD NOT use a prefix when sending a
message; if they use one, the only valid prefix is the registered
nickname associated with the client.
The command MUST either be a valid IRC command or a three (3) digit
number represented in ASCII text.
IRC messages are always lines of characters terminated with a CR-LF
(Carriage Return - Line Feed) pair, and these messages SHALL NOT
exceed 512 characters in length, counting all characters including
the trailing CR-LF. Thus, there are 510 characters maximum allowed
for the command and its parameters. There is no provision for
continuation of message lines. See section 6 for more details about
current implementations.
*/