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