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