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