12254e882ef0bed6e00a7d1de331931d55a25d6a
2 Colorful user's timeline stream
4 from multiprocessing
import Process
5 from dateutil
import parser
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
25 from .consumer
import *
26 from .interactive
import *
28 from .c_image
import *
62 def draw(t
, iot
=False, keyword
=None, fil
=[], ig
=[]):
70 screen_name
= t
['user']['screen_name']
71 name
= t
['user']['name']
72 created_at
= t
['created_at']
73 favorited
= t
['favorited']
74 date
= parser
.parse(created_at
)
75 date
= date
- datetime
.timedelta(seconds
=time
.timezone
)
76 clock
= date
.strftime('%Y/%m/%d %H:%M:%S')
82 urls
= t
['entities']['urls']
84 expanded_url
.append(u
['expanded_url'])
93 media
= t
['entities']['media']
95 media_url
.append(m
['media_url'])
100 screen_name
= '@' + screen_name
101 if fil
and screen_name
not in fil
:
103 if ig
and screen_name
in ig
:
107 res
= db
.tweet_to_rainbow_query(tid
)
110 res
= db
.tweet_to_rainbow_query(tid
)
111 rid
= res
[0].rainbow_id
114 user
= cycle_color(name
) + grey(' ' + screen_name
+ ' ')
115 meta
= grey('[' + clock
+ '] [id=' + str(rid
) + '] ')
117 meta
= meta
+ green(u
'\u2605')
121 for index
in range(len(expanded_url
)):
123 lambda x
: expanded_url
[index
] if x
== url
[index
] else x
,
126 tweet
= map(lambda x
: grey(x
) if x
== 'RT' else x
, tweet
)
127 # Highlight screen_name
128 tweet
= map(lambda x
: cycle_color(x
) if x
[0] == '@' else x
, tweet
)
130 tweet
= map(lambda x
: cyan(x
) if x
[0:7] == 'http://' else x
, tweet
)
131 # Highlight search keyword
134 lambda x
: on_yellow(x
) if
135 ''.join(c
for c
in x
if c
.isalnum()).lower() == keyword
.lower()
140 tweet
= ' '.join(tweet
)
143 line1
= u
"{u:>{uw}}:".format(
147 line2
= u
"{c:>{cw}}".format(
159 if iot
and media_url
:
161 response
= requests
.get(mu
)
162 image_to_display(StringIO(response
.content
))
165 def print_message(m
):
169 sender_screen_name
= '@' + m
['sender_screen_name']
170 sender_name
= m
['sender']['name']
172 recipient_screen_name
= '@' + m
['recipient_screen_name']
173 recipient_name
= m
['recipient']['name']
175 date
= parser
.parse(m
['created_at'])
176 date
= date
- datetime
.timedelta(seconds
=time
.timezone
)
177 clock
= date
.strftime('%Y/%m/%d %H:%M:%S')
180 res
= db
.message_to_rainbow_query(mid
)
182 db
.message_store(mid
)
183 res
= db
.message_to_rainbow_query(mid
)
184 rid
= res
[0].rainbow_id
186 sender
= cycle_color(sender_name
) + grey(' ' + sender_screen_name
+ ' ')
187 recipient
= cycle_color(recipient_name
) + grey(' ' + recipient_screen_name
+ ' ')
188 user
= sender
+ magenta(' >>> ') + recipient
189 meta
= grey('[' + clock
+ '] [message_id=' + str(rid
) + '] ')
190 text
= ''.join(map(lambda x
: x
+' ' if x
=='\n' else x
,text
))
192 line1
= u
"{u:>{uw}}:".format(
196 line2
= u
"{c:>{cw}}".format(
215 screen_name
= u
['screen_name']
216 description
= u
['description']
217 profile_image_url
= u
['profile_image_url']
218 location
= u
['location']
220 created_at
= u
['created_at']
221 statuses_count
= u
['statuses_count']
222 friends_count
= u
['friends_count']
223 followers_count
= u
['followers_count']
225 statuses_count
= green(str(statuses_count
) + ' tweets')
226 friends_count
= green(str(friends_count
) + ' following')
227 followers_count
= green(str(followers_count
) + ' followers')
228 count
= statuses_count
+ ' ' + friends_count
+ ' ' + followers_count
229 user
= cycle_color(name
) + grey(' ' + screen_name
+ ' : ') + count
230 profile_image_url
= 'Profile photo: ' + cyan(profile_image_url
)
231 description
= ''.join(map(lambda x
: x
+' '*4 if x
=='\n' else x
,description
))
232 description
= yellow(description
)
233 location
= 'Location : ' + magenta(location
)
234 url
= 'URL : ' + (cyan(url
) if url
else '')
235 created_at
= 'Join at ' + white(created_at
)
237 line1
= u
"{u:>{uw}}".format(
241 line2
= u
"{p:>{pw}}".format(
243 pw
=len(profile_image_url
) + 4,
245 line3
= u
"{d:>{dw}}".format(
247 dw
=len(description
) + 4,
249 line4
= u
"{l:>{lw}}".format(
251 lw
=len(location
) + 4,
253 line5
= u
"{u:>{uw}}".format(
257 line6
= u
"{j:>{jw}}".format(
259 jw
=len(created_at
) + 4,
263 for line
in [line1
,line2
,line3
,line4
,line5
,line6
]:
268 def parse_arguments():
272 parser
= argparse
.ArgumentParser(description
=__doc__
or "")
276 help='Timeout for the stream (seconds).')
279 '--heartbeat-timeout',
280 help='Set heartbeat timeout.',
286 help='Set stream to non-blocking.')
290 help='Search the stream for specific text.')
294 help='Filter specific screen_name.')
298 help='Ignore specific screen_name.')
303 help='Display all image on terminal.')
304 return parser
.parse_args()
309 Authenticate with Twitter OAuth
311 # When using rainbow stream you must authorize.
312 twitter_credential
= os
.environ
.get(
316 '')) + os
.sep
+ '.rainbow_oauth'
317 if not os
.path
.exists(twitter_credential
):
318 oauth_dance("Rainbow Stream",
322 oauth_token
, oauth_token_secret
= read_token_file(twitter_credential
)
330 def get_decorated_name():
332 Beginning of every line
334 t
= Twitter(auth
=authen())
335 name
= '@' + t
.account
.verify_credentials()['screen_name']
336 g
['original_name'] = name
[1:]
337 g
['decorated_name'] = grey('[') + grey(name
) + grey(']: ')
345 target
= g
['stuff'].split()[0]
348 args
= parse_arguments()
350 if g
['stuff'].split()[-1] == '-f':
351 only
= raw_input('Only nicks: ')
352 ignore
= raw_input('Ignore nicks: ')
353 args
.filter = filter(None, only
.split(','))
354 args
.ignore
= filter(None, ignore
.split(','))
355 elif g
['stuff'].split()[-1] == '-d':
356 args
.filter = ONLY_LIST
357 args
.ignore
= IGNORE_LIST
359 printNicely(red('Sorry, wrong format.'))
363 if target
== 'public':
364 keyword
= g
['stuff'].split()[1]
365 if keyword
[0] == '#':
366 keyword
= keyword
[1:]
368 os
.kill(g
['stream_pid'], signal
.SIGKILL
)
369 args
.track_keywords
= keyword
377 g
['stream_pid'] = p
.pid
380 elif target
== 'mine':
382 os
.kill(g
['stream_pid'], signal
.SIGKILL
)
391 g
['stream_pid'] = p
.pid
393 printNicely(green('Stream switched.'))
395 printNicely(cyan('Only: ' + str(args
.filter)))
397 printNicely(red('Ignore: ' + str(args
.ignore
)))
400 printNicely(red('Sorry I can\'t understand.'))
407 t
= Twitter(auth
=authen())
409 if g
['stuff'].isdigit():
410 num
= int(g
['stuff'])
411 for tweet
in reversed(t
.statuses
.home_timeline(count
=num
)):
412 draw(t
=tweet
, iot
=g
['iot'])
420 t
= Twitter(auth
=authen())
421 user
= g
['stuff'].split()[0]
424 num
= int(g
['stuff'].split()[1])
427 for tweet
in reversed(t
.statuses
.user_timeline(count
=num
, screen_name
=user
[1:])):
428 draw(t
=tweet
, iot
=g
['iot'])
431 printNicely(red('A name should begin with a \'@\''))
438 t
= Twitter(auth
=authen())
440 if g
['stuff'].isdigit():
441 num
= int(g
['stuff'])
442 for tweet
in reversed(t
.statuses
.mentions_timeline(count
=num
)):
443 draw(t
=tweet
, iot
=g
['iot'])
451 t
= Twitter(auth
=authen())
452 t
.statuses
.update(status
=g
['stuff'])
459 t
= Twitter(auth
=authen())
461 id = int(g
['stuff'].split()[0])
462 tid
= db
.rainbow_to_tweet_query(id)[0].tweet_id
463 t
.statuses
.retweet(id=tid
, include_entities
=False, trim_user
=True)
465 printNicely(red('Sorry I can\'t retweet for you.'))
472 t
= Twitter(auth
=authen())
474 id = int(g
['stuff'].split()[0])
475 tid
= db
.rainbow_to_tweet_query(id)[0].tweet_id
476 t
.favorites
.create(_id
=tid
, include_entities
=False)
477 printNicely(green('Favorited.'))
478 draw(t
.statuses
.show(id=tid
), iot
=g
['iot'])
480 printNicely(red('Omg some syntax is wrong.'))
487 t
= Twitter(auth
=authen())
489 id = int(g
['stuff'].split()[0])
490 tid
= db
.rainbow_to_tweet_query(id)[0].tweet_id
491 user
= t
.statuses
.show(id=tid
)['user']['screen_name']
492 status
= ' '.join(g
['stuff'].split()[1:])
493 status
= '@' + user
+ ' ' + status
.decode('utf-8')
494 t
.statuses
.update(status
=status
, in_reply_to_status_id
=tid
)
496 printNicely(red('Sorry I can\'t understand.'))
503 t
= Twitter(auth
=authen())
505 rid
= int(g
['stuff'].split()[0])
506 tid
= db
.rainbow_to_tweet_query(rid
)[0].tweet_id
507 t
.statuses
.destroy(id=tid
)
508 printNicely(green('Okay it\'s gone.'))
510 printNicely(red('Sorry I can\'t understand.'))
517 t
= Twitter(auth
=authen())
519 id = int(g
['stuff'].split()[0])
520 tid
= db
.rainbow_to_tweet_query(id)[0].tweet_id
521 t
.favorites
.destroy(_id
=tid
)
522 printNicely(green('Okay it\'s unfavorited.'))
523 draw(t
.statuses
.show(id=tid
), iot
=g
['iot'])
525 printNicely(red('Sorry I can\'t unfavorite this tweet for you.'))
532 t
= Twitter(auth
=authen())
534 if g
['stuff'][0] == '#':
535 rel
= t
.search
.tweets(q
=g
['stuff'])['statuses']
537 printNicely('Newest tweets:')
538 for i
in reversed(xrange(SEARCH_MAX_RECORD
)):
541 keyword
=g
['stuff'].strip()[1:])
544 printNicely(magenta('I\'m afraid there is no result'))
546 printNicely(red('A keyword should be a hashtag (like \'#AKB48\')'))
548 printNicely(red('Sorry I can\'t understand.'))
553 Send a direct message
555 t
= Twitter(auth
=authen())
556 user
= g
['stuff'].split()[0]
559 content
= g
['stuff'].split()[1]
560 t
.direct_messages
.new(
561 screen_name
=user
[1:],
564 printNicely(green('Message sent.'))
566 printNicely(red('Sorry I can\'t understand.'))
568 printNicely(red('A name should begin with a \'@\''))
575 t
= Twitter(auth
=authen())
577 target
= g
['stuff'].split()[0]
578 if target
!= 'image':
580 id = int(g
['stuff'].split()[1])
581 tid
= db
.rainbow_to_tweet_query(id)[0].tweet_id
582 tweet
= t
.statuses
.show(id=tid
)
583 media
= tweet
['entities']['media']
585 res
= requests
.get(m
['media_url'])
586 img
= Image
.open(StringIO(res
.content
))
589 printNicely(red('Sorry I can\'t show this image.'))
594 List friends for followers
596 t
= Twitter(auth
=authen())
599 name
= g
['stuff'].split()[1]
603 printNicely(red('A name should begin with a \'@\''))
604 raise Exception('Invalid name')
606 name
= g
['original_name']
607 # Get list followers or friends
609 target
= g
['stuff'].split()[0]
610 d
= {'fl': 'followers', 'fr': 'friends'}
614 while next_cursor
!= 0:
615 list = getattr(t
, d
[target
]).list(
619 include_entities
=False,
621 for u
in list['users']:
622 rel
[u
['name']] = '@' + u
['screen_name']
623 next_cursor
= list['next_cursor']
625 printNicely('All: ' + str(len(rel
)) + ' people.')
627 user
= ' ' + cycle_color(name
) + grey(' ' + rel
[name
] + ' ')
630 printNicely(red('Omg some syntax is wrong.'))
635 Inbox direct messages
637 t
= Twitter(auth
=authen())
638 num
= MESSAGES_DISPLAY
640 if g
['stuff'].isdigit():
643 # Max message per page is 20 so we have to loop
645 rel
= rel
+ t
.direct_messages(
648 include_entities
=False,
653 rel
= rel
+ t
.direct_messages(
656 include_entities
=False,
660 printNicely('Inbox: newest ' + str(len(rel
)) + ' messages.')
661 for m
in reversed(rel
):
670 t
= Twitter(auth
=authen())
671 num
= MESSAGES_DISPLAY
673 if g
['stuff'].isdigit():
674 num
= int(g
['stuff'])
676 # Max message per page is 20 so we have to loop
678 rel
= rel
+ t
.direct_messages
.sent(
681 include_entities
=False,
686 rel
= rel
+ t
.direct_messages
.sent(
689 include_entities
=False,
693 printNicely('Sent: newest ' + str(len(rel
)) + ' messages.')
694 for m
in reversed(rel
):
703 t
= Twitter(auth
=authen())
705 rid
= int(g
['stuff'].split()[0])
706 mid
= db
.rainbow_to_message_query(rid
)[0].message_id
707 t
.direct_messages
.destroy(id=mid
)
708 printNicely(green('Message deleted.'))
710 printNicely(red('Sorry I can\'t understand.'))
715 Show profile of a specific user
717 t
= Twitter(auth
=authen())
718 screen_name
= g
['stuff'].split()[0]
719 if screen_name
[0] == '@':
722 screen_name
=screen_name
[1:],
723 include_entities
=False)
726 printNicely(red('Omg no user.'))
728 printNicely(red('Sorry I can\'t understand.'))
735 t
= Twitter(auth
=authen())
736 screen_name
= g
['stuff'].split()[0]
737 if screen_name
[0] == '@':
739 t
.friendships
.create(screen_name
=screen_name
[1:], follow
=True)
740 printNicely(green('You are following ' + screen_name
+ ' now!'))
742 printNicely(red('Sorry can not follow at this time.'))
744 printNicely(red('Sorry I can\'t understand.'))
751 t
= Twitter(auth
=authen())
752 screen_name
= g
['stuff'].split()[0]
753 if screen_name
[0] == '@':
755 t
.friendships
.destroy(
756 screen_name
=screen_name
[1:],
757 include_entities
=False)
758 printNicely(green('Unfollow ' + screen_name
+ ' success!'))
760 printNicely(red('Sorry can not unfollow at this time.'))
762 printNicely(red('Sorry I can\'t understand.'))
769 t
= Twitter(auth
=authen())
770 screen_name
= g
['stuff'].split()[0]
771 if screen_name
[0] == '@':
774 screen_name
=screen_name
[1:],
775 include_entities
=False,
777 printNicely(green('You blocked ' + screen_name
+ '.'))
779 printNicely(red('Sorry something went wrong.'))
781 printNicely(red('Sorry I can\'t understand.'))
788 t
= Twitter(auth
=authen())
789 screen_name
= g
['stuff'].split()[0]
790 if screen_name
[0] == '@':
793 screen_name
=screen_name
[1:],
794 include_entities
=False,
796 printNicely(green('Unblock ' + screen_name
+ ' success!'))
798 printNicely(red('Sorry something went wrong.'))
800 printNicely(red('Sorry I can\'t understand.'))
805 Report a user as a spam account
807 t
= Twitter(auth
=authen())
808 screen_name
= g
['stuff'].split()[0]
809 if screen_name
[0] == '@':
812 screen_name
=screen_name
[1:])
813 printNicely(green('You reported ' + screen_name
+ '.'))
815 printNicely(red('Sorry something went wrong.'))
817 printNicely(red('Sorry I can\'t understand.'))
825 h
, w
= os
.popen('stty size', 'r').read().split()
828 usage
+= s
+ 'Hi boss! I\'m ready to serve you right now!\n'
829 usage
+= s
+ '-' * (int(w
) - 4) + '\n'
831 usage
+= s
+ 'You are ' + yellow('already') + ' on your personal stream.\n'
832 usage
+= s
* 2 + green('switch public #AKB') + \
833 ' will switch to public stream and follow "' + \
834 yellow('AKB') + '" keyword.\n'
835 usage
+= s
* 2 + green('switch mine') + \
836 ' will switch to your personal stream.\n'
837 usage
+= s
* 2 + green('switch mine -f ') + \
838 ' will prompt to enter the filter.\n'
839 usage
+= s
* 3 + yellow('Only nicks') + \
840 ' filter will decide nicks will be INCLUDE ONLY.\n'
841 usage
+= s
* 3 + yellow('Ignore nicks') + \
842 ' filter will decide nicks will be EXCLUDE.\n'
843 usage
+= s
* 2 + green('switch mine -d') + \
844 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
845 usage
+= s
* 3 + '(see ' + grey('rainbowstream/config.py') + ').\n'
847 usage
+= s
+ 'For more action: \n'
848 usage
+= s
* 2 + green('home') + ' will show your timeline. ' + \
849 green('home 7') + ' will show 7 tweets.\n'
850 usage
+= s
* 2 + green('view @mdo') + \
851 ' will show ' + magenta('@mdo') + '\'s home.\n'
852 usage
+= s
* 2 + green('mentions') + ' will show mentions timeline. ' + \
853 green('mentions 7') + ' will show 7 mention tweets.\n'
854 usage
+= s
* 2 + green('t oops ') + \
855 'will tweet "' + yellow('oops') + '" immediately.\n'
857 green('rt 12 ') + ' will retweet to tweet with ' + \
858 yellow('[id=12]') + '.\n'
860 green('fav 12 ') + ' will favorite the tweet with ' + \
861 yellow('[id=12]') + '.\n'
862 usage
+= s
* 2 + green('rep 12 oops') + ' will reply "' + \
863 yellow('oops') + '" to tweet with ' + yellow('[id=12]') + '.\n'
865 green('del 12 ') + ' will delete tweet with ' + \
866 yellow('[id=12]') + '.\n'
868 green('ufav 12 ') + ' will unfavorite tweet with ' + \
869 yellow('[id=12]') + '.\n'
870 usage
+= s
* 2 + green('s #AKB48') + ' will search for "' + \
871 yellow('AKB48') + '" and return 5 newest tweet.\n'
872 usage
+= s
* 2 + green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
873 magenta('@dtvd88') + '.\n'
874 usage
+= s
* 2 + green('show image 12') + ' will show image in tweet with ' + \
875 yellow('[id=12]') + ' in your OS\'s image viewer.\n'
878 ' will list all followers (people who are following you).\n'
881 ' will list all friends (people who you are following).\n'
882 usage
+= s
* 2 + green('inbox') + ' will show inbox messages. ' + \
883 green('inbox 7') + ' will show newest 7 messages.\n'
884 usage
+= s
* 2 + green('sent') + ' will show sent messages. ' + \
885 green('sent 7') + ' will show newest 7 messages.\n'
886 usage
+= s
* 2 + green('trash 5') + ' will remove message with ' + \
887 yellow('[message_id=5]') + '.\n'
888 usage
+= s
* 2 + green('whois @dtvd88') + ' will show profile of ' + \
889 magenta('@dtvd88') + '.\n'
890 usage
+= s
* 2 + green('fl @dtvd88') + ' will follow ' + \
891 magenta('@dtvd88') + '.\n'
892 usage
+= s
* 2 + green('ufl @dtvd88') + ' will unfollow ' + \
893 magenta('@dtvd88') + '.\n'
894 usage
+= s
* 2 + green('block @dtvd88') + ' will block ' + \
895 magenta('@dtvd88') + '.\n'
896 usage
+= s
* 2 + green('unblock @dtvd88') + ' will unblock ' + \
897 magenta('@dtvd88') + '.\n'
898 usage
+= s
* 2 + green('report @dtvd88') + ' will report ' + \
899 magenta('@dtvd88') + ' as a spam account.\n'
900 usage
+= s
* 2 + green('h') + ' will show this help again.\n'
901 usage
+= s
* 2 + green('c') + ' will clear the screen.\n'
902 usage
+= s
* 2 + green('q') + ' will quit.\n'
904 usage
+= s
+ '-' * (int(w
) - 4) + '\n'
905 usage
+= s
+ 'Have fun and hang tight!\n'
921 os
.system('rm -rf rainbow.db')
922 os
.kill(g
['stream_pid'], signal
.SIGKILL
)
931 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
974 Listen to user's input
979 ['public', 'mine'], # switch
991 ['image'], # show image
1007 init_interactive_shell(d
)
1012 line
= raw_input(g
['decorated_name'])
1016 cmd
= line
.split()[0]
1019 # Save cmd to global variable and call process
1020 g
['stuff'] = ' '.join(line
.split()[1:])
1022 if cmd
in ['switch', 't', 'rt', 'rep']:
1028 def stream(domain
, args
, name
='Rainbow Stream'):
1036 PUBLIC_DOMAIN
: args
.track_keywords
,
1037 SITE_DOMAIN
: 'Site Stream',
1039 ascii_art(art_dict
[domain
])
1041 # These arguments are optional:
1043 timeout
=args
.timeout
,
1044 block
=not args
.no_block
,
1045 heartbeat_timeout
=args
.heartbeat_timeout
)
1049 if args
.track_keywords
:
1050 query_args
['track'] = args
.track_keywords
1053 stream
= TwitterStream(
1058 if domain
== USER_DOMAIN
:
1059 tweet_iter
= stream
.user(**query_args
)
1060 elif domain
== SITE_DOMAIN
:
1061 tweet_iter
= stream
.site(**query_args
)
1063 if args
.track_keywords
:
1064 tweet_iter
= stream
.statuses
.filter(**query_args
)
1066 tweet_iter
= stream
.statuses
.sample()
1068 # Iterate over the stream.
1069 for tweet
in tweet_iter
:
1071 printNicely("-- None --")
1072 elif tweet
is Timeout
:
1073 printNicely("-- Timeout --")
1074 elif tweet
is HeartbeatTimeout
:
1075 printNicely("-- Heartbeat Timeout --")
1076 elif tweet
is Hangup
:
1077 printNicely("-- Hangup --")
1078 elif tweet
.get('text'):
1081 iot
=args
.image_on_term
,
1082 keyword
=args
.track_keywords
,
1092 # Spawn stream process
1093 args
= parse_arguments()
1094 get_decorated_name()
1095 p
= Process(target
=stream
, args
=(USER_DOMAIN
, args
, g
['original_name']))
1098 # Start listen process
1102 g
['stream_pid'] = p
.pid
1103 g
['iot'] = args
.image_on_term