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