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