sugar-toolkit-gtk3/services/presence2/presenceservice.py
Dan Williams 9d837710f5 Listen for and respond to Owner property changes
Pass the PS's owner object to each plugin.  Make the server plugin
listen to property changes and push those changes to the Jabber
server when they occur.
2007-03-09 16:29:49 -05:00

325 lines
12 KiB
Python

# Copyright (C) 2007, Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
import gobject
import dbus, dbus.service, dbus.glib
from telepathy.client import ManagerRegistry, Connection
from telepathy.interfaces import (CONN_MGR_INTERFACE, CONN_INTERFACE)
from telepathy.constants import (CONNECTION_STATUS_CONNECTING, CONNECTION_STATUS_CONNECTED,
CONNECTION_STATUS_DISCONNECTED, CONNECTION_HANDLE_TYPE_CONTACT)
from server_plugin import ServerPlugin
from linklocal_plugin import LinkLocalPlugin
from buddy import Buddy, Owner
from activity import Activity
_PRESENCE_SERVICE = "org.laptop.Sugar.Presence"
_PRESENCE_INTERFACE = "org.laptop.Sugar.Presence"
_PRESENCE_PATH = "/org/laptop/Sugar/Presence"
class NotFoundError(dbus.DBusException):
def __init__(self):
dbus.DBusException.__init__(self)
self._dbus_error_name = _PRESENCE_INTERFACE + '.NotFound'
class PresenceService(dbus.service.Object):
def __init__(self):
self._next_object_id = 0
self._buddies = {} # key -> Buddy
self._handles_buddies = {} # tp client -> (handle -> Buddy)
self._activities = {} # activity id -> Activity
bus = dbus.SessionBus()
self._bus_name = dbus.service.BusName(_PRESENCE_SERVICE, bus=bus)
# Create the Owner object
objid = self._get_next_object_id()
self._owner = Owner(self._bus_name, objid)
self._buddies[self._owner.props.key] = self._owner
self._registry = ManagerRegistry()
self._registry.LoadManagers()
# Set up the server connection
self._server_plugin = ServerPlugin(self._registry, self._owner)
self._handles_buddies[self._server_plugin] = {}
self._server_plugin.connect('status', self._server_status_cb)
self._server_plugin.connect('contact-online', self._contact_online)
self._server_plugin.connect('contact-offline', self._contact_offline)
self._server_plugin.connect('avatar-updated', self._avatar_updated)
self._server_plugin.connect('properties-changed', self._properties_changed)
self._server_plugin.connect('contact-activities-changed', self._contact_activities_changed)
self._server_plugin.connect('activity-invitation', self._activity_invitation)
self._server_plugin.connect('private-invitation', self._private_invitation)
self._server_plugin.start()
# Set up the link local connection
self._ll_plugin = LinkLocalPlugin(self._registry, self._owner)
self._handles_buddies[self._ll_plugin] = {}
dbus.service.Object.__init__(self, self._bus_name, _PRESENCE_PATH)
def _server_status_cb(self, plugin, status, reason):
if status == CONNECTION_STATUS_CONNECTED:
pass
def _contact_online(self, tp, handle, props):
new_buddy = False
key = props['key']
buddy = self._buddies.get(key)
if not buddy:
# we don't know yet this buddy
objid = self._get_next_object_id()
buddy = Buddy(self._bus_name, objid, key=key)
buddy.connect("validity-changed", self._buddy_validity_changed_cb)
self._buddies[key] = buddy
buddies = self._handles_buddies[tp]
buddies[handle] = buddy
# store the handle of the buddy for this CM
buddy.handles[tp] = handle
buddy.set_properties(props)
def _buddy_validity_changed_cb(self, buddy, valid):
if valid:
self.BuddyAppeared(buddy.object_path())
print "New Buddy: %s (%s)" % (buddy.props.nick, buddy.props.color)
else:
self.BuddyDisappeared(buddy.object_path())
print "Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)
def _contact_offline(self, tp, handle):
buddy = self._handles_buddies[tp].pop(handle)
key = buddy.props.key
# the handle of the buddy for this CM is not valid anymore
buddy.handles.pop(tp)
if not buddy.handles:
if buddy.props.valid:
self.BuddyDisappeared(buddy.object_path())
print "Buddy left: %s (%s)" % (buddy.props.nick, buddy.props.color)
self._buddies.pop(key)
def _get_next_object_id(self):
"""Increment and return the object ID counter."""
self._next_object_id = self._next_object_id + 1
return self._next_object_id
def _avatar_updated(self, tp, handle, avatar):
buddy = self._handles_buddies[tp].get(handle)
if buddy and not buddy.props.owner:
print "Buddy %s icon updated" % buddy.props.key
buddy.props.icon = avatar
def _properties_changed(self, tp, handle, prop):
buddy = self._handles_buddies[tp].get(handle)
if buddy:
buddy.set_properties(prop)
#print "Buddy %s properties updated" % buddy.props.key
def _new_activity(self, activity_id, tp):
try:
objid = self._get_next_object_id()
activity = Activity(self._bus_name, objid, tp, id=activity_id)
except Exception, e:
print "Invalid activity: %s" % e
return None
activity.connect("validity-changed", self._activity_validity_changed_cb)
self._activities[activity_id] = activity
# FIXME
# Use values from the network
import random
names = ["Tommy", "Susie", "Jill", "Bryan", "Nathan", "Sophia", "Haley", "Jimmy"]
name = names[random.randint(0, len(names) - 1)]
activity.props.name = "Chat with %s" % name
activity.props.type = "org.laptop.Sugar.Chat"
from sugar.graphics import xocolor
color = xocolor.XoColor().to_string()
activity.props.color = color
return activity
def _remove_activity(self, activity):
print "remove activity", activity.props.id
self.ActivityDisappeared(activity.object_path())
del self._activities[activity.props.id]
def _contact_activities_changed(self, tp, contact_handle, activities):
print "------------activities changed-------------"
buddies = self._handles_buddies[tp]
buddy = buddies.get(contact_handle)
if not buddy:
# We don't know this buddy
# FIXME: What should we do here?
# FIXME: Do we need to check if the buddy is valid or something?
print "contact_activities_changed: buddy unknow"
return
old_activities = set()
for activity in buddy.get_joined_activities():
old_activities.add(activity.props.id)
new_activities = set(activities)
activities_joined = new_activities - old_activities
for act in activities_joined:
print "buddy", contact_handle, "joined", act
activity = self._activities.get(act)
if not activity:
# new activity, can fail
activity = self._new_activity(act, tp)
if activity:
activity.buddy_joined(buddy)
buddy.add_activity(activity)
activities_left = old_activities - new_activities
for act in activities_left:
print "buddy", contact_handle, "left", act
activity = self._activities.get(act)
if not activity:
continue
activity.buddy_left(buddy)
buddy.remove_activity(activity)
if not activity.get_joined_buddies():
self._remove_activity(activity)
# current activity
if len(activities) > 0:
buddy.set_properties({'current-activity':activities[0]})
def _activity_invitation(self, tp, act_id):
activity = self._activities.get(act_id)
if activity:
self.ActivityInvitation(activity.object_path())
def _private_invitation(self, tp, chan_path):
conn = tp.get_connection()
self.PrivateInvitation(str(conn.service_name), conn.object_path, chan_path)
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
def ActivityAppeared(self, activity):
pass
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
def ActivityDisappeared(self, activity):
pass
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
def BuddyAppeared(self, buddy):
pass
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
def BuddyDisappeared(self, buddy):
pass
@dbus.service.signal(_PRESENCE_INTERFACE, signature="o")
def ActivityInvitation(self, activity):
pass
@dbus.service.signal(_PRESENCE_INTERFACE, signature="soo")
def PrivateInvitation(self, bus_name, connection, channel):
pass
@dbus.service.method(_PRESENCE_INTERFACE, out_signature="ao")
def GetActivities(self):
ret = []
for act in self._activities.values():
ret.append(act.object_path())
return ret
@dbus.service.method(_PRESENCE_INTERFACE, in_signature="s", out_signature="o")
def GetActivityById(self, actid):
if self._activities.has_key(actid):
return self._activities[actid].object_path()
raise NotFoundError("The activity was not found.")
@dbus.service.method(_PRESENCE_INTERFACE, out_signature="ao")
def GetBuddies(self):
ret = []
for buddy in self._buddies.values():
ret.append(buddy.object_path())
return ret
@dbus.service.method(_PRESENCE_INTERFACE, in_signature="ay", out_signature="o")
def GetBuddyByPublicKey(self, key):
if self._buddies.has_key(key):
return self._buddies[key].object_path()
raise NotFoundError("The buddy was not found.")
@dbus.service.method(_PRESENCE_INTERFACE, out_signature="o")
def GetOwner(self):
if not self._owner:
raise NotFoundError("The owner was not found.")
else:
return self._owner.get_object_path()
@dbus.service.method(_PRESENCE_INTERFACE, in_signature="sssa{sv}", out_signature="o")
def ShareActivity(self, actid, atype, name, properties):
activity = self._share_activity(actid, atype, name, properties)
return activity.object_path()
def cleanup(self):
for tp in self._handles_buddies:
tp.cleanup()
def _share_activity(self, actid, atype, name, properties):
objid = self._get_next_object_id()
# FIXME check which tp client we should use to share the activity
color = self._owner.props.color
activity = Activity(self._bus_name, objid, self._server_plugin,
id=actid, type=atype, name=name, color=color, local=True)
activity.connect("validity-changed", self._activity_validity_changed_cb)
self._activities[actid] = activity
activity.join()
return activity
def _activity_validity_changed_cb(self, activity, valid):
if valid:
self.ActivityAppeared(activity.object_path())
print "New Activity: %s (%s)" % (activity.props.name, activity.props.id)
else:
self.ActivityDisappeared(activity.object_path())
print "Activity disappeared: %s (%s)" % (activity.props.name, activity.props.id)
def main():
loop = gobject.MainLoop()
ps = PresenceService()
try:
loop.run()
except KeyboardInterrupt:
ps.cleanup()
print 'Ctrl+C pressed, exiting...'
if __name__ == "__main__":
main()