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