cd8be952f8ce47d85f3fb093d06f8c00bfdd0c54
[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 'list',
65 'cal',
66 'theme',
67 'h',
68 'c',
69 'q'
70 ]
71
72
73 def parse_arguments():
74 """
75 Parse the arguments
76 """
77 parser = argparse.ArgumentParser(description=__doc__ or "")
78 parser.add_argument(
79 '-to',
80 '--timeout',
81 help='Timeout for the stream (seconds).')
82 parser.add_argument(
83 '-ht',
84 '--heartbeat-timeout',
85 help='Set heartbeat timeout.',
86 default=90)
87 parser.add_argument(
88 '-nb',
89 '--no-block',
90 action='store_true',
91 help='Set stream to non-blocking.')
92 parser.add_argument(
93 '-tt',
94 '--track-keywords',
95 help='Search the stream for specific text.')
96 parser.add_argument(
97 '-fil',
98 '--filter',
99 help='Filter specific screen_name.')
100 parser.add_argument(
101 '-ig',
102 '--ignore',
103 help='Ignore specific screen_name.')
104 parser.add_argument(
105 '-iot',
106 '--image-on-term',
107 action='store_true',
108 help='Display all image on terminal.')
109 return parser.parse_args()
110
111
112 def authen():
113 """
114 Authenticate with Twitter OAuth
115 """
116 # When using rainbow stream you must authorize.
117 twitter_credential = os.environ.get(
118 'HOME',
119 os.environ.get(
120 'USERPROFILE',
121 '')) + os.sep + '.rainbow_oauth'
122 if not os.path.exists(twitter_credential):
123 oauth_dance("Rainbow Stream",
124 CONSUMER_KEY,
125 CONSUMER_SECRET,
126 twitter_credential)
127 oauth_token, oauth_token_secret = read_token_file(twitter_credential)
128 return OAuth(
129 oauth_token,
130 oauth_token_secret,
131 CONSUMER_KEY,
132 CONSUMER_SECRET)
133
134
135 def get_decorated_name():
136 """
137 Beginning of every line
138 """
139 t = Twitter(auth=authen())
140 name = '@' + t.account.verify_credentials()['screen_name']
141 g['original_name'] = name[1:]
142 g['decorated_name'] = color_func(c['DECORATED_NAME'])('[' + name + ']: ')
143 g['ascii_art'] = True
144
145 files = os.listdir(os.path.dirname(__file__)+'/colorset')
146 themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
147 themes += ['custom']
148 g['themes'] = themes
149 db.theme_store(c['theme'])
150
151
152 def switch():
153 """
154 Switch stream
155 """
156 try:
157 target = g['stuff'].split()[0]
158
159 # Filter and ignore
160 args = parse_arguments()
161 try:
162 if g['stuff'].split()[-1] == '-f':
163 only = raw_input('Only nicks: ')
164 ignore = raw_input('Ignore nicks: ')
165 args.filter = filter(None, only.split(','))
166 args.ignore = filter(None, ignore.split(','))
167 elif g['stuff'].split()[-1] == '-d':
168 args.filter = c['ONLY_LIST']
169 args.ignore = c['IGNORE_LIST']
170 except:
171 printNicely(red('Sorry, wrong format.'))
172 return
173
174 # Public stream
175 if target == 'public':
176 keyword = g['stuff'].split()[1]
177 if keyword[0] == '#':
178 keyword = keyword[1:]
179 # Kill old process
180 os.kill(g['stream_pid'], signal.SIGKILL)
181 args.track_keywords = keyword
182 # Start new process
183 p = Process(
184 target=stream,
185 args=(
186 c['PUBLIC_DOMAIN'],
187 args))
188 p.start()
189 g['stream_pid'] = p.pid
190
191 # Personal stream
192 elif target == 'mine':
193 # Kill old process
194 os.kill(g['stream_pid'], signal.SIGKILL)
195 # Start new process
196 p = Process(
197 target=stream,
198 args=(
199 c['USER_DOMAIN'],
200 args,
201 g['original_name']))
202 p.start()
203 g['stream_pid'] = p.pid
204 printNicely('')
205 if args.filter:
206 printNicely(cyan('Only: ' + str(args.filter)))
207 if args.ignore:
208 printNicely(red('Ignore: ' + str(args.ignore)))
209 printNicely('')
210 g['ascii_art'] = True
211 except:
212 printNicely(red('Sorry I can\'t understand.'))
213
214
215 def trend():
216 """
217 Trend
218 """
219 t = Twitter(auth=authen())
220 # Get country and town
221 try:
222 country = g['stuff'].split()[0]
223 except:
224 country = ''
225 try:
226 town = g['stuff'].split()[1]
227 except:
228 town = ''
229
230 avail = t.trends.available()
231 # World wide
232 if not country:
233 trends = t.trends.place(_id=1)[0]['trends']
234 print_trends(trends)
235 else:
236 for location in avail:
237 # Search for country and Town
238 if town:
239 if location['countryCode'] == country \
240 and location['placeType']['name'] == 'Town' \
241 and location['name'] == town:
242 trends = t.trends.place(_id=location['woeid'])[0]['trends']
243 print_trends(trends)
244 # Search for country only
245 else:
246 if location['countryCode'] == country \
247 and location['placeType']['name'] == 'Country':
248 trends = t.trends.place(_id=location['woeid'])[0]['trends']
249 print_trends(trends)
250
251
252 def home():
253 """
254 Home
255 """
256 t = Twitter(auth=authen())
257 num = c['HOME_TWEET_NUM']
258 if g['stuff'].isdigit():
259 num = int(g['stuff'])
260 for tweet in reversed(t.statuses.home_timeline(count=num)):
261 draw(t=tweet, iot=g['iot'])
262 printNicely('')
263
264
265 def view():
266 """
267 Friend view
268 """
269 t = Twitter(auth=authen())
270 user = g['stuff'].split()[0]
271 if user[0] == '@':
272 try:
273 num = int(g['stuff'].split()[1])
274 except:
275 num = c['HOME_TWEET_NUM']
276 for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])):
277 draw(t=tweet, iot=g['iot'])
278 printNicely('')
279 else:
280 printNicely(red('A name should begin with a \'@\''))
281
282
283 def mentions():
284 """
285 Mentions timeline
286 """
287 t = Twitter(auth=authen())
288 num = c['HOME_TWEET_NUM']
289 if g['stuff'].isdigit():
290 num = int(g['stuff'])
291 for tweet in reversed(t.statuses.mentions_timeline(count=num)):
292 draw(t=tweet, iot=g['iot'])
293 printNicely('')
294
295
296 def tweet():
297 """
298 Tweet
299 """
300 t = Twitter(auth=authen())
301 t.statuses.update(status=g['stuff'])
302
303
304 def retweet():
305 """
306 ReTweet
307 """
308 t = Twitter(auth=authen())
309 try:
310 id = int(g['stuff'].split()[0])
311 except:
312 printNicely(red('Sorry I can\'t understand.'))
313 return
314 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
315 t.statuses.retweet(id=tid, include_entities=False, trim_user=True)
316
317
318 def quote():
319 """
320 Quote a tweet
321 """
322 t = Twitter(auth=authen())
323 try:
324 id = int(g['stuff'].split()[0])
325 except:
326 printNicely(red('Sorry I can\'t understand.'))
327 return
328 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
329 tweet = t.statuses.show(id=tid)
330 screen_name = tweet['user']['screen_name']
331 text = tweet['text']
332 quote = '\"@' + screen_name + ': ' + text + '\"'
333 quote = quote.encode('utf8')
334 notice = light_magenta('Compose mode ')
335 notice += light_yellow('(Enter nothing will cancel the quote)')
336 notice += light_magenta(':')
337 printNicely(notice)
338 extra = raw_input(quote)
339 if extra:
340 t.statuses.update(status=quote+extra)
341 else:
342 printNicely(light_magenta('No text added.'))
343
344
345 def allretweet():
346 """
347 List all retweet
348 """
349 t = Twitter(auth=authen())
350 # Get rainbow id
351 try:
352 id = int(g['stuff'].split()[0])
353 except:
354 printNicely(red('Sorry I can\'t understand.'))
355 return
356 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
357 # Get display num if exist
358 try:
359 num = int(g['stuff'].split()[1])
360 except:
361 num = c['RETWEETS_SHOW_NUM']
362 # Get result and display
363 rt_ary = t.statuses.retweets(id=tid, count=num)
364 if not rt_ary:
365 printNicely(magenta('This tweet has no retweet.'))
366 return
367 for tweet in reversed(rt_ary):
368 draw(t=tweet, iot=g['iot'])
369 printNicely('')
370
371
372 def favorite():
373 """
374 Favorite
375 """
376 t = Twitter(auth=authen())
377 try:
378 id = int(g['stuff'].split()[0])
379 except:
380 printNicely(red('Sorry I can\'t understand.'))
381 return
382 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
383 t.favorites.create(_id=tid, include_entities=False)
384 printNicely(green('Favorited.'))
385 draw(t.statuses.show(id=tid), iot=g['iot'])
386 printNicely('')
387
388
389 def reply():
390 """
391 Reply
392 """
393 t = Twitter(auth=authen())
394 try:
395 id = int(g['stuff'].split()[0])
396 except:
397 printNicely(red('Sorry I can\'t understand.'))
398 return
399 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
400 user = t.statuses.show(id=tid)['user']['screen_name']
401 status = ' '.join(g['stuff'].split()[1:])
402 status = '@' + user + ' ' + status.decode('utf-8')
403 t.statuses.update(status=status, in_reply_to_status_id=tid)
404
405
406 def delete():
407 """
408 Delete
409 """
410 t = Twitter(auth=authen())
411 try:
412 rid = int(g['stuff'].split()[0])
413 except:
414 printNicely(red('Sorry I can\'t understand.'))
415 return
416 tid = db.rainbow_to_tweet_query(rid)[0].tweet_id
417 t.statuses.destroy(id=tid)
418 printNicely(green('Okay it\'s gone.'))
419
420
421 def unfavorite():
422 """
423 Unfavorite
424 """
425 t = Twitter(auth=authen())
426 try:
427 id = int(g['stuff'].split()[0])
428 except:
429 printNicely(red('Sorry I can\'t understand.'))
430 return
431 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
432 t.favorites.destroy(_id=tid)
433 printNicely(green('Okay it\'s unfavorited.'))
434 draw(t.statuses.show(id=tid), iot=g['iot'])
435 printNicely('')
436
437
438 def search():
439 """
440 Search
441 """
442 t = Twitter(auth=authen())
443 if g['stuff'].startswith('#'):
444 rel = t.search.tweets(q=g['stuff'])['statuses']
445 if rel:
446 printNicely('Newest tweets:')
447 for i in reversed(xrange(c['SEARCH_MAX_RECORD'])):
448 draw(t=rel[i],
449 iot=g['iot'],
450 keyword=g['stuff'].strip()[1:])
451 printNicely('')
452 else:
453 printNicely(magenta('I\'m afraid there is no result'))
454 else:
455 printNicely(red('A keyword should be a hashtag (like \'#AKB48\')'))
456
457
458 def message():
459 """
460 Send a direct message
461 """
462 t = Twitter(auth=authen())
463 user = g['stuff'].split()[0]
464 if user[0].startswith('@'):
465 try:
466 content = g['stuff'].split()[1]
467 except:
468 printNicely(red('Sorry I can\'t understand.'))
469 t.direct_messages.new(
470 screen_name=user[1:],
471 text=content
472 )
473 printNicely(green('Message sent.'))
474 else:
475 printNicely(red('A name should begin with a \'@\''))
476
477
478 def show():
479 """
480 Show image
481 """
482 t = Twitter(auth=authen())
483 try:
484 target = g['stuff'].split()[0]
485 if target != 'image':
486 return
487 id = int(g['stuff'].split()[1])
488 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
489 tweet = t.statuses.show(id=tid)
490 media = tweet['entities']['media']
491 for m in media:
492 res = requests.get(m['media_url'])
493 img = Image.open(StringIO(res.content))
494 img.show()
495 except:
496 printNicely(red('Sorry I can\'t show this image.'))
497
498
499 def urlopen():
500 """
501 Open url
502 """
503 t = Twitter(auth=authen())
504 try:
505 if not g['stuff'].isdigit():
506 return
507 tid = db.rainbow_to_tweet_query(g['stuff'])[0].tweet_id
508 tweet = t.statuses.show(id=tid)
509 link_ary = [u for u in tweet['text'].split() if u.startswith('http://')]
510 if not link_ary:
511 printNicely(light_magenta('No url here @.@!'))
512 return
513 for link in link_ary:
514 webbrowser.open(link)
515 except:
516 printNicely(red('Sorry I can\'t open url in this tweet.'))
517
518
519 def ls():
520 """
521 List friends for followers
522 """
523 t = Twitter(auth=authen())
524 # Get name
525 try:
526 name = g['stuff'].split()[1]
527 if name.startswith('@'):
528 name = name[1:]
529 else:
530 printNicely(red('A name should begin with a \'@\''))
531 raise Exception('Invalid name')
532 except:
533 name = g['original_name']
534 # Get list followers or friends
535 try:
536 target = g['stuff'].split()[0]
537 except:
538 printNicely(red('Omg some syntax is wrong.'))
539 # Init cursor
540 d = {'fl': 'followers', 'fr': 'friends'}
541 next_cursor = -1
542 rel = {}
543 # Cursor loop
544 while next_cursor != 0:
545 list = getattr(t, d[target]).list(
546 screen_name=name,
547 cursor=next_cursor,
548 skip_status=True,
549 include_entities=False,
550 )
551 for u in list['users']:
552 rel[u['name']] = '@' + u['screen_name']
553 next_cursor = list['next_cursor']
554 # Print out result
555 printNicely('All: ' + str(len(rel)) + ' ' + d[target] + '.')
556 for name in rel:
557 user = ' ' + cycle_color(name)
558 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
559 printNicely(user)
560
561
562 def inbox():
563 """
564 Inbox direct messages
565 """
566 t = Twitter(auth=authen())
567 num = c['MESSAGES_DISPLAY']
568 rel = []
569 if g['stuff'].isdigit():
570 num = g['stuff']
571 cur_page = 1
572 # Max message per page is 20 so we have to loop
573 while num > 20:
574 rel = rel + t.direct_messages(
575 count=20,
576 page=cur_page,
577 include_entities=False,
578 skip_status=False
579 )
580 num -= 20
581 cur_page += 1
582 rel = rel + t.direct_messages(
583 count=num,
584 page=cur_page,
585 include_entities=False,
586 skip_status=False
587 )
588 # Display
589 printNicely('Inbox: newest ' + str(len(rel)) + ' messages.')
590 for m in reversed(rel):
591 print_message(m)
592 printNicely('')
593
594
595 def sent():
596 """
597 Sent direct messages
598 """
599 t = Twitter(auth=authen())
600 num = c['MESSAGES_DISPLAY']
601 rel = []
602 if g['stuff'].isdigit():
603 num = int(g['stuff'])
604 cur_page = 1
605 # Max message per page is 20 so we have to loop
606 while num > 20:
607 rel = rel + t.direct_messages.sent(
608 count=20,
609 page=cur_page,
610 include_entities=False,
611 skip_status=False
612 )
613 num -= 20
614 cur_page += 1
615 rel = rel + t.direct_messages.sent(
616 count=num,
617 page=cur_page,
618 include_entities=False,
619 skip_status=False
620 )
621 # Display
622 printNicely('Sent: newest ' + str(len(rel)) + ' messages.')
623 for m in reversed(rel):
624 print_message(m)
625 printNicely('')
626
627
628 def trash():
629 """
630 Remove message
631 """
632 t = Twitter(auth=authen())
633 try:
634 rid = int(g['stuff'].split()[0])
635 except:
636 printNicely(red('Sorry I can\'t understand.'))
637 mid = db.rainbow_to_message_query(rid)[0].message_id
638 t.direct_messages.destroy(id=mid)
639 printNicely(green('Message deleted.'))
640
641
642 def whois():
643 """
644 Show profile of a specific user
645 """
646 t = Twitter(auth=authen())
647 screen_name = g['stuff'].split()[0]
648 if screen_name.startswith('@'):
649 try:
650 user = t.users.show(
651 screen_name=screen_name[1:],
652 include_entities=False)
653 show_profile(user, g['iot'])
654 except:
655 printNicely(red('Omg no user.'))
656 else:
657 printNicely(red('A name should begin with a \'@\''))
658
659
660 def follow():
661 """
662 Follow a user
663 """
664 t = Twitter(auth=authen())
665 screen_name = g['stuff'].split()[0]
666 if screen_name.startswith('@'):
667 t.friendships.create(screen_name=screen_name[1:], follow=True)
668 printNicely(green('You are following ' + screen_name + ' now!'))
669 else:
670 printNicely(red('A name should begin with a \'@\''))
671
672
673 def unfollow():
674 """
675 Unfollow a user
676 """
677 t = Twitter(auth=authen())
678 screen_name = g['stuff'].split()[0]
679 if screen_name.startswith('@'):
680 t.friendships.destroy(
681 screen_name=screen_name[1:],
682 include_entities=False)
683 printNicely(green('Unfollow ' + screen_name + ' success!'))
684 else:
685 printNicely(red('A name should begin with a \'@\''))
686
687
688 def mute():
689 """
690 Mute a user
691 """
692 t = Twitter(auth=authen())
693 try:
694 screen_name = g['stuff'].split()[0]
695 except:
696 printNicely(red('A name should be specified. '))
697 return
698 if screen_name.startswith('@'):
699 rel = t.mutes.users.create(screen_name=screen_name[1:])
700 if isinstance(rel, dict):
701 printNicely(green(screen_name + ' is muted.'))
702 else:
703 printNicely(red(rel))
704 else:
705 printNicely(red('A name should begin with a \'@\''))
706
707
708 def unmute():
709 """
710 Unmute a user
711 """
712 t = Twitter(auth=authen())
713 try:
714 screen_name = g['stuff'].split()[0]
715 except:
716 printNicely(red('A name should be specified. '))
717 return
718 if screen_name.startswith('@'):
719 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
720 if isinstance(rel, dict):
721 printNicely(green(screen_name + ' is unmuted.'))
722 else:
723 printNicely(red(rel))
724 else:
725 printNicely(red('A name should begin with a \'@\''))
726
727
728 def muting():
729 """
730 List muting user
731 """
732 t = Twitter(auth=authen())
733 # Init cursor
734 next_cursor = -1
735 rel = {}
736 # Cursor loop
737 while next_cursor != 0:
738 list = t.mutes.users.list(
739 screen_name=g['original_name'],
740 cursor=next_cursor,
741 skip_status=True,
742 include_entities=False,
743 )
744 for u in list['users']:
745 rel[u['name']] = '@' + u['screen_name']
746 next_cursor = list['next_cursor']
747 # Print out result
748 printNicely('All: ' + str(len(rel)) + ' people.')
749 for name in rel:
750 user = ' ' + cycle_color(name)
751 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
752 printNicely(user)
753
754
755 def block():
756 """
757 Block a user
758 """
759 t = Twitter(auth=authen())
760 screen_name = g['stuff'].split()[0]
761 if screen_name.startswith('@'):
762 t.blocks.create(
763 screen_name=screen_name[1:],
764 include_entities=False,
765 skip_status=True)
766 printNicely(green('You blocked ' + screen_name + '.'))
767 else:
768 printNicely(red('A name should begin with a \'@\''))
769
770
771 def unblock():
772 """
773 Unblock a user
774 """
775 t = Twitter(auth=authen())
776 screen_name = g['stuff'].split()[0]
777 if screen_name.startswith('@'):
778 t.blocks.destroy(
779 screen_name=screen_name[1:],
780 include_entities=False,
781 skip_status=True)
782 printNicely(green('Unblock ' + screen_name + ' success!'))
783 else:
784 printNicely(red('A name should begin with a \'@\''))
785
786
787 def report():
788 """
789 Report a user as a spam account
790 """
791 t = Twitter(auth=authen())
792 screen_name = g['stuff'].split()[0]
793 if screen_name.startswith('@'):
794 t.users.report_spam(
795 screen_name=screen_name[1:])
796 printNicely(green('You reported ' + screen_name + '.'))
797 else:
798 printNicely(red('Sorry I can\'t understand.'))
799
800
801 def show_lists(t):
802 """
803 list list
804 """
805 rel = t.lists.list(screen_name=g['original_name'])
806 if rel:
807 print_list(rel)
808 else:
809 printNicely(light_magenta('You belong to no lists :)'))
810
811
812 def list_home(t):
813 """
814 List home
815 """
816 # Get list name
817 list_name = raw_input(light_magenta('Give me the list\'s name: '))
818 if not list_name:
819 printNicely(light_magenta('No list specified.'))
820 return
821 # Print timeline
822 l = db.list_name_to_id_query(list_name)[0]
823 res = t.lists.statuses(
824 list_id = l.list_id,
825 count = c['LIST_MAX'],
826 include_entities=False)
827 for tweet in res:
828 draw(t=tweet)
829 printNicely('')
830
831
832 def list_members(t):
833 """
834 List members
835 """
836 # Get list name
837 list_name = raw_input(light_magenta('Give me the list\'s name: '))
838 if not list_name:
839 printNicely(light_magenta('No list specified.'))
840 return
841 # Members
842 l = db.list_name_to_id_query(list_name)[0]
843 rel = {}
844 next_cursor = -1
845 while next_cursor != 0 :
846 m = t.lists.members(
847 list_id = l.list_id,
848 cursor = next_cursor,
849 include_entities=False)
850 for u in m['users']:
851 rel[u['name']] = '@' + u['screen_name']
852 next_cursor = m['next_cursor']
853 printNicely('All: ' + str(len(rel)) + ' members.')
854 for name in rel:
855 user = ' ' + cycle_color(name)
856 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
857 printNicely(user)
858
859
860 def list_subscribers(t):
861 """
862 List subscribers
863 """
864 # Get list name
865 list_name = raw_input(light_magenta('Give me the list\'s name: '))
866 if not list_name:
867 printNicely(light_magenta('No list specified.'))
868 return
869 # Subscribers
870 l = db.list_name_to_id_query(list_name)[0]
871 rel = {}
872 next_cursor = -1
873 while next_cursor != 0 :
874 m = t.lists.subscribers(
875 list_id = l.list_id,
876 cursor = next_cursor,
877 include_entities=False)
878 for u in m['users']:
879 rel[u['name']] = '@' + u['screen_name']
880 next_cursor = m['next_cursor']
881 printNicely('All: ' + str(len(rel)) + ' subscribers.')
882 for name in rel:
883 user = ' ' + cycle_color(name)
884 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
885 printNicely(user)
886
887
888 def list_remove(t):
889 """
890 Remove specific user from a list
891 """
892 # Get list name
893 list_name = raw_input(light_magenta('Give me the list\'s name: '))
894 if not list_name:
895 printNicely(light_magenta('No list specified.'))
896 return
897 # Remove
898 l = db.list_name_to_id_query(list_name)[0]
899 user_name = raw_input(light_magenta('Give me the name of unlucky man: '))
900 try:
901 t.lists.members.destroy(
902 list_id = l.list_id,
903 screen_name = user_name)
904 printNicely(light_green('Okay he\'s gone :)'))
905 except:
906 printNicely(light_magenta('I\'m sorry we can not remove him.'))
907
908
909 def list():
910 """
911 Twitter's list
912 """
913 t = Twitter(auth=authen())
914 # List all lists or base on action
915 try:
916 g['list_action'] = g['stuff'].split()[0]
917 except:
918 show_lists(t)
919 return
920
921 # Sub function
922 action_ary = {
923 'home': list_home,
924 'all_mem': list_members,
925 'all_sub': list_subscribers,
926 'rm': list_remove,
927 }
928 try:
929 return action_ary[g['list_action']](t)
930 except:
931 printNicely(red('Sorry I can\'t understand.'))
932
933
934 def cal():
935 """
936 Unix's command `cal`
937 """
938 # Format
939 rel = os.popen('cal').read().split('\n')
940 month = rel.pop(0)
941 date = rel.pop(0)
942 show_calendar(month, date, rel)
943
944
945 def theme():
946 """
947 List and change theme
948 """
949 if not g['stuff']:
950 # List themes
951 for theme in g['themes']:
952 line = ''
953 # Detect custom config
954 if theme == 'custom':
955 line += light_magenta('custom')
956 custom_path = os.environ.get(
957 'HOME',
958 os.environ.get('USERPROFILE',
959 '')) + os.sep + '.rainbow_config.json'
960 if not os.path.exists(custom_path):
961 line += light_magenta(' (create your own config file at ~/.rainbow_config.json)')
962 else:
963 line += light_magenta(' (loaded)')
964 else:
965 line += light_magenta(theme)
966 if c['theme'] == theme :
967 line = ' '*2 + light_yellow('* ') + line
968 else:
969 line = ' '*4 + line
970 printNicely(line)
971 elif g['stuff'] == 'current_as_default':
972 # Set default
973 path = os.path.dirname(__file__) + '/colorset/init'
974 f = open(path,'w')
975 f.write(c['theme'])
976 f.close()
977 os.system('chmod 777 ' + path)
978 printNicely(light_green('Okay it will be applied from next time :)'))
979 else:
980 # Change theme
981 try:
982 # Load new config
983 if g['stuff'] != 'custom':
984 new_config = os.path.dirname(__file__) + '/colorset/' + g['stuff'] + '.json'
985 else:
986 new_config = os.environ.get(
987 'HOME',os.environ.get(
988 'USERPROFILE',
989 '')) + os.sep + '.rainbow_config.json'
990 new_config = load_config(new_config)
991 if new_config:
992 for nc in new_config:
993 c[nc] = new_config[nc]
994 # Update db and reset colors
995 db.theme_update(g['stuff'])
996 c['theme'] = g['stuff']
997 reset_cycle()
998 g['decorated_name'] = color_func(
999 c['DECORATED_NAME'])(
1000 '[@' + g['original_name'] + ']: ')
1001 printNicely(green('Theme changed.'))
1002 except:
1003 if g['stuff'] == 'custom':
1004 printNicely(red('~/.rainbow_config.json is not exists!'))
1005 else:
1006 printNicely(red('No such theme exists.'))
1007
1008
1009 def help_discover():
1010 """
1011 Discover the world
1012 """
1013 s = ' ' * 2
1014 # Discover the world
1015 usage = '\n'
1016 usage += s + grey(u'\u266A' + ' Discover the world \n')
1017 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
1018 'You can try ' + light_green('trend US') + ' or ' + \
1019 light_green('trend JP Tokyo') + '.\n'
1020 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
1021 light_green('home 7') + ' will show 7 tweets.\n'
1022 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
1023 light_green('mentions 7') + ' will show 7 mention tweets.\n'
1024 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
1025 magenta('@mdo') + '.\n'
1026 usage += s * 2 + light_green('view @mdo') + \
1027 ' will show ' + magenta('@mdo') + '\'s home.\n'
1028 usage += s * 2 + light_green('s #AKB48') + ' will search for "' + \
1029 light_yellow('AKB48') + '" and return 5 newest tweet.\n'
1030 printNicely(usage)
1031
1032
1033 def help_tweets():
1034 """
1035 Tweets
1036 """
1037 s = ' ' * 2
1038 # Tweet
1039 usage = '\n'
1040 usage += s + grey(u'\u266A' + ' Tweets \n')
1041 usage += s * 2 + light_green('t oops ') + \
1042 'will tweet "' + light_yellow('oops') + '" immediately.\n'
1043 usage += s * 2 + \
1044 light_green('rt 12 ') + ' will retweet to tweet with ' + \
1045 light_yellow('[id=12]') + '.\n'
1046 usage += s * 2 + \
1047 light_green('quote 12 ') + ' will quote the tweet with ' + \
1048 light_yellow('[id=12]') + '. If no extra text is added, ' + \
1049 'the quote will be canceled.\n'
1050 usage += s * 2 + \
1051 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
1052 light_yellow('[id=12]') + '.\n'
1053 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
1054 light_yellow('oops') + '" to tweet with ' + \
1055 light_yellow('[id=12]') + '.\n'
1056 usage += s * 2 + \
1057 light_green('fav 12 ') + ' will favorite the tweet with ' + \
1058 light_yellow('[id=12]') + '.\n'
1059 usage += s * 2 + \
1060 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
1061 light_yellow('[id=12]') + '.\n'
1062 usage += s * 2 + \
1063 light_green('del 12 ') + ' will delete tweet with ' + \
1064 light_yellow('[id=12]') + '.\n'
1065 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
1066 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
1067 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
1068 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
1069 printNicely(usage)
1070
1071
1072 def help_messages():
1073 """
1074 Messages
1075 """
1076 s = ' ' * 2
1077 # Direct message
1078 usage = '\n'
1079 usage += s + grey(u'\u266A' + ' Direct messages \n')
1080 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
1081 light_green('inbox 7') + ' will show newest 7 messages.\n'
1082 usage += s * 2 + light_green('sent') + ' will show sent messages. ' + \
1083 light_green('sent 7') + ' will show newest 7 messages.\n'
1084 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
1085 magenta('@dtvd88') + '.\n'
1086 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
1087 light_yellow('[message_id=5]') + '.\n'
1088 printNicely(usage)
1089
1090
1091 def help_friends_and_followers():
1092 """
1093 Friends and Followers
1094 """
1095 s = ' ' * 2
1096 # Follower and following
1097 usage = '\n'
1098 usage += s + grey(u'\u266A' + ' Friends and followers \n')
1099 usage += s * 2 + \
1100 light_green('ls fl') + \
1101 ' will list all followers (people who are following you).\n'
1102 usage += s * 2 + \
1103 light_green('ls fr') + \
1104 ' will list all friends (people who you are following).\n'
1105 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
1106 magenta('@dtvd88') + '.\n'
1107 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
1108 magenta('@dtvd88') + '.\n'
1109 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
1110 magenta('@dtvd88') + '.\n'
1111 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
1112 magenta('@dtvd88') + '.\n'
1113 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
1114 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
1115 magenta('@dtvd88') + '.\n'
1116 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
1117 magenta('@dtvd88') + '.\n'
1118 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
1119 magenta('@dtvd88') + ' as a spam account.\n'
1120 printNicely(usage)
1121
1122
1123 def help_list():
1124 """
1125 Lists
1126 """
1127 s = ' ' * 2
1128 # Twitter list
1129 usage = '\n'
1130 usage += s + grey(u'\u266A' + ' Twitter list\n')
1131 usage += s * 2 + light_green('list') + \
1132 ' will show all lists you are belong to.\n'
1133 usage += s * 2 + light_green('list home') + \
1134 ' will show timeline of list. You will be asked for list\'s name\n'
1135 usage += s * 2 + light_green('list all_mem') + \
1136 ' will show list\'s all members.\n'
1137 usage += s * 2 + light_green('list all_sub') + \
1138 ' will show list\'s all subscribers.\n'
1139 usage += s * 2 + light_green('list rm') + \
1140 ' will remove specific person from a list owned by you.' + \
1141 ' You will be asked for list\'s name and person\'s name.\n'
1142 printNicely(usage)
1143
1144
1145 def help_stream():
1146 """
1147 Stream switch
1148 """
1149 s = ' ' * 2
1150 # Switch
1151 usage = '\n'
1152 usage += s + grey(u'\u266A' + ' Switching streams \n')
1153 usage += s * 2 + light_green('switch public #AKB') + \
1154 ' will switch to public stream and follow "' + \
1155 light_yellow('AKB') + '" keyword.\n'
1156 usage += s * 2 + light_green('switch mine') + \
1157 ' will switch to your personal stream.\n'
1158 usage += s * 2 + light_green('switch mine -f ') + \
1159 ' will prompt to enter the filter.\n'
1160 usage += s * 3 + light_yellow('Only nicks') + \
1161 ' filter will decide nicks will be INCLUDE ONLY.\n'
1162 usage += s * 3 + light_yellow('Ignore nicks') + \
1163 ' filter will decide nicks will be EXCLUDE.\n'
1164 usage += s * 2 + light_green('switch mine -d') + \
1165 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
1166 printNicely(usage)
1167
1168
1169 def help():
1170 """
1171 Help
1172 """
1173 s = ' ' * 2
1174 h, w = os.popen('stty size', 'r').read().split()
1175
1176 # Start
1177 usage = '\n'
1178 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
1179 usage += s + '-' * (int(w) - 4) + '\n'
1180 usage += s + 'You are ' + \
1181 light_yellow('already') + ' on your personal stream.\n'
1182 usage += s + 'Any update from Twitter will show up ' + \
1183 light_yellow('immediately') + '.\n'
1184 usage += s + 'In addtion, following commands are available right now:\n'
1185
1186 # Twitter help section
1187 usage += '\n'
1188 usage += s + grey(u'\u266A' + ' Twitter help\n')
1189 usage += s * 2 + light_green('h discover') + \
1190 ' will show help for discover commands.\n'
1191 usage += s * 2 + light_green('h tweets') + \
1192 ' will show help for tweets commands.\n'
1193 usage += s * 2 + light_green('h messages') + \
1194 ' will show help for messages commands.\n'
1195 usage += s * 2 + light_green('h friends_and_followers') + \
1196 ' will show help for friends and followers commands.\n'
1197 usage += s * 2 + light_green('h list') + \
1198 ' will show help for list commands.\n'
1199 usage += s * 2 + light_green('h stream') + \
1200 ' will show help for stream commands.\n'
1201
1202 # Smart shell
1203 usage += '\n'
1204 usage += s + grey(u'\u266A' + ' Smart shell\n')
1205 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1206 'will be evaluate by Python interpreter.\n'
1207 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1208 ' for current month.\n'
1209
1210 # Screening
1211 usage += '\n'
1212 usage += s + grey(u'\u266A' + ' Screening \n')
1213 usage += s * 2 + light_green('theme') + ' will list available theme.' + \
1214 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
1215 ' theme immediately.\n'
1216 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1217 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1218 usage += s * 2 + light_green('q') + ' will quit.\n'
1219
1220 # End
1221 usage += '\n'
1222 usage += s + '-' * (int(w) - 4) + '\n'
1223 usage += s + 'Have fun and hang tight! \n'
1224
1225 # Show help
1226 d = {
1227 'discover' : help_discover,
1228 'tweets' : help_tweets,
1229 'messages' : help_messages,
1230 'friends_and_followers' : help_friends_and_followers,
1231 'list' : help_list,
1232 'stream' : help_stream,
1233 }
1234 if g['stuff']:
1235 d[g['stuff'].strip()]()
1236 else:
1237 printNicely(usage)
1238
1239
1240 def clear():
1241 """
1242 Clear screen
1243 """
1244 os.system('clear')
1245
1246
1247 def quit():
1248 """
1249 Exit all
1250 """
1251 save_history()
1252 os.system('rm -rf rainbow.db')
1253 os.kill(g['stream_pid'], signal.SIGKILL)
1254 sys.exit()
1255
1256
1257 def reset():
1258 """
1259 Reset prefix of line
1260 """
1261 if g['reset']:
1262 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
1263 g['reset'] = False
1264 try:
1265 printNicely(str(eval(g['cmd'])))
1266 except Exception:
1267 pass
1268
1269
1270 def process(cmd):
1271 """
1272 Process switch
1273 """
1274 return dict(zip(
1275 cmdset,
1276 [
1277 switch,
1278 trend,
1279 home,
1280 view,
1281 mentions,
1282 tweet,
1283 retweet,
1284 quote,
1285 allretweet,
1286 favorite,
1287 reply,
1288 delete,
1289 unfavorite,
1290 search,
1291 message,
1292 show,
1293 urlopen,
1294 ls,
1295 inbox,
1296 sent,
1297 trash,
1298 whois,
1299 follow,
1300 unfollow,
1301 mute,
1302 unmute,
1303 muting,
1304 block,
1305 unblock,
1306 report,
1307 list,
1308 cal,
1309 theme,
1310 help,
1311 clear,
1312 quit
1313 ]
1314 )).get(cmd, reset)
1315
1316
1317 def listen():
1318 """
1319 Listen to user's input
1320 """
1321 d = dict(zip(
1322 cmdset,
1323 [
1324 ['public', 'mine'], # switch
1325 [], # trend
1326 [], # home
1327 ['@'], # view
1328 [], # mentions
1329 [], # tweet
1330 [], # retweet
1331 [], # quote
1332 [], # allretweet
1333 [], # favorite
1334 [], # reply
1335 [], # delete
1336 [], # unfavorite
1337 ['#'], # search
1338 ['@'], # message
1339 ['image'], # show image
1340 [''], # open url
1341 ['fl', 'fr'], # list
1342 [], # inbox
1343 [], # sent
1344 [], # trash
1345 ['@'], # whois
1346 ['@'], # follow
1347 ['@'], # unfollow
1348 ['@'], # mute
1349 ['@'], # unmute
1350 ['@'], # muting
1351 ['@'], # block
1352 ['@'], # unblock
1353 ['@'], # report
1354 ['home','all_mem','all_sub','rm'], # list
1355 [], # cal
1356 g['themes'] + ['current_as_default'], # theme
1357 ['discover','tweets','messages','friends_and_followers','list','stream'], # help
1358 [], # clear
1359 [], # quit
1360 ]
1361 ))
1362 init_interactive_shell(d)
1363 read_history()
1364 reset()
1365 while True:
1366 if g['prefix']:
1367 line = raw_input(g['decorated_name'])
1368 else:
1369 line = raw_input()
1370 try:
1371 cmd = line.split()[0]
1372 except:
1373 cmd = ''
1374 g['cmd'] = cmd
1375 # Save cmd to global variable and call process
1376 try:
1377 g['stuff'] = ' '.join(line.split()[1:])
1378 process(cmd)()
1379 except Exception:
1380 printNicely(red('OMG something is wrong with Twitter right now.'))
1381 # Not redisplay prefix
1382 if cmd in ['switch', 't', 'rt', 'rep']:
1383 g['prefix'] = False
1384 else:
1385 g['prefix'] = True
1386
1387
1388 def stream(domain, args, name='Rainbow Stream'):
1389 """
1390 Track the stream
1391 """
1392
1393 # The Logo
1394 art_dict = {
1395 c['USER_DOMAIN']: name,
1396 c['PUBLIC_DOMAIN']: args.track_keywords,
1397 c['SITE_DOMAIN']: 'Site Stream',
1398 }
1399 if g['ascii_art']:
1400 ascii_art(art_dict[domain])
1401
1402 # These arguments are optional:
1403 stream_args = dict(
1404 timeout=args.timeout,
1405 block=not args.no_block,
1406 heartbeat_timeout=args.heartbeat_timeout)
1407
1408 # Track keyword
1409 query_args = dict()
1410 if args.track_keywords:
1411 query_args['track'] = args.track_keywords
1412
1413 # Get stream
1414 stream = TwitterStream(
1415 auth=authen(),
1416 domain=domain,
1417 **stream_args)
1418
1419 try:
1420 if domain == c['USER_DOMAIN']:
1421 tweet_iter = stream.user(**query_args)
1422 elif domain == c['SITE_DOMAIN']:
1423 tweet_iter = stream.site(**query_args)
1424 else:
1425 if args.track_keywords:
1426 tweet_iter = stream.statuses.filter(**query_args)
1427 else:
1428 tweet_iter = stream.statuses.sample()
1429
1430 # Iterate over the stream.
1431 for tweet in tweet_iter:
1432 if tweet is None:
1433 printNicely("-- None --")
1434 elif tweet is Timeout:
1435 printNicely("-- Timeout --")
1436 elif tweet is HeartbeatTimeout:
1437 printNicely("-- Heartbeat Timeout --")
1438 elif tweet is Hangup:
1439 printNicely("-- Hangup --")
1440 elif tweet.get('text'):
1441 draw(
1442 t=tweet,
1443 iot=args.image_on_term,
1444 keyword=args.track_keywords,
1445 fil=args.filter,
1446 ig=args.ignore,
1447 )
1448 except TwitterHTTPError:
1449 printNicely('')
1450 printNicely(
1451 magenta("We have maximum connection problem with twitter'stream API right now :("))
1452
1453
1454 def fly():
1455 """
1456 Main function
1457 """
1458 # Spawn stream process
1459 args = parse_arguments()
1460 try:
1461 get_decorated_name()
1462
1463 except TwitterHTTPError:
1464 printNicely('')
1465 printNicely(
1466 magenta("I'm afraid we have maximum connection problem with twitter right now :("))
1467 printNicely(magenta("Let's try again later."))
1468 save_history()
1469 os.system('rm -rf rainbow.db')
1470 sys.exit()
1471
1472 p = Process(
1473 target=stream,
1474 args=(
1475 c['USER_DOMAIN'],
1476 args,
1477 g['original_name']))
1478 p.start()
1479
1480 # Start listen process
1481 time.sleep(0.5)
1482 g['reset'] = True
1483 g['prefix'] = True
1484 g['stream_pid'] = p.pid
1485 g['iot'] = args.image_on_term
1486 listen()