133a31e9e65ee4bbb9e6053fde812a2d9ecff903
[rainbowstream.git] / rainbowstream / rainbow.py
1 """
2 Colorful user's timeline stream
3 """
4 import os
5 import os.path
6 import sys
7 import signal
8 import argparse
9 import time
10 import threading
11 import requests
12 import webbrowser
13
14 from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup
15 from twitter.api import *
16 from twitter.oauth import OAuth, read_token_file
17 from twitter.oauth_dance import oauth_dance
18 from twitter.util import printNicely
19
20 from .draw import *
21 from .colors import *
22 from .config import *
23 from .consumer import *
24 from .interactive import *
25 from .c_image import *
26 from .py3patch import *
27
28 # Global values
29 g = {}
30
31 # Lock for streams
32 StreamLock = threading.Lock()
33
34
35 def parse_arguments():
36 """
37 Parse the arguments
38 """
39 parser = argparse.ArgumentParser(description=__doc__ or "")
40 parser.add_argument(
41 '-to',
42 '--timeout',
43 help='Timeout for the stream (seconds).')
44 parser.add_argument(
45 '-ht',
46 '--heartbeat-timeout',
47 help='Set heartbeat timeout.',
48 default=90)
49 parser.add_argument(
50 '-nb',
51 '--no-block',
52 action='store_true',
53 help='Set stream to non-blocking.')
54 parser.add_argument(
55 '-tt',
56 '--track-keywords',
57 help='Search the stream for specific text.')
58 parser.add_argument(
59 '-fil',
60 '--filter',
61 help='Filter specific screen_name.')
62 parser.add_argument(
63 '-ig',
64 '--ignore',
65 help='Ignore specific screen_name.')
66 parser.add_argument(
67 '-iot',
68 '--image-on-term',
69 action='store_true',
70 help='Display all image on terminal.')
71 return parser.parse_args()
72
73
74 def authen():
75 """
76 Authenticate with Twitter OAuth
77 """
78 # When using rainbow stream you must authorize.
79 twitter_credential = os.environ.get(
80 'HOME',
81 os.environ.get(
82 'USERPROFILE',
83 '')) + os.sep + '.rainbow_oauth'
84 if not os.path.exists(twitter_credential):
85 oauth_dance("Rainbow Stream",
86 CONSUMER_KEY,
87 CONSUMER_SECRET,
88 twitter_credential)
89 oauth_token, oauth_token_secret = read_token_file(twitter_credential)
90 return OAuth(
91 oauth_token,
92 oauth_token_secret,
93 CONSUMER_KEY,
94 CONSUMER_SECRET)
95
96
97 def build_mute_dict(dict_data=False):
98 """
99 Build muting list
100 """
101 t = Twitter(auth=authen())
102 # Init cursor
103 next_cursor = -1
104 screen_name_list = []
105 name_list = []
106 # Cursor loop
107 while next_cursor != 0:
108 list = t.mutes.users.list(
109 screen_name=g['original_name'],
110 cursor=next_cursor,
111 skip_status=True,
112 include_entities=False,
113 )
114 screen_name_list += ['@' + u['screen_name'] for u in list['users']]
115 name_list += [u['name'] for u in list['users']]
116 next_cursor = list['next_cursor']
117 # Return dict or list
118 if dict_data:
119 return dict(zip(screen_name_list, name_list))
120 else:
121 return screen_name_list
122
123
124 def init(args):
125 """
126 Init function
127 """
128 # Handle Ctrl C
129 ctrl_c_handler = lambda signum, frame: quit()
130 signal.signal(signal.SIGINT, ctrl_c_handler)
131 # Get name
132 t = Twitter(auth=authen())
133 name = '@' + t.account.verify_credentials()['screen_name']
134 if not get_config('PREFIX'):
135 set_config('PREFIX', name)
136 g['original_name'] = name[1:]
137 g['decorated_name'] = lambda x: color_func(
138 c['DECORATED_NAME'])('[' + x + ']: ')
139 # Theme init
140 files = os.listdir(os.path.dirname(__file__) + '/colorset')
141 themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
142 g['themes'] = themes
143 # Startup cmd
144 g['cmd'] = ''
145 # Semaphore init
146 c['lock'] = False
147 c['pause'] = False
148 # Init tweet dict and message dict
149 c['tweet_dict'] = []
150 c['message_dict'] = []
151 # Image on term
152 c['IMAGE_ON_TERM'] = args.image_on_term
153 set_config('IMAGE_ON_TERM', str(c['IMAGE_ON_TERM']))
154 # Mute dict
155 c['IGNORE_LIST'] += build_mute_dict()
156
157
158 def switch():
159 """
160 Switch stream
161 """
162 try:
163 target = g['stuff'].split()[0]
164 # Filter and ignore
165 args = parse_arguments()
166 try:
167 if g['stuff'].split()[-1] == '-f':
168 guide = 'To ignore an option, just hit Enter key.'
169 printNicely(light_magenta(guide))
170 only = raw_input('Only nicks [Ex: @xxx,@yy]: ')
171 ignore = raw_input('Ignore nicks [Ex: @xxx,@yy]: ')
172 args.filter = filter(None, only.split(','))
173 args.ignore = filter(None, ignore.split(','))
174 elif g['stuff'].split()[-1] == '-d':
175 args.filter = c['ONLY_LIST']
176 args.ignore = c['IGNORE_LIST']
177 except:
178 printNicely(red('Sorry, wrong format.'))
179 return
180 # Public stream
181 if target == 'public':
182 keyword = g['stuff'].split()[1]
183 if keyword[0] == '#':
184 keyword = keyword[1:]
185 # Kill old thread
186 g['stream_stop'] = True
187 args.track_keywords = keyword
188 # Start new thread
189 th = threading.Thread(
190 target=stream,
191 args=(
192 c['PUBLIC_DOMAIN'],
193 args))
194 th.daemon = True
195 th.start()
196 # Personal stream
197 elif target == 'mine':
198 # Kill old thread
199 g['stream_stop'] = True
200 # Start new thread
201 th = threading.Thread(
202 target=stream,
203 args=(
204 c['USER_DOMAIN'],
205 args,
206 g['original_name']))
207 th.daemon = True
208 th.start()
209 printNicely('')
210 if args.filter:
211 printNicely(cyan('Only: ' + str(args.filter)))
212 if args.ignore:
213 printNicely(red('Ignore: ' + str(args.ignore)))
214 printNicely('')
215 except:
216 printNicely(red('Sorry I can\'t understand.'))
217
218
219 def trend():
220 """
221 Trend
222 """
223 t = Twitter(auth=authen())
224 # Get country and town
225 try:
226 country = g['stuff'].split()[0]
227 except:
228 country = ''
229 try:
230 town = g['stuff'].split()[1]
231 except:
232 town = ''
233 avail = t.trends.available()
234 # World wide
235 if not country:
236 trends = t.trends.place(_id=1)[0]['trends']
237 print_trends(trends)
238 else:
239 for location in avail:
240 # Search for country and Town
241 if town:
242 if location['countryCode'] == country \
243 and location['placeType']['name'] == 'Town' \
244 and location['name'] == town:
245 trends = t.trends.place(_id=location['woeid'])[0]['trends']
246 print_trends(trends)
247 # Search for country only
248 else:
249 if location['countryCode'] == country \
250 and location['placeType']['name'] == 'Country':
251 trends = t.trends.place(_id=location['woeid'])[0]['trends']
252 print_trends(trends)
253
254
255 def home():
256 """
257 Home
258 """
259 t = Twitter(auth=authen())
260 num = c['HOME_TWEET_NUM']
261 if g['stuff'].isdigit():
262 num = int(g['stuff'])
263 for tweet in reversed(t.statuses.home_timeline(count=num)):
264 draw(t=tweet)
265 printNicely('')
266
267
268 def view():
269 """
270 Friend view
271 """
272 t = Twitter(auth=authen())
273 user = g['stuff'].split()[0]
274 if user[0] == '@':
275 try:
276 num = int(g['stuff'].split()[1])
277 except:
278 num = c['HOME_TWEET_NUM']
279 for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])):
280 draw(t=tweet)
281 printNicely('')
282 else:
283 printNicely(red('A name should begin with a \'@\''))
284
285
286 def mentions():
287 """
288 Mentions timeline
289 """
290 t = Twitter(auth=authen())
291 num = c['HOME_TWEET_NUM']
292 if g['stuff'].isdigit():
293 num = int(g['stuff'])
294 for tweet in reversed(t.statuses.mentions_timeline(count=num)):
295 draw(t=tweet)
296 printNicely('')
297
298
299 def tweet():
300 """
301 Tweet
302 """
303 t = Twitter(auth=authen())
304 t.statuses.update(status=g['stuff'])
305
306
307 def retweet():
308 """
309 ReTweet
310 """
311 t = Twitter(auth=authen())
312 try:
313 id = int(g['stuff'].split()[0])
314 except:
315 printNicely(red('Sorry I can\'t understand.'))
316 return
317 tid = c['tweet_dict'][id]
318 t.statuses.retweet(id=tid, include_entities=False, trim_user=True)
319
320
321 def quote():
322 """
323 Quote a tweet
324 """
325 t = Twitter(auth=authen())
326 try:
327 id = int(g['stuff'].split()[0])
328 except:
329 printNicely(red('Sorry I can\'t understand.'))
330 return
331 tid = c['tweet_dict'][id]
332 tweet = t.statuses.show(id=tid)
333 screen_name = tweet['user']['screen_name']
334 text = tweet['text']
335 quote = '\"@' + screen_name + ': ' + text + '\"'
336 quote = quote.encode('utf8')
337 notice = light_magenta('Compose mode ')
338 notice += light_yellow('(Enter nothing will cancel the quote)')
339 notice += light_magenta(':')
340 printNicely(notice)
341 extra = raw_input(quote)
342 if extra:
343 t.statuses.update(status=quote + extra)
344 else:
345 printNicely(light_magenta('No text added.'))
346
347
348 def allretweet():
349 """
350 List all retweet
351 """
352 t = Twitter(auth=authen())
353 # Get rainbow id
354 try:
355 id = int(g['stuff'].split()[0])
356 except:
357 printNicely(red('Sorry I can\'t understand.'))
358 return
359 tid = c['tweet_dict'][id]
360 # Get display num if exist
361 try:
362 num = int(g['stuff'].split()[1])
363 except:
364 num = c['RETWEETS_SHOW_NUM']
365 # Get result and display
366 rt_ary = t.statuses.retweets(id=tid, count=num)
367 if not rt_ary:
368 printNicely(magenta('This tweet has no retweet.'))
369 return
370 for tweet in reversed(rt_ary):
371 draw(t=tweet)
372 printNicely('')
373
374
375 def favorite():
376 """
377 Favorite
378 """
379 t = Twitter(auth=authen())
380 try:
381 id = int(g['stuff'].split()[0])
382 except:
383 printNicely(red('Sorry I can\'t understand.'))
384 return
385 tid = c['tweet_dict'][id]
386 t.favorites.create(_id=tid, include_entities=False)
387 printNicely(green('Favorited.'))
388 draw(t.statuses.show(id=tid))
389 printNicely('')
390
391
392 def reply():
393 """
394 Reply
395 """
396 t = Twitter(auth=authen())
397 try:
398 id = int(g['stuff'].split()[0])
399 except:
400 printNicely(red('Sorry I can\'t understand.'))
401 return
402 tid = c['tweet_dict'][id]
403 user = t.statuses.show(id=tid)['user']['screen_name']
404 status = ' '.join(g['stuff'].split()[1:])
405 status = '@' + user + ' ' + unc(status)
406 t.statuses.update(status=status, in_reply_to_status_id=tid)
407
408
409 def delete():
410 """
411 Delete
412 """
413 t = Twitter(auth=authen())
414 try:
415 id = int(g['stuff'].split()[0])
416 except:
417 printNicely(red('Sorry I can\'t understand.'))
418 return
419 tid = c['tweet_dict'][id]
420 t.statuses.destroy(id=tid)
421 printNicely(green('Okay it\'s gone.'))
422
423
424 def unfavorite():
425 """
426 Unfavorite
427 """
428 t = Twitter(auth=authen())
429 try:
430 id = int(g['stuff'].split()[0])
431 except:
432 printNicely(red('Sorry I can\'t understand.'))
433 return
434 tid = c['tweet_dict'][id]
435 t.favorites.destroy(_id=tid)
436 printNicely(green('Okay it\'s unfavorited.'))
437 draw(t.statuses.show(id=tid))
438 printNicely('')
439
440
441 def search():
442 """
443 Search
444 """
445 t = Twitter(auth=authen())
446 g['stuff'] = g['stuff'].strip()
447 rel = t.search.tweets(q=g['stuff'])['statuses']
448 if rel:
449 printNicely('Newest tweets:')
450 for i in reversed(xrange(c['SEARCH_MAX_RECORD'])):
451 draw(t=rel[i],
452 keyword=g['stuff'])
453 printNicely('')
454 else:
455 printNicely(magenta('I\'m afraid there is no result'))
456
457
458 def message():
459 """
460 Send a direct message
461 """
462 t = Twitter(auth=authen())
463 user = g['stuff'].split()[0]
464 if user[0].startswith('@'):
465 try:
466 content = g['stuff'].split()[1]
467 except:
468 printNicely(red('Sorry I can\'t understand.'))
469 t.direct_messages.new(
470 screen_name=user[1:],
471 text=content
472 )
473 printNicely(green('Message sent.'))
474 else:
475 printNicely(red('A name should begin with a \'@\''))
476
477
478 def show():
479 """
480 Show image
481 """
482 t = Twitter(auth=authen())
483 try:
484 target = g['stuff'].split()[0]
485 if target != 'image':
486 return
487 id = int(g['stuff'].split()[1])
488 tid = c['tweet_dict'][id]
489 tweet = t.statuses.show(id=tid)
490 media = tweet['entities']['media']
491 for m in media:
492 res = requests.get(m['media_url'])
493 img = Image.open(BytesIO(res.content))
494 img.show()
495 except:
496 printNicely(red('Sorry I can\'t show this image.'))
497
498
499 def urlopen():
500 """
501 Open url
502 """
503 t = Twitter(auth=authen())
504 try:
505 if not g['stuff'].isdigit():
506 return
507 tid = c['tweet_dict'][int(g['stuff'])]
508 tweet = t.statuses.show(id=tid)
509 link_ary = [
510 u for u in tweet['text'].split() if u.startswith('http://')]
511 if not link_ary:
512 printNicely(light_magenta('No url here @.@!'))
513 return
514 for link in link_ary:
515 webbrowser.open(link)
516 except:
517 printNicely(red('Sorry I can\'t open url in this tweet.'))
518
519
520 def ls():
521 """
522 List friends for followers
523 """
524 t = Twitter(auth=authen())
525 # Get name
526 try:
527 name = g['stuff'].split()[1]
528 if name.startswith('@'):
529 name = name[1:]
530 else:
531 printNicely(red('A name should begin with a \'@\''))
532 raise Exception('Invalid name')
533 except:
534 name = g['original_name']
535 # Get list followers or friends
536 try:
537 target = g['stuff'].split()[0]
538 except:
539 printNicely(red('Omg some syntax is wrong.'))
540 # Init cursor
541 d = {'fl': 'followers', 'fr': 'friends'}
542 next_cursor = -1
543 rel = {}
544 # Cursor loop
545 while next_cursor != 0:
546 list = getattr(t, d[target]).list(
547 screen_name=name,
548 cursor=next_cursor,
549 skip_status=True,
550 include_entities=False,
551 )
552 for u in list['users']:
553 rel[u['name']] = '@' + u['screen_name']
554 next_cursor = list['next_cursor']
555 # Print out result
556 printNicely('All: ' + str(len(rel)) + ' ' + d[target] + '.')
557 for name in rel:
558 user = ' ' + cycle_color(name)
559 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
560 printNicely(user)
561
562
563 def inbox():
564 """
565 Inbox direct messages
566 """
567 t = Twitter(auth=authen())
568 num = c['MESSAGES_DISPLAY']
569 rel = []
570 if g['stuff'].isdigit():
571 num = g['stuff']
572 cur_page = 1
573 # Max message per page is 20 so we have to loop
574 while num > 20:
575 rel = rel + t.direct_messages(
576 count=20,
577 page=cur_page,
578 include_entities=False,
579 skip_status=False
580 )
581 num -= 20
582 cur_page += 1
583 rel = rel + t.direct_messages(
584 count=num,
585 page=cur_page,
586 include_entities=False,
587 skip_status=False
588 )
589 # Display
590 printNicely('Inbox: newest ' + str(len(rel)) + ' messages.')
591 for m in reversed(rel):
592 print_message(m)
593 printNicely('')
594
595
596 def sent():
597 """
598 Sent direct messages
599 """
600 t = Twitter(auth=authen())
601 num = c['MESSAGES_DISPLAY']
602 rel = []
603 if g['stuff'].isdigit():
604 num = int(g['stuff'])
605 cur_page = 1
606 # Max message per page is 20 so we have to loop
607 while num > 20:
608 rel = rel + t.direct_messages.sent(
609 count=20,
610 page=cur_page,
611 include_entities=False,
612 skip_status=False
613 )
614 num -= 20
615 cur_page += 1
616 rel = rel + t.direct_messages.sent(
617 count=num,
618 page=cur_page,
619 include_entities=False,
620 skip_status=False
621 )
622 # Display
623 printNicely('Sent: newest ' + str(len(rel)) + ' messages.')
624 for m in reversed(rel):
625 print_message(m)
626 printNicely('')
627
628
629 def trash():
630 """
631 Remove message
632 """
633 t = Twitter(auth=authen())
634 try:
635 id = int(g['stuff'].split()[0])
636 except:
637 printNicely(red('Sorry I can\'t understand.'))
638 mid = c['message_dict'][id]
639 t.direct_messages.destroy(id=mid)
640 printNicely(green('Message deleted.'))
641
642
643 def whois():
644 """
645 Show profile of a specific user
646 """
647 t = Twitter(auth=authen())
648 screen_name = g['stuff'].split()[0]
649 if screen_name.startswith('@'):
650 try:
651 user = t.users.show(
652 screen_name=screen_name[1:],
653 include_entities=False)
654 show_profile(user)
655 except:
656 printNicely(red('Omg no user.'))
657 else:
658 printNicely(red('A name should begin with a \'@\''))
659
660
661 def follow():
662 """
663 Follow a user
664 """
665 t = Twitter(auth=authen())
666 screen_name = g['stuff'].split()[0]
667 if screen_name.startswith('@'):
668 t.friendships.create(screen_name=screen_name[1:], follow=True)
669 printNicely(green('You are following ' + screen_name + ' now!'))
670 else:
671 printNicely(red('A name should begin with a \'@\''))
672
673
674 def unfollow():
675 """
676 Unfollow a user
677 """
678 t = Twitter(auth=authen())
679 screen_name = g['stuff'].split()[0]
680 if screen_name.startswith('@'):
681 t.friendships.destroy(
682 screen_name=screen_name[1:],
683 include_entities=False)
684 printNicely(green('Unfollow ' + screen_name + ' success!'))
685 else:
686 printNicely(red('A name should begin with a \'@\''))
687
688
689 def mute():
690 """
691 Mute a user
692 """
693 t = Twitter(auth=authen())
694 try:
695 screen_name = g['stuff'].split()[0]
696 except:
697 printNicely(red('A name should be specified. '))
698 return
699 if screen_name.startswith('@'):
700 try:
701 rel = t.mutes.users.create(screen_name=screen_name[1:])
702 if isinstance(rel, dict):
703 printNicely(green(screen_name + ' is muted.'))
704 c['IGNORE_LIST'] += [unc(screen_name)]
705 c['IGNORE_LIST'] = list(set(c['IGNORE_LIST']))
706 else:
707 printNicely(red(rel))
708 except:
709 printNicely(red('Something is wrong, can not mute now :('))
710 else:
711 printNicely(red('A name should begin with a \'@\''))
712
713
714 def unmute():
715 """
716 Unmute a user
717 """
718 t = Twitter(auth=authen())
719 try:
720 screen_name = g['stuff'].split()[0]
721 except:
722 printNicely(red('A name should be specified. '))
723 return
724 if screen_name.startswith('@'):
725 try:
726 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
727 if isinstance(rel, dict):
728 printNicely(green(screen_name + ' is unmuted.'))
729 c['IGNORE_LIST'].remove(screen_name)
730 else:
731 printNicely(red(rel))
732 except:
733 printNicely(red('Maybe you are not muting this person ?'))
734 else:
735 printNicely(red('A name should begin with a \'@\''))
736
737
738 def muting():
739 """
740 List muting user
741 """
742 # Get dict of muting users
743 md = build_mute_dict(dict_data=True)
744 printNicely('All: ' + str(len(md)) + ' people.')
745 for name in md:
746 user = ' ' + cycle_color(md[name])
747 user += color_func(c['TWEET']['nick'])(' ' + name + ' ')
748 printNicely(user)
749 # Update from Twitter
750 c['IGNORE_LIST'] = [n for n in md]
751
752
753 def block():
754 """
755 Block a user
756 """
757 t = Twitter(auth=authen())
758 screen_name = g['stuff'].split()[0]
759 if screen_name.startswith('@'):
760 t.blocks.create(
761 screen_name=screen_name[1:],
762 include_entities=False,
763 skip_status=True)
764 printNicely(green('You blocked ' + screen_name + '.'))
765 else:
766 printNicely(red('A name should begin with a \'@\''))
767
768
769 def unblock():
770 """
771 Unblock a user
772 """
773 t = Twitter(auth=authen())
774 screen_name = g['stuff'].split()[0]
775 if screen_name.startswith('@'):
776 t.blocks.destroy(
777 screen_name=screen_name[1:],
778 include_entities=False,
779 skip_status=True)
780 printNicely(green('Unblock ' + screen_name + ' success!'))
781 else:
782 printNicely(red('A name should begin with a \'@\''))
783
784
785 def report():
786 """
787 Report a user as a spam account
788 """
789 t = Twitter(auth=authen())
790 screen_name = g['stuff'].split()[0]
791 if screen_name.startswith('@'):
792 t.users.report_spam(
793 screen_name=screen_name[1:])
794 printNicely(green('You reported ' + screen_name + '.'))
795 else:
796 printNicely(red('Sorry I can\'t understand.'))
797
798
799 def get_slug():
800 """
801 Get Slug Decorator
802 """
803 # Get list name
804 list_name = raw_input(light_magenta('Give me the list\'s name: '))
805 # Get list name and owner
806 try:
807 owner, slug = list_name.split('/')
808 if slug.startswith('@'):
809 slug = slug[1:]
810 return owner, slug
811 except:
812 printNicely(
813 light_magenta('List name should follow "@owner/list_name" format.'))
814 raise Exception('Wrong list name')
815
816
817 def show_lists(t):
818 """
819 List list
820 """
821 rel = t.lists.list(screen_name=g['original_name'])
822 if rel:
823 print_list(rel)
824 else:
825 printNicely(light_magenta('You belong to no lists :)'))
826
827
828 def list_home(t):
829 """
830 List home
831 """
832 owner, slug = get_slug()
833 res = t.lists.statuses(
834 slug=slug,
835 owner_screen_name=owner,
836 count=c['LIST_MAX'],
837 include_entities=False)
838 for tweet in res:
839 draw(t=tweet)
840 printNicely('')
841
842
843 def list_members(t):
844 """
845 List members
846 """
847 owner, slug = get_slug()
848 # Get members
849 rel = {}
850 next_cursor = -1
851 while next_cursor != 0:
852 m = t.lists.members(
853 slug=slug,
854 owner_screen_name=owner,
855 cursor=next_cursor,
856 include_entities=False)
857 for u in m['users']:
858 rel[u['name']] = '@' + u['screen_name']
859 next_cursor = m['next_cursor']
860 printNicely('All: ' + str(len(rel)) + ' members.')
861 for name in rel:
862 user = ' ' + cycle_color(name)
863 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
864 printNicely(user)
865
866
867 def list_subscribers(t):
868 """
869 List subscribers
870 """
871 owner, slug = get_slug()
872 # Get subscribers
873 rel = {}
874 next_cursor = -1
875 while next_cursor != 0:
876 m = t.lists.subscribers(
877 slug=slug,
878 owner_screen_name=owner,
879 cursor=next_cursor,
880 include_entities=False)
881 for u in m['users']:
882 rel[u['name']] = '@' + u['screen_name']
883 next_cursor = m['next_cursor']
884 printNicely('All: ' + str(len(rel)) + ' subscribers.')
885 for name in rel:
886 user = ' ' + cycle_color(name)
887 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
888 printNicely(user)
889
890
891 def list_add(t):
892 """
893 Add specific user to a list
894 """
895 owner, slug = get_slug()
896 # Add
897 user_name = raw_input(light_magenta('Give me name of the newbie: '))
898 if user_name.startswith('@'):
899 user_name = user_name[1:]
900 try:
901 t.lists.members.create(
902 slug=slug,
903 owner_screen_name=owner,
904 screen_name=user_name)
905 printNicely(green('Added.'))
906 except:
907 printNicely(light_magenta('I\'m sorry we can not add him/her.'))
908
909
910 def list_remove(t):
911 """
912 Remove specific user from a list
913 """
914 owner, slug = get_slug()
915 # Remove
916 user_name = raw_input(light_magenta('Give me name of the unlucky one: '))
917 if user_name.startswith('@'):
918 user_name = user_name[1:]
919 try:
920 t.lists.members.destroy(
921 slug=slug,
922 owner_screen_name=owner,
923 screen_name=user_name)
924 printNicely(green('Gone.'))
925 except:
926 printNicely(light_magenta('I\'m sorry we can not remove him/her.'))
927
928
929 def list_subscribe(t):
930 """
931 Subscribe to a list
932 """
933 owner, slug = get_slug()
934 # Subscribe
935 try:
936 t.lists.subscribers.create(
937 slug=slug,
938 owner_screen_name=owner)
939 printNicely(green('Done.'))
940 except:
941 printNicely(
942 light_magenta('I\'m sorry you can not subscribe to this list.'))
943
944
945 def list_unsubscribe(t):
946 """
947 Unsubscribe a list
948 """
949 owner, slug = get_slug()
950 # Subscribe
951 try:
952 t.lists.subscribers.destroy(
953 slug=slug,
954 owner_screen_name=owner)
955 printNicely(green('Done.'))
956 except:
957 printNicely(
958 light_magenta('I\'m sorry you can not unsubscribe to this list.'))
959
960
961 def list_own(t):
962 """
963 List own
964 """
965 rel = []
966 next_cursor = -1
967 while next_cursor != 0:
968 res = t.lists.ownerships(
969 screen_name=g['original_name'],
970 cursor=next_cursor)
971 rel += res['lists']
972 next_cursor = res['next_cursor']
973 if rel:
974 print_list(rel)
975 else:
976 printNicely(light_magenta('You own no lists :)'))
977
978
979 def list_new(t):
980 """
981 Create a new list
982 """
983 name = raw_input(light_magenta('New list\'s name: '))
984 mode = raw_input(light_magenta('New list\'s mode (public/private): '))
985 description = raw_input(light_magenta('New list\'s description: '))
986 try:
987 t.lists.create(
988 name=name,
989 mode=mode,
990 description=description)
991 printNicely(green(name + ' list is created.'))
992 except:
993 printNicely(red('Oops something is wrong with Twitter :('))
994
995
996 def list_update(t):
997 """
998 Update a list
999 """
1000 slug = raw_input(light_magenta('Your list that you want to update: '))
1001 name = raw_input(light_magenta('Update name (leave blank to unchange): '))
1002 mode = raw_input(light_magenta('Update mode (public/private): '))
1003 description = raw_input(light_magenta('Update description: '))
1004 try:
1005 if name:
1006 t.lists.update(
1007 slug='-'.join(slug.split()),
1008 owner_screen_name=g['original_name'],
1009 name=name,
1010 mode=mode,
1011 description=description)
1012 else:
1013 t.lists.update(
1014 slug=slug,
1015 owner_screen_name=g['original_name'],
1016 mode=mode,
1017 description=description)
1018 printNicely(green(slug + ' list is updated.'))
1019 except:
1020 printNicely(red('Oops something is wrong with Twitter :('))
1021
1022
1023 def list_delete(t):
1024 """
1025 Delete a list
1026 """
1027 slug = raw_input(light_magenta('Your list that you want to update: '))
1028 try:
1029 t.lists.destroy(
1030 slug='-'.join(slug.split()),
1031 owner_screen_name=g['original_name'])
1032 printNicely(green(slug + ' list is deleted.'))
1033 except:
1034 printNicely(red('Oops something is wrong with Twitter :('))
1035
1036
1037 def twitterlist():
1038 """
1039 Twitter's list
1040 """
1041 t = Twitter(auth=authen())
1042 # List all lists or base on action
1043 try:
1044 g['list_action'] = g['stuff'].split()[0]
1045 except:
1046 show_lists(t)
1047 return
1048 # Sub-function
1049 action_ary = {
1050 'home': list_home,
1051 'all_mem': list_members,
1052 'all_sub': list_subscribers,
1053 'add': list_add,
1054 'rm': list_remove,
1055 'sub': list_subscribe,
1056 'unsub': list_unsubscribe,
1057 'own': list_own,
1058 'new': list_new,
1059 'update': list_update,
1060 'del': list_delete,
1061 }
1062 try:
1063 return action_ary[g['list_action']](t)
1064 except:
1065 printNicely(red('Please try again.'))
1066
1067
1068 def cal():
1069 """
1070 Unix's command `cal`
1071 """
1072 # Format
1073 rel = os.popen('cal').read().split('\n')
1074 month = rel.pop(0)
1075 date = rel.pop(0)
1076 show_calendar(month, date, rel)
1077
1078
1079 def config():
1080 """
1081 Browse and change config
1082 """
1083 all_config = get_all_config()
1084 g['stuff'] = g['stuff'].strip()
1085 # List all config
1086 if not g['stuff']:
1087 for k in all_config:
1088 line = ' ' * 2 + \
1089 green(k) + ': ' + light_yellow(str(all_config[k]))
1090 printNicely(line)
1091 guide = 'Detailed explanation can be found at ' + \
1092 color_func(c['TWEET']['link'])(
1093 'http://rainbowstream.readthedocs.org/en/latest/#config-explanation')
1094 printNicely(guide)
1095 # Print specific config
1096 elif len(g['stuff'].split()) == 1:
1097 if g['stuff'] in all_config:
1098 k = g['stuff']
1099 line = ' ' * 2 + \
1100 green(k) + ': ' + light_yellow(str(all_config[k]))
1101 printNicely(line)
1102 else:
1103 printNicely(red('No such config key.'))
1104 # Print specific config's default value
1105 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'default':
1106 key = g['stuff'].split()[0]
1107 try:
1108 value = get_default_config(key)
1109 line = ' ' * 2 + green(key) + ': ' + light_magenta(value)
1110 printNicely(line)
1111 except Exception as e:
1112 printNicely(red(e))
1113 # Delete specific config key in config file
1114 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'drop':
1115 key = g['stuff'].split()[0]
1116 try:
1117 delete_config(key)
1118 printNicely(green('Config key is dropped.'))
1119 except Exception as e:
1120 printNicely(red(e))
1121 # Set specific config
1122 elif len(g['stuff'].split()) == 3 and g['stuff'].split()[1] == '=':
1123 key = g['stuff'].split()[0]
1124 value = g['stuff'].split()[-1]
1125 if key == 'THEME' and not validate_theme(value):
1126 printNicely(red('Invalid theme\'s value.'))
1127 return
1128 try:
1129 set_config(key, value)
1130 # Apply theme immediately
1131 if key == 'THEME':
1132 c['THEME'] = reload_theme(value, c['THEME'])
1133 g['decorated_name'] = lambda x: color_func(
1134 c['DECORATED_NAME'])('[' + x + ']: ')
1135 reload_config()
1136 printNicely(green('Updated successfully.'))
1137 except Exception as e:
1138 printNicely(red(e))
1139 else:
1140 printNicely(light_magenta('Sorry I can\'s understand.'))
1141
1142
1143 def theme():
1144 """
1145 List and change theme
1146 """
1147 if not g['stuff']:
1148 # List themes
1149 for theme in g['themes']:
1150 line = light_magenta(theme)
1151 if c['THEME'] == theme:
1152 line = ' ' * 2 + light_yellow('* ') + line
1153 else:
1154 line = ' ' * 4 + line
1155 printNicely(line)
1156 else:
1157 # Change theme
1158 try:
1159 # Load new theme
1160 c['THEME'] = reload_theme(g['stuff'], c['THEME'])
1161 # Redefine decorated_name
1162 g['decorated_name'] = lambda x: color_func(
1163 c['DECORATED_NAME'])(
1164 '[' + x + ']: ')
1165 printNicely(green('Theme changed.'))
1166 except:
1167 printNicely(red('No such theme exists.'))
1168
1169
1170 def help_discover():
1171 """
1172 Discover the world
1173 """
1174 s = ' ' * 2
1175 # Discover the world
1176 usage = '\n'
1177 usage += s + grey(u'\u266A' + ' Discover the world \n')
1178 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
1179 'You can try ' + light_green('trend US') + ' or ' + \
1180 light_green('trend JP Tokyo') + '.\n'
1181 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
1182 light_green('home 7') + ' will show 7 tweets.\n'
1183 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
1184 light_green('mentions 7') + ' will show 7 mention tweets.\n'
1185 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
1186 magenta('@mdo') + '.\n'
1187 usage += s * 2 + light_green('view @mdo') + \
1188 ' will show ' + magenta('@mdo') + '\'s home.\n'
1189 usage += s * 2 + light_green('s AKB48') + ' will search for "' + \
1190 light_yellow('AKB48') + '" and return 5 newest tweet. ' + \
1191 'Search can be performed with or without hashtag.\n'
1192 printNicely(usage)
1193
1194
1195 def help_tweets():
1196 """
1197 Tweets
1198 """
1199 s = ' ' * 2
1200 # Tweet
1201 usage = '\n'
1202 usage += s + grey(u'\u266A' + ' Tweets \n')
1203 usage += s * 2 + light_green('t oops ') + \
1204 'will tweet "' + light_yellow('oops') + '" immediately.\n'
1205 usage += s * 2 + \
1206 light_green('rt 12 ') + ' will retweet to tweet with ' + \
1207 light_yellow('[id=12]') + '.\n'
1208 usage += s * 2 + \
1209 light_green('quote 12 ') + ' will quote the tweet with ' + \
1210 light_yellow('[id=12]') + '. If no extra text is added, ' + \
1211 'the quote will be canceled.\n'
1212 usage += s * 2 + \
1213 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
1214 light_yellow('[id=12]') + '.\n'
1215 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
1216 light_yellow('oops') + '" to tweet with ' + \
1217 light_yellow('[id=12]') + '.\n'
1218 usage += s * 2 + \
1219 light_green('fav 12 ') + ' will favorite the tweet with ' + \
1220 light_yellow('[id=12]') + '.\n'
1221 usage += s * 2 + \
1222 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
1223 light_yellow('[id=12]') + '.\n'
1224 usage += s * 2 + \
1225 light_green('del 12 ') + ' will delete tweet with ' + \
1226 light_yellow('[id=12]') + '.\n'
1227 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
1228 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
1229 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
1230 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
1231 printNicely(usage)
1232
1233
1234 def help_messages():
1235 """
1236 Messages
1237 """
1238 s = ' ' * 2
1239 # Direct message
1240 usage = '\n'
1241 usage += s + grey(u'\u266A' + ' Direct messages \n')
1242 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
1243 light_green('inbox 7') + ' will show newest 7 messages.\n'
1244 usage += s * 2 + light_green('sent') + ' will show sent messages. ' + \
1245 light_green('sent 7') + ' will show newest 7 messages.\n'
1246 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
1247 magenta('@dtvd88') + '.\n'
1248 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
1249 light_yellow('[message_id=5]') + '.\n'
1250 printNicely(usage)
1251
1252
1253 def help_friends_and_followers():
1254 """
1255 Friends and Followers
1256 """
1257 s = ' ' * 2
1258 # Follower and following
1259 usage = '\n'
1260 usage += s + grey(u'\u266A' + ' Friends and followers \n')
1261 usage += s * 2 + \
1262 light_green('ls fl') + \
1263 ' will list all followers (people who are following you).\n'
1264 usage += s * 2 + \
1265 light_green('ls fr') + \
1266 ' will list all friends (people who you are following).\n'
1267 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
1268 magenta('@dtvd88') + '.\n'
1269 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
1270 magenta('@dtvd88') + '.\n'
1271 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
1272 magenta('@dtvd88') + '.\n'
1273 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
1274 magenta('@dtvd88') + '.\n'
1275 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
1276 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
1277 magenta('@dtvd88') + '.\n'
1278 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
1279 magenta('@dtvd88') + '.\n'
1280 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
1281 magenta('@dtvd88') + ' as a spam account.\n'
1282 printNicely(usage)
1283
1284
1285 def help_list():
1286 """
1287 Lists
1288 """
1289 s = ' ' * 2
1290 # Twitter list
1291 usage = '\n'
1292 usage += s + grey(u'\u266A' + ' Twitter list\n')
1293 usage += s * 2 + light_green('list') + \
1294 ' will show all lists you are belong to.\n'
1295 usage += s * 2 + light_green('list home') + \
1296 ' will show timeline of list. You will be asked for list\'s name.\n'
1297 usage += s * 2 + light_green('list all_mem') + \
1298 ' will show list\'s all members.\n'
1299 usage += s * 2 + light_green('list all_sub') + \
1300 ' will show list\'s all subscribers.\n'
1301 usage += s * 2 + light_green('list add') + \
1302 ' will add specific person to a list owned by you.' + \
1303 ' You will be asked for list\'s name and person\'s name.\n'
1304 usage += s * 2 + light_green('list rm') + \
1305 ' will remove specific person from a list owned by you.' + \
1306 ' You will be asked for list\'s name and person\'s name.\n'
1307 usage += s * 2 + light_green('list sub') + \
1308 ' will subscribe you to a specific list.\n'
1309 usage += s * 2 + light_green('list unsub') + \
1310 ' will unsubscribe you from a specific list.\n'
1311 usage += s * 2 + light_green('list own') + \
1312 ' will show all list owned by you.\n'
1313 usage += s * 2 + light_green('list new') + \
1314 ' will create a new list.\n'
1315 usage += s * 2 + light_green('list update') + \
1316 ' will update a list owned by you.\n'
1317 usage += s * 2 + light_green('list del') + \
1318 ' will delete a list owned by you.\n'
1319 printNicely(usage)
1320
1321
1322 def help_stream():
1323 """
1324 Stream switch
1325 """
1326 s = ' ' * 2
1327 # Switch
1328 usage = '\n'
1329 usage += s + grey(u'\u266A' + ' Switching streams \n')
1330 usage += s * 2 + light_green('switch public #AKB') + \
1331 ' will switch to public stream and follow "' + \
1332 light_yellow('AKB') + '" keyword.\n'
1333 usage += s * 2 + light_green('switch mine') + \
1334 ' will switch to your personal stream.\n'
1335 usage += s * 2 + light_green('switch mine -f ') + \
1336 ' will prompt to enter the filter.\n'
1337 usage += s * 3 + light_yellow('Only nicks') + \
1338 ' filter will decide nicks will be INCLUDE ONLY.\n'
1339 usage += s * 3 + light_yellow('Ignore nicks') + \
1340 ' filter will decide nicks will be EXCLUDE.\n'
1341 usage += s * 2 + light_green('switch mine -d') + \
1342 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
1343 printNicely(usage)
1344
1345
1346 def help():
1347 """
1348 Help
1349 """
1350 s = ' ' * 2
1351 h, w = os.popen('stty size', 'r').read().split()
1352 # Start
1353 usage = '\n'
1354 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
1355 usage += s + '-' * (int(w) - 4) + '\n'
1356 usage += s + 'You are ' + \
1357 light_yellow('already') + ' on your personal stream.\n'
1358 usage += s + 'Any update from Twitter will show up ' + \
1359 light_yellow('immediately') + '.\n'
1360 usage += s + 'In addtion, following commands are available right now:\n'
1361 # Twitter help section
1362 usage += '\n'
1363 usage += s + grey(u'\u266A' + ' Twitter help\n')
1364 usage += s * 2 + light_green('h discover') + \
1365 ' will show help for discover commands.\n'
1366 usage += s * 2 + light_green('h tweets') + \
1367 ' will show help for tweets commands.\n'
1368 usage += s * 2 + light_green('h messages') + \
1369 ' will show help for messages commands.\n'
1370 usage += s * 2 + light_green('h friends_and_followers') + \
1371 ' will show help for friends and followers commands.\n'
1372 usage += s * 2 + light_green('h list') + \
1373 ' will show help for list commands.\n'
1374 usage += s * 2 + light_green('h stream') + \
1375 ' will show help for stream commands.\n'
1376 # Smart shell
1377 usage += '\n'
1378 usage += s + grey(u'\u266A' + ' Smart shell\n')
1379 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1380 'will be evaluate by Python interpreter.\n'
1381 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1382 ' for current month.\n'
1383 # Config
1384 usage += '\n'
1385 usage += s + grey(u'\u266A' + ' Config \n')
1386 usage += s * 2 + light_green('theme') + ' will list available theme. ' + \
1387 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
1388 ' theme immediately.\n'
1389 usage += s * 2 + light_green('config') + ' will list all config.\n'
1390 usage += s * 3 + \
1391 light_green('config ASCII_ART') + ' will output current value of ' +\
1392 light_yellow('ASCII_ART') + ' config key.\n'
1393 usage += s * 3 + \
1394 light_green('config TREND_MAX default') + ' will output default value of ' + \
1395 light_yellow('TREND_MAX') + ' config key.\n'
1396 usage += s * 3 + \
1397 light_green('config CUSTOM_CONFIG drop') + ' will drop ' + \
1398 light_yellow('CUSTOM_CONFIG') + ' config key.\n'
1399 usage += s * 3 + \
1400 light_green('config IMAGE_ON_TERM = true') + ' will set value of ' + \
1401 light_yellow('IMAGE_ON_TERM') + ' config key to ' + \
1402 light_yellow('True') + '.\n'
1403 # Screening
1404 usage += '\n'
1405 usage += s + grey(u'\u266A' + ' Screening \n')
1406 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1407 usage += s * 2 + light_green('p') + ' will pause the stream.\n'
1408 usage += s * 2 + light_green('r') + ' will unpause the stream.\n'
1409 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1410 usage += s * 2 + light_green('q') + ' will quit.\n'
1411 # End
1412 usage += '\n'
1413 usage += s + '-' * (int(w) - 4) + '\n'
1414 usage += s + 'Have fun and hang tight! \n'
1415 # Show help
1416 d = {
1417 'discover': help_discover,
1418 'tweets': help_tweets,
1419 'messages': help_messages,
1420 'friends_and_followers': help_friends_and_followers,
1421 'list': help_list,
1422 'stream': help_stream,
1423 }
1424 if g['stuff']:
1425 d.get(
1426 g['stuff'].strip(),
1427 lambda: printNicely(red('No such command.'))
1428 )()
1429 else:
1430 printNicely(usage)
1431
1432
1433 def pause():
1434 """
1435 Pause stream display
1436 """
1437 c['pause'] = True
1438 printNicely(green('Stream is paused'))
1439
1440
1441 def replay():
1442 """
1443 Replay stream
1444 """
1445 c['pause'] = False
1446 printNicely(green('Stream is running back now'))
1447
1448
1449 def clear():
1450 """
1451 Clear screen
1452 """
1453 os.system('clear')
1454
1455
1456 def quit():
1457 """
1458 Exit all
1459 """
1460 try:
1461 save_history()
1462 printNicely(green('See you next time :)'))
1463 except:
1464 pass
1465 sys.exit()
1466
1467
1468 def reset():
1469 """
1470 Reset prefix of line
1471 """
1472 if g['reset']:
1473 if c.get('USER_JSON_ERROR'):
1474 printNicely(red('Your ~/.rainbow_config.json is messed up:'))
1475 printNicely(red('>>> ' + c['USER_JSON_ERROR']))
1476 printNicely('')
1477 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
1478 g['reset'] = False
1479 try:
1480 printNicely(str(eval(g['cmd'])))
1481 except Exception:
1482 pass
1483
1484
1485 # Command set
1486 cmdset = [
1487 'switch',
1488 'trend',
1489 'home',
1490 'view',
1491 'mentions',
1492 't',
1493 'rt',
1494 'quote',
1495 'allrt',
1496 'fav',
1497 'rep',
1498 'del',
1499 'ufav',
1500 's',
1501 'mes',
1502 'show',
1503 'open',
1504 'ls',
1505 'inbox',
1506 'sent',
1507 'trash',
1508 'whois',
1509 'fl',
1510 'ufl',
1511 'mute',
1512 'unmute',
1513 'muting',
1514 'block',
1515 'unblock',
1516 'report',
1517 'list',
1518 'cal',
1519 'config',
1520 'theme',
1521 'h',
1522 'p',
1523 'r',
1524 'c',
1525 'q'
1526 ]
1527
1528 # Handle function set
1529 funcset = [
1530 switch,
1531 trend,
1532 home,
1533 view,
1534 mentions,
1535 tweet,
1536 retweet,
1537 quote,
1538 allretweet,
1539 favorite,
1540 reply,
1541 delete,
1542 unfavorite,
1543 search,
1544 message,
1545 show,
1546 urlopen,
1547 ls,
1548 inbox,
1549 sent,
1550 trash,
1551 whois,
1552 follow,
1553 unfollow,
1554 mute,
1555 unmute,
1556 muting,
1557 block,
1558 unblock,
1559 report,
1560 twitterlist,
1561 cal,
1562 config,
1563 theme,
1564 help,
1565 pause,
1566 replay,
1567 clear,
1568 quit
1569 ]
1570
1571
1572 def process(cmd):
1573 """
1574 Process switch
1575 """
1576 return dict(zip(cmdset, funcset)).get(cmd, reset)
1577
1578
1579 def listen():
1580 """
1581 Listen to user's input
1582 """
1583 d = dict(zip(
1584 cmdset,
1585 [
1586 ['public', 'mine'], # switch
1587 [], # trend
1588 [], # home
1589 ['@'], # view
1590 [], # mentions
1591 [], # tweet
1592 [], # retweet
1593 [], # quote
1594 [], # allretweet
1595 [], # favorite
1596 [], # reply
1597 [], # delete
1598 [], # unfavorite
1599 ['#'], # search
1600 ['@'], # message
1601 ['image'], # show image
1602 [''], # open url
1603 ['fl', 'fr'], # list
1604 [], # inbox
1605 [], # sent
1606 [], # trash
1607 ['@'], # whois
1608 ['@'], # follow
1609 ['@'], # unfollow
1610 ['@'], # mute
1611 ['@'], # unmute
1612 ['@'], # muting
1613 ['@'], # block
1614 ['@'], # unblock
1615 ['@'], # report
1616 [
1617 'home',
1618 'all_mem',
1619 'all_sub',
1620 'add',
1621 'rm',
1622 'sub',
1623 'unsub',
1624 'own',
1625 'new',
1626 'update',
1627 'del'
1628 ], # list
1629 [], # cal
1630 [key for key in dict(get_all_config())], # config
1631 g['themes'], # theme
1632 [
1633 'discover',
1634 'tweets',
1635 'messages',
1636 'friends_and_followers',
1637 'list',
1638 'stream'
1639 ], # help
1640 [], # pause
1641 [], # reconnect
1642 [], # clear
1643 [], # quit
1644 ]
1645 ))
1646 init_interactive_shell(d)
1647 read_history()
1648 reset()
1649 while True:
1650 # raw_input
1651 if g['prefix']:
1652 line = raw_input(g['decorated_name'](c['PREFIX']))
1653 else:
1654 line = raw_input()
1655 # Save cmd to compare with readline buffer
1656 g['cmd'] = line.strip()
1657 # Get short cmd to pass to handle function
1658 try:
1659 cmd = line.split()[0]
1660 except:
1661 cmd = ''
1662 try:
1663 # Lock the semaphore
1664 c['lock'] = True
1665 # Save cmd to global variable and call process
1666 g['stuff'] = ' '.join(line.split()[1:])
1667 # Process the command
1668 process(cmd)()
1669 # Not re-display
1670 if cmd in ['switch', 't', 'rt', 'rep']:
1671 g['prefix'] = False
1672 else:
1673 g['prefix'] = True
1674 # Release the semaphore lock
1675 c['lock'] = False
1676 except Exception:
1677 printNicely(red('OMG something is wrong with Twitter right now.'))
1678
1679
1680 def stream(domain, args, name='Rainbow Stream'):
1681 """
1682 Track the stream
1683 """
1684 # The Logo
1685 art_dict = {
1686 c['USER_DOMAIN']: name,
1687 c['PUBLIC_DOMAIN']: args.track_keywords,
1688 c['SITE_DOMAIN']: name,
1689 }
1690 if c['ASCII_ART']:
1691 ascii_art(art_dict[domain])
1692 # These arguments are optional:
1693 stream_args = dict(
1694 timeout=0.5, # To check g['stream_stop'] after each 0.5 s
1695 block=not args.no_block,
1696 heartbeat_timeout=args.heartbeat_timeout)
1697 # Track keyword
1698 query_args = dict()
1699 if args.track_keywords:
1700 query_args['track'] = args.track_keywords
1701 # Get stream
1702 stream = TwitterStream(
1703 auth=authen(),
1704 domain=domain,
1705 **stream_args)
1706 try:
1707 if domain == c['USER_DOMAIN']:
1708 tweet_iter = stream.user(**query_args)
1709 elif domain == c['SITE_DOMAIN']:
1710 tweet_iter = stream.site(**query_args)
1711 else:
1712 if args.track_keywords:
1713 tweet_iter = stream.statuses.filter(**query_args)
1714 else:
1715 tweet_iter = stream.statuses.sample()
1716 # Block new stream until other one exits
1717 StreamLock.acquire()
1718 g['stream_stop'] = False
1719 for tweet in tweet_iter:
1720 if tweet is None:
1721 printNicely("-- None --")
1722 elif tweet is Timeout:
1723 if(g['stream_stop']):
1724 StreamLock.release()
1725 break
1726 elif tweet is HeartbeatTimeout:
1727 printNicely("-- Heartbeat Timeout --")
1728 elif tweet is Hangup:
1729 printNicely("-- Hangup --")
1730 elif tweet.get('text'):
1731 draw(
1732 t=tweet,
1733 keyword=args.track_keywords,
1734 check_semaphore=True,
1735 fil=args.filter,
1736 ig=args.ignore,
1737 )
1738 # Current readline buffer
1739 current_buffer = readline.get_line_buffer().strip()
1740 # There is an unexpected behaviour in MacOSX readline + Python 2:
1741 # after completely delete a word after typing it,
1742 # somehow readline buffer still contains
1743 # the 1st character of that word
1744 if current_buffer and g['cmd'] != current_buffer:
1745 sys.stdout.write(
1746 g['decorated_name'](c['PREFIX']) + unc(current_buffer))
1747 sys.stdout.flush()
1748 elif not c['HIDE_PROMPT']:
1749 sys.stdout.write(g['decorated_name'](c['PREFIX']))
1750 sys.stdout.flush()
1751 elif tweet.get('direct_message'):
1752 print_message(tweet['direct_message'], check_semaphore=True)
1753 except TwitterHTTPError:
1754 printNicely('')
1755 printNicely(
1756 magenta("We have maximum connection problem with twitter'stream API right now :("))
1757
1758
1759 def fly():
1760 """
1761 Main function
1762 """
1763 # Initial
1764 args = parse_arguments()
1765 try:
1766 init(args)
1767 except TwitterHTTPError:
1768 printNicely('')
1769 printNicely(
1770 magenta("We have connection problem with twitter'stream API right now :("))
1771 printNicely(magenta("Let's try again later."))
1772 save_history()
1773 sys.exit()
1774 # Spawn stream thread
1775 th = threading.Thread(
1776 target=stream,
1777 args=(
1778 c['USER_DOMAIN'],
1779 args,
1780 g['original_name']))
1781 th.daemon = True
1782 th.start()
1783 # Start listen process
1784 time.sleep(0.5)
1785 g['reset'] = True
1786 g['prefix'] = True
1787 listen()