open url and quote
[rainbowstream.git] / rainbowstream / rainbow.py
CommitLineData
91476ec3
O
1"""
2Colorful user's timeline stream
3"""
78b81730 4from multiprocessing import Process
78b81730 5
b2b933a9 6import os
7import os.path
8import sys
9import signal
10import argparse
11import time
991c30af 12import requests
80b70d60 13import webbrowser
91476ec3 14
91476ec3 15from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup
54277114 16from twitter.api import *
91476ec3 17from twitter.oauth import OAuth, read_token_file
8c840a83 18from twitter.oauth_dance import oauth_dance
91476ec3 19from twitter.util import printNicely
991c30af 20from StringIO import StringIO
91476ec3 21
7500d90b 22from .draw import *
2a6238f5
O
23from .colors import *
24from .config import *
777c52d4 25from .consumer import *
94a5f62e 26from .interactive import *
18cab06a 27from .db import *
991c30af 28from .c_image import *
2a6238f5 29
f405a7d0 30g = {}
18cab06a 31db = RainbowDB()
94a5f62e 32cmdset = [
42fde775 33 'switch',
4592d231 34 'trend',
94a5f62e 35 'home',
36 'view',
305ce127 37 'mentions',
94a5f62e 38 't',
39 'rt',
80b70d60 40 'quote',
1f24a05a 41 'allrt',
7e4ccbf3 42 'fav',
94a5f62e 43 'rep',
44 'del',
7e4ccbf3 45 'ufav',
94a5f62e 46 's',
305ce127 47 'mes',
f5677fb1 48 'show',
80b70d60 49 'open',
0f6e4daf 50 'ls',
305ce127 51 'inbox',
52 'sent',
53 'trash',
e2b81717 54 'whois',
94a5f62e 55 'fl',
f5677fb1 56 'ufl',
5b2c4faf 57 'mute',
58 'unmute',
59 'muting',
305ce127 60 'block',
61 'unblock',
62 'report',
813a5d80 63 'cal',
632c6fa5 64 'theme',
94a5f62e 65 'h',
66 'c',
67 'q'
68]
22be990e 69
c075e6dc 70
91476ec3
O
71def parse_arguments():
72 """
73 Parse the arguments
74 """
91476ec3 75 parser = argparse.ArgumentParser(description=__doc__ or "")
2a6238f5
O
76 parser.add_argument(
77 '-to',
78 '--timeout',
79 help='Timeout for the stream (seconds).')
80 parser.add_argument(
81 '-ht',
82 '--heartbeat-timeout',
83 help='Set heartbeat timeout.',
84 default=90)
85 parser.add_argument(
86 '-nb',
87 '--no-block',
88 action='store_true',
89 help='Set stream to non-blocking.')
90 parser.add_argument(
91 '-tt',
92 '--track-keywords',
93 help='Search the stream for specific text.')
d51b4107
O
94 parser.add_argument(
95 '-fil',
96 '--filter',
97 help='Filter specific screen_name.')
98 parser.add_argument(
99 '-ig',
100 '--ignore',
101 help='Ignore specific screen_name.')
88af38d8 102 parser.add_argument(
c1fa7c94
O
103 '-iot',
104 '--image-on-term',
105 action='store_true',
106 help='Display all image on terminal.')
91476ec3
O
107 return parser.parse_args()
108
109
54277114
O
110def authen():
111 """
7b674cef 112 Authenticate with Twitter OAuth
54277114 113 """
8c840a83 114 # When using rainbow stream you must authorize.
2a6238f5
O
115 twitter_credential = os.environ.get(
116 'HOME',
117 os.environ.get(
118 'USERPROFILE',
119 '')) + os.sep + '.rainbow_oauth'
8c840a83
O
120 if not os.path.exists(twitter_credential):
121 oauth_dance("Rainbow Stream",
122 CONSUMER_KEY,
123 CONSUMER_SECRET,
124 twitter_credential)
125 oauth_token, oauth_token_secret = read_token_file(twitter_credential)
54277114 126 return OAuth(
2a6238f5
O
127 oauth_token,
128 oauth_token_secret,
129 CONSUMER_KEY,
130 CONSUMER_SECRET)
91476ec3 131
54277114
O
132
133def get_decorated_name():
134 """
135 Beginning of every line
136 """
137 t = Twitter(auth=authen())
c5ff542b 138 name = '@' + t.account.verify_credentials()['screen_name']
42fde775 139 g['original_name'] = name[1:]
c075e6dc
O
140 g['decorated_name'] = color_func(c['DECORATED_NAME'])('[' + name + ']: ')
141 g['ascii_art'] = True
54277114 142
15f3e155 143 files = os.listdir(os.path.dirname(__file__)+'/colorset')
c075e6dc 144 themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
f75930c6 145 themes += ['custom']
632c6fa5 146 g['themes'] = themes
ddb1e615 147 db.theme_store(c['theme'])
632c6fa5 148
f405a7d0 149
42fde775 150def switch():
151 """
152 Switch stream
153 """
154 try:
155 target = g['stuff'].split()[0]
156
d51b4107
O
157 # Filter and ignore
158 args = parse_arguments()
7e4ccbf3 159 try:
d51b4107
O
160 if g['stuff'].split()[-1] == '-f':
161 only = raw_input('Only nicks: ')
162 ignore = raw_input('Ignore nicks: ')
7e4ccbf3 163 args.filter = filter(None, only.split(','))
164 args.ignore = filter(None, ignore.split(','))
d51b4107 165 elif g['stuff'].split()[-1] == '-d':
632c6fa5
O
166 args.filter = c['ONLY_LIST']
167 args.ignore = c['IGNORE_LIST']
d51b4107
O
168 except:
169 printNicely(red('Sorry, wrong format.'))
170 return
171
42fde775 172 # Public stream
173 if target == 'public':
174 keyword = g['stuff'].split()[1]
175 if keyword[0] == '#':
176 keyword = keyword[1:]
42fde775 177 # Kill old process
178 os.kill(g['stream_pid'], signal.SIGKILL)
42fde775 179 args.track_keywords = keyword
42fde775 180 # Start new process
181 p = Process(
d51b4107 182 target=stream,
42fde775 183 args=(
632c6fa5 184 c['PUBLIC_DOMAIN'],
42fde775 185 args))
186 p.start()
187 g['stream_pid'] = p.pid
188
189 # Personal stream
190 elif target == 'mine':
42fde775 191 # Kill old process
192 os.kill(g['stream_pid'], signal.SIGKILL)
42fde775 193 # Start new process
194 p = Process(
195 target=stream,
196 args=(
632c6fa5 197 c['USER_DOMAIN'],
42fde775 198 args,
199 g['original_name']))
200 p.start()
201 g['stream_pid'] = p.pid
d51b4107 202 printNicely('')
d51b4107
O
203 if args.filter:
204 printNicely(cyan('Only: ' + str(args.filter)))
205 if args.ignore:
206 printNicely(red('Ignore: ' + str(args.ignore)))
207 printNicely('')
c075e6dc 208 g['ascii_art'] = True
42fde775 209 except:
210 printNicely(red('Sorry I can\'t understand.'))
42fde775 211
212
4592d231 213def trend():
214 """
215 Trend
216 """
217 t = Twitter(auth=authen())
48a25fe8 218 # Get country and town
4592d231 219 try:
220 country = g['stuff'].split()[0]
221 except:
222 country = ''
48a25fe8 223 try:
224 town = g['stuff'].split()[1]
225 except:
226 town = ''
227
228 avail = t.trends.available()
229 # World wide
230 if not country:
231 trends = t.trends.place(_id=1)[0]['trends']
232 print_trends(trends)
233 else:
234 for location in avail:
235 # Search for country and Town
236 if town:
237 if location['countryCode'] == country \
238 and location['placeType']['name'] == 'Town' \
239 and location['name'] == town:
240 trends = t.trends.place(_id=location['woeid'])[0]['trends']
241 print_trends(trends)
242 # Search for country only
243 else:
244 if location['countryCode'] == country \
245 and location['placeType']['name'] == 'Country':
246 trends = t.trends.place(_id=location['woeid'])[0]['trends']
247 print_trends(trends)
4592d231 248
249
7b674cef 250def home():
251 """
252 Home
253 """
254 t = Twitter(auth=authen())
632c6fa5 255 num = c['HOME_TWEET_NUM']
7b674cef 256 if g['stuff'].isdigit():
305ce127 257 num = int(g['stuff'])
94a5f62e 258 for tweet in reversed(t.statuses.home_timeline(count=num)):
c1fa7c94 259 draw(t=tweet, iot=g['iot'])
94a5f62e 260 printNicely('')
7b674cef 261
262
263def view():
264 """
265 Friend view
266 """
267 t = Twitter(auth=authen())
268 user = g['stuff'].split()[0]
b8fbcb70 269 if user[0] == '@':
270 try:
94a5f62e 271 num = int(g['stuff'].split()[1])
b8fbcb70 272 except:
632c6fa5 273 num = c['HOME_TWEET_NUM']
94a5f62e 274 for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])):
c1fa7c94 275 draw(t=tweet, iot=g['iot'])
94a5f62e 276 printNicely('')
b8fbcb70 277 else:
c91f75f2 278 printNicely(red('A name should begin with a \'@\''))
7b674cef 279
280
305ce127 281def mentions():
282 """
283 Mentions timeline
284 """
285 t = Twitter(auth=authen())
632c6fa5 286 num = c['HOME_TWEET_NUM']
305ce127 287 if g['stuff'].isdigit():
288 num = int(g['stuff'])
289 for tweet in reversed(t.statuses.mentions_timeline(count=num)):
290 draw(t=tweet, iot=g['iot'])
291 printNicely('')
292
293
f405a7d0 294def tweet():
54277114 295 """
7b674cef 296 Tweet
54277114
O
297 """
298 t = Twitter(auth=authen())
f405a7d0 299 t.statuses.update(status=g['stuff'])
f405a7d0 300
b2b933a9 301
1ba4abfd
O
302def retweet():
303 """
304 ReTweet
305 """
306 t = Twitter(auth=authen())
307 try:
308 id = int(g['stuff'].split()[0])
1ba4abfd 309 except:
b8c1f42a
O
310 printNicely(red('Sorry I can\'t understand.'))
311 return
312 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
313 t.statuses.retweet(id=tid, include_entities=False, trim_user=True)
1ba4abfd
O
314
315
80b70d60
O
316def quote():
317 """
318 Quote a tweet
319 """
320 t = Twitter(auth=authen())
321 try:
322 id = int(g['stuff'].split()[0])
323 except:
324 printNicely(red('Sorry I can\'t understand.'))
325 return
326 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
327 tweet = t.statuses.show(id=tid)
328 screen_name = tweet['user']['screen_name']
329 text = tweet['text']
330 quote = '\"@' + screen_name + ': ' + text + '\"'
331 quote = quote.encode('utf8')
332 printNicely(light_magenta('Compose mode:'))
333 extra = raw_input(quote)
334 if extra:
335 t.statuses.update(status=quote+extra)
336 else:
337 printNicely(light_magenta('No text added.'))
338
339
1f24a05a 340def allretweet():
341 """
342 List all retweet
343 """
344 t = Twitter(auth=authen())
345 # Get rainbow id
346 try:
347 id = int(g['stuff'].split()[0])
348 except:
349 printNicely(red('Sorry I can\'t understand.'))
350 return
351 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
352 # Get display num if exist
353 try:
354 num = int(g['stuff'].split()[1])
355 except:
632c6fa5 356 num = c['RETWEETS_SHOW_NUM']
1f24a05a 357 # Get result and display
d8e901a4 358 rt_ary = t.statuses.retweets(id=tid, count=num)
1f24a05a 359 if not rt_ary:
360 printNicely(magenta('This tweet has no retweet.'))
361 return
362 for tweet in reversed(rt_ary):
363 draw(t=tweet, iot=g['iot'])
364 printNicely('')
365
366
7e4ccbf3 367def favorite():
368 """
369 Favorite
370 """
371 t = Twitter(auth=authen())
372 try:
373 id = int(g['stuff'].split()[0])
7e4ccbf3 374 except:
b8c1f42a
O
375 printNicely(red('Sorry I can\'t understand.'))
376 return
377 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
378 t.favorites.create(_id=tid, include_entities=False)
379 printNicely(green('Favorited.'))
380 draw(t.statuses.show(id=tid), iot=g['iot'])
381 printNicely('')
7e4ccbf3 382
383
7b674cef 384def reply():
829cc2d8 385 """
7b674cef 386 Reply
829cc2d8
O
387 """
388 t = Twitter(auth=authen())
7b674cef 389 try:
390 id = int(g['stuff'].split()[0])
7b674cef 391 except:
c91f75f2 392 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
393 return
394 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
395 user = t.statuses.show(id=tid)['user']['screen_name']
396 status = ' '.join(g['stuff'].split()[1:])
397 status = '@' + user + ' ' + status.decode('utf-8')
398 t.statuses.update(status=status, in_reply_to_status_id=tid)
7b674cef 399
400
401def delete():
402 """
403 Delete
404 """
405 t = Twitter(auth=authen())
406 try:
305ce127 407 rid = int(g['stuff'].split()[0])
7b674cef 408 except:
305ce127 409 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
410 return
411 tid = db.rainbow_to_tweet_query(rid)[0].tweet_id
412 t.statuses.destroy(id=tid)
413 printNicely(green('Okay it\'s gone.'))
829cc2d8
O
414
415
7e4ccbf3 416def unfavorite():
417 """
418 Unfavorite
419 """
420 t = Twitter(auth=authen())
421 try:
422 id = int(g['stuff'].split()[0])
7e4ccbf3 423 except:
b8c1f42a
O
424 printNicely(red('Sorry I can\'t understand.'))
425 return
426 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
427 t.favorites.destroy(_id=tid)
428 printNicely(green('Okay it\'s unfavorited.'))
429 draw(t.statuses.show(id=tid), iot=g['iot'])
430 printNicely('')
7e4ccbf3 431
432
f405a7d0
O
433def search():
434 """
7b674cef 435 Search
f405a7d0
O
436 """
437 t = Twitter(auth=authen())
b8c1f42a
O
438 if g['stuff'].startswith('#'):
439 rel = t.search.tweets(q=g['stuff'])['statuses']
5b2c4faf 440 if rel:
b8c1f42a 441 printNicely('Newest tweets:')
632c6fa5 442 for i in reversed(xrange(c['SEARCH_MAX_RECORD'])):
b8c1f42a
O
443 draw(t=rel[i],
444 iot=g['iot'],
445 keyword=g['stuff'].strip()[1:])
446 printNicely('')
94a5f62e 447 else:
b8c1f42a
O
448 printNicely(magenta('I\'m afraid there is no result'))
449 else:
450 printNicely(red('A keyword should be a hashtag (like \'#AKB48\')'))
b2b933a9 451
f405a7d0 452
305ce127 453def message():
454 """
455 Send a direct message
456 """
457 t = Twitter(auth=authen())
458 user = g['stuff'].split()[0]
b8c1f42a 459 if user[0].startswith('@'):
305ce127 460 try:
461 content = g['stuff'].split()[1]
305ce127 462 except:
463 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
464 t.direct_messages.new(
465 screen_name=user[1:],
466 text=content
467 )
468 printNicely(green('Message sent.'))
305ce127 469 else:
470 printNicely(red('A name should begin with a \'@\''))
471
472
f5677fb1 473def show():
843647ad 474 """
f5677fb1 475 Show image
843647ad
O
476 """
477 t = Twitter(auth=authen())
f5677fb1
O
478 try:
479 target = g['stuff'].split()[0]
480 if target != 'image':
481 return
482 id = int(g['stuff'].split()[1])
305ce127 483 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
f5677fb1
O
484 tweet = t.statuses.show(id=tid)
485 media = tweet['entities']['media']
486 for m in media:
487 res = requests.get(m['media_url'])
488 img = Image.open(StringIO(res.content))
489 img.show()
490 except:
491 printNicely(red('Sorry I can\'t show this image.'))
843647ad
O
492
493
80b70d60
O
494def open():
495 """
496 Open url
497 """
498 t = Twitter(auth=authen())
499 try:
500 if not g['stuff'].isdigit():
501 return
502 tid = db.rainbow_to_tweet_query(g['stuff'])[0].tweet_id
503 tweet = t.statuses.show(id=tid)
504 link_ary = [u for u in tweet['text'].split() if u.startswith('http://')]
505 if not link_ary:
506 printNicely(light_magenta('No url here @.@!'))
507 return
508 for link in link_ary:
509 webbrowser.open(link)
510 except:
511 printNicely(red('Sorry I can\'t open url in this tweet.'))
512
513
0f6e4daf 514def list():
515 """
516 List friends for followers
517 """
518 t = Twitter(auth=authen())
e2b81717
O
519 # Get name
520 try:
521 name = g['stuff'].split()[1]
b8c1f42a 522 if name.startswith('@'):
e2b81717
O
523 name = name[1:]
524 else:
525 printNicely(red('A name should begin with a \'@\''))
526 raise Exception('Invalid name')
527 except:
528 name = g['original_name']
529 # Get list followers or friends
0f6e4daf 530 try:
531 target = g['stuff'].split()[0]
0f6e4daf 532 except:
533 printNicely(red('Omg some syntax is wrong.'))
b8c1f42a
O
534 # Init cursor
535 d = {'fl': 'followers', 'fr': 'friends'}
536 next_cursor = -1
537 rel = {}
538 # Cursor loop
539 while next_cursor != 0:
540 list = getattr(t, d[target]).list(
541 screen_name=name,
542 cursor=next_cursor,
543 skip_status=True,
544 include_entities=False,
545 )
546 for u in list['users']:
547 rel[u['name']] = '@' + u['screen_name']
548 next_cursor = list['next_cursor']
549 # Print out result
550 printNicely('All: ' + str(len(rel)) + ' people.')
551 for name in rel:
552 user = ' ' + cycle_color(name) + grey(' ' + rel[name] + ' ')
553 printNicely(user)
0f6e4daf 554
555
305ce127 556def inbox():
557 """
558 Inbox direct messages
559 """
560 t = Twitter(auth=authen())
632c6fa5 561 num = c['MESSAGES_DISPLAY']
305ce127 562 rel = []
563 if g['stuff'].isdigit():
564 num = g['stuff']
565 cur_page = 1
566 # Max message per page is 20 so we have to loop
567 while num > 20:
568 rel = rel + t.direct_messages(
569 count=20,
570 page=cur_page,
571 include_entities=False,
572 skip_status=False
48a25fe8 573 )
305ce127 574 num -= 20
575 cur_page += 1
576 rel = rel + t.direct_messages(
577 count=num,
578 page=cur_page,
579 include_entities=False,
580 skip_status=False
48a25fe8 581 )
e2b81717 582 # Display
305ce127 583 printNicely('Inbox: newest ' + str(len(rel)) + ' messages.')
584 for m in reversed(rel):
585 print_message(m)
586 printNicely('')
587
e2b81717 588
305ce127 589def sent():
590 """
591 Sent direct messages
592 """
593 t = Twitter(auth=authen())
632c6fa5 594 num = c['MESSAGES_DISPLAY']
305ce127 595 rel = []
596 if g['stuff'].isdigit():
597 num = int(g['stuff'])
598 cur_page = 1
599 # Max message per page is 20 so we have to loop
600 while num > 20:
601 rel = rel + t.direct_messages.sent(
602 count=20,
603 page=cur_page,
604 include_entities=False,
605 skip_status=False
48a25fe8 606 )
305ce127 607 num -= 20
608 cur_page += 1
609 rel = rel + t.direct_messages.sent(
610 count=num,
611 page=cur_page,
612 include_entities=False,
613 skip_status=False
48a25fe8 614 )
e2b81717 615 # Display
305ce127 616 printNicely('Sent: newest ' + str(len(rel)) + ' messages.')
617 for m in reversed(rel):
618 print_message(m)
619 printNicely('')
e2b81717 620
305ce127 621
622def trash():
623 """
624 Remove message
625 """
626 t = Twitter(auth=authen())
627 try:
628 rid = int(g['stuff'].split()[0])
305ce127 629 except:
630 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
631 mid = db.rainbow_to_message_query(rid)[0].message_id
632 t.direct_messages.destroy(id=mid)
633 printNicely(green('Message deleted.'))
305ce127 634
635
e2b81717
O
636def whois():
637 """
638 Show profile of a specific user
639 """
640 t = Twitter(auth=authen())
641 screen_name = g['stuff'].split()[0]
b8c1f42a 642 if screen_name.startswith('@'):
e2b81717
O
643 try:
644 user = t.users.show(
645 screen_name=screen_name[1:],
646 include_entities=False)
7500d90b 647 show_profile(user, g['iot'])
e2b81717
O
648 except:
649 printNicely(red('Omg no user.'))
650 else:
b8c1f42a 651 printNicely(red('A name should begin with a \'@\''))
e2b81717
O
652
653
f5677fb1 654def follow():
843647ad 655 """
f5677fb1 656 Follow a user
843647ad
O
657 """
658 t = Twitter(auth=authen())
f5677fb1 659 screen_name = g['stuff'].split()[0]
b8c1f42a
O
660 if screen_name.startswith('@'):
661 t.friendships.create(screen_name=screen_name[1:], follow=True)
662 printNicely(green('You are following ' + screen_name + ' now!'))
f5677fb1 663 else:
b8c1f42a 664 printNicely(red('A name should begin with a \'@\''))
f5677fb1
O
665
666
667def unfollow():
668 """
669 Unfollow a user
670 """
671 t = Twitter(auth=authen())
672 screen_name = g['stuff'].split()[0]
b8c1f42a
O
673 if screen_name.startswith('@'):
674 t.friendships.destroy(
675 screen_name=screen_name[1:],
676 include_entities=False)
677 printNicely(green('Unfollow ' + screen_name + ' success!'))
f5677fb1 678 else:
b8c1f42a 679 printNicely(red('A name should begin with a \'@\''))
843647ad
O
680
681
5b2c4faf 682def mute():
683 """
684 Mute a user
685 """
686 t = Twitter(auth=authen())
687 try:
688 screen_name = g['stuff'].split()[0]
689 except:
690 printNicely(red('A name should be specified. '))
691 return
692 if screen_name.startswith('@'):
693 rel = t.mutes.users.create(screen_name=screen_name[1:])
694 if isinstance(rel, dict):
695 printNicely(green(screen_name + ' is muted.'))
696 else:
697 printNicely(red(rel))
698 else:
699 printNicely(red('A name should begin with a \'@\''))
700
701
702def unmute():
703 """
704 Unmute a user
705 """
706 t = Twitter(auth=authen())
707 try:
708 screen_name = g['stuff'].split()[0]
709 except:
710 printNicely(red('A name should be specified. '))
711 return
712 if screen_name.startswith('@'):
713 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
714 if isinstance(rel, dict):
715 printNicely(green(screen_name + ' is unmuted.'))
716 else:
717 printNicely(red(rel))
718 else:
719 printNicely(red('A name should begin with a \'@\''))
720
721
722def muting():
723 """
724 List muting user
725 """
726 t = Twitter(auth=authen())
727 # Init cursor
5b2c4faf 728 next_cursor = -1
729 rel = {}
730 # Cursor loop
731 while next_cursor != 0:
732 list = t.mutes.users.list(
733 screen_name=g['original_name'],
734 cursor=next_cursor,
735 skip_status=True,
736 include_entities=False,
737 )
738 for u in list['users']:
739 rel[u['name']] = '@' + u['screen_name']
740 next_cursor = list['next_cursor']
741 # Print out result
742 printNicely('All: ' + str(len(rel)) + ' people.')
743 for name in rel:
744 user = ' ' + cycle_color(name) + grey(' ' + rel[name] + ' ')
745 printNicely(user)
746
747
305ce127 748def block():
749 """
750 Block a user
751 """
752 t = Twitter(auth=authen())
753 screen_name = g['stuff'].split()[0]
b8c1f42a
O
754 if screen_name.startswith('@'):
755 t.blocks.create(
5b2c4faf 756 screen_name=screen_name[1:],
757 include_entities=False,
758 skip_status=True)
b8c1f42a 759 printNicely(green('You blocked ' + screen_name + '.'))
305ce127 760 else:
b8c1f42a 761 printNicely(red('A name should begin with a \'@\''))
305ce127 762
763
764def unblock():
765 """
766 Unblock a user
767 """
768 t = Twitter(auth=authen())
769 screen_name = g['stuff'].split()[0]
b8c1f42a
O
770 if screen_name.startswith('@'):
771 t.blocks.destroy(
772 screen_name=screen_name[1:],
773 include_entities=False,
774 skip_status=True)
775 printNicely(green('Unblock ' + screen_name + ' success!'))
305ce127 776 else:
b8c1f42a 777 printNicely(red('A name should begin with a \'@\''))
305ce127 778
779
780def report():
781 """
782 Report a user as a spam account
783 """
784 t = Twitter(auth=authen())
785 screen_name = g['stuff'].split()[0]
b8c1f42a
O
786 if screen_name.startswith('@'):
787 t.users.report_spam(
788 screen_name=screen_name[1:])
789 printNicely(green('You reported ' + screen_name + '.'))
305ce127 790 else:
791 printNicely(red('Sorry I can\'t understand.'))
792
793
813a5d80 794def cal():
795 """
796 Unix's command `cal`
797 """
798 # Format
799 rel = os.popen('cal').read().split('\n')
800 month = rel.pop(0)
813a5d80 801 date = rel.pop(0)
2a0cabee 802 show_calendar(month, date, rel)
813a5d80 803
804
632c6fa5
O
805def theme():
806 """
807 List and change theme
808 """
809 if not g['stuff']:
810 # List themes
811 for theme in g['themes']:
ddb1e615 812 line = ''
4cf86720 813 # Detect custom config
ddb1e615 814 if theme == 'custom':
4cf86720 815 line += light_magenta('custom')
77c9b04e
VNM
816 custom_path = os.environ.get(
817 'HOME',
818 os.environ.get('USERPROFILE',
819 '')) + os.sep + '.rainbow_config.json'
820 if not os.path.exists(custom_path):
9bc91c78 821 line += light_magenta(' (create your own config file at ~/.rainbow_config.json)')
4cf86720 822 else:
77c9b04e 823 line += light_magenta(' (loaded)')
4cf86720
VNM
824 else:
825 line += light_magenta(theme)
ddb1e615
VNM
826 if c['theme'] == theme :
827 line = ' '*2 + light_yellow('* ') + line
828 else:
829 line = ' '*4 + line
632c6fa5 830 printNicely(line)
1c8a5082
O
831 elif g['stuff'] == 'current_as_default':
832 path = os.path.dirname(__file__) + '/colorset/init'
833 f = open(path,'w')
834 f.write(c['theme'])
835 f.close()
836 printNicely(light_green('Okay it will be applied from next time :)'))
632c6fa5
O
837 else:
838 # Change theme
c075e6dc
O
839 try:
840 # Load new config
bb03272e 841 if g['stuff'] != 'custom':
15f3e155 842 new_config = os.path.dirname(__file__) + '/colorset/' + g['stuff'] + '.json'
bb03272e
VNM
843 else:
844 new_config = os.environ.get(
845 'HOME',os.environ.get(
846 'USERPROFILE',
847 '')) + os.sep + '.rainbow_config.json'
632c6fa5 848 new_config = load_config(new_config)
9e3418f1 849 if new_config:
a5301bc0
VNM
850 for nc in new_config:
851 c[nc] = new_config[nc]
77c9b04e 852 # Update db and reset colors
ddb1e615 853 db.theme_update(g['stuff'])
75bc416c 854 c['theme'] = g['stuff']
e43ebfa6 855 reset_cycle()
c075e6dc
O
856 g['decorated_name'] = color_func(
857 c['DECORATED_NAME'])(
858 '[@' + g['original_name'] + ']: ')
632c6fa5 859 printNicely(green('Theme changed.'))
c075e6dc 860 except:
657c4708 861 if g['stuff'] == 'custom':
75bc416c 862 printNicely(red('~/.rainbow_config.json is not exists!'))
657c4708
VNM
863 else:
864 printNicely(red('No such theme exists.'))
632c6fa5
O
865
866
f405a7d0
O
867def help():
868 """
7b674cef 869 Help
f405a7d0 870 """
7e4ccbf3 871 s = ' ' * 2
872 h, w = os.popen('stty size', 'r').read().split()
e3885f55 873
8bc30efd 874 # Start
e3885f55
O
875 usage = '\n'
876 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
7e4ccbf3 877 usage += s + '-' * (int(w) - 4) + '\n'
c075e6dc
O
878 usage += s + 'You are ' + \
879 light_yellow('already') + ' on your personal stream.\n'
5b2c4faf 880 usage += s + 'Any update from Twitter will show up ' + \
c075e6dc 881 light_yellow('immediately') + '.\n'
5b2c4faf 882 usage += s + 'In addtion, following commands are available right now:\n'
e3885f55 883
1f24a05a 884 # Discover the world
8bc30efd 885 usage += '\n'
886 usage += s + grey(u'\u266A' + ' Discover the world \n')
c075e6dc
O
887 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
888 'You can try ' + light_green('trend US') + ' or ' + \
889 light_green('trend JP Tokyo') + '.\n'
890 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
891 light_green('home 7') + ' will show 7 tweets.\n'
892 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
893 light_green('mentions 7') + ' will show 7 mention tweets.\n'
894 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
8bc30efd 895 magenta('@mdo') + '.\n'
c075e6dc 896 usage += s * 2 + light_green('view @mdo') + \
8bc30efd 897 ' will show ' + magenta('@mdo') + '\'s home.\n'
c075e6dc
O
898 usage += s * 2 + light_green('s #AKB48') + ' will search for "' + \
899 light_yellow('AKB48') + '" and return 5 newest tweet.\n'
8bc30efd 900
1f24a05a 901 # Tweet
8bc30efd 902 usage += '\n'
903 usage += s + grey(u'\u266A' + ' Tweets \n')
c075e6dc
O
904 usage += s * 2 + light_green('t oops ') + \
905 'will tweet "' + light_yellow('oops') + '" immediately.\n'
7e4ccbf3 906 usage += s * 2 + \
c075e6dc
O
907 light_green('rt 12 ') + ' will retweet to tweet with ' + \
908 light_yellow('[id=12]') + '.\n'
80b70d60
O
909 usage += s * 2 + \
910 light_green('quote 12 ') + ' will quote the tweet with ' + \
911 light_yellow('[id=12]') + '. If no extra text is added, ' + \
912 'the quote will be canceled.\n'
1f24a05a 913 usage += s * 2 + \
c075e6dc
O
914 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
915 light_yellow('[id=12]') + '.\n'
916 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
917 light_yellow('oops') + '" to tweet with ' + \
918 light_yellow('[id=12]') + '.\n'
7e4ccbf3 919 usage += s * 2 + \
c075e6dc
O
920 light_green('fav 12 ') + ' will favorite the tweet with ' + \
921 light_yellow('[id=12]') + '.\n'
7e4ccbf3 922 usage += s * 2 + \
c075e6dc
O
923 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
924 light_yellow('[id=12]') + '.\n'
8bc30efd 925 usage += s * 2 + \
c075e6dc
O
926 light_green('del 12 ') + ' will delete tweet with ' + \
927 light_yellow('[id=12]') + '.\n'
928 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
929 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
80b70d60
O
930 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
931 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
8bc30efd 932
5b2c4faf 933 # Direct message
8bc30efd 934 usage += '\n'
935 usage += s + grey(u'\u266A' + ' Direct messages \n')
c075e6dc
O
936 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
937 light_green('inbox 7') + ' will show newest 7 messages.\n'
938 usage += s * 2 + light_green('sent') + ' will show sent messages. ' + \
939 light_green('sent 7') + ' will show newest 7 messages.\n'
940 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
8bc30efd 941 magenta('@dtvd88') + '.\n'
c075e6dc
O
942 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
943 light_yellow('[message_id=5]') + '.\n'
8bc30efd 944
945 # Follower and following
946 usage += '\n'
947 usage += s + grey(u'\u266A' + ' Fiends and followers \n')
948 usage += s * 2 + \
c075e6dc 949 light_green('ls fl') + \
8bc30efd 950 ' will list all followers (people who are following you).\n'
951 usage += s * 2 + \
c075e6dc 952 light_green('ls fr') + \
8bc30efd 953 ' will list all friends (people who you are following).\n'
c075e6dc 954 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
305ce127 955 magenta('@dtvd88') + '.\n'
c075e6dc 956 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
305ce127 957 magenta('@dtvd88') + '.\n'
c075e6dc 958 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
5b2c4faf 959 magenta('@dtvd88') + '.\n'
c075e6dc 960 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
5b2c4faf 961 magenta('@dtvd88') + '.\n'
c075e6dc
O
962 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
963 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
305ce127 964 magenta('@dtvd88') + '.\n'
c075e6dc 965 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
305ce127 966 magenta('@dtvd88') + '.\n'
c075e6dc 967 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
305ce127 968 magenta('@dtvd88') + ' as a spam account.\n'
8bc30efd 969
8bc30efd 970 # Switch
971 usage += '\n'
972 usage += s + grey(u'\u266A' + ' Switching streams \n')
c075e6dc 973 usage += s * 2 + light_green('switch public #AKB') + \
48a25fe8 974 ' will switch to public stream and follow "' + \
c075e6dc
O
975 light_yellow('AKB') + '" keyword.\n'
976 usage += s * 2 + light_green('switch mine') + \
48a25fe8 977 ' will switch to your personal stream.\n'
c075e6dc 978 usage += s * 2 + light_green('switch mine -f ') + \
48a25fe8 979 ' will prompt to enter the filter.\n'
c075e6dc 980 usage += s * 3 + light_yellow('Only nicks') + \
48a25fe8 981 ' filter will decide nicks will be INCLUDE ONLY.\n'
c075e6dc 982 usage += s * 3 + light_yellow('Ignore nicks') + \
48a25fe8 983 ' filter will decide nicks will be EXCLUDE.\n'
c075e6dc 984 usage += s * 2 + light_green('switch mine -d') + \
48a25fe8 985 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
48a25fe8 986
1f24a05a 987 # Smart shell
988 usage += '\n'
989 usage += s + grey(u'\u266A' + ' Smart shell\n')
c075e6dc 990 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1f24a05a 991 'will be evaluate by Python interpreter.\n'
c075e6dc 992 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1f24a05a 993 ' for current month.\n'
994
995 # Screening
996 usage += '\n'
997 usage += s + grey(u'\u266A' + ' Screening \n')
c075e6dc
O
998 usage += s * 2 + light_green('theme') + ' will list available theme.' + \
999 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
632c6fa5 1000 ' theme immediately.\n'
c075e6dc
O
1001 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1002 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1003 usage += s * 2 + light_green('q') + ' will quit.\n'
1f24a05a 1004
8bc30efd 1005 # End
1006 usage += '\n'
7e4ccbf3 1007 usage += s + '-' * (int(w) - 4) + '\n'
8bc30efd 1008 usage += s + 'Have fun and hang tight! \n'
f405a7d0 1009 printNicely(usage)
f405a7d0
O
1010
1011
843647ad 1012def clear():
f405a7d0 1013 """
7b674cef 1014 Clear screen
f405a7d0 1015 """
843647ad 1016 os.system('clear')
f405a7d0
O
1017
1018
843647ad 1019def quit():
b8dda704
O
1020 """
1021 Exit all
1022 """
f5677fb1 1023 save_history()
8e633322 1024 os.system('rm -rf rainbow.db')
843647ad
O
1025 os.kill(g['stream_pid'], signal.SIGKILL)
1026 sys.exit()
b8dda704
O
1027
1028
94a5f62e 1029def reset():
f405a7d0 1030 """
94a5f62e 1031 Reset prefix of line
f405a7d0 1032 """
c91f75f2 1033 if g['reset']:
e3885f55 1034 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
c91f75f2 1035 g['reset'] = False
d0a726d6 1036 try:
2a0cabee
O
1037 printNicely(str(eval(g['cmd'])))
1038 except Exception:
d0a726d6 1039 pass
54277114
O
1040
1041
94a5f62e 1042def process(cmd):
54277114 1043 """
94a5f62e 1044 Process switch
54277114 1045 """
94a5f62e 1046 return dict(zip(
1047 cmdset,
b2b933a9 1048 [
42fde775 1049 switch,
4592d231 1050 trend,
b2b933a9 1051 home,
1052 view,
305ce127 1053 mentions,
b2b933a9 1054 tweet,
1055 retweet,
80b70d60 1056 quote,
1f24a05a 1057 allretweet,
7e4ccbf3 1058 favorite,
b2b933a9 1059 reply,
1060 delete,
7e4ccbf3 1061 unfavorite,
b2b933a9 1062 search,
305ce127 1063 message,
f5677fb1 1064 show,
80b70d60 1065 open,
0f6e4daf 1066 list,
305ce127 1067 inbox,
1068 sent,
1069 trash,
e2b81717 1070 whois,
f5677fb1
O
1071 follow,
1072 unfollow,
5b2c4faf 1073 mute,
1074 unmute,
1075 muting,
305ce127 1076 block,
1077 unblock,
1078 report,
813a5d80 1079 cal,
632c6fa5 1080 theme,
b2b933a9 1081 help,
1082 clear,
1083 quit
1084 ]
94a5f62e 1085 )).get(cmd, reset)
1086
1087
1088def listen():
42fde775 1089 """
1090 Listen to user's input
1091 """
d51b4107
O
1092 d = dict(zip(
1093 cmdset,
1094 [
affcb149 1095 ['public', 'mine'], # switch
4592d231 1096 [], # trend
7e4ccbf3 1097 [], # home
1098 ['@'], # view
305ce127 1099 [], # mentions
7e4ccbf3 1100 [], # tweet
1101 [], # retweet
80b70d60 1102 [], # quote
1f24a05a 1103 [], # allretweet
f5677fb1 1104 [], # favorite
7e4ccbf3 1105 [], # reply
1106 [], # delete
f5677fb1 1107 [], # unfavorite
7e4ccbf3 1108 ['#'], # search
305ce127 1109 ['@'], # message
f5677fb1 1110 ['image'], # show image
80b70d60 1111 [''], # open url
305ce127 1112 ['fl', 'fr'], # list
1113 [], # inbox
1114 [], # sent
1115 [], # trash
e2b81717 1116 ['@'], # whois
affcb149
O
1117 ['@'], # follow
1118 ['@'], # unfollow
5b2c4faf 1119 ['@'], # mute
1120 ['@'], # unmute
1121 ['@'], # muting
305ce127 1122 ['@'], # block
1123 ['@'], # unblock
1124 ['@'], # report
813a5d80 1125 [], # cal
1c8a5082 1126 g['themes'] + ['current_as_default'], # theme
7e4ccbf3 1127 [], # help
1128 [], # clear
1129 [], # quit
d51b4107 1130 ]
7e4ccbf3 1131 ))
d51b4107 1132 init_interactive_shell(d)
f5677fb1 1133 read_history()
819569e8 1134 reset()
b2b933a9 1135 while True:
1dd312f5
O
1136 if g['prefix']:
1137 line = raw_input(g['decorated_name'])
1138 else:
1139 line = raw_input()
843647ad
O
1140 try:
1141 cmd = line.split()[0]
1142 except:
1143 cmd = ''
d0a726d6 1144 g['cmd'] = cmd
f405a7d0 1145 # Save cmd to global variable and call process
b8c1f42a
O
1146 try:
1147 g['stuff'] = ' '.join(line.split()[1:])
1148 process(cmd)()
2a0cabee 1149 except Exception:
b8c1f42a
O
1150 printNicely(red('OMG something is wrong with Twitter right now.'))
1151 # Not redisplay prefix
7e4ccbf3 1152 if cmd in ['switch', 't', 'rt', 'rep']:
1dd312f5
O
1153 g['prefix'] = False
1154 else:
1155 g['prefix'] = True
54277114
O
1156
1157
42fde775 1158def stream(domain, args, name='Rainbow Stream'):
54277114 1159 """
f405a7d0 1160 Track the stream
54277114 1161 """
d51b4107 1162
54277114 1163 # The Logo
42fde775 1164 art_dict = {
632c6fa5
O
1165 c['USER_DOMAIN']: name,
1166 c['PUBLIC_DOMAIN']: args.track_keywords,
1167 c['SITE_DOMAIN']: 'Site Stream',
42fde775 1168 }
c075e6dc
O
1169 if g['ascii_art']:
1170 ascii_art(art_dict[domain])
d51b4107 1171
91476ec3
O
1172 # These arguments are optional:
1173 stream_args = dict(
1174 timeout=args.timeout,
1175 block=not args.no_block,
1176 heartbeat_timeout=args.heartbeat_timeout)
1177
1178 # Track keyword
1179 query_args = dict()
1180 if args.track_keywords:
1181 query_args['track'] = args.track_keywords
1182
1183 # Get stream
2a6238f5 1184 stream = TwitterStream(
22be990e 1185 auth=authen(),
42fde775 1186 domain=domain,
2a6238f5 1187 **stream_args)
91476ec3 1188
2a0cabee
O
1189 try:
1190 if domain == c['USER_DOMAIN']:
1191 tweet_iter = stream.user(**query_args)
1192 elif domain == c['SITE_DOMAIN']:
1193 tweet_iter = stream.site(**query_args)
42fde775 1194 else:
2a0cabee
O
1195 if args.track_keywords:
1196 tweet_iter = stream.statuses.filter(**query_args)
1197 else:
1198 tweet_iter = stream.statuses.sample()
42fde775 1199
2a0cabee 1200 # Iterate over the stream.
72c02928
VNM
1201 for tweet in tweet_iter:
1202 if tweet is None:
1203 printNicely("-- None --")
1204 elif tweet is Timeout:
1205 printNicely("-- Timeout --")
1206 elif tweet is HeartbeatTimeout:
1207 printNicely("-- Heartbeat Timeout --")
1208 elif tweet is Hangup:
1209 printNicely("-- Hangup --")
1210 elif tweet.get('text'):
1211 draw(
1212 t=tweet,
1213 iot=args.image_on_term,
1214 keyword=args.track_keywords,
1215 fil=args.filter,
1216 ig=args.ignore,
1217 )
2a0cabee
O
1218 except TwitterHTTPError:
1219 printNicely('')
c075e6dc 1220 printNicely(
2a0cabee 1221 magenta("We have maximum connection problem with twitter'stream API right now :("))
54277114
O
1222
1223
1224def fly():
1225 """
1226 Main function
1227 """
42fde775 1228 # Spawn stream process
1229 args = parse_arguments()
2a0cabee
O
1230 try:
1231 get_decorated_name()
1232
1233 except TwitterHTTPError:
1234 printNicely('')
1235 printNicely(
1236 magenta("I'm afraid we have maximum connection problem with twitter right now :("))
1237 printNicely(magenta("Let's try again later."))
1238 save_history()
1239 os.system('rm -rf rainbow.db')
1240 sys.exit()
1241
c075e6dc
O
1242 p = Process(
1243 target=stream,
1244 args=(
2a0cabee 1245 c['USER_DOMAIN'],
c075e6dc
O
1246 args,
1247 g['original_name']))
42fde775 1248 p.start()
1249
1250 # Start listen process
819569e8 1251 time.sleep(0.5)
c91f75f2 1252 g['reset'] = True
1dd312f5 1253 g['prefix'] = True
f405a7d0 1254 g['stream_pid'] = p.pid
c1fa7c94 1255 g['iot'] = args.image_on_term
0f6e4daf 1256 listen()