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