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