Skip to content

Instantly share code, notes, and snippets.

@SchizoDuckie
Created August 2, 2013 08:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save SchizoDuckie/6138223 to your computer and use it in GitHub Desktop.
Save SchizoDuckie/6138223 to your computer and use it in GitHub Desktop.
JS IRC Client using Adobe Air for sockets.
IRC = new Class({
Implements: [Options, Events],
options: {
server: 'irc.tweakers.net',
port: 6667,
host: 'localhost',
password: false,
nick: 'SchizoIRC',
userName: 'SchizoIRC',
realName: 'SchizoIRC v0.2',
finger: 'Oh yeah, handle me like one of your french maids bebeh!',
autoJoinChannels: '#botjestest',
autoJoinOnInvite: true
},
connection: false,
server: false,
channels: false,
initialize: function(options) {
this.setOptions(options, this.options);
if(!this.connection) {
this.connection = new IRC.Connection(this.options);
}
},
connect: function() {
this.connection.connect();
},
disconnect: function() {
this.connection.disconnect();
}
});
IRC.Connection = new Class({
Implements: [Options, Events],
Binds: ['onStatus', 'onConnect', 'createSocket','onSocketStatus','onSocketData', 'autoReconnect','connectionError','destroy','send'],
monitor: false,
socket: false,
autoPing: true,
pingTimeout: 60000,
connected: false,
established: false,
accepted: false,
ctcpTimeout: 5000,
autoReconnect: true,
dataPackage: '',
eventChannels : { /* The mapping for individual event channels for this connection. */
AVAILABLE : false,
LOST: false,
ERROR: false,
DATAAVAILABLE: false,
SEND: false,
CLOSE: false
},
initialize: function(options) {
this.setOptions(options);
this.eventChannels.AVAILABLE = '/connection/'+options.server+'/available';
this.eventChannels.LOST = '/connection/'+options.server+'/lost';
this.eventChannels.ERROR = '/connection/'+options.server+'/error';
this.eventChannels.DATAAVAILABLE = '/connection/'+options.server+'/data';
this.eventChannels.SEND = '/connection/'+options.server+'/send';
this.eventChannels.CLOSE = '/connection/'+options.server+'/close';
document.addEvent(this.eventChannels.AVAILABLE, this.createSocket);
document.addEvent(this.eventChannels.LOST, this.disconnected);
document.addEvent(this.eventChannels.ERROR, this.connectionError);
document.addEvent(this.eventChannels.SEND, this.send);
document.addEvent('/connection/destroy', this.destroy);
},
connect: function() {
console.log("[IRC.Connection]: Connecting to ",this.options.server);
if(this.connected) return;
if(!this.monitor) {
this.monitor = new air.SocketMonitor(this.options.server, this.options.port);
this.monitor.addEventListener(air.StatusEvent.STATUS, this.onSocketStatus);
}
if(!this.socket) {
this.socket = new air.Socket();
this.socket.addEventListener(air.Event.CONNECT, this.onConnect);
this.socket.addEventListener(air.ProgressEvent.SOCKET_DATA, this.onSocketData);
}
this.monitor.start();
},
createSocket: function() {
console.log('in createSOcket');
if(this.monitor.available) {
console.log("Socket status avaialble, connecting socket.");
this.socket.connect(this.options.server, this.options.port);
}
},
onSocketStatus: function(evt) {
console.log("[IRC.Connection] Socket status changed: " , evt);
var status = this.monitor.available;
if (status && this.autoReconnect && !this.connected) {
document.fireEvent(this.eventChannels.AVAILABLE);
} else if (!status && this.stayConnected && this._isConnected) {
document.fireEvent(this.eventChannels.LOST);
} else if (!status && this.stayConnected) {
document.fireEvent(this.eventChannels.ERROR, [evt]);
}
},
autoReconnect: function() {
console.log("Connection lost, auto reconnecting.[TODO]");
},
onConnect: function () {
console.log("[IRC.Connection] Connected!");
if(!this.server) {
this.server = new IRC.Server(this.options, this.eventChannels);
}
this.connected = true;
},
send: function (data) {
if(this.socket && this.socket.connected) {
this.socket.writeUTFBytes(data+"\r\n");
this.socket.flush();
console.log("[IRC.Connection] OUT: ", data);
} else {
this.fireEvent(this.eventChannels.LOST, ['Could not send data due to connection lost.', data]);
}
},
onSocketData: function () {
var data, endLine;
if (!this.connected) { return; }
this.dataPackage += this.socket.readUTFBytes(this.socket.bytesAvailable);
console.log("DataPackage received: ", this.dataPackage);
endLine = this.dataPackage.lastIndexOf("\r\n");
if(endLine > -1) {
var messages = this.dataPackage.substr(0, endLine).split("\r\n"); // grab the messages that came in so far and split them by line.
for(i=0; i<messages.length; i++) {
document.fireEvent(this.eventChannels.DATAAVAILABLE, [new IRC.Server.Message(messages[i])]);
}
this.dataPackage = this.dataPackage.substr(endLine +2); // preseve the rest of the queue.
}
if (this.socket && this.socket.connected) {
this.socket.flush();
}
},
closeSocket: function () {
console.log("[IRC.Connection] Closing socket.");
if (this.socket && this.socket.connected) {
this.socket.close();
}
if (this.monitor && this.monitor.running) {
this.monitor.stop();
}
},
closeConnection: function (msg) {
msg = msg || 'Connection closed.';
this.stayConnected = false;
this.connected = false;
this.established = false;
this.accepted = false;
this.closeSocket();
document.fireEvent('/connection/'+this.options.server+'/quit', ['Quit: '+msg]);
},
destroy: function (data) {
this.closeConnection();
this.socket = false;
this.socketMonitor = false;
console.log("[IRC.Connection] IRC Connection Destroyed");
},
disconnected: function() {
this.dataPackage = '';
this.established = false;
this.eccepted = false;
this.connected = false;
console.log("[IRC.Connection] Disconnected from server.", this.options.server);
document.fireEvent('/connection/'+this.options.server+'/disconnected', [" Disconnected from server."+ this.options.server])
}
});
IRC.Server = new Class({
Implements: [Options, Events],
Binds: ['onData'],
eventChannels: false,
host: false,
ping: {
lastSent: false,
lastReceived: false,
time: false
},
initialize: function(options, eventChannels) {
this.setOptions(options);
this.eventChannels = eventChannels;
console.log("[IRC.SERVER] New Server connection created. handling input");
document.addEvent(eventChannels.DATAAVAILABLE, this.onData);
if (this.options.password) {
this.send('PASS '+this.password);
}
this.send("NICK " + this.options.nick);
this.send("USER " + this.options.userName + " " + this.options.server + " serverName " + " :" + this.options.realName);
},
send: function(data) {
document.fireEvent(this.eventChannels.SEND, data);
},
disconnect: function(message) {
console.log('IRC.Server Disconnect ', message);
document.fireEvent(this.eventChannels.CLOSE, message);
},
onData: function(message) {
console.log("[IRC.Server] data received for " + this.options.server, message.getContent(), message);
message.handle(this);
}
});
IRC.Server.Message = new Class({
commandType: false,
input: false,
messageData: [],
messageCode: false,
messageContent: false,
isServerMessage: false,
isUserMessage: false,
initialize: function(line, hostName) {
if(line[0] ==':') line = line.substring(1);
this.hostName = hostName;
this.messageData = line.split(" ");
this.isServerMessage = (this.messageData[0] == hostName);
if(Object.keyOf(COMMAND_NUMBERS, this.messageData[1]) != null) {
this.commandType = Object.keyOf(COMMAND_NUMBERS, this.messageData[1]);
this.isServerMessage = true;
this.messageCode = this.messageData[1];
}
else if(Object.keyOf(COMMAND_NUMBERS, this.messageData[0]) != null) {
this.commandType = Object.keyOf(COMMAND_NUMBERS, this.messageData[0]);
this.isServerMessage = true;
this.messageCode = this.messageData[0];
}
if(this.isServerMessage) {
switch(this.messageCode) {
case 'PING':
case 'PONG':
case 'ERROR':
this.messageData = Array.splice(this.messageData, 1);
break;
case 'NOTICE':
this.messageData = Array.splice(this.messageData, 2);
break;
case '001':
case '002':
case '251':
case '252':
case '254':
case '255' :
this.messageData = Array.splice(this.messageData, 3);
break;
default:
this.messageData = Array.splice(this.messageData, 4);
break;
}
} else {
this.isUserMessage = true;
}
this.messageContent = this.messageData.join(' ').trim();
if(this.isServerMessage && this.messageCode != 'PING' && this.messageContent[0] == ':' ) {
this.messageContent = this.messageContent.substring(1);
}
this.messageData = false;
console.log("[IRC.Server.Message] " +this.messageContent);
},
getContent: function() {
return this.messageContent;
},
/**
* Handle this message type for the server
* Checks if the function exists in the message class and performs any actions that need to be done for it.
* @param server the Server instance that handles this message.
*/
handle: function(server) {
if(this.commandType in this) {
this[this.commandType](server);
}
},
SERVER_CONNECT: function(server) {
console.log("[IRC.Server.Message] Connected starting ping service.!");
this.startPingPong(server);
},
ERROR: function(server) {
console.log("[IRC.Server.Message] Message contains an error!", this.messageContent);
server.disconnect(this.messageContent);
},
PING: function(server) {
console.log("PING Received! sending PONG!");
server.send('PONG '+this.messageContent.replace("PING ", ''));
},
startPingPong: function(server) {
server.send("PING SCHIZIRC-PINGPONG");
},
PONG: function(server) {
console.log("PONG Received! sending new Ping in 30s!");
var chan = server.eventChannels.SEND;
setTimeout(function() {
document.fireEvent(chan, "PING SCHIZIRC-PINGPONG");
}, 30000);
}
})
IRC.Channel = new Class({
MODES : {
"&":"&",
"#":"#",
"+":"+",
"!":"!"
},
initialize: function() {
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment