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