f8cc9bf26faf0a867f28ffa51fcffa2403b791a7
[rainbowstream.git] / rainbowstream / rainbow.py
1 """
2 Colorful user's timeline stream
3 """
4 from multiprocessing import Process
5
6 import os
7 import os.path
8 import sys
9 import signal
10 import argparse
11 import time
12 import requests
13 import webbrowser
14
15 from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup
16 from twitter.api import *
17 from twitter.oauth import OAuth, read_token_file
18 from twitter.oauth_dance import oauth_dance
19 from twitter.util import printNicely
20 from StringIO import StringIO
21
22 from .draw import *
23 from .colors import *
24 from .config import *
25 from .consumer import *
26 from .interactive import *
27 from .db import *
28 from .c_image import *
29
30 g = {}
31 db = RainbowDB()
32 cmdset = [
33 'switch',
34 'trend',
35 'home',
36 'view',
37 'mentions',
38 't',
39 'rt',
40 'quote',
41 'allrt',
42 'fav',
43 'rep',
44 'del',
45 'ufav',
46 's',
47 'mes',
48 'show',
49 'open',
50 'ls',
51 'inbox',
52 'sent',
53 'trash',
54 'whois',
55 'fl',
56 'ufl',
57 'mute',
58 'unmute',
59 'muting',
60 'block',
61 'unblock',
62 'report',
63 'cal',
64 'theme',
65 'h',
66 'c',
67 'q'
68 ]
69
70
71 def parse_arguments():
72 """
73 Parse the arguments
74 """
75 parser = argparse.ArgumentParser(description=__doc__ or "")
76 parser.add_argument(
77 '-to',
78 '--timeout',
79 help='Timeout for the stream (seconds).')
80 parser.add_argument(
81 '-ht',
82 '--heartbeat-timeout',
83 help='Set heartbeat timeout.',
84 default=90)
85 parser.add_argument(
86 '-nb',
87 '--no-block',
88 action='store_true',
89 help='Set stream to non-blocking.')
90 parser.add_argument(
91 '-tt',
92 '--track-keywords',
93 help='Search the stream for specific text.')
94 parser.add_argument(
95 '-fil',
96 '--filter',
97 help='Filter specific screen_name.')
98 parser.add_argument(
99 '-ig',
100 '--ignore',
101 help='Ignore specific screen_name.')
102 parser.add_argument(
103 '-iot',
104 '--image-on-term',
105 action='store_true',
106 help='Display all image on terminal.')
107 return parser.parse_args()
108
109
110 def authen():
111 """
112 Authenticate with Twitter OAuth
113 """
114 # When using rainbow stream you must authorize.
115 twitter_credential = os.environ.get(
116 'HOME',
117 os.environ.get(
118 'USERPROFILE',
119 '')) + os.sep + '.rainbow_oauth'
120 if not os.path.exists(twitter_credential):
121 oauth_dance("Rainbow Stream",
122 CONSUMER_KEY,
123 CONSUMER_SECRET,
124 twitter_credential)
125 oauth_token, oauth_token_secret = read_token_file(twitter_credential)
126 return OAuth(
127 oauth_token,
128 oauth_token_secret,
129 CONSUMER_KEY,
130 CONSUMER_SECRET)
131
132
133 def get_decorated_name():
134 """
135 Beginning of every line
136 """
137 t = Twitter(auth=authen())
138 name = '@' + t.account.verify_credentials()['screen_name']
139 g['original_name'] = name[1:]
140 g['decorated_name'] = color_func(c['DECORATED_NAME'])('[' + name + ']: ')
141 g['ascii_art'] = True
142
143 files = os.listdir(os.path.dirname(__file__)+'/colorset')
144 themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
145 themes += ['custom']
146 g['themes'] = themes
147 db.theme_store(c['theme'])
148
149
150 def switch():
151 """
152 Switch stream
153 """
154 try:
155 target = g['stuff'].split()[0]
156
157 # Filter and ignore
158 args = parse_arguments()
159 try:
160 if g['stuff'].split()[-1] == '-f':
161 only = raw_input('Only nicks: ')
162 ignore = raw_input('Ignore nicks: ')
163 args.filter = filter(None, only.split(','))
164 args.ignore = filter(None, ignore.split(','))
165 elif g['stuff'].split()[-1] == '-d':
166 args.filter = c['ONLY_LIST']
167 args.ignore = c['IGNORE_LIST']
168 except:
169 printNicely(red('Sorry, wrong format.'))
170 return
171
172 # Public stream
173 if target == 'public':
174 keyword = g['stuff'].split()[1]
175 if keyword[0] == '#':
176 keyword = keyword[1:]
177 # Kill old process
178 os.kill(g['stream_pid'], signal.SIGKILL)
179 args.track_keywords = keyword
180 # Start new process
181 p = Process(
182 target=stream,
183 args=(
184 c['PUBLIC_DOMAIN'],
185 args))
186 p.start()
187 g['stream_pid'] = p.pid
188
189 # Personal stream
190 elif target == 'mine':
191 # Kill old process
192 os.kill(g['stream_pid'], signal.SIGKILL)
193 # Start new process
194 p = Process(
195 target=stream,
196 args=(
197 c['USER_DOMAIN'],
198 args,
199 g['original_name']))
200 p.start()
201 g['stream_pid'] = p.pid
202 printNicely('')
203 if args.filter:
204 printNicely(cyan('Only: ' + str(args.filter)))
205 if args.ignore:
206 printNicely(red('Ignore: ' + str(args.ignore)))
207 printNicely('')
208 g['ascii_art'] = True
209 except:
210 printNicely(red('Sorry I can\'t understand.'))
211
212
213 def trend():
214 """
215 Trend
216 """
217 t = Twitter(auth=authen())
218 # Get country and town
219 try:
220 country = g['stuff'].split()[0]
221 except:
222 country = ''
223 try:
224 town = g['stuff'].split()[1]
225 except:
226 town = ''
227
228 avail = t.trends.available()
229 # World wide
230 if not country:
231 trends = t.trends.place(_id=1)[0]['trends']
232 print_trends(trends)
233 else:
234 for location in avail:
235 # Search for country and Town
236 if town:
237 if location['countryCode'] == country \
238 and location['placeType']['name'] == 'Town' \
239 and location['name'] == town:
240 trends = t.trends.place(_id=location['woeid'])[0]['trends']
241 print_trends(trends)
242 # Search for country only
243 else:
244 if location['countryCode'] == country \
245 and location['placeType']['name'] == 'Country':
246 trends = t.trends.place(_id=location['woeid'])[0]['trends']
247 print_trends(trends)
248
249
250 def home():
251 """
252 Home
253 """
254 t = Twitter(auth=authen())
255 num = c['HOME_TWEET_NUM']
256 if g['stuff'].isdigit():
257 num = int(g['stuff'])
258 for tweet in reversed(t.statuses.home_timeline(count=num)):
259 draw(t=tweet, iot=g['iot'])
260 printNicely('')
261
262
263 def view():
264 """
265 Friend view
266 """
267 t = Twitter(auth=authen())
268 user = g['stuff'].split()[0]
269 if user[0] == '@':
270 try:
271 num = int(g['stuff'].split()[1])
272 except:
273 num = c['HOME_TWEET_NUM']
274 for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])):
275 draw(t=tweet, iot=g['iot'])
276 printNicely('')
277 else:
278 printNicely(red('A name should begin with a \'@\''))
279
280
281 def mentions():
282 """
283 Mentions timeline
284 """
285 t = Twitter(auth=authen())
286 num = c['HOME_TWEET_NUM']
287 if g['stuff'].isdigit():
288 num = int(g['stuff'])
289 for tweet in reversed(t.statuses.mentions_timeline(count=num)):
290 draw(t=tweet, iot=g['iot'])
291 printNicely('')
292
293
294 def tweet():
295 """
296 Tweet
297 """
298 t = Twitter(auth=authen())
299 t.statuses.update(status=g['stuff'])
300
301
302 def retweet():
303 """
304 ReTweet
305 """
306 t = Twitter(auth=authen())
307 try:
308 id = int(g['stuff'].split()[0])
309 except:
310 printNicely(red('Sorry I can\'t understand.'))
311 return
312 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
313 t.statuses.retweet(id=tid, include_entities=False, trim_user=True)
314
315
316 def quote():
317 """
318 Quote a tweet
319 """
320 t = Twitter(auth=authen())
321 try:
322 id = int(g['stuff'].split()[0])
323 except:
324 printNicely(red('Sorry I can\'t understand.'))
325 return
326 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
327 tweet = t.statuses.show(id=tid)
328 screen_name = tweet['user']['screen_name']
329 text = tweet['text']
330 quote = '\"@' + screen_name + ': ' + text + '\"'
331 quote = quote.encode('utf8')
332 notice = light_magenta('Compose mode ')
333 notice += light_yellow('(Enter nothing will cancel the quote)')
334 notice += light_magenta(':')
335 printNicely(notice)
336 extra = raw_input(quote)
337 if extra:
338 t.statuses.update(status=quote+extra)
339 else:
340 printNicely(light_magenta('No text added.'))
341
342
343 def allretweet():
344 """
345 List all retweet
346 """
347 t = Twitter(auth=authen())
348 # Get rainbow id
349 try:
350 id = int(g['stuff'].split()[0])
351 except:
352 printNicely(red('Sorry I can\'t understand.'))
353 return
354 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
355 # Get display num if exist
356 try:
357 num = int(g['stuff'].split()[1])
358 except:
359 num = c['RETWEETS_SHOW_NUM']
360 # Get result and display
361 rt_ary = t.statuses.retweets(id=tid, count=num)
362 if not rt_ary:
363 printNicely(magenta('This tweet has no retweet.'))
364 return
365 for tweet in reversed(rt_ary):
366 draw(t=tweet, iot=g['iot'])
367 printNicely('')
368
369
370 def favorite():
371 """
372 Favorite
373 """
374 t = Twitter(auth=authen())
375 try:
376 id = int(g['stuff'].split()[0])
377 except:
378 printNicely(red('Sorry I can\'t understand.'))
379 return
380 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
381 t.favorites.create(_id=tid, include_entities=False)
382 printNicely(green('Favorited.'))
383 draw(t.statuses.show(id=tid), iot=g['iot'])
384 printNicely('')
385
386
387 def reply():
388 """
389 Reply
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 = db.rainbow_to_tweet_query(id)[0].tweet_id
398 user = t.statuses.show(id=tid)['user']['screen_name']
399 status = ' '.join(g['stuff'].split()[1:])
400 status = '@' + user + ' ' + status.decode('utf-8')
401 t.statuses.update(status=status, in_reply_to_status_id=tid)
402
403
404 def delete():
405 """
406 Delete
407 """
408 t = Twitter(auth=authen())
409 try:
410 rid = int(g['stuff'].split()[0])
411 except:
412 printNicely(red('Sorry I can\'t understand.'))
413 return
414 tid = db.rainbow_to_tweet_query(rid)[0].tweet_id
415 t.statuses.destroy(id=tid)
416 printNicely(green('Okay it\'s gone.'))
417
418
419 def unfavorite():
420 """
421 Unfavorite
422 """
423 t = Twitter(auth=authen())
424 try:
425 id = int(g['stuff'].split()[0])
426 except:
427 printNicely(red('Sorry I can\'t understand.'))
428 return
429 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
430 t.favorites.destroy(_id=tid)
431 printNicely(green('Okay it\'s unfavorited.'))
432 draw(t.statuses.show(id=tid), iot=g['iot'])
433 printNicely('')
434
435
436 def search():
437 """
438 Search
439 """
440 t = Twitter(auth=authen())
441 if g['stuff'].startswith('#'):
442 rel = t.search.tweets(q=g['stuff'])['statuses']
443 if rel:
444 printNicely('Newest tweets:')
445 for i in reversed(xrange(c['SEARCH_MAX_RECORD'])):
446 draw(t=rel[i],
447 iot=g['iot'],
448 keyword=g['stuff'].strip()[1:])
449 printNicely('')
450 else:
451 printNicely(magenta('I\'m afraid there is no result'))
452 else:
453 printNicely(red('A keyword should be a hashtag (like \'#AKB48\')'))
454
455
456 def message():
457 """
458 Send a direct message
459 """
460 t = Twitter(auth=authen())
461 user = g['stuff'].split()[0]
462 if user[0].startswith('@'):
463 try:
464 content = g['stuff'].split()[1]
465 except:
466 printNicely(red('Sorry I can\'t understand.'))
467 t.direct_messages.new(
468 screen_name=user[1:],
469 text=content
470 )
471 printNicely(green('Message sent.'))
472 else:
473 printNicely(red('A name should begin with a \'@\''))
474
475
476 def show():
477 """
478 Show image
479 """
480 t = Twitter(auth=authen())
481 try:
482 target = g['stuff'].split()[0]
483 if target != 'image':
484 return
485 id = int(g['stuff'].split()[1])
486 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
487 tweet = t.statuses.show(id=tid)
488 media = tweet['entities']['media']
489 for m in media:
490 res = requests.get(m['media_url'])
491 img = Image.open(StringIO(res.content))
492 img.show()
493 except:
494 printNicely(red('Sorry I can\'t show this image.'))
495
496
497 def urlopen():
498 """
499 Open url
500 """
501 t = Twitter(auth=authen())
502 try:
503 if not g['stuff'].isdigit():
504 return
505 tid = db.rainbow_to_tweet_query(g['stuff'])[0].tweet_id
506 tweet = t.statuses.show(id=tid)
507 link_ary = [u for u in tweet['text'].split() if u.startswith('http://')]
508 if not link_ary:
509 printNicely(light_magenta('No url here @.@!'))
510 return
511 for link in link_ary:
512 webbrowser.open(link)
513 except:
514 printNicely(red('Sorry I can\'t open url in this tweet.'))
515
516
517 def list():
518 """
519 List friends for followers
520 """
521 t = Twitter(auth=authen())
522 # Get name
523 try:
524 name = g['stuff'].split()[1]
525 if name.startswith('@'):
526 name = name[1:]
527 else:
528 printNicely(red('A name should begin with a \'@\''))
529 raise Exception('Invalid name')
530 except:
531 name = g['original_name']
532 # Get list followers or friends
533 try:
534 target = g['stuff'].split()[0]
535 except:
536 printNicely(red('Omg some syntax is wrong.'))
537 # Init cursor
538 d = {'fl': 'followers', 'fr': 'friends'}
539 next_cursor = -1
540 rel = {}
541 # Cursor loop
542 while next_cursor != 0:
543 list = getattr(t, d[target]).list(
544 screen_name=name,
545 cursor=next_cursor,
546 skip_status=True,
547 include_entities=False,
548 )
549 for u in list['users']:
550 rel[u['name']] = '@' + u['screen_name']
551 next_cursor = list['next_cursor']
552 # Print out result
553 printNicely('All: ' + str(len(rel)) + ' people.')
554 for name in rel:
555 user = ' ' + cycle_color(name) + grey(' ' + rel[name] + ' ')
556 printNicely(user)
557
558
559 def inbox():
560 """
561 Inbox direct messages
562 """
563 t = Twitter(auth=authen())
564 num = c['MESSAGES_DISPLAY']
565 rel = []
566 if g['stuff'].isdigit():
567 num = g['stuff']
568 cur_page = 1
569 # Max message per page is 20 so we have to loop
570 while num > 20:
571 rel = rel + t.direct_messages(
572 count=20,
573 page=cur_page,
574 include_entities=False,
575 skip_status=False
576 )
577 num -= 20
578 cur_page += 1
579 rel = rel + t.direct_messages(
580 count=num,
581 page=cur_page,
582 include_entities=False,
583 skip_status=False
584 )
585 # Display
586 printNicely('Inbox: newest ' + str(len(rel)) + ' messages.')
587 for m in reversed(rel):
588 print_message(m)
589 printNicely('')
590
591
592 def sent():
593 """
594 Sent direct messages
595 """
596 t = Twitter(auth=authen())
597 num = c['MESSAGES_DISPLAY']
598 rel = []
599 if g['stuff'].isdigit():
600 num = int(g['stuff'])
601 cur_page = 1
602 # Max message per page is 20 so we have to loop
603 while num > 20:
604 rel = rel + t.direct_messages.sent(
605 count=20,
606 page=cur_page,
607 include_entities=False,
608 skip_status=False
609 )
610 num -= 20
611 cur_page += 1
612 rel = rel + t.direct_messages.sent(
613 count=num,
614 page=cur_page,
615 include_entities=False,
616 skip_status=False
617 )
618 # Display
619 printNicely('Sent: newest ' + str(len(rel)) + ' messages.')
620 for m in reversed(rel):
621 print_message(m)
622 printNicely('')
623
624
625 def trash():
626 """
627 Remove message
628 """
629 t = Twitter(auth=authen())
630 try:
631 rid = int(g['stuff'].split()[0])
632 except:
633 printNicely(red('Sorry I can\'t understand.'))
634 mid = db.rainbow_to_message_query(rid)[0].message_id
635 t.direct_messages.destroy(id=mid)
636 printNicely(green('Message deleted.'))
637
638
639 def whois():
640 """
641 Show profile of a specific user
642 """
643 t = Twitter(auth=authen())
644 screen_name = g['stuff'].split()[0]
645 if screen_name.startswith('@'):
646 try:
647 user = t.users.show(
648 screen_name=screen_name[1:],
649 include_entities=False)
650 show_profile(user, g['iot'])
651 except:
652 printNicely(red('Omg no user.'))
653 else:
654 printNicely(red('A name should begin with a \'@\''))
655
656
657 def follow():
658 """
659 Follow a user
660 """
661 t = Twitter(auth=authen())
662 screen_name = g['stuff'].split()[0]
663 if screen_name.startswith('@'):
664 t.friendships.create(screen_name=screen_name[1:], follow=True)
665 printNicely(green('You are following ' + screen_name + ' now!'))
666 else:
667 printNicely(red('A name should begin with a \'@\''))
668
669
670 def unfollow():
671 """
672 Unfollow a user
673 """
674 t = Twitter(auth=authen())
675 screen_name = g['stuff'].split()[0]
676 if screen_name.startswith('@'):
677 t.friendships.destroy(
678 screen_name=screen_name[1:],
679 include_entities=False)
680 printNicely(green('Unfollow ' + screen_name + ' success!'))
681 else:
682 printNicely(red('A name should begin with a \'@\''))
683
684
685 def mute():
686 """
687 Mute a user
688 """
689 t = Twitter(auth=authen())
690 try:
691 screen_name = g['stuff'].split()[0]
692 except:
693 printNicely(red('A name should be specified. '))
694 return
695 if screen_name.startswith('@'):
696 rel = t.mutes.users.create(screen_name=screen_name[1:])
697 if isinstance(rel, dict):
698 printNicely(green(screen_name + ' is muted.'))
699 else:
700 printNicely(red(rel))
701 else:
702 printNicely(red('A name should begin with a \'@\''))
703
704
705 def unmute():
706 """
707 Unmute a user
708 """
709 t = Twitter(auth=authen())
710 try:
711 screen_name = g['stuff'].split()[0]
712 except:
713 printNicely(red('A name should be specified. '))
714 return
715 if screen_name.startswith('@'):
716 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
717 if isinstance(rel, dict):
718 printNicely(green(screen_name + ' is unmuted.'))
719 else:
720 printNicely(red(rel))
721 else:
722 printNicely(red('A name should begin with a \'@\''))
723
724
725 def muting():
726 """
727 List muting user
728 """
729 t = Twitter(auth=authen())
730 # Init cursor
731 next_cursor = -1
732 rel = {}
733 # Cursor loop
734 while next_cursor != 0:
735 list = t.mutes.users.list(
736 screen_name=g['original_name'],
737 cursor=next_cursor,
738 skip_status=True,
739 include_entities=False,
740 )
741 for u in list['users']:
742 rel[u['name']] = '@' + u['screen_name']
743 next_cursor = list['next_cursor']
744 # Print out result
745 printNicely('All: ' + str(len(rel)) + ' people.')
746 for name in rel:
747 user = ' ' + cycle_color(name) + grey(' ' + rel[name] + ' ')
748 printNicely(user)
749
750
751 def block():
752 """
753 Block a user
754 """
755 t = Twitter(auth=authen())
756 screen_name = g['stuff'].split()[0]
757 if screen_name.startswith('@'):
758 t.blocks.create(
759 screen_name=screen_name[1:],
760 include_entities=False,
761 skip_status=True)
762 printNicely(green('You blocked ' + screen_name + '.'))
763 else:
764 printNicely(red('A name should begin with a \'@\''))
765
766
767 def unblock():
768 """
769 Unblock a user
770 """
771 t = Twitter(auth=authen())
772 screen_name = g['stuff'].split()[0]
773 if screen_name.startswith('@'):
774 t.blocks.destroy(
775 screen_name=screen_name[1:],
776 include_entities=False,
777 skip_status=True)
778 printNicely(green('Unblock ' + screen_name + ' success!'))
779 else:
780 printNicely(red('A name should begin with a \'@\''))
781
782
783 def report():
784 """
785 Report a user as a spam account
786 """
787 t = Twitter(auth=authen())
788 screen_name = g['stuff'].split()[0]
789 if screen_name.startswith('@'):
790 t.users.report_spam(
791 screen_name=screen_name[1:])
792 printNicely(green('You reported ' + screen_name + '.'))
793 else:
794 printNicely(red('Sorry I can\'t understand.'))
795
796
797 def cal():
798 """
799 Unix's command `cal`
800 """
801 # Format
802 rel = os.popen('cal').read().split('\n')
803 month = rel.pop(0)
804 date = rel.pop(0)
805 show_calendar(month, date, rel)
806
807
808 def theme():
809 """
810 List and change theme
811 """
812 if not g['stuff']:
813 # List themes
814 for theme in g['themes']:
815 line = ''
816 # Detect custom config
817 if theme == 'custom':
818 line += light_magenta('custom')
819 custom_path = os.environ.get(
820 'HOME',
821 os.environ.get('USERPROFILE',
822 '')) + os.sep + '.rainbow_config.json'
823 if not os.path.exists(custom_path):
824 line += light_magenta(' (create your own config file at ~/.rainbow_config.json)')
825 else:
826 line += light_magenta(' (loaded)')
827 else:
828 line += light_magenta(theme)
829 if c['theme'] == theme :
830 line = ' '*2 + light_yellow('* ') + line
831 else:
832 line = ' '*4 + line
833 printNicely(line)
834 elif g['stuff'] == 'current_as_default':
835 # Set default
836 path = os.path.dirname(__file__) + '/colorset/init'
837 f = open(path,'w')
838 f.write(c['theme'])
839 f.close()
840 printNicely(light_green('Okay it will be applied from next time :)'))
841 else:
842 # Change theme
843 try:
844 # Load new config
845 if g['stuff'] != 'custom':
846 new_config = os.path.dirname(__file__) + '/colorset/' + g['stuff'] + '.json'
847 else:
848 new_config = os.environ.get(
849 'HOME',os.environ.get(
850 'USERPROFILE',
851 '')) + os.sep + '.rainbow_config.json'
852 new_config = load_config(new_config)
853 if new_config:
854 for nc in new_config:
855 c[nc] = new_config[nc]
856 # Update db and reset colors
857 db.theme_update(g['stuff'])
858 c['theme'] = g['stuff']
859 reset_cycle()
860 g['decorated_name'] = color_func(
861 c['DECORATED_NAME'])(
862 '[@' + g['original_name'] + ']: ')
863 printNicely(green('Theme changed.'))
864 except:
865 if g['stuff'] == 'custom':
866 printNicely(red('~/.rainbow_config.json is not exists!'))
867 else:
868 printNicely(red('No such theme exists.'))
869
870
871 def help():
872 """
873 Help
874 """
875 s = ' ' * 2
876 h, w = os.popen('stty size', 'r').read().split()
877
878 # Start
879 usage = '\n'
880 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
881 usage += s + '-' * (int(w) - 4) + '\n'
882 usage += s + 'You are ' + \
883 light_yellow('already') + ' on your personal stream.\n'
884 usage += s + 'Any update from Twitter will show up ' + \
885 light_yellow('immediately') + '.\n'
886 usage += s + 'In addtion, following commands are available right now:\n'
887
888 # Discover the world
889 usage += '\n'
890 usage += s + grey(u'\u266A' + ' Discover the world \n')
891 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
892 'You can try ' + light_green('trend US') + ' or ' + \
893 light_green('trend JP Tokyo') + '.\n'
894 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
895 light_green('home 7') + ' will show 7 tweets.\n'
896 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
897 light_green('mentions 7') + ' will show 7 mention tweets.\n'
898 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
899 magenta('@mdo') + '.\n'
900 usage += s * 2 + light_green('view @mdo') + \
901 ' will show ' + magenta('@mdo') + '\'s home.\n'
902 usage += s * 2 + light_green('s #AKB48') + ' will search for "' + \
903 light_yellow('AKB48') + '" and return 5 newest tweet.\n'
904
905 # Tweet
906 usage += '\n'
907 usage += s + grey(u'\u266A' + ' Tweets \n')
908 usage += s * 2 + light_green('t oops ') + \
909 'will tweet "' + light_yellow('oops') + '" immediately.\n'
910 usage += s * 2 + \
911 light_green('rt 12 ') + ' will retweet to tweet with ' + \
912 light_yellow('[id=12]') + '.\n'
913 usage += s * 2 + \
914 light_green('quote 12 ') + ' will quote the tweet with ' + \
915 light_yellow('[id=12]') + '. If no extra text is added, ' + \
916 'the quote will be canceled.\n'
917 usage += s * 2 + \
918 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
919 light_yellow('[id=12]') + '.\n'
920 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
921 light_yellow('oops') + '" to tweet with ' + \
922 light_yellow('[id=12]') + '.\n'
923 usage += s * 2 + \
924 light_green('fav 12 ') + ' will favorite the tweet with ' + \
925 light_yellow('[id=12]') + '.\n'
926 usage += s * 2 + \
927 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
928 light_yellow('[id=12]') + '.\n'
929 usage += s * 2 + \
930 light_green('del 12 ') + ' will delete tweet with ' + \
931 light_yellow('[id=12]') + '.\n'
932 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
933 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
934 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
935 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
936
937 # Direct message
938 usage += '\n'
939 usage += s + grey(u'\u266A' + ' Direct messages \n')
940 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
941 light_green('inbox 7') + ' will show newest 7 messages.\n'
942 usage += s * 2 + light_green('sent') + ' will show sent messages. ' + \
943 light_green('sent 7') + ' will show newest 7 messages.\n'
944 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
945 magenta('@dtvd88') + '.\n'
946 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
947 light_yellow('[message_id=5]') + '.\n'
948
949 # Follower and following
950 usage += '\n'
951 usage += s + grey(u'\u266A' + ' Fiends and followers \n')
952 usage += s * 2 + \
953 light_green('ls fl') + \
954 ' will list all followers (people who are following you).\n'
955 usage += s * 2 + \
956 light_green('ls fr') + \
957 ' will list all friends (people who you are following).\n'
958 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
959 magenta('@dtvd88') + '.\n'
960 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
961 magenta('@dtvd88') + '.\n'
962 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
963 magenta('@dtvd88') + '.\n'
964 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
965 magenta('@dtvd88') + '.\n'
966 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
967 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
968 magenta('@dtvd88') + '.\n'
969 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
970 magenta('@dtvd88') + '.\n'
971 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
972 magenta('@dtvd88') + ' as a spam account.\n'
973
974 # Switch
975 usage += '\n'
976 usage += s + grey(u'\u266A' + ' Switching streams \n')
977 usage += s * 2 + light_green('switch public #AKB') + \
978 ' will switch to public stream and follow "' + \
979 light_yellow('AKB') + '" keyword.\n'
980 usage += s * 2 + light_green('switch mine') + \
981 ' will switch to your personal stream.\n'
982 usage += s * 2 + light_green('switch mine -f ') + \
983 ' will prompt to enter the filter.\n'
984 usage += s * 3 + light_yellow('Only nicks') + \
985 ' filter will decide nicks will be INCLUDE ONLY.\n'
986 usage += s * 3 + light_yellow('Ignore nicks') + \
987 ' filter will decide nicks will be EXCLUDE.\n'
988 usage += s * 2 + light_green('switch mine -d') + \
989 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
990
991 # Smart shell
992 usage += '\n'
993 usage += s + grey(u'\u266A' + ' Smart shell\n')
994 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
995 'will be evaluate by Python interpreter.\n'
996 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
997 ' for current month.\n'
998
999 # Screening
1000 usage += '\n'
1001 usage += s + grey(u'\u266A' + ' Screening \n')
1002 usage += s * 2 + light_green('theme') + ' will list available theme.' + \
1003 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
1004 ' theme immediately.\n'
1005 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1006 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1007 usage += s * 2 + light_green('q') + ' will quit.\n'
1008
1009 # End
1010 usage += '\n'
1011 usage += s + '-' * (int(w) - 4) + '\n'
1012 usage += s + 'Have fun and hang tight! \n'
1013 printNicely(usage)
1014
1015
1016 def clear():
1017 """
1018 Clear screen
1019 """
1020 os.system('clear')
1021
1022
1023 def quit():
1024 """
1025 Exit all
1026 """
1027 save_history()
1028 os.system('rm -rf rainbow.db')
1029 os.kill(g['stream_pid'], signal.SIGKILL)
1030 sys.exit()
1031
1032
1033 def reset():
1034 """
1035 Reset prefix of line
1036 """
1037 if g['reset']:
1038 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
1039 g['reset'] = False
1040 try:
1041 printNicely(str(eval(g['cmd'])))
1042 except Exception:
1043 pass
1044
1045
1046 def process(cmd):
1047 """
1048 Process switch
1049 """
1050 return dict(zip(
1051 cmdset,
1052 [
1053 switch,
1054 trend,
1055 home,
1056 view,
1057 mentions,
1058 tweet,
1059 retweet,
1060 quote,
1061 allretweet,
1062 favorite,
1063 reply,
1064 delete,
1065 unfavorite,
1066 search,
1067 message,
1068 show,
1069 urlopen,
1070 list,
1071 inbox,
1072 sent,
1073 trash,
1074 whois,
1075 follow,
1076 unfollow,
1077 mute,
1078 unmute,
1079 muting,
1080 block,
1081 unblock,
1082 report,
1083 cal,
1084 theme,
1085 help,
1086 clear,
1087 quit
1088 ]
1089 )).get(cmd, reset)
1090
1091
1092 def listen():
1093 """
1094 Listen to user's input
1095 """
1096 d = dict(zip(
1097 cmdset,
1098 [
1099 ['public', 'mine'], # switch
1100 [], # trend
1101 [], # home
1102 ['@'], # view
1103 [], # mentions
1104 [], # tweet
1105 [], # retweet
1106 [], # quote
1107 [], # allretweet
1108 [], # favorite
1109 [], # reply
1110 [], # delete
1111 [], # unfavorite
1112 ['#'], # search
1113 ['@'], # message
1114 ['image'], # show image
1115 [''], # open url
1116 ['fl', 'fr'], # list
1117 [], # inbox
1118 [], # sent
1119 [], # trash
1120 ['@'], # whois
1121 ['@'], # follow
1122 ['@'], # unfollow
1123 ['@'], # mute
1124 ['@'], # unmute
1125 ['@'], # muting
1126 ['@'], # block
1127 ['@'], # unblock
1128 ['@'], # report
1129 [], # cal
1130 g['themes'] + ['current_as_default'], # theme
1131 [], # help
1132 [], # clear
1133 [], # quit
1134 ]
1135 ))
1136 init_interactive_shell(d)
1137 read_history()
1138 reset()
1139 while True:
1140 if g['prefix']:
1141 line = raw_input(g['decorated_name'])
1142 else:
1143 line = raw_input()
1144 try:
1145 cmd = line.split()[0]
1146 except:
1147 cmd = ''
1148 g['cmd'] = cmd
1149 # Save cmd to global variable and call process
1150 try:
1151 g['stuff'] = ' '.join(line.split()[1:])
1152 process(cmd)()
1153 except Exception,e :
1154 print e
1155 printNicely(red('OMG something is wrong with Twitter right now.'))
1156 # Not redisplay prefix
1157 if cmd in ['switch', 't', 'rt', 'rep']:
1158 g['prefix'] = False
1159 else:
1160 g['prefix'] = True
1161
1162
1163 def stream(domain, args, name='Rainbow Stream'):
1164 """
1165 Track the stream
1166 """
1167
1168 # The Logo
1169 art_dict = {
1170 c['USER_DOMAIN']: name,
1171 c['PUBLIC_DOMAIN']: args.track_keywords,
1172 c['SITE_DOMAIN']: 'Site Stream',
1173 }
1174 if g['ascii_art']:
1175 ascii_art(art_dict[domain])
1176
1177 # These arguments are optional:
1178 stream_args = dict(
1179 timeout=args.timeout,
1180 block=not args.no_block,
1181 heartbeat_timeout=args.heartbeat_timeout)
1182
1183 # Track keyword
1184 query_args = dict()
1185 if args.track_keywords:
1186 query_args['track'] = args.track_keywords
1187
1188 # Get stream
1189 stream = TwitterStream(
1190 auth=authen(),
1191 domain=domain,
1192 **stream_args)
1193
1194 try:
1195 if domain == c['USER_DOMAIN']:
1196 tweet_iter = stream.user(**query_args)
1197 elif domain == c['SITE_DOMAIN']:
1198 tweet_iter = stream.site(**query_args)
1199 else:
1200 if args.track_keywords:
1201 tweet_iter = stream.statuses.filter(**query_args)
1202 else:
1203 tweet_iter = stream.statuses.sample()
1204
1205 # Iterate over the stream.
1206 for tweet in tweet_iter:
1207 if tweet is None:
1208 printNicely("-- None --")
1209 elif tweet is Timeout:
1210 printNicely("-- Timeout --")
1211 elif tweet is HeartbeatTimeout:
1212 printNicely("-- Heartbeat Timeout --")
1213 elif tweet is Hangup:
1214 printNicely("-- Hangup --")
1215 elif tweet.get('text'):
1216 draw(
1217 t=tweet,
1218 iot=args.image_on_term,
1219 keyword=args.track_keywords,
1220 fil=args.filter,
1221 ig=args.ignore,
1222 )
1223 except TwitterHTTPError:
1224 printNicely('')
1225 printNicely(
1226 magenta("We have maximum connection problem with twitter'stream API right now :("))
1227
1228
1229 def fly():
1230 """
1231 Main function
1232 """
1233 # Spawn stream process
1234 args = parse_arguments()
1235 try:
1236 get_decorated_name()
1237
1238 except TwitterHTTPError:
1239 printNicely('')
1240 printNicely(
1241 magenta("I'm afraid we have maximum connection problem with twitter right now :("))
1242 printNicely(magenta("Let's try again later."))
1243 save_history()
1244 os.system('rm -rf rainbow.db')
1245 sys.exit()
1246
1247 p = Process(
1248 target=stream,
1249 args=(
1250 c['USER_DOMAIN'],
1251 args,
1252 g['original_name']))
1253 p.start()
1254
1255 # Start listen process
1256 time.sleep(0.5)
1257 g['reset'] = True
1258 g['prefix'] = True
1259 g['stream_pid'] = p.pid
1260 g['iot'] = args.image_on_term
1261 listen()