Esempio n. 1
0
class Turpial(cmd.Cmd):
    def __init__(self):
        cmd.Cmd.__init__(self)
        
        parser = OptionParser()
        parser.add_option('-d', '--debug', dest='debug', action='store_true',
            help='show debug info in shell during execution', default=False)
        parser.add_option('-m', '--command', dest='command', action='store_true',
            help='execute a single command', default=False)
        parser.add_option('-c', '--clean', dest='clean', action='store_true',
            help='clean all bytecodes', default=False)
        parser.add_option('-s', '--save-credentials', dest='save', action='store_true',
            help='save user credentials', default=False)
        parser.add_option('--version', dest='version', action='store_true',
            help='show the version of Turpial and exit', default=False)
        
        (options, args) = parser.parse_args()
        
        if options.debug or options.clean: 
            logging.basicConfig(level=logging.DEBUG)
        else:
            logging.basicConfig(level=logging.INFO)
        
        self.log = logging.getLogger('Turpial:Cmd')
        #self.config = None
        self.prompt = 'turpial> '
        self.intro = '\n'.join(INTRO)
        self.core = Core()
        #self.app_cfg = ConfigApp()
        #self.version = self.app_cfg.read('App', 'version')
        
        if options.clean:
            clean_bytecodes(__file__, self.log)
            sys.exit(0)
            
        if options.version:
            print "turpial (cmd) v%s" % self.version
            print "libturpial v%s" % libturpial_ver
            print "python v%X" % sys.hexversion
            sys.exit(0)
        
        self.account = None
        
        try:
            self.cmdloop()
        except KeyboardInterrupt:
            self.do_exit()
        except EOFError:
            self.do_exit()
    
    def __validate_index(self, index, array, blank=False):
        try:
            a = array[int(index)]
            return True
        except IndexError:
            return False
        except ValueError:
            if blank and index == '':
                return True
            elif not blank and index == '':
                return False
            elif blank and index != '':
                return False
        except TypeError:
            if index is None:
                return False
    
    def __validate_accounts(self):
        if len(self.core.list_accounts()) > 0:
            return True
        print "You don't have any registered account. Run 'account add' command"
        return False
    
    def __validate_default_account(self):
        if self.account:
            return True
        print "You don't have a default account. Run 'account change' command"
        return False
        
    def __validate_arguments(self, arg_array, value):
        if value in arg_array:
            return True
        else:
            print 'Invalid Argument'
            return False
    
    def __build_message_menu(self):
        text = raw_input('Message: ')
        if text == '':
            print 'You must write something to post'
            return None
        
        if len(text) > 140:
            trunc = raw_input ('Your message has more than 140 characters. Do you want truncate it? [Y/n]: ')
            if trunc.lower() == 'y' or trunc == '':
                return text[:140]
            return None
        return text
    
    def __build_accounts_menu(self, _all=False):
        if len(self.core.list_accounts()) == 1: 
            return self.core.list_accounts()[0]
        
        index = None
        while 1:
            accounts = self.__show_accounts()
            if _all:
                index = raw_input('Select account (or Enter for all): ')
            else:
                index = raw_input('Select account: ')
            if not self.__validate_index(index, accounts, _all):
                print "Invalid account"
            else:
                break
        if index == '':
            return ''
        else:
            return accounts[int(index)]
    
    def __build_password_menu(self, account):
        passwd = None
        while 1:
            passwd = getpass.unix_getpass("Password for '%s' in '%s': " % (
                account.split('-')[0], account.split('-')[1]))
            if passwd:
                return passwd
            else:
                print "Password can't be blank"
            
    def __build_change_account_menu(self):
        if len(self.core.list_accounts()) == 1:
            if self.account:
                print "Your unique account is already your default"
            else:
                self.__add_first_account_as_default()
        elif len(self.core.list_accounts()) > 1:
            while 1:
                accounts = self.__show_accounts()
                index = raw_input('Select you new default account (or Enter for keep current): ')
                if index == '':
                    print "Default account remain with no changes"
                    return True
                if not self.__validate_index(index, accounts):
                    print "Invalid account"
                else:
                    break
            self.account = accounts[int(index)]
            print "Set %s in %s as your new default account" % (
                self.account.split('-')[0], self.account.split('-')[1])
        
    def __build_protocols_menu(self):
        index = None
        protocols = self.core.list_protocols()
        while 1:
            print "Available protocols:"
            for i in range(len(protocols)):
                print "[%i] %s" % (i, protocols[i])
            index = raw_input('Select protocol: ')
            if not self.__validate_index(index, protocols):
                print "Invalid protocol"
            else:
                break
        return protocols[int(index)]
    
    def __build_confirm_menu(self, message):
        confirm = raw_input(message + ' [y/N]: ')
        if confirm.lower() == 'y':
            return True
        else:
            return False
            
    def __user_input(self, message, blank=False):
        while 1:
            text = raw_input(message)
            if text == '' and not blank:
                print "You can't leave this field blank"
                continue
            break
        return text
        
    def __add_first_account_as_default(self):
        self.account = self.core.list_accounts()[0]
        print "Selected account %s in %s as default (*)" % (
            self.account.split('-')[0], self.account.split('-')[1])
    
    def __show_accounts(self):
        if len(self.core.list_accounts()) == 0:
            print "There are no registered accounts"
            return
        
        accounts = []
        print "Available accounts:"
        for acc in self.core.list_accounts():
            ch = ''
            if acc == self.account:
                ch = ' (*)'
            print "[%i] %s - %s%s" % (len(accounts), acc.split('-')[0], acc.split('-')[1], ch)
            accounts.append(acc)
        return accounts
        
    def __show_profiles(self, people):
        if not statuses:
            print "There are no profiles to show"
            return

        if people.code > 0: 
            print people.errmsg
            return
        
        for p in people:
            protected = '<protected>' if p.protected else ''
            following = '<following>' if p.following else ''
            
            header = "@%s (%s) %s %s" % (p.username, p.fullname, 
                following, protected)
            print header
            print '-' * len(header)
            print "URL: %s" % p.url
            print "Location: %s" % p.location
            print "Bio: %s" % p.bio
            if p.last_update: 
                print "Last: %s" % p.last_update
            print ''
    
    def __show_statuses(self, statuses):
        if not statuses:
            print "There are no statuses to show"
            return
        
        if statuses.code > 0:
            print statuses.errmsg
            return
        
        count = 1
        for status in statuses:
            text = status.text.replace('\n', ' ')
            inreply = ''
            client = ''
            if status.in_reply_to_user:
                inreply = ' in reply to %s' % status.in_reply_to_user
            if status.source:
                client = ' from %s' % status.source
            print "%d. @%s: %s (id: %s)" % (count, status.username, text, status.id_)
            print "%s%s%s" % (status.datetime, client, inreply)
            if status.reposted_by:
                users = ''
                for u in status.reposted_by:
                    users += u + ' '
                print 'Retweeted by %s' % status.reposted_by
            print
            count += 1
    
    def __process_login(self, acc):
        if not self.core.has_stored_passwd(acc):
            passwd = self.__build_password_menu(acc)
            username = acc.split('-')[0]
            protocol = acc.split('-')[1]
            self.core.register_account(username, protocol, passwd)
        
        rtn = self.core.login(acc)
        if rtn.code > 0:
            print rtn.errmsg
            return
        
        auth_obj = rtn.items
        if auth_obj.must_auth():
            print "Please visit %s, authorize Turpial and type the pin returned" % auth_obj.url
            pin = self.__user_input('Pin: ')
            self.core.authorize_oauth_token(acc, pin)
        
        rtn = self.core.auth(acc)
        if rtn.code > 0:
            print rtn.errmsg
        else:
            print 'Logged in with account %s' % acc.split('-')[0]
        
    def default(self, line):
        print '\n'.join(['Command not found.', INTRO[1], INTRO[2]])
        
    def emptyline(self):
        pass
    
    def do_account(self, arg):
        if not self.__validate_arguments(ARGUMENTS['account'], arg): 
            self.help_account(False)
            return False
        
        if arg == 'add':
            username = raw_input('Username: '******'Password: '******'Remember password')
            protocol = self.__build_protocols_menu()
            acc_id = self.core.register_account(username, protocol, password, remember)
            print 'Account added'
            if len(self.core.list_accounts()) == 1: 
                self.__add_first_account_as_default()
        elif arg == 'edit':
            if not self.__validate_default_account(): 
                return False
            password = getpass.unix_getpass('New Password: '******'-')[0]
            protocol = self.account.split('-')[1]
            remember = self.__build_confirm_menu('Remember password')
            self.core.register_account(username, protocol, password, remember)
            print 'Account edited'
        elif arg == 'delete':
            if not self.__validate_accounts(): 
                return False
            account = self.__build_accounts_menu()
            conf = self.__build_confirm_menu('Do you want to delete account %s?' %
                account)
            if not conf:
                print 'Command cancelled'
                return False
            del_all = self.__build_confirm_menu('Do you want to delete all data?')
            self.core.unregister_account(account, del_all)
            if self.account == account:
                self.account = None
            print 'Account deleted'
        elif arg == 'change':
            if not self.__validate_accounts():
                return False
            self.__build_change_account_menu()
        elif arg == 'list':
            self.__show_accounts()
        elif arg == 'default':
            print "Your default account is %s in %s" % (
                self.account.split('-')[0], self.account.split('-')[1])
    
    def help_account(self, desc=True):
        text = 'Manage user accounts'
        if not desc:
            text = ''
        print '\n'.join([text,
            'Usage: account <arg>\n',
            'Possible arguments are:',
            '  add:\t\t Add a new user account',
            '  edit:\t\t Edit an existing user account',
            '  delete:\t Delete a user account',
            '  list:\t\t Show all registered accounts',
            '  default:\t Show default account',
        ])
    
    def do_login(self, arg):
        if not self.__validate_accounts(): 
            return False
        
        _all = True
        if len(self.core.list_accounts()) > 1:
            _all = self.__build_confirm_menu('Do you want to login with all available accounts?')
        
        if _all:
            work = False
            for acc in self.core.list_accounts():
                if self.core.is_account_logged_in(acc):
                    continue
                work = True
                self.__process_login(acc)
            if not work:
                print "Already logged in with all available accounts"
        else:
            acc = self.__build_accounts_menu()
            self.__process_login(acc)
    
    def help_login(self):
        print 'Login with one or many accounts'
    
    def do_profile(self, arg):
        if not self.__validate_arguments(ARGUMENTS['profile'], arg): 
            self.help_profile(False)
            return False
        
        if not self.__validate_default_account(): 
            return False
        
        if arg == 'me':
            profile = self.core.get_own_profile(self.account)
            if profile is None:
                print 'You must be logged in'
            else:
                self.__show_profiles(profile)
        elif arg == 'user':
            username = raw_input('Type the username: '******'':
                print 'You must specify a username'
                return False
            profile = self.core.get_user_profile(self.account, username)
            if profile is None:
                print 'You must be logged in'
            else:
                self.__show_profiles(profile)
        elif arg == 'update':
            args = {}
            name = raw_input('Type your name (ENTER for none): ')
            bio = raw_input('Type your bio (ENTER for none): ')
            url = raw_input('Type your url (ENTER for none): ')
            location = raw_input('Type your location (ENTER for none): ')
            
            if name != '':
                args['name'] = name
            if bio != '':
                args['description'] = bio
            if url != '':
                args['url'] = url
            if location != '':
                args['location'] = location
            result = self.core.update_profile(self.account, args)
            
            if result.code > 0: 
                print result.errmsg
            else:
                print 'Profile updated'
    
    def help_profile(self, desc=True):
        text = 'Manage user profile'
        if not desc:
            text = ''
        print '\n'.join([text,
            'Usage: profile <arg>\n',
            'Possible arguments are:',
            '  me:\t\t Show own profile',
            '  user:\t\t Show profile for a specific user',
            '  update:\t Update own profile',
        ])
    
    def do_status(self, arg):
        if not self.__validate_default_account(): 
            return False
        
        if not self.__validate_arguments(ARGUMENTS['status'], arg): 
            self.help_status(False)
            return False
        
        if arg == 'update':
            message = self.__build_message_menu()
            if not message:
                print 'You must to write something'
                return False
            
            broadcast = self.__build_confirm_menu('Do you want to post the message in all available accounts?')
            if broadcast:
                for acc in self.core.list_accounts():
                    rtn = self.core.update_status(acc, message)
                    if rtn.code > 0:
                        print rtn.errmsg
                    else:
                        print 'Message posted in account %s' % acc.split('-')[0]
            else:
                rtn = self.core.update_status(self.account, message)
                if rtn.code > 0:
                    print rtn.errmsg
                else:
                    print 'Message posted in account %s' % self.account.split('-')[0]
        elif arg == 'reply':
            reply_id = raw_input('Status ID: ')
            if reply_id == '':
                print "You must specify a valid id"
                return False
            message = self.__build_message_menu()
            if not message:
                print 'You must to write something'
                return False
            rtn = self.core.update_status(self.account, message, reply_id)
            if rtn.code > 0:
                print rtn.errmsg
            else:
                print 'Reply posted in account %s' % self.account.split('-')[0]
        elif arg == 'delete':
            status_id = raw_input('Status ID: ')
            if status_id == '':
                print "You must specify a valid id"
                return False
            rtn = self.core.destroy_status(self.account, status_id)
            if rtn.code > 0:
                print rtn.errmsg
            else:
                print 'Status deleted'
        elif arg == 'conversation':
            status_id = raw_input('Status ID: ')
            if status_id == '':
                print "You must specify a valid id"
                return False
            rtn = self.core.get_conversation(self.account, status_id)
            if rtn.code > 0:
                print rtn.errmsg
            else:
                self.__show_statuses(rtn)
    
    def help_status(self, desc=True):
        text = 'Manage statuses for each protocol'
        if not desc:
            text = ''
        print '\n'.join([text,
           'Usage: status <arg>\n',
            'Possible arguments are:',
            '  update:\t Update status ',
            '  delete:\t Delete status',
            '  conversation:\t Show related tweets as conversation',
        ])
    
    def do_column(self, arg):
        if not self.__validate_default_account(): 
            return False
        
        lists = self.core.list_columns(self.account)
        if arg == '':
            self.help_column(False)
        elif arg == 'list':
            if len(lists) == 0:
                print "No column available. Maybe you need to login"
                return False
            print "Available columns:"
            for li in lists:
                print "  %s" % li
        elif arg == 'public':
            rtn = self.core.get_public_timeline(self.account)
            self.__show_statuses(rtn)
        else:
            if len(lists) == 0:
                print "No column available. Maybe you need to login"
                return False
            if arg in lists:
                rtn = self.core.get_column_statuses(self.account, arg)
                self.__show_statuses(rtn)
            else:
                print "Invalid column '%s'" % arg
    
    def help_column(self, desc=True):
        text = 'Show user columns'
        if not desc:
            text = ''
        print '\n'.join([text,
           'Usage: column <arg>\n',
            'Possible arguments are:',
            '  list:\t\t List all available columns for that account',
            '  timeline:\t Show timeline',
            '  replies:\t Show replies',
            '  directs:\t Show directs messages',
            '  favorites:\t Show statuses marked as favorites',
            '  public:\t Show public timeline',
            '  <list_id>:\t Show statuses for the user list with id <list_id>',
        ])
        
    def do_friend(self, arg):
        if not self.__validate_default_account(): 
            return False
        
        if not self.__validate_arguments(ARGUMENTS['friend'], arg): 
            self.help_friend(False)
            return False
        
        if arg == 'list':
            friends = self.core.get_friends(self.account)
            if friends.code > 0:
                print rtn.errmsg
                return False
            
            if len(friends) == 0:
                print "Hey! What's wrong with you? You've no friends"
                return False
            print "Friends list:"
            for fn in friends:
                print "+ @%s (%s)" % (fn.username, fn.fullname)
        elif arg == 'follow':
            username = raw_input('Username: '******'':
                print "You must specify a valid user"
                return False
            rtn = self.core.follow(self.account, username)
            if rtn.code > 0:
                print rtn.errmsg
                return False
            print "Following %s" % user
        elif arg == 'unfollow':
            username = raw_input('Username: '******'':
                print "You must specify a valid user"
                return False
            rtn = self.core.unfollow(self.account, username)
            if rtn.code > 0:
                print rtn.errmsg
                return False
            print "Not following %s" % user
        elif arg == 'block':
            username = raw_input('Username: '******'':
                print "You must specify a valid user"
                return False
            rtn = self.core.block(self.account, username)
            if rtn.code > 0:
                print rtn.errmsg
                return False
            print "Blocking user %s" % username
        elif arg == 'unblock':
            username = raw_input('Username: '******'':
                print "You must specify a valid user"
                return False
            rtn = self.core.unblock(self.account, username)
            if rtn.code > 0:
                print rtn.errmsg
                return False
            print "Unblocking user %s" % username
        elif arg == 'spammer':
            username = raw_input('Username: '******'':
                print "You must specify a valid user"
                return False
            rtn = self.core.report_spam(self.account, username)
            if rtn.code > 0:
                print rtn.errmsg
                return False
            print "Reporting user %s as spammer" % username
        elif arg == 'check':
            username = raw_input('Username: '******'':
                print "You must specify a valid user"
                return False
            rtn = self.core.is_friend(self.account, username)
            if rtn.code > 0:
                print rtn.errmsg
                return False
            if rtn.items:
                print "%s is following you" % username
            else:
                print "%s is not following you" % username
    
    def help_friend(self, desc=True):
        text = 'Manage user friends'
        if not desc:
            text = ''
        print '\n'.join([text,
           'Usage: friend <arg>\n',
            'Possible arguments are:',
            '  list:\t\t List all friends',
            '  follow:\t Follow user',
            '  unfollow:\t Unfollow friend',
            '  block:\t Block user',
            '  unblock:\t Unblock user',
            '  spammer:\t Report user as spammer',
            '  check:\t Verify if certain user is following you',
        ])
    
    def do_direct(self, arg):
        if not self.__validate_default_account(): 
            return False
        
        if not self.__validate_arguments(ARGUMENTS['direct'], arg): 
            self.help_direct(False)
            return False
        
        if arg == 'send':
            username = raw_input('Username: '******'':
                print "You must specify a valid user"
                return False
            message = self.__build_message_menu()
            if not message:
                print 'You must to write something'
                return False
            
            rtn = self.core.send_direct(self.account, username, message)
            if rtn.code > 0:
                print rtn.errmsg
            else:
                print 'Direct message sent'
        elif arg == 'delete':
            dm_id = raw_input('Direct message ID: ')
            if dm_id == '':
                print "You must specify a valid id"
                return False
            rtn = self.core.destroy_direct(self.account, dm_id)
            if rtn.code > 0:
                print rtn.errmsg
            else:
                print 'Direct message deleted'
    
    def help_direct(self, desc=True):
        text = 'Manage user direct messages'
        if not desc:
            text = ''
        print '\n'.join([text,
           'Usage: direct <arg>\n',
            'Possible arguments are:',
            '  send:\t\t Send direct message',
            '  delete:\t Destroy direct message',
        ])
    
    def do_favorite(self, arg):
        if not self.__validate_default_account(): 
            return False
        
        if not self.__validate_arguments(ARGUMENTS['favorite'], arg): 
            self.help_status(False)
            return False
        
        if arg == 'mark':
            status_id = raw_input('Status ID: ')
            if status_id == '':
                print "You must specify a valid id"
                return False
            rtn = self.core.mark_favorite(self.account, status_id)
            if rtn.code > 0:
                print rtn.errmsg
            else:
                print 'Status marked as favorite'
        elif arg == 'unmark':
            status_id = raw_input('Status ID: ')
            if status_id == '':
                print "You must specify a valid id"
                return False
            rtn = self.core.unmark_favorite(self.account, status_id)
            if rtn.code > 0:
                print rtn.errmsg
            else:
                print 'Status unmarked as favorite'
    
    def help_favorite(self, desc=True):
        text = 'Manage favorite marks of statuses'
        if not desc:
            text = ''
        print '\n'.join([text,
           'Usage: direct <arg>\n',
            'Possible arguments are:',
            '  mark:\t\t Mark a status as favorite',
            '  unmark:\t Remove favorite mark from a status',
        ])
    
    def do_search(self, arg=None):
        if not self.__validate_default_account(): 
            return False
        
        if arg: 
            self.help_search()
            return False
        
        query = raw_input('Type what you want to search for: ')
        rtn = self.core.search(self.account, query)
        self.__show_statuses(rtn)
    
    def help_search(self):
        print 'Search for a pattern'
    
    def do_trends(self, arg=None):
        if not self.__validate_default_account(): 
            return False
        
        if arg: 
            self.help_trends()
            return False
        
        trends = self.core.trends(self.account)
        if trends.code > 0:
            print trends.errmsg
            return False
        
        for trend in trends:
            print trend.title
            print "=" * len(trend.title)
            for topic in trend.items:
                promoted = ''
                if topic.promoted:
                    promoted = '*'
                print "%s%s |" % (topic.name, promoted),
            print
    
    def help_trends(self):
        print 'Show global and local trends'
    
    def do_EOF(self, line):
        return self.do_exit('')
        
    def do_exit(self, line=None):
        print
        self.log.debug('Bye')
        return True
    
    def help_help(self):
        print 'Show help. Dah!'
        
    def help_exit(self):
        print 'Close the application'
    
    def help_EOF(self):
        print 'Close the application'
    
    def show_shorten_url(self, text):
        print "URL Cortada:", text