fix image IOError
[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 os.system('chmod 777 ' + path)
841 printNicely(light_green('Okay it will be applied from next time :)'))
842 else:
843 # Change theme
844 try:
845 # Load new config
846 if g['stuff'] != 'custom':
847 new_config = os.path.dirname(__file__) + '/colorset/' + g['stuff'] + '.json'
848 else:
849 new_config = os.environ.get(
850 'HOME',os.environ.get(
851 'USERPROFILE',
852 '')) + os.sep + '.rainbow_config.json'
853 new_config = load_config(new_config)
854 if new_config:
855 for nc in new_config:
856 c[nc] = new_config[nc]
857 # Update db and reset colors
858 db.theme_update(g['stuff'])
859 c['theme'] = g['stuff']
860 reset_cycle()
861 g['decorated_name'] = color_func(
862 c['DECORATED_NAME'])(
863 '[@' + g['original_name'] + ']: ')
864 printNicely(green('Theme changed.'))
865 except:
866 if g['stuff'] == 'custom':
867 printNicely(red('~/.rainbow_config.json is not exists!'))
868 else:
869 printNicely(red('No such theme exists.'))
870
871
872 def help():
873 """
874 Help
875 """
876 s = ' ' * 2
877 h, w = os.popen('stty size', 'r').read().split()
878
879 # Start
880 usage = '\n'
881 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
882 usage += s + '-' * (int(w) - 4) + '\n'
883 usage += s + 'You are ' + \
884 light_yellow('already') + ' on your personal stream.\n'
885 usage += s + 'Any update from Twitter will show up ' + \
886 light_yellow('immediately') + '.\n'
887 usage += s + 'In addtion, following commands are available right now:\n'
888
889 # Discover the world
890 usage += '\n'
891 usage += s + grey(u'\u266A' + ' Discover the world \n')
892 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
893 'You can try ' + light_green('trend US') + ' or ' + \
894 light_green('trend JP Tokyo') + '.\n'
895 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
896 light_green('home 7') + ' will show 7 tweets.\n'
897 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
898 light_green('mentions 7') + ' will show 7 mention tweets.\n'
899 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
900 magenta('@mdo') + '.\n'
901 usage += s * 2 + light_green('view @mdo') + \
902 ' will show ' + magenta('@mdo') + '\'s home.\n'
903 usage += s * 2 + light_green('s #AKB48') + ' will search for "' + \
904 light_yellow('AKB48') + '" and return 5 newest tweet.\n'
905
906 # Tweet
907 usage += '\n'
908 usage += s + grey(u'\u266A' + ' Tweets \n')
909 usage += s * 2 + light_green('t oops ') + \
910 'will tweet "' + light_yellow('oops') + '" immediately.\n'
911 usage += s * 2 + \
912 light_green('rt 12 ') + ' will retweet to tweet with ' + \
913 light_yellow('[id=12]') + '.\n'
914 usage += s * 2 + \
915 light_green('quote 12 ') + ' will quote the tweet with ' + \
916 light_yellow('[id=12]') + '. If no extra text is added, ' + \
917 'the quote will be canceled.\n'
918 usage += s * 2 + \
919 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
920 light_yellow('[id=12]') + '.\n'
921 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
922 light_yellow('oops') + '" to tweet with ' + \
923 light_yellow('[id=12]') + '.\n'
924 usage += s * 2 + \
925 light_green('fav 12 ') + ' will favorite the tweet with ' + \
926 light_yellow('[id=12]') + '.\n'
927 usage += s * 2 + \
928 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
929 light_yellow('[id=12]') + '.\n'
930 usage += s * 2 + \
931 light_green('del 12 ') + ' will delete tweet with ' + \
932 light_yellow('[id=12]') + '.\n'
933 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
934 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
935 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
936 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
937
938 # Direct message
939 usage += '\n'
940 usage += s + grey(u'\u266A' + ' Direct messages \n')
941 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
942 light_green('inbox 7') + ' will show newest 7 messages.\n'
943 usage += s * 2 + light_green('sent') + ' will show sent messages. ' + \
944 light_green('sent 7') + ' will show newest 7 messages.\n'
945 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
946 magenta('@dtvd88') + '.\n'
947 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
948 light_yellow('[message_id=5]') + '.\n'
949
950 # Follower and following
951 usage += '\n'
952 usage += s + grey(u'\u266A' + ' Friends and followers \n')
953 usage += s * 2 + \
954 light_green('ls fl') + \
955 ' will list all followers (people who are following you).\n'
956 usage += s * 2 + \
957 light_green('ls fr') + \
958 ' will list all friends (people who you are following).\n'
959 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
960 magenta('@dtvd88') + '.\n'
961 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
962 magenta('@dtvd88') + '.\n'
963 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
964 magenta('@dtvd88') + '.\n'
965 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
966 magenta('@dtvd88') + '.\n'
967 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
968 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
969 magenta('@dtvd88') + '.\n'
970 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
971 magenta('@dtvd88') + '.\n'
972 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
973 magenta('@dtvd88') + ' as a spam account.\n'
974
975 # Switch
976 usage += '\n'
977 usage += s + grey(u'\u266A' + ' Switching streams \n')
978 usage += s * 2 + light_green('switch public #AKB') + \
979 ' will switch to public stream and follow "' + \
980 light_yellow('AKB') + '" keyword.\n'
981 usage += s * 2 + light_green('switch mine') + \
982 ' will switch to your personal stream.\n'
983 usage += s * 2 + light_green('switch mine -f ') + \
984 ' will prompt to enter the filter.\n'
985 usage += s * 3 + light_yellow('Only nicks') + \
986 ' filter will decide nicks will be INCLUDE ONLY.\n'
987 usage += s * 3 + light_yellow('Ignore nicks') + \
988 ' filter will decide nicks will be EXCLUDE.\n'
989 usage += s * 2 + light_green('switch mine -d') + \
990 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
991
992 # Smart shell
993 usage += '\n'
994 usage += s + grey(u'\u266A' + ' Smart shell\n')
995 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
996 'will be evaluate by Python interpreter.\n'
997 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
998 ' for current month.\n'
999
1000 # Screening
1001 usage += '\n'
1002 usage += s + grey(u'\u266A' + ' Screening \n')
1003 usage += s * 2 + light_green('theme') + ' will list available theme.' + \
1004 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
1005 ' theme immediately.\n'
1006 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1007 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1008 usage += s * 2 + light_green('q') + ' will quit.\n'
1009
1010 # End
1011 usage += '\n'
1012 usage += s + '-' * (int(w) - 4) + '\n'
1013 usage += s + 'Have fun and hang tight! \n'
1014 printNicely(usage)
1015
1016
1017 def clear():
1018 """
1019 Clear screen
1020 """
1021 os.system('clear')
1022
1023
1024 def quit():
1025 """
1026 Exit all
1027 """
1028 save_history()
1029 os.system('rm -rf rainbow.db')
1030 os.kill(g['stream_pid'], signal.SIGKILL)
1031 sys.exit()
1032
1033
1034 def reset():
1035 """
1036 Reset prefix of line
1037 """
1038 if g['reset']:
1039 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
1040 g['reset'] = False
1041 try:
1042 printNicely(str(eval(g['cmd'])))
1043 except Exception:
1044 pass
1045
1046
1047 def process(cmd):
1048 """
1049 Process switch
1050 """
1051 return dict(zip(
1052 cmdset,
1053 [
1054 switch,
1055 trend,
1056 home,
1057 view,
1058 mentions,
1059 tweet,
1060 retweet,
1061 quote,
1062 allretweet,
1063 favorite,
1064 reply,
1065 delete,
1066 unfavorite,
1067 search,
1068 message,
1069 show,
1070 urlopen,
1071 list,
1072 inbox,
1073 sent,
1074 trash,
1075 whois,
1076 follow,
1077 unfollow,
1078 mute,
1079 unmute,
1080 muting,
1081 block,
1082 unblock,
1083 report,
1084 cal,
1085 theme,
1086 help,
1087 clear,
1088 quit
1089 ]
1090 )).get(cmd, reset)
1091
1092
1093 def listen():
1094 """
1095 Listen to user's input
1096 """
1097 d = dict(zip(
1098 cmdset,
1099 [
1100 ['public', 'mine'], # switch
1101 [], # trend
1102 [], # home
1103 ['@'], # view
1104 [], # mentions
1105 [], # tweet
1106 [], # retweet
1107 [], # quote
1108 [], # allretweet
1109 [], # favorite
1110 [], # reply
1111 [], # delete
1112 [], # unfavorite
1113 ['#'], # search
1114 ['@'], # message
1115 ['image'], # show image
1116 [''], # open url
1117 ['fl', 'fr'], # list
1118 [], # inbox
1119 [], # sent
1120 [], # trash
1121 ['@'], # whois
1122 ['@'], # follow
1123 ['@'], # unfollow
1124 ['@'], # mute
1125 ['@'], # unmute
1126 ['@'], # muting
1127 ['@'], # block
1128 ['@'], # unblock
1129 ['@'], # report
1130 [], # cal
1131 g['themes'] + ['current_as_default'], # theme
1132 [], # help
1133 [], # clear
1134 [], # quit
1135 ]
1136 ))
1137 init_interactive_shell(d)
1138 read_history()
1139 reset()
1140 while True:
1141 if g['prefix']:
1142 line = raw_input(g['decorated_name'])
1143 else:
1144 line = raw_input()
1145 try:
1146 cmd = line.split()[0]
1147 except:
1148 cmd = ''
1149 g['cmd'] = cmd
1150 # Save cmd to global variable and call process
1151 try:
1152 g['stuff'] = ' '.join(line.split()[1:])
1153 process(cmd)()
1154 except Exception,e :
1155 print e
1156 printNicely(red('OMG something is wrong with Twitter right now.'))
1157 # Not redisplay prefix
1158 if cmd in ['switch', 't', 'rt', 'rep']:
1159 g['prefix'] = False
1160 else:
1161 g['prefix'] = True
1162
1163
1164 def stream(domain, args, name='Rainbow Stream'):
1165 """
1166 Track the stream
1167 """
1168
1169 # The Logo
1170 art_dict = {
1171 c['USER_DOMAIN']: name,
1172 c['PUBLIC_DOMAIN']: args.track_keywords,
1173 c['SITE_DOMAIN']: 'Site Stream',
1174 }
1175 if g['ascii_art']:
1176 ascii_art(art_dict[domain])
1177
1178 # These arguments are optional:
1179 stream_args = dict(
1180 timeout=args.timeout,
1181 block=not args.no_block,
1182 heartbeat_timeout=args.heartbeat_timeout)
1183
1184 # Track keyword
1185 query_args = dict()
1186 if args.track_keywords:
1187 query_args['track'] = args.track_keywords
1188
1189 # Get stream
1190 stream = TwitterStream(
1191 auth=authen(),
1192 domain=domain,
1193 **stream_args)
1194
1195 try:
1196 if domain == c['USER_DOMAIN']:
1197 tweet_iter = stream.user(**query_args)
1198 elif domain == c['SITE_DOMAIN']:
1199 tweet_iter = stream.site(**query_args)
1200 else:
1201 if args.track_keywords:
1202 tweet_iter = stream.statuses.filter(**query_args)
1203 else:
1204 tweet_iter = stream.statuses.sample()
1205
1206 # Iterate over the stream.
1207 for tweet in tweet_iter:
1208 if tweet is None:
1209 printNicely("-- None --")
1210 elif tweet is Timeout:
1211 printNicely("-- Timeout --")
1212 elif tweet is HeartbeatTimeout:
1213 printNicely("-- Heartbeat Timeout --")
1214 elif tweet is Hangup:
1215 printNicely("-- Hangup --")
1216 elif tweet.get('text'):
1217 draw(
1218 t=tweet,
1219 iot=args.image_on_term,
1220 keyword=args.track_keywords,
1221 fil=args.filter,
1222 ig=args.ignore,
1223 )
1224 except TwitterHTTPError:
1225 printNicely('')
1226 printNicely(
1227 magenta("We have maximum connection problem with twitter'stream API right now :("))
1228
1229
1230 def fly():
1231 """
1232 Main function
1233 """
1234 # Spawn stream process
1235 args = parse_arguments()
1236 try:
1237 get_decorated_name()
1238
1239 except TwitterHTTPError:
1240 printNicely('')
1241 printNicely(
1242 magenta("I'm afraid we have maximum connection problem with twitter right now :("))
1243 printNicely(magenta("Let's try again later."))
1244 save_history()
1245 os.system('rm -rf rainbow.db')
1246 sys.exit()
1247
1248 p = Process(
1249 target=stream,
1250 args=(
1251 c['USER_DOMAIN'],
1252 args,
1253 g['original_name']))
1254 p.start()
1255
1256 # Start listen process
1257 time.sleep(0.5)
1258 g['reset'] = True
1259 g['prefix'] = True
1260 g['stream_pid'] = p.pid
1261 g['iot'] = args.image_on_term
1262 listen()