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