change command name
[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')
595fdb16
O
332 notice = light_magenta('Compose mode ')
333 notice += light_yellow('(Enter nothing will cancel the quote)')
334 notice += light_magenta(':')
335 printNicely(notice)
80b70d60
O
336 extra = raw_input(quote)
337 if extra:
338 t.statuses.update(status=quote+extra)
339 else:
340 printNicely(light_magenta('No text added.'))
341
342
1f24a05a 343def allretweet():
344 """
345 List all retweet
346 """
347 t = Twitter(auth=authen())
348 # Get rainbow id
349 try:
350 id = int(g['stuff'].split()[0])
351 except:
352 printNicely(red('Sorry I can\'t understand.'))
353 return
354 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
355 # Get display num if exist
356 try:
357 num = int(g['stuff'].split()[1])
358 except:
632c6fa5 359 num = c['RETWEETS_SHOW_NUM']
1f24a05a 360 # Get result and display
d8e901a4 361 rt_ary = t.statuses.retweets(id=tid, count=num)
1f24a05a 362 if not rt_ary:
363 printNicely(magenta('This tweet has no retweet.'))
364 return
365 for tweet in reversed(rt_ary):
366 draw(t=tweet, iot=g['iot'])
367 printNicely('')
368
369
7e4ccbf3 370def favorite():
371 """
372 Favorite
373 """
374 t = Twitter(auth=authen())
375 try:
376 id = int(g['stuff'].split()[0])
7e4ccbf3 377 except:
b8c1f42a
O
378 printNicely(red('Sorry I can\'t understand.'))
379 return
380 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
381 t.favorites.create(_id=tid, include_entities=False)
382 printNicely(green('Favorited.'))
383 draw(t.statuses.show(id=tid), iot=g['iot'])
384 printNicely('')
7e4ccbf3 385
386
7b674cef 387def reply():
829cc2d8 388 """
7b674cef 389 Reply
829cc2d8
O
390 """
391 t = Twitter(auth=authen())
7b674cef 392 try:
393 id = int(g['stuff'].split()[0])
7b674cef 394 except:
c91f75f2 395 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
396 return
397 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
398 user = t.statuses.show(id=tid)['user']['screen_name']
399 status = ' '.join(g['stuff'].split()[1:])
400 status = '@' + user + ' ' + status.decode('utf-8')
401 t.statuses.update(status=status, in_reply_to_status_id=tid)
7b674cef 402
403
404def delete():
405 """
406 Delete
407 """
408 t = Twitter(auth=authen())
409 try:
305ce127 410 rid = int(g['stuff'].split()[0])
7b674cef 411 except:
305ce127 412 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
413 return
414 tid = db.rainbow_to_tweet_query(rid)[0].tweet_id
415 t.statuses.destroy(id=tid)
416 printNicely(green('Okay it\'s gone.'))
829cc2d8
O
417
418
7e4ccbf3 419def unfavorite():
420 """
421 Unfavorite
422 """
423 t = Twitter(auth=authen())
424 try:
425 id = int(g['stuff'].split()[0])
7e4ccbf3 426 except:
b8c1f42a
O
427 printNicely(red('Sorry I can\'t understand.'))
428 return
429 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
430 t.favorites.destroy(_id=tid)
431 printNicely(green('Okay it\'s unfavorited.'))
432 draw(t.statuses.show(id=tid), iot=g['iot'])
433 printNicely('')
7e4ccbf3 434
435
f405a7d0
O
436def search():
437 """
7b674cef 438 Search
f405a7d0
O
439 """
440 t = Twitter(auth=authen())
b8c1f42a
O
441 if g['stuff'].startswith('#'):
442 rel = t.search.tweets(q=g['stuff'])['statuses']
5b2c4faf 443 if rel:
b8c1f42a 444 printNicely('Newest tweets:')
632c6fa5 445 for i in reversed(xrange(c['SEARCH_MAX_RECORD'])):
b8c1f42a
O
446 draw(t=rel[i],
447 iot=g['iot'],
448 keyword=g['stuff'].strip()[1:])
449 printNicely('')
94a5f62e 450 else:
b8c1f42a
O
451 printNicely(magenta('I\'m afraid there is no result'))
452 else:
453 printNicely(red('A keyword should be a hashtag (like \'#AKB48\')'))
b2b933a9 454
f405a7d0 455
305ce127 456def message():
457 """
458 Send a direct message
459 """
460 t = Twitter(auth=authen())
461 user = g['stuff'].split()[0]
b8c1f42a 462 if user[0].startswith('@'):
305ce127 463 try:
464 content = g['stuff'].split()[1]
305ce127 465 except:
466 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
467 t.direct_messages.new(
468 screen_name=user[1:],
469 text=content
470 )
471 printNicely(green('Message sent.'))
305ce127 472 else:
473 printNicely(red('A name should begin with a \'@\''))
474
475
f5677fb1 476def show():
843647ad 477 """
f5677fb1 478 Show image
843647ad
O
479 """
480 t = Twitter(auth=authen())
f5677fb1
O
481 try:
482 target = g['stuff'].split()[0]
483 if target != 'image':
484 return
485 id = int(g['stuff'].split()[1])
305ce127 486 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
f5677fb1
O
487 tweet = t.statuses.show(id=tid)
488 media = tweet['entities']['media']
489 for m in media:
490 res = requests.get(m['media_url'])
491 img = Image.open(StringIO(res.content))
492 img.show()
493 except:
494 printNicely(red('Sorry I can\'t show this image.'))
843647ad
O
495
496
80bb2040 497def urlopen():
80b70d60
O
498 """
499 Open url
500 """
501 t = Twitter(auth=authen())
502 try:
503 if not g['stuff'].isdigit():
504 return
505 tid = db.rainbow_to_tweet_query(g['stuff'])[0].tweet_id
506 tweet = t.statuses.show(id=tid)
507 link_ary = [u for u in tweet['text'].split() if u.startswith('http://')]
508 if not link_ary:
509 printNicely(light_magenta('No url here @.@!'))
510 return
511 for link in link_ary:
512 webbrowser.open(link)
513 except:
514 printNicely(red('Sorry I can\'t open url in this tweet.'))
515
516
0f6e4daf 517def list():
518 """
519 List friends for followers
520 """
521 t = Twitter(auth=authen())
e2b81717
O
522 # Get name
523 try:
524 name = g['stuff'].split()[1]
b8c1f42a 525 if name.startswith('@'):
e2b81717
O
526 name = name[1:]
527 else:
528 printNicely(red('A name should begin with a \'@\''))
529 raise Exception('Invalid name')
530 except:
531 name = g['original_name']
532 # Get list followers or friends
0f6e4daf 533 try:
534 target = g['stuff'].split()[0]
0f6e4daf 535 except:
536 printNicely(red('Omg some syntax is wrong.'))
b8c1f42a
O
537 # Init cursor
538 d = {'fl': 'followers', 'fr': 'friends'}
539 next_cursor = -1
540 rel = {}
541 # Cursor loop
542 while next_cursor != 0:
543 list = getattr(t, d[target]).list(
544 screen_name=name,
545 cursor=next_cursor,
546 skip_status=True,
547 include_entities=False,
548 )
549 for u in list['users']:
550 rel[u['name']] = '@' + u['screen_name']
551 next_cursor = list['next_cursor']
552 # Print out result
553 printNicely('All: ' + str(len(rel)) + ' people.')
554 for name in rel:
555 user = ' ' + cycle_color(name) + grey(' ' + rel[name] + ' ')
556 printNicely(user)
0f6e4daf 557
558
305ce127 559def inbox():
560 """
561 Inbox direct messages
562 """
563 t = Twitter(auth=authen())
632c6fa5 564 num = c['MESSAGES_DISPLAY']
305ce127 565 rel = []
566 if g['stuff'].isdigit():
567 num = g['stuff']
568 cur_page = 1
569 # Max message per page is 20 so we have to loop
570 while num > 20:
571 rel = rel + t.direct_messages(
572 count=20,
573 page=cur_page,
574 include_entities=False,
575 skip_status=False
48a25fe8 576 )
305ce127 577 num -= 20
578 cur_page += 1
579 rel = rel + t.direct_messages(
580 count=num,
581 page=cur_page,
582 include_entities=False,
583 skip_status=False
48a25fe8 584 )
e2b81717 585 # Display
305ce127 586 printNicely('Inbox: newest ' + str(len(rel)) + ' messages.')
587 for m in reversed(rel):
588 print_message(m)
589 printNicely('')
590
e2b81717 591
305ce127 592def sent():
593 """
594 Sent direct messages
595 """
596 t = Twitter(auth=authen())
632c6fa5 597 num = c['MESSAGES_DISPLAY']
305ce127 598 rel = []
599 if g['stuff'].isdigit():
600 num = int(g['stuff'])
601 cur_page = 1
602 # Max message per page is 20 so we have to loop
603 while num > 20:
604 rel = rel + t.direct_messages.sent(
605 count=20,
606 page=cur_page,
607 include_entities=False,
608 skip_status=False
48a25fe8 609 )
305ce127 610 num -= 20
611 cur_page += 1
612 rel = rel + t.direct_messages.sent(
613 count=num,
614 page=cur_page,
615 include_entities=False,
616 skip_status=False
48a25fe8 617 )
e2b81717 618 # Display
305ce127 619 printNicely('Sent: newest ' + str(len(rel)) + ' messages.')
620 for m in reversed(rel):
621 print_message(m)
622 printNicely('')
e2b81717 623
305ce127 624
625def trash():
626 """
627 Remove message
628 """
629 t = Twitter(auth=authen())
630 try:
631 rid = int(g['stuff'].split()[0])
305ce127 632 except:
633 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
634 mid = db.rainbow_to_message_query(rid)[0].message_id
635 t.direct_messages.destroy(id=mid)
636 printNicely(green('Message deleted.'))
305ce127 637
638
e2b81717
O
639def whois():
640 """
641 Show profile of a specific user
642 """
643 t = Twitter(auth=authen())
644 screen_name = g['stuff'].split()[0]
b8c1f42a 645 if screen_name.startswith('@'):
e2b81717
O
646 try:
647 user = t.users.show(
648 screen_name=screen_name[1:],
649 include_entities=False)
7500d90b 650 show_profile(user, g['iot'])
e2b81717
O
651 except:
652 printNicely(red('Omg no user.'))
653 else:
b8c1f42a 654 printNicely(red('A name should begin with a \'@\''))
e2b81717
O
655
656
f5677fb1 657def follow():
843647ad 658 """
f5677fb1 659 Follow a user
843647ad
O
660 """
661 t = Twitter(auth=authen())
f5677fb1 662 screen_name = g['stuff'].split()[0]
b8c1f42a
O
663 if screen_name.startswith('@'):
664 t.friendships.create(screen_name=screen_name[1:], follow=True)
665 printNicely(green('You are following ' + screen_name + ' now!'))
f5677fb1 666 else:
b8c1f42a 667 printNicely(red('A name should begin with a \'@\''))
f5677fb1
O
668
669
670def unfollow():
671 """
672 Unfollow a user
673 """
674 t = Twitter(auth=authen())
675 screen_name = g['stuff'].split()[0]
b8c1f42a
O
676 if screen_name.startswith('@'):
677 t.friendships.destroy(
678 screen_name=screen_name[1:],
679 include_entities=False)
680 printNicely(green('Unfollow ' + screen_name + ' success!'))
f5677fb1 681 else:
b8c1f42a 682 printNicely(red('A name should begin with a \'@\''))
843647ad
O
683
684
5b2c4faf 685def mute():
686 """
687 Mute a user
688 """
689 t = Twitter(auth=authen())
690 try:
691 screen_name = g['stuff'].split()[0]
692 except:
693 printNicely(red('A name should be specified. '))
694 return
695 if screen_name.startswith('@'):
696 rel = t.mutes.users.create(screen_name=screen_name[1:])
697 if isinstance(rel, dict):
698 printNicely(green(screen_name + ' is muted.'))
699 else:
700 printNicely(red(rel))
701 else:
702 printNicely(red('A name should begin with a \'@\''))
703
704
705def unmute():
706 """
707 Unmute a user
708 """
709 t = Twitter(auth=authen())
710 try:
711 screen_name = g['stuff'].split()[0]
712 except:
713 printNicely(red('A name should be specified. '))
714 return
715 if screen_name.startswith('@'):
716 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
717 if isinstance(rel, dict):
718 printNicely(green(screen_name + ' is unmuted.'))
719 else:
720 printNicely(red(rel))
721 else:
722 printNicely(red('A name should begin with a \'@\''))
723
724
725def muting():
726 """
727 List muting user
728 """
729 t = Twitter(auth=authen())
730 # Init cursor
5b2c4faf 731 next_cursor = -1
732 rel = {}
733 # Cursor loop
734 while next_cursor != 0:
735 list = t.mutes.users.list(
736 screen_name=g['original_name'],
737 cursor=next_cursor,
738 skip_status=True,
739 include_entities=False,
740 )
741 for u in list['users']:
742 rel[u['name']] = '@' + u['screen_name']
743 next_cursor = list['next_cursor']
744 # Print out result
745 printNicely('All: ' + str(len(rel)) + ' people.')
746 for name in rel:
747 user = ' ' + cycle_color(name) + grey(' ' + rel[name] + ' ')
748 printNicely(user)
749
750
305ce127 751def block():
752 """
753 Block a user
754 """
755 t = Twitter(auth=authen())
756 screen_name = g['stuff'].split()[0]
b8c1f42a
O
757 if screen_name.startswith('@'):
758 t.blocks.create(
5b2c4faf 759 screen_name=screen_name[1:],
760 include_entities=False,
761 skip_status=True)
b8c1f42a 762 printNicely(green('You blocked ' + screen_name + '.'))
305ce127 763 else:
b8c1f42a 764 printNicely(red('A name should begin with a \'@\''))
305ce127 765
766
767def unblock():
768 """
769 Unblock a user
770 """
771 t = Twitter(auth=authen())
772 screen_name = g['stuff'].split()[0]
b8c1f42a
O
773 if screen_name.startswith('@'):
774 t.blocks.destroy(
775 screen_name=screen_name[1:],
776 include_entities=False,
777 skip_status=True)
778 printNicely(green('Unblock ' + screen_name + ' success!'))
305ce127 779 else:
b8c1f42a 780 printNicely(red('A name should begin with a \'@\''))
305ce127 781
782
783def report():
784 """
785 Report a user as a spam account
786 """
787 t = Twitter(auth=authen())
788 screen_name = g['stuff'].split()[0]
b8c1f42a
O
789 if screen_name.startswith('@'):
790 t.users.report_spam(
791 screen_name=screen_name[1:])
792 printNicely(green('You reported ' + screen_name + '.'))
305ce127 793 else:
794 printNicely(red('Sorry I can\'t understand.'))
795
796
813a5d80 797def cal():
798 """
799 Unix's command `cal`
800 """
801 # Format
802 rel = os.popen('cal').read().split('\n')
803 month = rel.pop(0)
813a5d80 804 date = rel.pop(0)
2a0cabee 805 show_calendar(month, date, rel)
813a5d80 806
807
632c6fa5
O
808def theme():
809 """
810 List and change theme
811 """
812 if not g['stuff']:
813 # List themes
814 for theme in g['themes']:
ddb1e615 815 line = ''
4cf86720 816 # Detect custom config
ddb1e615 817 if theme == 'custom':
4cf86720 818 line += light_magenta('custom')
77c9b04e
VNM
819 custom_path = os.environ.get(
820 'HOME',
821 os.environ.get('USERPROFILE',
822 '')) + os.sep + '.rainbow_config.json'
823 if not os.path.exists(custom_path):
9bc91c78 824 line += light_magenta(' (create your own config file at ~/.rainbow_config.json)')
4cf86720 825 else:
77c9b04e 826 line += light_magenta(' (loaded)')
4cf86720
VNM
827 else:
828 line += light_magenta(theme)
ddb1e615
VNM
829 if c['theme'] == theme :
830 line = ' '*2 + light_yellow('* ') + line
831 else:
832 line = ' '*4 + line
632c6fa5 833 printNicely(line)
1c8a5082 834 elif g['stuff'] == 'current_as_default':
80bb2040 835 # Set default
1c8a5082
O
836 path = os.path.dirname(__file__) + '/colorset/init'
837 f = open(path,'w')
838 f.write(c['theme'])
839 f.close()
2a488171 840 os.system('chmod 777 ' + path)
1c8a5082 841 printNicely(light_green('Okay it will be applied from next time :)'))
632c6fa5
O
842 else:
843 # Change theme
c075e6dc
O
844 try:
845 # Load new config
bb03272e 846 if g['stuff'] != 'custom':
15f3e155 847 new_config = os.path.dirname(__file__) + '/colorset/' + g['stuff'] + '.json'
bb03272e
VNM
848 else:
849 new_config = os.environ.get(
850 'HOME',os.environ.get(
851 'USERPROFILE',
852 '')) + os.sep + '.rainbow_config.json'
632c6fa5 853 new_config = load_config(new_config)
9e3418f1 854 if new_config:
a5301bc0
VNM
855 for nc in new_config:
856 c[nc] = new_config[nc]
77c9b04e 857 # Update db and reset colors
ddb1e615 858 db.theme_update(g['stuff'])
75bc416c 859 c['theme'] = g['stuff']
e43ebfa6 860 reset_cycle()
c075e6dc
O
861 g['decorated_name'] = color_func(
862 c['DECORATED_NAME'])(
863 '[@' + g['original_name'] + ']: ')
632c6fa5 864 printNicely(green('Theme changed.'))
c075e6dc 865 except:
657c4708 866 if g['stuff'] == 'custom':
75bc416c 867 printNicely(red('~/.rainbow_config.json is not exists!'))
657c4708
VNM
868 else:
869 printNicely(red('No such theme exists.'))
632c6fa5
O
870
871
f405a7d0
O
872def help():
873 """
7b674cef 874 Help
f405a7d0 875 """
7e4ccbf3 876 s = ' ' * 2
877 h, w = os.popen('stty size', 'r').read().split()
e3885f55 878
8bc30efd 879 # Start
e3885f55
O
880 usage = '\n'
881 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
7e4ccbf3 882 usage += s + '-' * (int(w) - 4) + '\n'
c075e6dc
O
883 usage += s + 'You are ' + \
884 light_yellow('already') + ' on your personal stream.\n'
5b2c4faf 885 usage += s + 'Any update from Twitter will show up ' + \
c075e6dc 886 light_yellow('immediately') + '.\n'
5b2c4faf 887 usage += s + 'In addtion, following commands are available right now:\n'
e3885f55 888
1f24a05a 889 # Discover the world
8bc30efd 890 usage += '\n'
891 usage += s + grey(u'\u266A' + ' Discover the world \n')
c075e6dc
O
892 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
893 'You can try ' + light_green('trend US') + ' or ' + \
894 light_green('trend JP Tokyo') + '.\n'
895 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
896 light_green('home 7') + ' will show 7 tweets.\n'
897 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
898 light_green('mentions 7') + ' will show 7 mention tweets.\n'
899 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
8bc30efd 900 magenta('@mdo') + '.\n'
c075e6dc 901 usage += s * 2 + light_green('view @mdo') + \
8bc30efd 902 ' will show ' + magenta('@mdo') + '\'s home.\n'
c075e6dc
O
903 usage += s * 2 + light_green('s #AKB48') + ' will search for "' + \
904 light_yellow('AKB48') + '" and return 5 newest tweet.\n'
8bc30efd 905
1f24a05a 906 # Tweet
8bc30efd 907 usage += '\n'
908 usage += s + grey(u'\u266A' + ' Tweets \n')
c075e6dc
O
909 usage += s * 2 + light_green('t oops ') + \
910 'will tweet "' + light_yellow('oops') + '" immediately.\n'
7e4ccbf3 911 usage += s * 2 + \
c075e6dc
O
912 light_green('rt 12 ') + ' will retweet to tweet with ' + \
913 light_yellow('[id=12]') + '.\n'
80b70d60
O
914 usage += s * 2 + \
915 light_green('quote 12 ') + ' will quote the tweet with ' + \
916 light_yellow('[id=12]') + '. If no extra text is added, ' + \
917 'the quote will be canceled.\n'
1f24a05a 918 usage += s * 2 + \
c075e6dc
O
919 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
920 light_yellow('[id=12]') + '.\n'
921 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
922 light_yellow('oops') + '" to tweet with ' + \
923 light_yellow('[id=12]') + '.\n'
7e4ccbf3 924 usage += s * 2 + \
c075e6dc
O
925 light_green('fav 12 ') + ' will favorite the tweet with ' + \
926 light_yellow('[id=12]') + '.\n'
7e4ccbf3 927 usage += s * 2 + \
c075e6dc
O
928 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
929 light_yellow('[id=12]') + '.\n'
8bc30efd 930 usage += s * 2 + \
c075e6dc
O
931 light_green('del 12 ') + ' will delete tweet with ' + \
932 light_yellow('[id=12]') + '.\n'
933 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
934 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
80b70d60
O
935 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
936 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
8bc30efd 937
5b2c4faf 938 # Direct message
8bc30efd 939 usage += '\n'
940 usage += s + grey(u'\u266A' + ' Direct messages \n')
c075e6dc
O
941 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
942 light_green('inbox 7') + ' will show newest 7 messages.\n'
943 usage += s * 2 + light_green('sent') + ' will show sent messages. ' + \
944 light_green('sent 7') + ' will show newest 7 messages.\n'
945 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
8bc30efd 946 magenta('@dtvd88') + '.\n'
c075e6dc
O
947 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
948 light_yellow('[message_id=5]') + '.\n'
8bc30efd 949
950 # Follower and following
951 usage += '\n'
952 usage += s + grey(u'\u266A' + ' Fiends and followers \n')
953 usage += s * 2 + \
c075e6dc 954 light_green('ls fl') + \
8bc30efd 955 ' will list all followers (people who are following you).\n'
956 usage += s * 2 + \
c075e6dc 957 light_green('ls fr') + \
8bc30efd 958 ' will list all friends (people who you are following).\n'
c075e6dc 959 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
305ce127 960 magenta('@dtvd88') + '.\n'
c075e6dc 961 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
305ce127 962 magenta('@dtvd88') + '.\n'
c075e6dc 963 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
5b2c4faf 964 magenta('@dtvd88') + '.\n'
c075e6dc 965 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
5b2c4faf 966 magenta('@dtvd88') + '.\n'
c075e6dc
O
967 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
968 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
305ce127 969 magenta('@dtvd88') + '.\n'
c075e6dc 970 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
305ce127 971 magenta('@dtvd88') + '.\n'
c075e6dc 972 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
305ce127 973 magenta('@dtvd88') + ' as a spam account.\n'
8bc30efd 974
8bc30efd 975 # Switch
976 usage += '\n'
977 usage += s + grey(u'\u266A' + ' Switching streams \n')
c075e6dc 978 usage += s * 2 + light_green('switch public #AKB') + \
48a25fe8 979 ' will switch to public stream and follow "' + \
c075e6dc
O
980 light_yellow('AKB') + '" keyword.\n'
981 usage += s * 2 + light_green('switch mine') + \
48a25fe8 982 ' will switch to your personal stream.\n'
c075e6dc 983 usage += s * 2 + light_green('switch mine -f ') + \
48a25fe8 984 ' will prompt to enter the filter.\n'
c075e6dc 985 usage += s * 3 + light_yellow('Only nicks') + \
48a25fe8 986 ' filter will decide nicks will be INCLUDE ONLY.\n'
c075e6dc 987 usage += s * 3 + light_yellow('Ignore nicks') + \
48a25fe8 988 ' filter will decide nicks will be EXCLUDE.\n'
c075e6dc 989 usage += s * 2 + light_green('switch mine -d') + \
48a25fe8 990 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
48a25fe8 991
1f24a05a 992 # Smart shell
993 usage += '\n'
994 usage += s + grey(u'\u266A' + ' Smart shell\n')
c075e6dc 995 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1f24a05a 996 'will be evaluate by Python interpreter.\n'
c075e6dc 997 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1f24a05a 998 ' for current month.\n'
999
1000 # Screening
1001 usage += '\n'
1002 usage += s + grey(u'\u266A' + ' Screening \n')
c075e6dc
O
1003 usage += s * 2 + light_green('theme') + ' will list available theme.' + \
1004 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
632c6fa5 1005 ' theme immediately.\n'
c075e6dc
O
1006 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1007 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1008 usage += s * 2 + light_green('q') + ' will quit.\n'
1f24a05a 1009
8bc30efd 1010 # End
1011 usage += '\n'
7e4ccbf3 1012 usage += s + '-' * (int(w) - 4) + '\n'
8bc30efd 1013 usage += s + 'Have fun and hang tight! \n'
f405a7d0 1014 printNicely(usage)
f405a7d0
O
1015
1016
843647ad 1017def clear():
f405a7d0 1018 """
7b674cef 1019 Clear screen
f405a7d0 1020 """
843647ad 1021 os.system('clear')
f405a7d0
O
1022
1023
843647ad 1024def quit():
b8dda704
O
1025 """
1026 Exit all
1027 """
f5677fb1 1028 save_history()
8e633322 1029 os.system('rm -rf rainbow.db')
843647ad
O
1030 os.kill(g['stream_pid'], signal.SIGKILL)
1031 sys.exit()
b8dda704
O
1032
1033
94a5f62e 1034def reset():
f405a7d0 1035 """
94a5f62e 1036 Reset prefix of line
f405a7d0 1037 """
c91f75f2 1038 if g['reset']:
e3885f55 1039 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
c91f75f2 1040 g['reset'] = False
d0a726d6 1041 try:
2a0cabee
O
1042 printNicely(str(eval(g['cmd'])))
1043 except Exception:
d0a726d6 1044 pass
54277114
O
1045
1046
94a5f62e 1047def process(cmd):
54277114 1048 """
94a5f62e 1049 Process switch
54277114 1050 """
94a5f62e 1051 return dict(zip(
1052 cmdset,
b2b933a9 1053 [
42fde775 1054 switch,
4592d231 1055 trend,
b2b933a9 1056 home,
1057 view,
305ce127 1058 mentions,
b2b933a9 1059 tweet,
1060 retweet,
80b70d60 1061 quote,
1f24a05a 1062 allretweet,
7e4ccbf3 1063 favorite,
b2b933a9 1064 reply,
1065 delete,
7e4ccbf3 1066 unfavorite,
b2b933a9 1067 search,
305ce127 1068 message,
f5677fb1 1069 show,
80bb2040 1070 urlopen,
0f6e4daf 1071 list,
305ce127 1072 inbox,
1073 sent,
1074 trash,
e2b81717 1075 whois,
f5677fb1
O
1076 follow,
1077 unfollow,
5b2c4faf 1078 mute,
1079 unmute,
1080 muting,
305ce127 1081 block,
1082 unblock,
1083 report,
813a5d80 1084 cal,
632c6fa5 1085 theme,
b2b933a9 1086 help,
1087 clear,
1088 quit
1089 ]
94a5f62e 1090 )).get(cmd, reset)
1091
1092
1093def listen():
42fde775 1094 """
1095 Listen to user's input
1096 """
d51b4107
O
1097 d = dict(zip(
1098 cmdset,
1099 [
affcb149 1100 ['public', 'mine'], # switch
4592d231 1101 [], # trend
7e4ccbf3 1102 [], # home
1103 ['@'], # view
305ce127 1104 [], # mentions
7e4ccbf3 1105 [], # tweet
1106 [], # retweet
80b70d60 1107 [], # quote
1f24a05a 1108 [], # allretweet
f5677fb1 1109 [], # favorite
7e4ccbf3 1110 [], # reply
1111 [], # delete
f5677fb1 1112 [], # unfavorite
7e4ccbf3 1113 ['#'], # search
305ce127 1114 ['@'], # message
f5677fb1 1115 ['image'], # show image
80b70d60 1116 [''], # open url
305ce127 1117 ['fl', 'fr'], # list
1118 [], # inbox
1119 [], # sent
1120 [], # trash
e2b81717 1121 ['@'], # whois
affcb149
O
1122 ['@'], # follow
1123 ['@'], # unfollow
5b2c4faf 1124 ['@'], # mute
1125 ['@'], # unmute
1126 ['@'], # muting
305ce127 1127 ['@'], # block
1128 ['@'], # unblock
1129 ['@'], # report
813a5d80 1130 [], # cal
1c8a5082 1131 g['themes'] + ['current_as_default'], # theme
7e4ccbf3 1132 [], # help
1133 [], # clear
1134 [], # quit
d51b4107 1135 ]
7e4ccbf3 1136 ))
d51b4107 1137 init_interactive_shell(d)
f5677fb1 1138 read_history()
819569e8 1139 reset()
b2b933a9 1140 while True:
1dd312f5
O
1141 if g['prefix']:
1142 line = raw_input(g['decorated_name'])
1143 else:
1144 line = raw_input()
843647ad
O
1145 try:
1146 cmd = line.split()[0]
1147 except:
1148 cmd = ''
d0a726d6 1149 g['cmd'] = cmd
f405a7d0 1150 # Save cmd to global variable and call process
b8c1f42a
O
1151 try:
1152 g['stuff'] = ' '.join(line.split()[1:])
1153 process(cmd)()
80bb2040
O
1154 except Exception,e :
1155 print e
b8c1f42a
O
1156 printNicely(red('OMG something is wrong with Twitter right now.'))
1157 # Not redisplay prefix
7e4ccbf3 1158 if cmd in ['switch', 't', 'rt', 'rep']:
1dd312f5
O
1159 g['prefix'] = False
1160 else:
1161 g['prefix'] = True
54277114
O
1162
1163
42fde775 1164def stream(domain, args, name='Rainbow Stream'):
54277114 1165 """
f405a7d0 1166 Track the stream
54277114 1167 """
d51b4107 1168
54277114 1169 # The Logo
42fde775 1170 art_dict = {
632c6fa5
O
1171 c['USER_DOMAIN']: name,
1172 c['PUBLIC_DOMAIN']: args.track_keywords,
1173 c['SITE_DOMAIN']: 'Site Stream',
42fde775 1174 }
c075e6dc
O
1175 if g['ascii_art']:
1176 ascii_art(art_dict[domain])
d51b4107 1177
91476ec3
O
1178 # These arguments are optional:
1179 stream_args = dict(
1180 timeout=args.timeout,
1181 block=not args.no_block,
1182 heartbeat_timeout=args.heartbeat_timeout)
1183
1184 # Track keyword
1185 query_args = dict()
1186 if args.track_keywords:
1187 query_args['track'] = args.track_keywords
1188
1189 # Get stream
2a6238f5 1190 stream = TwitterStream(
22be990e 1191 auth=authen(),
42fde775 1192 domain=domain,
2a6238f5 1193 **stream_args)
91476ec3 1194
2a0cabee
O
1195 try:
1196 if domain == c['USER_DOMAIN']:
1197 tweet_iter = stream.user(**query_args)
1198 elif domain == c['SITE_DOMAIN']:
1199 tweet_iter = stream.site(**query_args)
42fde775 1200 else:
2a0cabee
O
1201 if args.track_keywords:
1202 tweet_iter = stream.statuses.filter(**query_args)
1203 else:
1204 tweet_iter = stream.statuses.sample()
42fde775 1205
2a0cabee 1206 # Iterate over the stream.
72c02928
VNM
1207 for tweet in tweet_iter:
1208 if tweet is None:
1209 printNicely("-- None --")
1210 elif tweet is Timeout:
1211 printNicely("-- Timeout --")
1212 elif tweet is HeartbeatTimeout:
1213 printNicely("-- Heartbeat Timeout --")
1214 elif tweet is Hangup:
1215 printNicely("-- Hangup --")
1216 elif tweet.get('text'):
1217 draw(
1218 t=tweet,
1219 iot=args.image_on_term,
1220 keyword=args.track_keywords,
1221 fil=args.filter,
1222 ig=args.ignore,
1223 )
2a0cabee
O
1224 except TwitterHTTPError:
1225 printNicely('')
c075e6dc 1226 printNicely(
2a0cabee 1227 magenta("We have maximum connection problem with twitter'stream API right now :("))
54277114
O
1228
1229
1230def fly():
1231 """
1232 Main function
1233 """
42fde775 1234 # Spawn stream process
1235 args = parse_arguments()
2a0cabee
O
1236 try:
1237 get_decorated_name()
1238
1239 except TwitterHTTPError:
1240 printNicely('')
1241 printNicely(
1242 magenta("I'm afraid we have maximum connection problem with twitter right now :("))
1243 printNicely(magenta("Let's try again later."))
1244 save_history()
1245 os.system('rm -rf rainbow.db')
1246 sys.exit()
1247
c075e6dc
O
1248 p = Process(
1249 target=stream,
1250 args=(
2a0cabee 1251 c['USER_DOMAIN'],
c075e6dc
O
1252 args,
1253 g['original_name']))
42fde775 1254 p.start()
1255
1256 # Start listen process
819569e8 1257 time.sleep(0.5)
c91f75f2 1258 g['reset'] = True
1dd312f5 1259 g['prefix'] = True
f405a7d0 1260 g['stream_pid'] = p.pid
c1fa7c94 1261 g['iot'] = args.image_on_term
0f6e4daf 1262 listen()