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