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