6a565f9fb2bcb48864a25e5b9ef9ce52daf8278e
[rainbowstream.git] / rainbowstream / rainbow.py
1 import os
2 import os.path
3 import sys
4 import signal
5 import argparse
6 import time
7 import threading
8 import requests
9 import webbrowser
10 import traceback
11 import pkg_resources
12
13 from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup
14 from twitter.api import *
15 from twitter.oauth import OAuth, read_token_file
16 from twitter.oauth_dance import oauth_dance
17 from twitter.util import printNicely
18
19 from .draw import *
20 from .colors import *
21 from .config import *
22 from .consumer import *
23 from .interactive import *
24 from .c_image import *
25 from .py3patch import *
26 from .emoji import *
27
28 # Global values
29 g = {}
30
31 # Lock for streams
32 StreamLock = threading.Lock()
33
34
35 def parse_arguments():
36 """
37 Parse the arguments
38 """
39 parser = argparse.ArgumentParser(description=__doc__ or "")
40 parser.add_argument(
41 '-to',
42 '--timeout',
43 help='Timeout for the stream (seconds).')
44 parser.add_argument(
45 '-tt',
46 '--track-keywords',
47 help='Search the stream for specific text.')
48 parser.add_argument(
49 '-fil',
50 '--filter',
51 help='Filter specific screen_name.')
52 parser.add_argument(
53 '-ig',
54 '--ignore',
55 help='Ignore specific screen_name.')
56 parser.add_argument(
57 '-iot',
58 '--image-on-term',
59 action='store_true',
60 help='Display all image on terminal.')
61 return parser.parse_args()
62
63
64 def authen():
65 """
66 Authenticate with Twitter OAuth
67 """
68 # When using rainbow stream you must authorize.
69 twitter_credential = os.environ.get(
70 'HOME',
71 os.environ.get(
72 'USERPROFILE',
73 '')) + os.sep + '.rainbow_oauth'
74 if not os.path.exists(twitter_credential):
75 oauth_dance("Rainbow Stream",
76 CONSUMER_KEY,
77 CONSUMER_SECRET,
78 twitter_credential)
79 oauth_token, oauth_token_secret = read_token_file(twitter_credential)
80 return OAuth(
81 oauth_token,
82 oauth_token_secret,
83 CONSUMER_KEY,
84 CONSUMER_SECRET)
85
86
87 def build_mute_dict(dict_data=False):
88 """
89 Build muting list
90 """
91 t = Twitter(auth=authen())
92 # Init cursor
93 next_cursor = -1
94 screen_name_list = []
95 name_list = []
96 # Cursor loop
97 while next_cursor != 0:
98 list = t.mutes.users.list(
99 screen_name=g['original_name'],
100 cursor=next_cursor,
101 skip_status=True,
102 include_entities=False,
103 )
104 screen_name_list += ['@' + u['screen_name'] for u in list['users']]
105 name_list += [u['name'] for u in list['users']]
106 next_cursor = list['next_cursor']
107 # Return dict or list
108 if dict_data:
109 return dict(zip(screen_name_list, name_list))
110 else:
111 return screen_name_list
112
113
114 def debug_option():
115 """
116 Save traceback when run in debug mode
117 """
118 if g['debug']:
119 g['traceback'].append(traceback.format_exc())
120
121
122 def upgrade_center():
123 """
124 Check latest and notify to upgrade
125 """
126 try:
127 current = pkg_resources.get_distribution("rainbowstream").version
128 url = 'https://raw.githubusercontent.com/DTVD/rainbowstream/master/setup.py'
129 readme = requests.get(url).content
130 latest = readme.split("version = \'")[1].split("\'")[0]
131 if current != latest:
132 notice = light_magenta('RainbowStream latest version is ')
133 notice += light_green(latest)
134 notice += light_magenta(' while your current version is ')
135 notice += light_yellow(current) + '\n'
136 notice += light_magenta('You should upgrade with ')
137 notice += light_green('pip install -U rainbowstream')
138 printNicely(notice)
139 except:
140 pass
141
142
143 def init(args):
144 """
145 Init function
146 """
147 # Handle Ctrl C
148 ctrl_c_handler = lambda signum, frame: quit()
149 signal.signal(signal.SIGINT, ctrl_c_handler)
150 # Upgrade notify
151 upgrade_center()
152 # Get name
153 t = Twitter(auth=authen())
154 credential = t.account.verify_credentials()
155 screen_name = '@' + credential['screen_name']
156 name = credential['name']
157 if not get_config('PREFIX'):
158 set_config('PREFIX', screen_name)
159 c['PREFIX'] = emojize(c['PREFIX'])
160 g['PREFIX'] = u2str(c['PREFIX'])
161 c['original_name'] = g['original_name'] = screen_name[1:]
162 g['full_name'] = name
163 g['decorated_name'] = lambda x: color_func(
164 c['DECORATED_NAME'])('[' + x + ']: ')
165 # Theme init
166 files = os.listdir(os.path.dirname(__file__) + '/colorset')
167 themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
168 g['themes'] = themes
169 g['pause'] = False
170 g['message_threads'] = {}
171 # Events
172 g['events'] = []
173 # Startup cmd
174 g['cmd'] = ''
175 # Debug option default = True
176 g['debug'] = True
177 g['traceback'] = []
178 # Retweet of mine events
179 c['events'] = []
180 # Semaphore init
181 c['lock'] = False
182 # Init tweet dict and message dict
183 c['tweet_dict'] = []
184 c['message_dict'] = []
185 # Image on term
186 c['IMAGE_ON_TERM'] = args.image_on_term
187 set_config('IMAGE_ON_TERM', str(c['IMAGE_ON_TERM']))
188 # Mute dict
189 c['IGNORE_LIST'] += build_mute_dict()
190
191
192 def trend():
193 """
194 Trend
195 """
196 t = Twitter(auth=authen())
197 # Get country and town
198 try:
199 country = g['stuff'].split()[0]
200 except:
201 country = ''
202 try:
203 town = g['stuff'].split()[1]
204 except:
205 town = ''
206 avail = t.trends.available()
207 # World wide
208 if not country:
209 trends = t.trends.place(_id=1)[0]['trends']
210 print_trends(trends)
211 else:
212 for location in avail:
213 # Search for country and Town
214 if town:
215 if location['countryCode'] == country \
216 and location['placeType']['name'] == 'Town' \
217 and location['name'] == town:
218 trends = t.trends.place(_id=location['woeid'])[0]['trends']
219 print_trends(trends)
220 # Search for country only
221 else:
222 if location['countryCode'] == country \
223 and location['placeType']['name'] == 'Country':
224 trends = t.trends.place(_id=location['woeid'])[0]['trends']
225 print_trends(trends)
226
227
228 def home():
229 """
230 Home
231 """
232 t = Twitter(auth=authen())
233 num = c['HOME_TWEET_NUM']
234 if g['stuff'].isdigit():
235 num = int(g['stuff'])
236 for tweet in reversed(t.statuses.home_timeline(count=num)):
237 draw(t=tweet)
238 printNicely('')
239
240
241 def notification():
242 """
243 Show notifications
244 """
245 g['events'] = g['events'] + c['events']
246 if g['events']:
247 for e in g['events']:
248 print_event(e)
249 printNicely('')
250 else:
251 printNicely(magenta('Nothing at this time.'))
252
253
254 def mentions():
255 """
256 Mentions timeline
257 """
258 t = Twitter(auth=authen())
259 num = c['HOME_TWEET_NUM']
260 if g['stuff'].isdigit():
261 num = int(g['stuff'])
262 for tweet in reversed(t.statuses.mentions_timeline(count=num)):
263 draw(t=tweet)
264 printNicely('')
265
266
267 def whois():
268 """
269 Show profile of a specific user
270 """
271 t = Twitter(auth=authen())
272 screen_name = g['stuff'].split()[0]
273 if screen_name.startswith('@'):
274 try:
275 user = t.users.show(
276 screen_name=screen_name[1:],
277 include_entities=False)
278 show_profile(user)
279 except:
280 debug_option()
281 printNicely(red('No user.'))
282 else:
283 printNicely(red('A name should begin with a \'@\''))
284
285
286 def view():
287 """
288 Friend view
289 """
290 t = Twitter(auth=authen())
291 user = g['stuff'].split()[0]
292 if user[0] == '@':
293 try:
294 num = int(g['stuff'].split()[1])
295 except:
296 num = c['HOME_TWEET_NUM']
297 for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])):
298 draw(t=tweet)
299 printNicely('')
300 else:
301 printNicely(red('A name should begin with a \'@\''))
302
303
304 def search():
305 """
306 Search
307 """
308 t = Twitter(auth=authen())
309 # Setup query
310 query = g['stuff'].strip()
311 type = c['SEARCH_TYPE']
312 if type not in ['mixed', 'recent', 'popular']:
313 type = 'mixed'
314 max_record = c['SEARCH_MAX_RECORD']
315 count = min(max_record, 100)
316 # Perform search
317 rel = t.search.tweets(
318 q=query,
319 type=type,
320 count=count
321 )['statuses']
322 # Return results
323 if rel:
324 printNicely('Newest tweets:')
325 for i in reversed(xrange(count)):
326 draw(t=rel[i], keyword=query)
327 printNicely('')
328 else:
329 printNicely(magenta('I\'m afraid there is no result'))
330
331
332 def tweet():
333 """
334 Tweet
335 """
336 t = Twitter(auth=authen())
337 t.statuses.update(status=g['stuff'])
338
339
340 def retweet():
341 """
342 ReTweet
343 """
344 t = Twitter(auth=authen())
345 try:
346 id = int(g['stuff'].split()[0])
347 except:
348 printNicely(red('Sorry I can\'t understand.'))
349 return
350 tid = c['tweet_dict'][id]
351 t.statuses.retweet(id=tid, include_entities=False, trim_user=True)
352
353
354 def quote():
355 """
356 Quote a tweet
357 """
358 # Get tweet
359 t = Twitter(auth=authen())
360 try:
361 id = int(g['stuff'].split()[0])
362 except:
363 printNicely(red('Sorry I can\'t understand.'))
364 return
365 tid = c['tweet_dict'][id]
366 tweet = t.statuses.show(id=tid)
367 # Get formater
368 formater = format_quote(tweet)
369 if not formater:
370 return
371 # Get comment
372 prefix = light_magenta('Compose your ') + light_green('#comment: ')
373 comment = raw_input(prefix)
374 if comment:
375 quote = comment.join(formater.split('#comment'))
376 t.statuses.update(status=quote)
377 else:
378 printNicely(light_magenta('No text added.'))
379
380
381 def allretweet():
382 """
383 List all retweet
384 """
385 t = Twitter(auth=authen())
386 # Get rainbow id
387 try:
388 id = int(g['stuff'].split()[0])
389 except:
390 printNicely(red('Sorry I can\'t understand.'))
391 return
392 tid = c['tweet_dict'][id]
393 # Get display num if exist
394 try:
395 num = int(g['stuff'].split()[1])
396 except:
397 num = c['RETWEETS_SHOW_NUM']
398 # Get result and display
399 rt_ary = t.statuses.retweets(id=tid, count=num)
400 if not rt_ary:
401 printNicely(magenta('This tweet has no retweet.'))
402 return
403 for tweet in reversed(rt_ary):
404 draw(t=tweet)
405 printNicely('')
406
407
408 def conversation():
409 """
410 Conversation view
411 """
412 t = Twitter(auth=authen())
413 try:
414 id = int(g['stuff'].split()[0])
415 except:
416 printNicely(red('Sorry I can\'t understand.'))
417 return
418 tid = c['tweet_dict'][id]
419 tweet = t.statuses.show(id=tid)
420 limit = c['CONVERSATION_MAX']
421 thread_ref = []
422 thread_ref.append(tweet)
423 prev_tid = tweet['in_reply_to_status_id']
424 while prev_tid and limit:
425 limit -= 1
426 tweet = t.statuses.show(id=prev_tid)
427 prev_tid = tweet['in_reply_to_status_id']
428 thread_ref.append(tweet)
429
430 for tweet in reversed(thread_ref):
431 draw(t=tweet)
432 printNicely('')
433
434
435 def reply():
436 """
437 Reply
438 """
439 t = Twitter(auth=authen())
440 try:
441 id = int(g['stuff'].split()[0])
442 except:
443 printNicely(red('Sorry I can\'t understand.'))
444 return
445 tid = c['tweet_dict'][id]
446 user = t.statuses.show(id=tid)['user']['screen_name']
447 status = ' '.join(g['stuff'].split()[1:])
448 status = '@' + user + ' ' + str2u(status)
449 t.statuses.update(status=status, in_reply_to_status_id=tid)
450
451
452 def favorite():
453 """
454 Favorite
455 """
456 t = Twitter(auth=authen())
457 try:
458 id = int(g['stuff'].split()[0])
459 except:
460 printNicely(red('Sorry I can\'t understand.'))
461 return
462 tid = c['tweet_dict'][id]
463 t.favorites.create(_id=tid, include_entities=False)
464 printNicely(green('Favorited.'))
465 draw(t.statuses.show(id=tid))
466 printNicely('')
467
468
469 def unfavorite():
470 """
471 Unfavorite
472 """
473 t = Twitter(auth=authen())
474 try:
475 id = int(g['stuff'].split()[0])
476 except:
477 printNicely(red('Sorry I can\'t understand.'))
478 return
479 tid = c['tweet_dict'][id]
480 t.favorites.destroy(_id=tid)
481 printNicely(green('Okay it\'s unfavorited.'))
482 draw(t.statuses.show(id=tid))
483 printNicely('')
484
485
486 def delete():
487 """
488 Delete
489 """
490 t = Twitter(auth=authen())
491 try:
492 id = int(g['stuff'].split()[0])
493 except:
494 printNicely(red('Sorry I can\'t understand.'))
495 return
496 tid = c['tweet_dict'][id]
497 t.statuses.destroy(id=tid)
498 printNicely(green('Okay it\'s gone.'))
499
500
501 def show():
502 """
503 Show image
504 """
505 t = Twitter(auth=authen())
506 try:
507 target = g['stuff'].split()[0]
508 if target != 'image':
509 return
510 id = int(g['stuff'].split()[1])
511 tid = c['tweet_dict'][id]
512 tweet = t.statuses.show(id=tid)
513 media = tweet['entities']['media']
514 for m in media:
515 res = requests.get(m['media_url'])
516 img = Image.open(BytesIO(res.content))
517 img.show()
518 except:
519 debug_option()
520 printNicely(red('Sorry I can\'t show this image.'))
521
522
523 def urlopen():
524 """
525 Open url
526 """
527 t = Twitter(auth=authen())
528 try:
529 if not g['stuff'].isdigit():
530 return
531 tid = c['tweet_dict'][int(g['stuff'])]
532 tweet = t.statuses.show(id=tid)
533 link_prefix = ('http://', 'https://')
534 link_ary = [u for u in tweet['text'].split()
535 if u.startswith(link_prefix)]
536 if not link_ary:
537 printNicely(light_magenta('No url here @.@!'))
538 return
539 for link in link_ary:
540 webbrowser.open(link)
541 except:
542 debug_option()
543 printNicely(red('Sorry I can\'t open url in this tweet.'))
544
545
546 def inbox():
547 """
548 Inbox threads
549 """
550 t = Twitter(auth=authen())
551 num = c['MESSAGES_DISPLAY']
552 if g['stuff'].isdigit():
553 num = g['stuff']
554 # Get inbox messages
555 cur_page = 1
556 inbox = []
557 while num > 20:
558 inbox = inbox + t.direct_messages(
559 count=20,
560 page=cur_page,
561 include_entities=False,
562 skip_status=False
563 )
564 num -= 20
565 cur_page += 1
566 inbox = inbox + t.direct_messages(
567 count=num,
568 page=cur_page,
569 include_entities=False,
570 skip_status=False
571 )
572 # Get sent messages
573 num = c['MESSAGES_DISPLAY']
574 if g['stuff'].isdigit():
575 num = g['stuff']
576 cur_page = 1
577 sent = []
578 while num > 20:
579 sent = sent + t.direct_messages.sent(
580 count=20,
581 page=cur_page,
582 include_entities=False,
583 skip_status=False
584 )
585 num -= 20
586 cur_page += 1
587 sent = sent + t.direct_messages.sent(
588 count=num,
589 page=cur_page,
590 include_entities=False,
591 skip_status=False
592 )
593
594 d = {}
595 uniq_inbox = list(set(
596 [(m['sender_screen_name'], m['sender']['name']) for m in inbox]
597 ))
598 uniq_sent = list(set(
599 [(m['recipient_screen_name'], m['recipient']['name']) for m in sent]
600 ))
601 for partner in uniq_inbox:
602 inbox_ary = [m for m in inbox if m['sender_screen_name'] == partner[0]]
603 sent_ary = [
604 m for m in sent if m['recipient_screen_name'] == partner[0]]
605 d[partner] = inbox_ary + sent_ary
606 for partner in uniq_sent:
607 if partner not in d:
608 d[partner] = [
609 m for m in sent if m['recipient_screen_name'] == partner[0]]
610 g['message_threads'] = print_threads(d)
611
612
613 def thread():
614 """
615 View a thread of message
616 """
617 try:
618 thread_id = int(g['stuff'])
619 print_thread(
620 g['message_threads'][thread_id],
621 g['original_name'],
622 g['full_name'])
623 except Exception:
624 debug_option()
625 printNicely(red('No such thread.'))
626
627
628 def message():
629 """
630 Send a direct message
631 """
632 t = Twitter(auth=authen())
633 try:
634 user = g['stuff'].split()[0]
635 if user[0].startswith('@'):
636 content = ' '.join(g['stuff'].split()[1:])
637 t.direct_messages.new(
638 screen_name=user[1:],
639 text=content
640 )
641 printNicely(green('Message sent.'))
642 else:
643 printNicely(red('A name should begin with a \'@\''))
644 except:
645 debug_option()
646 printNicely(red('Sorry I can\'t understand.'))
647
648
649 def trash():
650 """
651 Remove message
652 """
653 t = Twitter(auth=authen())
654 try:
655 id = int(g['stuff'].split()[0])
656 except:
657 printNicely(red('Sorry I can\'t understand.'))
658 mid = c['message_dict'][id]
659 t.direct_messages.destroy(id=mid)
660 printNicely(green('Message deleted.'))
661
662
663 def ls():
664 """
665 List friends for followers
666 """
667 t = Twitter(auth=authen())
668 # Get name
669 try:
670 name = g['stuff'].split()[1]
671 if name.startswith('@'):
672 name = name[1:]
673 else:
674 printNicely(red('A name should begin with a \'@\''))
675 raise Exception('Invalid name')
676 except:
677 name = g['original_name']
678 # Get list followers or friends
679 try:
680 target = g['stuff'].split()[0]
681 except:
682 printNicely(red('Omg some syntax is wrong.'))
683 # Init cursor
684 d = {'fl': 'followers', 'fr': 'friends'}
685 next_cursor = -1
686 rel = {}
687 # Cursor loop
688 while next_cursor != 0:
689 list = getattr(t, d[target]).list(
690 screen_name=name,
691 cursor=next_cursor,
692 skip_status=True,
693 include_entities=False,
694 )
695 for u in list['users']:
696 rel[u['name']] = '@' + u['screen_name']
697 next_cursor = list['next_cursor']
698 # Print out result
699 printNicely('All: ' + str(len(rel)) + ' ' + d[target] + '.')
700 for name in rel:
701 user = ' ' + cycle_color(name)
702 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
703 printNicely(user)
704
705
706 def follow():
707 """
708 Follow a user
709 """
710 t = Twitter(auth=authen())
711 screen_name = g['stuff'].split()[0]
712 if screen_name.startswith('@'):
713 t.friendships.create(screen_name=screen_name[1:], follow=True)
714 printNicely(green('You are following ' + screen_name + ' now!'))
715 else:
716 printNicely(red('A name should begin with a \'@\''))
717
718
719 def unfollow():
720 """
721 Unfollow a user
722 """
723 t = Twitter(auth=authen())
724 screen_name = g['stuff'].split()[0]
725 if screen_name.startswith('@'):
726 t.friendships.destroy(
727 screen_name=screen_name[1:],
728 include_entities=False)
729 printNicely(green('Unfollow ' + screen_name + ' success!'))
730 else:
731 printNicely(red('A name should begin with a \'@\''))
732
733
734 def mute():
735 """
736 Mute a user
737 """
738 t = Twitter(auth=authen())
739 try:
740 screen_name = g['stuff'].split()[0]
741 except:
742 printNicely(red('A name should be specified. '))
743 return
744 if screen_name.startswith('@'):
745 try:
746 rel = t.mutes.users.create(screen_name=screen_name[1:])
747 if isinstance(rel, dict):
748 printNicely(green(screen_name + ' is muted.'))
749 c['IGNORE_LIST'] += [unc(screen_name)]
750 c['IGNORE_LIST'] = list(set(c['IGNORE_LIST']))
751 else:
752 printNicely(red(rel))
753 except:
754 debug_option()
755 printNicely(red('Something is wrong, can not mute now :('))
756 else:
757 printNicely(red('A name should begin with a \'@\''))
758
759
760 def unmute():
761 """
762 Unmute a user
763 """
764 t = Twitter(auth=authen())
765 try:
766 screen_name = g['stuff'].split()[0]
767 except:
768 printNicely(red('A name should be specified. '))
769 return
770 if screen_name.startswith('@'):
771 try:
772 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
773 if isinstance(rel, dict):
774 printNicely(green(screen_name + ' is unmuted.'))
775 c['IGNORE_LIST'].remove(screen_name)
776 else:
777 printNicely(red(rel))
778 except:
779 printNicely(red('Maybe you are not muting this person ?'))
780 else:
781 printNicely(red('A name should begin with a \'@\''))
782
783
784 def muting():
785 """
786 List muting user
787 """
788 # Get dict of muting users
789 md = build_mute_dict(dict_data=True)
790 printNicely('All: ' + str(len(md)) + ' people.')
791 for name in md:
792 user = ' ' + cycle_color(md[name])
793 user += color_func(c['TWEET']['nick'])(' ' + name + ' ')
794 printNicely(user)
795 # Update from Twitter
796 c['IGNORE_LIST'] = [n for n in md]
797
798
799 def block():
800 """
801 Block a user
802 """
803 t = Twitter(auth=authen())
804 screen_name = g['stuff'].split()[0]
805 if screen_name.startswith('@'):
806 t.blocks.create(
807 screen_name=screen_name[1:],
808 include_entities=False,
809 skip_status=True)
810 printNicely(green('You blocked ' + screen_name + '.'))
811 else:
812 printNicely(red('A name should begin with a \'@\''))
813
814
815 def unblock():
816 """
817 Unblock a user
818 """
819 t = Twitter(auth=authen())
820 screen_name = g['stuff'].split()[0]
821 if screen_name.startswith('@'):
822 t.blocks.destroy(
823 screen_name=screen_name[1:],
824 include_entities=False,
825 skip_status=True)
826 printNicely(green('Unblock ' + screen_name + ' success!'))
827 else:
828 printNicely(red('A name should begin with a \'@\''))
829
830
831 def report():
832 """
833 Report a user as a spam account
834 """
835 t = Twitter(auth=authen())
836 screen_name = g['stuff'].split()[0]
837 if screen_name.startswith('@'):
838 t.users.report_spam(
839 screen_name=screen_name[1:])
840 printNicely(green('You reported ' + screen_name + '.'))
841 else:
842 printNicely(red('Sorry I can\'t understand.'))
843
844
845 def get_slug():
846 """
847 Get Slug Decorator
848 """
849 # Get list name
850 list_name = raw_input(light_magenta('Give me the list\'s name: '))
851 # Get list name and owner
852 try:
853 owner, slug = list_name.split('/')
854 if slug.startswith('@'):
855 slug = slug[1:]
856 return owner, slug
857 except:
858 printNicely(
859 light_magenta('List name should follow "@owner/list_name" format.'))
860 raise Exception('Wrong list name')
861
862
863 def show_lists(t):
864 """
865 List list
866 """
867 rel = t.lists.list(screen_name=g['original_name'])
868 if rel:
869 print_list(rel)
870 else:
871 printNicely(light_magenta('You belong to no lists :)'))
872
873
874 def list_home(t):
875 """
876 List home
877 """
878 owner, slug = get_slug()
879 res = t.lists.statuses(
880 slug=slug,
881 owner_screen_name=owner,
882 count=c['LIST_MAX'],
883 include_entities=False)
884 for tweet in res:
885 draw(t=tweet)
886 printNicely('')
887
888
889 def list_members(t):
890 """
891 List members
892 """
893 owner, slug = get_slug()
894 # Get members
895 rel = {}
896 next_cursor = -1
897 while next_cursor != 0:
898 m = t.lists.members(
899 slug=slug,
900 owner_screen_name=owner,
901 cursor=next_cursor,
902 include_entities=False)
903 for u in m['users']:
904 rel[u['name']] = '@' + u['screen_name']
905 next_cursor = m['next_cursor']
906 printNicely('All: ' + str(len(rel)) + ' members.')
907 for name in rel:
908 user = ' ' + cycle_color(name)
909 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
910 printNicely(user)
911
912
913 def list_subscribers(t):
914 """
915 List subscribers
916 """
917 owner, slug = get_slug()
918 # Get subscribers
919 rel = {}
920 next_cursor = -1
921 while next_cursor != 0:
922 m = t.lists.subscribers(
923 slug=slug,
924 owner_screen_name=owner,
925 cursor=next_cursor,
926 include_entities=False)
927 for u in m['users']:
928 rel[u['name']] = '@' + u['screen_name']
929 next_cursor = m['next_cursor']
930 printNicely('All: ' + str(len(rel)) + ' subscribers.')
931 for name in rel:
932 user = ' ' + cycle_color(name)
933 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
934 printNicely(user)
935
936
937 def list_add(t):
938 """
939 Add specific user to a list
940 """
941 owner, slug = get_slug()
942 # Add
943 user_name = raw_input(light_magenta('Give me name of the newbie: '))
944 if user_name.startswith('@'):
945 user_name = user_name[1:]
946 try:
947 t.lists.members.create(
948 slug=slug,
949 owner_screen_name=owner,
950 screen_name=user_name)
951 printNicely(green('Added.'))
952 except:
953 debug_option()
954 printNicely(light_magenta('I\'m sorry we can not add him/her.'))
955
956
957 def list_remove(t):
958 """
959 Remove specific user from a list
960 """
961 owner, slug = get_slug()
962 # Remove
963 user_name = raw_input(light_magenta('Give me name of the unlucky one: '))
964 if user_name.startswith('@'):
965 user_name = user_name[1:]
966 try:
967 t.lists.members.destroy(
968 slug=slug,
969 owner_screen_name=owner,
970 screen_name=user_name)
971 printNicely(green('Gone.'))
972 except:
973 debug_option()
974 printNicely(light_magenta('I\'m sorry we can not remove him/her.'))
975
976
977 def list_subscribe(t):
978 """
979 Subscribe to a list
980 """
981 owner, slug = get_slug()
982 # Subscribe
983 try:
984 t.lists.subscribers.create(
985 slug=slug,
986 owner_screen_name=owner)
987 printNicely(green('Done.'))
988 except:
989 debug_option()
990 printNicely(
991 light_magenta('I\'m sorry you can not subscribe to this list.'))
992
993
994 def list_unsubscribe(t):
995 """
996 Unsubscribe a list
997 """
998 owner, slug = get_slug()
999 # Subscribe
1000 try:
1001 t.lists.subscribers.destroy(
1002 slug=slug,
1003 owner_screen_name=owner)
1004 printNicely(green('Done.'))
1005 except:
1006 debug_option()
1007 printNicely(
1008 light_magenta('I\'m sorry you can not unsubscribe to this list.'))
1009
1010
1011 def list_own(t):
1012 """
1013 List own
1014 """
1015 rel = []
1016 next_cursor = -1
1017 while next_cursor != 0:
1018 res = t.lists.ownerships(
1019 screen_name=g['original_name'],
1020 cursor=next_cursor)
1021 rel += res['lists']
1022 next_cursor = res['next_cursor']
1023 if rel:
1024 print_list(rel)
1025 else:
1026 printNicely(light_magenta('You own no lists :)'))
1027
1028
1029 def list_new(t):
1030 """
1031 Create a new list
1032 """
1033 name = raw_input(light_magenta('New list\'s name: '))
1034 mode = raw_input(light_magenta('New list\'s mode (public/private): '))
1035 description = raw_input(light_magenta('New list\'s description: '))
1036 try:
1037 t.lists.create(
1038 name=name,
1039 mode=mode,
1040 description=description)
1041 printNicely(green(name + ' list is created.'))
1042 except:
1043 debug_option()
1044 printNicely(red('Oops something is wrong with Twitter :('))
1045
1046
1047 def list_update(t):
1048 """
1049 Update a list
1050 """
1051 slug = raw_input(light_magenta('Your list that you want to update: '))
1052 name = raw_input(light_magenta('Update name (leave blank to unchange): '))
1053 mode = raw_input(light_magenta('Update mode (public/private): '))
1054 description = raw_input(light_magenta('Update description: '))
1055 try:
1056 if name:
1057 t.lists.update(
1058 slug='-'.join(slug.split()),
1059 owner_screen_name=g['original_name'],
1060 name=name,
1061 mode=mode,
1062 description=description)
1063 else:
1064 t.lists.update(
1065 slug=slug,
1066 owner_screen_name=g['original_name'],
1067 mode=mode,
1068 description=description)
1069 printNicely(green(slug + ' list is updated.'))
1070 except:
1071 debug_option()
1072 printNicely(red('Oops something is wrong with Twitter :('))
1073
1074
1075 def list_delete(t):
1076 """
1077 Delete a list
1078 """
1079 slug = raw_input(light_magenta('Your list that you want to delete: '))
1080 try:
1081 t.lists.destroy(
1082 slug='-'.join(slug.split()),
1083 owner_screen_name=g['original_name'])
1084 printNicely(green(slug + ' list is deleted.'))
1085 except:
1086 debug_option()
1087 printNicely(red('Oops something is wrong with Twitter :('))
1088
1089
1090 def twitterlist():
1091 """
1092 Twitter's list
1093 """
1094 t = Twitter(auth=authen())
1095 # List all lists or base on action
1096 try:
1097 g['list_action'] = g['stuff'].split()[0]
1098 except:
1099 show_lists(t)
1100 return
1101 # Sub-function
1102 action_ary = {
1103 'home': list_home,
1104 'all_mem': list_members,
1105 'all_sub': list_subscribers,
1106 'add': list_add,
1107 'rm': list_remove,
1108 'sub': list_subscribe,
1109 'unsub': list_unsubscribe,
1110 'own': list_own,
1111 'new': list_new,
1112 'update': list_update,
1113 'del': list_delete,
1114 }
1115 try:
1116 return action_ary[g['list_action']](t)
1117 except:
1118 printNicely(red('Please try again.'))
1119
1120
1121 def switch():
1122 """
1123 Switch stream
1124 """
1125 try:
1126 target = g['stuff'].split()[0]
1127 # Filter and ignore
1128 args = parse_arguments()
1129 try:
1130 if g['stuff'].split()[-1] == '-f':
1131 guide = 'To ignore an option, just hit Enter key.'
1132 printNicely(light_magenta(guide))
1133 only = raw_input('Only nicks [Ex: @xxx,@yy]: ')
1134 ignore = raw_input('Ignore nicks [Ex: @xxx,@yy]: ')
1135 args.filter = filter(None, only.split(','))
1136 args.ignore = filter(None, ignore.split(','))
1137 elif g['stuff'].split()[-1] == '-d':
1138 args.filter = c['ONLY_LIST']
1139 args.ignore = c['IGNORE_LIST']
1140 except:
1141 printNicely(red('Sorry, wrong format.'))
1142 return
1143 # Public stream
1144 if target == 'public':
1145 keyword = g['stuff'].split()[1]
1146 if keyword[0] == '#':
1147 keyword = keyword[1:]
1148 # Kill old thread
1149 g['stream_stop'] = True
1150 args.track_keywords = keyword
1151 # Start new thread
1152 th = threading.Thread(
1153 target=stream,
1154 args=(
1155 c['PUBLIC_DOMAIN'],
1156 args))
1157 th.daemon = True
1158 th.start()
1159 # Personal stream
1160 elif target == 'mine':
1161 # Kill old thread
1162 g['stream_stop'] = True
1163 # Start new thread
1164 th = threading.Thread(
1165 target=stream,
1166 args=(
1167 c['USER_DOMAIN'],
1168 args,
1169 g['original_name']))
1170 th.daemon = True
1171 th.start()
1172 printNicely('')
1173 if args.filter:
1174 printNicely(cyan('Only: ' + str(args.filter)))
1175 if args.ignore:
1176 printNicely(red('Ignore: ' + str(args.ignore)))
1177 printNicely('')
1178 except:
1179 printNicely(red('Sorry I can\'t understand.'))
1180
1181
1182 def cal():
1183 """
1184 Unix's command `cal`
1185 """
1186 # Format
1187 rel = os.popen('cal').read().split('\n')
1188 month = rel.pop(0)
1189 date = rel.pop(0)
1190 show_calendar(month, date, rel)
1191
1192
1193 def theme():
1194 """
1195 List and change theme
1196 """
1197 if not g['stuff']:
1198 # List themes
1199 for theme in g['themes']:
1200 line = light_magenta(theme)
1201 if c['THEME'] == theme:
1202 line = ' ' * 2 + light_yellow('* ') + line
1203 else:
1204 line = ' ' * 4 + line
1205 printNicely(line)
1206 else:
1207 # Change theme
1208 try:
1209 # Load new theme
1210 c['THEME'] = reload_theme(g['stuff'], c['THEME'])
1211 # Redefine decorated_name
1212 g['decorated_name'] = lambda x: color_func(
1213 c['DECORATED_NAME'])(
1214 '[' + x + ']: ')
1215 printNicely(green('Theme changed.'))
1216 except:
1217 printNicely(red('No such theme exists.'))
1218
1219
1220 def config():
1221 """
1222 Browse and change config
1223 """
1224 all_config = get_all_config()
1225 g['stuff'] = g['stuff'].strip()
1226 # List all config
1227 if not g['stuff']:
1228 for k in all_config:
1229 line = ' ' * 2 + \
1230 green(k) + ': ' + light_yellow(str(all_config[k]))
1231 printNicely(line)
1232 guide = 'Detailed explanation can be found at ' + \
1233 color_func(c['TWEET']['link'])(
1234 'http://rainbowstream.readthedocs.org/en/latest/#config-explanation')
1235 printNicely(guide)
1236 # Print specific config
1237 elif len(g['stuff'].split()) == 1:
1238 if g['stuff'] in all_config:
1239 k = g['stuff']
1240 line = ' ' * 2 + \
1241 green(k) + ': ' + light_yellow(str(all_config[k]))
1242 printNicely(line)
1243 else:
1244 printNicely(red('No such config key.'))
1245 # Print specific config's default value
1246 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'default':
1247 key = g['stuff'].split()[0]
1248 try:
1249 value = get_default_config(key)
1250 line = ' ' * 2 + green(key) + ': ' + light_magenta(value)
1251 printNicely(line)
1252 except Exception as e:
1253 printNicely(red(e))
1254 # Delete specific config key in config file
1255 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'drop':
1256 key = g['stuff'].split()[0]
1257 try:
1258 delete_config(key)
1259 printNicely(green('Config key is dropped.'))
1260 except Exception as e:
1261 printNicely(red(e))
1262 # Set specific config
1263 elif len(g['stuff'].split()) == 3 and g['stuff'].split()[1] == '=':
1264 key = g['stuff'].split()[0]
1265 value = g['stuff'].split()[-1]
1266 if key == 'THEME' and not validate_theme(value):
1267 printNicely(red('Invalid theme\'s value.'))
1268 return
1269 try:
1270 set_config(key, value)
1271 # Apply theme immediately
1272 if key == 'THEME':
1273 c['THEME'] = reload_theme(value, c['THEME'])
1274 g['decorated_name'] = lambda x: color_func(
1275 c['DECORATED_NAME'])('[' + x + ']: ')
1276 reload_config()
1277 printNicely(green('Updated successfully.'))
1278 except Exception as e:
1279 printNicely(red(e))
1280 else:
1281 printNicely(light_magenta('Sorry I can\'s understand.'))
1282
1283
1284 def help_discover():
1285 """
1286 Discover the world
1287 """
1288 s = ' ' * 2
1289 # Discover the world
1290 usage = '\n'
1291 usage += s + grey(u'\u266A' + ' Discover the world \n')
1292 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
1293 'You can try ' + light_green('trend US') + ' or ' + \
1294 light_green('trend JP Tokyo') + '.\n'
1295 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
1296 light_green('home 7') + ' will show 7 tweets.\n'
1297 usage += s * 2 + \
1298 light_green('notification') + ' will show your recent notification.\n'
1299 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
1300 light_green('mentions 7') + ' will show 7 mention tweets.\n'
1301 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
1302 magenta('@mdo') + '.\n'
1303 usage += s * 2 + light_green('view @mdo') + \
1304 ' will show ' + magenta('@mdo') + '\'s home.\n'
1305 usage += s * 2 + light_green('s AKB48') + ' will search for "' + \
1306 light_yellow('AKB48') + '" and return 5 newest tweet. ' + \
1307 'Search can be performed with or without hashtag.\n'
1308 printNicely(usage)
1309
1310
1311 def help_tweets():
1312 """
1313 Tweets
1314 """
1315 s = ' ' * 2
1316 # Tweet
1317 usage = '\n'
1318 usage += s + grey(u'\u266A' + ' Tweets \n')
1319 usage += s * 2 + light_green('t oops ') + \
1320 'will tweet "' + light_yellow('oops') + '" immediately.\n'
1321 usage += s * 2 + \
1322 light_green('rt 12 ') + ' will retweet to tweet with ' + \
1323 light_yellow('[id=12]') + '.\n'
1324 usage += s * 2 + \
1325 light_green('quote 12 ') + ' will quote the tweet with ' + \
1326 light_yellow('[id=12]') + '. If no extra text is added, ' + \
1327 'the quote will be canceled.\n'
1328 usage += s * 2 + \
1329 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
1330 light_yellow('[id=12]') + '.\n'
1331 usage += s * 2 + light_green('conversation 12') + ' will show the chain of ' + \
1332 'replies prior to the tweet with ' + light_yellow('[id=12]') + '.\n'
1333 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
1334 light_yellow('oops') + '" to tweet with ' + \
1335 light_yellow('[id=12]') + '.\n'
1336 usage += s * 2 + \
1337 light_green('fav 12 ') + ' will favorite the tweet with ' + \
1338 light_yellow('[id=12]') + '.\n'
1339 usage += s * 2 + \
1340 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
1341 light_yellow('[id=12]') + '.\n'
1342 usage += s * 2 + \
1343 light_green('del 12 ') + ' will delete tweet with ' + \
1344 light_yellow('[id=12]') + '.\n'
1345 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
1346 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
1347 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
1348 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
1349 printNicely(usage)
1350
1351
1352 def help_messages():
1353 """
1354 Messages
1355 """
1356 s = ' ' * 2
1357 # Direct message
1358 usage = '\n'
1359 usage += s + grey(u'\u266A' + ' Direct messages \n')
1360 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
1361 light_green('inbox 7') + ' will show newest 7 messages.\n'
1362 usage += s * 2 + light_green('thread 2') + ' will show full thread with ' + \
1363 light_yellow('[thread_id=2]') + '.\n'
1364 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
1365 magenta('@dtvd88') + '.\n'
1366 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
1367 light_yellow('[message_id=5]') + '.\n'
1368 printNicely(usage)
1369
1370
1371 def help_friends_and_followers():
1372 """
1373 Friends and Followers
1374 """
1375 s = ' ' * 2
1376 # Follower and following
1377 usage = '\n'
1378 usage += s + grey(u'\u266A' + ' Friends and followers \n')
1379 usage += s * 2 + \
1380 light_green('ls fl') + \
1381 ' will list all followers (people who are following you).\n'
1382 usage += s * 2 + \
1383 light_green('ls fr') + \
1384 ' will list all friends (people who you are following).\n'
1385 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
1386 magenta('@dtvd88') + '.\n'
1387 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
1388 magenta('@dtvd88') + '.\n'
1389 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
1390 magenta('@dtvd88') + '.\n'
1391 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
1392 magenta('@dtvd88') + '.\n'
1393 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
1394 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
1395 magenta('@dtvd88') + '.\n'
1396 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
1397 magenta('@dtvd88') + '.\n'
1398 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
1399 magenta('@dtvd88') + ' as a spam account.\n'
1400 printNicely(usage)
1401
1402
1403 def help_list():
1404 """
1405 Lists
1406 """
1407 s = ' ' * 2
1408 # Twitter list
1409 usage = '\n'
1410 usage += s + grey(u'\u266A' + ' Twitter list\n')
1411 usage += s * 2 + light_green('list') + \
1412 ' will show all lists you are belong to.\n'
1413 usage += s * 2 + light_green('list home') + \
1414 ' will show timeline of list. You will be asked for list\'s name.\n'
1415 usage += s * 2 + light_green('list all_mem') + \
1416 ' will show list\'s all members.\n'
1417 usage += s * 2 + light_green('list all_sub') + \
1418 ' will show list\'s all subscribers.\n'
1419 usage += s * 2 + light_green('list add') + \
1420 ' will add specific person to a list owned by you.' + \
1421 ' You will be asked for list\'s name and person\'s name.\n'
1422 usage += s * 2 + light_green('list rm') + \
1423 ' will remove specific person from a list owned by you.' + \
1424 ' You will be asked for list\'s name and person\'s name.\n'
1425 usage += s * 2 + light_green('list sub') + \
1426 ' will subscribe you to a specific list.\n'
1427 usage += s * 2 + light_green('list unsub') + \
1428 ' will unsubscribe you from a specific list.\n'
1429 usage += s * 2 + light_green('list own') + \
1430 ' will show all list owned by you.\n'
1431 usage += s * 2 + light_green('list new') + \
1432 ' will create a new list.\n'
1433 usage += s * 2 + light_green('list update') + \
1434 ' will update a list owned by you.\n'
1435 usage += s * 2 + light_green('list del') + \
1436 ' will delete a list owned by you.\n'
1437 printNicely(usage)
1438
1439
1440 def help_stream():
1441 """
1442 Stream switch
1443 """
1444 s = ' ' * 2
1445 # Switch
1446 usage = '\n'
1447 usage += s + grey(u'\u266A' + ' Switching streams \n')
1448 usage += s * 2 + light_green('switch public #AKB') + \
1449 ' will switch to public stream and follow "' + \
1450 light_yellow('AKB') + '" keyword.\n'
1451 usage += s * 2 + light_green('switch mine') + \
1452 ' will switch to your personal stream.\n'
1453 usage += s * 2 + light_green('switch mine -f ') + \
1454 ' will prompt to enter the filter.\n'
1455 usage += s * 3 + light_yellow('Only nicks') + \
1456 ' filter will decide nicks will be INCLUDE ONLY.\n'
1457 usage += s * 3 + light_yellow('Ignore nicks') + \
1458 ' filter will decide nicks will be EXCLUDE.\n'
1459 usage += s * 2 + light_green('switch mine -d') + \
1460 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
1461 printNicely(usage)
1462
1463
1464 def help():
1465 """
1466 Help
1467 """
1468 s = ' ' * 2
1469 h, w = os.popen('stty size', 'r').read().split()
1470 # Start
1471 usage = '\n'
1472 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
1473 usage += s + '-' * (int(w) - 4) + '\n'
1474 usage += s + 'You are ' + \
1475 light_yellow('already') + ' on your personal stream.\n'
1476 usage += s + 'Any update from Twitter will show up ' + \
1477 light_yellow('immediately') + '.\n'
1478 usage += s + 'In addition, following commands are available right now:\n'
1479 # Twitter help section
1480 usage += '\n'
1481 usage += s + grey(u'\u266A' + ' Twitter help\n')
1482 usage += s * 2 + light_green('h discover') + \
1483 ' will show help for discover commands.\n'
1484 usage += s * 2 + light_green('h tweets') + \
1485 ' will show help for tweets commands.\n'
1486 usage += s * 2 + light_green('h messages') + \
1487 ' will show help for messages commands.\n'
1488 usage += s * 2 + light_green('h friends_and_followers') + \
1489 ' will show help for friends and followers commands.\n'
1490 usage += s * 2 + light_green('h list') + \
1491 ' will show help for list commands.\n'
1492 usage += s * 2 + light_green('h stream') + \
1493 ' will show help for stream commands.\n'
1494 # Smart shell
1495 usage += '\n'
1496 usage += s + grey(u'\u266A' + ' Smart shell\n')
1497 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1498 'will be evaluate by Python interpreter.\n'
1499 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1500 ' for current month.\n'
1501 # Config
1502 usage += '\n'
1503 usage += s + grey(u'\u266A' + ' Config \n')
1504 usage += s * 2 + light_green('theme') + ' will list available theme. ' + \
1505 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
1506 ' theme immediately.\n'
1507 usage += s * 2 + light_green('config') + ' will list all config.\n'
1508 usage += s * 3 + \
1509 light_green('config ASCII_ART') + ' will output current value of ' +\
1510 light_yellow('ASCII_ART') + ' config key.\n'
1511 usage += s * 3 + \
1512 light_green('config TREND_MAX default') + ' will output default value of ' + \
1513 light_yellow('TREND_MAX') + ' config key.\n'
1514 usage += s * 3 + \
1515 light_green('config CUSTOM_CONFIG drop') + ' will drop ' + \
1516 light_yellow('CUSTOM_CONFIG') + ' config key.\n'
1517 usage += s * 3 + \
1518 light_green('config IMAGE_ON_TERM = true') + ' will set value of ' + \
1519 light_yellow('IMAGE_ON_TERM') + ' config key to ' + \
1520 light_yellow('True') + '.\n'
1521 # Screening
1522 usage += '\n'
1523 usage += s + grey(u'\u266A' + ' Screening \n')
1524 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1525 usage += s * 2 + light_green('p') + ' will pause the stream.\n'
1526 usage += s * 2 + light_green('r') + ' will unpause the stream.\n'
1527 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1528 usage += s * 2 + light_green('q') + ' will quit.\n'
1529 # End
1530 usage += '\n'
1531 usage += s + '-' * (int(w) - 4) + '\n'
1532 usage += s + 'Have fun and hang tight! \n'
1533 # Show help
1534 d = {
1535 'discover': help_discover,
1536 'tweets': help_tweets,
1537 'messages': help_messages,
1538 'friends_and_followers': help_friends_and_followers,
1539 'list': help_list,
1540 'stream': help_stream,
1541 }
1542 if g['stuff']:
1543 d.get(
1544 g['stuff'].strip(),
1545 lambda: printNicely(red('No such command.'))
1546 )()
1547 else:
1548 printNicely(usage)
1549
1550
1551 def pause():
1552 """
1553 Pause stream display
1554 """
1555 g['pause'] = True
1556 printNicely(green('Stream is paused'))
1557
1558
1559 def replay():
1560 """
1561 Replay stream
1562 """
1563 g['pause'] = False
1564 printNicely(green('Stream is running back now'))
1565
1566
1567 def clear():
1568 """
1569 Clear screen
1570 """
1571 os.system('clear')
1572
1573
1574 def quit():
1575 """
1576 Exit all
1577 """
1578 try:
1579 save_history()
1580 printNicely(green('See you next time :)'))
1581 except:
1582 pass
1583 sys.exit()
1584
1585
1586 def reset():
1587 """
1588 Reset prefix of line
1589 """
1590 if g['reset']:
1591 if c.get('USER_JSON_ERROR'):
1592 printNicely(red('Your ~/.rainbow_config.json is messed up:'))
1593 printNicely(red('>>> ' + c['USER_JSON_ERROR']))
1594 printNicely('')
1595 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
1596 g['reset'] = False
1597 try:
1598 printNicely(str(eval(g['cmd'])))
1599 except Exception:
1600 pass
1601
1602
1603 # Command set
1604 cmdset = [
1605 'switch',
1606 'trend',
1607 'home',
1608 'notification',
1609 'view',
1610 'mentions',
1611 't',
1612 'rt',
1613 'quote',
1614 'allrt',
1615 'conversation',
1616 'fav',
1617 'rep',
1618 'del',
1619 'ufav',
1620 's',
1621 'mes',
1622 'show',
1623 'open',
1624 'ls',
1625 'inbox',
1626 'thread',
1627 'trash',
1628 'whois',
1629 'fl',
1630 'ufl',
1631 'mute',
1632 'unmute',
1633 'muting',
1634 'block',
1635 'unblock',
1636 'report',
1637 'list',
1638 'cal',
1639 'config',
1640 'theme',
1641 'h',
1642 'p',
1643 'r',
1644 'c',
1645 'q'
1646 ]
1647
1648 # Handle function set
1649 funcset = [
1650 switch,
1651 trend,
1652 home,
1653 notification,
1654 view,
1655 mentions,
1656 tweet,
1657 retweet,
1658 quote,
1659 allretweet,
1660 conversation,
1661 favorite,
1662 reply,
1663 delete,
1664 unfavorite,
1665 search,
1666 message,
1667 show,
1668 urlopen,
1669 ls,
1670 inbox,
1671 thread,
1672 trash,
1673 whois,
1674 follow,
1675 unfollow,
1676 mute,
1677 unmute,
1678 muting,
1679 block,
1680 unblock,
1681 report,
1682 twitterlist,
1683 cal,
1684 config,
1685 theme,
1686 help,
1687 pause,
1688 replay,
1689 clear,
1690 quit
1691 ]
1692
1693
1694 def process(cmd):
1695 """
1696 Process switch
1697 """
1698 return dict(zip(cmdset, funcset)).get(cmd, reset)
1699
1700
1701 def listen():
1702 """
1703 Listen to user's input
1704 """
1705 d = dict(zip(
1706 cmdset,
1707 [
1708 ['public', 'mine'], # switch
1709 [], # trend
1710 [], # home
1711 [], # notification
1712 ['@'], # view
1713 [], # mentions
1714 [], # tweet
1715 [], # retweet
1716 [], # quote
1717 [], # allretweet
1718 [], # conversation
1719 [], # favorite
1720 [], # reply
1721 [], # delete
1722 [], # unfavorite
1723 ['#'], # search
1724 ['@'], # message
1725 ['image'], # show image
1726 [''], # open url
1727 ['fl', 'fr'], # list
1728 [], # inbox
1729 [i for i in g['message_threads']], # sent
1730 [], # trash
1731 ['@'], # whois
1732 ['@'], # follow
1733 ['@'], # unfollow
1734 ['@'], # mute
1735 ['@'], # unmute
1736 ['@'], # muting
1737 ['@'], # block
1738 ['@'], # unblock
1739 ['@'], # report
1740 [
1741 'home',
1742 'all_mem',
1743 'all_sub',
1744 'add',
1745 'rm',
1746 'sub',
1747 'unsub',
1748 'own',
1749 'new',
1750 'update',
1751 'del'
1752 ], # list
1753 [], # cal
1754 [key for key in dict(get_all_config())], # config
1755 g['themes'], # theme
1756 [
1757 'discover',
1758 'tweets',
1759 'messages',
1760 'friends_and_followers',
1761 'list',
1762 'stream'
1763 ], # help
1764 [], # pause
1765 [], # reconnect
1766 [], # clear
1767 [], # quit
1768 ]
1769 ))
1770 init_interactive_shell(d)
1771 read_history()
1772 reset()
1773 while True:
1774 try:
1775 # raw_input
1776 if g['prefix']:
1777 # Only use PREFIX as a string with raw_input
1778 line = raw_input(g['decorated_name'](g['PREFIX']))
1779 else:
1780 line = raw_input()
1781 # Save cmd to compare with readline buffer
1782 g['cmd'] = line.strip()
1783 # Get short cmd to pass to handle function
1784 try:
1785 cmd = line.split()[0]
1786 except:
1787 cmd = ''
1788 # Lock the semaphore
1789 c['lock'] = True
1790 # Save cmd to global variable and call process
1791 g['stuff'] = ' '.join(line.split()[1:])
1792 # Process the command
1793 process(cmd)()
1794 # Not re-display
1795 if cmd in ['switch', 't', 'rt', 'rep']:
1796 g['prefix'] = False
1797 else:
1798 g['prefix'] = True
1799 # Release the semaphore lock
1800 c['lock'] = False
1801 except EOFError:
1802 printNicely('')
1803 except Exception:
1804 debug_option()
1805 printNicely(red('OMG something is wrong with Twitter right now.'))
1806
1807
1808 def stream(domain, args, name='Rainbow Stream'):
1809 """
1810 Track the stream
1811 """
1812 # The Logo
1813 art_dict = {
1814 c['USER_DOMAIN']: name,
1815 c['PUBLIC_DOMAIN']: args.track_keywords,
1816 c['SITE_DOMAIN']: name,
1817 }
1818 if c['ASCII_ART']:
1819 ascii_art(art_dict[domain])
1820 # These arguments are optional:
1821 stream_args = dict(
1822 timeout=0.5, # To check g['stream_stop'] after each 0.5 s
1823 block=True,
1824 heartbeat_timeout=c['HEARTBEAT_TIMEOUT'] * 60)
1825 # Track keyword
1826 query_args = dict()
1827 if args.track_keywords:
1828 query_args['track'] = args.track_keywords
1829 # Get stream
1830 stream = TwitterStream(
1831 auth=authen(),
1832 domain=domain,
1833 **stream_args)
1834 try:
1835 if domain == c['USER_DOMAIN']:
1836 tweet_iter = stream.user(**query_args)
1837 elif domain == c['SITE_DOMAIN']:
1838 tweet_iter = stream.site(**query_args)
1839 else:
1840 if args.track_keywords:
1841 tweet_iter = stream.statuses.filter(**query_args)
1842 else:
1843 tweet_iter = stream.statuses.sample()
1844 # Block new stream until other one exits
1845 StreamLock.acquire()
1846 g['stream_stop'] = False
1847 for tweet in tweet_iter:
1848 if tweet is None:
1849 printNicely("-- None --")
1850 elif tweet is Timeout:
1851 if(g['stream_stop']):
1852 StreamLock.release()
1853 break
1854 elif tweet is HeartbeatTimeout:
1855 printNicely("-- Heartbeat Timeout --")
1856 guide = light_magenta("You can use ") + \
1857 light_green("switch") + \
1858 light_magenta(" command to return to your stream.\n")
1859 guide += light_magenta("Type ") + \
1860 light_green("h stream") + \
1861 light_magenta(" for more details.")
1862 printNicely(guide)
1863 sys.stdout.write(g['decorated_name'](c['PREFIX']))
1864 sys.stdout.flush()
1865 StreamLock.release()
1866 break
1867 elif tweet is Hangup:
1868 printNicely("-- Hangup --")
1869 elif tweet.get('text'):
1870 # Check the semaphore pause and lock (stream process only)
1871 if g['pause']:
1872 continue
1873 while c['lock']:
1874 time.sleep(0.5)
1875 # Draw the tweet
1876 draw(
1877 t=tweet,
1878 keyword=args.track_keywords,
1879 humanize=False,
1880 fil=args.filter,
1881 ig=args.ignore,
1882 )
1883 # Current readline buffer
1884 current_buffer = readline.get_line_buffer().strip()
1885 # There is an unexpected behaviour in MacOSX readline + Python 2:
1886 # after completely delete a word after typing it,
1887 # somehow readline buffer still contains
1888 # the 1st character of that word
1889 if current_buffer and g['cmd'] != current_buffer:
1890 sys.stdout.write(
1891 g['decorated_name'](c['PREFIX']) + str2u(current_buffer))
1892 sys.stdout.flush()
1893 elif not c['HIDE_PROMPT']:
1894 sys.stdout.write(g['decorated_name'](c['PREFIX']))
1895 sys.stdout.flush()
1896 elif tweet.get('direct_message'):
1897 # Check the semaphore pause and lock (stream process only)
1898 if g['pause']:
1899 continue
1900 while c['lock']:
1901 time.sleep(0.5)
1902 print_message(tweet['direct_message'])
1903 elif tweet.get('event'):
1904 g['events'].append(tweet)
1905 print_event(tweet)
1906 except TwitterHTTPError:
1907 printNicely('')
1908 printNicely(
1909 magenta("We have maximum connection problem with twitter'stream API right now :("))
1910
1911
1912 def fly():
1913 """
1914 Main function
1915 """
1916 # Initial
1917 args = parse_arguments()
1918 try:
1919 init(args)
1920 except TwitterHTTPError:
1921 printNicely('')
1922 printNicely(
1923 magenta("We have connection problem with twitter'stream API right now :("))
1924 printNicely(magenta("Let's try again later."))
1925 save_history()
1926 sys.exit()
1927 # Spawn stream thread
1928 th = threading.Thread(
1929 target=stream,
1930 args=(
1931 c['USER_DOMAIN'],
1932 args,
1933 g['original_name']))
1934 th.daemon = True
1935 th.start()
1936 # Start listen process
1937 time.sleep(0.5)
1938 g['reset'] = True
1939 g['prefix'] = True
1940 listen()