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