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