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