X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=rainbowstream%2Frainbow.py;h=14149d9e1ec3c8c586f74f9e5e618dbdcbd304ae;hb=46c9fb816604f4c604e24a923770f8bf12e0adac;hp=08fee9e22d685e85f3a4fb7fe787b60546bf5d84;hpb=fe9bb33b8bcedf485523d9f5979073775daa8ec5;p=rainbowstream.git diff --git a/rainbowstream/rainbow.py b/rainbowstream/rainbow.py index 08fee9e..14149d9 100644 --- a/rainbowstream/rainbow.py +++ b/rainbowstream/rainbow.py @@ -1,14 +1,13 @@ """ 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 @@ -23,15 +22,14 @@ 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 = {} -# Database -db = RainbowDB() +# Lock for streams +StreamLock = threading.Lock() # Commands cmdset = [ @@ -70,6 +68,8 @@ cmdset = [ 'config', 'theme', 'h', + 'p', + 'r', 'c', 'q' ] @@ -141,21 +141,32 @@ def init(args): """ 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']) # Semaphore init - db.semaphore_store(False) + 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 + def switch(): """ Switch stream @@ -183,30 +194,21 @@ def switch(): 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( - target=stream, - args=( - c['PUBLIC_DOMAIN'], - args)) - p.start() - g['stream_pid'] = p.pid + # Start new thread + th = threading.Thread(target=stream, args=(c['PUBLIC_DOMAIN'], args)) + 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( - target=stream, - args=( - c['USER_DOMAIN'], - args, - g['original_name'])) - p.start() - g['stream_pid'] = p.pid + # Kill old thread + g['stream_stop'] = True + # Start new thread + th = threading.Thread(target=stream, args=(c['USER_DOMAIN'], args, g['original_name'])) + th.daemon = True + th.start() printNicely('') if args.filter: printNicely(cyan('Only: ' + str(args.filter))) @@ -315,7 +317,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) @@ -329,7 +331,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'] @@ -357,7 +359,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]) @@ -383,7 +385,7 @@ 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)) @@ -400,7 +402,7 @@ 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') @@ -413,11 +415,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.')) @@ -432,7 +434,7 @@ 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)) @@ -486,7 +488,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: @@ -505,7 +507,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://')] @@ -633,10 +635,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.')) @@ -906,7 +908,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.')) @@ -925,7 +927,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.')) @@ -940,7 +942,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.')) @@ -956,7 +958,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.')) @@ -992,7 +994,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 :(')) @@ -1019,7 +1021,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 :(')) @@ -1033,7 +1035,7 @@ 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 :(')) @@ -1090,7 +1092,7 @@ def config(): if not g['stuff']: for k in all_config: line = ' ' * 2 + \ - light_green(k) + ': ' + light_yellow(str(all_config[k])) + green(k) + ': ' + light_yellow(str(all_config[k])) printNicely(line) guide = 'Detailed explanation can be found at ' + \ color_func(c['TWEET']['link'])( @@ -1101,7 +1103,7 @@ def config(): if g['stuff'] in all_config: k = g['stuff'] line = ' ' * 2 + \ - light_green(k) + ': ' + light_yellow(str(all_config[k])) + green(k) + ': ' + light_yellow(str(all_config[k])) printNicely(line) else: printNicely(red('No such config key.')) @@ -1110,25 +1112,35 @@ def config(): key = g['stuff'].split()[0] try: value = get_default_config(key) - line = ' ' * 2 + light_green(key) + ': ' + light_magenta(value) + line = ' ' * 2 + green(key) + ': ' + light_magenta(value) printNicely(line) except: - printNicely(light_magenta('This config key does not exist in default.')) + printNicely( + light_magenta('This config key does not exist in default.')) # 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(light_green('Config key is dropped.')) + printNicely(green('Config key is dropped.')) except: printNicely(red('No such config key.')) # 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) - printNicely(light_green('Updated successfully.')) + # Apply theme immediately + if key == 'THEME': + c['THEME'] = reload_theme(value,c['THEME']) + g['decorated_name'] = lambda x: color_func( + c['DECORATED_NAME'])( + '[' + x + ']: ') + printNicely(green('Updated successfully.')) except: printNicely(light_magenta('Not valid value.')) return @@ -1150,33 +1162,15 @@ def theme(): else: line = ' ' * 4 + line printNicely(line) - elif g['stuff'] == 'current_as_default': - # Set as default - set_config('THEME', c['THEME']) - 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.')) @@ -1419,6 +1413,8 @@ def help(): 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 @@ -1440,6 +1436,22 @@ def help(): 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 @@ -1451,9 +1463,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() @@ -1512,6 +1526,8 @@ def process(cmd): config, theme, help, + pause, + replay, clear, quit ] @@ -1570,7 +1586,7 @@ def listen(): ], # list [], # cal [key for key in dict(get_all_config())], # config - g['themes'] + ['current_as_default'], # theme + g['themes'], # theme [ 'discover', 'tweets', @@ -1579,6 +1595,8 @@ def listen(): 'list', 'stream' ], # help + [], # pause + [], # reconnect [], # clear [], # quit ] @@ -1587,8 +1605,9 @@ 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() try: @@ -1598,7 +1617,7 @@ def listen(): g['cmd'] = cmd try: # Lock the semaphore - db.semaphore_update(True) + c['lock'] = True # Save cmd to global variable and call process g['stuff'] = ' '.join(line.split()[1:]) # Process the command @@ -1609,7 +1628,7 @@ def listen(): else: g['prefix'] = True # Release the semaphore lock - db.semaphore_update(False) + c['lock'] = False except Exception: printNicely(red('OMG something is wrong with Twitter right now.')) @@ -1629,7 +1648,7 @@ def stream(domain, args, name='Rainbow Stream'): # These arguments are optional: stream_args = dict( timeout=args.timeout, - block=not args.no_block, + block=False, heartbeat_timeout=args.heartbeat_timeout) # Track keyword query_args = dict() @@ -1650,9 +1669,15 @@ def stream(domain, args, name='Rainbow Stream'): tweet_iter = stream.statuses.filter(**query_args) else: tweet_iter = stream.statuses.sample() + # Block new stream until other one exits + StreamLock.acquire() + g['stream_stop'] = False for tweet in tweet_iter: + if(g['stream_stop']): + StreamLock.release() + break if tweet is None: - printNicely("-- None --") + pass elif tweet is Timeout: printNicely("-- Timeout --") elif tweet is HeartbeatTimeout: @@ -1668,7 +1693,7 @@ def stream(domain, args, name='Rainbow Stream'): ig=args.ignore, ) elif tweet.get('direct_message'): - print_message(tweet['direct_message']) + print_message(tweet['direct_message'], check_semaphore=True) except TwitterHTTPError: printNicely('') printNicely( @@ -1686,23 +1711,16 @@ def fly(): except TwitterHTTPError: printNicely('') printNicely( - magenta("Something wrong with Twitter Oauth right now :(")) - printNicely( - magenta("Please delete ~/.rainbow_oauth and try again.")) + magenta("We have maximum 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() - # Spawn stream process - p = Process( - target=stream, - args=( - c['USER_DOMAIN'], - args, - g['original_name'])) - p.start() + # Spawn stream thread + th = threading.Thread(target=stream, args=(c['USER_DOMAIN'], args, g['original_name'])) + th.daemon = True + th.start() # Start listen process time.sleep(0.5) g['reset'] = True g['prefix'] = True - g['stream_pid'] = p.pid listen()