diff --git a/chat/BuddyList.py b/chat/BuddyList.py new file mode 100644 index 00000000..570d9e73 --- /dev/null +++ b/chat/BuddyList.py @@ -0,0 +1,84 @@ +import presence +import avahi + +ACTION_BUDDY_ADDED = "added" +ACTION_BUDDY_REMOVED = "removed" + + +class Buddy(object): + def __init__(self, nick, realname, servicename, key=None): + self._nick = nick + self._realname = realname + self._servicename = servicename + self._key = key + + def nick(self): + return self._nick + + def realname(self): + return self._realname + + def servicename(self): + return self._servicename + + def key(self): + return self._key + +class BuddyList(object): + """ Manage a list of buddies """ + + def __init__(self): + self._listeners = [] + self._buddies = {} + self._pdiscovery = presence.PresenceDiscovery() + self._pdiscovery.add_service_listener(self._on_service_change) + self._pdiscovery.start() + + def add_buddy_listener(self, listener): + self._listeners.append(listener) + + def _add_buddy(self, host, address, servicename, data): + if len(data) > 0 and 'name' in data.keys(): + buddy = Buddy(data['name'], data['realname'], servicename) + self._buddies[data['name']] = buddy + self._notify_listeners(ACTION_BUDDY_ADDED, buddy) + + def _remove_buddy(self, buddy): + nick = buddy.nick() + self._notify_listeners(ACTION_BUDDY_REMOVED, buddy) + del self._buddies[nick] + + def _find_buddy_by_service_name(self, servicename): + for buddy in self._buddies.keys(): + if buddy.servicename() == servicename: + return buddy + return None + + def _notify_listeners(self, action, buddy): + for listener in self._listeners: + listener(action, buddy) + + def _on_service_change(self, action, interface, protocol, name, stype, domain, flags): + if stype != presence.OLPC_CHAT_SERVICE: + return + if action == presence.ACTION_SERVICE_NEW: + self._pdiscovery.resolve_service(interface, protocol, name, stype, domain, self._on_service_resolved) + elif action == presence.ACTION_SERVICE_REMOVED: + buddy = self._find_buddy_by_service_name(name) + if buddy: + self._remove_buddy(buddy) + + def _pair_to_dict(self, l): + res = {} + for el in l: + tmp = el.split('=', 1) + if len(tmp) > 1: + res[tmp[0]] = tmp[1] + else: + res[tmp[0]] = '' + return res + + def _on_service_resolved(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags): + data = self._pair_to_dict(avahi.txt_array_to_string_array(txt)) + self._add_buddy(host, address, name, data) + diff --git a/chat/chat.glade b/chat/chat.glade index 69f58c8a..f8195768 100644 --- a/chat/chat.glade +++ b/chat/chat.glade @@ -83,7 +83,7 @@ 0 - + 2 167 True diff --git a/chat/main.py b/chat/main.py index 5eff7429..dff8cde7 100755 --- a/chat/main.py +++ b/chat/main.py @@ -8,14 +8,16 @@ from SimpleGladeApp import SimpleGladeApp import presence import network import avahi +import BuddyList glade_dir = os.getcwd() class ChatApp(SimpleGladeApp): def __init__(self, glade_file="chat.glade", root="mainWindow", domain=None, **kwargs): - self._pdiscovery = presence.PresenceDiscovery() self._pannounce = presence.PresenceAnnounce() + self._buddy_list = BuddyList.BuddyList() + self._buddy_list.add_buddy_listener(self._on_buddy_presence_event) (self._nick, self._realname) = self._get_name() @@ -33,22 +35,26 @@ class ChatApp(SimpleGladeApp): realname = "Some Clueless User" return (nick, realname) - def new_service(self, action, interface, protocol, name, stype, domain, flags): - if action != 'added' or stype != presence.OLPC_CHAT_SERVICE: - return - self._pdiscovery.resolve_service(interface, protocol, name, stype, domain, self.service_resolved) - - def on_buddyList_buddy_selected(self, widget, *args): + def _on_buddyList_buddy_selected(self, widget, *args): (model, aniter) = widget.get_selection().get_selected() name = self.treemodel.get(aniter,0) print "Selected %s" % name - def on_buddyList_buddy_double_clicked(self, widget, *args): + def _on_buddyList_buddy_double_clicked(self, widget, *args): (model, aniter) = widget.get_selection().get_selected() name = self.treemodel.get(aniter,0) print "Double-clicked %s" % name - def on_main_window_delete(self, widget, *args): + def _on_buddy_presence_event(self, action, buddy): + if action == BuddyList.ACTION_BUDDY_ADDED: + aniter = self.treemodel.insert_after(None,None) + self.treemodel.set(aniter, 0, buddy.nick()) + elif action == BuddyList.ACCTION_BUDDY_REMOVED: + aniter = self.treemodel.get_iter(buddy.nick()) + if aniter: + self.treemodel.remove(iter) + + def _on_main_window_delete(self, widget, *args): self.quit() def _recv_group_message(self, msg): @@ -62,31 +68,15 @@ class ChatApp(SimpleGladeApp): self._gc_controller.send_msg(text) widget.set_text("") - def _pair_to_dict(self, l): - res = {} - for el in l: - tmp = el.split('=', 1) - if len(tmp) > 1: - res[tmp[0]] = tmp[1] - else: - res[tmp[0]] = '' - return res - - def service_resolved(self, interface, protocol, name, stype, domain, host, aprotocol, address, port, txt, flags): - data = self._pair_to_dict(avahi.txt_array_to_string_array(txt)) - if len(data) > 0 and 'name' in data.keys(): - aniter = self.treemodel.insert_after(None,None) - self.treemodel.set(aniter, 0, data['name']) - def new(self): self._group_chat_buffer = gtk.TextBuffer() self.chatView.set_buffer(self._group_chat_buffer) self.treemodel = gtk.TreeStore(gobject.TYPE_STRING) - self.buddyList.set_model(self.treemodel) - self.buddyList.connect("cursor-changed", self.on_buddyList_buddy_selected) - self.buddyList.connect("row-activated", self.on_buddyList_buddy_double_clicked) - self.mainWindow.connect("delete-event", self.on_main_window_delete) + self.buddyListView.set_model(self.treemodel) + self.buddyListView.connect("cursor-changed", self._on_buddyList_buddy_selected) + self.buddyListView.connect("row-activated", self._on_buddyList_buddy_double_clicked) + self.mainWindow.connect("delete-event", self._on_main_window_delete) self.entry.connect("activate", self._send_group_message) renderer = gtk.CellRendererText() @@ -94,11 +84,10 @@ class ChatApp(SimpleGladeApp): column.set_resizable(True) column.set_sizing("GTK_TREE_VIEW_COLUMN_GROW_ONLY"); column.set_expand(True); - self.buddyList.append_column(column) + self.buddyListView.append_column(column) - self._pannounce.register_service(self._realname, 6666, presence.OLPC_CHAT_SERVICE, name=self._nick) - self._pdiscovery.add_service_listener(self.new_service) - self._pdiscovery.start() + self._pannounce.register_service(self._realname, 6666, presence.OLPC_CHAT_SERVICE, + name = self._nick, realname = self._realname) self._gc_controller = network.GroupChatController('224.0.0.221', 6666, self._recv_group_message) self._gc_controller.start() diff --git a/chat/presence.py b/chat/presence.py index 55d8011c..45a31f96 100644 --- a/chat/presence.py +++ b/chat/presence.py @@ -1,10 +1,9 @@ -#!/usr/bin/python -t - - import avahi, dbus, dbus.glib OLPC_CHAT_SERVICE = "_olpc_chat._udp" +ACTION_SERVICE_NEW = 'new' +ACTION_SERVICE_REMOVED = 'removed' class PresenceDiscovery(object): def __init__(self): @@ -33,15 +32,13 @@ class PresenceDiscovery(object): def new_service(self, interface, protocol, name, stype, domain, flags): # print "Found service '%s' (%d) of type '%s' in domain '%s' on %i.%i." % (name, flags, stype, domain, interface, protocol) - for listener in self._service_listeners: - listener('added', interface, protocol, name, stype, domain, flags) + listener(ACTION_SERVICE_NEW, interface, protocol, name, stype, domain, flags) def remove_service(self, interface, protocol, name, stype, domain, flags): # print "Service '%s' of type '%s' in domain '%s' on %i.%i disappeared." % (name, stype, domain, interface, protocol) - for listener in self._service_listeners: - listener('removed', interface, protocol, name, stype, domain, flags) + listener(ACTION_SERVICE_REMOVED, interface, protocol, name, stype, domain, flags) def new_service_type(self, interface, protocol, stype, domain, flags): # Are we already browsing this domain for this type?