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: 240
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: 392 lines (+165/-51)
6 files modified
Twitter/ChangeLog (+1/-0)
Twitter/Twitter (+72/-28)
Twitter/Twitter.conf (+1/-1)
Twitter/auto-load.conf (+2/-2)
Twitter/http.py (+24/-20)
Twitter/menu.py (+65/-0)
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+97325@code.launchpad.net

Description of the change

Direct messages are shown in a gtk-based menu, and it is possible to reply them. Fixing the post method, and the stream callback.

To post a comment you must log in.
Revision history for this message
Matthieu Baerts (matttbe) wrote :

Thank you ;)

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Twitter/ChangeLog'
2--- Twitter/ChangeLog 2012-03-10 20:27:57 +0000
3+++ Twitter/ChangeLog 2012-03-14 00:09:17 +0000
4@@ -1,3 +1,4 @@
5+0.1.3: (March/14/2012): Direct messages are shown in a gtk-based menu, and it is possible to reply them. Fixing the post method, and the stream callback.
6 0.1.2: (March/8/2012): Adding the emblem maker to inform the number of new tweets from the stream.
7 0.1.1: (March/6/2012): Using callback instead of a thread to check for the new tweet from the stream, faster, better, and cleaner. Fixed new tweets count that was not being updated. Increased modularization.
8 0.1: (March/5/2012): Finally, after long work, compatible with Twitter Stream, and using it to show the tweets that just arrived. Changed the "received" icon for direct messages, and added the "new" for new tweets, both are from the icon pack Basic made by Pixel Maker, http://pixel-mixer.com
9
10=== modified file 'Twitter/Twitter'
11--- Twitter/Twitter 2012-03-10 20:27:57 +0000
12+++ Twitter/Twitter 2012-03-14 00:09:17 +0000
13@@ -24,14 +24,14 @@
14 # The plugin is going to inform that you are successfully connected.
15
16 # To see the received tweets right-click on the icon -> Twitter -> New tweets.
17-# To see the received direct messages right-click on the icon -> Twitter -> Received direct messages
18+# To see the received direct messages right-click on the icon -> Twitter -> Received direct messages. You can reply one of them just by left-clicking on it.
19 # To see some user's info right-click on the icon -> Twitter -> Info
20
21 import os, webbrowser, simplejson, threading, Queue, urllib2
22 from oauth import oauth
23 from http import post, get #, stream
24 from util import *
25-import emblem
26+import emblem, menu
27 from CDApplet import CDApplet, _
28 # TODO import ConfigParser later conver files to config syntax
29
30@@ -54,7 +54,7 @@
31 http_url = self.authorize_url)
32 oauth_request.sign_request(self.signature_method, self.consumer, self.request_token)
33 return oauth_request.to_url()
34-
35+
36 def get_unauthorized_request_token(self):
37 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer, http_url = self.request_token_url)
38 oauth_request.sign_request(self.signature_method, self.consumer, None)
39@@ -103,39 +103,51 @@
40
41 buffer = ''
42 while True:
43- chunk = req.read(1) # read character per character from the connection ...
44+ chunk = req.read(1) # read character per character from the connection ...
45 if not chunk:
46 break
47
48 buffer += chunk
49- tweets = buffer.split("\n",1) # ... until find the end of a tweet marked with a '\n'
50+ tweets = buffer.split("\n",1) # ... until find the end of a tweet marked with a '\n'
51 if len(tweets) > 1:
52 content = tweets[0]
53 if "text" in content:
54 content = simplejson.loads(content)
55 logp("Received from Twitter Stream: %s" % content)
56- self.callback(content) # at the moment this method is called 'on_receive_new_tweet_callback'
57+ self.callback(content) # at the moment this method is called 'on_receive_new_entry_into_stream_callback'
58 buffer = tweets[1]
59
60 class TwitterAPI(API):
61 def __init__(self, access_key, access_secret):
62 API.__init__(self, access_key, access_secret)
63
64- self.update_url = 'http://twitter.com/statuses/update.json'
65- self.home_timeline_url = 'http://twitter.com/statuses/home_timeline.json'
66- self.direct_messages_url = 'https://api.twitter.com/1/direct_messages.json'
67- self.verify_credentials_url = 'https://api.twitter.com/1/account/verify_credentials.json'
68+ self.update_url = 'http://twitter.com/statuses/update.json'
69+ self.home_timeline_url = 'http://twitter.com/statuses/home_timeline.json'
70+ self.direct_messages_url = 'https://api.twitter.com/1/direct_messages.json'
71+ self.new_direct_messages_url = 'https://api.twitter.com/1/direct_messages/new.json'
72+ self.verify_credentials_url = 'https://api.twitter.com/1/account/verify_credentials.json'
73
74 def tweet(self, message): # popularly "send a tweet"
75+ params = {'status':message}
76 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
77 token = self.access_token,
78 http_url = self.update_url,
79- parameters = {'status':message},
80+ parameters = params,
81 http_method = "POST")
82 oauth_request.sign_request(self.signature_method, self.consumer, self.access_token)
83- post_data = oauth_request.to_postdata()
84- return post(self.update_url, post_data)
85+ header = oauth_request.to_header()
86+ post(self.update_url, params, header)
87
88+ def new_direct_message(self, message, destinatary):
89+ params = {'text':message, 'screen_name':destinatary}
90+ oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
91+ token = self.access_token,
92+ http_url = self.new_direct_messages_url,
93+ parameters = params,
94+ http_method = "POST")
95+ oauth_request.sign_request(self.signature_method, self.consumer, self.access_token)
96+ header = oauth_request.to_header()
97+ post(self.new_direct_messages_url, params, header)
98
99 def home_timeline(self):
100 oauth_request = oauth.OAuthRequest.from_consumer_and_token(self.consumer,
101@@ -167,6 +179,11 @@
102 response = get(url)
103 return simplejson.loads(response)
104
105+class Message:
106+ def __init__(self, text, sender):
107+ self.text = text
108+ self.sender = sender
109+
110 class User:
111 def __init__(self, screen_name="", access_key="", access_secret=""):
112 self.screen_name = screen_name
113@@ -183,18 +200,22 @@
114
115 # Twitter methods
116
117- # This method is a callback that is called as soon as a new tweet that arrives on the stream
118+ # This method is a callback that is called as soon as a new entry arrives on the stream
119 # It is passed as parameter when creating the instance for the TwitterStreamAPI
120- # TwitterStreamAPI(access_key, access_secret, self.on_receive_new_tweet_callback)
121- # TODO: Make available an "Animation" option upon a new tweet arrival
122- def on_receive_new_tweet_callback(self, tweet):
123- if not tweet['user']['screen_name'] == self.user.screen_name:
124- logp("Inserting new tweet on the stream Queue: %s" % tweet) # not sent by the own user
125- self.stream.put(tweet) # put the new tweet on the stream queue
126- self.emblem.update(self.stream.qsize()) # create the emblem with the counter
127- self.icon.SetEmblem(self.emblem.emblem, CDApplet.EMBLEM_TOP_RIGHT + CDApplet.EMBLEM_PERSISTENT) # add emblem
128- #self.icon.SetQuickInfo(str(self.stream.qsize())) # update the new tweets counter on the icon
129+ # TwitterStreamAPI(access_key, access_secret, self.on_receive_new_entry_into_stream_callback)
130+ # TODO: Make available an "Animation" option upon a new entry arrival
131+ # TODO: Consider the direct messages arriving on the stream
132+ # Create two Queues to deal with tweets and messages separately
133+ def on_receive_new_entry_into_stream_callback(self, entry):
134+ #if 'direct_message' in entry: #TODO direct message
135+ if 'user' in entry: # tweet
136+ if not entry['user']['screen_name'] == self.user.screen_name:
137+ logp("Inserting new tweet on the stream Queue: %s" % entry) # not sent by the own user
138+ self.stream.put(entry) # put the new tweet on the stream queue
139+ self.emblem.update(self.stream.qsize()) # create the emblem with the counter
140+ self.icon.SetEmblem(self.emblem.emblem, CDApplet.EMBLEM_TOP_RIGHT + CDApplet.EMBLEM_PERSISTENT) # add emblem
141
142+ # TODO: Use the Menu class
143 def show_new_tweets(self):
144 self.inform_start_of_waiting_process()
145 message = ''
146@@ -206,11 +227,12 @@
147 self.inform_end_of_waiting_process()
148 self.show_popup_message(message, dialog)
149
150+ # TODO: Use the Menu class
151 def show_home_timeline(self):
152 self.inform_start_of_waiting_process()
153 timeline = self.api.home_timeline()
154 if len(timeline) > 0:
155- message = "".join (["[<b>%s</b>] %s\n" % (status['user']['name'], status['text']) for status in timeline])
156+ message = "".join (["[<b>%s</b>] %s\n" % (status['user']['screen_name'], status['text']) for status in timeline])
157 else:
158 message = _("Oh, dear, your timeline is empty :-(")
159 dialog = {'use-markup':True}
160@@ -220,13 +242,30 @@
161 def show_direct_messages(self):
162 self.inform_start_of_waiting_process()
163 messages = self.api.direct_messages()
164+ # message = ""
165+ itens = []
166 if len(messages) > 0:
167- message = "".join (["[<b>%s</b>] %s\n" % (status['sender']['name'], status['text']) for status in messages])
168+ # Message (content of text, nick name of who sent it)
169+ ([itens.append(Message(status['text'], status['sender']['screen_name'])) for status in messages])
170+ #message += "[<b>%s</b>] %s\n" % (sender, text)
171+ direct_messages_list_menu = menu.Menu(itens, self.on_direct_messages_list_menu_clicked)
172+ direct_messages_list_menu.pop_up(self.icon)
173+ #message = "".join (["[<b>%s</b>] %s\n" % (status['sender']['screen_name'], status['text']) for status in messages])
174 else:
175 message = _("Oh, dear, you do not have direct messages :-(")
176 dialog = {'use-markup':True}
177 self.inform_end_of_waiting_process()
178- self.show_popup_message(message, dialog)
179+ #self.show_popup_message(message, dialog)
180+
181+ def on_direct_messages_list_menu_clicked(self, widget):
182+ self.ask_for_direct_message_reply(widget.get_label()) # label holds the sender of the message, reply to him/her now
183+
184+ def ask_for_direct_message_reply(self, destinatary):
185+ dialog = {'buttons':'ok;cancel'}
186+ widget = {'widget-type':'text-entry', 'nb-chars':140} # 140 characters max, a direct message
187+ self.show_popup_message((_("%s, write a reply to %s")) % (self.user.screen_name, destinatary), dialog, widget)
188+ self.dialog_type = self.responding_sending_direct_message_reply
189+ self.replying_direct_message_to = destinatary
190
191 def tweet(self, message): # popularly "send a tweet"
192 self.inform_start_of_waiting_process()
193@@ -349,7 +388,8 @@
194 self.api = None
195 self.stream_api = None
196 (self.responding_screen_name, self.responding_authorization, self.responding_pin,
197- self.responding_success, self.responding_tweet, self.responding_initial_informations) = range(6)
198+ self.responding_success, self.responding_tweet, self.responding_initial_informations,
199+ self.responding_sending_direct_message_reply) = range(7)
200 self.dialog_type = None
201 self.emblem = emblem.Emblem() # emblem maker, see emblem.py
202
203@@ -358,6 +398,7 @@
204 self.user_stream_menu_id = 3000
205
206 self.stream = Queue.Queue()
207+ #self.direct_messages = {}
208
209 CDApplet.__init__(self) # call CDApplet interface init
210
211@@ -371,7 +412,7 @@
212 logp("User '%s' found" % self.user.screen_name)
213 self.api = TwitterAPI(self.user.access_key, self.user.access_secret) # getting control over the api
214 # setting the callback to receive the data of every entry on the stream
215- self.stream_api = TwitterStreamAPI(self.user.access_key, self.user.access_secret, self.on_receive_new_tweet_callback)
216+ self.stream_api = TwitterStreamAPI(self.user.access_key, self.user.access_secret, self.on_receive_new_entry_into_stream_callback)
217
218 #def reload(self):
219 #self.read_user_data()
220@@ -401,6 +442,9 @@
221 elif self.dialog_type == self.responding_tweet:
222 logp("Sending a tweet '%s'" % content)
223 self.api.tweet(content)
224+ elif self.dialog_type == self.responding_sending_direct_message_reply:
225+ logp("Sending a direct message '%s'" % content)
226+ self.api.new_direct_message(content, self.replying_direct_message_to)
227
228 def on_click(self, key):
229 self.ask_for_tweet()
230
231=== modified file 'Twitter/Twitter.conf'
232--- Twitter/Twitter.conf 2012-03-10 17:13:47 +0000
233+++ Twitter/Twitter.conf 2012-03-14 00:09:17 +0000
234@@ -1,4 +1,4 @@
235-#!en;0.1.2
236+#!en;0.1.3
237
238 #[gtk-about]
239 [Icon]
240
241=== modified file 'Twitter/auto-load.conf'
242--- Twitter/auto-load.conf 2012-03-10 20:27:57 +0000
243+++ Twitter/auto-load.conf 2012-03-14 00:09:17 +0000
244@@ -4,13 +4,13 @@
245 author = Eduardo Mucelli Rezende Oliveira
246
247 # A short description of the applet and how to use it.
248-description = You can send tweets, see your timeline, the received directed messages, and new tweets.\nOn the first time, the applet is going to ask your nickname and authorization to connect with Twitter.\nThe applet is going to open your browser with the authorization page\nAs soon as you authorize it, a PIN number will be shown on the page, copy this number\nPaste this number on the next dialog box will be shown.\nThe plugin is going to inform that you are successfully connected.\nTo see the received direct messages right-click on the icon -> Twitter -> Received direct messages.\nTo see some user's info right-click on the icon -> Twitter -> Info.\nTo see the received tweets right-click on the icon -> Twitter -> New tweets.
249+description = You can send tweets, see your timeline, the received directed messages, and new tweets.\nOn the first time, the applet is going to ask your nickname and authorization to connect with Twitter.\nThe applet is going to open your browser with the authorization page\nAs soon as you authorize it, a PIN number will be shown on the page, copy this number\nPaste this number on the next dialog box will be shown.\nThe plugin is going to inform that you are successfully connected.\nTo see the received direct messages right-click on the icon -> Twitter -> Received direct messages. You can reply one of them just by left-clicking on it.\nTo see some user's info right-click on the icon -> Twitter -> Info.\nTo see the received tweets right-click on the icon -> Twitter -> New tweets.
250
251 # Category of the applet : 2 = files, 3 = internet, 4 = Desktop, 5 = accessory, 6 = system, 7 = fun
252 category = 3
253
254 # 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.
255-version = 0.1.2
256+version = 0.1.3
257
258 # Whether the applet can be instanciated several times or not.
259 multi-instance = true
260
261=== added file 'Twitter/data/received_menu.png'
262Binary files Twitter/data/received_menu.png 1970-01-01 00:00:00 +0000 and Twitter/data/received_menu.png 2012-03-14 00:09:17 +0000 differ
263=== modified file 'Twitter/http.py'
264--- Twitter/http.py 2012-03-05 14:44:13 +0000
265+++ Twitter/http.py 2012-03-14 00:09:17 +0000
266@@ -5,32 +5,36 @@
267 # Author: Eduardo Mucelli Rezende Oliveira
268 # E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
269
270-import urllib2, json
271+import urllib2, urllib
272 from util import logp, logm
273
274 # HTTP GET
275 def get(url, tries = 0):
276- while True:
277- try:
278- logp("Trying to connect to %s" % url)
279- request = urllib2.Request(url)
280- response = urllib2.urlopen(request)
281- return response.read()
282- except urllib2.HTTPError:
283- tries += 1
284- if tries > 3:
285- raise
286+ while True:
287+ try:
288+ logp("GET: Trying to connect to %s" % url)
289+ request = urllib2.Request(url)
290+ response = urllib2.urlopen(request)
291+ return response.read()
292+ except urllib2.HTTPError:
293+ tries += 1
294+ if tries > 3:
295+ raise
296
297 # HTTP POST
298-def post(url, post_data, tries = 0):
299- while True:
300- try:
301- return urllib2.urlopen(url, post_data)
302- except urllib2.HTTPError:
303- tries += 1
304- if tries > 3:
305- raise
306-
307+def post(url, params, header, tries = 0):
308+ while True:
309+ try:
310+ logp("POST: Trying to connect to %s" % url)
311+ data = urllib.urlencode(params)
312+ request = urllib2.Request(url, data, headers=header)
313+ response = urllib2.urlopen(request)
314+ return response.read()
315+ except urllib2.HTTPError:
316+ tries += 1
317+ if tries > 3:
318+ raise
319+
320 #def stream(url):
321 # req = urllib2.urlopen(url)
322 # buffer = ''
323
324=== added file 'Twitter/menu.py'
325--- Twitter/menu.py 1970-01-01 00:00:00 +0000
326+++ Twitter/menu.py 2012-03-14 00:09:17 +0000
327@@ -0,0 +1,65 @@
328+#!/usr/bin/python
329+
330+# This is a part of the external Twitter applet for Cairo-Dock
331+#
332+# Author: Eduardo Mucelli Rezende Oliveira
333+# E-mail: edumucelli@gmail.com or eduardom@dcc.ufmg.br
334+
335+import gtk, os
336+
337+class Menu(gtk.Menu):
338+
339+ def __init__(self, messages, callback):
340+ gtk.Menu.__init__(self)
341+
342+ for message in messages:
343+ text = "<b>%s</b>\n%s" % (message.sender, message.text)
344+ item = gtk.ImageMenuItem()
345+ # the true label is set after with set_markup()
346+ item.set_label(message.sender)
347+ item.set_image(gtk.image_new_from_file(os.path.abspath("./data/received_menu.png")))
348+ item.get_children()[0].set_markup(text)
349+ item.connect('activate', callback)
350+ self.append(item)
351+ item.show()
352+ # add a separator if mail is not last in list
353+ if messages.index(message) != len(messages) - 1:
354+ separator = gtk.SeparatorMenuItem()
355+ self.append(separator)
356+
357+ self.show_all()
358+
359+ def pop_up(self, icon):
360+ self.icon = icon
361+ self.popup(parent_menu_shell=None, parent_menu_item=None, func=self.get_xy, data=(400, 400), button=1, activate_time=0)
362+
363+ def get_xy(self, m, data):
364+ # fetch icon geometry
365+ icondata = self.icon.GetAll()
366+ iconContainer = icondata['container']
367+ iconOrientation = icondata['orientation']
368+ iconWidth = icondata['width']
369+ iconHeight = icondata['height']
370+ iconPosX = icondata['x']
371+ iconPosY = icondata['y']
372+
373+ # get menu geometry
374+ menuWidth, menuHeight = m.size_request()
375+
376+ # adapt to container and orientation
377+ if iconContainer == 1: # Then it's a desklet, always oriented in a bottom-like way.
378+ if iconPosY['y'] < (gtk.gdk.screen_height() / 2):
379+ iconOrientation = 1
380+ else:
381+ iconOrientation = 0
382+
383+ if iconOrientation == 0:
384+ # compute position of menu
385+ x = iconPosX - (menuWidth / 2)
386+ y = iconPosY - (iconHeight / 2) - menuHeight
387+
388+ else:
389+ x = iconPosX - (menuWidth / 2)
390+ y = iconPosY + (iconHeight / 2)
391+
392+ return (x, y, True)

Subscribers

People subscribed via source and target branches