20
20
# License along with this program; if not, write to the
21
21
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22
22
# Boston, MA 02111-1307, USA.
24
23
from spectlib.watch import Watch
24
import spectlib.config
25
25
from spectlib.i18n import _
31
from xml.dom import minidom, Node
37
class Google_reader(Watch):
39
this watch will check if you have a new mail on your gmail account
42
actually_updated = False
46
## subscription_list_url = reader_url + '/api/0/subscription/list'
47
## reading_url = reader_url + '/atom/user/-/state/com.google/reading-list'
48
## read_items_url = reader_url + '/atom/user/-/state/com.google/read'
49
## reading_tag_url = reader_url + '/atom/user/-/label/%s'
50
## starred_url = reader_url + '/atom/user/-/state/com.google/starred'
51
## subscription_url = reader_url + '/api/0/subscription/edit'
52
## get_feed_url = reader_url + '/atom/feed/'
55
def __init__(self, refresh, username, password, specto, id, name = _("Unknown Google Reader Watch")):
56
Watch.__init__(self, specto)
58
self.refresh = refresh
60
self.password = password
28
type = "Watch_web_greader"
29
type_desc = "Google Reader"
30
url = "http://www.google.com/reader/"
31
icon = 'internet-news-reader'
33
def get_add_gui_info():
35
("username", spectlib.gtkconfig.Entry("Username")),
36
("password", spectlib.gtkconfig.PasswordEntry("Password"))
40
class Watch_web_greader(Watch):
42
this watch will check if you have new news on your google reader account
45
def __init__(self, specto, id, values):
47
( "username", spectlib.config.String(True) ),
48
( "password", spectlib.config.String(True) )
51
self.standard_open_command = spectlib.util.return_webpage(url)
53
#Init the superclass and set some specto values
54
Watch.__init__(self, specto, id, values, watch_values)
56
self.use_network = True
58
self.type_desc = type_desc
59
self.cache_file = os.path.join(self.specto.CACHE_DIR, "greader" + self.username + ".cache")
61
#watch specific values
66
self.source = "Specto"
67
self.google_url = 'http://www.google.com'
68
self.reader_url = self.google_url + '/reader'
69
self.reading_url = self.reader_url + '/atom/user/-/state/com.google/reading-list'
70
self.login_url = 'https://www.google.com/accounts/ClientLogin'
71
self.token_url = self.reader_url + '/api/0/token'
73
cacheSubDir__ = os.environ['HOME'] + "/.specto/cache/"
74
if not os.path.exists(cacheSubDir__):
75
os.mkdir(cacheSubDir__)
76
self.cache_file = cacheSubDir__ + "googleReader.xml.cache"
78
def dict_values(self):
79
return { 'name': self.name, 'refresh': self.refresh, 'username': self.user, 'password':self.password, 'type':5 }
81
def start_watch(self):
82
""" Start the watch. """
85
def _real_update(self):
86
lock = thread.allocate_lock()
88
t=thread.start_new_thread(self.update,(lock,))
90
while gtk.events_pending():
93
while gtk.events_pending():
96
def thread_update(self):
97
if not self.specto.connection_manager.connected():
98
self.specto.logger.log(_("No network connection detected"),
99
"info", self.__class__)
100
self.specto.connection_manager.add_callback(self._real_update)
101
self.specto.mark_watch_busy(False, self.id)
105
def update(self, lock):
106
""" Check for new mails on your gmail account. """
108
self.specto.mark_watch_busy(True, self.id)
109
self.specto.logger.log(_("Updating watch: \"%s\"") % self.name, "info", self.__class__)
64
self.news_info = Feed_collection()
67
self.read_cache_file()
70
""" Check for new news on your greader account. """
112
self.oldMsg = self.newMsg
115
f = file(self.cache_file , "w")
116
f.write(str(self.get_reading_list(SID)))
73
greader = Greader(self.username, self.password)
74
unread, info_friends, info = greader.refresh()
77
self.specto.logger.log(_("Watch: \"%s\" has error: %s") % (self.name, unread[1]), "error", self.__class__)
78
elif unread[0] == 1:#no unread items, we need to clear the watch
79
self.unreadMsg = unread[1]
80
self.actually_updated = False
81
self.specto.mark_watch_status("clear", self.id)
82
self.news_info = Feed_collection()
84
self.unreadMsg = int(unread[1])
86
if self.unreadMsg == 1000:
87
self.or_more = " or more"
89
self.news_info.clear_old()
92
_feed = Feed(feed, info[feed])
93
if self.news_info.add(_feed):
94
self.actually_updated = True
97
self.news_info.remove_old()
98
self.write_cache_file()
102
self.specto.logger.log(_("Watch: \"%s\" has an unknown error") % self.name, "error", self.__class__)
103
Watch.timer_update(self)
105
def get_gui_info(self):
108
('Last updated', self.last_updated),
109
('Username', self.username),
110
("Unread messages", str(self.unreadMsg) + self.or_more)
113
def get_balloon_text(self):
114
""" create the text for the balloon """
115
unread_messages = self.news_info.get_unread_messages()
116
if len(unread_messages) == 1:
117
text = ("<b>%s</b> has a new newsitems in <b>%s</b>\n\n <b>totalling %s</b> unread items.") % (self.name, unread_messages[0].name, str(self.unreadMsg) + self.or_more)
119
i = 0 #show max 4 feeds
121
while i < len(unread_messages) and i < 4:
122
feed_info += unread_messages[i].name + ", "
123
if i == 3 and i < len(unread_messages) - 1:
124
feed_info += "and others..."
126
feed_info = feed_info.rstrip(", ")
127
text = ("<b>%s</b> has received %d new newsitems in <b>%s</b>\n\n <b>totalling %s</b> unread items.") % (self.name, self.newMsg, feed_info, str(self.unreadMsg) + self.or_more)
130
def get_extra_information(self):
133
while i < len(self.news_info) and i < 4:
134
feed_info += "<b>" + self.news_info[i].name + "</b>: <i>" + str(self.news_info[i].messages) + "</i>\n"
135
if i == 3 and i < len(self.news_info) - 1:
136
feed_info += "and others..."
141
def read_cache_file(self):
142
if os.path.exists(self.cache_file):
144
f = open(self.cache_file, "r")
146
self.specto.logger.log(_("There was an error reader the file %s") % self.cache_file, "critical", self.__class__)
149
info = line.split("&Seperator;")
150
feed = Feed(info[0], info[1].replace("\n", ""))
151
self.news_info.add(feed)
156
def write_cache_file(self):
158
f = open(self.cache_file, "w")
160
self.specto.logger.log(_("There was an error opening the file %s") % self.cache_file, "critical", self.__class__)
162
for feed in self.news_info:
163
f.write(feed.name + "&Seperator;" + str(feed.messages) + "\n")
118
doc = minidom.parse(self.cache_file)
119
rootNode = doc.documentElement
121
self.walk(rootNode, False, attributes)
123
if self.oldMsg == 0 and self.newMsg == 0:#no unread messages, we need to clear the watch
124
self.actually_updated=False
125
self.specto.notifier.clear_watch("", self.id)
127
self.actually_updated=True
129
self.oldMsg = self.newMsg
132
self.specto.logger.log(_("Watch: \"%s\" has error: Error in processing cache file") % self.name, "error", self.__class__)
134
self.specto.mark_watch_busy(False, self.id)
135
Watch.update(self, lock)
139
header = {'User-agent' : self.source}
140
post_data = urllib.urlencode({ 'Email': self.user, 'Passwd': self.password, 'service': 'reader', 'source': self.source, 'continue': self.google_url, })
141
request = urllib2.Request(self.login_url, post_data, header)
144
f = urllib2.urlopen( request )
148
self.specto.logger.log(_("Watch: \"%s\" has error: wrong username/password") % self.name, "error", self.__class__)
150
return re.search('SID=(\S*)', result).group(1)
152
def walk(self, parent, get_news, attributes): # [1]
153
for node in parent.childNodes:
154
if node.nodeType == Node.ELEMENT_NODE:
155
attrs = node.attributes
156
for attrName in attrs.keys():
157
attrNode = attrs.get(attrName)
158
attrValue = attrNode.nodeValue
159
if node.nodeName == "entry":
160
attributes.append(attrName)
163
if node.nodeName == 'title' and get_news == True:
164
self.get_news_information(node, attrs, attrName)
166
if 'gr:is-read-state-locked' in attributes:
168
# Walk the child nodes.
169
self.walk(node, get_news, attributes)
171
def get_news_information(self,node, attrs, attrName):
172
# Walk over any text nodes in the current node.
174
for child in node.childNodes:
175
if child.nodeType == Node.TEXT_NODE:
176
content.append(child.nodeValue)
178
if content[0] not in self.news_titles:
179
#print content[0] #uncomment this to see the titles from the unread news items
180
self.news_titles.append(content[0])
183
#get a feed of the users unread items
184
def get_reading_list(self,SID):
185
return self.get_results(SID, self.reading_url)
187
#get results from url
188
def get_results(self, SID, url):
189
header = {'User-agent' : self.source}
190
header['Cookie']='Name=SID;SID=%s;Domain=.google.com;Path=/;Expires=160000000000' % SID
192
request = urllib2.Request(url, None, header)
195
f = urllib2.urlopen( request )
199
self.specto.logger.log(_("Watch: \"%s\" has error: Error getting data from %s") % self.name %url, "error", self.__class__)
203
def set_username(self, username):
204
""" Set the username for the watch. """
207
def set_password(self, password):
208
""" Set the password for the watch. """
209
self.password = password
167
def remove_cache_files(self):
168
os.unlink(self.cache_file)
172
def __init__(self, name, messages):
174
self.messages = int(messages)
178
class Feed_collection():
181
self.feed_collection = []
186
for _feed in self.feed_collection:
187
if feed.name == _feed.name:
188
if feed.messages > _feed.messages:
191
_feed.messages = feed.messages
194
_feed.messages = feed.messages
201
self.feed_collection.append(feed)
203
elif self.updated == True:
210
def __getitem__(self, id):
211
return self.feed_collection[id]
214
return len(self.feed_collection)
216
def remove_old(self):
219
for _feed in self.feed_collection:
220
if _feed.found == True:
221
collection_copy.append(_feed)
223
self.feed_collection = collection_copy
226
for _feed in self.feed_collection:
229
_feed.updated = False
231
def get_unread_messages(self):
233
for _feed in self.feed_collection:
234
if _feed.new == True or _feed.updated == True:
245
GrNotify is a simple Python written tray application that will allow you to know when there are new items in the Google Reader.
247
GrNotify is written by Kristof Bamps
248
- And maintained by Bram Bonne and Eric Lembregts
253
* Python >= 2.2 <http://www.python.org>
254
* PyXML >= 0.8.3 <http://pyxml.sourceforge.net>
255
* PyGTK >= 2.0 <http://www.pygtk.org>
259
import xml.dom.minidom
264
###############################
267
counter = 1 #boolean to show counter or not
268
numberFeeds = 5 #default maximum number of feeds of which info is shows
269
L = [] #contains feed ids and their number of unread items
270
names = [] #contains names of all feeds
271
feeds = 0 # number of feeds user is subscribed to
278
################################
281
COOKIEFILE = os.path.join(spectlib.util.get_path('tmp'), 'cookies.lwp')
282
# the path and filename to save your cookies in
288
# Let's see if cookielib is available
292
# If importing cookielib fails
293
# let's try ClientCookie
297
# ClientCookie isn't available either
298
urlopen = urllib2.urlopen
299
Request = urllib2.Request
301
# imported ClientCookie
302
urlopen = ClientCookie.urlopen
303
Request = ClientCookie.Request
304
cj = ClientCookie.LWPCookieJar()
307
# importing cookielib worked
308
urlopen = urllib2.urlopen
309
Request = urllib2.Request
310
cj = cookielib.LWPCookieJar()
311
# This is a subclass of FileCookieJar
312
# that has useful load and save methods
315
# we successfully imported
316
# one of the two cookie handling modules
317
if os.path.isfile(COOKIEFILE):
318
# if we have a cookie file already saved
319
# then load the cookies into the Cookie Jar
322
# Now we need to get our Cookie Jar
323
# installed in the opener;
325
if cookielib is not None:
326
# if we use cookielib
327
# then we get the HTTPCookieProcessor
328
# and install the opener in urllib2
329
opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(cj))
330
urllib2.install_opener(opener)
333
# if we use ClientCookie
334
# then we get the HTTPCookieProcessor
335
# and install the opener in ClientCookie
336
opener = ClientCookie.build_opener(ClientCookie.HTTPCookieProcessor(cj))
337
ClientCookie.install_opener(opener)
340
url = 'https://www.google.com/accounts/ServiceLoginAuth'
341
user_agent = 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'
342
login = {'Email' : email,
345
data = urllib.urlencode(login)
348
# an example url that sets a cookie,
349
# try different urls here and see the cookie collection you can make !
352
# if we were making a POST type request,
353
# we could encode a dictionary of values here,
354
# using urllib.urlencode(somedict)
356
txheaders = {'User-agent' : 'Mozilla/4.0 (compatible; MSIE 5.5; Windows NT)'}
357
# fake a user agent, some websites (like google) don't like automated exploration
360
req = Request(theurl, txdata, txheaders)
361
# create a request object
363
handle = urlopen(req)
364
# and open it to return a handle on the url
369
return 2 #we didn't get a connection
373
return 3 #we got a connection, but didn't get any cookies
375
cj.save(COOKIEFILE) # save the cookies again
376
return 1 #everything went ok
378
##########################
379
#Get the number of unread items
381
def getUnreadItems():
382
global unread, L, feeds
383
LISTFILE = os.path.join(spectlib.util.get_path('tmp'), 'list.xml')
384
url = 'https://www.google.com/reader/api/0/unread-count?all=true'
387
response = urlopen(req)
390
return 2 #we didn't get a connection
391
testxml = response.read()
393
if '<object>' in testxml:
394
fileHandle = open ( LISTFILE, 'w' )
395
fileHandle.write (testxml)
398
fileHandle = open ( LISTFILE )
399
unread = xml.dom.minidom.parse(fileHandle)
402
countlist = unread.getElementsByTagName('number')
403
namelist = unread.getElementsByTagName('string')
404
for count in countlist:
405
if count.attributes["name"].value != 'count':
406
countlist.remove (count)
410
for i in xrange (0, len(countlist)):
411
if 'state/com.google/reading-list' in namelist[i].firstChild.toxml():
412
unread = countlist[i].firstChild.toxml()
415
L.append ((countlist[i].firstChild.toxml() , namelist[i].firstChild.toxml() ))
418
if not found: #if there aren't any subscribed feeds
420
L = sorted (L, compare)
426
##################################
427
#Set the names of feeds the user is subscribed to
431
LISTFILE = os.path.join(spectlib.util.get_path('tmp'), 'names.xml')
432
url = 'http://www.google.com/reader/api/0/subscription/list'
435
response = urlopen(req)
438
return 2 #we didn't get a connection
439
testxml = response.read() #read the opened page
441
if '<object>' in testxml: #if we got a XML file
442
fileHandle = open ( LISTFILE, 'w' )
443
fileHandle.write (testxml)
446
fileHandle = open ( LISTFILE )
447
document = xml.dom.minidom.parse(fileHandle)
451
feedlist = document.getElementsByTagName('string')
452
for j in xrange (0, len(feedlist)):
453
if (feedlist[j].attributes["name"].value == 'id' or feedlist[j].attributes["name"].value == 'title'):
454
if ('/state/com.google/broadcast' in feedlist[j].firstChild.toxml() or feedlist[j].firstChild.toxml()[0] != 'u'):
455
names.append (feedlist[j].firstChild.toxml())
461
LISTFILE = os.path.join(spectlib.util.get_path('tmp'), 'names.xml')
462
if os.path.isfile(LISTFILE):
463
fileHandle = open ( LISTFILE )
464
document = xml.dom.minidom.parse(fileHandle)
466
feedlist = document.getElementsByTagName('string')
467
for j in xrange (0, len(feedlist)):
468
if (feedlist[j].attributes["name"].value == 'id' or feedlist[j].attributes["name"].value == 'title'):
469
if ('/state/com.google/broadcast' in feedlist[j].firstChild.toxml() or feedlist[j].firstChild.toxml()[0] != 'u'):
470
names.append (feedlist[j].firstChild.toxml())
477
#################################
478
#Compare function to sort the feeds by number of unread items
481
return cmp(int(b[0]), int(a[0]))
484
def __init__(self, username, password):
485
global unread, info, extra_info, extra_info_friends, email, passwd
486
global config_changed
491
cookies = getcookies()
493
cookies = getUnreadItems()
495
info = -1, 'Wrong username or password'
498
info = -1, 'Could not establish a connection to server'
501
info = -1, 'Could not get cookies'
504
cookies = getcookies()
507
info = 1, 'You are not subscribed to any feeds'
508
elif (unread == '0'):
509
info = 1, 'No unread items'
510
elif (unread >= '1'):
514
if (len(L) >= numberFeeds):
519
extra_info_friends = ''
520
for i in xrange (0,i):
522
for j in xrange (0, len(names)):
524
if (str(L[i][1]) == names[j] or '/state/com.google/broadcast-friends' in L[i][1]) and int (L[i][0]) != 0:
526
if ('/state/com.google/broadcast-friends' in L[i][1]):
527
extra_info_friends += str(L[i][0])
529
extra_info.update({names[j+1]: int(L[i][0])})
530
del L[:] #set the table back to empty, so same items don't get added time after time
533
#remove the cache files
534
os.unlink(os.path.join(spectlib.util.get_path('tmp'), 'names.xml'))
535
os.unlink(os.path.join(spectlib.util.get_path('tmp'), 'cookies.lwp'))
536
os.unlink(os.path.join(spectlib.util.get_path('tmp'), 'list.xml'))
539
cj.clear_session_cookies()
540
return info, extra_info_friends, extra_info