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