X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=rainbowstream%2Frainbow.py;h=dfbfb3b7ac3d559e7ad5a76bc92e88a1504c4197;hb=37d1047f8c5e064eba4ef1cbae46d735568f0a9b;hp=14149d9e1ec3c8c586f74f9e5e618dbdcbd304ae;hpb=8101275ee2ac9e3c315643650c929a1ac227a1ea;p=rainbowstream.git diff --git a/rainbowstream/rainbow.py b/rainbowstream/rainbow.py index 14149d9..dfbfb3b 100644 --- a/rainbowstream/rainbow.py +++ b/rainbowstream/rainbow.py @@ -31,49 +31,6 @@ g = {} # Lock for streams StreamLock = threading.Lock() -# Commands -cmdset = [ - 'switch', - 'trend', - 'home', - 'view', - 'mentions', - 't', - 'rt', - 'quote', - 'allrt', - 'fav', - 'rep', - 'del', - 'ufav', - 's', - 'mes', - 'show', - 'open', - 'ls', - 'inbox', - 'sent', - 'trash', - 'whois', - 'fl', - 'ufl', - 'mute', - 'unmute', - 'muting', - 'block', - 'unblock', - 'report', - 'list', - 'cal', - 'config', - 'theme', - 'h', - 'p', - 'r', - 'c', - 'q' -] - def parse_arguments(): """ @@ -84,16 +41,6 @@ def parse_arguments(): '-to', '--timeout', help='Timeout for the stream (seconds).') - parser.add_argument( - '-ht', - '--heartbeat-timeout', - help='Set heartbeat timeout.', - default=90) - parser.add_argument( - '-nb', - '--no-block', - action='store_true', - help='Set stream to non-blocking.') parser.add_argument( '-tt', '--track-keywords', @@ -137,6 +84,33 @@ def authen(): CONSUMER_SECRET) +def build_mute_dict(dict_data=False): + """ + Build muting list + """ + t = Twitter(auth=authen()) + # Init cursor + next_cursor = -1 + screen_name_list = [] + name_list = [] + # Cursor loop + while next_cursor != 0: + list = t.mutes.users.list( + screen_name=g['original_name'], + cursor=next_cursor, + skip_status=True, + include_entities=False, + ) + screen_name_list += ['@' + u['screen_name'] for u in list['users']] + name_list += [u['name'] for u in list['users']] + next_cursor = list['next_cursor'] + # Return dict or list + if dict_data: + return dict(zip(screen_name_list, name_list)) + else: + return screen_name_list + + def init(args): """ Init function @@ -151,12 +125,13 @@ def init(args): set_config('PREFIX', name) g['original_name'] = name[1:] g['decorated_name'] = lambda x: color_func( - c['DECORATED_NAME'])( - '[' + x + ']: ') + c['DECORATED_NAME'])('[' + x + ']: ') # Theme init files = os.listdir(os.path.dirname(__file__) + '/colorset') themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json'] g['themes'] = themes + # Startup cmd + g['cmd'] = '' # Semaphore init c['lock'] = False c['pause'] = False @@ -165,6 +140,9 @@ def init(args): c['message_dict'] = [] # Image on term c['IMAGE_ON_TERM'] = args.image_on_term + set_config('IMAGE_ON_TERM', str(c['IMAGE_ON_TERM'])) + # Mute dict + c['IGNORE_LIST'] += build_mute_dict() def switch(): @@ -198,7 +176,11 @@ def switch(): g['stream_stop'] = True args.track_keywords = keyword # Start new thread - th = threading.Thread(target=stream, args=(c['PUBLIC_DOMAIN'], args)) + th = threading.Thread( + target=stream, + args=( + c['PUBLIC_DOMAIN'], + args)) th.daemon = True th.start() # Personal stream @@ -206,7 +188,12 @@ def switch(): # Kill old thread g['stream_stop'] = True # Start new thread - th = threading.Thread(target=stream, args=(c['USER_DOMAIN'], args, g['original_name'])) + th = threading.Thread( + target=stream, + args=( + c['USER_DOMAIN'], + args, + g['original_name'])) th.daemon = True th.start() printNicely('') @@ -325,6 +312,7 @@ def quote(): """ Quote a tweet """ + # Get tweet t = Twitter(auth=authen()) try: id = int(g['stuff'].split()[0]) @@ -333,17 +321,16 @@ def quote(): return tid = c['tweet_dict'][id] tweet = t.statuses.show(id=tid) - screen_name = tweet['user']['screen_name'] - text = tweet['text'] - quote = '\"@' + screen_name + ': ' + text + '\"' - quote = quote.encode('utf8') - notice = light_magenta('Compose mode ') - notice += light_yellow('(Enter nothing will cancel the quote)') - notice += light_magenta(':') - printNicely(notice) - extra = raw_input(quote) - if extra: - t.statuses.update(status=quote + extra) + # Get formater + formater = format_quote(tweet) + if not formater: + return + # Get comment + prefix = light_magenta('Compose your ') + light_green('#comment: ') + comment = raw_input(prefix) + if comment: + quote = comment.join(formater.split('#comment')) + t.statuses.update(status=quote) else: printNicely(light_magenta('No text added.')) @@ -405,7 +392,7 @@ def reply(): tid = c['tweet_dict'][id] user = t.statuses.show(id=tid)['user']['screen_name'] status = ' '.join(g['stuff'].split()[1:]) - status = '@' + user + ' ' + status.decode('utf-8') + status = '@' + user + ' ' + str2u(status) t.statuses.update(status=status, in_reply_to_status_id=tid) @@ -700,11 +687,16 @@ def mute(): printNicely(red('A name should be specified. ')) return if screen_name.startswith('@'): - rel = t.mutes.users.create(screen_name=screen_name[1:]) - if isinstance(rel, dict): - printNicely(green(screen_name + ' is muted.')) - else: - printNicely(red(rel)) + try: + rel = t.mutes.users.create(screen_name=screen_name[1:]) + if isinstance(rel, dict): + printNicely(green(screen_name + ' is muted.')) + c['IGNORE_LIST'] += [unc(screen_name)] + c['IGNORE_LIST'] = list(set(c['IGNORE_LIST'])) + else: + printNicely(red(rel)) + except: + printNicely(red('Something is wrong, can not mute now :(')) else: printNicely(red('A name should begin with a \'@\'')) @@ -720,11 +712,15 @@ def unmute(): printNicely(red('A name should be specified. ')) return if screen_name.startswith('@'): - rel = t.mutes.users.destroy(screen_name=screen_name[1:]) - if isinstance(rel, dict): - printNicely(green(screen_name + ' is unmuted.')) - else: - printNicely(red(rel)) + try: + rel = t.mutes.users.destroy(screen_name=screen_name[1:]) + if isinstance(rel, dict): + printNicely(green(screen_name + ' is unmuted.')) + c['IGNORE_LIST'].remove(screen_name) + else: + printNicely(red(rel)) + except: + printNicely(red('Maybe you are not muting this person ?')) else: printNicely(red('A name should begin with a \'@\'')) @@ -733,27 +729,15 @@ def muting(): """ List muting user """ - t = Twitter(auth=authen()) - # Init cursor - next_cursor = -1 - rel = {} - # Cursor loop - while next_cursor != 0: - list = t.mutes.users.list( - screen_name=g['original_name'], - cursor=next_cursor, - skip_status=True, - include_entities=False, - ) - for u in list['users']: - rel[u['name']] = '@' + u['screen_name'] - next_cursor = list['next_cursor'] - # Print out result - printNicely('All: ' + str(len(rel)) + ' people.') - for name in rel: - user = ' ' + cycle_color(name) - user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ') + # Get dict of muting users + md = build_mute_dict(dict_data=True) + printNicely('All: ' + str(len(md)) + ' people.') + for name in md: + user = ' ' + cycle_color(md[name]) + user += color_func(c['TWEET']['nick'])(' ' + name + ' ') printNicely(user) + # Update from Twitter + c['IGNORE_LIST'] = [n for n in md] def block(): @@ -1040,7 +1024,7 @@ def list_delete(t): printNicely(red('Oops something is wrong with Twitter :(')) -def list(): +def twitterlist(): """ Twitter's list """ @@ -1114,17 +1098,16 @@ def config(): value = get_default_config(key) line = ' ' * 2 + green(key) + ': ' + light_magenta(value) printNicely(line) - except: - printNicely( - light_magenta('This config key does not exist in default.')) + except Exception as e: + printNicely(red(e)) # Delete specific config key in config file elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'drop': key = g['stuff'].split()[0] try: delete_config(key) printNicely(green('Config key is dropped.')) - except: - printNicely(red('No such config key.')) + except Exception as e: + printNicely(red(e)) # Set specific config elif len(g['stuff'].split()) == 3 and g['stuff'].split()[1] == '=': key = g['stuff'].split()[0] @@ -1136,15 +1119,13 @@ def config(): set_config(key, value) # Apply theme immediately if key == 'THEME': - c['THEME'] = reload_theme(value,c['THEME']) + c['THEME'] = reload_theme(value, c['THEME']) g['decorated_name'] = lambda x: color_func( - c['DECORATED_NAME'])( - '[' + x + ']: ') + c['DECORATED_NAME'])('[' + x + ']: ') + reload_config() printNicely(green('Updated successfully.')) - except: - printNicely(light_magenta('Not valid value.')) - return - reload_config() + except Exception as e: + printNicely(red(e)) else: printNicely(light_magenta('Sorry I can\'s understand.')) @@ -1166,7 +1147,7 @@ def theme(): # Change theme try: # Load new theme - c['THEME'] = reload_theme(g['stuff'],c['THEME']) + c['THEME'] = reload_theme(g['stuff'], c['THEME']) # Redefine decorated_name g['decorated_name'] = lambda x: color_func( c['DECORATED_NAME'])( @@ -1366,7 +1347,7 @@ def help(): light_yellow('already') + ' on your personal stream.\n' usage += s + 'Any update from Twitter will show up ' + \ light_yellow('immediately') + '.\n' - usage += s + 'In addtion, following commands are available right now:\n' + usage += s + 'In addition, following commands are available right now:\n' # Twitter help section usage += '\n' usage += s + grey(u'\u266A' + ' Twitter help\n') @@ -1431,7 +1412,10 @@ def help(): 'stream': help_stream, } if g['stuff']: - d[g['stuff'].strip()]() + d.get( + g['stuff'].strip(), + lambda: printNicely(red('No such command.')) + )() else: printNicely(usage) @@ -1476,6 +1460,10 @@ def reset(): Reset prefix of line """ if g['reset']: + if c.get('USER_JSON_ERROR'): + printNicely(red('Your ~/.rainbow_config.json is messed up:')) + printNicely(red('>>> ' + c['USER_JSON_ERROR'])) + printNicely('') printNicely(magenta('Need tips ? Type "h" and hit Enter key!')) g['reset'] = False try: @@ -1484,54 +1472,98 @@ def reset(): pass +# Command set +cmdset = [ + 'switch', + 'trend', + 'home', + 'view', + 'mentions', + 't', + 'rt', + 'quote', + 'allrt', + 'fav', + 'rep', + 'del', + 'ufav', + 's', + 'mes', + 'show', + 'open', + 'ls', + 'inbox', + 'sent', + 'trash', + 'whois', + 'fl', + 'ufl', + 'mute', + 'unmute', + 'muting', + 'block', + 'unblock', + 'report', + 'list', + 'cal', + 'config', + 'theme', + 'h', + 'p', + 'r', + 'c', + 'q' +] + +# Handle function set +funcset = [ + switch, + trend, + home, + view, + mentions, + tweet, + retweet, + quote, + allretweet, + favorite, + reply, + delete, + unfavorite, + search, + message, + show, + urlopen, + ls, + inbox, + sent, + trash, + whois, + follow, + unfollow, + mute, + unmute, + muting, + block, + unblock, + report, + twitterlist, + cal, + config, + theme, + help, + pause, + replay, + clear, + quit +] + + def process(cmd): """ Process switch """ - return dict(zip( - cmdset, - [ - switch, - trend, - home, - view, - mentions, - tweet, - retweet, - quote, - allretweet, - favorite, - reply, - delete, - unfavorite, - search, - message, - show, - urlopen, - ls, - inbox, - sent, - trash, - whois, - follow, - unfollow, - mute, - unmute, - muting, - block, - unblock, - report, - list, - cal, - config, - theme, - help, - pause, - replay, - clear, - quit - ] - )).get(cmd, reset) + return dict(zip(cmdset, funcset)).get(cmd, reset) def listen(): @@ -1610,11 +1642,13 @@ def listen(): line = raw_input(g['decorated_name'](c['PREFIX'])) else: line = raw_input() + # Save cmd to compare with readline buffer + g['cmd'] = line.strip() + # Get short cmd to pass to handle function try: cmd = line.split()[0] except: cmd = '' - g['cmd'] = cmd try: # Lock the semaphore c['lock'] = True @@ -1647,9 +1681,9 @@ def stream(domain, args, name='Rainbow Stream'): ascii_art(art_dict[domain]) # These arguments are optional: stream_args = dict( - timeout=args.timeout, - block=False, - heartbeat_timeout=args.heartbeat_timeout) + timeout=0.5, # To check g['stream_stop'] after each 0.5 s + block=True, + heartbeat_timeout=c['HEARTBEAT_TIMEOUT'] * 60) # Track keyword query_args = dict() if args.track_keywords: @@ -1673,15 +1707,25 @@ def stream(domain, args, name='Rainbow Stream'): StreamLock.acquire() g['stream_stop'] = False for tweet in tweet_iter: - if(g['stream_stop']): - StreamLock.release() - break if tweet is None: - pass + printNicely("-- None --") elif tweet is Timeout: - printNicely("-- Timeout --") + if(g['stream_stop']): + StreamLock.release() + break elif tweet is HeartbeatTimeout: printNicely("-- Heartbeat Timeout --") + guide = light_magenta("You can use ") + \ + light_green("switch") + \ + light_magenta(" command to return to your stream.\n") + guide += light_magenta("Type ") + \ + light_green("h stream") + \ + light_magenta(" for more details.") + printNicely(guide) + sys.stdout.write(g['decorated_name'](c['PREFIX'])) + sys.stdout.flush() + StreamLock.release() + break elif tweet is Hangup: printNicely("-- Hangup --") elif tweet.get('text'): @@ -1692,6 +1736,19 @@ def stream(domain, args, name='Rainbow Stream'): fil=args.filter, ig=args.ignore, ) + # Current readline buffer + current_buffer = readline.get_line_buffer().strip() + # There is an unexpected behaviour in MacOSX readline + Python 2: + # after completely delete a word after typing it, + # somehow readline buffer still contains + # the 1st character of that word + if current_buffer and g['cmd'] != current_buffer: + sys.stdout.write( + g['decorated_name'](c['PREFIX']) + str2u(current_buffer)) + sys.stdout.flush() + elif not c['HIDE_PROMPT']: + sys.stdout.write(g['decorated_name'](c['PREFIX'])) + sys.stdout.flush() elif tweet.get('direct_message'): print_message(tweet['direct_message'], check_semaphore=True) except TwitterHTTPError: @@ -1711,12 +1768,17 @@ def fly(): except TwitterHTTPError: printNicely('') printNicely( - magenta("We have maximum connection problem with twitter'stream API right now :(")) + magenta("We have connection problem with twitter'stream API right now :(")) printNicely(magenta("Let's try again later.")) save_history() sys.exit() # Spawn stream thread - th = threading.Thread(target=stream, args=(c['USER_DOMAIN'], args, g['original_name'])) + th = threading.Thread( + target=stream, + args=( + c['USER_DOMAIN'], + args, + g['original_name'])) th.daemon = True th.start() # Start listen process