add notice
[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()
840 printNicely(light_green('Okay it will be applied from next time :)'))
632c6fa5
O
841 else:
842 # Change theme
c075e6dc
O
843 try:
844 # Load new config
bb03272e 845 if g['stuff'] != 'custom':
15f3e155 846 new_config = os.path.dirname(__file__) + '/colorset/' + g['stuff'] + '.json'
bb03272e
VNM
847 else:
848 new_config = os.environ.get(
849 'HOME',os.environ.get(
850 'USERPROFILE',
851 '')) + os.sep + '.rainbow_config.json'
632c6fa5 852 new_config = load_config(new_config)
9e3418f1 853 if new_config:
a5301bc0
VNM
854 for nc in new_config:
855 c[nc] = new_config[nc]
77c9b04e 856 # Update db and reset colors
ddb1e615 857 db.theme_update(g['stuff'])
75bc416c 858 c['theme'] = g['stuff']
e43ebfa6 859 reset_cycle()
c075e6dc
O
860 g['decorated_name'] = color_func(
861 c['DECORATED_NAME'])(
862 '[@' + g['original_name'] + ']: ')
632c6fa5 863 printNicely(green('Theme changed.'))
c075e6dc 864 except:
657c4708 865 if g['stuff'] == 'custom':
75bc416c 866 printNicely(red('~/.rainbow_config.json is not exists!'))
657c4708
VNM
867 else:
868 printNicely(red('No such theme exists.'))
632c6fa5
O
869
870
f405a7d0
O
871def help():
872 """
7b674cef 873 Help
f405a7d0 874 """
7e4ccbf3 875 s = ' ' * 2
876 h, w = os.popen('stty size', 'r').read().split()
e3885f55 877
8bc30efd 878 # Start
e3885f55
O
879 usage = '\n'
880 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
7e4ccbf3 881 usage += s + '-' * (int(w) - 4) + '\n'
c075e6dc
O
882 usage += s + 'You are ' + \
883 light_yellow('already') + ' on your personal stream.\n'
5b2c4faf 884 usage += s + 'Any update from Twitter will show up ' + \
c075e6dc 885 light_yellow('immediately') + '.\n'
5b2c4faf 886 usage += s + 'In addtion, following commands are available right now:\n'
e3885f55 887
1f24a05a 888 # Discover the world
8bc30efd 889 usage += '\n'
890 usage += s + grey(u'\u266A' + ' Discover the world \n')
c075e6dc
O
891 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
892 'You can try ' + light_green('trend US') + ' or ' + \
893 light_green('trend JP Tokyo') + '.\n'
894 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
895 light_green('home 7') + ' will show 7 tweets.\n'
896 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
897 light_green('mentions 7') + ' will show 7 mention tweets.\n'
898 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
8bc30efd 899 magenta('@mdo') + '.\n'
c075e6dc 900 usage += s * 2 + light_green('view @mdo') + \
8bc30efd 901 ' will show ' + magenta('@mdo') + '\'s home.\n'
c075e6dc
O
902 usage += s * 2 + light_green('s #AKB48') + ' will search for "' + \
903 light_yellow('AKB48') + '" and return 5 newest tweet.\n'
8bc30efd 904
1f24a05a 905 # Tweet
8bc30efd 906 usage += '\n'
907 usage += s + grey(u'\u266A' + ' Tweets \n')
c075e6dc
O
908 usage += s * 2 + light_green('t oops ') + \
909 'will tweet "' + light_yellow('oops') + '" immediately.\n'
7e4ccbf3 910 usage += s * 2 + \
c075e6dc
O
911 light_green('rt 12 ') + ' will retweet to tweet with ' + \
912 light_yellow('[id=12]') + '.\n'
1f24a05a 913 usage += s * 2 + \
80b70d60
O
914 light_green('quote 12 ') + ' will quote the tweet with ' + \
915 light_yellow('[id=12]') + '. If no extra text is added, ' + \
916 'the quote will be canceled.\n'
917 usage += s * 2 + \
c075e6dc
O
918 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
919 light_yellow('[id=12]') + '.\n'
920 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
921 light_yellow('oops') + '" to tweet with ' + \
922 light_yellow('[id=12]') + '.\n'
7e4ccbf3 923 usage += s * 2 + \
c075e6dc
O
924 light_green('fav 12 ') + ' will favorite the tweet with ' + \
925 light_yellow('[id=12]') + '.\n'
7e4ccbf3 926 usage += s * 2 + \
c075e6dc
O
927 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
928 light_yellow('[id=12]') + '.\n'
8bc30efd 929 usage += s * 2 + \
c075e6dc
O
930 light_green('del 12 ') + ' will delete tweet with ' + \
931 light_yellow('[id=12]') + '.\n'
932 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
933 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
80b70d60
O
934 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
935 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
8bc30efd 936
5b2c4faf 937 # Direct message
8bc30efd 938 usage += '\n'
939 usage += s + grey(u'\u266A' + ' Direct messages \n')
c075e6dc
O
940 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
941 light_green('inbox 7') + ' will show newest 7 messages.\n'
942 usage += s * 2 + light_green('sent') + ' will show sent messages. ' + \
943 light_green('sent 7') + ' will show newest 7 messages.\n'
944 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
8bc30efd 945 magenta('@dtvd88') + '.\n'
c075e6dc
O
946 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
947 light_yellow('[message_id=5]') + '.\n'
8bc30efd 948
949 # Follower and following
950 usage += '\n'
951 usage += s + grey(u'\u266A' + ' Fiends and followers \n')
952 usage += s * 2 + \
c075e6dc 953 light_green('ls fl') + \
8bc30efd 954 ' will list all followers (people who are following you).\n'
955 usage += s * 2 + \
c075e6dc 956 light_green('ls fr') + \
8bc30efd 957 ' will list all friends (people who you are following).\n'
c075e6dc 958 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
305ce127 959 magenta('@dtvd88') + '.\n'
c075e6dc 960 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
305ce127 961 magenta('@dtvd88') + '.\n'
c075e6dc 962 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
5b2c4faf 963 magenta('@dtvd88') + '.\n'
c075e6dc 964 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
5b2c4faf 965 magenta('@dtvd88') + '.\n'
c075e6dc
O
966 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
967 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
305ce127 968 magenta('@dtvd88') + '.\n'
c075e6dc 969 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
305ce127 970 magenta('@dtvd88') + '.\n'
c075e6dc 971 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
305ce127 972 magenta('@dtvd88') + ' as a spam account.\n'
8bc30efd 973
8bc30efd 974 # Switch
975 usage += '\n'
976 usage += s + grey(u'\u266A' + ' Switching streams \n')
c075e6dc 977 usage += s * 2 + light_green('switch public #AKB') + \
48a25fe8 978 ' will switch to public stream and follow "' + \
c075e6dc
O
979 light_yellow('AKB') + '" keyword.\n'
980 usage += s * 2 + light_green('switch mine') + \
48a25fe8 981 ' will switch to your personal stream.\n'
c075e6dc 982 usage += s * 2 + light_green('switch mine -f ') + \
48a25fe8 983 ' will prompt to enter the filter.\n'
c075e6dc 984 usage += s * 3 + light_yellow('Only nicks') + \
48a25fe8 985 ' filter will decide nicks will be INCLUDE ONLY.\n'
c075e6dc 986 usage += s * 3 + light_yellow('Ignore nicks') + \
48a25fe8 987 ' filter will decide nicks will be EXCLUDE.\n'
c075e6dc 988 usage += s * 2 + light_green('switch mine -d') + \
48a25fe8 989 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
48a25fe8 990
1f24a05a 991 # Smart shell
992 usage += '\n'
993 usage += s + grey(u'\u266A' + ' Smart shell\n')
c075e6dc 994 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1f24a05a 995 'will be evaluate by Python interpreter.\n'
c075e6dc 996 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1f24a05a 997 ' for current month.\n'
998
999 # Screening
1000 usage += '\n'
1001 usage += s + grey(u'\u266A' + ' Screening \n')
c075e6dc
O
1002 usage += s * 2 + light_green('theme') + ' will list available theme.' + \
1003 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
632c6fa5 1004 ' theme immediately.\n'
c075e6dc
O
1005 usage += s * 2 + light_green('h') + ' will show this help again.\n'
1006 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
1007 usage += s * 2 + light_green('q') + ' will quit.\n'
1f24a05a 1008
8bc30efd 1009 # End
1010 usage += '\n'
7e4ccbf3 1011 usage += s + '-' * (int(w) - 4) + '\n'
8bc30efd 1012 usage += s + 'Have fun and hang tight! \n'
f405a7d0 1013 printNicely(usage)
f405a7d0
O
1014
1015
843647ad 1016def clear():
f405a7d0 1017 """
7b674cef 1018 Clear screen
f405a7d0 1019 """
843647ad 1020 os.system('clear')
f405a7d0
O
1021
1022
843647ad 1023def quit():
b8dda704
O
1024 """
1025 Exit all
1026 """
f5677fb1 1027 save_history()
8e633322 1028 os.system('rm -rf rainbow.db')
843647ad
O
1029 os.kill(g['stream_pid'], signal.SIGKILL)
1030 sys.exit()
b8dda704
O
1031
1032
94a5f62e 1033def reset():
f405a7d0 1034 """
94a5f62e 1035 Reset prefix of line
f405a7d0 1036 """
c91f75f2 1037 if g['reset']:
e3885f55 1038 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
c91f75f2 1039 g['reset'] = False
d0a726d6 1040 try:
2a0cabee
O
1041 printNicely(str(eval(g['cmd'])))
1042 except Exception:
d0a726d6 1043 pass
54277114
O
1044
1045
94a5f62e 1046def process(cmd):
54277114 1047 """
94a5f62e 1048 Process switch
54277114 1049 """
94a5f62e 1050 return dict(zip(
1051 cmdset,
b2b933a9 1052 [
42fde775 1053 switch,
4592d231 1054 trend,
b2b933a9 1055 home,
1056 view,
305ce127 1057 mentions,
b2b933a9 1058 tweet,
1059 retweet,
80b70d60 1060 quote,
1f24a05a 1061 allretweet,
7e4ccbf3 1062 favorite,
b2b933a9 1063 reply,
1064 delete,
7e4ccbf3 1065 unfavorite,
b2b933a9 1066 search,
305ce127 1067 message,
f5677fb1 1068 show,
80bb2040 1069 urlopen,
0f6e4daf 1070 list,
305ce127 1071 inbox,
1072 sent,
1073 trash,
e2b81717 1074 whois,
f5677fb1
O
1075 follow,
1076 unfollow,
5b2c4faf 1077 mute,
1078 unmute,
1079 muting,
305ce127 1080 block,
1081 unblock,
1082 report,
813a5d80 1083 cal,
632c6fa5 1084 theme,
b2b933a9 1085 help,
1086 clear,
1087 quit
1088 ]
94a5f62e 1089 )).get(cmd, reset)
1090
1091
1092def listen():
42fde775 1093 """
1094 Listen to user's input
1095 """
d51b4107
O
1096 d = dict(zip(
1097 cmdset,
1098 [
affcb149 1099 ['public', 'mine'], # switch
4592d231 1100 [], # trend
7e4ccbf3 1101 [], # home
1102 ['@'], # view
305ce127 1103 [], # mentions
7e4ccbf3 1104 [], # tweet
1105 [], # retweet
80b70d60 1106 [], # quote
1f24a05a 1107 [], # allretweet
f5677fb1 1108 [], # favorite
7e4ccbf3 1109 [], # reply
1110 [], # delete
f5677fb1 1111 [], # unfavorite
7e4ccbf3 1112 ['#'], # search
305ce127 1113 ['@'], # message
f5677fb1 1114 ['image'], # show image
80b70d60 1115 [''], # open url
305ce127 1116 ['fl', 'fr'], # list
1117 [], # inbox
1118 [], # sent
1119 [], # trash
e2b81717 1120 ['@'], # whois
affcb149
O
1121 ['@'], # follow
1122 ['@'], # unfollow
5b2c4faf 1123 ['@'], # mute
1124 ['@'], # unmute
1125 ['@'], # muting
305ce127 1126 ['@'], # block
1127 ['@'], # unblock
1128 ['@'], # report
813a5d80 1129 [], # cal
1c8a5082 1130 g['themes'] + ['current_as_default'], # theme
7e4ccbf3 1131 [], # help
1132 [], # clear
1133 [], # quit
d51b4107 1134 ]
7e4ccbf3 1135 ))
d51b4107 1136 init_interactive_shell(d)
f5677fb1 1137 read_history()
819569e8 1138 reset()
b2b933a9 1139 while True:
1dd312f5
O
1140 if g['prefix']:
1141 line = raw_input(g['decorated_name'])
1142 else:
1143 line = raw_input()
843647ad
O
1144 try:
1145 cmd = line.split()[0]
1146 except:
1147 cmd = ''
d0a726d6 1148 g['cmd'] = cmd
f405a7d0 1149 # Save cmd to global variable and call process
b8c1f42a
O
1150 try:
1151 g['stuff'] = ' '.join(line.split()[1:])
1152 process(cmd)()
80bb2040
O
1153 except Exception,e :
1154 print e
b8c1f42a
O
1155 printNicely(red('OMG something is wrong with Twitter right now.'))
1156 # Not redisplay prefix
7e4ccbf3 1157 if cmd in ['switch', 't', 'rt', 'rep']:
1dd312f5
O
1158 g['prefix'] = False
1159 else:
1160 g['prefix'] = True
54277114
O
1161
1162
42fde775 1163def stream(domain, args, name='Rainbow Stream'):
54277114 1164 """
f405a7d0 1165 Track the stream
54277114 1166 """
d51b4107 1167
54277114 1168 # The Logo
42fde775 1169 art_dict = {
632c6fa5
O
1170 c['USER_DOMAIN']: name,
1171 c['PUBLIC_DOMAIN']: args.track_keywords,
1172 c['SITE_DOMAIN']: 'Site Stream',
42fde775 1173 }
c075e6dc
O
1174 if g['ascii_art']:
1175 ascii_art(art_dict[domain])
d51b4107 1176
91476ec3
O
1177 # These arguments are optional:
1178 stream_args = dict(
1179 timeout=args.timeout,
1180 block=not args.no_block,
1181 heartbeat_timeout=args.heartbeat_timeout)
1182
1183 # Track keyword
1184 query_args = dict()
1185 if args.track_keywords:
1186 query_args['track'] = args.track_keywords
1187
1188 # Get stream
2a6238f5 1189 stream = TwitterStream(
22be990e 1190 auth=authen(),
42fde775 1191 domain=domain,
2a6238f5 1192 **stream_args)
91476ec3 1193
2a0cabee
O
1194 try:
1195 if domain == c['USER_DOMAIN']:
1196 tweet_iter = stream.user(**query_args)
1197 elif domain == c['SITE_DOMAIN']:
1198 tweet_iter = stream.site(**query_args)
42fde775 1199 else:
2a0cabee
O
1200 if args.track_keywords:
1201 tweet_iter = stream.statuses.filter(**query_args)
1202 else:
1203 tweet_iter = stream.statuses.sample()
42fde775 1204
2a0cabee 1205 # Iterate over the stream.
72c02928
VNM
1206 for tweet in tweet_iter:
1207 if tweet is None:
1208 printNicely("-- None --")
1209 elif tweet is Timeout:
1210 printNicely("-- Timeout --")
1211 elif tweet is HeartbeatTimeout:
1212 printNicely("-- Heartbeat Timeout --")
1213 elif tweet is Hangup:
1214 printNicely("-- Hangup --")
1215 elif tweet.get('text'):
1216 draw(
1217 t=tweet,
1218 iot=args.image_on_term,
1219 keyword=args.track_keywords,
1220 fil=args.filter,
1221 ig=args.ignore,
1222 )
2a0cabee
O
1223 except TwitterHTTPError:
1224 printNicely('')
c075e6dc 1225 printNicely(
2a0cabee 1226 magenta("We have maximum connection problem with twitter'stream API right now :("))
54277114
O
1227
1228
1229def fly():
1230 """
1231 Main function
1232 """
42fde775 1233 # Spawn stream process
1234 args = parse_arguments()
2a0cabee
O
1235 try:
1236 get_decorated_name()
1237
1238 except TwitterHTTPError:
1239 printNicely('')
1240 printNicely(
1241 magenta("I'm afraid we have maximum connection problem with twitter right now :("))
1242 printNicely(magenta("Let's try again later."))
1243 save_history()
1244 os.system('rm -rf rainbow.db')
1245 sys.exit()
1246
c075e6dc
O
1247 p = Process(
1248 target=stream,
1249 args=(
2a0cabee 1250 c['USER_DOMAIN'],
c075e6dc
O
1251 args,
1252 g['original_name']))
42fde775 1253 p.start()
1254
1255 # Start listen process
819569e8 1256 time.sleep(0.5)
c91f75f2 1257 g['reset'] = True
1dd312f5 1258 g['prefix'] = True
f405a7d0 1259 g['stream_pid'] = p.pid
c1fa7c94 1260 g['iot'] = args.image_on_term
0f6e4daf 1261 listen()