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: 215
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: 444 lines (+399/-0)
8 files modified
Twitter/.keys (+1/-0)
Twitter/ChangeLog (+1/-0)
Twitter/README (+6/-0)
Twitter/Twitter (+246/-0)
Twitter/Twitter.conf (+99/-0)
Twitter/auto-load.conf (+16/-0)
Twitter/http.py (+18/-0)
Twitter/util.py (+12/-0)
To merge this branch: bzr merge lp:~eduardo-mucelli/cairo-dock-plug-ins-extras/Twitter
Reviewer Review Type Date Requested Status
Cairo-Dock Devs Pending
Review via email: mp+86120@code.launchpad.net

Description of the change

First version of Twitter applet. It is usable, but I really need to polish the code yet.

To post a comment you must log in.

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== added directory 'Twitter'
2=== added file 'Twitter/.keys'
3--- Twitter/.keys 1970-01-01 00:00:00 +0000
4+++ Twitter/.keys 2011-12-16 21:19:23 +0000
5@@ -0,0 +1,1 @@
6+OzZoSVpO6PZqByM15MsLlg mKsbuXgpHEO6C2axmUI8cPUt0ZPCbDb67uvT5wOIW1s
7
8=== added file 'Twitter/.users'
9=== added file 'Twitter/ChangeLog'
10--- Twitter/ChangeLog 1970-01-01 00:00:00 +0000
11+++ Twitter/ChangeLog 2011-12-16 21:19:23 +0000
12@@ -0,0 +1,1 @@
13+0.0.1: (December/16/2011): Possible to send a tweety, and see the home timeline.
14
15=== added file 'Twitter/README'
16--- Twitter/README 1970-01-01 00:00:00 +0000
17+++ Twitter/README 2011-12-16 21:19:23 +0000
18@@ -0,0 +1,6 @@
19+# Contact me
20+
21+Any doubt, suggestion or anything else, except asking for some money, I would be pleased to received a message from you. :¬)
22+
23+Author: Eduardo Mucelli Rezende Oliveira
24+E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
25
26=== added file 'Twitter/Twitter'
27--- Twitter/Twitter 1970-01-01 00:00:00 +0000
28+++ Twitter/Twitter 2011-12-16 21:19:23 +0000
29@@ -0,0 +1,246 @@
30+#!/usr/bin/python
31+
32+# This is a part of the external Twitter applet for Cairo-Dock
33+#
34+# Author: Eduardo Mucelli Rezende Oliveira
35+# E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
36+#
37+# This program is free software: you can redistribute it and/or modify
38+# it under the terms of the GNU General Public License as published by
39+# the Free Software Foundation, either version 3 of the License, or
40+# (at your option) any later version.
41+
42+# This program is distributed in the hope that it will be useful,
43+# but WITHOUT ANY WARRANTY; without even the implied warranty of
44+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
45+# GNU General Public License for more details.
46+
47+# This applet provides for Cairo-Dock an interface with Twitter
48+
49+import urlparse, os, webbrowser, simplejson
50+from oauth import oauth
51+from http import post, get
52+from util import logp, logm
53+from CDApplet import CDApplet
54+#TODO import ConfigParser later conver files to config syntax
55+
56+CONSUMER_KEY = 'OzZoSVpO6PZqByM15MsLlg'
57+CONSUMER_SECRET = 'mKsbuXgpHEO6C2axmUI8cPUt0ZPCbDb67uvT5wOIW1s'
58+
59+class TwitterOauth:
60+ def __init__(self):
61+ self.request_token_url = 'https://twitter.com/oauth/request_token'
62+ self.access_token_url = 'https://twitter.com/oauth/access_token'
63+ self.authorize_url = 'http://twitter.com/oauth/authorize'
64+
65+ self.consumer_key, self.consumer_secret = self.read_consumer_key_and_secret()
66+ print "=========================="
67+ print self.consumer_key
68+ print self.consumer_secret
69+ print "=========================="
70+ self.consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
71+ self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
72+ self.request_token = None
73+ self.access_token = None
74+
75+ def read_consumer_key_and_secret(self):
76+ try:
77+ f = open('.keys')
78+ except IOError:
79+ logm("It was not possible to read the consumer key and secret, check the .keys file")
80+ else:
81+ return f.read().split()
82+
83+ def get_authorization_url(self):
84+ self.request_token = self.get_unauthorized_request_token()
85+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.request_token, http_url=self.authorize_url)
86+ oauth_request.sign_request(self.signature_method, self.consumer, self.request_token)
87+ return oauth_request.to_url()
88+
89+ def get_unauthorized_request_token(self):
90+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.request_token_url)
91+ oauth_request.sign_request(self.signature_method, self.consumer, None)
92+ url = oauth_request.to_url()
93+ response = get(url)
94+ token = oauth.OAuthToken.from_string(response)
95+ return token
96+
97+ # Exchange request token for access token
98+ def get_access_token_and_secret(self, pin):
99+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url=self.access_token_url, verifier=pin, token=self.request_token )
100+ oauth_request.sign_request(self.signature_method, self.consumer, self.request_token)
101+ url = oauth_request.to_url()
102+ response = get(url)
103+ self.access_token = oauth.OAuthToken.from_string(response)
104+ access_token_data = dict((x, y) for x, y in urlparse.parse_qsl(response)) # tuple to dict
105+ return access_token_data['oauth_token'], access_token_data['oauth_token_secret']
106+
107+class TwitterAPI:
108+ def __init__(self, access_key, access_secret):
109+ self.update_url = 'http://twitter.com/statuses/update.json'
110+ self.home_timeline_url = 'http://twitter.com/statuses/home_timeline.json'
111+
112+ self.signature_method = oauth.OAuthSignatureMethod_HMAC_SHA1()
113+ self.consumer = oauth.OAuthConsumer(CONSUMER_KEY, CONSUMER_SECRET)
114+ self.access_token = oauth.OAuthToken(access_key, access_secret)
115+
116+ def tweety(self, message): # popularly "send a tweety"
117+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_url=self.update_url, parameters = {'status':message}, http_method="POST")
118+ oauth_request.sign_request(self.signature_method, self.consumer, self.access_token)
119+ post_data = oauth_request.to_postdata()
120+ return post(self.update_url, post_data)
121+
122+ def home_timeline(self):
123+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, token=self.access_token, http_url=self.home_timeline_url, http_method="GET")
124+ oauth_request.sign_request(self.signature_method, self.consumer, self.access_token)
125+ url = oauth_request.to_url()
126+ response = get(url)
127+ return simplejson.loads(response)
128+
129+class User:
130+ def __init__(self, screen_name="", access_key="", access_secret=""):
131+ self.screen_name = screen_name
132+ self.access_key = access_key
133+ self.access_secret = access_secret
134+
135+class Applet(CDApplet):
136+
137+ def inform_start_of_waiting_process(self):
138+ self.icon.SetQuickInfo("...")
139+
140+ def inform_end_of_waiting_process(self):
141+ self.icon.SetQuickInfo("")
142+
143+ # Twitter methods
144+
145+ def show_home_timeline(self):
146+ self.inform_start_of_waiting_process()
147+
148+ timeline = self.api.home_timeline()
149+ if len(timeline) > 0:
150+ message = "".join (["[%s] %s\n" % (status['user']['name'], status['text']) for status in timeline])
151+ else:
152+ message = "Oh, dear, your timeline is empty :-("
153+
154+ self.inform_end_of_waiting_process()
155+ self.show_popup_message(message)
156+
157+ def tweety(self, message): # popularly "send a tweety"
158+ self.api.update_status(message)
159+
160+ # Applet methods
161+
162+ def ask_for_tweety(self):
163+ dialog = {'buttons':'ok;cancel'}
164+ widget = {'widget-type':'text-entry', 'nb-chars':140}
165+ self.show_popup_message(("%s, send a tweety") % self.user.screen_name, dialog, widget)
166+ self.dialog_type = self.responding_tweety
167+
168+ # TODO: Handle multiple users inside the .users files
169+ # TODO: Implement it as a config file using screen_name as section index
170+ def read_user_data(self):
171+ """Read the users file formated as Screen Name<space>Access Key<space>Access Secret"""
172+ found = False
173+ if os.path.exists(self.user_file):
174+ if os.path.getsize(self.user_file) > 0:
175+ f = open(self.user_file, "rb")
176+ # for line in f:
177+ data = f.read()
178+ self.user.screen_name, self.user.access_key, self.user.access_secret = data.split() # split the line by space token
179+ f.close()
180+ found = True
181+ return found
182+
183+ def write_user_data(self):
184+ f = open(self.user_file, 'w')
185+ f.write("%s %s %s" % (self.user.screen_name, self.user.access_key, self.user.access_secret))
186+ f.close()
187+
188+ def ask_for_screen_name(self):
189+ message = "What is your Twitter screen name?"
190+ dialog = {'buttons':'ok'}
191+ widget = {'widget-type':'text-entry'}
192+ self.show_popup_message(message, dialog, widget)
193+ # self.dialog_type = self.responding_screen_name
194+
195+ def ask_for_authorization(self):
196+ message = "Twitter applet needs you to give the authorization. Press Enter and your browser will be open with the URL shown bellow. Copy the PIN number that will be shown in the browser"
197+ dialog = {'buttons':'ok'}
198+ widget = {'widget-type':'text-entry', 'initial-value':self.twitter_auth.get_authorization_url()}
199+ self.show_popup_message(message, dialog, widget)
200+ self.dialog_type = self.responding_authorization
201+
202+ def ask_for_pin_number(self):
203+ message = "Enter the PIN number that appeared when you accessed the URL shown before"
204+ dialog = {'buttons':'ok'}
205+ widget = {'widget-type':'text-entry'}
206+ self.show_popup_message(message, dialog, widget)
207+ self.dialog_type = self.responding_pin
208+
209+ def show_popup_successful_connection(self):
210+ self.show_popup_message("Successfully connected with Twitter")
211+
212+ def show_popup_message(self, message, dialog={}, widget={}):
213+ dialog_attributes = {'message':message}
214+ widget_attributes = {}
215+ dialog_attributes.update(dialog)
216+ widget_attributes.update(widget)
217+ self.icon.PopupDialog (dialog_attributes, widget_attributes)
218+
219+ def __init__(self):
220+ self.user = User()
221+ self.user_file = '.users'
222+ self.twitter_auth = TwitterOauth()
223+ self.api = None
224+ self.responding_screen_name, self.responding_authorization, self.responding_pin, self.responding_success, self.responding_tweety = range(5)
225+ self.dialog_type = self.responding_screen_name
226+
227+ CDApplet.__init__(self) # call CDApplet interface init
228+
229+ # Inherited methods from CDApplet
230+ def begin(self):
231+ logp("Looking for user ...")
232+ if not self.read_user_data():
233+ logm("User not found")
234+ self.ask_for_screen_name()
235+ else:
236+ logp("User '%s' found" % self.user.screen_name)
237+ self.api = TwitterAPI(self.user.access_key, self.user.access_secret)
238+
239+ def reload(self):
240+ self.read_user_data()
241+
242+ # Callbacks
243+ def on_answer_dialog(self, key, content):
244+ if (key == 0 or key == -1) and content: # ... and pressed Ok or Enter
245+ if self.dialog_type == self.responding_screen_name: # user typed screen name ...
246+ logp("Receiving screen name '%s'" % content)
247+ self.user.screen_name = content
248+ self.ask_for_authorization()
249+ elif self.dialog_type == self.responding_authorization:
250+ logp("Authorizing ...")
251+ webbrowser.open(content)
252+ logp("Opening the auth URL '%s'" % content)
253+ self.ask_for_pin_number() # ask for the PIN number received when acessed the auth URL
254+ elif self.dialog_type == self.responding_pin: # user typed the PIN number
255+ logp("Receiving PIN: %s" % content)
256+ self.user.access_key, self.user.access_secret = self.twitter_auth.get_access_token_and_secret(content)
257+ logp("Writing user data")
258+ self.write_user_data() # writing the new users data
259+ self.api = TwitterAPI(self.user.access_key, self.user.access_secret) # getting control over the api
260+ if self.api:
261+ self.show_popup_successful_connection()
262+ else:
263+ logm("A problem has occurred while getting access to the API")
264+ elif self.dialog_type == self.responding_tweety:
265+ logp("Sending a tweety '%s'" % content)
266+ self.api.tweety(content)
267+
268+ def on_click(self, key):
269+ self.ask_for_tweety()
270+
271+ def on_middle_click(self):
272+ self.show_home_timeline()
273+
274+if __name__ == '__main__':
275+ Applet().run()
276
277=== added file 'Twitter/Twitter.conf'
278--- Twitter/Twitter.conf 1970-01-01 00:00:00 +0000
279+++ Twitter/Twitter.conf 2011-12-16 21:19:23 +0000
280@@ -0,0 +1,99 @@
281+#!en;0.0.1
282+
283+#[gtk-about]
284+[Icon]
285+#F[Applet]
286+frame_maininfo=
287+
288+#d Name of the dock it belongs to:
289+dock name =
290+
291+#s Name of the icon as it will appear in its caption in the dock:
292+name = Twitter
293+
294+#F[Display]
295+frame_display=
296+
297+#S+ Image's filename :
298+#{Let empty to use the default one.}
299+icon =
300+
301+#j+[0;128] Desired icon size for this applet
302+#{Set to 0 to use the default applet size}
303+icon size = 0;0
304+
305+order=
306+
307+#F[Applet's Handbook]
308+frame_hand=
309+#A
310+handbook=Twitter
311+
312+
313+#[gtk-convert]
314+[Desklet]
315+
316+#j+[48;512] Desklet's dimension (width x height) :
317+#{Depending on your WindowManager, you can resize it with ALT + middle_click or ALT + left_click for exemple.}
318+size = 164;96
319+
320+#i[-2048;2048] Desklet's position (x ; y) :
321+#{Depending on your WindowManager, you can move it with ALT + left_click}
322+x position=0
323+#i[-2048;2048] ...
324+y position=0
325+
326+#b Is detached from the dock ?
327+initially detached=false
328+#l[Normal;Keep above;Keep below;On Widget Layer;Reserve space] Accessibility :
329+#{for CompizFusion's "widget layer", set behaviour in Compiz to: (class=Cairo-dock & type=utility)}
330+accessibility=0
331+#b Should be visible on all desktops ?
332+sticky=true
333+
334+#b Lock position ?
335+#{If locked, the desklet can't be moved by simply dragging it with the left mouse button. Of course you can still move it with ALT + left_click.}
336+locked = false
337+
338+#I[-180;180] Rotation :
339+#{in degrees.}
340+rotation = 0
341+
342+use size=
343+
344+#F[Decorations;gtk-orientation-portrait]
345+frame_deco=
346+
347+#o+ Choose a decoration theme for this desklet :
348+#{Choose the 'personnal' one to define your own decorations below.}
349+decorations = default
350+
351+#v
352+sep_deco =
353+
354+#S+ Background image :
355+#{It's an image that will be displayed below the drawings, like a frame for exemple. Let empty to not use any.}
356+bg desklet =
357+#e+[0;1] Background tansparency :
358+bg alpha = 1
359+#i+[0;256] Left offset :
360+#{in pixels. Use this to adjust the left position of the drawings.}
361+left offset = 0
362+#i+[0;256] Top offset :
363+#{in pixels. Use this to adjust the top position of the drawings.}
364+top offset = 0
365+#i+[0;256] Right offset :
366+#{in pixels. Use this to adjust the right position of the drawings.}
367+right offset = 0
368+#i+[0;256] Bottom offset :
369+#{in pixels. Use this to adjust the bottom position of the drawings.}
370+bottom offset = 0
371+#S+ Foreground image :
372+#{It's an image that will be displayed above the drawings, like a reflect for exemple. Let empty to not use any.}
373+fg desklet =
374+#e+[0;1] Foreground tansparency :
375+fg alpha = 1
376+
377+
378+#[gtk-preferences]
379+[Configuration]
380
381=== added file 'Twitter/auto-load.conf'
382--- Twitter/auto-load.conf 1970-01-01 00:00:00 +0000
383+++ Twitter/auto-load.conf 2011-12-16 21:19:23 +0000
384@@ -0,0 +1,16 @@
385+[Register]
386+
387+# Author of the applet
388+author = Eduardo Mucelli Rezende Oliveira
389+
390+# A short description of the applet and how to use it.
391+description = This applet provides an interface with Twitter. For the first time, the applet is going to ask your screen name and later the authorization to connect with Twitter.\nAuthorize opening the URL shown in the dialog box pressing Enter.\nLog in on the browser and copy the number that you will be presented. On the dock, paste this number on the next dialog box will be shown.\nThe plugin is going to inform that you are successfully connected.
392+
393+# Category of the applet : 2 = files, 3 = internet, 4 = Desktop, 5 = accessory, 6 = system, 7 = fun
394+category = 5
395+
396+# 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.
397+version = 0.0.1
398+
399+# Whether the applet can be instanciated several times or not.
400+multi-instance = true
401
402=== added file 'Twitter/http.py'
403--- Twitter/http.py 1970-01-01 00:00:00 +0000
404+++ Twitter/http.py 2011-12-16 21:19:23 +0000
405@@ -0,0 +1,18 @@
406+#!/usr/bin/python
407+
408+# This is a part of the external Twitter applet for Cairo-Dock
409+#
410+# Author: Eduardo Mucelli Rezende Oliveira
411+# E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
412+
413+import urllib2
414+
415+# HTTP GET
416+def get(url):
417+ request = urllib2.Request(url)
418+ response = urllib2.urlopen(request)
419+ return response.read()
420+
421+# HTTP POST
422+def post(url, post_data):
423+ return urllib2.urlopen(url, post_data)
424
425=== added file 'Twitter/icon'
426Binary files Twitter/icon 1970-01-01 00:00:00 +0000 and Twitter/icon 2011-12-16 21:19:23 +0000 differ
427=== added file 'Twitter/preview'
428Binary files Twitter/preview 1970-01-01 00:00:00 +0000 and Twitter/preview 2011-12-16 21:19:23 +0000 differ
429=== added file 'Twitter/util.py'
430--- Twitter/util.py 1970-01-01 00:00:00 +0000
431+++ Twitter/util.py 2011-12-16 21:19:23 +0000
432@@ -0,0 +1,12 @@
433+#!/usr/bin/python
434+
435+# This is a part of the external Twitter applet for Cairo-Dock
436+#
437+# Author: Eduardo Mucelli Rezende Oliveira
438+# E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
439+
440+def logp (string):
441+ print "[+] Twitter: %s" % string
442+
443+def logm (string):
444+ print "[-] Twitter: %s" % string

Subscribers

People subscribed via source and target branches