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