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