f684ac4ed3a6ea38404e98167e6b7ee1031325b9
[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 # Start new thread
1199 th = threading.Thread(
1200 target=stream,
1201 args=(
1202 c['PUBLIC_DOMAIN'],
1203 args))
1204 th.daemon = True
1205 th.start()
1206 # Personal stream
1207 elif target == 'mine':
1208 # Kill old thread
1209 g['stream_stop'] = True
1210 # Start new thread
1211 th = threading.Thread(
1212 target=stream,
1213 args=(
1214 c['USER_DOMAIN'],
1215 args,
1216 g['original_name']))
1217 th.daemon = True
1218 th.start()
1219 # Stream base on list
1220 elif target == 'list':
1221 owner, slug = get_slug()
1222 # Force python 2 not redraw readline buffer
1223 g['cmd'] = '/'.join([owner, slug])
1224 printNicely(light_yellow('getting list members ...'))
1225 # Get members
1226 t = Twitter(auth=authen())
1227 members = []
1228 next_cursor = -1
1229 while next_cursor != 0:
1230 m = t.lists.members(
1231 slug=slug,
1232 owner_screen_name=owner,
1233 cursor=next_cursor,
1234 include_entities=False)
1235 for u in m['users']:
1236 members.append('@' + u['screen_name'])
1237 next_cursor = m['next_cursor']
1238 printNicely(light_yellow('... done.'))
1239 # Build thread filter array
1240 args.filter = members
1241 # Kill old thread
1242 g['stream_stop'] = True
1243 # Start new thread
1244 th = threading.Thread(
1245 target=stream,
1246 args=(
1247 c['USER_DOMAIN'],
1248 args,
1249 slug))
1250 th.daemon = True
1251 th.start()
1252 printNicely('')
1253 if args.filter:
1254 printNicely(cyan('Include: ' + str(len(args.filter)) + ' people.'))
1255 if args.ignore:
1256 printNicely(red('Ignore: ' + str(len(args.ignore)) + ' people.'))
1257 printNicely('')
1258 except:
1259 debug_option()
1260 printNicely(red('Sorry I can\'t understand.'))
1261
1262
1263 def cal():
1264 """
1265 Unix's command `cal`
1266 """
1267 # Format
1268 rel = os.popen('cal').read().split('\n')
1269 month = rel.pop(0)
1270 date = rel.pop(0)
1271 show_calendar(month, date, rel)
1272
1273
1274 def theme():
1275 """
1276 List and change theme
1277 """
1278 if not g['stuff']:
1279 # List themes
1280 for theme in g['themes']:
1281 line = light_magenta(theme)
1282 if c['THEME'] == theme:
1283 line = ' ' * 2 + light_yellow('* ') + line
1284 else:
1285 line = ' ' * 4 + line
1286 printNicely(line)
1287 else:
1288 # Change theme
1289 try:
1290 # Load new theme
1291 c['THEME'] = reload_theme(g['stuff'], c['THEME'])
1292 # Redefine decorated_name
1293 g['decorated_name'] = lambda x: color_func(
1294 c['DECORATED_NAME'])(
1295 '[' + x + ']: ')
1296 printNicely(green('Theme changed.'))
1297 except:
1298 printNicely(red('No such theme exists.'))
1299
1300
1301 def config():
1302 """
1303 Browse and change config
1304 """
1305 all_config = get_all_config()
1306 g['stuff'] = g['stuff'].strip()
1307 # List all config
1308 if not g['stuff']:
1309 for k in all_config:
1310 line = ' ' * 2 + \
1311 green(k) + ': ' + light_yellow(str(all_config[k]))
1312 printNicely(line)
1313 guide = 'Detailed explanation can be found at ' + \
1314 color_func(c['TWEET']['link'])(
1315 'http://rainbowstream.readthedocs.org/en/latest/#config-explanation')
1316 printNicely(guide)
1317 # Print specific config
1318 elif len(g['stuff'].split()) == 1:
1319 if g['stuff'] in all_config:
1320 k = g['stuff']
1321 line = ' ' * 2 + \
1322 green(k) + ': ' + light_yellow(str(all_config[k]))
1323 printNicely(line)
1324 else:
1325 printNicely(red('No such config key.'))
1326 # Print specific config's default value
1327 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'default':
1328 key = g['stuff'].split()[0]
1329 try:
1330 value = get_default_config(key)
1331 line = ' ' * 2 + green(key) + ': ' + light_magenta(value)
1332 printNicely(line)
1333 except:
1334 debug_option()
1335 printNicely(red('Just can not get the default.'))
1336 # Delete specific config key in config file
1337 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'drop':
1338 key = g['stuff'].split()[0]
1339 try:
1340 delete_config(key)
1341 printNicely(green('Config key is dropped.'))
1342 except:
1343 debug_option()
1344 printNicely(red('Just can not drop the key.'))
1345 # Set specific config
1346 elif len(g['stuff'].split()) == 3 and g['stuff'].split()[1] == '=':
1347 key = g['stuff'].split()[0]
1348 value = g['stuff'].split()[-1]
1349 if key == 'THEME' and not validate_theme(value):
1350 printNicely(red('Invalid theme\'s value.'))
1351 return
1352 try:
1353 set_config(key, value)
1354 # Keys that needs to be apply immediately
1355 if key == 'THEME':
1356 c['THEME'] = reload_theme(value, c['THEME'])
1357 g['decorated_name'] = lambda x: color_func(
1358 c['DECORATED_NAME'])('[' + x + ']: ')
1359 elif key == 'PREFIX':
1360 g['PREFIX'] = u2str(emojize(c['PREFIX']))
1361 reload_config()
1362 printNicely(green('Updated successfully.'))
1363 except:
1364 debug_option()
1365 printNicely(red('Just can not set the key.'))
1366 else:
1367 printNicely(light_magenta('Sorry I can\'s understand.'))
1368
1369
1370 def help_discover():
1371 """
1372 Discover the world
1373 """
1374 s = ' ' * 2
1375 # Discover the world
1376 usage = '\n'
1377 usage += s + grey(u'\u266A' + ' Discover the world \n')
1378 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
1379 'You can try ' + light_green('trend US') + ' or ' + \
1380 light_green('trend JP Tokyo') + '.\n'
1381 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
1382 light_green('home 7') + ' will show 7 tweets.\n'
1383 usage += s * 2 + \
1384 light_green('notification') + ' will show your recent notification.\n'
1385 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
1386 light_green('mentions 7') + ' will show 7 mention tweets.\n'
1387 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
1388 magenta('@mdo') + '.\n'
1389 usage += s * 2 + light_green('view @mdo') + \
1390 ' will show ' + magenta('@mdo') + '\'s home.\n'
1391 usage += s * 2 + light_green('s AKB48') + ' will search for "' + \
1392 light_yellow('AKB48') + '" and return 5 newest tweet. ' + \
1393 'Search can be performed with or without hashtag.\n'
1394 printNicely(usage)
1395
1396
1397 def help_tweets():
1398 """
1399 Tweets
1400 """
1401 s = ' ' * 2
1402 # Tweet
1403 usage = '\n'
1404 usage += s + grey(u'\u266A' + ' Tweets \n')
1405 usage += s * 2 + light_green('t oops ') + \
1406 'will tweet "' + light_yellow('oops') + '" immediately.\n'
1407 usage += s * 2 + \
1408 light_green('rt 12 ') + ' will retweet to tweet with ' + \
1409 light_yellow('[id=12]') + '.\n'
1410 usage += s * 2 + \
1411 light_green('quote 12 ') + ' will quote the tweet with ' + \
1412 light_yellow('[id=12]') + '. If no extra text is added, ' + \
1413 'the quote will be canceled.\n'
1414 usage += s * 2 + \
1415 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
1416 light_yellow('[id=12]') + '.\n'
1417 usage += s * 2 + light_green('conversation 12') + ' will show the chain of ' + \
1418 'replies prior to the tweet with ' + light_yellow('[id=12]') + '.\n'
1419 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
1420 light_yellow('oops') + '" to tweet with ' + \
1421 light_yellow('[id=12]') + '.\n'
1422 usage += s * 2 + \
1423 light_green('fav 12 ') + ' will favorite the tweet with ' + \
1424 light_yellow('[id=12]') + '.\n'
1425 usage += s * 2 + \
1426 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
1427 light_yellow('[id=12]') + '.\n'
1428 usage += s * 2 + \
1429 light_green('share 12 ') + ' will get the direct link of the tweet with ' + \
1430 light_yellow('[id=12]') + '.\n'
1431 usage += s * 2 + \
1432 light_green('del 12 ') + ' will delete tweet with ' + \
1433 light_yellow('[id=12]') + '.\n'
1434 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
1435 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
1436 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
1437 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
1438 printNicely(usage)
1439
1440
1441 def help_messages():
1442 """
1443 Messages
1444 """
1445 s = ' ' * 2
1446 # Direct message
1447 usage = '\n'
1448 usage += s + grey(u'\u266A' + ' Direct messages \n')
1449 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
1450 light_green('inbox 7') + ' will show newest 7 messages.\n'
1451 usage += s * 2 + light_green('thread 2') + ' will show full thread with ' + \
1452 light_yellow('[thread_id=2]') + '.\n'
1453 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
1454 magenta('@dtvd88') + '.\n'
1455 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
1456 light_yellow('[message_id=5]') + '.\n'
1457 printNicely(usage)
1458
1459
1460 def help_friends_and_followers():
1461 """
1462 Friends and Followers
1463 """
1464 s = ' ' * 2
1465 # Follower and following
1466 usage = '\n'
1467 usage += s + grey(u'\u266A' + ' Friends and followers \n')
1468 usage += s * 2 + \
1469 light_green('ls fl') + \
1470 ' will list all followers (people who are following you).\n'
1471 usage += s * 2 + \
1472 light_green('ls fr') + \
1473 ' will list all friends (people who you are following).\n'
1474 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
1475 magenta('@dtvd88') + '.\n'
1476 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
1477 magenta('@dtvd88') + '.\n'
1478 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
1479 magenta('@dtvd88') + '.\n'
1480 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
1481 magenta('@dtvd88') + '.\n'
1482 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
1483 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
1484 magenta('@dtvd88') + '.\n'
1485 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
1486 magenta('@dtvd88') + '.\n'
1487 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
1488 magenta('@dtvd88') + ' as a spam account.\n'
1489 printNicely(usage)
1490
1491
1492 def help_list():
1493 """
1494 Lists
1495 """
1496 s = ' ' * 2
1497 # Twitter list
1498 usage = '\n'
1499 usage += s + grey(u'\u266A' + ' Twitter list\n')
1500 usage += s * 2 + light_green('list') + \
1501 ' will show all lists you are belong to.\n'
1502 usage += s * 2 + light_green('list home') + \
1503 ' will show timeline of list. You will be asked for list\'s name.\n'
1504 usage += s * 2 + light_green('list all_mem') + \
1505 ' will show list\'s all members.\n'
1506 usage += s * 2 + light_green('list all_sub') + \
1507 ' will show list\'s all subscribers.\n'
1508 usage += s * 2 + light_green('list add') + \
1509 ' will add specific person to a list owned by you.' + \
1510 ' You will be asked for list\'s name and person\'s name.\n'
1511 usage += s * 2 + light_green('list rm') + \
1512 ' will remove specific person from a list owned by you.' + \
1513 ' You will be asked for list\'s name and person\'s name.\n'
1514 usage += s * 2 + light_green('list sub') + \
1515 ' will subscribe you to a specific list.\n'
1516 usage += s * 2 + light_green('list unsub') + \
1517 ' will unsubscribe you from a specific list.\n'
1518 usage += s * 2 + light_green('list own') + \
1519 ' will show all list owned by you.\n'
1520 usage += s * 2 + light_green('list new') + \
1521 ' will create a new list.\n'
1522 usage += s * 2 + light_green('list update') + \
1523 ' will update a list owned by you.\n'
1524 usage += s * 2 + light_green('list del') + \
1525 ' will delete a list owned by you.\n'
1526 printNicely(usage)
1527
1528
1529 def help_stream():
1530 """
1531 Stream switch
1532 """
1533 s = ' ' * 2
1534 # Switch
1535 usage = '\n'
1536 usage += s + grey(u'\u266A' + ' Switching streams \n')
1537 usage += s * 2 + light_green('switch public #AKB') + \
1538 ' will switch to public stream and follow "' + \
1539 light_yellow('AKB') + '" keyword.\n'
1540 usage += s * 2 + light_green('switch mine') + \
1541 ' will switch to your personal stream.\n'
1542 usage += s * 2 + light_green('switch mine -f ') + \
1543 ' will prompt to enter the filter.\n'
1544 usage += s * 3 + light_yellow('Only nicks') + \
1545 ' filter will decide nicks will be INCLUDE ONLY.\n'
1546 usage += s * 3 + light_yellow('Ignore nicks') + \
1547 ' filter will decide nicks will be EXCLUDE.\n'
1548 usage += s * 2 + light_green('switch list') + \
1549 ' will switch to a Twitter list\'s stream. You will be asked for list name\n'
1550 printNicely(usage)
1551
1552
1553 def help():
1554 """
1555 Help
1556 """
1557 s = ' ' * 2
1558 h, w = os.popen('stty size', 'r').read().split()
1559 # Start
1560 usage = '\n'
1561 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
1562 usage += s + '-' * (int(w) - 4) + '\n'
1563 usage += s + 'You are ' + \
1564 light_yellow('already') + ' on your personal stream.\n'
1565 usage += s + 'Any update from Twitter will show up ' + \
1566 light_yellow('immediately') + '.\n'
1567 usage += s + 'In addition, following commands are available right now:\n'
1568 # Twitter help section
1569 usage += '\n'
1570 usage += s + grey(u'\u266A' + ' Twitter help\n')
1571 usage += s * 2 + light_green('h discover') + \
1572 ' will show help for discover commands.\n'
1573 usage += s * 2 + light_green('h tweets') + \
1574 ' will show help for tweets commands.\n'
1575 usage += s * 2 + light_green('h messages') + \
1576 ' will show help for messages commands.\n'
1577 usage += s * 2 + light_green('h friends_and_followers') + \
1578 ' will show help for friends and followers commands.\n'
1579 usage += s * 2 + light_green('h list') + \
1580 ' will show help for list commands.\n'
1581 usage += s * 2 + light_green('h stream') + \
1582 ' will show help for stream commands.\n'
1583 # Smart shell
1584 usage += '\n'
1585 usage += s + grey(u'\u266A' + ' Smart shell\n')
1586 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1587 'will be evaluate by Python interpreter.\n'
1588 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1589 ' for current month.\n'
1590 # Config
1591 usage += '\n'
1592 usage += s + grey(u'\u266A' + ' Config \n')
1593 usage += s * 2 + light_green('theme') + ' will list available theme. ' + \
1594 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
1595 ' theme immediately.\n'
1596 usage += s * 2 + light_green('config') + ' will list all config.\n'
1597 usage += s * 3 + \
1598 light_green('config ASCII_ART') + ' will output current value of ' +\
1599 light_yellow('ASCII_ART') + ' config key.\n'
1600 usage += s * 3 + \
1601 light_green('config TREND_MAX default') + ' will output default value of ' + \
1602 light_yellow('TREND_MAX') + ' config key.\n'
1603 usage += s * 3 + \
1604 light_green('config CUSTOM_CONFIG drop') + ' will drop ' + \
1605 light_yellow('CUSTOM_CONFIG') + ' config key.\n'
1606 usage += s * 3 + \
1607 light_green('config IMAGE_ON_TERM = true') + ' will set value of ' + \
1608 light_yellow('IMAGE_ON_TERM') + ' config key to ' + \
1609 light_yellow('True') + '.\n'
1610 # Screening
1611 usage += '\n'
1612 usage += s + grey(u'\u266A' + ' Screening \n')
1613 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1614 usage += s * 2 + light_green('p') + ' will pause the stream.\n'
1615 usage += s * 2 + light_green('r') + ' will unpause the stream.\n'
1616 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1617 usage += s * 2 + light_green('q') + ' will quit.\n'
1618 # End
1619 usage += '\n'
1620 usage += s + '-' * (int(w) - 4) + '\n'
1621 usage += s + 'Have fun and hang tight! \n'
1622 # Show help
1623 d = {
1624 'discover': help_discover,
1625 'tweets': help_tweets,
1626 'messages': help_messages,
1627 'friends_and_followers': help_friends_and_followers,
1628 'list': help_list,
1629 'stream': help_stream,
1630 }
1631 if g['stuff']:
1632 d.get(
1633 g['stuff'].strip(),
1634 lambda: printNicely(red('No such command.'))
1635 )()
1636 else:
1637 printNicely(usage)
1638
1639
1640 def pause():
1641 """
1642 Pause stream display
1643 """
1644 g['pause'] = True
1645 printNicely(green('Stream is paused'))
1646
1647
1648 def replay():
1649 """
1650 Replay stream
1651 """
1652 g['pause'] = False
1653 printNicely(green('Stream is running back now'))
1654
1655
1656 def clear():
1657 """
1658 Clear screen
1659 """
1660 os.system('clear')
1661
1662
1663 def quit():
1664 """
1665 Exit all
1666 """
1667 try:
1668 save_history()
1669 printNicely(green('See you next time :)'))
1670 except:
1671 pass
1672 sys.exit()
1673
1674
1675 def reset():
1676 """
1677 Reset prefix of line
1678 """
1679 if g['reset']:
1680 if c.get('USER_JSON_ERROR'):
1681 printNicely(red('Your ~/.rainbow_config.json is messed up:'))
1682 printNicely(red('>>> ' + c['USER_JSON_ERROR']))
1683 printNicely('')
1684 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
1685 g['reset'] = False
1686 try:
1687 printNicely(str(eval(g['cmd'])))
1688 except Exception:
1689 pass
1690
1691
1692 # Command set
1693 cmdset = [
1694 'switch',
1695 'trend',
1696 'home',
1697 'notification',
1698 'view',
1699 'mentions',
1700 't',
1701 'rt',
1702 'quote',
1703 'allrt',
1704 'conversation',
1705 'fav',
1706 'rep',
1707 'del',
1708 'ufav',
1709 'share',
1710 's',
1711 'mes',
1712 'show',
1713 'open',
1714 'ls',
1715 'inbox',
1716 'thread',
1717 'trash',
1718 'whois',
1719 'fl',
1720 'ufl',
1721 'mute',
1722 'unmute',
1723 'muting',
1724 'block',
1725 'unblock',
1726 'report',
1727 'list',
1728 'cal',
1729 'config',
1730 'theme',
1731 'h',
1732 'p',
1733 'r',
1734 'c',
1735 'q'
1736 ]
1737
1738 # Handle function set
1739 funcset = [
1740 switch,
1741 trend,
1742 home,
1743 notification,
1744 view,
1745 mentions,
1746 tweet,
1747 retweet,
1748 quote,
1749 allretweet,
1750 conversation,
1751 favorite,
1752 reply,
1753 delete,
1754 unfavorite,
1755 share,
1756 search,
1757 message,
1758 show,
1759 urlopen,
1760 ls,
1761 inbox,
1762 thread,
1763 trash,
1764 whois,
1765 follow,
1766 unfollow,
1767 mute,
1768 unmute,
1769 muting,
1770 block,
1771 unblock,
1772 report,
1773 twitterlist,
1774 cal,
1775 config,
1776 theme,
1777 help,
1778 pause,
1779 replay,
1780 clear,
1781 quit
1782 ]
1783
1784
1785 def process(cmd):
1786 """
1787 Process switch
1788 """
1789 return dict(zip(cmdset, funcset)).get(cmd, reset)
1790
1791
1792 def listen():
1793 """
1794 Listen to user's input
1795 """
1796 d = dict(zip(
1797 cmdset,
1798 [
1799 ['public', 'mine', 'list'], # switch
1800 [], # trend
1801 [], # home
1802 [], # notification
1803 ['@'], # view
1804 [], # mentions
1805 [], # tweet
1806 [], # retweet
1807 [], # quote
1808 [], # allretweet
1809 [], # conversation
1810 [], # favorite
1811 [], # reply
1812 [], # delete
1813 [], # unfavorite
1814 [], # url
1815 ['#'], # search
1816 ['@'], # message
1817 ['image'], # show image
1818 [''], # open url
1819 ['fl', 'fr'], # list
1820 [], # inbox
1821 [i for i in g['message_threads']], # sent
1822 [], # trash
1823 ['@'], # whois
1824 ['@'], # follow
1825 ['@'], # unfollow
1826 ['@'], # mute
1827 ['@'], # unmute
1828 ['@'], # muting
1829 ['@'], # block
1830 ['@'], # unblock
1831 ['@'], # report
1832 [
1833 'home',
1834 'all_mem',
1835 'all_sub',
1836 'add',
1837 'rm',
1838 'sub',
1839 'unsub',
1840 'own',
1841 'new',
1842 'update',
1843 'del'
1844 ], # list
1845 [], # cal
1846 [key for key in dict(get_all_config())], # config
1847 g['themes'], # theme
1848 [
1849 'discover',
1850 'tweets',
1851 'messages',
1852 'friends_and_followers',
1853 'list',
1854 'stream'
1855 ], # help
1856 [], # pause
1857 [], # reconnect
1858 [], # clear
1859 [], # quit
1860 ]
1861 ))
1862 init_interactive_shell(d)
1863 read_history()
1864 reset()
1865 while True:
1866 try:
1867 # raw_input
1868 if g['prefix']:
1869 # Only use PREFIX as a string with raw_input
1870 line = raw_input(g['decorated_name'](g['PREFIX']))
1871 else:
1872 line = raw_input()
1873 # Save cmd to compare with readline buffer
1874 g['cmd'] = line.strip()
1875 # Get short cmd to pass to handle function
1876 try:
1877 cmd = line.split()[0]
1878 except:
1879 cmd = ''
1880 # Lock the semaphore
1881 c['lock'] = True
1882 # Save cmd to global variable and call process
1883 g['stuff'] = ' '.join(line.split()[1:])
1884 # Process the command
1885 process(cmd)()
1886 # Not re-display
1887 if cmd in ['switch', 't', 'rt', 'rep']:
1888 g['prefix'] = False
1889 else:
1890 g['prefix'] = True
1891 # Release the semaphore lock
1892 c['lock'] = False
1893 except EOFError:
1894 printNicely('')
1895 except Exception:
1896 debug_option()
1897 printNicely(red('OMG something is wrong with Twitter right now.'))
1898
1899
1900 def reconn_notice():
1901 """
1902 Notice when Hangup or Timeout
1903 """
1904 guide = light_magenta("You can use ") + \
1905 light_green("switch") + \
1906 light_magenta(" command to return to your stream.\n")
1907 guide += light_magenta("Type ") + \
1908 light_green("h stream") + \
1909 light_magenta(" for more details.")
1910 printNicely(guide)
1911 sys.stdout.write(g['decorated_name'](c['PREFIX']))
1912 sys.stdout.flush()
1913
1914
1915 def stream(domain, args, name='Rainbow Stream'):
1916 """
1917 Track the stream
1918 """
1919 # The Logo
1920 art_dict = {
1921 c['USER_DOMAIN']: name,
1922 c['PUBLIC_DOMAIN']: args.track_keywords,
1923 c['SITE_DOMAIN']: name,
1924 }
1925 if c['ASCII_ART']:
1926 ascii_art(art_dict[domain])
1927 # These arguments are optional:
1928 stream_args = dict(
1929 timeout=0.5, # To check g['stream_stop'] after each 0.5 s
1930 block=True,
1931 heartbeat_timeout=c['HEARTBEAT_TIMEOUT'] * 60)
1932 # Track keyword
1933 query_args = dict()
1934 if args.track_keywords:
1935 query_args['track'] = args.track_keywords
1936 # Get stream
1937 stream = TwitterStream(
1938 auth=authen(),
1939 domain=domain,
1940 **stream_args)
1941 try:
1942 if domain == c['USER_DOMAIN']:
1943 tweet_iter = stream.user(**query_args)
1944 elif domain == c['SITE_DOMAIN']:
1945 tweet_iter = stream.site(**query_args)
1946 else:
1947 if args.track_keywords:
1948 tweet_iter = stream.statuses.filter(**query_args)
1949 else:
1950 tweet_iter = stream.statuses.sample()
1951 # Block new stream until other one exits
1952 StreamLock.acquire()
1953 g['stream_stop'] = False
1954 for tweet in tweet_iter:
1955 if tweet is None:
1956 printNicely("-- None --")
1957 elif tweet is Timeout:
1958 # Because the stream check for each 0.3s
1959 # so we shouldn't output anything here
1960 if(g['stream_stop']):
1961 StreamLock.release()
1962 break
1963 elif tweet is HeartbeatTimeout:
1964 printNicely("-- Heartbeat Timeout --")
1965 reconn_notice()
1966 StreamLock.release()
1967 break
1968 elif tweet is Hangup:
1969 printNicely("-- Hangup --")
1970 reconn_notice()
1971 StreamLock.release()
1972 break
1973 elif tweet.get('text'):
1974 # Check the semaphore pause and lock (stream process only)
1975 if g['pause']:
1976 continue
1977 while c['lock']:
1978 time.sleep(0.5)
1979 # Draw the tweet
1980 draw(
1981 t=tweet,
1982 keyword=args.track_keywords,
1983 humanize=False,
1984 fil=args.filter,
1985 ig=args.ignore,
1986 )
1987 # Current readline buffer
1988 current_buffer = readline.get_line_buffer().strip()
1989 # There is an unexpected behaviour in MacOSX readline + Python 2:
1990 # after completely delete a word after typing it,
1991 # somehow readline buffer still contains
1992 # the 1st character of that word
1993 if current_buffer and g['cmd'] != current_buffer:
1994 sys.stdout.write(
1995 g['decorated_name'](c['PREFIX']) + str2u(current_buffer))
1996 sys.stdout.flush()
1997 elif not c['HIDE_PROMPT']:
1998 sys.stdout.write(g['decorated_name'](c['PREFIX']))
1999 sys.stdout.flush()
2000 elif tweet.get('direct_message'):
2001 # Check the semaphore pause and lock (stream process only)
2002 if g['pause']:
2003 continue
2004 while c['lock']:
2005 time.sleep(0.5)
2006 print_message(tweet['direct_message'])
2007 elif tweet.get('event'):
2008 c['events'].append(tweet)
2009 print_event(tweet)
2010 except TwitterHTTPError:
2011 printNicely('')
2012 printNicely(
2013 magenta("We have maximum connection problem with twitter'stream API right now :("))
2014
2015
2016 def fly():
2017 """
2018 Main function
2019 """
2020 # Initial
2021 args = parse_arguments()
2022 try:
2023 init(args)
2024 except TwitterHTTPError:
2025 printNicely('')
2026 printNicely(
2027 magenta("We have connection problem with twitter'stream API right now :("))
2028 printNicely(magenta("Let's try again later."))
2029 save_history()
2030 sys.exit()
2031 # Spawn stream thread
2032 th = threading.Thread(
2033 target=stream,
2034 args=(
2035 c['USER_DOMAIN'],
2036 args,
2037 g['original_name']))
2038 th.daemon = True
2039 th.start()
2040 # Start listen process
2041 time.sleep(0.5)
2042 g['reset'] = True
2043 g['prefix'] = True
2044 listen()