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