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