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