forked from cory/tildefriends
		
	A detachable multi-protocol chat client is starting to come together.
git-svn-id: https://www.unprompted.com/svn/projects/tildefriends/trunk@3232 ed5197a5-7fde-0310-b194-c3ffbd925b24
This commit is contained in:
		| @@ -4,6 +4,7 @@ var gFocus = true; | |||||||
| var gUnread = 0; | var gUnread = 0; | ||||||
| var gPresence = {}; | var gPresence = {}; | ||||||
| let gSessions = {}; | let gSessions = {}; | ||||||
|  | let gState = {}; | ||||||
| let gCurrentConversation; | let gCurrentConversation; | ||||||
|  |  | ||||||
| function updateTitle() { | function updateTitle() { | ||||||
| @@ -11,6 +12,7 @@ function updateTitle() { | |||||||
| } | } | ||||||
|  |  | ||||||
| let kAccountsKey = JSON.stringify(["accounts", core.user.name]); | let kAccountsKey = JSON.stringify(["accounts", core.user.name]); | ||||||
|  | let kStateKey = JSON.stringify(["state", core.user.name]); | ||||||
|  |  | ||||||
| function runCommand(data) { | function runCommand(data) { | ||||||
| 	if (data.action == "addAccount") { | 	if (data.action == "addAccount") { | ||||||
| @@ -27,12 +29,18 @@ function runCommand(data) { | |||||||
| 	} else if (data.action == "disconnect") { | 	} else if (data.action == "disconnect") { | ||||||
| 		disconnect(data.id); | 		disconnect(data.id); | ||||||
| 	} else if (data.action == "window") { | 	} else if (data.action == "window") { | ||||||
| 		gCurrentConversation = gSessions[data.account].conversations[data.conversation]; | 		setWindow(data.account, data.conversation); | ||||||
| 		updateConversation(); |  | ||||||
| 		updateWindows(); |  | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | 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() { | function addAccount() { | ||||||
| 	return database.get(kAccountsKey).then(function(data) { | 	return database.get(kAccountsKey).then(function(data) { | ||||||
| 		let accounts = data ? JSON.parse(data) : []; | 		let accounts = data ? JSON.parse(data) : []; | ||||||
| @@ -152,10 +160,11 @@ function connect(id) { | |||||||
| 					self.session = session; | 					self.session = session; | ||||||
| 					gSessions[id] = session; | 					gSessions[id] = session; | ||||||
| 					session.conversations = {}; | 					session.conversations = {}; | ||||||
| 					getConversation(session, {}); | 					session.account = account; | ||||||
|  | 					getConversation(session, null); | ||||||
| 					session.getConversations().then(function(conversations) { | 					session.getConversations().then(function(conversations) { | ||||||
| 						for (let j in conversations) { | 						for (let j in conversations) { | ||||||
| 							getConversation(session, {conversation: conversations[j]}); | 							getConversation(session, conversations[j]); | ||||||
| 						} | 						} | ||||||
| 					}); | 					}); | ||||||
| 				}); | 				}); | ||||||
| @@ -208,13 +217,18 @@ function updateConversation() { | |||||||
| 		]).then(function(data) { | 		]).then(function(data) { | ||||||
| 			let history = data[0]; | 			let history = data[0]; | ||||||
| 			let participants = data[1]; | 			let participants = data[1]; | ||||||
| 			gCurrentConversation.messages = history; | 			gCurrentConversation.messages = history || []; | ||||||
| 			gCurrentConversation.participants = participants; | 			gCurrentConversation.participants = participants || []; | ||||||
| 			terminal.cork(); | 			terminal.cork(); | ||||||
| 			terminal.select("terminal"); | 			terminal.select("terminal"); | ||||||
| 			terminal.clear(); | 			terminal.clear(); | ||||||
| 			for (var i in gCurrentConversation.messages) { | 			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(); | 			updateUsers(); | ||||||
| 			terminal.uncork(); | 			terminal.uncork(); | ||||||
| @@ -255,65 +269,81 @@ terminal.select("terminal"); | |||||||
| terminal.print("~Friends Chat"); | terminal.print("~Friends Chat"); | ||||||
| terminal.uncork(); | terminal.uncork(); | ||||||
|  |  | ||||||
| function getConversation(session, message) { | function getConversation(session, conversationName) { | ||||||
| 	let result; | 	let result; | ||||||
| 	for (var i in gSessions) { | 	let key = conversationName || ""; | ||||||
| 		if (session == gSessions[i]) { | 	if (!session.conversations) { | ||||||
| 			let key = message.conversation || message.from || ""; | 		session.conversations = {}; | ||||||
| 			if (!session.conversations[key]) { | 	} | ||||||
| 				session.conversations[key] = { | 	if (!session.conversations[key]) { | ||||||
| 					session: session, | 		session.conversations[key] = { | ||||||
| 					name: key, | 			session: session, | ||||||
| 					messages: [], | 			name: key, | ||||||
| 					sendMessage: function(message) { | 			messages: [], | ||||||
| 						return session.sendMessage(key, message); | 			sendMessage: function(message) { | ||||||
| 					}, | 				return session.sendMessage(key, message); | ||||||
| 				}; | 			}, | ||||||
| 				updateWindows(); | 		}; | ||||||
|  | 		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; | 	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) { | function chatCallback(event) { | ||||||
| 	try { | 	try { | ||||||
| 		if (event.action == "message") { | 		if (event.action == "message") { | ||||||
| 			let conversation = getConversation(this.session, event); | 			let conversation = getConversation(this.session, event.conversation); | ||||||
| 			if (conversation == gCurrentConversation) { | 			printToConversation(conversation, event); | ||||||
| 				printMessage(event); |  | ||||||
| 			} |  | ||||||
| 			conversation.messages.push(event); |  | ||||||
|  |  | ||||||
| 			if (!gFocus) { |  | ||||||
| 				gUnread++; |  | ||||||
| 				updateTitle(); |  | ||||||
| 			} |  | ||||||
| 		} else if (event.action == "presence") { | 		} else if (event.action == "presence") { | ||||||
| 			let conversation = event.jid.split('/', 2)[0]; | 			let conversation = getConversation(this.session, event.conversation); | ||||||
| 			if (gCurrentConversation.name == conversation) { | 			let index = conversation.participants.indexOf(event.user); | ||||||
| 				let index = gCurrentConversation.participants.indexOf(event.name); | 			if (event.presence == "unavailable") { | ||||||
| 				if (event.type == "unavailable") { | 				if (index != -1) { | ||||||
| 					if (index != -1) { | 					conversation.participants.splice(index, 1); | ||||||
| 						gCurrentConversation.participants.splice(index, 1); | 					if (conversation == gCurrentConversation) { | ||||||
| 						updateUsers(); | 						updateUsers(); | ||||||
| 						terminal.print(new Date().toString(), ": ", event.name + " has left the room."); |  | ||||||
| 					} | 					} | ||||||
| 				} else { | 					printToConversation(conversation, [new Date().toString(), ": ", event.user + " has left the room."]); | ||||||
| 					if (index == -1) { | 				} | ||||||
| 						gCurrentConversation.participants.push(event.name); | 			} else { | ||||||
|  | 				if (index == -1) { | ||||||
|  | 					conversation.participants.push(event.user); | ||||||
|  | 					if (conversation == gCurrentConversation) { | ||||||
| 						updateUsers(); | 						updateUsers(); | ||||||
| 						terminal.print(new Date().toString(), ": ", event.name + " has joined the room."); |  | ||||||
| 					} | 					} | ||||||
|  | 					printToConversation(conversation, [new Date().toString(), ": ", event.user + " has joined the room."]); | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			terminal.print("Unhandled event: ", JSON.stringify(event)); | 			let conversation = getConversation(this.session, event.conversation); | ||||||
|  | 			printToConversation(conversation, ["Unhandled event: ", JSON.stringify(event)]); | ||||||
| 		} | 		} | ||||||
| 	} catch (error) { | 	} catch (error) { | ||||||
| 		terminal.print("chatCallback: ", error); | 		terminal.print("chatCallback: ", error); | ||||||
| @@ -391,8 +421,9 @@ core.register("blur", function() { | |||||||
| }); | }); | ||||||
|  |  | ||||||
| // Connect all accounts on start. | // 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]) : []; | 	let accounts = results[0] ? JSON.parse(results[0]) : []; | ||||||
|  | 	gState = results[1] ? JSON.parse(results[1]) : gState; | ||||||
| 	for (let i in accounts) { | 	for (let i in accounts) { | ||||||
| 		connect(accounts[i].id); | 		connect(accounts[i].id); | ||||||
| 	} | 	} | ||||||
|   | |||||||
							
								
								
									
										140
									
								
								packages/cory/irc/irc.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								packages/cory/irc/irc.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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); | ||||||
							
								
								
									
										126
									
								
								packages/cory/libchat/libchat.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										126
									
								
								packages/cory/libchat/libchat.js
									
									
									
									
									
										Normal file
									
								
							| @@ -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; | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @@ -12,7 +12,10 @@ | |||||||
| //! 			{"name": "resource", "type": "text", "default": "tildefriends"}, | //! 			{"name": "resource", "type": "text", "default": "tildefriends"}, | ||||||
| //! 			{"name": "server", "type": "text"} | //! 			{"name": "server", "type": "text"} | ||||||
| //! 		] | //! 		] | ||||||
| //! 	} | //! 	}, | ||||||
|  | //! 	"require": [ | ||||||
|  | //! 		"libchat" | ||||||
|  | //! 	] | ||||||
| //! } | //! } | ||||||
|  |  | ||||||
| // md5.js | // md5.js | ||||||
| @@ -678,57 +681,23 @@ XmlStanzaParser.prototype.parseNode = function(node) { | |||||||
|  |  | ||||||
| // end xmpp.js | // end xmpp.js | ||||||
|  |  | ||||||
|  | let ChatService = require("libchat").ChatService; | ||||||
|  |  | ||||||
| var gPingCount = 0; | var gPingCount = 0; | ||||||
|  |  | ||||||
| class XmppService { | class XmppService { | ||||||
| 	constructor(options) { | 	constructor(options) { | ||||||
| 		let self = this; | 		let self = this; | ||||||
| 		self._callbacks = [options.callback]; | 		self._service = new ChatService(options.callback); | ||||||
| 		self._conversations = {}; |  | ||||||
|  |  | ||||||
| 		network.newConnection().then(function(socket) { | 		network.newConnection().then(function(socket) { | ||||||
| 			self._socket = socket; | 			self._socket = socket; | ||||||
| 			return self._connect(options); | 			return self._connect(options); | ||||||
| 		}).catch(self._reportError); | 		}).catch(self._service.reportError); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	sendMessage(to, message) { | 	sendMessage(to, message) { | ||||||
| 		this._socket.write("<message type='groupchat' to='" + xmlEncode(to) + "'><body>" + xmlEncode(message) + "</body></message>"); | 		this._socket.write("<message type='groupchat' to='" + xmlEncode(to) + "'><body>" + xmlEncode(message) + "</body></message>").catch(this._service.reportError); | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	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); |  | ||||||
| 			} |  | ||||||
| 		} |  | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_connect(options) { | 	_connect(options) { | ||||||
| @@ -755,9 +724,7 @@ class XmppService { | |||||||
| 		let password = options.password; | 		let password = options.password; | ||||||
| 		let server = options.server; | 		let server = options.server; | ||||||
| 		self._socket.connect("jabber.troubleimpact.com", 5222).then(function() { | 		self._socket.connect("jabber.troubleimpact.com", 5222).then(function() { | ||||||
| 			print("actually connected"); | 			self._service.notifyStateChanged("connected"); | ||||||
| 			self.invokeCallback({action: "connected"}); |  | ||||||
| 			print("wtf"); |  | ||||||
| 			var parse = new XmlStanzaParser(1); | 			var parse = new XmlStanzaParser(1); | ||||||
| 			self._socket.write("<?xml version='1.0'?>"); | 			self._socket.write("<?xml version='1.0'?>"); | ||||||
| 			self._socket.write("<stream:stream to='" + xmlEncode(server) + "' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"); | 			self._socket.write("<stream:stream to='" + xmlEncode(server) + "' xmlns='jabber:client' xmlns:stream='http://etherx.jabber.org/streams' version='1.0'>"); | ||||||
| @@ -768,7 +735,7 @@ class XmppService { | |||||||
| 			self._socket.read(function(data) { | 			self._socket.read(function(data) { | ||||||
| 				try { | 				try { | ||||||
| 					if (!data) { | 					if (!data) { | ||||||
| 						self.invokeCallback({action: "disconnected"}); | 						self._service.notifyStateChanged("disconnected"); | ||||||
| 						return; | 						return; | ||||||
| 					} | 					} | ||||||
| 					parse.parse(data).forEach(function(stanza) { | 					parse.parse(data).forEach(function(stanza) { | ||||||
| @@ -800,19 +767,26 @@ class XmppService { | |||||||
| 							} else if (stanza.attributes.id == "session0") { | 							} else if (stanza.attributes.id == "session0") { | ||||||
| 								self._socket.write("<presence to='chadhappyfuntime@conference.jabber.troubleimpact.com/" + userName + "'><priority>1</priority><x xmlns='http://jabber.org/protocol/muc'/></presence>"); | 								self._socket.write("<presence to='chadhappyfuntime@conference.jabber.troubleimpact.com/" + userName + "'><priority>1</priority><x xmlns='http://jabber.org/protocol/muc'/></presence>"); | ||||||
| 								self._schedulePing(); | 								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") { | 							} else if (stanza.children.length && stanza.children[0].name == "ping") { | ||||||
| 								// Ping response. | 								// Ping response. | ||||||
| 							} else { | 							} else { | ||||||
| 								self.invokeCallback({ | 								self._service.notifyMessageReceived(null, {unknown: stanza}); | ||||||
| 									action: "unknown", |  | ||||||
| 									stanza: stanza, |  | ||||||
| 								}); |  | ||||||
| 							} | 							} | ||||||
| 						} else if (stanza.name == "message") { | 						} else if (stanza.name == "message") { | ||||||
| 							let message = self._convertMessage(stanza); | 							let message = self._convertMessage(stanza); | ||||||
| 							self._conversations[message.conversation].history.push(message); | 							let conversation = stanza.attributes.from; | ||||||
| 							self.invokeCallback(message); | 							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") { | 						} else if (stanza.name == "challenge") { | ||||||
| 							var challenge = Base64.decode(stanza.text); | 							var challenge = Base64.decode(stanza.text); | ||||||
| 							var parts = challenge.split(','); | 							var parts = challenge.split(','); | ||||||
| @@ -849,33 +823,16 @@ class XmppService { | |||||||
| 						} else if (stanza.name == "presence") { | 						} else if (stanza.name == "presence") { | ||||||
| 							let name = stanza.attributes.from.split('/', 2)[1]; | 							let name = stanza.attributes.from.split('/', 2)[1]; | ||||||
| 							let conversation = stanza.attributes.from.split('/', 2)[0]; | 							let conversation = stanza.attributes.from.split('/', 2)[0]; | ||||||
| 							let leaving = stanza.attributes.type == "unavailable"; | 							self._service.notifyPresenceChanged(conversation, name, stanza.attributes.type); | ||||||
| 							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, |  | ||||||
| 							}); |  | ||||||
| 						} else { | 						} else { | ||||||
| 							self.invokeCallback({ | 							self._service.notifyMessageReceived(null, {unknown: stanza}); | ||||||
| 								action: "unknown", |  | ||||||
| 								stanza: stanza, |  | ||||||
| 							}); |  | ||||||
| 						} | 						} | ||||||
| 					}); | 					}); | ||||||
| 				} catch (error) { | 				} catch (error) { | ||||||
| 					self._reportError(error); | 					self._service.reportError(error); | ||||||
| 				} | 				} | ||||||
| 			}); | 			}); | ||||||
| 		}).catch(self._reportError); | 		}).catch(self._service.reportError); | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	disconnect() { | 	disconnect() { | ||||||
| @@ -884,15 +841,6 @@ class XmppService { | |||||||
| 		delete gSessions[self._name]; | 		delete gSessions[self._name]; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| 	_reportError(error) { |  | ||||||
| 		this.invokeCallback({ |  | ||||||
| 			action: "error", |  | ||||||
| 			error: error, |  | ||||||
| 		}).catch(function(error) { |  | ||||||
| 			print(error); |  | ||||||
| 		}); |  | ||||||
| 	} |  | ||||||
|  |  | ||||||
| 	_convertMessage(stanza) { | 	_convertMessage(stanza) { | ||||||
| 		let self = this; | 		let self = this; | ||||||
| 		let text; | 		let text; | ||||||
| @@ -909,18 +857,8 @@ class XmppService { | |||||||
| 		if (from && from.indexOf('/') != -1) { | 		if (from && from.indexOf('/') != -1) { | ||||||
| 			from = from.split("/")[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 = { | 		let message = { | ||||||
| 			action: "message", |  | ||||||
| 			from: from, | 			from: from, | ||||||
| 			conversation: conversation, |  | ||||||
| 			message: text, | 			message: text, | ||||||
| 			stanza: stanza, | 			stanza: stanza, | ||||||
| 			timestamp: now, | 			timestamp: now, | ||||||
| @@ -937,23 +875,4 @@ class XmppService { | |||||||
| 	} | 	} | ||||||
| }; | }; | ||||||
|  |  | ||||||
| let gSessions = {}; | ChatService.handleMessages(XmppService); | ||||||
|  |  | ||||||
| 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), |  | ||||||
| 	}; |  | ||||||
| }); |  | ||||||
		Reference in New Issue
	
	Block a user