Refactor improvements from Fredrik Wendt; and initial LinkedIn and
[hermes] / package / src / org / maemo / hermes / engine / facebook / service.py
1 import gnome.gconf
2 import org.maemo.hermes.engine.service
3
4 from facebook import Facebook,FacebookError
5 from org.maemo.hermes.engine.names import canonical
6 from org.maemo.hermes.engine.friend import Friend
7
8 class Service(org.maemo.hermes.engine.service.Service):
9     """Facebook backend for Hermes.
10                 
11        This requires two gconf paths to contain Facebook application keys:
12            /apps/maemo/hermes/key_app
13            /apps/maemo/hermes/key_secret
14        
15        Copyright (c) Andrew Flegg <andrew@bleb.org> 2010.
16        Released under the Artistic Licence."""
17        
18        
19     # -----------------------------------------------------------------------
20     def __init__(self, autocreate = False, gui_callback = None):
21         """Initialise the Facebook service, finding Facebook API keys in gconf and
22            having a gui_callback available."""
23         
24         self._gc  = gnome.gconf.client_get_default()
25         self._gui = gui_callback
26         self._autocreate = autocreate
27         
28         # -- Check the environment is going to work...
29         #
30         if (self._gc.get_string('/desktop/gnome/url-handlers/http/command') == 'epiphany %s'):
31             raise Exception('Browser in gconf invalid (see NB#136012). Installation error.')
32
33         key_app    = self._gc.get_string('/apps/maemo/hermes/key_app')
34         key_secret = self._gc.get_string('/apps/maemo/hermes/key_secret')
35         if key_app is None or key_secret is None:
36             raise Exception('No Facebook application keys found. Installation error.')
37
38         self.fb = Facebook(key_app, key_secret)
39         self.fb.desktop = True
40         
41         self._friends = None
42         self._friends_by_url = {}
43         self._friends_by_contact = {}
44         self._should_create = set()
45         
46         self.get_friends()
47
48
49     # -----------------------------------------------------------------------
50     def get_name(self):
51         return "Facebook"
52     
53
54     # -----------------------------------------------------------------------
55     def _do_fb_login(self):
56         """Perform authentication against Facebook and store the result in gconf
57              for later use. Uses the 'need_auth' and 'block_for_auth' methods on
58              the callback class. The former allows a message to warn the user
59              about what is about to happen to be shown; the second is to wait
60              for the user to confirm they have logged in."""
61         self.fb.session_key = None
62         self.fb.secret = None
63         self.fb.uid = None
64         
65         if self._gui:
66             self._gui.need_auth()
67             
68         self.fb.auth.createToken()
69         self.fb.login()
70         
71         if self._gui:
72             self._gui.block_for_auth()
73           
74         session = self.fb.auth.getSession()
75         self._gc.set_string('/apps/maemo/hermes/session_key', session['session_key'])
76         self._gc.set_string('/apps/maemo/hermes/secret_key', session['secret'])
77         self._gc.set_string('/apps/maemo/hermes/uid', str(session['uid']))
78
79    
80     # -----------------------------------------------------------------------
81     def get_friends(self):
82         """Return a list of friends from this service, or 'None' if manual mapping
83            is not supported."""
84            
85         if self._friends:
86             return self._friends.values()
87          
88         if self.fb.session_key is None:
89             self.fb.session_key = self._gc.get_string('/apps/maemo/hermes/session_key')
90             self.fb.secret = self._gc.get_string('/apps/maemo/hermes/secret_key')
91             self.fb.uid = self._gc.get_string('/apps/maemo/hermes/uid')
92         
93         # Check the available session is still valid...
94         while True:
95             try:
96                 if self.fb.users.getLoggedInUser() and self.fb.session_key:
97                     break
98             except FacebookError:
99                 pass
100             self._do_fb_login()
101             
102             
103         def if_defined(data, key, callback):
104             if key in data and data[key]:
105                 callback(data[key])
106         
107         self._friends = {}
108         attrs = ['uid', 'name', 'pic_big', 'birthday_date', 'profile_url', 'first_name', 'last_name', 'website']
109         for data in self.fb.users.getInfo(self.fb.friends.get(), attrs):
110             key = canonical(data['name'])
111             friend = Friend(data['name'])
112             self._friends[key] = friend
113         
114             if 'profile_url' not in data:
115                 data['profile_url'] = "http://www.facebook.com/profile.php?id=" + str(data['uid'])
116         
117             self._friends_by_url[data['profile_url']] = friend
118             
119
120             if_defined(data, 'website', friend.add_url)
121             if_defined(data, 'profile_url', friend.add_url)
122             if_defined(data, 'birthday_date', friend.set_birthday_date)
123
124             # exception from the rule:
125 #            if_defined(data, 'pic_big', friend.set_photo_url)
126             friend.set_photo_url(data[attrs[2]])
127             
128             if friend.has_birthday_date():
129                 self._should_create.add(friend)
130                 
131         return self._friends.values()
132
133
134     # -----------------------------------------------------------------------
135     def pre_process_contact(self, contact):
136         
137         if not self._friends:
138             self.get_friends()
139         
140         for url in contact.get_urls():
141             if url in self._friends_by_url:
142                 matched_friend = self._friends_by_url[url]
143                 self._friends_by_contact[contact] = matched_friend
144                 self._should_create.discard(matched_friend)
145         
146     
147     # -----------------------------------------------------------------------
148     def process_contact(self, contact, friend):
149            
150         if not self._friends:
151             self.get_friends()
152
153         if self._friends_by_contact.has_key(contact):
154             friend.update(self._friends_by_contact[contact])
155             return
156         
157         matched_friend = None
158         # we might get a hit if the friend has setup a URL with another service 
159         for url in contact.get_urls():
160             if url in self._friends_by_url:
161                 matched_friend = self._friends_by_url[url]
162                 self._friends_by_contact[contact] = matched_friend
163                 break
164
165         if not matched_friend:
166             for id in contact.get_identifiers():
167                 if id in self._friends:
168                     matched_friend = self._friends[id]
169                     self._friends_by_contact[contact] = matched_friend
170                     break
171                 
172         if matched_friend:
173             print contact.get_name(), " -> ", matched_friend
174             self._should_create.discard(matched_friend)
175         
176             
177     
178     
179     # -----------------------------------------------------------------------
180     def finalise(self, updated, overwrite = False):
181         """Once all contacts have been processed, allows for any tidy-up/additional
182            enrichment. If any contacts are updated at this stage, 'updated' should
183            be added to."""
184
185         if True or self._autocreate: # FIXME - remove True or
186             for friend in self._should_create:
187                 print "Need to autocreate: %s (%s)" % (friend._attributes, friend._multi_attributes)