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