add comments and fix bug in upgrade_center with python3
[rainbowstream.git] / rainbowstream / rainbow.py
index fa66626634ab1f68061374f0d8892ae10e9cbf60..d67be609a9378dd515025c7e06e3c5163768ee94 100644 (file)
@@ -7,6 +7,8 @@ import time
 import threading
 import requests
 import webbrowser
+import traceback
+import pkg_resources
 
 from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup
 from twitter.api import *
@@ -21,6 +23,7 @@ from .consumer import *
 from .interactive import *
 from .c_image import *
 from .py3patch import *
+from .emoji import *
 
 # Global values
 g = {}
@@ -108,6 +111,35 @@ def build_mute_dict(dict_data=False):
         return screen_name_list
 
 
+def debug_option():
+    """
+    Save traceback when run in debug mode
+    """
+    if g['debug']:
+        g['traceback'].append(traceback.format_exc())
+
+
+def upgrade_center():
+    """
+    Check latest and notify to upgrade
+    """
+    try:
+        current = pkg_resources.get_distribution("rainbowstream").version
+        url = 'https://raw.githubusercontent.com/DTVD/rainbowstream/master/setup.py'
+        readme = requests.get(url).text
+        latest = readme.split("version = \'")[1].split("\'")[0]
+        if current != latest:
+            notice = light_magenta('RainbowStream latest version is ')
+            notice += light_green(latest)
+            notice += light_magenta(' while your current version is ')
+            notice += light_yellow(current) + '\n'
+            notice += light_magenta('You should upgrade with ')
+            notice += light_green('pip install -U rainbowstream')
+            printNicely(notice)
+    except:
+        pass
+
+
 def init(args):
     """
     Init function
@@ -115,6 +147,8 @@ def init(args):
     # Handle Ctrl C
     ctrl_c_handler = lambda signum, frame: quit()
     signal.signal(signal.SIGINT, ctrl_c_handler)
+    # Upgrade notify
+    upgrade_center()
     # Get name
     t = Twitter(auth=authen())
     credential = t.account.verify_credentials()
@@ -122,11 +156,12 @@ def init(args):
     name = credential['name']
     if not get_config('PREFIX'):
         set_config('PREFIX', screen_name)
+    c['PREFIX'] = emojize(c['PREFIX'])
     g['PREFIX'] = u2str(c['PREFIX'])
     c['original_name'] = g['original_name'] = screen_name[1:]
     g['full_name'] = name
     g['decorated_name'] = lambda x: color_func(
-        c['DECORATED_NAME'])('[' + x + ']: ')
+        c['DECORATED_NAME'])('[' + x + ']: ', rl=True)
     # Theme init
     files = os.listdir(os.path.dirname(__file__) + '/colorset')
     themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
@@ -135,6 +170,11 @@ def init(args):
     g['message_threads'] = {}
     # Startup cmd
     g['cmd'] = ''
+    # Debug option default = True
+    g['debug'] = True
+    g['traceback'] = []
+    # Events
+    c['events'] = []
     # Semaphore init
     c['lock'] = False
     # Init tweet dict and message dict
@@ -196,6 +236,18 @@ def home():
     printNicely('')
 
 
+def notification():
+    """
+    Show notifications
+    """
+    if c['events']:
+        for e in c['events']:
+            print_event(e)
+        printNicely('')
+    else:
+        printNicely(magenta('Nothing at this time.'))
+
+
 def mentions():
     """
     Mentions timeline
@@ -222,7 +274,8 @@ def whois():
                 include_entities=False)
             show_profile(user)
         except:
-            printNicely(red('Omg no user.'))
+            debug_option()
+            printNicely(red('No user.'))
     else:
         printNicely(red('A name should begin with a \'@\''))
 
@@ -238,7 +291,8 @@ def view():
             num = int(g['stuff'].split()[1])
         except:
             num = c['HOME_TWEET_NUM']
-        for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])):
+        for tweet in reversed(
+                t.statuses.user_timeline(count=num, screen_name=user[1:])):
             draw(t=tweet)
         printNicely('')
     else:
@@ -313,7 +367,8 @@ def quote():
     if not formater:
         return
     # Get comment
-    prefix = light_magenta('Compose your ') + light_green('#comment: ')
+    prefix = light_magenta('Compose your ', rl=True) + \
+        light_green('#comment: ', rl=True)
     comment = raw_input(prefix)
     if comment:
         quote = comment.join(formater.split('#comment'))
@@ -460,6 +515,7 @@ def show():
             img = Image.open(BytesIO(res.content))
             img.show()
     except:
+        debug_option()
         printNicely(red('Sorry I can\'t show this image.'))
 
 
@@ -482,6 +538,7 @@ def urlopen():
         for link in link_ary:
             webbrowser.open(link)
     except:
+        debug_option()
         printNicely(red('Sorry I can\'t open url in this tweet.'))
 
 
@@ -563,6 +620,7 @@ def thread():
             g['original_name'],
             g['full_name'])
     except Exception:
+        debug_option()
         printNicely(red('No such thread.'))
 
 
@@ -583,6 +641,7 @@ def message():
         else:
             printNicely(red('A name should begin with a \'@\''))
     except:
+        debug_option()
         printNicely(red('Sorry I can\'t understand.'))
 
 
@@ -691,6 +750,7 @@ def mute():
             else:
                 printNicely(red(rel))
         except:
+            debug_option()
             printNicely(red('Something is wrong, can not mute now :('))
     else:
         printNicely(red('A name should begin with a \'@\''))
@@ -783,10 +843,11 @@ def report():
 
 def get_slug():
     """
-    Get Slug Decorator
+    Get slug
     """
     # Get list name
-    list_name = raw_input(light_magenta('Give me the list\'s name: '))
+    list_name = raw_input(
+        light_magenta('Give me the list\'s name ("@owner/list_name"): ', rl=True))
     # Get list name and owner
     try:
         owner, slug = list_name.split('/')
@@ -879,7 +940,10 @@ def list_add(t):
     """
     owner, slug = get_slug()
     # Add
-    user_name = raw_input(light_magenta('Give me name of the newbie: '))
+    user_name = raw_input(
+        light_magenta(
+            'Give me name of the newbie: ',
+            rl=True))
     if user_name.startswith('@'):
         user_name = user_name[1:]
     try:
@@ -889,6 +953,7 @@ def list_add(t):
             screen_name=user_name)
         printNicely(green('Added.'))
     except:
+        debug_option()
         printNicely(light_magenta('I\'m sorry we can not add him/her.'))
 
 
@@ -898,7 +963,10 @@ def list_remove(t):
     """
     owner, slug = get_slug()
     # Remove
-    user_name = raw_input(light_magenta('Give me name of the unlucky one: '))
+    user_name = raw_input(
+        light_magenta(
+            'Give me name of the unlucky one: ',
+            rl=True))
     if user_name.startswith('@'):
         user_name = user_name[1:]
     try:
@@ -908,6 +976,7 @@ def list_remove(t):
             screen_name=user_name)
         printNicely(green('Gone.'))
     except:
+        debug_option()
         printNicely(light_magenta('I\'m sorry we can not remove him/her.'))
 
 
@@ -923,6 +992,7 @@ def list_subscribe(t):
             owner_screen_name=owner)
         printNicely(green('Done.'))
     except:
+        debug_option()
         printNicely(
             light_magenta('I\'m sorry you can not subscribe to this list.'))
 
@@ -939,6 +1009,7 @@ def list_unsubscribe(t):
             owner_screen_name=owner)
         printNicely(green('Done.'))
     except:
+        debug_option()
         printNicely(
             light_magenta('I\'m sorry you can not unsubscribe to this list.'))
 
@@ -965,9 +1036,15 @@ def list_new(t):
     """
     Create a new list
     """
-    name = raw_input(light_magenta('New list\'s name: '))
-    mode = raw_input(light_magenta('New list\'s mode (public/private): '))
-    description = raw_input(light_magenta('New list\'s description: '))
+    name = raw_input(light_magenta('New list\'s name: ', rl=True))
+    mode = raw_input(
+        light_magenta(
+            'New list\'s mode (public/private): ',
+            rl=True))
+    description = raw_input(
+        light_magenta(
+            'New list\'s description: ',
+            rl=True))
     try:
         t.lists.create(
             name=name,
@@ -975,6 +1052,7 @@ def list_new(t):
             description=description)
         printNicely(green(name + ' list is created.'))
     except:
+        debug_option()
         printNicely(red('Oops something is wrong with Twitter :('))
 
 
@@ -982,10 +1060,16 @@ def list_update(t):
     """
     Update a list
     """
-    slug = raw_input(light_magenta('Your list that you want to update: '))
-    name = raw_input(light_magenta('Update name (leave blank to unchange): '))
-    mode = raw_input(light_magenta('Update mode (public/private): '))
-    description = raw_input(light_magenta('Update description: '))
+    slug = raw_input(
+        light_magenta(
+            'Your list that you want to update: ',
+            rl=True))
+    name = raw_input(
+        light_magenta(
+            'Update name (leave blank to unchange): ',
+            rl=True))
+    mode = raw_input(light_magenta('Update mode (public/private): ', rl=True))
+    description = raw_input(light_magenta('Update description: ', rl=True))
     try:
         if name:
             t.lists.update(
@@ -1002,6 +1086,7 @@ def list_update(t):
                 description=description)
         printNicely(green(slug + ' list is updated.'))
     except:
+        debug_option()
         printNicely(red('Oops something is wrong with Twitter :('))
 
 
@@ -1009,13 +1094,17 @@ def list_delete(t):
     """
     Delete a list
     """
-    slug = raw_input(light_magenta('Your list that you want to delete: '))
+    slug = raw_input(
+        light_magenta(
+            'Your list that you want to delete: ',
+            rl=True))
     try:
         t.lists.destroy(
             slug='-'.join(slug.split()),
             owner_screen_name=g['original_name'])
         printNicely(green(slug + ' list is deleted.'))
     except:
+        debug_option()
         printNicely(red('Oops something is wrong with Twitter :('))
 
 
@@ -1101,13 +1190,47 @@ def switch():
                     g['original_name']))
             th.daemon = True
             th.start()
+        # Stream base on list
+        elif target == 'list':
+            owner, slug = get_slug()
+            # Force python 2 not redraw readline buffer
+            g['cmd'] = '/'.join([owner, slug])
+            printNicely(light_yellow('getting list members ...'))
+            # Get members
+            t = Twitter(auth=authen())
+            members = []
+            next_cursor = -1
+            while next_cursor != 0:
+                m = t.lists.members(
+                    slug=slug,
+                    owner_screen_name=owner,
+                    cursor=next_cursor,
+                    include_entities=False)
+                for u in m['users']:
+                    members.append('@' + u['screen_name'])
+                next_cursor = m['next_cursor']
+            printNicely(light_yellow('... done.'))
+            # Build thread filter array
+            args.filter = members
+            # Kill old thread
+            g['stream_stop'] = True
+            # Start new thread
+            th = threading.Thread(
+                target=stream,
+                args=(
+                    c['USER_DOMAIN'],
+                    args,
+                    slug))
+            th.daemon = True
+            th.start()
         printNicely('')
         if args.filter:
-            printNicely(cyan('Only: ' + str(args.filter)))
+            printNicely(cyan('Include: ' + str(len(args.filter)) + ' people.'))
         if args.ignore:
-            printNicely(red('Ignore: ' + str(args.ignore)))
+            printNicely(red('Ignore: ' + str(len(args.ignore)) + ' people.'))
         printNicely('')
-    except:
+    except Exception:
+        debug_option()
         printNicely(red('Sorry I can\'t understand.'))
 
 
@@ -1226,6 +1349,8 @@ def help_discover():
         light_green('trend JP Tokyo') + '.\n'
     usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
         light_green('home 7') + ' will show 7 tweets.\n'
+    usage += s * 2 + \
+        light_green('notification') + ' will show your recent notification.\n'
     usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
         light_green('mentions 7') + ' will show 7 mention tweets.\n'
     usage += s * 2 + light_green('whois @mdo') + ' will show profile  of ' + \
@@ -1388,6 +1513,8 @@ def help_stream():
         ' filter will decide nicks will be EXCLUDE.\n'
     usage += s * 2 + light_green('switch mine -d') + \
         ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
+    usage += s * 2 + light_green('switch list') + \
+        ' will switch to a Twitter list\'s stream. You will be asked for list name\n'
     printNicely(usage)
 
 
@@ -1535,6 +1662,7 @@ cmdset = [
     'switch',
     'trend',
     'home',
+    'notification',
     'view',
     'mentions',
     't',
@@ -1579,6 +1707,7 @@ funcset = [
     switch,
     trend,
     home,
+    notification,
     view,
     mentions,
     tweet,
@@ -1633,9 +1762,10 @@ def listen():
     d = dict(zip(
         cmdset,
         [
-            ['public', 'mine'],  # switch
+            ['public', 'mine', 'list'],  # switch
             [],  # trend
             [],  # home
+            [],  # notification
             ['@'],  # view
             [],  # mentions
             [],  # tweet
@@ -1701,6 +1831,7 @@ def listen():
         try:
             # raw_input
             if g['prefix']:
+                # Only use PREFIX as a string with raw_input
                 line = raw_input(g['decorated_name'](g['PREFIX']))
             else:
                 line = raw_input()
@@ -1727,6 +1858,7 @@ def listen():
         except EOFError:
             printNicely('')
         except Exception:
+            debug_option()
             printNicely(red('OMG something is wrong with Twitter right now.'))
 
 
@@ -1785,7 +1917,7 @@ def stream(domain, args, name='Rainbow Stream'):
                     light_green("h stream") + \
                     light_magenta(" for more details.")
                 printNicely(guide)
-                sys.stdout.write(g['decorated_name'](g['PREFIX']))
+                sys.stdout.write(g['decorated_name'](c['PREFIX']))
                 sys.stdout.flush()
                 StreamLock.release()
                 break
@@ -1813,10 +1945,10 @@ def stream(domain, args, name='Rainbow Stream'):
                 # the 1st character of that word
                 if current_buffer and g['cmd'] != current_buffer:
                     sys.stdout.write(
-                        g['decorated_name'](g['PREFIX']) + str2u(current_buffer))
+                        g['decorated_name'](c['PREFIX']) + str2u(current_buffer))
                     sys.stdout.flush()
                 elif not c['HIDE_PROMPT']:
-                    sys.stdout.write(g['decorated_name'](g['PREFIX']))
+                    sys.stdout.write(g['decorated_name'](c['PREFIX']))
                     sys.stdout.flush()
             elif tweet.get('direct_message'):
                 # Check the semaphore pause and lock (stream process only)
@@ -1825,6 +1957,9 @@ def stream(domain, args, name='Rainbow Stream'):
                 while c['lock']:
                     time.sleep(0.5)
                 print_message(tweet['direct_message'])
+            elif tweet.get('event'):
+                c['events'].append(tweet)
+                print_event(tweet)
     except TwitterHTTPError:
         printNicely('')
         printNicely(