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