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