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