#!/usr/bin/python2.7 # -*- coding: utf-8 -*- '''Post weekly Last.fm top artist to Twitter''' __author__ = 'bdpepple@google.com' import ConfigParser import getopt import os import sys import pylast import twitter USAGE = '''Usage: last_shout.py [options] This script posts users Top Artist from Last.fm to Twitter. Options: -h --help : print this help -u --user : last.fm username -n --number : number of last.fm top artists (default is 3) --consumer-key : the twitter consumer key --consumer-secret : the twitter consumer secret --access-key : the twitter access token key --access-secret : the twitter access token secret --last-access-key : the last.fm api key --encoding : the character set encoding used in input strings, e.g. "utf-8". [optional] Documentation: If either of the command line flags are not present, the environment variables TWEETUSERNAME and TWEETPASSWORD will then be checked for your consumer_key or consumer_secret, respectively. If neither the command line flags nor the environment variables are present, the .tweetrc file, if it exists, can be used to set the default consumer_key and consumer_secret. The file should contain the following three lines, replacing *consumer_key* with your consumer key, and *consumer_secret* with your consumer secret: A skeletal .tweetrc file: [Tweet] consumer_key: *consumer_key* consumer_secret: *consumer_password* access_key: *access_key* access_secret: *access_password* last_access_key: *lastfm_api_key* last_user: *lastfm username* ''' def PrintUsageAndExit(): print USAGE sys.exit(2) def GetConsumerKeyEnv(): return os.environ.get("TWEETUSERNAME", None) def GetConsumerSecretEnv(): return os.environ.get("TWEETPASSWORD", None) def GetAccessKeyEnv(): return os.environ.get("TWEETACCESSKEY", None) def GetAccessSecretEnv(): return os.environ.get("TWEETACCESSSECRET", None) def GetLastAccessKeyEnv(): return os.environ.get("LASTACCESSKEY", None) class TweetRc(object): def __init__(self): self._config = None def GetConsumerKey(self): return self._GetOption('consumer_key') def GetConsumerSecret(self): return self._GetOption('consumer_secret') def GetAccessKey(self): return self._GetOption('access_key') def GetAccessSecret(self): return self._GetOption('access_secret') def GetLastAccessKey(self): return self._GetOption('last_access_key') def GetLastUser(self): return self._GetOption('last_user') def _GetOption(self, option): try: return self._GetConfig().get('Tweet', option) except: return None def _GetConfig(self): if not self._config: self._config = ConfigParser.ConfigParser() self._config.read(os.path.expanduser('~/.tweetrc')) return self._config def truncate_tweet(s, min_pos=0, max_pos=134, ellipsis=True): NOT_FOUND = -1 ERR_MAXMIN = 'Minimum position cannot be greater than maximum position' if max_pos < min_pos: raise ValueError(ERR_MAXMIN) if ellipsis: suffix = '...' else: suffix = '' length = len(s) if length <= max_pos: return s + suffix else: try: # Return it to nearest comma if possible end = s.rindex(',',min_pos,max_pos) except ValueError: # Return string to nearest space end = s.rfind(' ',min_pos,max_pos) if end == NOT_FOUND: end = max_pos return s[0:end] + suffix def build_string(list): c = len(list) txt = u"\u266A" + " My Top " + str(c) + " #lastfm artists: " x = 1 for i in list: txt = txt + i.item.name + " (" + i.weight + ")" if x < c - 1: txt = txt + ", " x += 1 elif x == c - 1: txt = txt + " & " x += 1 txt = truncate_tweet(txt, ellipsis=False) txt = txt + " #music" return txt def get_top_artist(last_access_key, username, number): network = pylast.LastFMNetwork(last_access_key) user = network.get_user(username) artist = user.get_top_artists(period="7day", limit=number) return artist def send_tweet(consumer_key, consumer_secret, access_key, access_secret, txt, encoding): api = twitter.Api(consumer_key = consumer_key, consumer_secret = consumer_secret, access_token_key = access_key, access_token_secret = access_secret, input_encoding=encoding) try: status = api.PostUpdate(txt) except UnicodeDecodeError: print "Your message could not be encoded. " print "Perhaps it contains non-ASCII characters? " print "Try explicitly specifying the encoding with the --encoding flag" sys.exit(2) print "%s just posted: %s" % (status.user.name, status.text) def main(): try: longflags = ['help', 'user=', 'number=', 'consumer-key=', 'consumer-secret=', 'access-key=', 'access-secret=', 'last-access-key', 'encoding='] opts, args = getopt.gnu_getopt(sys.argv[1:], "hu:n:", longflags) except getopt.GetoptError: PrintUsageAndExit() consumer_keyflag = None consumer_secretflag = None access_keyflag = None access_secretflag = None last_access_keyflag = None username = None number = 3 encoding = None for o, a in opts: if o in ("-h", "--help"): PrintUsageAndExit() if o in ("-u", "--user"): username = a if o in ("-n", "--number"): number = a if o in ("--consumer-key"): consumer_keyflag = a if o in ("--consumer-secret"): consumer_secretflag = a if o in ("--access-key"): access_keyflag = a if o in ("--access-secret"): access_secretflag = a if o in ("--last-access-key"): last_access_keyflag = a if o in ("--encoding"): encoding = a rc = TweetRc() consumer_key = consumer_keyflag or GetConsumerKeyEnv() or rc.GetConsumerKey() consumer_secret = consumer_secretflag or GetConsumerSecretEnv() or rc.GetConsumerSecret() access_key = access_keyflag or GetAccessKeyEnv() or rc.GetAccessKey() access_secret = access_secretflag or GetAccessSecretEnv() or rc.GetAccessSecret() last_access_key = last_access_keyflag or GetLastAccessKeyEnv() or rc.GetLastAccessKey() last_user = username or rc.GetLastUser() if (not consumer_key or not consumer_secret or not access_key or not access_secret or not last_access_key or not last_user): PrintUsageAndExit() artist = get_top_artist(last_access_key, last_user, number) txt = build_string(artist) send_tweet(consumer_key, consumer_secret, access_key, access_secret, txt, encoding) if __name__ == "__main__": main()