Merge lp:~eduardo-mucelli/cairo-dock-plug-ins-extras/Twitter into lp:~cairo-dock-team/cairo-dock-plug-ins-extras/third-party

Proposed by Eduardo Mucelli Rezende Oliveira
Status: Merged
Merged at revision: 262
Proposed branch: lp:~eduardo-mucelli/cairo-dock-plug-ins-extras/Twitter
Merge into: lp:~cairo-dock-team/cairo-dock-plug-ins-extras/third-party
Diff against target: 1077 lines (+569/-235)
13 files modified
Twitter/.keys (+0/-1)
Twitter/.keys.cfg (+7/-0)
Twitter/ChangeLog (+1/-0)
Twitter/Twitter (+147/-223)
Twitter/Twitter.conf (+1/-1)
Twitter/auto-load.conf (+1/-1)
Twitter/emblem.py (+10/-0)
Twitter/identica.py (+113/-0)
Twitter/message.py (+10/-0)
Twitter/networks.py (+26/-0)
Twitter/twitter.py (+181/-0)
Twitter/user.py (+44/-0)
Twitter/util.py (+28/-9)
To merge this branch: bzr merge lp:~eduardo-mucelli/cairo-dock-plug-ins-extras/Twitter
Reviewer Review Type Date Requested Status
Matthieu Baerts Approve
Review via email: mp+105573@code.launchpad.net

Description of the change

Added support to Identi.ca! Many code changes, refactoring, and improving. Ps.: At the moment is possible only to post a tweet on Identi.ca. The applet post both on Twitter and Identi.ca the same tweet.

To post a comment you must log in.
Revision history for this message
Eduardo Mucelli Rezende Oliveira (eduardo-mucelli) wrote :

Massive code changing, and remodularization, possible that some bugs were introduced. I still need lots of time to polish the code, honestly, I do not like the code the way it is. Identi.ca support was too much obstrusive, and I needed to redesign almost everything on the code.

Revision history for this message
Matthieu Baerts (matttbe) wrote :

Thank you for this new feature ;)

It's maybe time to change the name of this applet ;) (by using bzr mv)

Revision history for this message
Matthieu Baerts (matttbe) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== removed file 'Twitter/.keys'
2--- Twitter/.keys 2011-12-17 00:14:56 +0000
3+++ Twitter/.keys 1970-01-01 00:00:00 +0000
4@@ -1,1 +0,0 @@
5-OzZoSVpO6PZqByM15MsLlg mKsbuXgpHEO6C2axmUI8cPUt0ZPCbDb67uvT5wOIW1s
6
7=== added file 'Twitter/.keys.cfg'
8--- Twitter/.keys.cfg 1970-01-01 00:00:00 +0000
9+++ Twitter/.keys.cfg 2012-05-13 01:03:19 +0000
10@@ -0,0 +1,7 @@
11+[twitter]
12+consumer_key: OzZoSVpO6PZqByM15MsLlg
13+consumer_secret: mKsbuXgpHEO6C2axmUI8cPUt0ZPCbDb67uvT5wOIW1s
14+
15+[identica]
16+consumer_key: 490af31436e6f4cbd7bbf76e3a84ee5d
17+consumer_secret: 9796c4e80d43373135849c014b227182
18
19=== modified file 'Twitter/ChangeLog'
20--- Twitter/ChangeLog 2012-04-15 17:42:50 +0000
21+++ Twitter/ChangeLog 2012-05-13 01:03:19 +0000
22@@ -1,3 +1,4 @@
23+0.3: (May/13/2012): Added support to Identi.ca! Many code changes, refactoring, and improving.
24 0.2.2: (April/15/2012): Configurable notifications.
25 0.2.1: (March/22/2012): Added user timeline.
26 0.2: (March/19/2012): Possible to retweet tweets. Tweets being shown with standard menu.
27
28=== modified file 'Twitter/Twitter'
29--- Twitter/Twitter 2012-04-17 18:40:21 +0000
30+++ Twitter/Twitter 2012-05-13 01:03:19 +0000
31@@ -27,154 +27,19 @@
32 # To see the received direct messages right-click on the icon -> Twitter -> [New] direct messages. You can reply any of them by left-clicking on it.
33 # To see some user's info right-click on the icon -> Twitter -> Info
34
35-import os, webbrowser, simplejson, threading, Queue, urllib2
36-from oauth import oauth
37-from util import *
38+import os, webbrowser, Queue
39
40-from http import post, get #, stream
41-import emblem, menu
42-from message import DirectMessage, Tweet
43+from networks import Networks # networks.py
44+from user import User # user.py
45+import emblem, menu # emblem.py, menu.py
46+from message import DirectMessage, Tweet # message.py
47+from util import * # util.py
48
49 from CDApplet import CDApplet, _
50-# TODO import ConfigParser later conver files to config syntax
51-
52-class TwitterOauth:
53- def __init__(self):
54- self.request_token_url = 'https://twitter.com/oauth/request_token'
55- self.access_token_url = 'https://twitter.com/oauth/access_token'
56- self.authorize_url = 'https://twitter.com/oauth/authorize'
57-
58- consumer_key, consumer_secret = read_consumer_key_and_secret()
59- self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
60- self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
61- self.request_token = None
62- self.access_token = None
63-
64- def get_authorization_url(self):
65- self.request_token = self.get_unauthorized_request_token()
66- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
67- token = self.request_token,
68- http_url = self.authorize_url)
69- oauth_request.sign_request(self.signature_method, self.consumer, self.request_token)
70- return oauth_request.to_url()
71-
72- def get_unauthorized_request_token(self):
73- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url = self.request_token_url)
74- oauth_request.sign_request(self.signature_method, self.consumer, None)
75- url = oauth_request.to_url()
76- response = get(url)
77- token = oauth.OAuthToken.from_string(response)
78- return token
79-
80- # Exchange request token for access token
81- def get_access_token_and_secret(self, pin):
82- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
83- http_url = self.access_token_url,
84- verifier = pin,
85- token = self.request_token)
86- oauth_request.sign_request(self.signature_method, self.consumer, self.request_token)
87- url = oauth_request.to_url()
88- response = get(url)
89- self.access_token = oauth.OAuthToken.from_string(response) # create both .key and .secret attributes
90- return self.access_token.key, self.access_token.secret
91-
92-# TODO: Check also the possible inheritance with TwitterOauth
93-class API():
94- def __init__(self, access_key, access_secret):
95- self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
96- consumer_key, consumer_secret = read_consumer_key_and_secret()
97- self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
98- self.access_token = oauth.OAuthToken(access_key, access_secret)
99-
100-class TwitterStreamAPI(API):
101- def __init__(self, access_key, access_secret, callback):
102- API.__init__(self, access_key, access_secret)
103-
104- self.user_stream_url = "https://userstream.twitter.com/2/user.json"
105- self.callback = callback # called as soon as a new entry on the stream appears
106- thread = threading.Thread(target=self.tweet_streaming) # keep checking for new entries on the stream
107- thread.start() # run, forrest run
108-
109- def tweet_streaming(self):
110- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
111- token = self.access_token,
112- http_url = self.user_stream_url)
113- oauth_request.sign_request(self.signature_method, self.consumer, self.access_token)
114-
115- url = oauth_request.to_url()
116- req = urllib2.urlopen(url)
117-
118- buffer = ''
119- while True:
120- chunk = req.read(1) # read character per character from the connection ...
121- if not chunk:
122- break
123-
124- buffer += chunk
125- tweets = buffer.split("\n",1) # ... until find the end of a tweet marked with a '\n'
126- if len(tweets) > 1:
127- content = tweets[0]
128- if "text" in content:
129- content = simplejson.loads(content)
130- logp("Received from Twitter Stream: %s" % content)
131- self.callback(content) # at the moment this method is called 'on_receive_new_entry_into_stream_callback'
132- buffer = tweets[1]
133-
134-class TwitterAPI(API):
135- def __init__(self, access_key, access_secret):
136- API.__init__(self, access_key, access_secret)
137-
138- self.update_url = 'https://api.twitter.com/1/statuses/update.json'
139- self.home_timeline_url = 'https://api.twitter.com/1/statuses/home_timeline.json'
140- self.direct_messages_url = 'https://api.twitter.com/1/direct_messages.json'
141- self.new_direct_messages_url = 'https://api.twitter.com/1/direct_messages/new.json'
142- self.verify_credentials_url = 'https://api.twitter.com/1/account/verify_credentials.json'
143- self.user_timeline_url = 'https://api.twitter.com/1/statuses/user_timeline.json'
144- self.retweet_url_prefix = 'https://api.twitter.com/1/statuses/retweet/' # lacks the id of the tweet to be retweeted
145-
146- def dispatch(self, url, mode, parameters={}):
147- oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
148- token = self.access_token,
149- http_url = url,
150- parameters = parameters,
151- http_method = mode)
152- oauth_request.sign_request(self.signature_method, self.consumer, self.access_token)
153- if mode == "GET":
154- url = oauth_request.to_url()
155- response = get(url)
156- return simplejson.loads(response)
157- elif mode == "POST":
158- header = oauth_request.to_header()
159- post(url, parameters, header)
160-
161- def tweet(self, message): # popularly "send a tweet"
162- self.dispatch(self.update_url, "POST", {'status':message})
163-
164- def retweet(self, tweet_id):
165- url = "%s%s.json" % (self.retweet_url_prefix, tweet_id)
166- self.dispatch(url, "POST")
167-
168- def new_direct_message(self, message, destinatary):
169- self.dispatch(self.new_direct_messages_url, "POST", {'text':message, 'screen_name':destinatary})
170-
171- def home_timeline(self):
172- return self.dispatch(self.home_timeline_url, "GET")
173-
174- #TODO: Use it.
175- def user_timeline(self):
176- return self.dispatch(self.user_timeline_url, "GET")
177-
178- def direct_messages(self):
179- return self.dispatch(self.direct_messages_url, "GET")
180-
181- def verify_credentials(self):
182- return self.dispatch(self.verify_credentials_url, "GET")
183-
184-class User:
185- def __init__(self, screen_name="", access_key="", access_secret=""):
186- self.screen_name = screen_name
187- self.access_key = access_key
188- self.access_secret = access_secret
189+
190+# Twitter ----> Networks |----> twitter ---> Oauth, TwitterAPI, TwitterStreamAPI
191+# |
192+# |----> identica ---> Oauth, IdenticaAPI
193
194 class Applet(CDApplet):
195
196@@ -187,7 +52,7 @@
197 def refresh_emblem_counter(self):
198 counter = self.tweet_stream.qsize() + self.message_stream.qsize()
199 if counter > 0:
200- self.emblem.update(counter) # create the emblem with the counter
201+ self.emblem.update(counter) # create the emblem with the counter
202 self.icon.SetIcon(self.emblem.emblem)
203 else:
204 self.icon.SetIcon(os.path.abspath("./icon"))
205@@ -221,14 +86,14 @@
206 # TwitterStreamAPI(access_key, access_secret, self.on_receive_new_entry_into_stream_callback)
207 def on_receive_new_entry_into_stream_callback(self, entry):
208 if 'user' in entry: # tweet
209- if not entry['user']['screen_name'] == self.user.screen_name: # not sent by the own user
210+ if not entry['user']['screen_name'] == self.twitter.user.screen_name: # not sent by the own user
211 logp("Inserting new tweet on the stream Queue: %s" % entry)
212 self.tweet_stream.put(entry) # put the new tweet on the stream queue
213 self.refresh_emblem_counter()
214 if self.config['alert']: # user wants alert?
215 self.send_alert()
216 elif 'direct_message' in entry: # direct messages
217- if not entry['direct_message']['sender']['screen_name'] == self.user.screen_name: # not sent by the own user
218+ if not entry['direct_message']['sender']['screen_name'] == self.twitter.user.screen_name: # not sent by the own user
219 logp("Inserting new message on the message Queue: %s" % entry)
220 self.message_stream.put(entry) # put the new message on the message queue
221 self.refresh_emblem_counter()
222@@ -238,7 +103,7 @@
223 def show_user_timeline(self):
224 self.inform_start_of_waiting_process()
225
226- timeline = self.api.user_timeline()
227+ timeline = self.twitter_api.user_timeline()
228 if len(timeline) > 0:
229 ut_menu = menu.Menu(self.icon) # callback not set since there is no action when clicking ...
230 for status in timeline: # ... on the menu generated from this list
231@@ -272,7 +137,7 @@
232 def show_home_timeline(self):
233 self.inform_start_of_waiting_process()
234
235- timeline = self.api.home_timeline()
236+ timeline = self.twitter_api.home_timeline()
237 if len(timeline) > 0:
238 ht_menu = menu.Menu(self.icon, self.on_tweet_list_menu_clicked)
239 for status in timeline:
240@@ -305,7 +170,7 @@
241 def show_direct_messages(self):
242 self.inform_start_of_waiting_process()
243
244- messages = self.api.direct_messages()
245+ messages = self.twitter_api.direct_messages()
246 if len(messages) > 0:
247 dm_menu = menu.Menu(self.icon, self.on_direct_messages_list_menu_clicked)
248 for status in messages:
249@@ -328,99 +193,143 @@
250
251 def tweet(self, message): # popularly "send a tweet"
252 self.inform_start_of_waiting_process()
253- self.api.update_status(message)
254+ self.twitter_api.update_status(message)
255 self.inform_end_of_waiting_process()
256
257 def show_credentials(self):
258 self.inform_start_of_waiting_process()
259- credentials = self.api.verify_credentials()
260+ credentials = self.twitter_api.verify_credentials()
261 message = _("%s [<b>%s</b>]\nFollowers: %s\nFriends: %s\nTweets: %s\n") % (credentials['name'], credentials['screen_name'], credentials['followers_count'], credentials['friends_count'], credentials['statuses_count'])
262 dialog = {'use-markup':True}
263 self.inform_end_of_waiting_process()
264 self.show_popup_message(message, dialog)
265
266- # Applet methods
267+ # Dialogs for some kind of input
268
269 def ask_for_direct_message_reply(self, destinatary):
270 dialog = {'buttons':'ok;cancel'}
271 widget = {'widget-type':'text-entry', 'nb-chars':self.tweet_length + 1} # 140 characters max, a direct message
272- self.show_popup_message((_("%s, write a reply to %s")) % (self.user.screen_name, destinatary), dialog, widget)
273+ self.show_popup_message((_("%s, write a reply to %s")) % (self.twitter.user.screen_name, destinatary), dialog, widget)
274 self.dialog_type = self.responding_sending_direct_message_reply
275 self.replying_direct_message_to = destinatary
276+
277+ def responding_sending_direct_message_reply(self, content):
278+ logp("Sending a direct message '%s'" % content)
279+ self.twitter_api.new_direct_message(content, self.replying_direct_message_to)
280
281- # Opens the dialog to be filled with the tweet and also
282- # informs about tweet longer than 140 characters
283+ # Opens the dialog to be filled with the tweet and also informs about tweet longer than 140 characters
284 def ask_for_tweet(self, default="", warning=False):
285 dialog = {'buttons':'ok;cancel'}
286 widget = {'widget-type':'text-entry', 'nb-chars':self.tweet_length + 1, 'initial-value':default} # 140 characters max, a tweet :)
287 if not warning:
288- message = _("%s, send a tweet") % self.user.screen_name
289+ message = _("%s, send a tweet") % self.twitter.user.screen_name
290 self.dialog_type = self.responding_tweet
291 else: # user entered more than 140 characters
292 message = _("Your tweet has more than %d characters, please make it shorter") % self.tweet_length
293 self.dialog_type = self.responding_tweet
294 self.show_popup_message(message, dialog, widget)
295
296+ def responding_to_tweet(self, content):
297+ if len(content) > self.tweet_length:
298+ self.ask_for_tweet(content, warning=True)
299+ else:
300+ logp("Sending a tweet '%s'" % content)
301+ self.twitter_api.tweet(content)
302+ self.identica_api.tweet(content)
303+
304 def ask_for_retweet(self, tweet_id):
305 dialog = {'buttons':'ok;cancel'}
306- self.show_popup_message((_("%s, retweet?")) % self.user.screen_name, dialog)
307+ self.show_popup_message((_("%s, retweet?")) % self.twitter.user.screen_name, dialog)
308 self.dialog_type = self.responding_retweet
309 self.retweet_tweet_id = tweet_id
310-
311- # TODO: Implement it as a config file using screen_name as section index
312- def read_user_data(self):
313- """Read the users file formated as Screen Name<space>Access Key<space>Access Secret"""
314- found = False
315- if os.path.exists(self.user_file):
316- if os.path.getsize(self.user_file) > 0:
317- f = open(self.user_file, "rb")
318- data = f.read()
319- self.user.screen_name, self.user.access_key, self.user.access_secret = data.split() # split the line by space token
320- f.close()
321- found = True
322- return found
323-
324- def write_user_data(self):
325- f = open(self.user_file, 'w')
326- f.write("%s %s %s" % (self.user.screen_name, self.user.access_key, self.user.access_secret))
327- f.close()
328+
329+ def responding_to_retweet(self):
330+ logp("Retweeting")
331+ self.twitter_api.retweet(self.retweet_tweet_id)
332+
333+ # Dialogs for the initial wizard to authorize the user
334+ # Initial Information -> Ask for screen name (username) -> Ask for authorization (open browser) -> Insert PIN -> Succesful connected
335
336 def show_initial_informations(self):
337- message = _("Twitter Applet needs your nickname, and an authorization\nthat you accept it to connect on your Twitter account")
338+ if not self.adding_identica:
339+ message = _("Twitter Applet needs your nickname, and an authorization\nthat you accept it to connect on your Twitter account")
340+ else:
341+ message = _("Identi.ca Applet needs your nickname, and an authorization\nthat you accept it to connect on your Identi.ca account")
342 dialog = {'buttons':'next'}
343 self.show_popup_message(message, dialog)
344 self.dialog_type = self.responding_initial_informations
345+
346+ def responding_to_initial_informations(self):
347+ self.ask_for_screen_name()
348
349 def ask_for_screen_name(self):
350- message = _("What is your Twitter nickname?")
351+ if not self.adding_identica:
352+ message = _("What is your Twitter nickname?")
353+ else:
354+ message = _("What is your Identi.ca nickname?")
355 dialog = {'buttons':'next'}
356 widget = {'widget-type':'text-entry'}
357 self.show_popup_message(message, dialog, widget)
358 self.dialog_type = self.responding_screen_name
359
360+ def responding_to_screen_name(self, content):
361+ logp("Receiving screen name '%s'" % content)
362+ if not self.adding_identica:
363+ self.twitter.user.screen_name = content
364+ else:
365+ self.identica.user.screen_name = content
366+ self.ask_for_authorization()
367+
368 def ask_for_authorization(self):
369- authorization_url = self.twitter_auth.get_authorization_url()
370+ if not self.adding_identica:
371+ authorization_url = self.twitter_auth.get_authorization_url()
372+ network = "Twitter"
373+ else:
374+ authorization_url = self.identica_auth.get_authorization_url()
375+ network = "Identi.ca"
376 logp("Opening the auth URL '%s'" % authorization_url)
377 dialog = {'buttons':'next'}
378 try:
379 webbrowser.open(authorization_url)
380- message = _("Twitter applet needs you to give the authorization. Authorization page was opened on your browser. As soon as you authorize it, copy the PIN number that will be shown, and close this dialog")
381+ message = _("%s applet needs you to give the authorization. Authorization page was opened on your browser. As soon as you authorize it, copy the PIN number that will be shown, and go to the next dialog" % network)
382 self.show_popup_message(message, dialog)
383 except webbrowser.Error:
384- message = _("Twitter applet needs you to give the authorization. Copy the address bellow and access it with your browser. Copy the PIN number that will be shown as soon as you authorize")
385+ message = _("%s applet needs you to give the authorization. Copy the address bellow and access it with your browser. Copy the PIN number that will be shown as soon as you authorize" % network)
386 widget = {'widget-type':'text-entry', 'initial-value':authorization_url}
387 self.show_popup_message(message, dialog, widget)
388 self.dialog_type = self.responding_authorization
389
390+ def responding_to_authorization(self):
391+ logp("Asking for PIN")
392+ self.ask_for_pin_number() # ask for the PIN number received when acessed the auth URL
393+
394 def ask_for_pin_number(self):
395- message = _("Enter the PIN number on the authorization page")
396+ message = _("Enter the PIN number shown on the authorization page")
397 dialog = {'buttons':'next'}
398 widget = {'widget-type':'text-entry'}
399 self.show_popup_message(message, dialog, widget)
400 self.dialog_type = self.responding_pin
401-
402- def show_popup_successful_connection(self):
403- self.show_popup_message(_("Successfully connected with Twitter"))
404+
405+ def responding_to_pin(self, content):
406+ logp("Receiving PIN: %s" % content)
407+ if not self.adding_identica:
408+ self.twitter.user.access_key, self.twitter.user.access_secret = self.twitter_auth.get_access_token_and_secret(content)
409+ logp("Writing user data")
410+ self.twitter.user.write() # writing user's access token and access secret to be ...
411+ self.twitter_api = self.twitter.get_api(self.on_receive_new_entry_into_stream_callback) # ... used here, check twitter.py method get_api
412+ if self.twitter_api:
413+ self.show_popup_message(_("Successfully connected with Twitter"))
414+ else:
415+ logm("A problem has occurred while getting access to Twitter API")
416+ else:
417+ self.identica.user.access_key, self.identica.user.access_secret = self.identica_auth.get_access_token_and_secret(content)
418+ logp("Writing user data")
419+ self.identica.user.write() # writing user's access token and access secret to be ...
420+ self.identica_api = self.identica.get_api() # ... used here, check identica.py method get_api
421+ if self.identica_api:
422+ self.show_popup_message(_("Successfully connected with Identi.ca"))
423+ else:
424+ logm("A problem has occurred while getting access to the Identi.ca API")
425
426 def show_popup_message(self, message, dialog={}, widget={}):
427 dialog_attributes = {'message':message}
428@@ -429,6 +338,8 @@
429 widget_attributes.update(widget)
430 self.icon.PopupDialog (dialog_attributes, widget_attributes)
431
432+ # Menus
433+
434 def build_direct_messages_menu(self):
435 direct_messages_menu = []
436 if self.message_stream.empty():
437@@ -477,14 +388,37 @@
438 'icon' : os.path.abspath("./data/tweet.png")
439 })
440 self.icon.AddMenuItems(user_timeline_menu)
441+
442+ def build_identica_menu(self):
443+ identica_menu = []
444+ menu = {
445+ 'type' : CDApplet.MENU_SUB_MENU,
446+ 'label' : _("Identi.ca"),
447+ 'id' : self.identica_menu_id,
448+ 'icon' : os.path.abspath("./data/identica.png")
449+ }
450+ sub_menu = {
451+ 'type' : CDApplet.MENU_ENTRY,
452+ 'label' : _("Add"),
453+ 'id' : self.add_identica_menu_id,
454+ 'menu' : self.identica_menu_id
455+ }
456+ identica_menu.append (menu)
457+ identica_menu.append (sub_menu)
458+ self.icon.AddMenuItems(identica_menu)
459
460 def __init__(self):
461- self.user = User()
462- self.user_file = os.path.abspath(os.path.join(os.getcwd(),'..','..','.twitter_users')) # ~/.config/cairo-dock/.twitter_users
463- self.twitter_auth = TwitterOauth()
464-
465- self.api = None
466- self.stream_api = None
467+
468+ self.networks = Networks()
469+
470+ self.twitter = self.networks.twitter()
471+ self.twitter_auth = self.twitter.Oauth()
472+ self.twitter_api = None
473+
474+ self.identica = self.networks.identica()
475+ self.identica_auth = self.identica.Oauth()
476+ self.identica_api = None
477+ self.adding_identica = False
478
479 (self.responding_screen_name, self.responding_authorization, self.responding_pin,
480 self.responding_success, self.responding_tweet, self.responding_initial_informations,
481@@ -497,6 +431,8 @@
482 self.credentials_menu_id = 2000
483 self.tweets_menu_id = 3000
484 self.user_timeline_menu_id = 4000
485+ self.identica_menu_id = 5000
486+ self.add_identica_menu_id = 5001
487
488 self.tweet_length = 140
489
490@@ -508,14 +444,14 @@
491 # Inherited methods from CDApplet
492 def begin(self):
493 logp("Looking for user ...")
494- if not self.read_user_data(): # first time for the user
495+ if self.twitter.user.read(): # first time for the user
496+ logp("User '%s' found" % self.twitter.user.screen_name)
497+ self.twitter_api = self.twitter.get_api(self.on_receive_new_entry_into_stream_callback) # pass stream api callback, kind of ugly it yet
498+ if self.identica.user.read():
499+ self.identica_api = self.identica.get_api()
500+ else: # user not found
501 logm("User not found")
502 self.show_initial_informations() # start the wizard
503- else: # user not found
504- logp("User '%s' found" % self.user.screen_name)
505- self.api = TwitterAPI(self.user.access_key, self.user.access_secret) # getting control over the api
506- # setting the callback to receive the data of every entry on the stream
507- self.stream_api = TwitterStreamAPI(self.user.access_key, self.user.access_secret, self.on_receive_new_entry_into_stream_callback)
508
509 def get_config(self, keyfile):
510 self.config['emblem_size'] = keyfile.get('Configuration', 'emblem_size')
511@@ -532,36 +468,19 @@
512 def on_answer_dialog(self, key, content):
513 if (key == 0 or key == -1): # ... and pressed Ok or Enter
514 if self.dialog_type == self.responding_initial_informations:
515- self.ask_for_screen_name()
516+ self.responding_to_initial_informations()
517 elif self.dialog_type == self.responding_screen_name: # user typed screen name ...
518- logp("Receiving screen name '%s'" % content)
519- self.user.screen_name = content
520- self.ask_for_authorization()
521+ self.responding_to_screen_name(content)
522 elif self.dialog_type == self.responding_authorization:
523- logp("Asking for PIN")
524- self.ask_for_pin_number() # ask for the PIN number received when acessed the auth URL
525- elif self.dialog_type == self.responding_pin: # user typed the PIN number
526- logp("Receiving PIN: %s" % content)
527- self.user.access_key, self.user.access_secret = self.twitter_auth.get_access_token_and_secret(content)
528- logp("Writing user data")
529- self.write_user_data() # writing the new users data
530- self.api = TwitterAPI(self.user.access_key, self.user.access_secret) # getting control over the api
531- if self.api:
532- self.show_popup_successful_connection()
533- else:
534- logm("A problem has occurred while getting access to the API")
535+ self.responding_to_authorization()
536+ elif self.dialog_type == self.responding_pin: # user typed the PIN number
537+ self.responding_to_pin(content)
538 elif self.dialog_type == self.responding_tweet:
539- if len(content) > self.tweet_length:
540- self.ask_for_tweet(content, True)
541- else:
542- logp("Sending a tweet '%s'" % content)
543- self.api.tweet(content)
544+ self.responding_to_tweet(content)
545 elif self.dialog_type == self.responding_sending_direct_message_reply:
546- logp("Sending a direct message '%s'" % content)
547- self.api.new_direct_message(content, self.replying_direct_message_to)
548+ self.responding_to_sending_direct_message_reply(content)
549 elif self.dialog_type == self.responding_retweet:
550- logp("Retweeting")
551- self.api.retweet(self.retweet_tweet_id)
552+ self.responding_to_retweet()
553
554 # If there is nothing to be seen, the left click opens the dialog to write a tweet
555 # Otherwise we check if there is something new, likely to be what the user wants
556@@ -582,6 +501,8 @@
557 self.build_user_timeline_menu()
558 self.build_direct_messages_menu()
559 self.build_tweets_menu()
560+ if not self.identica_api:
561+ self.build_identica_menu()
562
563 def on_menu_select(self, selected_menu):
564 if selected_menu == self.direct_messages_menu_id:
565@@ -598,6 +519,9 @@
566 self.show_new_tweets() # show new tweets
567 elif selected_menu == self.user_timeline_menu_id:
568 self.show_user_timeline()
569+ elif selected_menu == self.add_identica_menu_id:
570+ self.adding_identica = True
571+ self.show_initial_informations()
572
573 if __name__ == '__main__':
574 Applet().run()
575
576=== modified file 'Twitter/Twitter.conf'
577--- Twitter/Twitter.conf 2012-04-22 15:10:32 +0000
578+++ Twitter/Twitter.conf 2012-05-13 01:03:19 +0000
579@@ -1,4 +1,4 @@
580-#0.2.3
581+#0.3
582
583 #[gtk-about]
584 [Icon]
585
586=== modified file 'Twitter/auto-load.conf'
587--- Twitter/auto-load.conf 2012-04-22 15:10:32 +0000
588+++ Twitter/auto-load.conf 2012-05-13 01:03:19 +0000
589@@ -10,7 +10,7 @@
590 category = 3
591
592 # Version of the applet; change it everytime you change something in the config file. Don't forget to update the version both in this file and in the config file.
593-version = 0.2.3
594+version = 0.3
595
596 # Whether the applet can be instanciated several times or not.
597 multi-instance = true
598
599=== added file 'Twitter/data/identica.png'
600Binary files Twitter/data/identica.png 1970-01-01 00:00:00 +0000 and Twitter/data/identica.png 2012-05-13 01:03:19 +0000 differ
601=== modified file 'Twitter/emblem.py'
602--- Twitter/emblem.py 2012-03-21 00:02:15 +0000
603+++ Twitter/emblem.py 2012-05-13 01:03:19 +0000
604@@ -5,6 +5,16 @@
605 #
606 # Author: Eduardo Mucelli Rezende Oliveira
607 # E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
608+#
609+# This program is free software: you can redistribute it and/or modify
610+# it under the terms of the GNU General Public License as published by
611+# the Free Software Foundation, either version 3 of the License, or
612+# (at your option) any later version.
613+
614+# This program is distributed in the hope that it will be useful,
615+# but WITHOUT ANY WARRANTY; without even the implied warranty of
616+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
617+# GNU General Public License for more details.
618
619 import os
620
621
622=== added file 'Twitter/identica.py'
623--- Twitter/identica.py 1970-01-01 00:00:00 +0000
624+++ Twitter/identica.py 2012-05-13 01:03:19 +0000
625@@ -0,0 +1,113 @@
626+#!/usr/bin/python
627+
628+# This is a part of the external Twitter applet for Cairo-Dock
629+#
630+# Author: Eduardo Mucelli Rezende Oliveira
631+# E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
632+#
633+# This program is free software: you can redistribute it and/or modify
634+# it under the terms of the GNU General Public License as published by
635+# the Free Software Foundation, either version 3 of the License, or
636+# (at your option) any later version.
637+
638+# This program is distributed in the hope that it will be useful,
639+# but WITHOUT ANY WARRANTY; without even the implied warranty of
640+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
641+# GNU General Public License for more details.
642+
643+from oauth import oauth
644+import urllib2, urllib
645+
646+from user import User
647+from http import post, get
648+from util import *
649+
650+class Identica:
651+ def __init__(self):
652+ self.name = "identica"
653+ self.user = User(network=self.name)
654+
655+ def get_api(self):
656+ if self.user_exists():
657+ logp("Getting Identi.ca API")
658+ return self.IdenticaAPI(self.user.access_key, self.user.access_secret)
659+ else:
660+ return False
661+
662+ def user_exists(self):
663+ return self.user.access_secret and self.user.access_secret
664+
665+ class Oauth:
666+ def __init__(self):
667+ self.request_token_url = 'https://identi.ca/api/oauth/request_token'
668+ self.access_token_url = 'https://identi.ca/api/oauth/access_token'
669+ self.authorize_url = 'https://identi.ca/api/oauth/authorize'
670+
671+ consumer_key, consumer_secret = read_consumer_key_and_secret("identica")
672+ self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
673+ self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
674+
675+ def get_authorization_url(self):
676+ self.request_token = self.get_unauthorized_request_token()
677+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
678+ token = self.request_token,
679+ http_url = self.authorize_url)
680+ oauth_request.sign_request(self.signature_method, self.consumer, self.request_token)
681+ return oauth_request.to_url()
682+
683+ def get_unauthorized_request_token(self):
684+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_method="POST", callback="oob", parameters={"source": "Cairo-Dock"}, http_url = self.request_token_url)
685+ oauth_request.sign_request(self.signature_method, self.consumer, None)
686+ url = oauth_request.to_url()
687+ header = oauth_request.to_header()
688+ response = post(url, "", header)
689+ token = oauth.OAuthToken.from_string(response)
690+ return token
691+
692+ # Exchange request token for access token
693+ def get_access_token_and_secret(self, pin):
694+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(oauth_consumer=self.consumer,
695+ token = self.request_token,
696+ http_method="POST",
697+ callback="oob",
698+ verifier = pin,
699+ parameters={"oauth_verifier": str(pin), "source": "Cairo-Dock"},
700+ http_url=self.access_token_url)
701+
702+ oauth_request.sign_request(self.signature_method, self.consumer, self.request_token)
703+ url = oauth_request.to_url()
704+ header = oauth_request.to_header()
705+ response = post(url, "", header)
706+ access_token = oauth.OAuthToken.from_string(response)
707+ return access_token.key, access_token.secret
708+
709+ class IdenticaAPI():
710+ def __init__(self, access_key, access_secret):
711+ self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
712+ consumer_key, consumer_secret = read_consumer_key_and_secret("identica")
713+ self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
714+ self.access_token = oauth.OAuthToken(access_key, access_secret)
715+ print "=================="
716+ print self.access_token
717+ print "=================="
718+
719+ self.update_url = 'http://identi.ca/api/statuses/update.json'
720+
721+ def dispatch(self, url, mode, parameters={}):
722+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
723+ token = self.access_token,
724+ http_url = url,
725+ parameters = parameters,
726+ http_method = mode)
727+ oauth_request.sign_request(self.signature_method, self.consumer, self.access_token)
728+ print oauth_request.to_url()
729+ if mode == "GET":
730+ url = oauth_request.to_url()
731+ response = get(url)
732+ return simplejson.loads(response)
733+ elif mode == "POST":
734+ header = oauth_request.to_header()
735+ post(url, parameters, header)
736+
737+ def tweet(self, message): # popularly "send a tweet"
738+ self.dispatch(self.update_url, "POST", {'status':message})
739
740=== modified file 'Twitter/message.py'
741--- Twitter/message.py 2012-03-19 00:02:27 +0000
742+++ Twitter/message.py 2012-05-13 01:03:19 +0000
743@@ -4,6 +4,16 @@
744 #
745 # Author: Eduardo Mucelli Rezende Oliveira
746 # E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
747+#
748+# This program is free software: you can redistribute it and/or modify
749+# it under the terms of the GNU General Public License as published by
750+# the Free Software Foundation, either version 3 of the License, or
751+# (at your option) any later version.
752+
753+# This program is distributed in the hope that it will be useful,
754+# but WITHOUT ANY WARRANTY; without even the implied warranty of
755+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
756+# GNU General Public License for more details.
757
758 # Can be either a Tweet or a Direct Message
759 class Message:
760
761=== added file 'Twitter/networks.py'
762--- Twitter/networks.py 1970-01-01 00:00:00 +0000
763+++ Twitter/networks.py 2012-05-13 01:03:19 +0000
764@@ -0,0 +1,26 @@
765+#!/usr/bin/python
766+
767+# This is a part of the external Twitter applet for Cairo-Dock
768+#
769+# Author: Eduardo Mucelli Rezende Oliveira
770+# E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
771+#
772+# This program is free software: you can redistribute it and/or modify
773+# it under the terms of the GNU General Public License as published by
774+# the Free Software Foundation, either version 3 of the License, or
775+# (at your option) any later version.
776+
777+# This program is distributed in the hope that it will be useful,
778+# but WITHOUT ANY WARRANTY; without even the implied warranty of
779+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
780+# GNU General Public License for more details.
781+
782+class Networks:
783+
784+ def twitter(self):
785+ from twitter import Twitter
786+ return Twitter()
787+
788+ def identica(self):
789+ from identica import Identica
790+ return Identica()
791
792=== added file 'Twitter/twitter.py'
793--- Twitter/twitter.py 1970-01-01 00:00:00 +0000
794+++ Twitter/twitter.py 2012-05-13 01:03:19 +0000
795@@ -0,0 +1,181 @@
796+#!/usr/bin/python
797+
798+# This is a part of the external Twitter applet for Cairo-Dock
799+#
800+# Author: Eduardo Mucelli Rezende Oliveira
801+# E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
802+#
803+# This program is free software: you can redistribute it and/or modify
804+# it under the terms of the GNU General Public License as published by
805+# the Free Software Foundation, either version 3 of the License, or
806+# (at your option) any later version.
807+
808+# This program is distributed in the hope that it will be useful,
809+# but WITHOUT ANY WARRANTY; without even the implied warranty of
810+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
811+# GNU General Public License for more details.
812+
813+from oauth import oauth
814+import simplejson, threading, urllib2
815+
816+from user import User
817+from http import post, get #, stream
818+from util import *
819+
820+class Twitter:
821+ def __init__(self):
822+ self.name = "twitter"
823+ self.user = User(network=self.name)
824+
825+ def get_api(self, stream_api_callback):
826+ # start StreamAPI and return the instance of TwitterAPI
827+ if self.user_exists():
828+ logp("Getting Twitter API")
829+ self.TwitterStreamAPI(self.user.access_key, self.user.access_secret, stream_api_callback)
830+ return self.TwitterAPI(self.user.access_key, self.user.access_secret)
831+ else:
832+ return False
833+
834+ def user_exists(self):
835+ return self.user.access_secret and self.user.access_secret
836+
837+ class Oauth():
838+ def __init__(self):
839+ self.request_token_url = 'https://twitter.com/oauth/request_token'
840+ self.access_token_url = 'https://twitter.com/oauth/access_token'
841+ self.authorize_url = 'https://twitter.com/oauth/authorize'
842+
843+ consumer_key, consumer_secret = read_consumer_key_and_secret("twitter")
844+ self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
845+ self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
846+
847+ self.request_token = None
848+ self.access_token = None
849+
850+ def get_authorization_url(self):
851+ self.request_token = self.get_unauthorized_request_token()
852+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
853+ token = self.request_token,
854+ http_url = self.authorize_url)
855+ oauth_request.sign_request(self.signature_method, self.consumer, self.request_token)
856+ return oauth_request.to_url()
857+
858+ def get_unauthorized_request_token(self):
859+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url = self.request_token_url)
860+ oauth_request.sign_request(self.signature_method, self.consumer, None)
861+ url = oauth_request.to_url()
862+ response = get(url)
863+ token = oauth.OAuthToken.from_string(response)
864+ return token
865+
866+ # Exchange request token for access token
867+ def get_access_token_and_secret(self, pin):
868+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
869+ http_url = self.access_token_url,
870+ verifier = pin,
871+ token = self.request_token)
872+ oauth_request.sign_request(self.signature_method, self.consumer, self.request_token)
873+ url = oauth_request.to_url()
874+ response = get(url)
875+ self.access_token = oauth.OAuthToken.from_string(response) # create both .key and .secret attributes
876+ return self.access_token.key, self.access_token.secret
877+
878+ class API():
879+ def __init__(self, access_key, access_secret):
880+ self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
881+ consumer_key, consumer_secret = read_consumer_key_and_secret()
882+ self.consumer = oauth.OAuthConsumer(consumer_key, consumer_secret)
883+ self.access_token = oauth.OAuthToken(access_key, access_secret)
884+
885+ class TwitterStreamAPI(API):
886+ def __init__(self, access_key, access_secret, callback):
887+ Twitter.API.__init__(self, access_key, access_secret)
888+
889+ self.user_stream_url = "https://userstream.twitter.com/2/user.json"
890+ self.callback = callback # called as soon as a new entry on the stream appears
891+ thread = threading.Thread(target=self.tweet_streaming) # keep checking for new entries on the stream
892+ thread.start() # run, forrest run
893+
894+ def tweet_streaming(self):
895+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
896+ token = self.access_token,
897+ http_url = self.user_stream_url)
898+ oauth_request.sign_request(self.signature_method, self.consumer, self.access_token)
899+
900+ url = oauth_request.to_url()
901+ req = urllib2.urlopen(url)
902+
903+ buffer = ''
904+ while True:
905+ chunk = req.read(1) # read character per character from the connection ...
906+ if not chunk:
907+ break
908+
909+ buffer += chunk
910+ tweets = buffer.split("\n",1) # ... until find the end of a tweet marked with a '\n'
911+ if len(tweets) > 1:
912+ content = tweets[0]
913+ if "text" in content:
914+ content = simplejson.loads(content)
915+ logp("Received from Twitter Stream: %s" % content)
916+ self.callback(content) # callback == Twitter.on_receive_new_entry_into_stream_callback
917+ buffer = tweets[1]
918+
919+ class TwitterAPI(API):
920+ def __init__(self, access_key, access_secret):
921+ Twitter.API.__init__(self, access_key, access_secret)
922+
923+ self.update_url = 'https://api.twitter.com/1/statuses/update.json'
924+ self.home_timeline_url = 'https://api.twitter.com/1/statuses/home_timeline.json'
925+ self.direct_messages_url = 'https://api.twitter.com/1/direct_messages.json'
926+ self.new_direct_messages_url = 'https://api.twitter.com/1/direct_messages/new.json'
927+ self.verify_credentials_url = 'https://api.twitter.com/1/account/verify_credentials.json'
928+ self.user_timeline_url = 'https://api.twitter.com/1/statuses/user_timeline.json'
929+ self.retweet_url_prefix = 'https://api.twitter.com/1/statuses/retweet/' # lacks the id of the tweet to be retweeted
930+
931+ def dispatch(self, url, mode, parameters={}):
932+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
933+ token = self.access_token,
934+ http_url = url,
935+ parameters = parameters,
936+ http_method = mode)
937+ oauth_request.sign_request(self.signature_method, self.consumer, self.access_token)
938+ if mode == "GET":
939+ url = oauth_request.to_url()
940+ response = get(url)
941+ return simplejson.loads(response)
942+ elif mode == "POST":
943+ header = oauth_request.to_header()
944+ post(url, parameters, header)
945+
946+ # TODO: If an user tries to post the same tweet twice on a short period of time,
947+ # twitter is not going to allow, and a error 403 is thrown.
948+ # This method should return True based on something like:
949+ # try:
950+ # self.dispatch(self.update_url, "POST", {'status':message})
951+ # except urllib2.HTTPError, err:
952+ # if err.code == 403:
953+ # return False
954+ # else:
955+ # return True
956+ def tweet(self, message): # popularly "send a tweet"
957+ self.dispatch(self.update_url, "POST", {'status':message})
958+
959+ def retweet(self, tweet_id):
960+ url = "%s%s.json" % (self.retweet_url_prefix, tweet_id)
961+ self.dispatch(url, "POST")
962+
963+ def new_direct_message(self, message, destinatary):
964+ self.dispatch(self.new_direct_messages_url, "POST", {'text':message, 'screen_name':destinatary})
965+
966+ def home_timeline(self):
967+ return self.dispatch(self.home_timeline_url, "GET")
968+
969+ def user_timeline(self):
970+ return self.dispatch(self.user_timeline_url, "GET")
971+
972+ def direct_messages(self):
973+ return self.dispatch(self.direct_messages_url, "GET")
974+
975+ def verify_credentials(self):
976+ return self.dispatch(self.verify_credentials_url, "GET")
977
978=== added file 'Twitter/user.py'
979--- Twitter/user.py 1970-01-01 00:00:00 +0000
980+++ Twitter/user.py 2012-05-13 01:03:19 +0000
981@@ -0,0 +1,44 @@
982+#!/usr/bin/python
983+
984+# This is a part of the external Twitter applet for Cairo-Dock
985+#
986+# Author: Eduardo Mucelli Rezende Oliveira
987+# E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
988+#
989+# This program is free software: you can redistribute it and/or modify
990+# it under the terms of the GNU General Public License as published by
991+# the Free Software Foundation, either version 3 of the License, or
992+# (at your option) any later version.
993+
994+# This program is distributed in the hope that it will be useful,
995+# but WITHOUT ANY WARRANTY; without even the implied warranty of
996+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
997+# GNU General Public License for more details.
998+
999+import os
1000+
1001+class User:
1002+ def __init__(self, screen_name="", access_key="", access_secret="", network="twitter"):
1003+ self.screen_name = screen_name
1004+ self.access_key = access_key
1005+ self.access_secret = access_secret
1006+ self.user_file = os.path.abspath(os.path.join(os.getcwd(),'..','..','.%s_users' % network)) # ~/.config/cairo-dock/.twitter_users
1007+
1008+ # TODO: Implement it as a config file using screen_name as section index
1009+ def read(self):
1010+ """Read the users file formated as Screen Name<space>Access Key<space>Access Secret"""
1011+ found = False
1012+ if os.path.exists(self.user_file):
1013+ if os.path.getsize(self.user_file) > 0:
1014+ f = open(self.user_file, "rb")
1015+ data = f.read()
1016+ self.screen_name, self.access_key, self.access_secret = data.split() # split the line by space token
1017+ f.close()
1018+ found = True
1019+ return found
1020+
1021+ def write(self):
1022+ f = open(self.user_file, 'w')
1023+ f.write("%s %s %s" % (self.screen_name, self.access_key, self.access_secret))
1024+ f.close()
1025+
1026
1027=== modified file 'Twitter/util.py'
1028--- Twitter/util.py 2011-12-17 03:28:23 +0000
1029+++ Twitter/util.py 2012-05-13 01:03:19 +0000
1030@@ -4,6 +4,18 @@
1031 #
1032 # Author: Eduardo Mucelli Rezende Oliveira
1033 # E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
1034+#
1035+# This program is free software: you can redistribute it and/or modify
1036+# it under the terms of the GNU General Public License as published by
1037+# the Free Software Foundation, either version 3 of the License, or
1038+# (at your option) any later version.
1039+
1040+# This program is distributed in the hope that it will be useful,
1041+# but WITHOUT ANY WARRANTY; without even the implied warranty of
1042+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1043+# GNU General Public License for more details.
1044+
1045+import ConfigParser
1046
1047 def logp (string):
1048 print "[+] Twitter: %s" % string
1049@@ -12,12 +24,19 @@
1050 print "[-] Twitter: %s" % string
1051
1052 # Read the user's consumer key and secret necessary for the requests
1053-def read_consumer_key_and_secret():
1054- try:
1055- f = open('.keys')
1056- data = f.read()
1057- f.close()
1058- except IOError:
1059- logm("It was not possible to read the consumer key and secret, check the .keys file")
1060- else:
1061- return data.split()
1062+def read_consumer_key_and_secret(network="twitter"):
1063+ try:
1064+ config = ConfigParser.ConfigParser()
1065+ config.read('.keys.cfg')
1066+ logp("%s: Consumer key: %s\nConsumer secret: %s" % (network, config.get(network, 'consumer_key'), config.get(network, 'consumer_secret')) )
1067+ return config.get(network, 'consumer_key'), config.get(network, 'consumer_secret')
1068+ except configparser.Error:
1069+ logm("It was not possible to read the consumer key and secret for '%s', check the .keys.cgf file" % network)
1070+# try:
1071+# f = open('.keys')
1072+# data = f.read()
1073+# f.close()
1074+# except IOError:
1075+# logm("It was not possible to read the consumer key and secret, check the .keys file")
1076+# else:
1077+# return data.split()

Subscribers

People subscribed via source and target branches