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