2013-12-12 21:38:31 +00:00
|
|
|
#!/usr/bin/env python
|
2013-11-28 08:33:25 +00:00
|
|
|
|
2014-06-07 05:18:04 +00:00
|
|
|
import os
|
|
|
|
|
|
|
|
if(os.environ.get('TRAVIS')!='true'):
|
2014-06-06 21:38:31 +00:00
|
|
|
import pygtk
|
2013-12-12 21:38:31 +00:00
|
|
|
|
2014-06-06 21:38:31 +00:00
|
|
|
pygtk.require('2.0')
|
|
|
|
import gtk
|
2013-11-28 08:33:25 +00:00
|
|
|
|
2014-06-06 21:38:31 +00:00
|
|
|
import webbrowser
|
2014-06-07 05:27:48 +00:00
|
|
|
|
|
|
|
try:
|
|
|
|
import appindicator
|
|
|
|
except ImportError:
|
|
|
|
import appindicator_replacement as appindicator
|
|
|
|
|
|
|
|
from appindicator_replacement import get_icon_filename
|
|
|
|
|
2013-11-28 08:33:25 +00:00
|
|
|
import json
|
2013-12-01 11:15:12 +00:00
|
|
|
import argparse
|
2013-11-28 08:33:25 +00:00
|
|
|
from os.path import expanduser
|
2013-12-01 12:22:59 +00:00
|
|
|
import signal
|
2013-11-28 08:33:25 +00:00
|
|
|
|
2013-12-01 11:15:12 +00:00
|
|
|
##This is to get --version to work
|
|
|
|
try:
|
2013-12-12 21:38:31 +00:00
|
|
|
import pkg_resources
|
|
|
|
|
|
|
|
__version = pkg_resources.require("hackertray")[0].version
|
2014-06-07 05:35:42 +00:00
|
|
|
except:
|
2013-12-12 21:38:31 +00:00
|
|
|
__version = "Can't read version number."
|
2013-12-01 11:15:12 +00:00
|
|
|
|
2013-11-29 19:46:39 +00:00
|
|
|
from hackernews import HackerNews
|
2014-06-06 21:12:59 +00:00
|
|
|
from chrome import Chrome
|
2013-12-12 21:38:31 +00:00
|
|
|
|
2013-11-28 08:33:25 +00:00
|
|
|
class HackerNewsApp:
|
2013-12-12 21:38:31 +00:00
|
|
|
HN_URL_PREFIX = "https://news.ycombinator.com/item?id="
|
|
|
|
|
2014-06-07 06:54:37 +00:00
|
|
|
def __init__(self, args):
|
2013-12-12 21:38:31 +00:00
|
|
|
#Load the database
|
|
|
|
home = expanduser("~")
|
|
|
|
with open(home + '/.hackertray.json', 'a+') as content_file:
|
|
|
|
content_file.seek(0)
|
|
|
|
content = content_file.read()
|
|
|
|
try:
|
|
|
|
self.db = set(json.loads(content))
|
|
|
|
except ValueError:
|
|
|
|
self.db = set()
|
|
|
|
|
|
|
|
# create an indicator applet
|
|
|
|
self.ind = appindicator.Indicator("Hacker Tray", "hacker-tray", appindicator.CATEGORY_APPLICATION_STATUS)
|
|
|
|
self.ind.set_status(appindicator.STATUS_ACTIVE)
|
|
|
|
self.ind.set_icon(get_icon_filename("hacker-tray.png"))
|
|
|
|
|
|
|
|
# create a menu
|
|
|
|
self.menu = gtk.Menu()
|
|
|
|
|
|
|
|
#The default state is false, and it toggles when you click on it
|
2014-06-07 06:54:37 +00:00
|
|
|
self.commentState = args.comments
|
2013-12-12 21:38:31 +00:00
|
|
|
|
|
|
|
# create items for the menu - refresh, quit and a separator
|
|
|
|
menuSeparator = gtk.SeparatorMenuItem()
|
|
|
|
menuSeparator.show()
|
|
|
|
self.menu.append(menuSeparator)
|
|
|
|
|
|
|
|
btnComments = gtk.CheckMenuItem("Show Comments")
|
|
|
|
btnComments.show()
|
2014-06-07 06:54:37 +00:00
|
|
|
btnComments.set_active(args.comments)
|
2013-12-12 21:38:31 +00:00
|
|
|
btnComments.connect("activate", self.toggleComments)
|
|
|
|
self.menu.append(btnComments)
|
|
|
|
|
|
|
|
btnAbout = gtk.MenuItem("About")
|
|
|
|
btnAbout.show()
|
|
|
|
btnAbout.connect("activate", self.showAbout)
|
|
|
|
self.menu.append(btnAbout)
|
|
|
|
|
|
|
|
btnRefresh = gtk.MenuItem("Refresh")
|
|
|
|
btnRefresh.show()
|
|
|
|
#the last parameter is for not running the timer
|
|
|
|
btnRefresh.connect("activate", self.refresh, True)
|
|
|
|
self.menu.append(btnRefresh)
|
|
|
|
|
|
|
|
btnQuit = gtk.MenuItem("Quit")
|
|
|
|
btnQuit.show()
|
|
|
|
btnQuit.connect("activate", self.quit)
|
|
|
|
self.menu.append(btnQuit)
|
|
|
|
|
|
|
|
self.menu.show()
|
|
|
|
|
|
|
|
self.ind.set_menu(self.menu)
|
2014-06-07 06:54:37 +00:00
|
|
|
self.refresh(chrome_data_directory=args.chrome)
|
2013-12-12 21:38:31 +00:00
|
|
|
|
|
|
|
def toggleComments(self, widget):
|
|
|
|
"""Whether comments page is opened or not"""
|
|
|
|
self.commentState = not self.commentState
|
|
|
|
|
|
|
|
def showAbout(self, widget):
|
|
|
|
"""Handle the about btn"""
|
|
|
|
webbrowser.open("https://github.com/captn3m0/hackertray/")
|
|
|
|
|
|
|
|
#ToDo: Handle keyboard interrupt properly
|
|
|
|
def quit(self, widget, data=None):
|
|
|
|
""" Handler for the quit button"""
|
|
|
|
l = list(self.db)
|
|
|
|
home = expanduser("~")
|
|
|
|
|
|
|
|
#truncate the file
|
|
|
|
with open(home + '/.hackertray.json', 'w+') as file:
|
|
|
|
file.write(json.dumps(l))
|
|
|
|
|
|
|
|
gtk.main_quit()
|
|
|
|
|
|
|
|
def run(self):
|
|
|
|
signal.signal(signal.SIGINT, self.quit)
|
|
|
|
gtk.main()
|
|
|
|
return 0
|
|
|
|
|
|
|
|
def open(self, widget, event=None, data=None):
|
|
|
|
"""Opens the link in the web browser"""
|
|
|
|
#We disconnect and reconnect the event in case we have
|
|
|
|
#to set it to active and we don't want the signal to be processed
|
|
|
|
if not widget.get_active():
|
|
|
|
widget.disconnect(widget.signal_id)
|
|
|
|
widget.set_active(True)
|
|
|
|
widget.signal_id = widget.connect('activate', self.open)
|
|
|
|
|
|
|
|
self.db.add(widget.item_id)
|
|
|
|
webbrowser.open(widget.url)
|
|
|
|
|
|
|
|
if self.commentState:
|
|
|
|
webbrowser.open(self.HN_URL_PREFIX + widget.hn_id)
|
|
|
|
|
|
|
|
def addItem(self, item):
|
|
|
|
"""Adds an item to the menu"""
|
|
|
|
#This is in the case of YC Job Postings, which we skip
|
|
|
|
if item['points'] == 0 or item['points'] is None:
|
|
|
|
return
|
|
|
|
|
|
|
|
i = gtk.CheckMenuItem(
|
|
|
|
"(" + str(item['points']).zfill(3) + "/" + str(item['comments_count']).zfill(3) + ") " + item['title'])
|
|
|
|
|
2014-06-07 06:54:37 +00:00
|
|
|
visited = item['history'] or item['id'] in self.db
|
|
|
|
|
|
|
|
i.set_active(visited)
|
2013-12-12 21:38:31 +00:00
|
|
|
i.url = item['url']
|
|
|
|
i.signal_id = i.connect('activate', self.open)
|
|
|
|
i.hn_id = item['id']
|
|
|
|
i.item_id = item['id']
|
|
|
|
self.menu.prepend(i)
|
|
|
|
i.show()
|
|
|
|
|
2014-06-07 06:54:37 +00:00
|
|
|
def refresh(self, widget=None, no_timer=False, chrome_data_directory=None):
|
2013-12-12 21:38:31 +00:00
|
|
|
"""Refreshes the menu """
|
2014-06-07 06:54:37 +00:00
|
|
|
data = list(reversed(HackerNews.getHomePage()[0:20]))
|
|
|
|
|
|
|
|
if(chrome_data_directory):
|
|
|
|
urls = [item['url'] for item in data]
|
|
|
|
searchResults = Chrome.search(urls, chrome_data_directory)
|
2013-12-12 21:38:31 +00:00
|
|
|
|
|
|
|
#Remove all the current stories
|
|
|
|
for i in self.menu.get_children():
|
|
|
|
if hasattr(i, 'url'):
|
|
|
|
self.menu.remove(i)
|
|
|
|
|
|
|
|
#Add back all the refreshed news
|
2014-06-07 06:54:37 +00:00
|
|
|
for index, item in enumerate(data):
|
|
|
|
if(chrome_data_directory):
|
|
|
|
item['history'] = searchResults[index]
|
|
|
|
else:
|
|
|
|
item['history'] = False
|
|
|
|
self.addItem(item)
|
2013-12-12 21:38:31 +00:00
|
|
|
|
|
|
|
#Call every 5 minutes
|
|
|
|
if not no_timer:
|
2014-06-07 08:36:55 +00:00
|
|
|
gtk.timeout_add(10 * 60 * 1000, self.refresh, widget, no_timer, chrome_data_directory)
|
2013-12-12 21:38:31 +00:00
|
|
|
|
2013-11-28 08:33:25 +00:00
|
|
|
|
|
|
|
def main():
|
2013-12-12 21:38:31 +00:00
|
|
|
parser = argparse.ArgumentParser(description='Hacker News in your System Tray')
|
2014-01-29 14:34:10 +00:00
|
|
|
parser.add_argument('-v','--version', action='version', version=__version)
|
|
|
|
parser.add_argument('-c','--comments', dest='comments',action='store_true', help="Load the HN comments link for the article as well")
|
2014-06-07 06:54:37 +00:00
|
|
|
parser.add_argument('--chrome', dest='chrome', help="Specify a Google Chrome Profile directory to use for matching chrome history")
|
2014-01-29 14:34:10 +00:00
|
|
|
parser.set_defaults(comments=False)
|
|
|
|
args = parser.parse_args()
|
2014-06-07 06:54:37 +00:00
|
|
|
indicator = HackerNewsApp(args)
|
2013-12-12 21:38:31 +00:00
|
|
|
indicator.run()
|