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