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