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