diff --git a/packages/cory/chat/chat.js b/packages/cory/chat/chat.js
index d6b9380e6..94fbe1381 100644
--- a/packages/cory/chat/chat.js
+++ b/packages/cory/chat/chat.js
@@ -4,6 +4,7 @@ var gFocus = true;
var gUnread = 0;
var gPresence = {};
let gSessions = {};
+let gState = {};
let gCurrentConversation;
function updateTitle() {
@@ -11,6 +12,7 @@ function updateTitle() {
}
let kAccountsKey = JSON.stringify(["accounts", core.user.name]);
+let kStateKey = JSON.stringify(["state", core.user.name]);
function runCommand(data) {
if (data.action == "addAccount") {
@@ -27,12 +29,18 @@ function runCommand(data) {
} else if (data.action == "disconnect") {
disconnect(data.id);
} else if (data.action == "window") {
- gCurrentConversation = gSessions[data.account].conversations[data.conversation];
- updateConversation();
- updateWindows();
+ setWindow(data.account, data.conversation);
}
}
+function setWindow(accountId, conversation) {
+ gState.window = {account: accountId, conversation: conversation};
+ database.set(kStateKey, JSON.stringify(gState));
+ gCurrentConversation = gSessions[accountId].conversations[conversation];
+ updateConversation();
+ updateWindows();
+}
+
function addAccount() {
return database.get(kAccountsKey).then(function(data) {
let accounts = data ? JSON.parse(data) : [];
@@ -152,10 +160,11 @@ function connect(id) {
self.session = session;
gSessions[id] = session;
session.conversations = {};
- getConversation(session, {});
+ session.account = account;
+ getConversation(session, null);
session.getConversations().then(function(conversations) {
for (let j in conversations) {
- getConversation(session, {conversation: conversations[j]});
+ getConversation(session, conversations[j]);
}
});
});
@@ -208,13 +217,18 @@ function updateConversation() {
]).then(function(data) {
let history = data[0];
let participants = data[1];
- gCurrentConversation.messages = history;
- gCurrentConversation.participants = participants;
+ gCurrentConversation.messages = history || [];
+ gCurrentConversation.participants = participants || [];
terminal.cork();
terminal.select("terminal");
terminal.clear();
for (var i in gCurrentConversation.messages) {
- printMessage(gCurrentConversation.messages[i]);
+ let message = gCurrentConversation.messages[i];
+ if (message.action == "message") {
+ printMessage(message.message);
+ } else {
+ terminal.print(message);
+ }
}
updateUsers();
terminal.uncork();
@@ -255,65 +269,81 @@ terminal.select("terminal");
terminal.print("~Friends Chat");
terminal.uncork();
-function getConversation(session, message) {
+function getConversation(session, conversationName) {
let result;
- for (var i in gSessions) {
- if (session == gSessions[i]) {
- let key = message.conversation || message.from || "";
- if (!session.conversations[key]) {
- session.conversations[key] = {
- session: session,
- name: key,
- messages: [],
- sendMessage: function(message) {
- return session.sendMessage(key, message);
- },
- };
- updateWindows();
+ let key = conversationName || "";
+ if (!session.conversations) {
+ session.conversations = {};
+ }
+ if (!session.conversations[key]) {
+ session.conversations[key] = {
+ session: session,
+ name: key,
+ messages: [],
+ sendMessage: function(message) {
+ return session.sendMessage(key, message);
+ },
+ };
+ updateWindows();
+ }
+ result = session.conversations[key];
+ if (result) {
+ if (!gCurrentConversation) {
+ if (!gState.window) {
+ setWindow(session.account.id, key);
+ } else if (gState.window.account = session.account.id && gState.window.conversation == key) {
+ setWindow(session.account.id, key);
}
- result = session.conversations[key];
- break;
}
}
- if (result && !gCurrentConversation) {
- gCurrentConversation = result;
- }
return result;
}
+function printToConversation(conversation, message, notify) {
+ if (conversation == gCurrentConversation) {
+ if (message.action == "message") {
+ printMessage(message.message);
+ } else {
+ terminal.print(message);
+ }
+ }
+ if (conversation) {
+ conversation.messages.push(message);
+ }
+ if (notify && !gFocus) {
+ gUnread++;
+ updateTitle();
+ }
+}
+
function chatCallback(event) {
try {
if (event.action == "message") {
- let conversation = getConversation(this.session, event);
- if (conversation == gCurrentConversation) {
- printMessage(event);
- }
- conversation.messages.push(event);
-
- if (!gFocus) {
- gUnread++;
- updateTitle();
- }
+ let conversation = getConversation(this.session, event.conversation);
+ printToConversation(conversation, event);
} else if (event.action == "presence") {
- let conversation = event.jid.split('/', 2)[0];
- if (gCurrentConversation.name == conversation) {
- let index = gCurrentConversation.participants.indexOf(event.name);
- if (event.type == "unavailable") {
- if (index != -1) {
- gCurrentConversation.participants.splice(index, 1);
+ let conversation = getConversation(this.session, event.conversation);
+ let index = conversation.participants.indexOf(event.user);
+ if (event.presence == "unavailable") {
+ if (index != -1) {
+ conversation.participants.splice(index, 1);
+ if (conversation == gCurrentConversation) {
updateUsers();
- terminal.print(new Date().toString(), ": ", event.name + " has left the room.");
}
- } else {
- if (index == -1) {
- gCurrentConversation.participants.push(event.name);
+ printToConversation(conversation, [new Date().toString(), ": ", event.user + " has left the room."]);
+ }
+ } else {
+ if (index == -1) {
+ conversation.participants.push(event.user);
+ if (conversation == gCurrentConversation) {
updateUsers();
- terminal.print(new Date().toString(), ": ", event.name + " has joined the room.");
}
+ printToConversation(conversation, [new Date().toString(), ": ", event.user + " has joined the room."]);
}
}
} else {
- terminal.print("Unhandled event: ", JSON.stringify(event));
+ let conversation = getConversation(this.session, event.conversation);
+ printToConversation(conversation, ["Unhandled event: ", JSON.stringify(event)]);
}
} catch (error) {
terminal.print("chatCallback: ", error);
@@ -391,8 +421,9 @@ core.register("blur", function() {
});
// Connect all accounts on start.
-Promise.all([database.get(kAccountsKey), core.getPackages()]).then(function(results) {
+Promise.all([database.get(kAccountsKey), database.get(kStateKey)]).then(function(results) {
let accounts = results[0] ? JSON.parse(results[0]) : [];
+ gState = results[1] ? JSON.parse(results[1]) : gState;
for (let i in accounts) {
connect(accounts[i].id);
}
diff --git a/packages/cory/irc/irc.js b/packages/cory/irc/irc.js
new file mode 100644
index 000000000..cad0cdad3
--- /dev/null
+++ b/packages/cory/irc/irc.js
@@ -0,0 +1,140 @@
+"use strict";
+
+//! {
+//! "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"
+//! ]
+//! }
+
+let ChatService = require("libchat").ChatService;
+
+class IrcService {
+ constructor(options) {
+ let self = this;
+ self._service = new ChatService(options.callback);
+ self._name = options.name;
+ self._nick = options.nick;
+
+ 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 {
+ conversation = prefix.split('!')[0];
+ }
+ this._service.notifyMessageReceived(conversation, {
+ from: prefix.split('!')[0],
+ message: parts[parts.length - 1],
+ type: parts[0],
+ });
+ } 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] == "JOIN") {
+ let person = prefix.split('!')[0];
+ let conversation = parts[1];
+ this._service.notifyPresenceChanged(conversation, person, "unavailable");
+ } else {
+ this._service.notifyMessageReceived("", {from: prefix, message: lineNoPrefix});
+ }
+ } catch (error) {
+ this._service.reportError(error);
+ }
+ }
+
+ _connect(options) {
+ let self = this;
+
+ let readBuffer = "";
+ self._socket.read(function(data) {
+ if (data) {
+ readBuffer += data;
+ let end = readBuffer.indexOf("\n");
+ while (end != -1) {
+ let line = readBuffer.substring(0, end);
+ if (line.charAt(line.length - 1) == "\r") {
+ line = line.substring(0, line.length - 1);
+ }
+ readBuffer = readBuffer.substring(end + 1);
+ self._receivedLine(line);
+ end = readBuffer.indexOf("\n");
+ }
+ } 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: self._nick, message: text, timestamp: new Date().toString()});
+ }
+
+ disconnect() {
+ this._send("QUIT");
+ this._socket.close();
+ this._service.notifyStateChanged("disconnected");
+ }
+};
+
+ChatService.handleMessages(IrcService);
\ No newline at end of file
diff --git a/packages/cory/libchat/libchat.js b/packages/cory/libchat/libchat.js
new file mode 100644
index 000000000..508a0f31d
--- /dev/null
+++ b/packages/cory/libchat/libchat.js
@@ -0,0 +1,126 @@
+"use strict";
+
+exports.ChatService = class {
+ static handleMessages(serviceClass) {
+ let self = this;
+ let sessions = {};
+
+ core.register("onMessage", function(sender, options) {
+ let service = sessions[options.name];
+ if (!service) {
+ service = new serviceClass(options);
+ sessions[options.name] = service;
+ } else {
+ service._service.addCallback(options.callback);
+ }
+ return service._service.makeInterface(service);
+ });
+ }
+
+ constructor(callback) {
+ this._callbacks = [callback];
+ this._conversations = {};
+ this._state = null;
+ }
+
+ makeInterface(service) {
+ let self = this;
+ return {
+ sendMessage: service.sendMessage.bind(service),
+ disconnect: service.disconnect.bind(service),
+
+ getConversations: self.getConversations.bind(self),
+ getHistory: self.getHistory.bind(self),
+ getParticipants: self.getParticipants.bind(self),
+ };
+ }
+
+ addCallback(callback) {
+ if (this._callbacks.indexOf(callback) == -1) {
+ this._callbacks.push(callback);
+ }
+ }
+
+ _invokeCallback(message) {
+ let self = this;
+ for (let i = self._callbacks.length - 1; i >= 0; i--) {
+ let callback = self._callbacks[i];
+ try {
+ callback(message);
+ } catch (error) {
+ self._callbacks.splice(i, 1);
+
+ // XXX: Send it to the other connections?
+ print(error);
+ }
+ }
+ }
+
+ _getConversation(conversation) {
+ if (!this._conversations[conversation]) {
+ this._conversations[conversation] = {history: [], participants: []};
+ }
+ return this._conversations[conversation];
+ }
+
+ notifyMessageReceived(conversation, message) {
+ let fullMessage = {action: "message", conversation: conversation || "", message: message};
+ this._getConversation(conversation || "").history.push(fullMessage);
+ this._invokeCallback(fullMessage);
+ }
+
+ notifyPresenceChanged(conversation, user, state) {
+ let leaving = state == "unavailable";
+ let participants = this._getConversation(conversation).participants;
+ let index = participants.indexOf(user);
+ if (leaving) {
+ participants.splice(index, 1);
+ } else if (index == -1) {
+ participants.push(user);
+ }
+ this._invokeCallback({
+ action: "presence",
+ conversation: conversation,
+ user: user,
+ presence: state,
+ });
+ }
+
+ notifyStateChanged(state) {
+ this._state = state;
+ this._invokeCallback({action: state});
+ }
+
+ reportError(error) {
+ this._invokeCallback({
+ action: "error",
+ error: error,
+ }).catch(function(error) {
+ print(error);
+ });
+ }
+
+ isConversation(conversation) {
+ return this._conversations[conversation] != null;
+ }
+
+ getConversations() {
+ return Object.keys(this._conversations);
+ }
+
+ getHistory(conversation) {
+ let result;
+ if (this._conversations[conversation]) {
+ result = this._conversations[conversation].history;
+ }
+ return result;
+ }
+
+ getParticipants(conversation) {
+ let result;
+ if (this._conversations[conversation]) {
+ result = this._conversations[conversation].participants;
+ }
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/packages/cory/xmpp2/xmpp2.js b/packages/cory/xmpp2/xmpp2.js
index 6cea548d0..bd9462168 100644
--- a/packages/cory/xmpp2/xmpp2.js
+++ b/packages/cory/xmpp2/xmpp2.js
@@ -12,7 +12,10 @@
//! {"name": "resource", "type": "text", "default": "tildefriends"},
//! {"name": "server", "type": "text"}
//! ]
-//! }
+//! },
+//! "require": [
+//! "libchat"
+//! ]
//! }
// md5.js
@@ -678,57 +681,23 @@ XmlStanzaParser.prototype.parseNode = function(node) {
// end xmpp.js
+let ChatService = require("libchat").ChatService;
+
var gPingCount = 0;
class XmppService {
constructor(options) {
let self = this;
- self._callbacks = [options.callback];
- self._conversations = {};
+ self._service = new ChatService(options.callback);
network.newConnection().then(function(socket) {
self._socket = socket;
return self._connect(options);
- }).catch(self._reportError);
+ }).catch(self._service.reportError);
}
sendMessage(to, message) {
- this._socket.write("" + xmlEncode(message) + "");
- }
-
- getConversations() {
- return Object.keys(this._conversations);
- }
-
- getParticipants(conversation) {
- let result;
- if (this._conversations[conversation]) {
- result = this._conversations[conversation].participants;
- }
- return result;
- }
-
- getHistory(conversation) {
- let result;
- if (this._conversations[conversation]) {
- result = this._conversations[conversation].history;
- }
- return result;
- }
-
- invokeCallback(message) {
- let self = this;
- for (let i = self._callbacks.length - 1; i >= 0; i--) {
- let callback = self._callbacks[i];
- try {
- callback(message);
- } catch (error) {
- self._callbacks.splice(i, 1);
-
- // XXX: Send it to the other connections?
- print(error);
- }
- }
+ this._socket.write("" + xmlEncode(message) + "").catch(this._service.reportError);
}
_connect(options) {
@@ -755,9 +724,7 @@ class XmppService {
let password = options.password;
let server = options.server;
self._socket.connect("jabber.troubleimpact.com", 5222).then(function() {
- print("actually connected");
- self.invokeCallback({action: "connected"});
- print("wtf");
+ self._service.notifyStateChanged("connected");
var parse = new XmlStanzaParser(1);
self._socket.write("");
self._socket.write("");
@@ -768,7 +735,7 @@ class XmppService {
self._socket.read(function(data) {
try {
if (!data) {
- self.invokeCallback({action: "disconnected"});
+ self._service.notifyStateChanged("disconnected");
return;
}
parse.parse(data).forEach(function(stanza) {
@@ -800,19 +767,26 @@ class XmppService {
} else if (stanza.attributes.id == "session0") {
self._socket.write("1");
self._schedulePing();
- self._conversations["chadhappyfuntime@conference.jabber.troubleimpact.com"] = {participants: [], history: []};
+ //self._conversations["chadhappyfuntime@conference.jabber.troubleimpact.com"] = {participants: [], history: []};
} else if (stanza.children.length && stanza.children[0].name == "ping") {
// Ping response.
} else {
- self.invokeCallback({
- action: "unknown",
- stanza: stanza,
- });
+ self._service.notifyMessageReceived(null, {unknown: stanza});
}
} else if (stanza.name == "message") {
let message = self._convertMessage(stanza);
- self._conversations[message.conversation].history.push(message);
- self.invokeCallback(message);
+ let conversation = stanza.attributes.from;
+ if (conversation && conversation.indexOf('/') != -1) {
+ conversation = conversation.split("/")[1];
+ }
+ if (stanza.attributes.type == "groupchat") {
+ if (self._service.isConversation(stanza.attributes.to.split("/")[0])) {
+ conversation = stanza.attributes.to.split("/")[0];
+ } else if (self._service.isConversation(stanza.attributes.from.split("/")[0])) {
+ conversation = stanza.attributes.from.split("/")[0];
+ }
+ }
+ self._service.notifyMessageReceived(conversation, message);
} else if (stanza.name == "challenge") {
var challenge = Base64.decode(stanza.text);
var parts = challenge.split(',');
@@ -849,33 +823,16 @@ class XmppService {
} else if (stanza.name == "presence") {
let name = stanza.attributes.from.split('/', 2)[1];
let conversation = stanza.attributes.from.split('/', 2)[0];
- let leaving = stanza.attributes.type == "unavailable";
- let index = self._conversations[conversation].participants.indexOf(name);
- if (leaving) {
- self._conversations[conversation].participants.splice(index, 1);
- } else {
- if (index == -1) {
- self._conversations[conversation].participants.push(name);
- }
- }
- self.invokeCallback({
- action: "presence",
- name: name,
- jid: stanza.attributes.from,
- type: stanza.attributes.type,
- });
+ self._service.notifyPresenceChanged(conversation, name, stanza.attributes.type);
} else {
- self.invokeCallback({
- action: "unknown",
- stanza: stanza,
- });
+ self._service.notifyMessageReceived(null, {unknown: stanza});
}
});
} catch (error) {
- self._reportError(error);
+ self._service.reportError(error);
}
});
- }).catch(self._reportError);
+ }).catch(self._service.reportError);
}
disconnect() {
@@ -884,15 +841,6 @@ class XmppService {
delete gSessions[self._name];
}
- _reportError(error) {
- this.invokeCallback({
- action: "error",
- error: error,
- }).catch(function(error) {
- print(error);
- });
- }
-
_convertMessage(stanza) {
let self = this;
let text;
@@ -909,18 +857,8 @@ class XmppService {
if (from && from.indexOf('/') != -1) {
from = from.split("/")[1];
}
- let conversation = from;
- if (stanza.attributes.type == "groupchat") {
- if (self._conversations[stanza.attributes.to.split("/")[0]]) {
- conversation = stanza.attributes.to.split("/")[0];
- } else if (self._conversations[stanza.attributes.from.split("/")[0]]) {
- conversation = stanza.attributes.from.split("/")[0];
- }
- }
let message = {
- action: "message",
from: from,
- conversation: conversation,
message: text,
stanza: stanza,
timestamp: now,
@@ -937,23 +875,4 @@ class XmppService {
}
};
-let gSessions = {};
-
-core.register("onMessage", function(sender, options) {
- let service = gSessions[options.name];
- if (!service) {
- service = new XmppService(options);
- gSessions[options.name] = service;
- } else {
- if (service._callbacks.indexOf(options.callback) == -1) {
- service._callbacks.push(options.callback);
- }
- }
- return {
- sendMessage: service.sendMessage.bind(service),
- getConversations: service.getConversations.bind(service),
- getHistory: service.getHistory.bind(service),
- getParticipants: service.getParticipants.bind(service),
- disconnect: service.disconnect.bind(service),
- };
-});
\ No newline at end of file
+ChatService.handleMessages(XmppService);
\ No newline at end of file