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