X-Git-Url: https://vcs.fsf.org/?p=rainbowstream.git;a=blobdiff_plain;f=rainbowstream%2Frainbow.py;h=6464e593b8d5e413d12e38896aebe326991e62a8;hp=42bafba16671e8e7398b71bcabdd136bd4f5d167;hb=779b06408936e365b959bcf5365d3b051fa628e7;hpb=03e08f86c35352685959847ca0b233d6846dd452;ds=sidebyside diff --git a/rainbowstream/rainbow.py b/rainbowstream/rainbow.py index 42bafba..6464e59 100644 --- a/rainbowstream/rainbow.py +++ b/rainbowstream/rainbow.py @@ -1,17 +1,15 @@ """ Colorful user's timeline stream """ -from multiprocessing import Process - import os import os.path import sys import signal import argparse import time +import threading import requests import webbrowser -import json from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup from twitter.api import * @@ -24,13 +22,16 @@ from .colors import * from .config import * from .consumer import * from .interactive import * -from .db import * from .c_image import * from .py3patch import * - +# Global values g = {} -db = RainbowDB() + +# Lock for streams +StreamLock = threading.Lock() + +# Commands cmdset = [ 'switch', 'trend', @@ -64,8 +65,11 @@ cmdset = [ 'report', 'list', 'cal', + 'config', 'theme', 'h', + 'p', + 'r', 'c', 'q' ] @@ -133,19 +137,65 @@ def authen(): CONSUMER_SECRET) -def get_decorated_name(): +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): """ - Beginning of every line + Init function """ + # Handle Ctrl C + ctrl_c_handler = lambda signum, frame: quit() + signal.signal(signal.SIGINT, ctrl_c_handler) + # Get name t = Twitter(auth=authen()) name = '@' + t.account.verify_credentials()['screen_name'] + if not get_config('PREFIX'): + set_config('PREFIX', name) g['original_name'] = name[1:] - g['decorated_name'] = color_func(c['DECORATED_NAME'])('[' + name + ']: ') - + g['decorated_name'] = lambda x: color_func( + 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 - db.theme_store(c['THEME']) + # Startup cmd + g['previous_cmd'] = '' + # Semaphore init + c['lock'] = False + c['pause'] = False + # Init tweet dict and message dict + c['tweet_dict'] = [] + 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(): @@ -154,13 +204,14 @@ def switch(): """ try: target = g['stuff'].split()[0] - # Filter and ignore args = parse_arguments() try: if g['stuff'].split()[-1] == '-f': - only = raw_input('Only nicks: ') - ignore = raw_input('Ignore nicks: ') + guide = 'To ignore an option, just hit Enter key.' + printNicely(light_magenta(guide)) + only = raw_input('Only nicks [Ex: @xxx,@yy]: ') + ignore = raw_input('Ignore nicks [Ex: @xxx,@yy]: ') args.filter = filter(None, only.split(',')) args.ignore = filter(None, ignore.split(',')) elif g['stuff'].split()[-1] == '-d': @@ -169,37 +220,35 @@ def switch(): except: printNicely(red('Sorry, wrong format.')) return - # Public stream if target == 'public': keyword = g['stuff'].split()[1] if keyword[0] == '#': keyword = keyword[1:] - # Kill old process - os.kill(g['stream_pid'], signal.SIGKILL) + # Kill old thread + g['stream_stop'] = True args.track_keywords = keyword - # Start new process - p = Process( + # Start new thread + th = threading.Thread( target=stream, args=( c['PUBLIC_DOMAIN'], args)) - p.start() - g['stream_pid'] = p.pid - + th.daemon = True + th.start() # Personal stream elif target == 'mine': - # Kill old process - os.kill(g['stream_pid'], signal.SIGKILL) - # Start new process - p = Process( + # Kill old thread + g['stream_stop'] = True + # Start new thread + th = threading.Thread( target=stream, args=( c['USER_DOMAIN'], args, g['original_name'])) - p.start() - g['stream_pid'] = p.pid + th.daemon = True + th.start() printNicely('') if args.filter: printNicely(cyan('Only: ' + str(args.filter))) @@ -224,7 +273,6 @@ def trend(): town = g['stuff'].split()[1] except: town = '' - avail = t.trends.available() # World wide if not country: @@ -256,7 +304,7 @@ def home(): if g['stuff'].isdigit(): num = int(g['stuff']) for tweet in reversed(t.statuses.home_timeline(count=num)): - draw(t=tweet, iot=g['iot']) + draw(t=tweet) printNicely('') @@ -272,7 +320,7 @@ def view(): except: num = c['HOME_TWEET_NUM'] for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])): - draw(t=tweet, iot=g['iot']) + draw(t=tweet) printNicely('') else: printNicely(red('A name should begin with a \'@\'')) @@ -287,7 +335,7 @@ def mentions(): if g['stuff'].isdigit(): num = int(g['stuff']) for tweet in reversed(t.statuses.mentions_timeline(count=num)): - draw(t=tweet, iot=g['iot']) + draw(t=tweet) printNicely('') @@ -309,7 +357,7 @@ def retweet(): except: printNicely(red('Sorry I can\'t understand.')) return - tid = db.rainbow_to_tweet_query(id)[0].tweet_id + tid = c['tweet_dict'][id] t.statuses.retweet(id=tid, include_entities=False, trim_user=True) @@ -323,7 +371,7 @@ def quote(): except: printNicely(red('Sorry I can\'t understand.')) return - tid = db.rainbow_to_tweet_query(id)[0].tweet_id + tid = c['tweet_dict'][id] tweet = t.statuses.show(id=tid) screen_name = tweet['user']['screen_name'] text = tweet['text'] @@ -351,7 +399,7 @@ def allretweet(): except: printNicely(red('Sorry I can\'t understand.')) return - tid = db.rainbow_to_tweet_query(id)[0].tweet_id + tid = c['tweet_dict'][id] # Get display num if exist try: num = int(g['stuff'].split()[1]) @@ -363,7 +411,7 @@ def allretweet(): printNicely(magenta('This tweet has no retweet.')) return for tweet in reversed(rt_ary): - draw(t=tweet, iot=g['iot']) + draw(t=tweet) printNicely('') @@ -377,10 +425,10 @@ def favorite(): except: printNicely(red('Sorry I can\'t understand.')) return - tid = db.rainbow_to_tweet_query(id)[0].tweet_id + tid = c['tweet_dict'][id] t.favorites.create(_id=tid, include_entities=False) printNicely(green('Favorited.')) - draw(t.statuses.show(id=tid), iot=g['iot']) + draw(t.statuses.show(id=tid)) printNicely('') @@ -394,10 +442,10 @@ def reply(): except: printNicely(red('Sorry I can\'t understand.')) return - tid = db.rainbow_to_tweet_query(id)[0].tweet_id + 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 + ' ' + unc(status) t.statuses.update(status=status, in_reply_to_status_id=tid) @@ -407,11 +455,11 @@ def delete(): """ t = Twitter(auth=authen()) try: - rid = int(g['stuff'].split()[0]) + id = int(g['stuff'].split()[0]) except: printNicely(red('Sorry I can\'t understand.')) return - tid = db.rainbow_to_tweet_query(rid)[0].tweet_id + tid = c['tweet_dict'][id] t.statuses.destroy(id=tid) printNicely(green('Okay it\'s gone.')) @@ -426,10 +474,10 @@ def unfavorite(): except: printNicely(red('Sorry I can\'t understand.')) return - tid = db.rainbow_to_tweet_query(id)[0].tweet_id + tid = c['tweet_dict'][id] t.favorites.destroy(_id=tid) printNicely(green('Okay it\'s unfavorited.')) - draw(t.statuses.show(id=tid), iot=g['iot']) + draw(t.statuses.show(id=tid)) printNicely('') @@ -444,7 +492,6 @@ def search(): printNicely('Newest tweets:') for i in reversed(xrange(c['SEARCH_MAX_RECORD'])): draw(t=rel[i], - iot=g['iot'], keyword=g['stuff']) printNicely('') else: @@ -481,7 +528,7 @@ def show(): if target != 'image': return id = int(g['stuff'].split()[1]) - tid = db.rainbow_to_tweet_query(id)[0].tweet_id + tid = c['tweet_dict'][id] tweet = t.statuses.show(id=tid) media = tweet['entities']['media'] for m in media: @@ -500,7 +547,7 @@ def urlopen(): try: if not g['stuff'].isdigit(): return - tid = db.rainbow_to_tweet_query(g['stuff'])[0].tweet_id + tid = c['tweet_dict'][int(g['stuff'])] tweet = t.statuses.show(id=tid) link_ary = [ u for u in tweet['text'].split() if u.startswith('http://')] @@ -628,10 +675,10 @@ def trash(): """ t = Twitter(auth=authen()) try: - rid = int(g['stuff'].split()[0]) + id = int(g['stuff'].split()[0]) except: printNicely(red('Sorry I can\'t understand.')) - mid = db.rainbow_to_message_query(rid)[0].message_id + mid = c['message_dict'][id] t.direct_messages.destroy(id=mid) printNicely(green('Message deleted.')) @@ -647,7 +694,7 @@ def whois(): user = t.users.show( screen_name=screen_name[1:], include_entities=False) - show_profile(user, g['iot']) + show_profile(user) except: printNicely(red('Omg no user.')) else: @@ -693,11 +740,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 \'@\'')) @@ -713,11 +765,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 \'@\'')) @@ -726,27 +782,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(): @@ -799,7 +843,7 @@ def get_slug(): """ Get Slug Decorator """ - # Get list name + # Get list name list_name = raw_input(light_magenta('Give me the list\'s name: ')) # Get list name and owner try: @@ -808,7 +852,8 @@ def get_slug(): slug = slug[1:] return owner, slug except: - printNicely(light_magenta('List name should follow "@owner/list_name" format.')) + printNicely( + light_magenta('List name should follow "@owner/list_name" format.')) raise Exception('Wrong list name') @@ -900,7 +945,7 @@ def list_add(t): slug=slug, owner_screen_name=owner, screen_name=user_name) - printNicely(light_green('Added.')) + printNicely(green('Added.')) except: printNicely(light_magenta('I\'m sorry we can not add him/her.')) @@ -919,7 +964,7 @@ def list_remove(t): slug=slug, owner_screen_name=owner, screen_name=user_name) - printNicely(light_green('Gone.')) + printNicely(green('Gone.')) except: printNicely(light_magenta('I\'m sorry we can not remove him/her.')) @@ -934,7 +979,7 @@ def list_subscribe(t): t.lists.subscribers.create( slug=slug, owner_screen_name=owner) - printNicely(light_green('Done.')) + printNicely(green('Done.')) except: printNicely( light_magenta('I\'m sorry you can not subscribe to this list.')) @@ -950,7 +995,7 @@ def list_unsubscribe(t): t.lists.subscribers.destroy( slug=slug, owner_screen_name=owner) - printNicely(light_green('Done.')) + printNicely(green('Done.')) except: printNicely( light_magenta('I\'m sorry you can not unsubscribe to this list.')) @@ -986,7 +1031,7 @@ def list_new(t): name=name, mode=mode, description=description) - printNicely(light_green(name + ' list is created.')) + printNicely(green(name + ' list is created.')) except: printNicely(red('Oops something is wrong with Twitter :(')) @@ -1013,7 +1058,7 @@ def list_update(t): owner_screen_name=g['original_name'], mode=mode, description=description) - printNicely(light_green(slug + ' list is updated.')) + printNicely(green(slug + ' list is updated.')) except: printNicely(red('Oops something is wrong with Twitter :(')) @@ -1027,12 +1072,12 @@ def list_delete(t): t.lists.destroy( slug='-'.join(slug.split()), owner_screen_name=g['original_name']) - printNicely(light_green(slug + ' list is deleted.')) + printNicely(green(slug + ' list is deleted.')) except: printNicely(red('Oops something is wrong with Twitter :(')) -def list(): +def twitterlist(): """ Twitter's list """ @@ -1074,6 +1119,70 @@ def cal(): show_calendar(month, date, rel) +def config(): + """ + Browse and change config + """ + all_config = get_all_config() + g['stuff'] = g['stuff'].strip() + # List all config + if not g['stuff']: + for k in all_config: + line = ' ' * 2 + \ + green(k) + ': ' + light_yellow(str(all_config[k])) + printNicely(line) + guide = 'Detailed explanation can be found at ' + \ + color_func(c['TWEET']['link'])( + 'http://rainbowstream.readthedocs.org/en/latest/#config-explanation') + printNicely(guide) + # Print specific config + elif len(g['stuff'].split()) == 1: + if g['stuff'] in all_config: + k = g['stuff'] + line = ' ' * 2 + \ + green(k) + ': ' + light_yellow(str(all_config[k])) + printNicely(line) + else: + printNicely(red('No such config key.')) + # Print specific config's default value + elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'default': + key = g['stuff'].split()[0] + try: + value = get_default_config(key) + line = ' ' * 2 + green(key) + ': ' + light_magenta(value) + printNicely(line) + 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 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] + value = g['stuff'].split()[-1] + if key == 'THEME' and not validate_theme(value): + printNicely(red('Invalid theme\'s value.')) + return + try: + set_config(key, value) + # Apply theme immediately + if key == 'THEME': + c['THEME'] = reload_theme(value, c['THEME']) + g['decorated_name'] = lambda x: color_func( + c['DECORATED_NAME'])('[' + x + ']: ') + reload_config() + printNicely(green('Updated successfully.')) + except Exception as e: + printNicely(red(e)) + else: + printNicely(light_magenta('Sorry I can\'s understand.')) + + def theme(): """ List and change theme @@ -1087,50 +1196,15 @@ def theme(): else: line = ' ' * 4 + line printNicely(line) - elif g['stuff'] == 'current_as_default': - # Set as default - def fixup(adict, k, v): - for key in adict.keys(): - if key == k: - adict[key] = v - elif type(adict[key]) is dict: - fixup(adict[key], k, v) - # Modify - path = os.environ.get( - 'HOME', - os.environ.get( - 'USERPROFILE', - '')) + os.sep + '.rainbow_config.json' - data = load_config(rainbow_config) - fixup(data, 'THEME', c['THEME']) - # Save - with open(path, 'w') as out: - json.dump(data, out, indent = 4) - os.system('chmod 777 ' + path) - printNicely(light_green('Okay it will be applied from next time :)')) else: # Change theme try: - # Load new config - if g['stuff'] != 'custom': - new_config = os.path.dirname( - __file__) + '/colorset/' + g['stuff'] + '.json' - else: - new_config = os.environ.get( - 'HOME', os.environ.get( - 'USERPROFILE', - '')) + os.sep + '.rainbow_config.json' - new_config = load_config(new_config) - if new_config: - for nc in new_config: - c[nc] = new_config[nc] - # Update db and reset colors - db.theme_update(g['stuff']) - c['THEME'] = g['stuff'] - start_cycle() - g['decorated_name'] = color_func( + # Load new theme + c['THEME'] = reload_theme(g['stuff'], c['THEME']) + # Redefine decorated_name + g['decorated_name'] = lambda x: color_func( c['DECORATED_NAME'])( - '[@' + g['original_name'] + ']: ') + '[' + x + ']: ') printNicely(green('Theme changed.')) except: printNicely(red('No such theme exists.')) @@ -1318,7 +1392,6 @@ def help(): """ s = ' ' * 2 h, w = os.popen('stty size', 'r').read().split() - # Start usage = '\n' usage += s + 'Hi boss! I\'m ready to serve you right now!\n' @@ -1328,7 +1401,6 @@ def help(): usage += s + 'Any update from Twitter will show up ' + \ light_yellow('immediately') + '.\n' usage += s + 'In addtion, following commands are available right now:\n' - # Twitter help section usage += '\n' usage += s + grey(u'\u266A' + ' Twitter help\n') @@ -1344,7 +1416,6 @@ def help(): ' will show help for list commands.\n' usage += s * 2 + light_green('h stream') + \ ' will show help for stream commands.\n' - # Smart shell usage += '\n' usage += s + grey(u'\u266A' + ' Smart shell\n') @@ -1352,22 +1423,38 @@ def help(): 'will be evaluate by Python interpreter.\n' usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \ ' for current month.\n' - - # Screening + # Config usage += '\n' - usage += s + grey(u'\u266A' + ' Screening \n') - usage += s * 2 + light_green('theme') + ' will list available theme.' + \ + usage += s + grey(u'\u266A' + ' Config \n') + usage += s * 2 + light_green('theme') + ' will list available theme. ' + \ light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \ ' theme immediately.\n' + usage += s * 2 + light_green('config') + ' will list all config.\n' + usage += s * 3 + \ + light_green('config ASCII_ART') + ' will output current value of ' +\ + light_yellow('ASCII_ART') + ' config key.\n' + usage += s * 3 + \ + light_green('config TREND_MAX default') + ' will output default value of ' + \ + light_yellow('TREND_MAX') + ' config key.\n' + usage += s * 3 + \ + light_green('config CUSTOM_CONFIG drop') + ' will drop ' + \ + light_yellow('CUSTOM_CONFIG') + ' config key.\n' + usage += s * 3 + \ + light_green('config IMAGE_ON_TERM = true') + ' will set value of ' + \ + light_yellow('IMAGE_ON_TERM') + ' config key to ' + \ + light_yellow('True') + '.\n' + # Screening + usage += '\n' + usage += s + grey(u'\u266A' + ' Screening \n') usage += s * 2 + light_green('h') + ' will show this help again.\n' + usage += s * 2 + light_green('p') + ' will pause the stream.\n' + usage += s * 2 + light_green('r') + ' will unpause the stream.\n' usage += s * 2 + light_green('c') + ' will clear the screen.\n' usage += s * 2 + light_green('q') + ' will quit.\n' - # End usage += '\n' usage += s + '-' * (int(w) - 4) + '\n' usage += s + 'Have fun and hang tight! \n' - # Show help d = { 'discover': help_discover, @@ -1378,11 +1465,30 @@ 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) +def pause(): + """ + Pause stream display + """ + c['pause'] = True + printNicely(green('Stream is paused')) + + +def replay(): + """ + Replay stream + """ + c['pause'] = False + printNicely(green('Stream is running back now')) + + def clear(): """ Clear screen @@ -1394,9 +1500,11 @@ def quit(): """ Exit all """ - save_history() - os.system('rm -rf rainbow.db') - os.kill(g['stream_pid'], signal.SIGKILL) + try: + save_history() + printNicely(green('See you next time :)')) + except: + pass sys.exit() @@ -1405,6 +1513,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: @@ -1450,10 +1562,13 @@ def process(cmd): block, unblock, report, - list, + twitterlist, cal, + config, theme, help, + pause, + replay, clear, quit ] @@ -1511,7 +1626,8 @@ def listen(): 'del' ], # list [], # cal - g['themes'] + ['current_as_default'], # theme + [key for key in dict(get_all_config())], # config + g['themes'], # theme [ 'discover', 'tweets', @@ -1520,6 +1636,8 @@ def listen(): 'list', 'stream' ], # help + [], # pause + [], # reconnect [], # clear [], # quit ] @@ -1528,33 +1646,42 @@ def listen(): read_history() reset() while True: + # raw_input if g['prefix']: - line = raw_input(g['decorated_name']) + line = raw_input(g['decorated_name'](c['PREFIX'])) else: line = raw_input() + # Save previous cmd in order to compare with readline buffer + g['previous_cmd'] = line.strip() + # Get short cmd to filter try: cmd = line.split()[0] except: cmd = '' - g['cmd'] = cmd - # Save cmd to global variable and call process + # Save whold the entire cmd + g['cmd'] = line try: + # Lock the semaphore + c['lock'] = True + # Save cmd to global variable and call process g['stuff'] = ' '.join(line.split()[1:]) + # Process the command process(cmd)() + # Not re-display + if cmd in ['switch', 't', 'rt', 'rep']: + g['prefix'] = False + else: + g['prefix'] = True + # Release the semaphore lock + c['lock'] = False except Exception: printNicely(red('OMG something is wrong with Twitter right now.')) - # Not redisplay prefix - if cmd in ['switch', 't', 'rt', 'rep']: - g['prefix'] = False - else: - g['prefix'] = True def stream(domain, args, name='Rainbow Stream'): """ Track the stream """ - # The Logo art_dict = { c['USER_DOMAIN']: name, @@ -1563,24 +1690,20 @@ def stream(domain, args, name='Rainbow Stream'): } if c['ASCII_ART']: ascii_art(art_dict[domain]) - # These arguments are optional: stream_args = dict( - timeout=args.timeout, + timeout=0.5, # To check g['stream_stop'] after each 0.5 s block=not args.no_block, heartbeat_timeout=args.heartbeat_timeout) - # Track keyword query_args = dict() if args.track_keywords: query_args['track'] = args.track_keywords - # Get stream stream = TwitterStream( auth=authen(), domain=domain, **stream_args) - try: if domain == c['USER_DOMAIN']: tweet_iter = stream.user(**query_args) @@ -1591,13 +1714,16 @@ def stream(domain, args, name='Rainbow Stream'): tweet_iter = stream.statuses.filter(**query_args) else: tweet_iter = stream.statuses.sample() - - # Iterate over the stream. + # Block new stream until other one exits + StreamLock.acquire() + g['stream_stop'] = False for tweet in tweet_iter: if tweet is None: printNicely("-- None --") elif tweet is Timeout: - printNicely("-- Timeout --") + if(g['stream_stop']): + StreamLock.release() + break elif tweet is HeartbeatTimeout: printNicely("-- Heartbeat Timeout --") elif tweet is Hangup: @@ -1605,11 +1731,26 @@ def stream(domain, args, name='Rainbow Stream'): elif tweet.get('text'): draw( t=tweet, - iot=args.image_on_term, keyword=args.track_keywords, + check_semaphore=True, 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['previous_cmd'] != current_buffer: + sys.stdout.write( + g['decorated_name'](c['PREFIX']) + unc(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: printNicely('') printNicely( @@ -1620,33 +1761,28 @@ def fly(): """ Main function """ - # Spawn stream process + # Initial args = parse_arguments() try: - get_decorated_name() - + init(args) except TwitterHTTPError: printNicely('') printNicely( - magenta("Something wrong with Twitter Oauth right now :(")) - printNicely( - magenta("Please delete ~/.rainbow_oauth and try again.")) + magenta("We have connection problem with twitter'stream API right now :(")) + printNicely(magenta("Let's try again later.")) save_history() - os.system('rm -rf rainbow.db') sys.exit() - - p = Process( + # Spawn stream thread + th = threading.Thread( target=stream, args=( c['USER_DOMAIN'], args, g['original_name'])) - p.start() - + th.daemon = True + th.start() # Start listen process time.sleep(0.5) g['reset'] = True g['prefix'] = True - g['stream_pid'] = p.pid - g['iot'] = args.image_on_term listen()