Created
July 28, 2009 06:51
-
-
Save yanbe/156996 to your computer and use it in GitHub Desktop.
A 200 lines Twitter client in Python featuring ballon notification on GNOME
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
# Your account information | |
username="" | |
password="" | |
# screen_resolution is used for balloon positioning | |
screen_resolution=1024,768 | |
# Minimum update_interval under Twitter API restriction is 60*60/70=51.4 | |
update_interval=52 | |
# Balloon will disapear after notify_timeout seconds | |
notify_timeout=10 | |
import os | |
import sys | |
import urllib2 | |
from xml.dom.minidom import parse | |
import re | |
import webbrowser | |
import time | |
import pygtk | |
pygtk.require('2.0') | |
import gtk | |
import gobject | |
import pynotify | |
tmpdir="/tmp/twitter-notifier/" | |
favicon_url = "http://assets3.twitter.com/images/favicon.ico" | |
update_url = "http://twitter.com/statuses/update.xml" | |
timeline_url ='http://twitter.com/statuses/friends_timeline.xml' | |
auth_handler = urllib2.HTTPBasicAuthHandler() | |
auth_handler.add_password( | |
'Twitter API', 'http://twitter.com/', | |
username, password) | |
opener = urllib2.build_opener(auth_handler) | |
urllib2.install_opener(opener) | |
tagText = lambda node, tagName: \ | |
node.getElementsByTagName(tagName)[0].firstChild.nodeValue | |
def update(text): | |
print >>sys.stderr, time.strftime("[%H:%M:%S] "), "updating" | |
req = urllib2.Request(update_url) | |
req.add_header("User-Agent", "TwitterNotifier http://yanbe.org/twitter-notifier/") | |
req.add_header("X-Twitter-Client", "TwitterNotifier") | |
req.add_header("X-Twitter-Client-URL", "http://yanbe.org/twitter-notifier/") | |
req.add_header("X-Twitter-Client-Version", "0.1") | |
req.add_data("source=twnotifier&status="+text) | |
res = parse(file=urllib2.urlopen(req)) | |
last_id = 0 | |
twits = [] | |
def check_update(): | |
print >>sys.stderr, time.strftime("[%H:%M:%S] "), "checking update" | |
gobject.timeout_add(update_interval*1000, check_update) | |
r = urllib2.Request(timeline_url) | |
global last_id | |
r.add_data("since_id="+str(last_id)) | |
e = parse(file=urllib2.urlopen(r)) | |
for s in reversed(e.getElementsByTagName("status")): | |
if last_id and int(tagText(s, "id")) > last_id: | |
screen_name = tagText(s, "screen_name") | |
text = tagText(s, "text") | |
source = tagText(s,"source") | |
icon_url = tagText(s,"profile_image_url") | |
twits.append((screen_name, text, source, icon_url)) | |
#show_notify(screen_name, text, source, icon_url) | |
print >>sys.stderr, "%s: %s from %s" % (screen_name, text, source) | |
last_id = int(tagText(s, "id")) | |
user_page_tmpl = '<a href="http://twitter.com/%s/with_friends">%s</a>' | |
last_notify=None | |
def show_notify(): | |
gobject.timeout_add(notify_timeout*1000+1000, show_notify) | |
global twits | |
if len(twits) == 0: | |
return | |
screen_name, text, source, icon_url = twits.pop(0) | |
url = re.search('http://\S+', text, re.UNICODE) | |
reply = re.search('@(\w+)', text) | |
if url: | |
url = url.group(0) | |
a = '<a href="%s">%s</a>' % (url, url) | |
text = text.replace(url, a) | |
if reply: | |
reply = reply.group(1) | |
a = user_page_tmpl % (reply, reply) | |
text = text.replace(reply, a) | |
n = pynotify.Notification(screen_name, '%s from %s' % (text, source), get_icon(screen_name, icon_url)) | |
n.set_hint("x", screen_resolution[0]-50) | |
n.set_hint("y", screen_resolution[1]-30) | |
n.add_action("@%s " % screen_name, "Reply", edit_cb) | |
n.add_action("http://twitter.com/%s/with_friends" % screen_name, "Browse", browse_url) | |
n.set_timeout(notify_timeout*1000) | |
n.show() | |
global last_notify | |
last_notify=n | |
def browse_url(n, url): | |
webbrowser.open(url) | |
def get_icon(screen_name, icon_url): | |
icon_url = icon_url.replace("s3.amazonaws.com:", "s3.amazonaws.com") | |
if not os.access(tmpdir, os.F_OK): | |
os.mkdir(tmpdir) | |
filename = tmpdir+screen_name | |
if not os.access(filename, os.F_OK): | |
f = file(filename, "wb") | |
f.write(urllib2.urlopen(icon_url).read()) | |
return filename | |
def quit_cb(widget, data = None): | |
if data: | |
data.set_visible(False) | |
gtk.main_quit() | |
def last_cb(wigdet, data = None): | |
global last_notify | |
if last_notify: | |
last_notify.show() | |
def edit_cb(widget, data = None): | |
text = gtk.Entry() | |
text.connect("activate", enter_cb) | |
if data: | |
text.set_text(data) | |
win = gtk.Window() | |
win.add(text) | |
win.set_size_request(600,25) | |
win.set_title("What are you doing?") | |
win.set_position(gtk.WIN_POS_CENTER) | |
text.show() | |
win.show() | |
def enter_cb(widget, data = None): | |
widget.parent.hide() | |
update(widget.get_text()) | |
def refresh_cb(widget, data = None): | |
notify() | |
def home_cb(widget, data = None): | |
browse_url(None, "http://twitter.com/home") | |
def popup_menu_cb(widget, button, time, data = None): | |
if button == 3: | |
if data: | |
data.show_all() | |
data.popup(None, None, None, 3, time) | |
def activate_icon_cb(widget, data = None): | |
msgBox = gtk.MessageDialog(parent = None, buttons = gtk.BUTTONS_OK, | |
message_format = "TwitterNotifier") | |
msgBox.run() | |
msgBox.destroy() | |
def main(): | |
pynotify.init("TwitterNotifier") | |
statusIcon = gtk.StatusIcon() | |
menu = gtk.Menu() | |
menuItem = gtk.ImageMenuItem(gtk.STOCK_ABOUT) | |
menuItem.connect('activate', activate_icon_cb) | |
menu.append(menuItem) | |
menuItem = gtk.ImageMenuItem(gtk.STOCK_HOME) | |
menuItem.connect('activate', home_cb, statusIcon) | |
menu.append(menuItem) | |
menuItem = gtk.ImageMenuItem(gtk.STOCK_REFRESH) | |
menuItem.connect('activate', refresh_cb, statusIcon) | |
menu.append(menuItem) | |
menuItem = gtk.ImageMenuItem(gtk.STOCK_UNDO) | |
menuItem.connect('activate', last_cb, statusIcon) | |
menu.append(menuItem) | |
menuItem = gtk.ImageMenuItem(gtk.STOCK_EDIT) | |
menuItem.connect('activate', edit_cb, statusIcon) | |
menu.append(menuItem) | |
menuItem = gtk.ImageMenuItem(gtk.STOCK_QUIT) | |
menuItem.connect('activate', quit_cb, statusIcon) | |
menu.append(menuItem) | |
statusIcon.set_from_file(get_icon("twitter", favicon_url)) | |
statusIcon.set_tooltip("TwitterNotifier") | |
statusIcon.connect('popup-menu', popup_menu_cb, menu) | |
statusIcon.connect('activate', edit_cb, None) | |
statusIcon.set_visible(True) | |
gobject.timeout_add(1000, check_update) | |
gobject.timeout_add(4000, show_notify) | |
gtk.main() | |
if __name__ == '__main__': | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment