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