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