Update README.rst
[rainbowstream.git] / rainbowstream / rainbow.py
... / ...
CommitLineData
1import os
2import os.path
3import sys
4import signal
5import argparse
6import time
7import threading
8import requests
9import webbrowser
10import traceback
11import pkg_resources
12import socks
13import socket
14import re
15
16from io import BytesIO
17from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup
18from twitter.api import *
19from twitter.oauth import OAuth, read_token_file
20from twitter.oauth_dance import oauth_dance
21from twitter.util import printNicely
22
23from pocket import Pocket
24
25from .draw import *
26from .colors import *
27from .config import *
28from .consumer import *
29from .interactive import *
30from .c_image import *
31from .py3patch import *
32from .emoji import *
33from .util import *
34
35# Global values
36g = {}
37
38# Lock for streams
39StreamLock = threading.Lock()
40
41
42def parse_arguments():
43 """
44 Parse the arguments
45 """
46 parser = argparse.ArgumentParser(description=__doc__ or "")
47 parser.add_argument(
48 '-s',
49 '--stream',
50 default="mine",
51 help='Default stream after program start. (Default: mine)')
52 parser.add_argument(
53 '-to',
54 '--timeout',
55 help='Timeout for the stream (seconds).')
56 parser.add_argument(
57 '-tt',
58 '--track-keywords',
59 help='Search the stream for specific text.')
60 parser.add_argument(
61 '-fil',
62 '--filter',
63 help='Filter specific screen_name.')
64 parser.add_argument(
65 '-ig',
66 '--ignore',
67 help='Ignore specific screen_name.')
68 parser.add_argument(
69 '-iot',
70 '--image-on-term',
71 action='store_true',
72 help='Display all image on terminal.')
73 parser.add_argument(
74 '-24',
75 '--color-24bit',
76 action='store_true',
77 help='Display images using 24bit color codes.')
78 parser.add_argument(
79 '-ph',
80 '--proxy-host',
81 help='Use HTTP/SOCKS proxy for network connections.')
82 parser.add_argument(
83 '-pp',
84 '--proxy-port',
85 default=8080,
86 help='HTTP/SOCKS proxy port (Default: 8080).')
87 parser.add_argument(
88 '-pt',
89 '--proxy-type',
90 default='SOCKS5',
91 help='Proxy type (HTTP, SOCKS4, SOCKS5; Default: SOCKS5).')
92 return parser.parse_args()
93
94
95def proxy_connect(args):
96 """
97 Connect to specified proxy
98 """
99 if args.proxy_host:
100 # Setup proxy by monkeypatching the standard lib
101 if args.proxy_type.lower() == "socks5" or not args.proxy_type:
102 socks.set_default_proxy(
103 socks.SOCKS5, args.proxy_host,
104 int(args.proxy_port))
105 elif args.proxy_type.lower() == "http":
106 socks.set_default_proxy(
107 socks.HTTP, args.proxy_host,
108 int(args.proxy_port))
109 elif args.proxy_type.lower() == "socks4":
110 socks.set_default_proxy(
111 socks.SOCKS4, args.proxy_host,
112 int(args.proxy_port))
113 else:
114 printNicely(
115 magenta('Sorry, wrong proxy type specified! Aborting...'))
116 sys.exit()
117 socket.socket = socks.socksocket
118
119
120def authen():
121 """
122 Authenticate with Twitter OAuth
123 """
124 # When using rainbow stream you must authorize.
125 twitter_credential = os.environ.get(
126 'HOME',
127 os.environ.get(
128 'USERPROFILE',
129 '')) + os.sep + '.rainbow_oauth'
130 if not os.path.exists(twitter_credential):
131 oauth_dance('Rainbow Stream',
132 CONSUMER_KEY,
133 CONSUMER_SECRET,
134 twitter_credential)
135 oauth_token, oauth_token_secret = read_token_file(twitter_credential)
136 return OAuth(
137 oauth_token,
138 oauth_token_secret,
139 CONSUMER_KEY,
140 CONSUMER_SECRET)
141
142
143def pckt_authen():
144 """
145 Authenticate with Pocket OAuth
146 """
147 pocket_credential = os.environ.get(
148 'HOME',
149 os.environ.get(
150 'USERPROFILE',
151 '')) + os.sep + '.rainbow_pckt_oauth'
152
153 if not os.path.exists(pocket_credential):
154 request_token = Pocket.get_request_token(consumer_key=PCKT_CONSUMER_KEY)
155 auth_url = Pocket.get_auth_url(code=request_token, redirect_uri="/")
156 webbrowser.open(auth_url)
157 printNicely(green("*** Press [ENTER] after authorization ***"))
158 raw_input()
159 user_credentials = Pocket.get_credentials(consumer_key=PCKT_CONSUMER_KEY, code=request_token)
160 access_token = user_credentials['access_token']
161 f = open(pocket_credential, 'w')
162 f.write(access_token)
163 f.close()
164 else:
165 with open(pocket_credential, 'r') as f:
166 access_token = str(f.readlines()[0])
167 f.close()
168
169 return Pocket(PCKT_CONSUMER_KEY, access_token)
170
171
172def build_mute_dict(dict_data=False):
173 """
174 Build muting list
175 """
176 t = Twitter(auth=authen())
177 # Init cursor
178 next_cursor = -1
179 screen_name_list = []
180 name_list = []
181 # Cursor loop
182 while next_cursor != 0:
183 list = t.mutes.users.list(
184 screen_name=g['original_name'],
185 cursor=next_cursor,
186 skip_status=True,
187 include_entities=False,
188 )
189 screen_name_list += ['@' + u['screen_name'] for u in list['users']]
190 name_list += [u['name'] for u in list['users']]
191 next_cursor = list['next_cursor']
192 # Return dict or list
193 if dict_data:
194 return dict(zip(screen_name_list, name_list))
195 else:
196 return screen_name_list
197
198
199def debug_option():
200 """
201 Save traceback when run in debug mode
202 """
203 if g['debug']:
204 g['traceback'].append(traceback.format_exc())
205
206
207def upgrade_center():
208 """
209 Check latest and notify to upgrade
210 """
211 try:
212 current = pkg_resources.get_distribution('rainbowstream').version
213 url = 'https://raw.githubusercontent.com/DTVD/rainbowstream/master/setup.py'
214 readme = requests.get(url).text
215 latest = readme.split('version = \'')[1].split('\'')[0]
216 g['using_latest'] = current == latest
217 if not g['using_latest']:
218 notice = light_magenta('RainbowStream latest version is ')
219 notice += light_green(latest)
220 notice += light_magenta(' while your current version is ')
221 notice += light_yellow(current) + '\n'
222 notice += light_magenta('You should upgrade with ')
223 notice += light_green('pip install -U rainbowstream')
224 else:
225 notice = light_yellow('You are running latest version (')
226 notice += light_green(current)
227 notice += light_yellow(')')
228 notice += '\n'
229 printNicely(notice)
230 except:
231 pass
232
233
234def init(args):
235 """
236 Init function
237 """
238 # Handle Ctrl C
239 ctrl_c_handler = lambda signum, frame: quit()
240 signal.signal(signal.SIGINT, ctrl_c_handler)
241 # Upgrade notify
242 upgrade_center()
243 # Get name
244 t = Twitter(auth=authen())
245 credential = t.account.verify_credentials()
246 screen_name = '@' + credential['screen_name']
247 name = credential['name']
248 c['original_name'] = g['original_name'] = screen_name[1:]
249 g['listname'] = g['keyword'] = ''
250 g['PREFIX'] = u2str(emojize(format_prefix()))
251 g['full_name'] = name
252 g['decorated_name'] = lambda x: color_func(
253 c['DECORATED_NAME'])('[' + x + ']: ', rl=True)
254 # Theme init
255 files = os.listdir(os.path.dirname(__file__) + '/colorset')
256 themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
257 g['themes'] = themes
258 g['pause'] = False
259 g['message_threads'] = {}
260 # Startup cmd
261 g['cmd'] = ''
262 # Debug option default = True
263 g['debug'] = True
264 g['traceback'] = []
265 # Events
266 c['events'] = []
267 # Semaphore init
268 c['lock'] = False
269 # Init tweet dict and message dict
270 c['tweet_dict'] = []
271 c['message_dict'] = []
272 # Image on term
273 c['IMAGE_ON_TERM'] = args.image_on_term
274 # Use 24 bit color
275 c['24BIT'] = args.color_24bit
276 # Check type of ONLY_LIST and IGNORE_LIST
277 if not isinstance(c['ONLY_LIST'], list):
278 printNicely(red('ONLY_LIST is not a valid list value.'))
279 c['ONLY_LIST'] = []
280 if not isinstance(c['IGNORE_LIST'], list):
281 printNicely(red('IGNORE_LIST is not a valid list value.'))
282 c['IGNORE_LIST'] = []
283 # Mute dict
284 c['IGNORE_LIST'] += build_mute_dict()
285 # Pocket init
286 pckt = pckt_authen() if c['POCKET_SUPPORT'] else None
287
288
289def trend():
290 """
291 Trend
292 """
293 t = Twitter(auth=authen())
294 # Get country and town
295 try:
296 country = g['stuff'].split()[0]
297 except:
298 country = ''
299 try:
300 town = g['stuff'].split()[1]
301 except:
302 town = ''
303 avail = t.trends.available()
304 # World wide
305 if not country:
306 trends = t.trends.place(_id=1)[0]['trends']
307 print_trends(trends)
308 else:
309 for location in avail:
310 # Search for country and Town
311 if town:
312 if location['countryCode'] == country \
313 and location['placeType']['name'] == 'Town' \
314 and location['name'] == town:
315 trends = t.trends.place(_id=location['woeid'])[0]['trends']
316 print_trends(trends)
317 # Search for country only
318 else:
319 if location['countryCode'] == country \
320 and location['placeType']['name'] == 'Country':
321 trends = t.trends.place(_id=location['woeid'])[0]['trends']
322 print_trends(trends)
323
324
325def home():
326 """
327 Home
328 """
329 t = Twitter(auth=authen())
330 num = c['HOME_TWEET_NUM']
331 if g['stuff'].isdigit():
332 num = int(g['stuff'])
333 kwargs = {'count': num}
334 kwargs = add_tweetmode_parameter(kwargs)
335 for tweet in reversed(t.statuses.home_timeline(**kwargs)):
336 draw(t=tweet)
337 printNicely('')
338
339
340def notification():
341 """
342 Show notifications
343 """
344 if c['events']:
345 for e in c['events']:
346 print_event(e)
347 printNicely('')
348 else:
349 printNicely(magenta('Nothing at this time.'))
350
351
352def mentions():
353 """
354 Mentions timeline
355 """
356 t = Twitter(auth=authen())
357 num = c['HOME_TWEET_NUM']
358 if g['stuff'].isdigit():
359 num = int(g['stuff'])
360 kwargs = {'count': num}
361 kwargs = add_tweetmode_parameter(kwargs)
362 for tweet in reversed(t.statuses.mentions_timeline(**kwargs)):
363 draw(t=tweet)
364 printNicely('')
365
366
367def whois():
368 """
369 Show profile of a specific user
370 """
371 t = Twitter(auth=authen())
372 try:
373 screen_name = g['stuff'].split()[0]
374 except:
375 printNicely(red('Sorry I can\'t understand.'))
376 return
377 if screen_name.startswith('@'):
378 try:
379 user = t.users.show(
380 screen_name=screen_name[1:],
381 include_entities=False)
382 show_profile(user)
383 except:
384 debug_option()
385 printNicely(red('No user.'))
386 else:
387 printNicely(red('A name should begin with a \'@\''))
388
389
390def view():
391 """
392 Friend view
393 """
394 t = Twitter(auth=authen())
395 try:
396 user = g['stuff'].split()[0]
397 except:
398 printNicely(red('Sorry I can\'t understand.'))
399 return
400 if user[0] == '@':
401 try:
402 num = int(g['stuff'].split()[1])
403 except:
404 num = c['HOME_TWEET_NUM']
405 kwargs = {'count': num, 'screen_name': user[1:]}
406 kwargs = add_tweetmode_parameter(kwargs)
407 for tweet in reversed(t.statuses.user_timeline(**kwargs)):
408 draw(t=tweet)
409 printNicely('')
410 else:
411 printNicely(red('A name should begin with a \'@\''))
412
413
414def view_my_tweets():
415 """
416 Display user's recent tweets.
417 """
418 t = Twitter(auth=authen())
419 try:
420 num = int(g['stuff'])
421 except:
422 num = c['HOME_TWEET_NUM']
423 kwargs = {'count': num, 'screen_name': g['original_name']}
424 kwargs = add_tweetmode_parameter(kwargs)
425 for tweet in reversed(
426 t.statuses.user_timeline(**kwargs)):
427 draw(t=tweet)
428 printNicely('')
429
430
431def search():
432 """
433 Search
434 """
435 t = Twitter(auth=authen())
436 # Setup query
437 query = g['stuff'].strip()
438 if not query:
439 printNicely(red('Sorry I can\'t understand.'))
440 return
441 type = c['SEARCH_TYPE']
442 if type not in ['mixed', 'recent', 'popular']:
443 type = 'mixed'
444 max_record = c['SEARCH_MAX_RECORD']
445 count = min(max_record, 100)
446 kwargs = {
447 'q': query,
448 'type': type,
449 'count': count,
450 }
451 kwargs = add_tweetmode_parameter(kwargs)
452 # Perform search
453 rel = t.search.tweets(**kwargs)['statuses']
454 # Return results
455 if rel:
456 printNicely('Newest tweets:')
457 for i in reversed(xrange(count)):
458 draw(t=rel[i], keyword=query)
459 printNicely('')
460 else:
461 printNicely(magenta('I\'m afraid there is no result'))
462
463
464def tweet():
465 """
466 Tweet
467 """
468 t = Twitter(auth=authen())
469 t.statuses.update(status=g['stuff'])
470
471
472def pocket():
473 """
474 Add new link to Pocket along with tweet id
475 """
476 if not c['POCKET_SUPPORT']:
477 printNicely(yellow('Pocket isn\'t enabled.'))
478 printNicely(yellow('You need to "config POCKET_SUPPORT = true"'))
479 return
480
481 # Get tweet infos
482 p = pckt_authen()
483
484 t = Twitter(auth=authen())
485 try:
486 id = int(g['stuff'].split()[0])
487 tid = c['tweet_dict'][id]
488 except:
489 printNicely(red('Sorry I can\'t understand.'))
490 return
491
492 tweet = t.statuses.show(id=tid)
493
494 if len(tweet['entities']['urls']) > 0:
495 url = tweet['entities']['urls'][0]['expanded_url']
496 else:
497 url = "https://twitter.com/" + \
498 tweet['user']['screen_name'] + '/status/' + str(tid)
499
500 # Add link to pocket
501 try:
502 p.add(title=re.sub(r'(http:\/\/\S+)', r'', tweet['text']),
503 url=url,
504 tweet_id=tid)
505 except:
506 printNicely(red('Something is wrong about your Pocket account,'+ \
507 ' please restart Rainbowstream.'))
508 pocket_credential = os.environ.get(
509 'HOME',
510 os.environ.get(
511 'USERPROFILE',
512 '')) + os.sep + '.rainbow_pckt_oauth'
513 if os.path.exists(pocket_credential):
514 os.remove(pocket_credential)
515 return
516
517 printNicely(green('Pocketed !'))
518 printNicely('')
519
520
521def retweet():
522 """
523 ReTweet
524 """
525 t = Twitter(auth=authen())
526 try:
527 id = int(g['stuff'].split()[0])
528 except:
529 printNicely(red('Sorry I can\'t understand.'))
530 return
531 tid = c['tweet_dict'][id]
532 t.statuses.retweet(id=tid, include_entities=False, trim_user=True)
533
534
535def quote():
536 """
537 Quote a tweet
538 """
539 # Get tweet
540 t = Twitter(auth=authen())
541 try:
542 id = int(g['stuff'].split()[0])
543 except:
544 printNicely(red('Sorry I can\'t understand.'))
545 return
546 tid = c['tweet_dict'][id]
547 kwargs = {'id': tid}
548 kwargs = add_tweetmode_parameter(kwargs)
549 tweet = t.statuses.show(**kwargs)
550 # Get formater
551 formater = format_quote(tweet)
552 if not formater:
553 return
554 # Get comment
555 prefix = light_magenta('Compose your ', rl=True) + \
556 light_green('#comment: ', rl=True)
557 comment = raw_input(prefix)
558 if comment:
559 quote = comment.join(formater.split('#comment'))
560 t.statuses.update(status=quote)
561 else:
562 printNicely(light_magenta('No text added.'))
563
564
565def allretweet():
566 """
567 List all retweet
568 """
569 t = Twitter(auth=authen())
570 # Get rainbow id
571 try:
572 id = int(g['stuff'].split()[0])
573 except:
574 printNicely(red('Sorry I can\'t understand.'))
575 return
576 tid = c['tweet_dict'][id]
577 # Get display num if exist
578 try:
579 num = int(g['stuff'].split()[1])
580 except:
581 num = c['RETWEETS_SHOW_NUM']
582 # Get result and display
583 kwargs = {'id': tid, 'count': num}
584 kwargs = add_tweetmode_parameter(kwargs)
585 rt_ary = t.statuses.retweets(**kwargs)
586 if not rt_ary:
587 printNicely(magenta('This tweet has no retweet.'))
588 return
589 for tweet in reversed(rt_ary):
590 draw(t=tweet)
591 printNicely('')
592
593
594def conversation():
595 """
596 Conversation view
597 """
598 t = Twitter(auth=authen())
599 try:
600 id = int(g['stuff'].split()[0])
601 except:
602 printNicely(red('Sorry I can\'t understand.'))
603 return
604 tid = c['tweet_dict'][id]
605 kwargs = {'id': tid}
606 kwargs = add_tweetmode_parameter(kwargs)
607 tweet = t.statuses.show(**kwargs)
608 limit = c['CONVERSATION_MAX']
609 thread_ref = []
610 thread_ref.append(tweet)
611 prev_tid = tweet['in_reply_to_status_id']
612 while prev_tid and limit:
613 limit -= 1
614 kwargs['id'] = prev_tid
615 tweet = t.statuses.show(**kwargs)
616 prev_tid = tweet['in_reply_to_status_id']
617 thread_ref.append(tweet)
618
619 for tweet in reversed(thread_ref):
620 draw(t=tweet)
621 printNicely('')
622
623
624def reply():
625 """
626 Reply
627 """
628 t = Twitter(auth=authen())
629 try:
630 id = int(g['stuff'].split()[0])
631 except:
632 printNicely(red('Sorry I can\'t understand.'))
633 return
634 tid = c['tweet_dict'][id]
635 user = t.statuses.show(id=tid)['user']['screen_name']
636 status = ' '.join(g['stuff'].split()[1:])
637 # don't include own username for tweet chains
638 # for details see issue https://github.com/DTVD/rainbowstream/issues/163
639 if user == g['original_name']:
640 status = str2u(status)
641 else:
642 status = '@' + user + ' ' + str2u(status)
643 t.statuses.update(status=status, in_reply_to_status_id=tid)
644
645
646def reply_all():
647 """
648 Reply to all
649 """
650 t = Twitter(auth=authen())
651 try:
652 id = int(g['stuff'].split()[0])
653 except:
654 printNicely(red('Sorry I can\'t understand.'))
655 return
656 tid = c['tweet_dict'][id]
657 original_tweet = t.statuses.show(id=tid)
658 text = original_tweet['text']
659 nick_ary = [original_tweet['user']['screen_name']]
660 for user in list(original_tweet['entities']['user_mentions']):
661 if user['screen_name'] not in nick_ary \
662 and user['screen_name'] != g['original_name']:
663 nick_ary.append(user['screen_name'])
664 status = ' '.join(g['stuff'].split()[1:])
665 status = ' '.join(['@' + nick for nick in nick_ary]) + ' ' + str2u(status)
666 t.statuses.update(status=status, in_reply_to_status_id=tid)
667
668
669def favorite():
670 """
671 Favorite
672 """
673 t = Twitter(auth=authen())
674 try:
675 id = int(g['stuff'].split()[0])
676 except:
677 printNicely(red('Sorry I can\'t understand.'))
678 return
679 tid = c['tweet_dict'][id]
680 t.favorites.create(_id=tid, include_entities=False)
681 printNicely(green('Favorited.'))
682 kwargs = {'id': tid}
683 kwargs = add_tweetmode_parameter(kwargs)
684 draw(t.statuses.show(**kwargs))
685 printNicely('')
686
687
688def unfavorite():
689 """
690 Unfavorite
691 """
692 t = Twitter(auth=authen())
693 try:
694 id = int(g['stuff'].split()[0])
695 except:
696 printNicely(red('Sorry I can\'t understand.'))
697 return
698 tid = c['tweet_dict'][id]
699 t.favorites.destroy(_id=tid)
700 printNicely(green('Okay it\'s unfavorited.'))
701 kwargs = {'id': tid}
702 kwargs = add_tweetmode_parameter(kwargs)
703 draw(t.statuses.show(**kwargs))
704 printNicely('')
705
706
707def share():
708 """
709 Copy url of a tweet to clipboard
710 """
711 t = Twitter(auth=authen())
712 try:
713 id = int(g['stuff'].split()[0])
714 tid = c['tweet_dict'][id]
715 except:
716 printNicely(red('Tweet id is not valid.'))
717 return
718 kwargs = {'id': tid}
719 kwargs = add_tweetmode_parameter(kwargs)
720 tweet = t.statuses.show(**kwargs)
721 url = 'https://twitter.com/' + \
722 tweet['user']['screen_name'] + '/status/' + str(tid)
723 import platform
724 if platform.system().lower() == 'darwin':
725 os.system("echo '%s' | pbcopy" % url)
726 printNicely(green('Copied tweet\'s url to clipboard.'))
727 else:
728 printNicely('Direct link: ' + yellow(url))
729
730
731def delete():
732 """
733 Delete
734 """
735 t = Twitter(auth=authen())
736 try:
737 id = int(g['stuff'].split()[0])
738 except:
739 printNicely(red('Sorry I can\'t understand.'))
740 return
741 tid = c['tweet_dict'][id]
742 t.statuses.destroy(id=tid)
743 printNicely(green('Okay it\'s gone.'))
744
745
746def show():
747 """
748 Show image
749 """
750 t = Twitter(auth=authen())
751 try:
752 target = g['stuff'].split()[0]
753 if target != 'image':
754 return
755 id = int(g['stuff'].split()[1])
756 tid = c['tweet_dict'][id]
757 tweet = t.statuses.show(id=tid)
758 media = tweet['entities']['media']
759 for m in media:
760 res = requests.get(m['media_url'])
761 img = Image.open(BytesIO(res.content))
762 img.show()
763 except:
764 debug_option()
765 printNicely(red('Sorry I can\'t show this image.'))
766
767
768def urlopen():
769 """
770 Open url
771 """
772 t = Twitter(auth=authen())
773 try:
774 if not g['stuff'].isdigit():
775 return
776 tid = c['tweet_dict'][int(g['stuff'])]
777 tweet = t.statuses.show(id=tid)
778 urls = tweet['entities']['urls']
779 if not urls:
780 printNicely(light_magenta('No url here @.@!'))
781 return
782 else:
783 for url in urls:
784 expanded_url = url['expanded_url']
785 webbrowser.open(expanded_url)
786 except:
787 debug_option()
788 printNicely(red('Sorry I can\'t open url in this tweet.'))
789
790
791def inbox():
792 """
793 Inbox threads
794 """
795 t = Twitter(auth=authen())
796 num = c['MESSAGES_DISPLAY']
797 if g['stuff'].isdigit():
798 num = g['stuff']
799 # Get inbox messages
800 cur_page = 1
801 inbox = []
802 while num > 20:
803 inbox = inbox + t.direct_messages(
804 count=20,
805 page=cur_page,
806 include_entities=False,
807 skip_status=False
808 )
809 num -= 20
810 cur_page += 1
811 inbox = inbox + t.direct_messages(
812 count=num,
813 page=cur_page,
814 include_entities=False,
815 skip_status=False
816 )
817 # Get sent messages
818 num = c['MESSAGES_DISPLAY']
819 if g['stuff'].isdigit():
820 num = g['stuff']
821 cur_page = 1
822 sent = []
823 while num > 20:
824 sent = sent + t.direct_messages.sent(
825 count=20,
826 page=cur_page,
827 include_entities=False,
828 skip_status=False
829 )
830 num -= 20
831 cur_page += 1
832 sent = sent + t.direct_messages.sent(
833 count=num,
834 page=cur_page,
835 include_entities=False,
836 skip_status=False
837 )
838
839 d = {}
840 uniq_inbox = list(set(
841 [(m['sender_screen_name'], m['sender']['name']) for m in inbox]
842 ))
843 uniq_sent = list(set(
844 [(m['recipient_screen_name'], m['recipient']['name']) for m in sent]
845 ))
846 for partner in uniq_inbox:
847 inbox_ary = [m for m in inbox if m['sender_screen_name'] == partner[0]]
848 sent_ary = [
849 m for m in sent if m['recipient_screen_name'] == partner[0]]
850 d[partner] = inbox_ary + sent_ary
851 for partner in uniq_sent:
852 if partner not in d:
853 d[partner] = [
854 m for m in sent if m['recipient_screen_name'] == partner[0]]
855 g['message_threads'] = print_threads(d)
856
857
858def thread():
859 """
860 View a thread of message
861 """
862 try:
863 thread_id = int(g['stuff'])
864 print_thread(
865 g['message_threads'][thread_id],
866 g['original_name'],
867 g['full_name'])
868 except Exception:
869 debug_option()
870 printNicely(red('No such thread.'))
871
872
873def message():
874 """
875 Send a direct message
876 """
877 t = Twitter(auth=authen())
878 try:
879 user = g['stuff'].split()[0]
880 if user[0].startswith('@'):
881 content = ' '.join(g['stuff'].split()[1:])
882 t.direct_messages.new(
883 screen_name=user[1:],
884 text=content
885 )
886 printNicely(green('Message sent.'))
887 else:
888 printNicely(red('A name should begin with a \'@\''))
889 except:
890 debug_option()
891 printNicely(red('Sorry I can\'t understand.'))
892
893
894def trash():
895 """
896 Remove message
897 """
898 t = Twitter(auth=authen())
899 try:
900 id = int(g['stuff'].split()[0])
901 except:
902 printNicely(red('Sorry I can\'t understand.'))
903 mid = c['message_dict'][id]
904 t.direct_messages.destroy(id=mid)
905 printNicely(green('Message deleted.'))
906
907
908def ls():
909 """
910 List friends for followers
911 """
912 t = Twitter(auth=authen())
913 # Get name
914 try:
915 name = g['stuff'].split()[1]
916 if name.startswith('@'):
917 name = name[1:]
918 else:
919 printNicely(red('A name should begin with a \'@\''))
920 raise Exception('Invalid name')
921 except:
922 name = g['original_name']
923 # Get list followers or friends
924 try:
925 target = g['stuff'].split()[0]
926 except:
927 printNicely(red('Omg some syntax is wrong.'))
928 return
929 # Init cursor
930 d = {'fl': 'followers', 'fr': 'friends'}
931 next_cursor = -1
932 rel = {}
933
934 printNicely('All ' + d[target] + ':')
935
936 # Cursor loop
937 number_of_users = 0
938 while next_cursor != 0:
939
940 list = getattr(t, d[target]).list(
941 screen_name=name,
942 cursor=next_cursor,
943 skip_status=True,
944 include_entities=False,
945 )
946
947 for u in list['users']:
948
949 number_of_users += 1
950
951 # Print out result
952 printNicely( ' ' \
953 + cycle_color( u['name'] ) \
954 + color_func(c['TWEET']['nick'])( ' @' \
955 + u['screen_name'] \
956 + ' ' ) )
957
958 next_cursor = list['next_cursor']
959
960 # 300 users means 15 calls to the related API. The rate limit is 15
961 # calls per 15mn periods (see Twitter documentation).
962 if ( number_of_users % 300 == 0 ):
963 printNicely(light_yellow( 'We reached the limit of Twitter API.' ))
964 printNicely(light_yellow( 'You may need to wait about 15 minutes.' ))
965 break
966
967 printNicely('All: ' + str(number_of_users) + ' ' + d[target] + '.')
968
969def follow():
970 """
971 Follow a user
972 """
973 t = Twitter(auth=authen())
974 screen_name = g['stuff'].split()[0]
975 if screen_name.startswith('@'):
976 t.friendships.create(screen_name=screen_name[1:], follow=True)
977 printNicely(green('You are following ' + screen_name + ' now!'))
978 else:
979 printNicely(red('A name should begin with a \'@\''))
980
981
982def unfollow():
983 """
984 Unfollow a user
985 """
986 t = Twitter(auth=authen())
987 screen_name = g['stuff'].split()[0]
988 if screen_name.startswith('@'):
989 t.friendships.destroy(
990 screen_name=screen_name[1:],
991 include_entities=False)
992 printNicely(green('Unfollow ' + screen_name + ' success!'))
993 else:
994 printNicely(red('A name should begin with a \'@\''))
995
996
997def mute():
998 """
999 Mute a user
1000 """
1001 t = Twitter(auth=authen())
1002 try:
1003 screen_name = g['stuff'].split()[0]
1004 except:
1005 printNicely(red('A name should be specified. '))
1006 return
1007 if screen_name.startswith('@'):
1008 try:
1009 rel = t.mutes.users.create(screen_name=screen_name[1:])
1010 if isinstance(rel, dict):
1011 printNicely(green(screen_name + ' is muted.'))
1012 c['IGNORE_LIST'] += [screen_name]
1013 c['IGNORE_LIST'] = list(set(c['IGNORE_LIST']))
1014 else:
1015 printNicely(red(rel))
1016 except:
1017 debug_option()
1018 printNicely(red('Something is wrong, can not mute now :('))
1019 else:
1020 printNicely(red('A name should begin with a \'@\''))
1021
1022
1023def unmute():
1024 """
1025 Unmute a user
1026 """
1027 t = Twitter(auth=authen())
1028 try:
1029 screen_name = g['stuff'].split()[0]
1030 except:
1031 printNicely(red('A name should be specified. '))
1032 return
1033 if screen_name.startswith('@'):
1034 try:
1035 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
1036 if isinstance(rel, dict):
1037 printNicely(green(screen_name + ' is unmuted.'))
1038 c['IGNORE_LIST'].remove(screen_name)
1039 else:
1040 printNicely(red(rel))
1041 except:
1042 printNicely(red('Maybe you are not muting this person ?'))
1043 else:
1044 printNicely(red('A name should begin with a \'@\''))
1045
1046
1047def muting():
1048 """
1049 List muting user
1050 """
1051 # Get dict of muting users
1052 md = build_mute_dict(dict_data=True)
1053 printNicely('All: ' + str(len(md)) + ' people.')
1054 for name in md:
1055 user = ' ' + cycle_color(md[name])
1056 user += color_func(c['TWEET']['nick'])(' ' + name + ' ')
1057 printNicely(user)
1058 # Update from Twitter
1059 c['IGNORE_LIST'] = [n for n in md]
1060
1061
1062def block():
1063 """
1064 Block a user
1065 """
1066 t = Twitter(auth=authen())
1067 screen_name = g['stuff'].split()[0]
1068 if screen_name.startswith('@'):
1069 t.blocks.create(
1070 screen_name=screen_name[1:],
1071 include_entities=False,
1072 skip_status=True)
1073 printNicely(green('You blocked ' + screen_name + '.'))
1074 else:
1075 printNicely(red('A name should begin with a \'@\''))
1076
1077
1078def unblock():
1079 """
1080 Unblock a user
1081 """
1082 t = Twitter(auth=authen())
1083 screen_name = g['stuff'].split()[0]
1084 if screen_name.startswith('@'):
1085 t.blocks.destroy(
1086 screen_name=screen_name[1:],
1087 include_entities=False,
1088 skip_status=True)
1089 printNicely(green('Unblock ' + screen_name + ' success!'))
1090 else:
1091 printNicely(red('A name should begin with a \'@\''))
1092
1093
1094def report():
1095 """
1096 Report a user as a spam account
1097 """
1098 t = Twitter(auth=authen())
1099 screen_name = g['stuff'].split()[0]
1100 if screen_name.startswith('@'):
1101 t.users.report_spam(
1102 screen_name=screen_name[1:])
1103 printNicely(green('You reported ' + screen_name + '.'))
1104 else:
1105 printNicely(red('Sorry I can\'t understand.'))
1106
1107
1108def get_slug():
1109 """
1110 Get slug
1111 """
1112 # Get list name
1113 list_name = raw_input(
1114 light_magenta('Give me the list\'s name ("@owner/list_name"): ', rl=True))
1115 # Get list name and owner
1116 try:
1117 owner, slug = list_name.split('/')
1118 if slug.startswith('@'):
1119 slug = slug[1:]
1120 return owner, slug
1121 except:
1122 printNicely(
1123 light_magenta('List name should follow "@owner/list_name" format.'))
1124 raise Exception('Wrong list name')
1125
1126
1127def check_slug(list_name):
1128 """
1129 Check slug
1130 """
1131 # Get list name and owner
1132 try:
1133 owner, slug = list_name.split('/')
1134 if slug.startswith('@'):
1135 slug = slug[1:]
1136 return owner, slug
1137 except:
1138 printNicely(
1139 light_magenta('List name should follow "@owner/list_name" format.'))
1140 raise Exception('Wrong list name')
1141
1142
1143def show_lists(t):
1144 """
1145 List list
1146 """
1147 rel = t.lists.list(screen_name=g['original_name'])
1148 if rel:
1149 print_list(rel)
1150 else:
1151 printNicely(light_magenta('You belong to no lists :)'))
1152
1153
1154def list_home(t):
1155 """
1156 List home
1157 """
1158 owner, slug = get_slug()
1159 res = t.lists.statuses(
1160 slug=slug,
1161 owner_screen_name=owner,
1162 count=c['LIST_MAX'],
1163 include_entities=False)
1164 for tweet in reversed(res):
1165 draw(t=tweet)
1166 printNicely('')
1167
1168
1169def list_members(t):
1170 """
1171 List members
1172 """
1173 owner, slug = get_slug()
1174 # Get members
1175 rel = {}
1176 next_cursor = -1
1177 while next_cursor != 0:
1178 m = t.lists.members(
1179 slug=slug,
1180 owner_screen_name=owner,
1181 cursor=next_cursor,
1182 include_entities=False)
1183 for u in m['users']:
1184 rel[u['name']] = '@' + u['screen_name']
1185 next_cursor = m['next_cursor']
1186 printNicely('All: ' + str(len(rel)) + ' members.')
1187 for name in rel:
1188 user = ' ' + cycle_color(name)
1189 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
1190 printNicely(user)
1191
1192
1193def list_subscribers(t):
1194 """
1195 List subscribers
1196 """
1197 owner, slug = get_slug()
1198 # Get subscribers
1199 rel = {}
1200 next_cursor = -1
1201 while next_cursor != 0:
1202 m = t.lists.subscribers(
1203 slug=slug,
1204 owner_screen_name=owner,
1205 cursor=next_cursor,
1206 include_entities=False)
1207 for u in m['users']:
1208 rel[u['name']] = '@' + u['screen_name']
1209 next_cursor = m['next_cursor']
1210 printNicely('All: ' + str(len(rel)) + ' subscribers.')
1211 for name in rel:
1212 user = ' ' + cycle_color(name)
1213 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
1214 printNicely(user)
1215
1216
1217def list_add(t):
1218 """
1219 Add specific user to a list
1220 """
1221 owner, slug = get_slug()
1222 # Add
1223 user_name = raw_input(
1224 light_magenta(
1225 'Give me name of the newbie: ',
1226 rl=True))
1227 if user_name.startswith('@'):
1228 user_name = user_name[1:]
1229 try:
1230 t.lists.members.create(
1231 slug=slug,
1232 owner_screen_name=owner,
1233 screen_name=user_name)
1234 printNicely(green('Added.'))
1235 except:
1236 debug_option()
1237 printNicely(light_magenta('I\'m sorry we can not add him/her.'))
1238
1239
1240def list_remove(t):
1241 """
1242 Remove specific user from a list
1243 """
1244 owner, slug = get_slug()
1245 # Remove
1246 user_name = raw_input(
1247 light_magenta(
1248 'Give me name of the unlucky one: ',
1249 rl=True))
1250 if user_name.startswith('@'):
1251 user_name = user_name[1:]
1252 try:
1253 t.lists.members.destroy(
1254 slug=slug,
1255 owner_screen_name=owner,
1256 screen_name=user_name)
1257 printNicely(green('Gone.'))
1258 except:
1259 debug_option()
1260 printNicely(light_magenta('I\'m sorry we can not remove him/her.'))
1261
1262
1263def list_subscribe(t):
1264 """
1265 Subscribe to a list
1266 """
1267 owner, slug = get_slug()
1268 # Subscribe
1269 try:
1270 t.lists.subscribers.create(
1271 slug=slug,
1272 owner_screen_name=owner)
1273 printNicely(green('Done.'))
1274 except:
1275 debug_option()
1276 printNicely(
1277 light_magenta('I\'m sorry you can not subscribe to this list.'))
1278
1279
1280def list_unsubscribe(t):
1281 """
1282 Unsubscribe a list
1283 """
1284 owner, slug = get_slug()
1285 # Subscribe
1286 try:
1287 t.lists.subscribers.destroy(
1288 slug=slug,
1289 owner_screen_name=owner)
1290 printNicely(green('Done.'))
1291 except:
1292 debug_option()
1293 printNicely(
1294 light_magenta('I\'m sorry you can not unsubscribe to this list.'))
1295
1296
1297def list_own(t):
1298 """
1299 List own
1300 """
1301 rel = []
1302 next_cursor = -1
1303 while next_cursor != 0:
1304 res = t.lists.ownerships(
1305 screen_name=g['original_name'],
1306 cursor=next_cursor)
1307 rel += res['lists']
1308 next_cursor = res['next_cursor']
1309 if rel:
1310 print_list(rel)
1311 else:
1312 printNicely(light_magenta('You own no lists :)'))
1313
1314
1315def list_new(t):
1316 """
1317 Create a new list
1318 """
1319 name = raw_input(light_magenta('New list\'s name: ', rl=True))
1320 mode = raw_input(
1321 light_magenta(
1322 'New list\'s mode (public/private): ',
1323 rl=True))
1324 description = raw_input(
1325 light_magenta(
1326 'New list\'s description: ',
1327 rl=True))
1328 try:
1329 t.lists.create(
1330 name=name,
1331 mode=mode,
1332 description=description)
1333 printNicely(green(name + ' list is created.'))
1334 except:
1335 debug_option()
1336 printNicely(red('Oops something is wrong with Twitter :('))
1337
1338
1339def list_update(t):
1340 """
1341 Update a list
1342 """
1343 slug = raw_input(
1344 light_magenta(
1345 'Your list that you want to update: ',
1346 rl=True))
1347 name = raw_input(
1348 light_magenta(
1349 'Update name (leave blank to unchange): ',
1350 rl=True))
1351 mode = raw_input(light_magenta('Update mode (public/private): ', rl=True))
1352 description = raw_input(light_magenta('Update description: ', rl=True))
1353 try:
1354 if name:
1355 t.lists.update(
1356 slug='-'.join(slug.split()),
1357 owner_screen_name=g['original_name'],
1358 name=name,
1359 mode=mode,
1360 description=description)
1361 else:
1362 t.lists.update(
1363 slug=slug,
1364 owner_screen_name=g['original_name'],
1365 mode=mode,
1366 description=description)
1367 printNicely(green(slug + ' list is updated.'))
1368 except:
1369 debug_option()
1370 printNicely(red('Oops something is wrong with Twitter :('))
1371
1372
1373def list_delete(t):
1374 """
1375 Delete a list
1376 """
1377 slug = raw_input(
1378 light_magenta(
1379 'Your list that you want to delete: ',
1380 rl=True))
1381 try:
1382 t.lists.destroy(
1383 slug='-'.join(slug.split()),
1384 owner_screen_name=g['original_name'])
1385 printNicely(green(slug + ' list is deleted.'))
1386 except:
1387 debug_option()
1388 printNicely(red('Oops something is wrong with Twitter :('))
1389
1390
1391def twitterlist():
1392 """
1393 Twitter's list
1394 """
1395 t = Twitter(auth=authen())
1396 # List all lists or base on action
1397 try:
1398 g['list_action'] = g['stuff'].split()[0]
1399 except:
1400 show_lists(t)
1401 return
1402 # Sub-function
1403 action_ary = {
1404 'home': list_home,
1405 'all_mem': list_members,
1406 'all_sub': list_subscribers,
1407 'add': list_add,
1408 'rm': list_remove,
1409 'sub': list_subscribe,
1410 'unsub': list_unsubscribe,
1411 'own': list_own,
1412 'new': list_new,
1413 'update': list_update,
1414 'del': list_delete,
1415 }
1416 try:
1417 return action_ary[g['list_action']](t)
1418 except:
1419 printNicely(red('Please try again.'))
1420
1421
1422def switch():
1423 """
1424 Switch stream
1425 """
1426 try:
1427 target = g['stuff'].split()[0]
1428 # Filter and ignore
1429 args = parse_arguments()
1430 try:
1431 if g['stuff'].split()[-1] == '-f':
1432 guide = 'To ignore an option, just hit Enter key.'
1433 printNicely(light_magenta(guide))
1434 only = raw_input('Only nicks [Ex: @xxx,@yy]: ')
1435 ignore = raw_input('Ignore nicks [Ex: @xxx,@yy]: ')
1436 args.filter = list(filter(None, only.split(',')))
1437 args.ignore = list(filter(None, ignore.split(',')))
1438 except:
1439 printNicely(red('Sorry, wrong format.'))
1440 return
1441 # Kill old thread
1442 g['stream_stop'] = True
1443 try:
1444 stuff = g['stuff'].split()[1]
1445 except:
1446 stuff = None
1447 # Spawn new thread
1448 spawn_dict = {
1449 'public': spawn_public_stream,
1450 'list': spawn_list_stream,
1451 'mine': spawn_personal_stream,
1452 }
1453 spawn_dict.get(target)(args, stuff)
1454 except:
1455 debug_option()
1456 printNicely(red('Sorry I can\'t understand.'))
1457
1458
1459def cal():
1460 """
1461 Unix's command `cal`
1462 """
1463 # Format
1464 rel = os.popen('cal').read().split('\n')
1465 month = rel.pop(0)
1466 date = rel.pop(0)
1467 show_calendar(month, date, rel)
1468
1469
1470def theme():
1471 """
1472 List and change theme
1473 """
1474 if not g['stuff']:
1475 # List themes
1476 for theme in g['themes']:
1477 line = light_magenta(theme)
1478 if c['THEME'] == theme:
1479 line = ' ' * 2 + light_yellow('* ') + line
1480 else:
1481 line = ' ' * 4 + line
1482 printNicely(line)
1483 else:
1484 # Change theme
1485 try:
1486 # Load new theme
1487 c['THEME'] = reload_theme(g['stuff'], c['THEME'])
1488 # Redefine decorated_name
1489 g['decorated_name'] = lambda x: color_func(
1490 c['DECORATED_NAME'])(
1491 '[' + x + ']: ')
1492 printNicely(green('Theme changed.'))
1493 except:
1494 printNicely(red('No such theme exists.'))
1495
1496
1497def config():
1498 """
1499 Browse and change config
1500 """
1501 all_config = get_all_config()
1502 g['stuff'] = g['stuff'].strip()
1503 # List all config
1504 if not g['stuff']:
1505 for k in all_config:
1506 line = ' ' * 2 + \
1507 green(k) + ': ' + light_yellow(str(all_config[k]))
1508 printNicely(line)
1509 guide = 'Detailed explanation can be found at ' + \
1510 color_func(c['TWEET']['link'])(
1511 'http://rainbowstream.readthedocs.org/en/latest/#config-explanation')
1512 printNicely(guide)
1513 # Print specific config
1514 elif len(g['stuff'].split()) == 1:
1515 if g['stuff'] in all_config:
1516 k = g['stuff']
1517 line = ' ' * 2 + \
1518 green(k) + ': ' + light_yellow(str(all_config[k]))
1519 printNicely(line)
1520 else:
1521 printNicely(red('No such config key.'))
1522 # Print specific config's default value
1523 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'default':
1524 key = g['stuff'].split()[0]
1525 try:
1526 value = get_default_config(key)
1527 line = ' ' * 2 + green(key) + ': ' + light_magenta(value)
1528 printNicely(line)
1529 except:
1530 debug_option()
1531 printNicely(red('Just can not get the default.'))
1532 # Delete specific config key in config file
1533 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'drop':
1534 key = g['stuff'].split()[0]
1535 try:
1536 delete_config(key)
1537 printNicely(green('Config key is dropped.'))
1538 except:
1539 debug_option()
1540 printNicely(red('Just can not drop the key.'))
1541 # Set specific config
1542 elif len(g['stuff'].split()) == 3 and g['stuff'].split()[1] == '=':
1543 key = g['stuff'].split()[0]
1544 value = g['stuff'].split()[-1]
1545 if key == 'THEME' and not validate_theme(value):
1546 printNicely(red('Invalid theme\'s value.'))
1547 return
1548 try:
1549 set_config(key, value)
1550 # Keys that needs to be apply immediately
1551 if key == 'THEME':
1552 c['THEME'] = reload_theme(value, c['THEME'])
1553 g['decorated_name'] = lambda x: color_func(
1554 c['DECORATED_NAME'])('[' + x + ']: ')
1555 elif key == 'PREFIX':
1556 g['PREFIX'] = u2str(emojize(format_prefix(
1557 listname=g['listname'],
1558 keyword=g['keyword']
1559 )))
1560 reload_config()
1561 printNicely(green('Updated successfully.'))
1562 except:
1563 debug_option()
1564 printNicely(red('Just can not set the key.'))
1565 else:
1566 printNicely(light_magenta('Sorry I can\'t understand.'))
1567
1568
1569def help_discover():
1570 """
1571 Discover the world
1572 """
1573 s = ' ' * 2
1574 # Discover the world
1575 usage = '\n'
1576 usage += s + grey(u'\u266A' + ' Discover the world \n')
1577 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
1578 'You can try ' + light_green('trend US') + ' or ' + \
1579 light_green('trend JP Tokyo') + '.\n'
1580 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
1581 light_green('home 7') + ' will show 7 tweets.\n'
1582 usage += s * 2 + light_green('me') + ' will show your latest tweets. ' + \
1583 light_green('me 2') + ' will show your last 2 tweets.\n'
1584 usage += s * 2 + \
1585 light_green('notification') + ' will show your recent notification.\n'
1586 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
1587 light_green('mentions 7') + ' will show 7 mention tweets.\n'
1588 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
1589 magenta('@mdo') + '.\n'
1590 usage += s * 2 + light_green('view @mdo') + \
1591 ' will show ' + magenta('@mdo') + '\'s home.\n'
1592 usage += s * 2 + light_green('s AKB48') + ' will search for "' + \
1593 light_yellow('AKB48') + '" and return 5 newest tweet. ' + \
1594 'Search can be performed with or without hashtag.\n'
1595 printNicely(usage)
1596
1597
1598def help_tweets():
1599 """
1600 Tweets
1601 """
1602 s = ' ' * 2
1603 # Tweet
1604 usage = '\n'
1605 usage += s + grey(u'\u266A' + ' Tweets \n')
1606 usage += s * 2 + light_green('t oops ') + \
1607 'will tweet "' + light_yellow('oops') + '" immediately.\n'
1608 usage += s * 2 + \
1609 light_green('rt 12 ') + ' will retweet to tweet with ' + \
1610 light_yellow('[id=12]') + '.\n'
1611 usage += s * 2 + \
1612 light_green('quote 12 ') + ' will quote the tweet with ' + \
1613 light_yellow('[id=12]') + '. If no extra text is added, ' + \
1614 'the quote will be canceled.\n'
1615 usage += s * 2 + \
1616 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
1617 light_yellow('[id=12]') + '.\n'
1618 usage += s * 2 + light_green('conversation 12') + ' will show the chain of ' + \
1619 'replies prior to the tweet with ' + light_yellow('[id=12]') + '.\n'
1620 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
1621 light_yellow('oops') + '" to the owner of the tweet with ' + \
1622 light_yellow('[id=12]') + '.\n'
1623 usage += s * 2 + light_green('repall 12 oops') + ' will reply "' + \
1624 light_yellow('oops') + '" to all people in the tweet with ' + \
1625 light_yellow('[id=12]') + '.\n'
1626 usage += s * 2 + \
1627 light_green('fav 12 ') + ' will favorite the tweet with ' + \
1628 light_yellow('[id=12]') + '.\n'
1629 usage += s * 2 + \
1630 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
1631 light_yellow('[id=12]') + '.\n'
1632 usage += s * 2 + \
1633 light_green('share 12 ') + ' will get the direct link of the tweet with ' + \
1634 light_yellow('[id=12]') + '.\n'
1635 usage += s * 2 + \
1636 light_green('del 12 ') + ' will delete tweet with ' + \
1637 light_yellow('[id=12]') + '.\n'
1638 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
1639 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
1640 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
1641 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
1642 usage += s * 2 + light_green('pt 12') + ' will add tweet with ' + \
1643 light_yellow('[id=12]') + ' in your Pocket list.\n'
1644 printNicely(usage)
1645
1646
1647def help_messages():
1648 """
1649 Messages
1650 """
1651 s = ' ' * 2
1652 # Direct message
1653 usage = '\n'
1654 usage += s + grey(u'\u266A' + ' Direct messages \n')
1655 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
1656 light_green('inbox 7') + ' will show newest 7 messages.\n'
1657 usage += s * 2 + light_green('thread 2') + ' will show full thread with ' + \
1658 light_yellow('[thread_id=2]') + '.\n'
1659 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
1660 magenta('@dtvd88') + '.\n'
1661 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
1662 light_yellow('[message_id=5]') + '.\n'
1663 printNicely(usage)
1664
1665
1666def help_friends_and_followers():
1667 """
1668 Friends and Followers
1669 """
1670 s = ' ' * 2
1671 # Follower and following
1672 usage = '\n'
1673 usage += s + grey(u'\u266A' + ' Friends and followers \n')
1674 usage += s * 2 + \
1675 light_green('ls fl') + \
1676 ' will list all followers (people who are following you).\n'
1677 usage += s * 2 + \
1678 light_green('ls fr') + \
1679 ' will list all friends (people who you are following).\n'
1680 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
1681 magenta('@dtvd88') + '.\n'
1682 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
1683 magenta('@dtvd88') + '.\n'
1684 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
1685 magenta('@dtvd88') + '.\n'
1686 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
1687 magenta('@dtvd88') + '.\n'
1688 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
1689 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
1690 magenta('@dtvd88') + '.\n'
1691 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
1692 magenta('@dtvd88') + '.\n'
1693 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
1694 magenta('@dtvd88') + ' as a spam account.\n'
1695 printNicely(usage)
1696
1697
1698def help_list():
1699 """
1700 Lists
1701 """
1702 s = ' ' * 2
1703 # Twitter list
1704 usage = '\n'
1705 usage += s + grey(u'\u266A' + ' Twitter list\n')
1706 usage += s * 2 + light_green('list') + \
1707 ' will show all lists you are belong to.\n'
1708 usage += s * 2 + light_green('list home') + \
1709 ' will show timeline of list. You will be asked for list\'s name.\n'
1710 usage += s * 2 + light_green('list all_mem') + \
1711 ' will show list\'s all members.\n'
1712 usage += s * 2 + light_green('list all_sub') + \
1713 ' will show list\'s all subscribers.\n'
1714 usage += s * 2 + light_green('list add') + \
1715 ' will add specific person to a list owned by you.' + \
1716 ' You will be asked for list\'s name and person\'s name.\n'
1717 usage += s * 2 + light_green('list rm') + \
1718 ' will remove specific person from a list owned by you.' + \
1719 ' You will be asked for list\'s name and person\'s name.\n'
1720 usage += s * 2 + light_green('list sub') + \
1721 ' will subscribe you to a specific list.\n'
1722 usage += s * 2 + light_green('list unsub') + \
1723 ' will unsubscribe you from a specific list.\n'
1724 usage += s * 2 + light_green('list own') + \
1725 ' will show all list owned by you.\n'
1726 usage += s * 2 + light_green('list new') + \
1727 ' will create a new list.\n'
1728 usage += s * 2 + light_green('list update') + \
1729 ' will update a list owned by you.\n'
1730 usage += s * 2 + light_green('list del') + \
1731 ' will delete a list owned by you.\n'
1732 printNicely(usage)
1733
1734
1735def help_stream():
1736 """
1737 Stream switch
1738 """
1739 s = ' ' * 2
1740 # Switch
1741 usage = '\n'
1742 usage += s + grey(u'\u266A' + ' Switching streams \n')
1743 usage += s * 2 + light_green('switch public #AKB') + \
1744 ' will switch to public stream and follow "' + \
1745 light_yellow('AKB') + '" keyword.\n'
1746 usage += s * 2 + light_green('switch mine') + \
1747 ' will switch to your personal stream.\n'
1748 usage += s * 2 + light_green('switch mine -f ') + \
1749 ' will prompt to enter the filter.\n'
1750 usage += s * 3 + light_yellow('Only nicks') + \
1751 ' filter will decide nicks will be INCLUDE ONLY.\n'
1752 usage += s * 3 + light_yellow('Ignore nicks') + \
1753 ' filter will decide nicks will be EXCLUDE.\n'
1754 usage += s * 2 + light_green('switch list') + \
1755 ' will switch to a Twitter list\'s stream. You will be asked for list name\n'
1756 printNicely(usage)
1757
1758
1759def help():
1760 """
1761 Help
1762 """
1763 s = ' ' * 2
1764 h, w = os.popen('stty size', 'r').read().split()
1765 # Start
1766 usage = '\n'
1767 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
1768 usage += s + '-' * (int(w) - 4) + '\n'
1769 usage += s + 'You are ' + \
1770 light_yellow('already') + ' on your personal stream.\n'
1771 usage += s + 'Any update from Twitter will show up ' + \
1772 light_yellow('immediately') + '.\n'
1773 usage += s + 'In addition, following commands are available right now:\n'
1774 # Twitter help section
1775 usage += '\n'
1776 usage += s + grey(u'\u266A' + ' Twitter help\n')
1777 usage += s * 2 + light_green('h discover') + \
1778 ' will show help for discover commands.\n'
1779 usage += s * 2 + light_green('h tweets') + \
1780 ' will show help for tweets commands.\n'
1781 usage += s * 2 + light_green('h messages') + \
1782 ' will show help for messages commands.\n'
1783 usage += s * 2 + light_green('h friends_and_followers') + \
1784 ' will show help for friends and followers commands.\n'
1785 usage += s * 2 + light_green('h list') + \
1786 ' will show help for list commands.\n'
1787 usage += s * 2 + light_green('h stream') + \
1788 ' will show help for stream commands.\n'
1789 # Smart shell
1790 usage += '\n'
1791 usage += s + grey(u'\u266A' + ' Smart shell\n')
1792 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1793 'will be evaluate by Python interpreter.\n'
1794 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1795 ' for current month.\n'
1796 # Config
1797 usage += '\n'
1798 usage += s + grey(u'\u266A' + ' Config \n')
1799 usage += s * 2 + light_green('theme') + ' will list available theme. ' + \
1800 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
1801 ' theme immediately.\n'
1802 usage += s * 2 + light_green('config') + ' will list all config.\n'
1803 usage += s * 3 + \
1804 light_green('config ASCII_ART') + ' will output current value of ' +\
1805 light_yellow('ASCII_ART') + ' config key.\n'
1806 usage += s * 3 + \
1807 light_green('config TREND_MAX default') + ' will output default value of ' + \
1808 light_yellow('TREND_MAX') + ' config key.\n'
1809 usage += s * 3 + \
1810 light_green('config CUSTOM_CONFIG drop') + ' will drop ' + \
1811 light_yellow('CUSTOM_CONFIG') + ' config key.\n'
1812 usage += s * 3 + \
1813 light_green('config IMAGE_ON_TERM = true') + ' will set value of ' + \
1814 light_yellow('IMAGE_ON_TERM') + ' config key to ' + \
1815 light_yellow('True') + '.\n'
1816 # Screening
1817 usage += '\n'
1818 usage += s + grey(u'\u266A' + ' Screening \n')
1819 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1820 usage += s * 2 + light_green('p') + ' will pause the stream.\n'
1821 usage += s * 2 + light_green('r') + ' will unpause the stream.\n'
1822 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1823 usage += s * 2 + light_green('v') + ' will show version info.\n'
1824 usage += s * 2 + light_green('q') + ' will quit.\n'
1825 # End
1826 usage += '\n'
1827 usage += s + '-' * (int(w) - 4) + '\n'
1828 usage += s + 'Have fun and hang tight! \n'
1829 # Show help
1830 d = {
1831 'discover': help_discover,
1832 'tweets': help_tweets,
1833 'messages': help_messages,
1834 'friends_and_followers': help_friends_and_followers,
1835 'list': help_list,
1836 'stream': help_stream,
1837 }
1838 if g['stuff']:
1839 d.get(
1840 g['stuff'].strip(),
1841 lambda: printNicely(red('No such command.'))
1842 )()
1843 else:
1844 printNicely(usage)
1845
1846
1847def pause():
1848 """
1849 Pause stream display
1850 """
1851 g['pause'] = True
1852 printNicely(green('Stream is paused'))
1853
1854
1855def replay():
1856 """
1857 Replay stream
1858 """
1859 g['pause'] = False
1860 printNicely(green('Stream is running back now'))
1861
1862
1863def clear():
1864 """
1865 Clear screen
1866 """
1867 os.system('clear')
1868
1869
1870def quit():
1871 """
1872 Exit all
1873 """
1874 try:
1875 save_history()
1876 printNicely(green('See you next time :)'))
1877 except:
1878 pass
1879 sys.exit()
1880
1881
1882def reset():
1883 """
1884 Reset prefix of line
1885 """
1886 if g['reset']:
1887 if c.get('USER_JSON_ERROR'):
1888 printNicely(red('Your ~/.rainbow_config.json is messed up:'))
1889 printNicely(red('>>> ' + c['USER_JSON_ERROR']))
1890 printNicely('')
1891 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
1892 g['reset'] = False
1893 try:
1894 printNicely(str(eval(g['cmd'])))
1895 except Exception:
1896 pass
1897
1898
1899# Command set
1900cmdset = [
1901 'switch',
1902 'trend',
1903 'home',
1904 'notification',
1905 'view',
1906 'mentions',
1907 't',
1908 'rt',
1909 'quote',
1910 'me',
1911 'allrt',
1912 'conversation',
1913 'fav',
1914 'rep',
1915 'repall',
1916 'del',
1917 'ufav',
1918 'share',
1919 's',
1920 'mes',
1921 'show',
1922 'open',
1923 'ls',
1924 'inbox',
1925 'thread',
1926 'trash',
1927 'whois',
1928 'fl',
1929 'ufl',
1930 'mute',
1931 'unmute',
1932 'muting',
1933 'block',
1934 'unblock',
1935 'report',
1936 'list',
1937 'cal',
1938 'config',
1939 'theme',
1940 'h',
1941 'p',
1942 'r',
1943 'c',
1944 'v',
1945 'q',
1946 'pt',
1947]
1948
1949# Handle function set
1950funcset = [
1951 switch,
1952 trend,
1953 home,
1954 notification,
1955 view,
1956 mentions,
1957 tweet,
1958 retweet,
1959 quote,
1960 view_my_tweets,
1961 allretweet,
1962 conversation,
1963 favorite,
1964 reply,
1965 reply_all,
1966 delete,
1967 unfavorite,
1968 share,
1969 search,
1970 message,
1971 show,
1972 urlopen,
1973 ls,
1974 inbox,
1975 thread,
1976 trash,
1977 whois,
1978 follow,
1979 unfollow,
1980 mute,
1981 unmute,
1982 muting,
1983 block,
1984 unblock,
1985 report,
1986 twitterlist,
1987 cal,
1988 config,
1989 theme,
1990 help,
1991 pause,
1992 replay,
1993 clear,
1994 upgrade_center,
1995 quit,
1996 pocket,
1997]
1998
1999
2000def process(cmd):
2001 """
2002 Process switch
2003 """
2004 return dict(zip(cmdset, funcset)).get(cmd, reset)
2005
2006
2007def listen():
2008 """
2009 Listen to user's input
2010 """
2011 d = dict(zip(
2012 cmdset,
2013 [
2014 ['public', 'mine', 'list'], # switch
2015 [], # trend
2016 [], # home
2017 [], # notification
2018 ['@'], # view
2019 [], # mentions
2020 [], # tweet
2021 [], # retweet
2022 [], # quote
2023 [], # view_my_tweets
2024 [], # allretweet
2025 [], # conversation
2026 [], # favorite
2027 [], # reply
2028 [], # reply_all
2029 [], # delete
2030 [], # unfavorite
2031 [], # url
2032 ['#'], # search
2033 ['@'], # message
2034 ['image'], # show image
2035 [''], # open url
2036 ['fl', 'fr'], # list
2037 [], # inbox
2038 [i for i in g['message_threads']], # sent
2039 [], # trash
2040 ['@'], # whois
2041 ['@'], # follow
2042 ['@'], # unfollow
2043 ['@'], # mute
2044 ['@'], # unmute
2045 ['@'], # muting
2046 ['@'], # block
2047 ['@'], # unblock
2048 ['@'], # report
2049 [
2050 'home',
2051 'all_mem',
2052 'all_sub',
2053 'add',
2054 'rm',
2055 'sub',
2056 'unsub',
2057 'own',
2058 'new',
2059 'update',
2060 'del'
2061 ], # list
2062 [], # cal
2063 [key for key in dict(get_all_config())], # config
2064 g['themes'], # theme
2065 [
2066 'discover',
2067 'tweets',
2068 'messages',
2069 'friends_and_followers',
2070 'list',
2071 'stream'
2072 ], # help
2073 [], # pause
2074 [], # reconnect
2075 [], # clear
2076 [], # version
2077 [], # quit
2078 [], # pocket
2079 ]
2080 ))
2081 init_interactive_shell(d)
2082 read_history()
2083 reset()
2084 while True:
2085 try:
2086 # raw_input
2087 if g['prefix']:
2088 # Only use PREFIX as a string with raw_input
2089 line = raw_input(g['decorated_name'](g['PREFIX']))
2090 else:
2091 line = raw_input()
2092 # Save cmd to compare with readline buffer
2093 g['cmd'] = line.strip()
2094 # Get short cmd to pass to handle function
2095 try:
2096 cmd = line.split()[0]
2097 except:
2098 cmd = ''
2099 # Lock the semaphore
2100 c['lock'] = True
2101 # Save cmd to global variable and call process
2102 g['stuff'] = ' '.join(line.split()[1:])
2103 # Check tweet length
2104 # Process the command
2105 process(cmd)()
2106 # Not re-display
2107 if cmd in ['switch', 't', 'rt', 'rep']:
2108 g['prefix'] = False
2109 else:
2110 g['prefix'] = True
2111 except EOFError:
2112 printNicely('')
2113 except TwitterHTTPError as e:
2114 detail_twitter_error(e)
2115 except Exception:
2116 debug_option()
2117 printNicely(red('OMG something is wrong with Twitter API right now.'))
2118 finally:
2119 # Release the semaphore lock
2120 c['lock'] = False
2121
2122
2123def reconn_notice():
2124 """
2125 Notice when Hangup or Timeout
2126 """
2127 guide = light_magenta('You can use ') + \
2128 light_green('switch') + \
2129 light_magenta(' command to return to your stream.\n')
2130 guide += light_magenta('Type ') + \
2131 light_green('h stream') + \
2132 light_magenta(' for more details.')
2133 printNicely(guide)
2134 sys.stdout.write(g['decorated_name'](g['PREFIX']))
2135 sys.stdout.flush()
2136
2137
2138def stream(domain, args, name='Rainbow Stream'):
2139 """
2140 Track the stream
2141 """
2142 # The Logo
2143 art_dict = {
2144 c['USER_DOMAIN']: name,
2145 c['PUBLIC_DOMAIN']: args.track_keywords or 'Global',
2146 c['SITE_DOMAIN']: name,
2147 }
2148 if c['ASCII_ART']:
2149 ascii_art(art_dict.get(domain, name))
2150 # These arguments are optional:
2151 stream_args = dict(
2152 timeout=0.5, # To check g['stream_stop'] after each 0.5 s
2153 block=True,
2154 heartbeat_timeout=c['HEARTBEAT_TIMEOUT'] * 60)
2155 # Track keyword
2156 query_args = dict()
2157 if args.track_keywords:
2158 query_args['track'] = args.track_keywords
2159 # Get stream
2160 stream = TwitterStream(
2161 auth=authen(),
2162 domain=domain,
2163 **stream_args)
2164 try:
2165 if domain == c['USER_DOMAIN']:
2166 tweet_iter = stream.user(**query_args)
2167 elif domain == c['SITE_DOMAIN']:
2168 tweet_iter = stream.site(**query_args)
2169 else:
2170 if args.track_keywords:
2171 tweet_iter = stream.statuses.filter(**query_args)
2172 else:
2173 tweet_iter = stream.statuses.sample()
2174 # Block new stream until other one exits
2175 StreamLock.acquire()
2176 g['stream_stop'] = False
2177 last_tweet_time = time.time()
2178 for tweet in tweet_iter:
2179 if tweet is None:
2180 printNicely('-- None --')
2181 elif tweet is Timeout:
2182 # Because the stream check for each 0.3s
2183 # so we shouldn't output anything here
2184 if(g['stream_stop']):
2185 StreamLock.release()
2186 break
2187 elif tweet is HeartbeatTimeout:
2188 printNicely('-- Heartbeat Timeout --')
2189 reconn_notice()
2190 StreamLock.release()
2191 break
2192 elif tweet is Hangup:
2193 printNicely('-- Hangup --')
2194 reconn_notice()
2195 StreamLock.release()
2196 break
2197 elif tweet.get('text'):
2198 # Slow down the stream by STREAM_DELAY config key
2199 if time.time() - last_tweet_time < c['STREAM_DELAY']:
2200 continue
2201 last_tweet_time = time.time()
2202 # Check the semaphore pause and lock (stream process only)
2203 if g['pause']:
2204 continue
2205 while c['lock']:
2206 time.sleep(0.5)
2207 # Draw the tweet
2208 draw(
2209 t=tweet,
2210 keyword=args.track_keywords,
2211 humanize=False,
2212 fil=args.filter,
2213 ig=args.ignore,
2214 )
2215 # Current readline buffer
2216 current_buffer = readline.get_line_buffer().strip()
2217 # There is an unexpected behaviour in MacOSX readline + Python 2:
2218 # after completely delete a word after typing it,
2219 # somehow readline buffer still contains
2220 # the 1st character of that word
2221 if current_buffer and g['cmd'] != current_buffer:
2222 sys.stdout.write(
2223 g['decorated_name'](g['PREFIX']) + current_buffer)
2224 sys.stdout.flush()
2225 elif not c['HIDE_PROMPT']:
2226 sys.stdout.write(g['decorated_name'](g['PREFIX']))
2227 sys.stdout.flush()
2228 elif tweet.get('direct_message'):
2229 # Check the semaphore pause and lock (stream process only)
2230 if g['pause']:
2231 continue
2232 while c['lock']:
2233 time.sleep(0.5)
2234 print_message(tweet['direct_message'])
2235 elif tweet.get('event'):
2236 c['events'].append(tweet)
2237 print_event(tweet)
2238 except TwitterHTTPError as e:
2239 printNicely('')
2240 printNicely(
2241 magenta('We have connection problem with twitter stream API right now :('))
2242 detail_twitter_error(e)
2243 sys.stdout.write(g['decorated_name'](g['PREFIX']))
2244 sys.stdout.flush()
2245 except (URLError):
2246 printNicely(
2247 magenta('There seems to be a connection problem.'))
2248 save_history()
2249 sys.exit()
2250
2251
2252def spawn_public_stream(args, keyword=None):
2253 """
2254 Spawn a new public stream
2255 """
2256 # Only set keyword if specified
2257 if keyword:
2258 if keyword[0] == '#':
2259 keyword = keyword[1:]
2260 args.track_keywords = keyword
2261 g['keyword'] = keyword
2262 else:
2263 g['keyword'] = 'Global'
2264 g['PREFIX'] = u2str(emojize(format_prefix(keyword=g['keyword'])))
2265 g['listname'] = ''
2266 # Start new thread
2267 th = threading.Thread(
2268 target=stream,
2269 args=(
2270 c['PUBLIC_DOMAIN'],
2271 args))
2272 th.daemon = True
2273 th.start()
2274
2275
2276def spawn_list_stream(args, stuff=None):
2277 """
2278 Spawn a new list stream
2279 """
2280 try:
2281 owner, slug = check_slug(stuff)
2282 except:
2283 owner, slug = get_slug()
2284
2285 # Force python 2 not redraw readline buffer
2286 listname = '/'.join([owner, slug])
2287 # Set the listname variable
2288 # and reset tracked keyword
2289 g['listname'] = listname
2290 g['keyword'] = ''
2291 g['PREFIX'] = g['cmd'] = u2str(emojize(format_prefix(
2292 listname=g['listname']
2293 )))
2294 printNicely(light_yellow('getting list members ...'))
2295 # Get members
2296 t = Twitter(auth=authen())
2297 members = []
2298 next_cursor = -1
2299 while next_cursor != 0:
2300 m = t.lists.members(
2301 slug=slug,
2302 owner_screen_name=owner,
2303 cursor=next_cursor,
2304 include_entities=False)
2305 for u in m['users']:
2306 members.append('@' + u['screen_name'])
2307 next_cursor = m['next_cursor']
2308 printNicely(light_yellow('... done.'))
2309 # Build thread filter array
2310 args.filter = members
2311 # Start new thread
2312 th = threading.Thread(
2313 target=stream,
2314 args=(
2315 c['USER_DOMAIN'],
2316 args,
2317 slug))
2318 th.daemon = True
2319 th.start()
2320 printNicely('')
2321 if args.filter:
2322 printNicely(cyan('Include: ' + str(len(args.filter)) + ' people.'))
2323 if args.ignore:
2324 printNicely(red('Ignore: ' + str(len(args.ignore)) + ' people.'))
2325 printNicely('')
2326
2327
2328def spawn_personal_stream(args, stuff=None):
2329 """
2330 Spawn a new personal stream
2331 """
2332 # Reset the tracked keyword and listname
2333 g['keyword'] = g['listname'] = ''
2334 # Reset prefix
2335 g['PREFIX'] = u2str(emojize(format_prefix()))
2336 # Start new thread
2337 th = threading.Thread(
2338 target=stream,
2339 args=(
2340 c['USER_DOMAIN'],
2341 args,
2342 g['original_name']))
2343 th.daemon = True
2344 th.start()
2345
2346
2347def fly():
2348 """
2349 Main function
2350 """
2351 # Initial
2352 args = parse_arguments()
2353 try:
2354 proxy_connect(args)
2355 init(args)
2356 # Twitter API connection problem
2357 except TwitterHTTPError as e:
2358 printNicely('')
2359 printNicely(
2360 magenta('We have connection problem with twitter REST API right now :('))
2361 detail_twitter_error(e)
2362 save_history()
2363 sys.exit()
2364 # Proxy connection problem
2365 except (socks.ProxyConnectionError, URLError):
2366 printNicely(
2367 magenta('There seems to be a connection problem.'))
2368 printNicely(
2369 magenta('You might want to check your proxy settings (host, port and type)!'))
2370 save_history()
2371 sys.exit()
2372
2373 # Spawn stream thread
2374 target = args.stream.split()[0]
2375 if target == 'mine':
2376 spawn_personal_stream(args)
2377 else:
2378 try:
2379 stuff = args.stream.split()[1]
2380 except:
2381 stuff = None
2382 spawn_dict = {
2383 'public': spawn_public_stream,
2384 'list': spawn_list_stream,
2385 }
2386 spawn_dict.get(target)(args, stuff)
2387
2388 # Start listen process
2389 time.sleep(0.5)
2390 g['reset'] = True
2391 g['prefix'] = True
2392 listen()