2016-05-15 09:23:38 -04:00
|
|
|
"use strict";
|
|
|
|
|
|
|
|
//! {
|
2016-06-05 09:12:52 -04:00
|
|
|
//! "category": "libraries",
|
2016-05-15 09:23:38 -04:00
|
|
|
//! "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": [
|
2016-12-23 14:36:14 -05:00
|
|
|
//! "libchat",
|
|
|
|
//! "libencoding"
|
2016-05-15 09:23:38 -04:00
|
|
|
//! ]
|
|
|
|
//! }
|
|
|
|
|
|
|
|
let ChatService = require("libchat").ChatService;
|
2016-12-23 14:36:14 -05:00
|
|
|
require("libencoding");
|
2016-05-15 09:23:38 -04:00
|
|
|
|
|
|
|
class IrcService {
|
|
|
|
constructor(options) {
|
|
|
|
let self = this;
|
|
|
|
self._service = new ChatService(options.callback);
|
|
|
|
self._name = options.name;
|
|
|
|
self._nick = options.nick;
|
2017-01-10 21:14:22 -05:00
|
|
|
self._autoJoinChannels = options.autoJoinChannels;
|
2016-06-02 19:01:55 -04:00
|
|
|
self._nameReplies = {};
|
2016-05-15 09:23:38 -04:00
|
|
|
|
|
|
|
network.newConnection().then(function(socket) {
|
|
|
|
self._socket = socket;
|
|
|
|
return self._connect(options);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
_send(line) {
|
|
|
|
return this._socket.write(line + "\r\n");
|
|
|
|
}
|
|
|
|
|
|
|
|
_receivedLine(originalLine) {
|
|
|
|
try {
|
2016-12-23 14:36:14 -05:00
|
|
|
let line = originalLine;
|
2016-05-15 09:23:38 -04:00
|
|
|
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];
|
2016-06-02 19:01:55 -04:00
|
|
|
} else if (prefix.indexOf('!') != -1) {
|
2016-05-15 09:23:38 -04:00
|
|
|
conversation = prefix.split('!')[0];
|
|
|
|
}
|
2016-12-23 15:04:38 -05:00
|
|
|
let message = {
|
2016-05-15 09:23:38 -04:00
|
|
|
from: prefix.split('!')[0],
|
|
|
|
message: parts[parts.length - 1],
|
|
|
|
type: parts[0],
|
2016-12-23 15:04:38 -05:00
|
|
|
};
|
|
|
|
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);
|
2016-05-15 09:23:38 -04:00
|
|
|
} 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");
|
2016-06-02 19:01:55 -04:00
|
|
|
} else if (parts[0] == "PART") {
|
2016-05-15 09:23:38 -04:00
|
|
|
let person = prefix.split('!')[0];
|
|
|
|
let conversation = parts[1];
|
|
|
|
this._service.notifyPresenceChanged(conversation, person, "unavailable");
|
2016-07-31 10:23:47 -04:00
|
|
|
} 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");
|
|
|
|
}
|
2017-01-10 21:14:22 -05:00
|
|
|
} else if (parts[0] == "001") { // RPL_WELCOME
|
|
|
|
if (this._autoJoinChannels) {
|
|
|
|
this._send("JOIN " + this._autoJoinChannels);
|
|
|
|
}
|
2016-06-02 19:01:55 -04:00
|
|
|
} 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 = {};
|
2016-05-15 09:23:38 -04:00
|
|
|
} else {
|
|
|
|
this._service.notifyMessageReceived("", {from: prefix, message: lineNoPrefix});
|
|
|
|
}
|
|
|
|
} catch (error) {
|
|
|
|
this._service.reportError(error);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
_connect(options) {
|
|
|
|
let self = this;
|
|
|
|
|
2016-12-23 14:36:14 -05:00
|
|
|
let kNewLine = '\n'.charCodeAt(0);
|
|
|
|
let kCarriageReturn = '\r'.charCodeAt(0);
|
|
|
|
|
|
|
|
let readBuffer = new Uint8Array(0);
|
2016-05-15 09:23:38 -04:00
|
|
|
self._socket.read(function(data) {
|
|
|
|
if (data) {
|
2016-12-23 14:36:14 -05:00
|
|
|
let newBuffer = new Uint8Array(readBuffer.length + data.length);
|
|
|
|
newBuffer.set(readBuffer, 0);
|
|
|
|
newBuffer.set(data, readBuffer.length);
|
|
|
|
readBuffer = newBuffer;
|
|
|
|
|
|
|
|
let end = readBuffer.indexOf(kNewLine);
|
2016-05-15 09:23:38 -04:00
|
|
|
while (end != -1) {
|
2016-12-23 14:36:14 -05:00
|
|
|
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);
|
2016-05-15 09:23:38 -04:00
|
|
|
}
|
|
|
|
} 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");
|
|
|
|
}
|
2016-06-02 19:01:55 -04:00
|
|
|
this._service.notifyMessageReceived(target || "", {from: this._nick, message: text, timestamp: new Date().toString()});
|
2016-05-15 09:23:38 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
disconnect() {
|
|
|
|
this._send("QUIT");
|
|
|
|
this._socket.close();
|
|
|
|
this._service.notifyStateChanged("disconnected");
|
2016-12-22 13:24:19 -05:00
|
|
|
exit(0);
|
2016-05-15 09:23:38 -04:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
ChatService.handleMessages(IrcService);
|