3 from facebook import Facebook, FacebookError
6 from contacts import ContactStore
10 """Encapsulate the process of syncing Facebook friends' information with the
11 Evolution contacts' database. This should be used as follows:
13 * Initialise, passing in a callback (methods: need_auth(),
14 block_for_auth(), use_twitter(), use_facebook()).
15 * Call load_friends().
16 * Call sync_contacts().
17 * Retrieve information on changes effected.
19 This requires two gconf paths to contain Facebook application keys:
20 /apps/maemo/hermes/key_app
21 /apps/maemo/hermes/key_secret
23 Copyright (c) Andrew Flegg <andrew@bleb.org> 2009.
24 Released under the Artistic Licence."""
27 # -----------------------------------------------------------------------
28 def __init__(self, callback, twitter = None, facebook = False):
29 """Constructor. Passed a callback which must implement three informational
32 need_auth() - called to indicate a login is about to occur. The user
35 block_for_auth() - prompt the user to take some action once they have
36 successfully logged in to Facebook.
38 progress(i, j) - the application is currently processing friend 'i' of
39 'j'. Should be used to provide the user a progress bar.
42 twitter - a username/password tuple or None if Twitter should not be
43 used. Defaults to None.
45 facebook - boolean indicating if Facebook should be used. Defaults to
49 self.gc = gnome.gconf.client_get_default()
50 self.callback = callback
51 self.twitter = twitter
52 self.facebook = facebook
54 # -- Check the environment is going to work...
56 if (self.gc.get_string('/desktop/gnome/url-handlers/http/command') == 'epiphany %s'):
57 raise Exception('Browser in gconf invalid (see NB#136012). Installation error.')
59 # -- Get private keys for this app...
61 key_app = self.gc.get_string('/apps/maemo/hermes/key_app')
62 key_secret = self.gc.get_string('/apps/maemo/hermes/key_secret')
63 if (key_app is None or key_secret is None):
64 raise Exception('No Facebook application keys found. Installation error.')
66 self.fb = Facebook(key_app, key_secret)
67 self.fb.desktop = True
70 # -----------------------------------------------------------------------
71 def do_fb_login(self):
72 """Perform authentication against Facebook and store the result in gconf
73 for later use. Uses the 'need_auth' and 'block_for_auth' methods on
74 the callback class. The former allows a message to warn the user
75 about what is about to happen to be shown; the second is to wait
76 for the user to confirm they have logged in."""
77 self.fb.session_key = None
81 self.callback.need_auth()
82 self.fb.auth.createToken()
84 self.callback.block_for_auth()
85 session = self.fb.auth.getSession()
87 self.gc.set_string('/apps/maemo/hermes/session_key', session['session_key'])
88 self.gc.set_string('/apps/maemo/hermes/secret_key', session['secret'])
89 self.gc.set_int('/apps/maemo/hermes/uid', session['uid'])
92 # -----------------------------------------------------------------------
93 def load_friends(self):
94 """Load information on the authenticated user's friends. If no user is
95 currently authenticated, prompts for a login."""
98 self.blocked_pictures = []
99 self.callback.progress(0, 0)
101 # -- Get a user session and retrieve Facebook friends...
104 if self.fb.session_key is None:
105 self.fb.session_key = self.gc.get_string('/apps/maemo/hermes/session_key')
106 self.fb.secret = self.gc.get_string('/apps/maemo/hermes/secret_key')
107 self.fb.uid = self.gc.get_int('/apps/maemo/hermes/uid')
109 # Check the available session is still valid...
112 if self.fb.users.getLoggedInUser() and self.fb.session_key:
114 except FacebookError:
118 # Get the list of friends...
119 attrs = ['uid', 'name', 'pic_big', 'birthday_date', 'profile_url']
120 for friend in self.fb.users.getInfo(self.fb.friends.get(), attrs):
121 friend['pic'] = friend[attrs[2]]
122 self.friends[friend['name']] = friend
123 if not friend['pic']:
124 self.blocked_pictures.append(friend)
126 # -- Retrieve following information from Twitter...
128 if self.twitter is not None:
129 (user, passwd) = self.twitter
130 api = twitter.Api(username=user, password=passwd)
131 users = api.GetFriends()
132 for friend in api.GetFriends():
133 self.friends[friend.name] = {'name': friend.name, 'pic': friend.profile_image_url, 'birthday_date': None, 'twitter_url': 'http://twitter.com/%s' % (friend.screen_name), 'homepage' : friend.url}
135 # TODO What if the user has *no* contacts?
138 # -----------------------------------------------------------------------
139 def sync_contacts(self, resync = False):
140 """Synchronise Facebook profiles to contact database. If resync is false,
141 no existing information will be overwritten."""
143 # -- Find addresses...
145 print "+++ Syncing contacts..."
146 addresses = evolution.ebook.open_addressbook('default')
147 print "+++ Addressbook opened..."
148 store = ContactStore(addresses)
149 print "+++ Contact store created..."
153 contacts = addresses.get_all_contacts()
154 contacts.sort(key=lambda obj: obj.get_name())
156 maximum = len(contacts)
157 for contact in contacts:
159 self.callback.progress(current, maximum)
161 for name in names.variants(contact.get_name()):
162 if name in self.friends:
163 friend = self.friends[name]
167 if friend['pic'] and (resync or contact.get_property('photo') is None):
168 updated = store.set_photo(contact, friend['pic']) or updated
170 if friend['birthday_date'] and (resync or contact.get_property('birth-date') is None):
171 date_str = friend['birthday_date'].split('/')
173 updated = store.set_birthday(contact, int(date_str[1]),
175 int(date_str[2])) or updated
177 if 'profile_url' in friend and friend['profile_url']:
178 updated = store.add_url(contact, friend['profile_url'], unique='facebook.com') or updated
180 if 'twitter_url' in friend and friend['twitter_url']:
181 updated = store.add_url(contact, friend['twitter_url'], unique='twitter.com') or updated
183 if 'homepage' in friend and friend['homepage']:
184 updated = store.add_url(contact, friend['homepage']) or updated
187 self.updated.append(contact)
188 addresses.commit_contact(contact)
189 print "Saved changes to [%s]" % (contact.get_name())
194 self.matched.append(contact)
196 self.unmatched.append(contact)