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