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