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