add list support and seperate help
[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 'list',
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 ls():
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)) + ' ' + d[target] + '.')
555 for name in rel:
556 user = ' ' + cycle_color(name)
557 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
558 printNicely(user)
559
560
561 def inbox():
562 """
563 Inbox direct messages
564 """
565 t = Twitter(auth=authen())
566 num = c['MESSAGES_DISPLAY']
567 rel = []
568 if g['stuff'].isdigit():
569 num = g['stuff']
570 cur_page = 1
571 # Max message per page is 20 so we have to loop
572 while num > 20:
573 rel = rel + t.direct_messages(
574 count=20,
575 page=cur_page,
576 include_entities=False,
577 skip_status=False
578 )
579 num -= 20
580 cur_page += 1
581 rel = rel + t.direct_messages(
582 count=num,
583 page=cur_page,
584 include_entities=False,
585 skip_status=False
586 )
587 # Display
588 printNicely('Inbox: newest ' + str(len(rel)) + ' messages.')
589 for m in reversed(rel):
590 print_message(m)
591 printNicely('')
592
593
594 def sent():
595 """
596 Sent direct messages
597 """
598 t = Twitter(auth=authen())
599 num = c['MESSAGES_DISPLAY']
600 rel = []
601 if g['stuff'].isdigit():
602 num = int(g['stuff'])
603 cur_page = 1
604 # Max message per page is 20 so we have to loop
605 while num > 20:
606 rel = rel + t.direct_messages.sent(
607 count=20,
608 page=cur_page,
609 include_entities=False,
610 skip_status=False
611 )
612 num -= 20
613 cur_page += 1
614 rel = rel + t.direct_messages.sent(
615 count=num,
616 page=cur_page,
617 include_entities=False,
618 skip_status=False
619 )
620 # Display
621 printNicely('Sent: newest ' + str(len(rel)) + ' messages.')
622 for m in reversed(rel):
623 print_message(m)
624 printNicely('')
625
626
627 def trash():
628 """
629 Remove message
630 """
631 t = Twitter(auth=authen())
632 try:
633 rid = int(g['stuff'].split()[0])
634 except:
635 printNicely(red('Sorry I can\'t understand.'))
636 mid = db.rainbow_to_message_query(rid)[0].message_id
637 t.direct_messages.destroy(id=mid)
638 printNicely(green('Message deleted.'))
639
640
641 def whois():
642 """
643 Show profile of a specific user
644 """
645 t = Twitter(auth=authen())
646 screen_name = g['stuff'].split()[0]
647 if screen_name.startswith('@'):
648 try:
649 user = t.users.show(
650 screen_name=screen_name[1:],
651 include_entities=False)
652 show_profile(user, g['iot'])
653 except:
654 printNicely(red('Omg no user.'))
655 else:
656 printNicely(red('A name should begin with a \'@\''))
657
658
659 def follow():
660 """
661 Follow a user
662 """
663 t = Twitter(auth=authen())
664 screen_name = g['stuff'].split()[0]
665 if screen_name.startswith('@'):
666 t.friendships.create(screen_name=screen_name[1:], follow=True)
667 printNicely(green('You are following ' + screen_name + ' now!'))
668 else:
669 printNicely(red('A name should begin with a \'@\''))
670
671
672 def unfollow():
673 """
674 Unfollow a user
675 """
676 t = Twitter(auth=authen())
677 screen_name = g['stuff'].split()[0]
678 if screen_name.startswith('@'):
679 t.friendships.destroy(
680 screen_name=screen_name[1:],
681 include_entities=False)
682 printNicely(green('Unfollow ' + screen_name + ' success!'))
683 else:
684 printNicely(red('A name should begin with a \'@\''))
685
686
687 def mute():
688 """
689 Mute a user
690 """
691 t = Twitter(auth=authen())
692 try:
693 screen_name = g['stuff'].split()[0]
694 except:
695 printNicely(red('A name should be specified. '))
696 return
697 if screen_name.startswith('@'):
698 rel = t.mutes.users.create(screen_name=screen_name[1:])
699 if isinstance(rel, dict):
700 printNicely(green(screen_name + ' is muted.'))
701 else:
702 printNicely(red(rel))
703 else:
704 printNicely(red('A name should begin with a \'@\''))
705
706
707 def unmute():
708 """
709 Unmute a user
710 """
711 t = Twitter(auth=authen())
712 try:
713 screen_name = g['stuff'].split()[0]
714 except:
715 printNicely(red('A name should be specified. '))
716 return
717 if screen_name.startswith('@'):
718 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
719 if isinstance(rel, dict):
720 printNicely(green(screen_name + ' is unmuted.'))
721 else:
722 printNicely(red(rel))
723 else:
724 printNicely(red('A name should begin with a \'@\''))
725
726
727 def muting():
728 """
729 List muting user
730 """
731 t = Twitter(auth=authen())
732 # Init cursor
733 next_cursor = -1
734 rel = {}
735 # Cursor loop
736 while next_cursor != 0:
737 list = t.mutes.users.list(
738 screen_name=g['original_name'],
739 cursor=next_cursor,
740 skip_status=True,
741 include_entities=False,
742 )
743 for u in list['users']:
744 rel[u['name']] = '@' + u['screen_name']
745 next_cursor = list['next_cursor']
746 # Print out result
747 printNicely('All: ' + str(len(rel)) + ' people.')
748 for name in rel:
749 user = ' ' + cycle_color(name)
750 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
751 printNicely(user)
752
753
754 def block():
755 """
756 Block a user
757 """
758 t = Twitter(auth=authen())
759 screen_name = g['stuff'].split()[0]
760 if screen_name.startswith('@'):
761 t.blocks.create(
762 screen_name=screen_name[1:],
763 include_entities=False,
764 skip_status=True)
765 printNicely(green('You blocked ' + screen_name + '.'))
766 else:
767 printNicely(red('A name should begin with a \'@\''))
768
769
770 def unblock():
771 """
772 Unblock a user
773 """
774 t = Twitter(auth=authen())
775 screen_name = g['stuff'].split()[0]
776 if screen_name.startswith('@'):
777 t.blocks.destroy(
778 screen_name=screen_name[1:],
779 include_entities=False,
780 skip_status=True)
781 printNicely(green('Unblock ' + screen_name + ' success!'))
782 else:
783 printNicely(red('A name should begin with a \'@\''))
784
785
786 def report():
787 """
788 Report a user as a spam account
789 """
790 t = Twitter(auth=authen())
791 screen_name = g['stuff'].split()[0]
792 if screen_name.startswith('@'):
793 t.users.report_spam(
794 screen_name=screen_name[1:])
795 printNicely(green('You reported ' + screen_name + '.'))
796 else:
797 printNicely(red('Sorry I can\'t understand.'))
798
799
800 def show_lists(t):
801 """
802 list list
803 """
804 rel = t.lists.list(screen_name=g['original_name'])
805 if rel:
806 print_list(rel)
807 else:
808 printNicely(light_magenta('You belong to no lists :)'))
809
810
811 def list_home(t):
812 """
813 List home
814 """
815 # Get list name
816 list_name = raw_input(light_magenta('Give me the list\'s name: '))
817 if not list_name:
818 printNicely(light_magenta('No list specified.'))
819 return
820 # Print timeline
821 l = db.list_name_to_id_query(list_name)[0]
822 res = t.lists.statuses(
823 list_id = l.list_id,
824 count = c['LIST_MAX'],
825 include_entities=False)
826 for tweet in res:
827 draw(t=tweet)
828 printNicely('')
829
830
831 def list_members(t):
832 """
833 List members
834 """
835 # Get list name
836 list_name = raw_input(light_magenta('Give me the list\'s name: '))
837 if not list_name:
838 printNicely(light_magenta('No list specified.'))
839 return
840 # Members
841 l = db.list_name_to_id_query(list_name)[0]
842 rel = {}
843 next_cursor = -1
844 while next_cursor != 0 :
845 m = t.lists.members(
846 list_id = l.list_id,
847 cursor = next_cursor,
848 include_entities=False)
849 for u in m['users']:
850 rel[u['name']] = '@' + u['screen_name']
851 next_cursor = m['next_cursor']
852 printNicely('All: ' + str(len(rel)) + ' members.')
853 for name in rel:
854 user = ' ' + cycle_color(name)
855 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
856 printNicely(user)
857
858
859 def list_subscribers(t):
860 """
861 List subscribers
862 """
863 # Get list name
864 list_name = raw_input(light_magenta('Give me the list\'s name: '))
865 if not list_name:
866 printNicely(light_magenta('No list specified.'))
867 return
868 # Subscribers
869 l = db.list_name_to_id_query(list_name)[0]
870 rel = {}
871 next_cursor = -1
872 while next_cursor != 0 :
873 m = t.lists.subscribers(
874 list_id = l.list_id,
875 cursor = next_cursor,
876 include_entities=False)
877 for u in m['users']:
878 rel[u['name']] = '@' + u['screen_name']
879 next_cursor = m['next_cursor']
880 printNicely('All: ' + str(len(rel)) + ' subscribers.')
881 for name in rel:
882 user = ' ' + cycle_color(name)
883 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
884 printNicely(user)
885
886
887 def list_remove(t):
888 """
889 Remove specific user from a list
890 """
891 # Get list name
892 list_name = raw_input(light_magenta('Give me the list\'s name: '))
893 if not list_name:
894 printNicely(light_magenta('No list specified.'))
895 return
896 # Remove
897 l = db.list_name_to_id_query(list_name)[0]
898 user_name = raw_input(light_magenta('Give me the name of unlucky man: '))
899 try:
900 t.lists.members.destroy(
901 list_id = l.list_id,
902 screen_name = user_name)
903 printNicely(light_green('Okay he\'s gone :)'))
904 except:
905 printNicely(light_magenta('I\'m sorry we can not remove him.'))
906
907
908 def list():
909 """
910 Twitter's list
911 """
912 t = Twitter(auth=authen())
913 # List all lists or base on action
914 try:
915 g['list_action'] = g['stuff'].split()[0]
916 except:
917 show_lists(t)
918 return
919
920 # Sub function
921 action_ary = {
922 'home': list_home,
923 'all_mem': list_members,
924 'all_sub': list_subscribers,
925 'rm': list_remove,
926 }
927 try:
928 return action_ary[g['list_action']](t)
929 except:
930 printNicely(red('Sorry I can\'t understand.'))
931
932
933 def cal():
934 """
935 Unix's command `cal`
936 """
937 # Format
938 rel = os.popen('cal').read().split('\n')
939 month = rel.pop(0)
940 date = rel.pop(0)
941 show_calendar(month, date, rel)
942
943
944 def theme():
945 """
946 List and change theme
947 """
948 if not g['stuff']:
949 # List themes
950 for theme in g['themes']:
951 line = ''
952 # Detect custom config
953 if theme == 'custom':
954 line += light_magenta('custom')
955 custom_path = os.environ.get(
956 'HOME',
957 os.environ.get('USERPROFILE',
958 '')) + os.sep + '.rainbow_config.json'
959 if not os.path.exists(custom_path):
960 line += light_magenta(' (create your own config file at ~/.rainbow_config.json)')
961 else:
962 line += light_magenta(' (loaded)')
963 else:
964 line += light_magenta(theme)
965 if c['theme'] == theme :
966 line = ' '*2 + light_yellow('* ') + line
967 else:
968 line = ' '*4 + line
969 printNicely(line)
970 elif g['stuff'] == 'current_as_default':
971 # Set default
972 path = os.path.dirname(__file__) + '/colorset/init'
973 f = open(path,'w')
974 f.write(c['theme'])
975 f.close()
976 os.system('chmod 777 ' + path)
977 printNicely(light_green('Okay it will be applied from next time :)'))
978 else:
979 # Change theme
980 try:
981 # Load new config
982 if g['stuff'] != 'custom':
983 new_config = os.path.dirname(__file__) + '/colorset/' + g['stuff'] + '.json'
984 else:
985 new_config = os.environ.get(
986 'HOME',os.environ.get(
987 'USERPROFILE',
988 '')) + os.sep + '.rainbow_config.json'
989 new_config = load_config(new_config)
990 if new_config:
991 for nc in new_config:
992 c[nc] = new_config[nc]
993 # Update db and reset colors
994 db.theme_update(g['stuff'])
995 c['theme'] = g['stuff']
996 reset_cycle()
997 g['decorated_name'] = color_func(
998 c['DECORATED_NAME'])(
999 '[@' + g['original_name'] + ']: ')
1000 printNicely(green('Theme changed.'))
1001 except:
1002 if g['stuff'] == 'custom':
1003 printNicely(red('~/.rainbow_config.json is not exists!'))
1004 else:
1005 printNicely(red('No such theme exists.'))
1006
1007
1008 def help_discover():
1009 """
1010 Discover the world
1011 """
1012 s = ' ' * 2
1013 # Discover the world
1014 usage = '\n'
1015 usage += s + grey(u'\u266A' + ' Discover the world \n')
1016 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
1017 'You can try ' + light_green('trend US') + ' or ' + \
1018 light_green('trend JP Tokyo') + '.\n'
1019 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
1020 light_green('home 7') + ' will show 7 tweets.\n'
1021 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
1022 light_green('mentions 7') + ' will show 7 mention tweets.\n'
1023 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
1024 magenta('@mdo') + '.\n'
1025 usage += s * 2 + light_green('view @mdo') + \
1026 ' will show ' + magenta('@mdo') + '\'s home.\n'
1027 usage += s * 2 + light_green('s #AKB48') + ' will search for "' + \
1028 light_yellow('AKB48') + '" and return 5 newest tweet.\n'
1029 printNicely(usage)
1030
1031
1032 def help_tweets():
1033 """
1034 Tweets
1035 """
1036 s = ' ' * 2
1037 # Tweet
1038 usage = '\n'
1039 usage += s + grey(u'\u266A' + ' Tweets \n')
1040 usage += s * 2 + light_green('t oops ') + \
1041 'will tweet "' + light_yellow('oops') + '" immediately.\n'
1042 usage += s * 2 + \
1043 light_green('rt 12 ') + ' will retweet to tweet with ' + \
1044 light_yellow('[id=12]') + '.\n'
1045 usage += s * 2 + \
1046 light_green('quote 12 ') + ' will quote the tweet with ' + \
1047 light_yellow('[id=12]') + '. If no extra text is added, ' + \
1048 'the quote will be canceled.\n'
1049 usage += s * 2 + \
1050 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
1051 light_yellow('[id=12]') + '.\n'
1052 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
1053 light_yellow('oops') + '" to tweet with ' + \
1054 light_yellow('[id=12]') + '.\n'
1055 usage += s * 2 + \
1056 light_green('fav 12 ') + ' will favorite the tweet with ' + \
1057 light_yellow('[id=12]') + '.\n'
1058 usage += s * 2 + \
1059 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
1060 light_yellow('[id=12]') + '.\n'
1061 usage += s * 2 + \
1062 light_green('del 12 ') + ' will delete tweet with ' + \
1063 light_yellow('[id=12]') + '.\n'
1064 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
1065 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
1066 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
1067 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
1068 printNicely(usage)
1069
1070
1071 def help_messages():
1072 """
1073 Messages
1074 """
1075 s = ' ' * 2
1076 # Direct message
1077 usage = '\n'
1078 usage += s + grey(u'\u266A' + ' Direct messages \n')
1079 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
1080 light_green('inbox 7') + ' will show newest 7 messages.\n'
1081 usage += s * 2 + light_green('sent') + ' will show sent messages. ' + \
1082 light_green('sent 7') + ' will show newest 7 messages.\n'
1083 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
1084 magenta('@dtvd88') + '.\n'
1085 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
1086 light_yellow('[message_id=5]') + '.\n'
1087 printNicely(usage)
1088
1089
1090 def help_friends_and_followers():
1091 """
1092 Friends and Followers
1093 """
1094 s = ' ' * 2
1095 # Follower and following
1096 usage = '\n'
1097 usage += s + grey(u'\u266A' + ' Friends and followers \n')
1098 usage += s * 2 + \
1099 light_green('ls fl') + \
1100 ' will list all followers (people who are following you).\n'
1101 usage += s * 2 + \
1102 light_green('ls fr') + \
1103 ' will list all friends (people who you are following).\n'
1104 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
1105 magenta('@dtvd88') + '.\n'
1106 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
1107 magenta('@dtvd88') + '.\n'
1108 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
1109 magenta('@dtvd88') + '.\n'
1110 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
1111 magenta('@dtvd88') + '.\n'
1112 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
1113 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
1114 magenta('@dtvd88') + '.\n'
1115 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
1116 magenta('@dtvd88') + '.\n'
1117 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
1118 magenta('@dtvd88') + ' as a spam account.\n'
1119 printNicely(usage)
1120
1121
1122 def help_list():
1123 """
1124 Lists
1125 """
1126 s = ' ' * 2
1127 # Twitter list
1128 usage = '\n'
1129 usage += s + grey(u'\u266A' + ' Twitter list\n')
1130 usage += s * 2 + light_green('list') + \
1131 ' will show all lists you are belong to.\n'
1132 usage += s * 2 + light_green('list home') + \
1133 ' will show timeline of list. You will be asked for list\'s name\n'
1134 usage += s * 2 + light_green('list mem') + \
1135 ' will show list\'s all members.\n'
1136 usage += s * 2 + light_green('list sub') + \
1137 ' will show list\'s all subscribers.\n'
1138 usage += s * 2 + light_green('list rm') + \
1139 ' will remove specific person from a list owned by you.' + \
1140 ' You will be asked for list\'s name and person\'s name.\n'
1141 printNicely(usage)
1142
1143
1144 def help_stream():
1145 """
1146 Stream switch
1147 """
1148 s = ' ' * 2
1149 # Switch
1150 usage = '\n'
1151 usage += s + grey(u'\u266A' + ' Switching streams \n')
1152 usage += s * 2 + light_green('switch public #AKB') + \
1153 ' will switch to public stream and follow "' + \
1154 light_yellow('AKB') + '" keyword.\n'
1155 usage += s * 2 + light_green('switch mine') + \
1156 ' will switch to your personal stream.\n'
1157 usage += s * 2 + light_green('switch mine -f ') + \
1158 ' will prompt to enter the filter.\n'
1159 usage += s * 3 + light_yellow('Only nicks') + \
1160 ' filter will decide nicks will be INCLUDE ONLY.\n'
1161 usage += s * 3 + light_yellow('Ignore nicks') + \
1162 ' filter will decide nicks will be EXCLUDE.\n'
1163 usage += s * 2 + light_green('switch mine -d') + \
1164 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
1165 printNicely(usage)
1166
1167
1168 def help():
1169 """
1170 Help
1171 """
1172 s = ' ' * 2
1173 h, w = os.popen('stty size', 'r').read().split()
1174
1175 # Start
1176 usage = '\n'
1177 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
1178 usage += s + '-' * (int(w) - 4) + '\n'
1179 usage += s + 'You are ' + \
1180 light_yellow('already') + ' on your personal stream.\n'
1181 usage += s + 'Any update from Twitter will show up ' + \
1182 light_yellow('immediately') + '.\n'
1183 usage += s + 'In addtion, following commands are available right now:\n'
1184
1185 # Twitter help section
1186 usage += '\n'
1187 usage += s + grey(u'\u266A' + ' Twitter help\n')
1188 usage += s * 2 + light_green('h discover') + \
1189 ' will show help for discover commands.\n'
1190 usage += s * 2 + light_green('h tweets') + \
1191 ' will show help for tweets commands.\n'
1192 usage += s * 2 + light_green('h messages') + \
1193 ' will show help for messages commands.\n'
1194 usage += s * 2 + light_green('h friends_and_followers') + \
1195 ' will show help for friends and followers commands.\n'
1196 usage += s * 2 + light_green('h list') + \
1197 ' will show help for list commands.\n'
1198 usage += s * 2 + light_green('h stream') + \
1199 ' will show help for stream commands.\n'
1200
1201 # Smart shell
1202 usage += '\n'
1203 usage += s + grey(u'\u266A' + ' Smart shell\n')
1204 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1205 'will be evaluate by Python interpreter.\n'
1206 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1207 ' for current month.\n'
1208
1209 # Screening
1210 usage += '\n'
1211 usage += s + grey(u'\u266A' + ' Screening \n')
1212 usage += s * 2 + light_green('theme') + ' will list available theme.' + \
1213 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
1214 ' theme immediately.\n'
1215 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1216 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1217 usage += s * 2 + light_green('q') + ' will quit.\n'
1218
1219 # End
1220 usage += '\n'
1221 usage += s + '-' * (int(w) - 4) + '\n'
1222 usage += s + 'Have fun and hang tight! \n'
1223
1224 # Show help
1225 d = {
1226 'discover' : help_discover,
1227 'tweets' : help_tweets,
1228 'messages' : help_messages,
1229 'friends_and_followers' : help_friends_and_followers,
1230 'list' : help_list,
1231 'stream' : help_stream,
1232 }
1233 if g['stuff']:
1234 d[g['stuff'].strip()]()
1235 else:
1236 printNicely(usage)
1237
1238
1239 def clear():
1240 """
1241 Clear screen
1242 """
1243 os.system('clear')
1244
1245
1246 def quit():
1247 """
1248 Exit all
1249 """
1250 save_history()
1251 os.system('rm -rf rainbow.db')
1252 os.kill(g['stream_pid'], signal.SIGKILL)
1253 sys.exit()
1254
1255
1256 def reset():
1257 """
1258 Reset prefix of line
1259 """
1260 if g['reset']:
1261 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
1262 g['reset'] = False
1263 try:
1264 printNicely(str(eval(g['cmd'])))
1265 except Exception:
1266 pass
1267
1268
1269 def process(cmd):
1270 """
1271 Process switch
1272 """
1273 return dict(zip(
1274 cmdset,
1275 [
1276 switch,
1277 trend,
1278 home,
1279 view,
1280 mentions,
1281 tweet,
1282 retweet,
1283 quote,
1284 allretweet,
1285 favorite,
1286 reply,
1287 delete,
1288 unfavorite,
1289 search,
1290 message,
1291 show,
1292 urlopen,
1293 ls,
1294 inbox,
1295 sent,
1296 trash,
1297 whois,
1298 follow,
1299 unfollow,
1300 mute,
1301 unmute,
1302 muting,
1303 block,
1304 unblock,
1305 report,
1306 list,
1307 cal,
1308 theme,
1309 help,
1310 clear,
1311 quit
1312 ]
1313 )).get(cmd, reset)
1314
1315
1316 def listen():
1317 """
1318 Listen to user's input
1319 """
1320 d = dict(zip(
1321 cmdset,
1322 [
1323 ['public', 'mine'], # switch
1324 [], # trend
1325 [], # home
1326 ['@'], # view
1327 [], # mentions
1328 [], # tweet
1329 [], # retweet
1330 [], # quote
1331 [], # allretweet
1332 [], # favorite
1333 [], # reply
1334 [], # delete
1335 [], # unfavorite
1336 ['#'], # search
1337 ['@'], # message
1338 ['image'], # show image
1339 [''], # open url
1340 ['fl', 'fr'], # list
1341 [], # inbox
1342 [], # sent
1343 [], # trash
1344 ['@'], # whois
1345 ['@'], # follow
1346 ['@'], # unfollow
1347 ['@'], # mute
1348 ['@'], # unmute
1349 ['@'], # muting
1350 ['@'], # block
1351 ['@'], # unblock
1352 ['@'], # report
1353 ['home','all_mem','all_sub','rm'], # list
1354 [], # cal
1355 g['themes'] + ['current_as_default'], # theme
1356 ['discover','tweets','messages','friends_and_followers','list','stream'], # help
1357 [], # clear
1358 [], # quit
1359 ]
1360 ))
1361 init_interactive_shell(d)
1362 read_history()
1363 reset()
1364 while True:
1365 if g['prefix']:
1366 line = raw_input(g['decorated_name'])
1367 else:
1368 line = raw_input()
1369 try:
1370 cmd = line.split()[0]
1371 except:
1372 cmd = ''
1373 g['cmd'] = cmd
1374 # Save cmd to global variable and call process
1375 try:
1376 g['stuff'] = ' '.join(line.split()[1:])
1377 process(cmd)()
1378 except Exception:
1379 printNicely(red('OMG something is wrong with Twitter right now.'))
1380 # Not redisplay prefix
1381 if cmd in ['switch', 't', 'rt', 'rep']:
1382 g['prefix'] = False
1383 else:
1384 g['prefix'] = True
1385
1386
1387 def stream(domain, args, name='Rainbow Stream'):
1388 """
1389 Track the stream
1390 """
1391
1392 # The Logo
1393 art_dict = {
1394 c['USER_DOMAIN']: name,
1395 c['PUBLIC_DOMAIN']: args.track_keywords,
1396 c['SITE_DOMAIN']: 'Site Stream',
1397 }
1398 if g['ascii_art']:
1399 ascii_art(art_dict[domain])
1400
1401 # These arguments are optional:
1402 stream_args = dict(
1403 timeout=args.timeout,
1404 block=not args.no_block,
1405 heartbeat_timeout=args.heartbeat_timeout)
1406
1407 # Track keyword
1408 query_args = dict()
1409 if args.track_keywords:
1410 query_args['track'] = args.track_keywords
1411
1412 # Get stream
1413 stream = TwitterStream(
1414 auth=authen(),
1415 domain=domain,
1416 **stream_args)
1417
1418 try:
1419 if domain == c['USER_DOMAIN']:
1420 tweet_iter = stream.user(**query_args)
1421 elif domain == c['SITE_DOMAIN']:
1422 tweet_iter = stream.site(**query_args)
1423 else:
1424 if args.track_keywords:
1425 tweet_iter = stream.statuses.filter(**query_args)
1426 else:
1427 tweet_iter = stream.statuses.sample()
1428
1429 # Iterate over the stream.
1430 for tweet in tweet_iter:
1431 if tweet is None:
1432 printNicely("-- None --")
1433 elif tweet is Timeout:
1434 printNicely("-- Timeout --")
1435 elif tweet is HeartbeatTimeout:
1436 printNicely("-- Heartbeat Timeout --")
1437 elif tweet is Hangup:
1438 printNicely("-- Hangup --")
1439 elif tweet.get('text'):
1440 draw(
1441 t=tweet,
1442 iot=args.image_on_term,
1443 keyword=args.track_keywords,
1444 fil=args.filter,
1445 ig=args.ignore,
1446 )
1447 except TwitterHTTPError:
1448 printNicely('')
1449 printNicely(
1450 magenta("We have maximum connection problem with twitter'stream API right now :("))
1451
1452
1453 def fly():
1454 """
1455 Main function
1456 """
1457 # Spawn stream process
1458 args = parse_arguments()
1459 try:
1460 get_decorated_name()
1461
1462 except TwitterHTTPError:
1463 printNicely('')
1464 printNicely(
1465 magenta("I'm afraid we have maximum connection problem with twitter right now :("))
1466 printNicely(magenta("Let's try again later."))
1467 save_history()
1468 os.system('rm -rf rainbow.db')
1469 sys.exit()
1470
1471 p = Process(
1472 target=stream,
1473 args=(
1474 c['USER_DOMAIN'],
1475 args,
1476 g['original_name']))
1477 p.start()
1478
1479 # Start listen process
1480 time.sleep(0.5)
1481 g['reset'] = True
1482 g['prefix'] = True
1483 g['stream_pid'] = p.pid
1484 g['iot'] = args.image_on_term
1485 listen()