Skip to content

Instantly share code, notes, and snippets.

@SchizoDuckie
Created August 1, 2013 19:41
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/6134548 to your computer and use it in GitHub Desktop.
Save SchizoDuckie/6134548 to your computer and use it in GitHub Desktop.
IRC Client for Adobe Air W.I.P.
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,
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;
}
else if(Object.keyOf(COMMAND_NUMBERS, this.messageData[0]) != null) {
this.commandType = Object.keyOf(COMMAND_NUMBERS, this.messageData[0]);
this.isServerMessage = true;
}
if(this.isServerMessage) {
switch(this.messageType) {
case 'NOTICE':
delete this.messageData[0];
delete this.messageData[1];
break;
case 'PING':
delete this.messageData[0];
break;
case 'ERROR':
delete this.messageData[0];
break;
case '001':
case '002':
delete this.messageData[0];
delete this.messageData[1];
break;
case '251':
case '252':
delete this.messageData[0];
delete this.messageData[1];
delete this.messageData[2];
break;
default:
delete this.messageData[0];
delete this.messageData[1];
delete this.messageData[2];
delete this.messageData[3];
break;
}
} else {
this.isUserMessage = true;
}
this.messageContent = this.messageData.join(' ');
},
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) {
console.log("Message.handle: "+ this.commandType, this.commandType in this)
if(this.messageType in this) {
this[this.messageType](server);
}
},
SERVER_CONNECT: function() {
console.log("[IRC.Server] Connected!");
server.disconnect(this.messageContent);
},
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);
}
})
IRC.Channel = new Class({
MODES : {
"&":"&",
"#":"#",
"+":"+",
"!":"!"
},
initialize: function() {
},
/* todo: refactor */
getModes: function (modes, target) {
var parts, modeChanges, setTypes, toggleTypes,
i, j, c, part, toggle, modeObj;
parts = modes.split(" ");
//array items for the below array follow this structure:
// { "toggle": "+", "type": "o", "arg": "user1", "target": "#myChannel" }
// { "toggle": "-", "type": "n", "arg": null, "target": "#myChannel" }
modeChanges = [];
setTypes = {
O : 'give "channel creator" status;',
o : 'give/take channel operator privilege;',
h : 'give/take halfop operator privilege;',
v : 'give/take the voice privilege;',
k : 'set/remove the channel key (password);',
l : 'set/remove the user limit to channel;',
b : 'set/remove ban mask to keep users out;',
e : 'set/remove an exception mask to override a ban mask;',
I : 'set/remove an invitation mask to automatically override the invite-only flag;'
};
toggleTypes = {
a : 'toggle the anonymous channel flag;',
i : 'toggle the invite-only channel flag;',
m : 'toggle the moderated channel;',
n : 'toggle the no messages to channel from clients on the outside;',
q : 'toggle the quiet channel flag;',
p : 'toggle the private channel flag;',
s : 'toggle the secret channel flag;',
r : 'toggle the server reop channel flag;',
t : 'toggle the topic settable by channel operator only flag;'
};
for (i = 0; i < parts.length; i++) {
part = parts[i];
if (part) {
toggle = part[0];
if (toggle === "+" || toggle === "-") {
for (j = 1; j < part.length; j++) {
c = part[j];
modeObj = {};
modeObj.toggle = toggle;
modeObj.target = target;
if (c === "+" || c === "-") {
toggle = c;
modeObj = null;
continue;
}
if (c in setTypes) {
modeObj.arg = undefined;
modeObj.type = c;
modeChanges.push(modeObj);
continue;
}
if (c in toggleTypes) {
modeObj.arg = null;
modeObj.type = c;
modeChanges.push(modeObj);
}
}
} else {
for (j = 0; j < modeChanges.length; j++){
if (modeChanges[j].arg === undefined) {
modeChanges[j].arg = part;
break;
}
}
}
}
}
return modeChanges;
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment