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