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