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