tildefriends/packages/cory/libirc/libirc.js
2017-01-11 02:14:22 +00:00

187 lines
5.5 KiB
JavaScript

"use strict";
//! {
//! "category": "libraries",
//! "permissions": [
//! "network"
//! ],
//! "chat": {
//! "version": 1,
//! "settings": [
//! {"name": "user", "type": "text"},
//! {"name": "realName", "type": "text"},
//! {"name": "password", "type": "password"},
//! {"name": "nick", "type": "text"},
//! {"name": "server", "type": "text"},
//! {"name": "port", "type": "text"},
//! {"name": "autoJoinChannels", "type": "text"}
//! ]
//! },
//! "require": [
//! "libchat",
//! "libencoding"
//! ]
//! }
let ChatService = require("libchat").ChatService;
require("libencoding");
class IrcService {
constructor(options) {
let self = this;
self._service = new ChatService(options.callback);
self._name = options.name;
self._nick = options.nick;
self._autoJoinChannels = options.autoJoinChannels;
self._nameReplies = {};
network.newConnection().then(function(socket) {
self._socket = socket;
return self._connect(options);
});
}
_send(line) {
return this._socket.write(line + "\r\n");
}
_receivedLine(originalLine) {
try {
let line = originalLine;
let prefix;
if (line.charAt(0) == ":") {
let space = line.indexOf(" ");
prefix = line.substring(1, space);
line = line.substring(space + 1);
}
let lineNoPrefix = line;
let remainder;
let colon = line.indexOf(" :");
if (colon != -1) {
remainder = line.substring(colon + 2);
line = line.substring(0, colon);
}
let parts = line.split(" ");
if (remainder) {
parts.push(remainder);
}
let conversation = "";
if (parts[0] == "PRIVMSG" || parts[0] == "NOTICE") {
// Is it a channel type?
if ("&#!+.".indexOf(parts[1].charAt(0)) != -1) {
conversation = parts[1];
} else if (prefix.indexOf('!') != -1) {
conversation = prefix.split('!')[0];
}
let message = {
from: prefix.split('!')[0],
message: parts[parts.length - 1],
type: parts[0],
};
if (message.message.length > 2 && message.message.charCodeAt(0) == 1 && message.message.charCodeAt(message.message.length - 1) == 1) {
message.ctcp = true;
if (message.message.substring(1, 1 + "ACTION ".length) == "ACTION ") {
message.action = true;
message.message = message.message.substring(1 + "ACTION ".length, message.message.length - 1);
}
}
this._service.notifyMessageReceived(conversation, message);
} else if (parts[0] == "PING") {
parts[0] = "PONG";
this._send(parts.join(" "));
} else if (parts[0] == "JOIN") {
let person = prefix.split('!')[0];
let conversation = parts[1];
this._service.notifyPresenceChanged(conversation, person, "present");
} else if (parts[0] == "PART") {
let person = prefix.split('!')[0];
let conversation = parts[1];
this._service.notifyPresenceChanged(conversation, person, "unavailable");
} else if (parts[0] == "QUIT") {
let person = prefix.split('!')[0];
let conversations = this._service.getConversations();
for (let i in conversations) {
this._service.notifyPresenceChanged(conversations[i], person, "unavailable");
}
} else if (parts[0] == "001") { // RPL_WELCOME
if (this._autoJoinChannels) {
this._send("JOIN " + this._autoJoinChannels);
}
} else if (parts[0] == "353") { // RPL_NAMREPLY
if (!this._nameReplies[parts[3]]) {
this._nameReplies[parts[3]] = [];
}
let users = parts[4].split(' ');
for (let i in users) {
let user = users[i];
let state = "present";
if ("@+".indexOf(user.charAt(0)) != -1) {
state = user.charAt(0);
user = user.substring(1);
}
this._nameReplies[parts[3]][user] = state;
}
} else if (parts[0] == "366") { // RPL_ENDOFNAMES
for (let conversation in this._nameReplies) {
this._service.notifyParticipantList(conversation, this._nameReplies[conversation]);
}
this._nameReplies = {};
} else {
this._service.notifyMessageReceived("", {from: prefix, message: lineNoPrefix});
}
} catch (error) {
this._service.reportError(error);
}
}
_connect(options) {
let self = this;
let kNewLine = '\n'.charCodeAt(0);
let kCarriageReturn = '\r'.charCodeAt(0);
let readBuffer = new Uint8Array(0);
self._socket.read(function(data) {
if (data) {
let newBuffer = new Uint8Array(readBuffer.length + data.length);
newBuffer.set(readBuffer, 0);
newBuffer.set(data, readBuffer.length);
readBuffer = newBuffer;
let end = readBuffer.indexOf(kNewLine);
while (end != -1) {
let line = readBuffer.slice(0, (end > 0 && readBuffer[end - 1] == kCarriageReturn) ? end - 1 : end);
readBuffer = readBuffer.slice(end + 1);
self._receivedLine(new TextDecoder("UTF-8").decode(line));
end = readBuffer.indexOf(kNewLine);
}
} else {
self._service.notifyStateChanged("disconnected");
}
});
return self._socket.connect(options.server, options.port).then(function() {
self._service.notifyStateChanged("connected");
self._send("USER " + options.user + " 0 * :" + options.realName);
self._send("NICK " + options.nick);
}).catch(self._service.reportError);
}
sendMessage(target, text) {
if (!target) {
this._socket.write(text + "\r\n");
} else {
this._socket.write("PRIVMSG " + target + " :" + text + "\r\n");
}
this._service.notifyMessageReceived(target || "", {from: this._nick, message: text, timestamp: new Date().toString()});
}
disconnect() {
this._send("QUIT");
this._socket.close();
this._service.notifyStateChanged("disconnected");
exit(0);
}
};
ChatService.handleMessages(IrcService);