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