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