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