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