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