enabled tweet chains. see issue #163
[rainbowstream.git] / rainbowstream / rainbow.py
CommitLineData
b2b933a9 1import os
2import os.path
3import sys
4import signal
5import argparse
6import time
92983945 7import threading
991c30af 8import requests
80b70d60 9import webbrowser
7a8a52fc 10import traceback
6e8fb961 11import pkg_resources
c426a344 12import socks
13import socket
337fa242 14import re
91476ec3 15
a65129d4 16from io import BytesIO
91476ec3 17from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup
54277114 18from twitter.api import *
91476ec3 19from twitter.oauth import OAuth, read_token_file
8c840a83 20from twitter.oauth_dance import oauth_dance
91476ec3 21from twitter.util import printNicely
91476ec3 22
337fa242
L
23from pocket import Pocket
24
7500d90b 25from .draw import *
2a6238f5
O
26from .colors import *
27from .config import *
777c52d4 28from .consumer import *
94a5f62e 29from .interactive import *
991c30af 30from .c_image import *
c3bab4ef 31from .py3patch import *
841260fe 32from .emoji import *
9e38891f 33from .util import *
c3bab4ef 34
531f5682 35# Global values
f405a7d0 36g = {}
531f5682 37
92983945 38# Lock for streams
92983945
BS
39StreamLock = threading.Lock()
40
c075e6dc 41
91476ec3
O
42def parse_arguments():
43 """
44 Parse the arguments
45 """
91476ec3 46 parser = argparse.ArgumentParser(description=__doc__ or "")
434c2160 47 parser.add_argument(
48 '-s',
49 '--stream',
fd2da0c3 50 default="mine",
434c2160 51 help='Default stream after program start. (Default: mine)')
2a6238f5
O
52 parser.add_argument(
53 '-to',
54 '--timeout',
55 help='Timeout for the stream (seconds).')
56 parser.add_argument(
2a6238f5
O
57 '-tt',
58 '--track-keywords',
59 help='Search the stream for specific text.')
d51b4107
O
60 parser.add_argument(
61 '-fil',
62 '--filter',
63 help='Filter specific screen_name.')
64 parser.add_argument(
65 '-ig',
66 '--ignore',
67 help='Ignore specific screen_name.')
88af38d8 68 parser.add_argument(
c1fa7c94
O
69 '-iot',
70 '--image-on-term',
71 action='store_true',
72 help='Display all image on terminal.')
c426a344 73 parser.add_argument(
69b6ab70
JH
74 '-24',
75 '--color-24bit',
76 action='store_true',
77 help='Display images using 24bit color codes.')
78 parser.add_argument(
c426a344 79 '-ph',
80 '--proxy-host',
81 help='Use HTTP/SOCKS proxy for network connections.')
82 parser.add_argument(
83 '-pp',
84 '--proxy-port',
85 default=8080,
86 help='HTTP/SOCKS proxy port (Default: 8080).')
87 parser.add_argument(
88 '-pt',
89 '--proxy-type',
89e6bf4b 90 default='SOCKS5',
c426a344 91 help='Proxy type (HTTP, SOCKS4, SOCKS5; Default: SOCKS5).')
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'])
94a5f62e 333 for tweet in reversed(t.statuses.home_timeline(count=num)):
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
O
401 for tweet in reversed(
402 t.statuses.user_timeline(count=num, screen_name=user[1:])):
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(
f54a7f38 419 t.statuses.user_timeline(count=num, screen_name=g['original_name'])):
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 = {}
911 # Cursor loop
912 while next_cursor != 0:
913 list = getattr(t, d[target]).list(
914 screen_name=name,
915 cursor=next_cursor,
916 skip_status=True,
917 include_entities=False,
918 )
919 for u in list['users']:
920 rel[u['name']] = '@' + u['screen_name']
921 next_cursor = list['next_cursor']
922 # Print out result
923 printNicely('All: ' + str(len(rel)) + ' ' + d[target] + '.')
924 for name in rel:
925 user = ' ' + cycle_color(name)
926 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
927 printNicely(user)
e2b81717
O
928
929
f5677fb1 930def follow():
843647ad 931 """
f5677fb1 932 Follow a user
843647ad
O
933 """
934 t = Twitter(auth=authen())
f5677fb1 935 screen_name = g['stuff'].split()[0]
b8c1f42a
O
936 if screen_name.startswith('@'):
937 t.friendships.create(screen_name=screen_name[1:], follow=True)
938 printNicely(green('You are following ' + screen_name + ' now!'))
f5677fb1 939 else:
b8c1f42a 940 printNicely(red('A name should begin with a \'@\''))
f5677fb1
O
941
942
943def unfollow():
944 """
945 Unfollow a user
946 """
947 t = Twitter(auth=authen())
948 screen_name = g['stuff'].split()[0]
b8c1f42a
O
949 if screen_name.startswith('@'):
950 t.friendships.destroy(
951 screen_name=screen_name[1:],
952 include_entities=False)
953 printNicely(green('Unfollow ' + screen_name + ' success!'))
f5677fb1 954 else:
b8c1f42a 955 printNicely(red('A name should begin with a \'@\''))
843647ad
O
956
957
5b2c4faf 958def mute():
959 """
960 Mute a user
961 """
962 t = Twitter(auth=authen())
963 try:
964 screen_name = g['stuff'].split()[0]
965 except:
966 printNicely(red('A name should be specified. '))
967 return
968 if screen_name.startswith('@'):
e3927852
O
969 try:
970 rel = t.mutes.users.create(screen_name=screen_name[1:])
971 if isinstance(rel, dict):
972 printNicely(green(screen_name + ' is muted.'))
5caaaccb 973 c['IGNORE_LIST'] += [screen_name]
e3927852
O
974 c['IGNORE_LIST'] = list(set(c['IGNORE_LIST']))
975 else:
976 printNicely(red(rel))
977 except:
7a8a52fc 978 debug_option()
e3927852 979 printNicely(red('Something is wrong, can not mute now :('))
5b2c4faf 980 else:
981 printNicely(red('A name should begin with a \'@\''))
982
983
984def unmute():
985 """
986 Unmute a user
987 """
988 t = Twitter(auth=authen())
989 try:
990 screen_name = g['stuff'].split()[0]
991 except:
992 printNicely(red('A name should be specified. '))
993 return
994 if screen_name.startswith('@'):
e3927852
O
995 try:
996 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
997 if isinstance(rel, dict):
998 printNicely(green(screen_name + ' is unmuted.'))
999 c['IGNORE_LIST'].remove(screen_name)
1000 else:
1001 printNicely(red(rel))
1002 except:
1003 printNicely(red('Maybe you are not muting this person ?'))
5b2c4faf 1004 else:
1005 printNicely(red('A name should begin with a \'@\''))
1006
1007
1008def muting():
1009 """
1010 List muting user
1011 """
e3927852
O
1012 # Get dict of muting users
1013 md = build_mute_dict(dict_data=True)
1014 printNicely('All: ' + str(len(md)) + ' people.')
1015 for name in md:
1016 user = ' ' + cycle_color(md[name])
1017 user += color_func(c['TWEET']['nick'])(' ' + name + ' ')
5b2c4faf 1018 printNicely(user)
e3927852
O
1019 # Update from Twitter
1020 c['IGNORE_LIST'] = [n for n in md]
5b2c4faf 1021
1022
305ce127 1023def block():
1024 """
1025 Block a user
1026 """
1027 t = Twitter(auth=authen())
1028 screen_name = g['stuff'].split()[0]
b8c1f42a
O
1029 if screen_name.startswith('@'):
1030 t.blocks.create(
5b2c4faf 1031 screen_name=screen_name[1:],
1032 include_entities=False,
1033 skip_status=True)
b8c1f42a 1034 printNicely(green('You blocked ' + screen_name + '.'))
305ce127 1035 else:
b8c1f42a 1036 printNicely(red('A name should begin with a \'@\''))
305ce127 1037
1038
1039def unblock():
1040 """
1041 Unblock a user
1042 """
1043 t = Twitter(auth=authen())
1044 screen_name = g['stuff'].split()[0]
b8c1f42a
O
1045 if screen_name.startswith('@'):
1046 t.blocks.destroy(
1047 screen_name=screen_name[1:],
1048 include_entities=False,
1049 skip_status=True)
1050 printNicely(green('Unblock ' + screen_name + ' success!'))
305ce127 1051 else:
b8c1f42a 1052 printNicely(red('A name should begin with a \'@\''))
305ce127 1053
1054
1055def report():
1056 """
1057 Report a user as a spam account
1058 """
1059 t = Twitter(auth=authen())
1060 screen_name = g['stuff'].split()[0]
b8c1f42a
O
1061 if screen_name.startswith('@'):
1062 t.users.report_spam(
1063 screen_name=screen_name[1:])
1064 printNicely(green('You reported ' + screen_name + '.'))
305ce127 1065 else:
1066 printNicely(red('Sorry I can\'t understand.'))
1067
1068
8b8566d1
O
1069def get_slug():
1070 """
ee4c94b1 1071 Get slug
8b8566d1 1072 """
a8c5fce4 1073 # Get list name
be4dba0e 1074 list_name = raw_input(
1075 light_magenta('Give me the list\'s name ("@owner/list_name"): ', rl=True))
8b8566d1
O
1076 # Get list name and owner
1077 try:
1078 owner, slug = list_name.split('/')
1079 if slug.startswith('@'):
1080 slug = slug[1:]
1081 return owner, slug
1082 except:
a8c5fce4
O
1083 printNicely(
1084 light_magenta('List name should follow "@owner/list_name" format.'))
8b8566d1
O
1085 raise Exception('Wrong list name')
1086
56702c8f 1087
434c2160 1088def check_slug(list_name):
1089 """
1090 Check slug
1091 """
1092 # Get list name and owner
1093 try:
1094 owner, slug = list_name.split('/')
1095 if slug.startswith('@'):
1096 slug = slug[1:]
1097 return owner, slug
1098 except:
1099 printNicely(
1100 light_magenta('List name should follow "@owner/list_name" format.'))
1101 raise Exception('Wrong list name')
8b8566d1 1102
56702c8f 1103
2d341029
O
1104def show_lists(t):
1105 """
422dd385 1106 List list
2d341029
O
1107 """
1108 rel = t.lists.list(screen_name=g['original_name'])
1109 if rel:
1110 print_list(rel)
1111 else:
1112 printNicely(light_magenta('You belong to no lists :)'))
1113
1114
1115def list_home(t):
1116 """
1117 List home
1118 """
8b8566d1 1119 owner, slug = get_slug()
2d341029 1120 res = t.lists.statuses(
422dd385
O
1121 slug=slug,
1122 owner_screen_name=owner,
1123 count=c['LIST_MAX'],
2d341029 1124 include_entities=False)
7304256c 1125 for tweet in reversed(res):
2d341029
O
1126 draw(t=tweet)
1127 printNicely('')
1128
1129
1130def list_members(t):
1131 """
1132 List members
1133 """
8b8566d1 1134 owner, slug = get_slug()
422dd385 1135 # Get members
2d341029
O
1136 rel = {}
1137 next_cursor = -1
422dd385 1138 while next_cursor != 0:
2d341029 1139 m = t.lists.members(
422dd385
O
1140 slug=slug,
1141 owner_screen_name=owner,
1142 cursor=next_cursor,
2d341029
O
1143 include_entities=False)
1144 for u in m['users']:
1145 rel[u['name']] = '@' + u['screen_name']
1146 next_cursor = m['next_cursor']
1147 printNicely('All: ' + str(len(rel)) + ' members.')
1148 for name in rel:
1149 user = ' ' + cycle_color(name)
422dd385 1150 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
2d341029
O
1151 printNicely(user)
1152
1153
1154def list_subscribers(t):
1155 """
1156 List subscribers
1157 """
8b8566d1 1158 owner, slug = get_slug()
422dd385 1159 # Get subscribers
2d341029
O
1160 rel = {}
1161 next_cursor = -1
422dd385 1162 while next_cursor != 0:
2d341029 1163 m = t.lists.subscribers(
422dd385
O
1164 slug=slug,
1165 owner_screen_name=owner,
1166 cursor=next_cursor,
2d341029
O
1167 include_entities=False)
1168 for u in m['users']:
1169 rel[u['name']] = '@' + u['screen_name']
1170 next_cursor = m['next_cursor']
1171 printNicely('All: ' + str(len(rel)) + ' subscribers.')
1172 for name in rel:
1173 user = ' ' + cycle_color(name)
422dd385 1174 user += color_func(c['TWEET']['nick'])(' ' + rel[name] + ' ')
2d341029
O
1175 printNicely(user)
1176
1177
422dd385
O
1178def list_add(t):
1179 """
1180 Add specific user to a list
1181 """
8b8566d1 1182 owner, slug = get_slug()
422dd385 1183 # Add
be4dba0e 1184 user_name = raw_input(
1185 light_magenta(
1186 'Give me name of the newbie: ',
1187 rl=True))
422dd385
O
1188 if user_name.startswith('@'):
1189 user_name = user_name[1:]
1190 try:
1191 t.lists.members.create(
1192 slug=slug,
1193 owner_screen_name=owner,
1194 screen_name=user_name)
d6cc4c67 1195 printNicely(green('Added.'))
422dd385 1196 except:
7a8a52fc 1197 debug_option()
422dd385
O
1198 printNicely(light_magenta('I\'m sorry we can not add him/her.'))
1199
1200
2d341029
O
1201def list_remove(t):
1202 """
1203 Remove specific user from a list
1204 """
8b8566d1 1205 owner, slug = get_slug()
2d341029 1206 # Remove
be4dba0e 1207 user_name = raw_input(
1208 light_magenta(
1209 'Give me name of the unlucky one: ',
1210 rl=True))
422dd385
O
1211 if user_name.startswith('@'):
1212 user_name = user_name[1:]
2d341029
O
1213 try:
1214 t.lists.members.destroy(
422dd385
O
1215 slug=slug,
1216 owner_screen_name=owner,
1217 screen_name=user_name)
d6cc4c67 1218 printNicely(green('Gone.'))
422dd385 1219 except:
7a8a52fc 1220 debug_option()
422dd385
O
1221 printNicely(light_magenta('I\'m sorry we can not remove him/her.'))
1222
1223
1224def list_subscribe(t):
1225 """
1226 Subscribe to a list
1227 """
8b8566d1 1228 owner, slug = get_slug()
422dd385
O
1229 # Subscribe
1230 try:
1231 t.lists.subscribers.create(
1232 slug=slug,
1233 owner_screen_name=owner)
d6cc4c67 1234 printNicely(green('Done.'))
422dd385 1235 except:
7a8a52fc 1236 debug_option()
422dd385
O
1237 printNicely(
1238 light_magenta('I\'m sorry you can not subscribe to this list.'))
1239
1240
1241def list_unsubscribe(t):
1242 """
1243 Unsubscribe a list
1244 """
8b8566d1 1245 owner, slug = get_slug()
422dd385
O
1246 # Subscribe
1247 try:
1248 t.lists.subscribers.destroy(
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 unsubscribe to this list.'))
1256
1257
1258def list_own(t):
1259 """
1260 List own
1261 """
1262 rel = []
1263 next_cursor = -1
1264 while next_cursor != 0:
1265 res = t.lists.ownerships(
1266 screen_name=g['original_name'],
1267 cursor=next_cursor)
1268 rel += res['lists']
1269 next_cursor = res['next_cursor']
1270 if rel:
1271 print_list(rel)
1272 else:
1273 printNicely(light_magenta('You own no lists :)'))
1274
1275
1276def list_new(t):
1277 """
1278 Create a new list
1279 """
be4dba0e 1280 name = raw_input(light_magenta('New list\'s name: ', rl=True))
1281 mode = raw_input(
1282 light_magenta(
1283 'New list\'s mode (public/private): ',
1284 rl=True))
1285 description = raw_input(
1286 light_magenta(
1287 'New list\'s description: ',
1288 rl=True))
422dd385
O
1289 try:
1290 t.lists.create(
1291 name=name,
1292 mode=mode,
1293 description=description)
d6cc4c67 1294 printNicely(green(name + ' list is created.'))
422dd385 1295 except:
7a8a52fc 1296 debug_option()
422dd385
O
1297 printNicely(red('Oops something is wrong with Twitter :('))
1298
1299
1300def list_update(t):
1301 """
1302 Update a list
1303 """
be4dba0e 1304 slug = raw_input(
1305 light_magenta(
1306 'Your list that you want to update: ',
1307 rl=True))
1308 name = raw_input(
1309 light_magenta(
1310 'Update name (leave blank to unchange): ',
1311 rl=True))
1312 mode = raw_input(light_magenta('Update mode (public/private): ', rl=True))
1313 description = raw_input(light_magenta('Update description: ', rl=True))
422dd385
O
1314 try:
1315 if name:
1316 t.lists.update(
1317 slug='-'.join(slug.split()),
1318 owner_screen_name=g['original_name'],
1319 name=name,
1320 mode=mode,
1321 description=description)
1322 else:
1323 t.lists.update(
1324 slug=slug,
1325 owner_screen_name=g['original_name'],
1326 mode=mode,
1327 description=description)
d6cc4c67 1328 printNicely(green(slug + ' list is updated.'))
3c85d8fc 1329 except:
7a8a52fc 1330 debug_option()
422dd385
O
1331 printNicely(red('Oops something is wrong with Twitter :('))
1332
1333
1334def list_delete(t):
1335 """
1336 Delete a list
1337 """
be4dba0e 1338 slug = raw_input(
1339 light_magenta(
1340 'Your list that you want to delete: ',
1341 rl=True))
422dd385
O
1342 try:
1343 t.lists.destroy(
1344 slug='-'.join(slug.split()),
1345 owner_screen_name=g['original_name'])
d6cc4c67 1346 printNicely(green(slug + ' list is deleted.'))
2d341029 1347 except:
7a8a52fc 1348 debug_option()
422dd385 1349 printNicely(red('Oops something is wrong with Twitter :('))
2d341029
O
1350
1351
e3927852 1352def twitterlist():
2d341029
O
1353 """
1354 Twitter's list
1355 """
1356 t = Twitter(auth=authen())
1357 # List all lists or base on action
1358 try:
1359 g['list_action'] = g['stuff'].split()[0]
1360 except:
1361 show_lists(t)
1362 return
422dd385 1363 # Sub-function
2d341029
O
1364 action_ary = {
1365 'home': list_home,
1366 'all_mem': list_members,
1367 'all_sub': list_subscribers,
422dd385 1368 'add': list_add,
2d341029 1369 'rm': list_remove,
422dd385
O
1370 'sub': list_subscribe,
1371 'unsub': list_unsubscribe,
1372 'own': list_own,
1373 'new': list_new,
1374 'update': list_update,
1375 'del': list_delete,
2d341029
O
1376 }
1377 try:
1378 return action_ary[g['list_action']](t)
3c85d8fc 1379 except:
8b8566d1 1380 printNicely(red('Please try again.'))
2d341029
O
1381
1382
fd87ddac
O
1383def switch():
1384 """
1385 Switch stream
1386 """
1387 try:
1388 target = g['stuff'].split()[0]
1389 # Filter and ignore
1390 args = parse_arguments()
1391 try:
1392 if g['stuff'].split()[-1] == '-f':
1393 guide = 'To ignore an option, just hit Enter key.'
1394 printNicely(light_magenta(guide))
1395 only = raw_input('Only nicks [Ex: @xxx,@yy]: ')
1396 ignore = raw_input('Ignore nicks [Ex: @xxx,@yy]: ')
f5ea6385 1397 args.filter = list(filter(None, only.split(',')))
1398 args.ignore = list(filter(None, ignore.split(',')))
fd87ddac
O
1399 except:
1400 printNicely(red('Sorry, wrong format.'))
1401 return
56702c8f 1402 # Kill old thread
1403 g['stream_stop'] = True
69b6ab70 1404 try:
56702c8f 1405 stuff = g['stuff'].split()[1]
1406 except:
1407 stuff = None
1408 # Spawn new thread
1409 spawn_dict = {
1410 'public': spawn_public_stream,
1411 'list': spawn_list_stream,
1412 'mine': spawn_personal_stream,
1413 }
1414 spawn_dict.get(target)(args, stuff)
050a294e 1415 except:
ee4c94b1 1416 debug_option()
fd87ddac
O
1417 printNicely(red('Sorry I can\'t understand.'))
1418
1419
813a5d80 1420def cal():
1421 """
1422 Unix's command `cal`
1423 """
1424 # Format
1425 rel = os.popen('cal').read().split('\n')
1426 month = rel.pop(0)
813a5d80 1427 date = rel.pop(0)
2a0cabee 1428 show_calendar(month, date, rel)
813a5d80 1429
1430
fd87ddac
O
1431def theme():
1432 """
1433 List and change theme
1434 """
1435 if not g['stuff']:
1436 # List themes
1437 for theme in g['themes']:
1438 line = light_magenta(theme)
1439 if c['THEME'] == theme:
1440 line = ' ' * 2 + light_yellow('* ') + line
1441 else:
1442 line = ' ' * 4 + line
1443 printNicely(line)
1444 else:
1445 # Change theme
1446 try:
1447 # Load new theme
1448 c['THEME'] = reload_theme(g['stuff'], c['THEME'])
1449 # Redefine decorated_name
1450 g['decorated_name'] = lambda x: color_func(
1451 c['DECORATED_NAME'])(
1452 '[' + x + ']: ')
1453 printNicely(green('Theme changed.'))
1454 except:
1455 printNicely(red('No such theme exists.'))
1456
1457
29fd0be6
O
1458def config():
1459 """
1460 Browse and change config
1461 """
1462 all_config = get_all_config()
1463 g['stuff'] = g['stuff'].strip()
1464 # List all config
1465 if not g['stuff']:
1466 for k in all_config:
a8c5fce4 1467 line = ' ' * 2 + \
d6cc4c67 1468 green(k) + ': ' + light_yellow(str(all_config[k]))
29fd0be6
O
1469 printNicely(line)
1470 guide = 'Detailed explanation can be found at ' + \
a8c5fce4
O
1471 color_func(c['TWEET']['link'])(
1472 'http://rainbowstream.readthedocs.org/en/latest/#config-explanation')
29fd0be6
O
1473 printNicely(guide)
1474 # Print specific config
1475 elif len(g['stuff'].split()) == 1:
1476 if g['stuff'] in all_config:
1477 k = g['stuff']
a8c5fce4 1478 line = ' ' * 2 + \
d6cc4c67 1479 green(k) + ': ' + light_yellow(str(all_config[k]))
29fd0be6
O
1480 printNicely(line)
1481 else:
fe9bb33b 1482 printNicely(red('No such config key.'))
29fd0be6
O
1483 # Print specific config's default value
1484 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'default':
1485 key = g['stuff'].split()[0]
fe9bb33b 1486 try:
1487 value = get_default_config(key)
d6cc4c67 1488 line = ' ' * 2 + green(key) + ': ' + light_magenta(value)
fe9bb33b 1489 printNicely(line)
050a294e
O
1490 except:
1491 debug_option()
1492 printNicely(red('Just can not get the default.'))
fe9bb33b 1493 # Delete specific config key in config file
1494 elif len(g['stuff'].split()) == 2 and g['stuff'].split()[-1] == 'drop':
1495 key = g['stuff'].split()[0]
1496 try:
1497 delete_config(key)
d6cc4c67 1498 printNicely(green('Config key is dropped.'))
050a294e
O
1499 except:
1500 debug_option()
1501 printNicely(red('Just can not drop the key.'))
29fd0be6 1502 # Set specific config
a8c5fce4 1503 elif len(g['stuff'].split()) == 3 and g['stuff'].split()[1] == '=':
29fd0be6
O
1504 key = g['stuff'].split()[0]
1505 value = g['stuff'].split()[-1]
ceec8593 1506 if key == 'THEME' and not validate_theme(value):
1507 printNicely(red('Invalid theme\'s value.'))
1508 return
3c01ba57 1509 try:
a8c5fce4 1510 set_config(key, value)
050a294e 1511 # Keys that needs to be apply immediately
ceec8593 1512 if key == 'THEME':
baec5f50 1513 c['THEME'] = reload_theme(value, c['THEME'])
ceec8593 1514 g['decorated_name'] = lambda x: color_func(
a8e71259 1515 c['DECORATED_NAME'])('[' + x + ']: ')
050a294e 1516 elif key == 'PREFIX':
063f6bb1 1517 g['PREFIX'] = u2str(emojize(format_prefix(
1518 listname=g['listname'],
1519 keyword=g['keyword']
1520 )))
a8e71259 1521 reload_config()
d6cc4c67 1522 printNicely(green('Updated successfully.'))
050a294e
O
1523 except:
1524 debug_option()
1525 printNicely(red('Just can not set the key.'))
29fd0be6 1526 else:
337fa242 1527 printNicely(light_magenta('Sorry I can\'t understand.'))
29fd0be6
O
1528
1529
2d341029 1530def help_discover():
f405a7d0 1531 """
2d341029 1532 Discover the world
f405a7d0 1533 """
7e4ccbf3 1534 s = ' ' * 2
1f24a05a 1535 # Discover the world
2d341029 1536 usage = '\n'
8bc30efd 1537 usage += s + grey(u'\u266A' + ' Discover the world \n')
c075e6dc
O
1538 usage += s * 2 + light_green('trend') + ' will show global trending topics. ' + \
1539 'You can try ' + light_green('trend US') + ' or ' + \
1540 light_green('trend JP Tokyo') + '.\n'
1541 usage += s * 2 + light_green('home') + ' will show your timeline. ' + \
1542 light_green('home 7') + ' will show 7 tweets.\n'
67eccba1
MKN
1543 usage += s * 2 + light_green('me') + ' will show your latest tweets. ' + \
1544 light_green('me 2') + ' will show your last 2 tweets.\n'
99cd1fba
O
1545 usage += s * 2 + \
1546 light_green('notification') + ' will show your recent notification.\n'
c075e6dc
O
1547 usage += s * 2 + light_green('mentions') + ' will show mentions timeline. ' + \
1548 light_green('mentions 7') + ' will show 7 mention tweets.\n'
1549 usage += s * 2 + light_green('whois @mdo') + ' will show profile of ' + \
8bc30efd 1550 magenta('@mdo') + '.\n'
c075e6dc 1551 usage += s * 2 + light_green('view @mdo') + \
8bc30efd 1552 ' will show ' + magenta('@mdo') + '\'s home.\n'
03e08f86
O
1553 usage += s * 2 + light_green('s AKB48') + ' will search for "' + \
1554 light_yellow('AKB48') + '" and return 5 newest tweet. ' + \
1555 'Search can be performed with or without hashtag.\n'
2d341029
O
1556 printNicely(usage)
1557
8bc30efd 1558
2d341029
O
1559def help_tweets():
1560 """
1561 Tweets
1562 """
1563 s = ' ' * 2
1f24a05a 1564 # Tweet
2d341029 1565 usage = '\n'
8bc30efd 1566 usage += s + grey(u'\u266A' + ' Tweets \n')
c075e6dc
O
1567 usage += s * 2 + light_green('t oops ') + \
1568 'will tweet "' + light_yellow('oops') + '" immediately.\n'
7e4ccbf3 1569 usage += s * 2 + \
c075e6dc
O
1570 light_green('rt 12 ') + ' will retweet to tweet with ' + \
1571 light_yellow('[id=12]') + '.\n'
1f24a05a 1572 usage += s * 2 + \
80b70d60
O
1573 light_green('quote 12 ') + ' will quote the tweet with ' + \
1574 light_yellow('[id=12]') + '. If no extra text is added, ' + \
1575 'the quote will be canceled.\n'
1576 usage += s * 2 + \
c075e6dc
O
1577 light_green('allrt 12 20 ') + ' will list 20 newest retweet of the tweet with ' + \
1578 light_yellow('[id=12]') + '.\n'
fd87ddac
O
1579 usage += s * 2 + light_green('conversation 12') + ' will show the chain of ' + \
1580 'replies prior to the tweet with ' + light_yellow('[id=12]') + '.\n'
c075e6dc 1581 usage += s * 2 + light_green('rep 12 oops') + ' will reply "' + \
ba6c09d1
O
1582 light_yellow('oops') + '" to the owner of the tweet with ' + \
1583 light_yellow('[id=12]') + '.\n'
1584 usage += s * 2 + light_green('repall 12 oops') + ' will reply "' + \
1585 light_yellow('oops') + '" to all people in the tweet with ' + \
c075e6dc 1586 light_yellow('[id=12]') + '.\n'
7e4ccbf3 1587 usage += s * 2 + \
c075e6dc
O
1588 light_green('fav 12 ') + ' will favorite the tweet with ' + \
1589 light_yellow('[id=12]') + '.\n'
7e4ccbf3 1590 usage += s * 2 + \
c075e6dc
O
1591 light_green('ufav 12 ') + ' will unfavorite tweet with ' + \
1592 light_yellow('[id=12]') + '.\n'
8bc30efd 1593 usage += s * 2 + \
66fe9f75 1594 light_green('share 12 ') + ' will get the direct link of the tweet with ' + \
1595 light_yellow('[id=12]') + '.\n'
413857b5 1596 usage += s * 2 + \
c075e6dc
O
1597 light_green('del 12 ') + ' will delete tweet with ' + \
1598 light_yellow('[id=12]') + '.\n'
1599 usage += s * 2 + light_green('show image 12') + ' will show image in tweet with ' + \
1600 light_yellow('[id=12]') + ' in your OS\'s image viewer.\n'
80b70d60
O
1601 usage += s * 2 + light_green('open 12') + ' will open url in tweet with ' + \
1602 light_yellow('[id=12]') + ' in your OS\'s default browser.\n'
337fa242
L
1603 usage += s * 2 + light_green('pt 12') + ' will add tweet with ' + \
1604 light_yellow('[id=12]') + ' in your Pocket list.\n'
2d341029 1605 printNicely(usage)
8bc30efd 1606
2d341029
O
1607
1608def help_messages():
1609 """
1610 Messages
1611 """
1612 s = ' ' * 2
5b2c4faf 1613 # Direct message
2d341029 1614 usage = '\n'
8bc30efd 1615 usage += s + grey(u'\u266A' + ' Direct messages \n')
c075e6dc
O
1616 usage += s * 2 + light_green('inbox') + ' will show inbox messages. ' + \
1617 light_green('inbox 7') + ' will show newest 7 messages.\n'
03c0d30b 1618 usage += s * 2 + light_green('thread 2') + ' will show full thread with ' + \
1619 light_yellow('[thread_id=2]') + '.\n'
c075e6dc 1620 usage += s * 2 + light_green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
8bc30efd 1621 magenta('@dtvd88') + '.\n'
c075e6dc
O
1622 usage += s * 2 + light_green('trash 5') + ' will remove message with ' + \
1623 light_yellow('[message_id=5]') + '.\n'
2d341029 1624 printNicely(usage)
8bc30efd 1625
2d341029
O
1626
1627def help_friends_and_followers():
1628 """
1629 Friends and Followers
1630 """
1631 s = ' ' * 2
8bc30efd 1632 # Follower and following
2d341029 1633 usage = '\n'
cdccb0d6 1634 usage += s + grey(u'\u266A' + ' Friends and followers \n')
8bc30efd 1635 usage += s * 2 + \
c075e6dc 1636 light_green('ls fl') + \
8bc30efd 1637 ' will list all followers (people who are following you).\n'
1638 usage += s * 2 + \
c075e6dc 1639 light_green('ls fr') + \
8bc30efd 1640 ' will list all friends (people who you are following).\n'
c075e6dc 1641 usage += s * 2 + light_green('fl @dtvd88') + ' will follow ' + \
305ce127 1642 magenta('@dtvd88') + '.\n'
c075e6dc 1643 usage += s * 2 + light_green('ufl @dtvd88') + ' will unfollow ' + \
305ce127 1644 magenta('@dtvd88') + '.\n'
c075e6dc 1645 usage += s * 2 + light_green('mute @dtvd88') + ' will mute ' + \
5b2c4faf 1646 magenta('@dtvd88') + '.\n'
c075e6dc 1647 usage += s * 2 + light_green('unmute @dtvd88') + ' will unmute ' + \
5b2c4faf 1648 magenta('@dtvd88') + '.\n'
c075e6dc
O
1649 usage += s * 2 + light_green('muting') + ' will list muting users.\n'
1650 usage += s * 2 + light_green('block @dtvd88') + ' will block ' + \
305ce127 1651 magenta('@dtvd88') + '.\n'
c075e6dc 1652 usage += s * 2 + light_green('unblock @dtvd88') + ' will unblock ' + \
305ce127 1653 magenta('@dtvd88') + '.\n'
c075e6dc 1654 usage += s * 2 + light_green('report @dtvd88') + ' will report ' + \
305ce127 1655 magenta('@dtvd88') + ' as a spam account.\n'
2d341029
O
1656 printNicely(usage)
1657
1658
1659def help_list():
1660 """
1661 Lists
1662 """
1663 s = ' ' * 2
1664 # Twitter list
1665 usage = '\n'
1666 usage += s + grey(u'\u266A' + ' Twitter list\n')
1667 usage += s * 2 + light_green('list') + \
1668 ' will show all lists you are belong to.\n'
1669 usage += s * 2 + light_green('list home') + \
bef33491 1670 ' will show timeline of list. You will be asked for list\'s name.\n'
a65bd34c 1671 usage += s * 2 + light_green('list all_mem') + \
2d341029 1672 ' will show list\'s all members.\n'
a65bd34c 1673 usage += s * 2 + light_green('list all_sub') + \
2d341029 1674 ' will show list\'s all subscribers.\n'
422dd385
O
1675 usage += s * 2 + light_green('list add') + \
1676 ' will add specific person to a list owned by you.' + \
1677 ' You will be asked for list\'s name and person\'s name.\n'
2d341029
O
1678 usage += s * 2 + light_green('list rm') + \
1679 ' will remove specific person from a list owned by you.' + \
1680 ' You will be asked for list\'s name and person\'s name.\n'
422dd385
O
1681 usage += s * 2 + light_green('list sub') + \
1682 ' will subscribe you to a specific list.\n'
1683 usage += s * 2 + light_green('list unsub') + \
1684 ' will unsubscribe you from a specific list.\n'
1685 usage += s * 2 + light_green('list own') + \
1686 ' will show all list owned by you.\n'
1687 usage += s * 2 + light_green('list new') + \
1688 ' will create a new list.\n'
1689 usage += s * 2 + light_green('list update') + \
1690 ' will update a list owned by you.\n'
1691 usage += s * 2 + light_green('list del') + \
1692 ' will delete a list owned by you.\n'
2d341029 1693 printNicely(usage)
8bc30efd 1694
2d341029
O
1695
1696def help_stream():
1697 """
1698 Stream switch
1699 """
1700 s = ' ' * 2
8bc30efd 1701 # Switch
2d341029 1702 usage = '\n'
8bc30efd 1703 usage += s + grey(u'\u266A' + ' Switching streams \n')
c075e6dc 1704 usage += s * 2 + light_green('switch public #AKB') + \
48a25fe8 1705 ' will switch to public stream and follow "' + \
c075e6dc
O
1706 light_yellow('AKB') + '" keyword.\n'
1707 usage += s * 2 + light_green('switch mine') + \
48a25fe8 1708 ' will switch to your personal stream.\n'
c075e6dc 1709 usage += s * 2 + light_green('switch mine -f ') + \
48a25fe8 1710 ' will prompt to enter the filter.\n'
c075e6dc 1711 usage += s * 3 + light_yellow('Only nicks') + \
48a25fe8 1712 ' filter will decide nicks will be INCLUDE ONLY.\n'
c075e6dc 1713 usage += s * 3 + light_yellow('Ignore nicks') + \
48a25fe8 1714 ' filter will decide nicks will be EXCLUDE.\n'
ee4c94b1
O
1715 usage += s * 2 + light_green('switch list') + \
1716 ' will switch to a Twitter list\'s stream. You will be asked for list name\n'
2d341029
O
1717 printNicely(usage)
1718
1719
1720def help():
1721 """
1722 Help
1723 """
1724 s = ' ' * 2
1725 h, w = os.popen('stty size', 'r').read().split()
2d341029
O
1726 # Start
1727 usage = '\n'
1728 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
1729 usage += s + '-' * (int(w) - 4) + '\n'
1730 usage += s + 'You are ' + \
1731 light_yellow('already') + ' on your personal stream.\n'
1732 usage += s + 'Any update from Twitter will show up ' + \
1733 light_yellow('immediately') + '.\n'
37d1047f 1734 usage += s + 'In addition, following commands are available right now:\n'
2d341029
O
1735 # Twitter help section
1736 usage += '\n'
1737 usage += s + grey(u'\u266A' + ' Twitter help\n')
1738 usage += s * 2 + light_green('h discover') + \
1739 ' will show help for discover commands.\n'
1740 usage += s * 2 + light_green('h tweets') + \
1741 ' will show help for tweets commands.\n'
1742 usage += s * 2 + light_green('h messages') + \
1743 ' will show help for messages commands.\n'
1744 usage += s * 2 + light_green('h friends_and_followers') + \
1745 ' will show help for friends and followers commands.\n'
1746 usage += s * 2 + light_green('h list') + \
1747 ' will show help for list commands.\n'
1748 usage += s * 2 + light_green('h stream') + \
1749 ' will show help for stream commands.\n'
1f24a05a 1750 # Smart shell
1751 usage += '\n'
1752 usage += s + grey(u'\u266A' + ' Smart shell\n')
c075e6dc 1753 usage += s * 2 + light_green('111111 * 9 / 7') + ' or any math expression ' + \
1f24a05a 1754 'will be evaluate by Python interpreter.\n'
c075e6dc 1755 usage += s * 2 + 'Even ' + light_green('cal') + ' will show the calendar' + \
1f24a05a 1756 ' for current month.\n'
29fd0be6 1757 # Config
1f24a05a 1758 usage += '\n'
29fd0be6
O
1759 usage += s + grey(u'\u266A' + ' Config \n')
1760 usage += s * 2 + light_green('theme') + ' will list available theme. ' + \
c075e6dc 1761 light_green('theme monokai') + ' will apply ' + light_yellow('monokai') + \
632c6fa5 1762 ' theme immediately.\n'
29fd0be6
O
1763 usage += s * 2 + light_green('config') + ' will list all config.\n'
1764 usage += s * 3 + \
1765 light_green('config ASCII_ART') + ' will output current value of ' +\
a8c5fce4 1766 light_yellow('ASCII_ART') + ' config key.\n'
29fd0be6 1767 usage += s * 3 + \
fe9bb33b 1768 light_green('config TREND_MAX default') + ' will output default value of ' + \
1769 light_yellow('TREND_MAX') + ' config key.\n'
1770 usage += s * 3 + \
1771 light_green('config CUSTOM_CONFIG drop') + ' will drop ' + \
1772 light_yellow('CUSTOM_CONFIG') + ' config key.\n'
29fd0be6 1773 usage += s * 3 + \
fe9bb33b 1774 light_green('config IMAGE_ON_TERM = true') + ' will set value of ' + \
1775 light_yellow('IMAGE_ON_TERM') + ' config key to ' + \
1776 light_yellow('True') + '.\n'
29fd0be6
O
1777 # Screening
1778 usage += '\n'
1779 usage += s + grey(u'\u266A' + ' Screening \n')
c075e6dc 1780 usage += s * 2 + light_green('h') + ' will show this help again.\n'
d6cc4c67
O
1781 usage += s * 2 + light_green('p') + ' will pause the stream.\n'
1782 usage += s * 2 + light_green('r') + ' will unpause the stream.\n'
c075e6dc 1783 usage += s * 2 + light_green('c') + ' will clear the screen.\n'
806f42df 1784 usage += s * 2 + light_green('v') + ' will show version info.\n'
c075e6dc 1785 usage += s * 2 + light_green('q') + ' will quit.\n'
8bc30efd 1786 # End
1787 usage += '\n'
7e4ccbf3 1788 usage += s + '-' * (int(w) - 4) + '\n'
8bc30efd 1789 usage += s + 'Have fun and hang tight! \n'
2d341029
O
1790 # Show help
1791 d = {
422dd385
O
1792 'discover': help_discover,
1793 'tweets': help_tweets,
1794 'messages': help_messages,
1795 'friends_and_followers': help_friends_and_followers,
1796 'list': help_list,
1797 'stream': help_stream,
2d341029
O
1798 }
1799 if g['stuff']:
baec5f50 1800 d.get(
1801 g['stuff'].strip(),
1802 lambda: printNicely(red('No such command.'))
3d48702f 1803 )()
2d341029
O
1804 else:
1805 printNicely(usage)
f405a7d0
O
1806
1807
d6cc4c67
O
1808def pause():
1809 """
1810 Pause stream display
1811 """
4dc385b5 1812 g['pause'] = True
d6cc4c67
O
1813 printNicely(green('Stream is paused'))
1814
1815
1816def replay():
1817 """
1818 Replay stream
1819 """
4dc385b5 1820 g['pause'] = False
d6cc4c67
O
1821 printNicely(green('Stream is running back now'))
1822
1823
843647ad 1824def clear():
f405a7d0 1825 """
7b674cef 1826 Clear screen
f405a7d0 1827 """
843647ad 1828 os.system('clear')
f405a7d0
O
1829
1830
843647ad 1831def quit():
b8dda704
O
1832 """
1833 Exit all
1834 """
4c025026 1835 try:
1836 save_history()
4c025026 1837 printNicely(green('See you next time :)'))
1838 except:
1839 pass
843647ad 1840 sys.exit()
b8dda704
O
1841
1842
94a5f62e 1843def reset():
f405a7d0 1844 """
94a5f62e 1845 Reset prefix of line
f405a7d0 1846 """
c91f75f2 1847 if g['reset']:
a8e71259 1848 if c.get('USER_JSON_ERROR'):
1849 printNicely(red('Your ~/.rainbow_config.json is messed up:'))
1850 printNicely(red('>>> ' + c['USER_JSON_ERROR']))
1851 printNicely('')
e3885f55 1852 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
c91f75f2 1853 g['reset'] = False
d0a726d6 1854 try:
779b0640 1855 printNicely(str(eval(g['cmd'])))
2a0cabee 1856 except Exception:
d0a726d6 1857 pass
54277114
O
1858
1859
f1c1dfea
O
1860# Command set
1861cmdset = [
1862 'switch',
1863 'trend',
1864 'home',
99cd1fba 1865 'notification',
f1c1dfea
O
1866 'view',
1867 'mentions',
1868 't',
1869 'rt',
1870 'quote',
67eccba1 1871 'me',
f1c1dfea 1872 'allrt',
fd87ddac 1873 'conversation',
f1c1dfea
O
1874 'fav',
1875 'rep',
ba6c09d1 1876 'repall',
f1c1dfea
O
1877 'del',
1878 'ufav',
413857b5 1879 'share',
f1c1dfea
O
1880 's',
1881 'mes',
1882 'show',
1883 'open',
1884 'ls',
1885 'inbox',
67c663f8 1886 'thread',
f1c1dfea
O
1887 'trash',
1888 'whois',
1889 'fl',
1890 'ufl',
1891 'mute',
1892 'unmute',
1893 'muting',
1894 'block',
1895 'unblock',
1896 'report',
1897 'list',
1898 'cal',
1899 'config',
1900 'theme',
1901 'h',
1902 'p',
1903 'r',
1904 'c',
806f42df 1905 'v',
bf766c7b 1906 'q',
337fa242 1907 'pt',
f1c1dfea
O
1908]
1909
1910# Handle function set
1911funcset = [
1912 switch,
1913 trend,
1914 home,
99cd1fba 1915 notification,
f1c1dfea
O
1916 view,
1917 mentions,
1918 tweet,
1919 retweet,
1920 quote,
084a4cbc 1921 view_my_tweets,
f1c1dfea 1922 allretweet,
fd87ddac 1923 conversation,
f1c1dfea
O
1924 favorite,
1925 reply,
ba6c09d1 1926 reply_all,
f1c1dfea
O
1927 delete,
1928 unfavorite,
413857b5 1929 share,
f1c1dfea
O
1930 search,
1931 message,
1932 show,
1933 urlopen,
1934 ls,
1935 inbox,
67c663f8 1936 thread,
f1c1dfea
O
1937 trash,
1938 whois,
1939 follow,
1940 unfollow,
1941 mute,
1942 unmute,
1943 muting,
1944 block,
1945 unblock,
1946 report,
1947 twitterlist,
1948 cal,
1949 config,
1950 theme,
1951 help,
1952 pause,
1953 replay,
1954 clear,
bf766c7b 1955 upgrade_center,
806f42df 1956 quit,
337fa242 1957 pocket,
f1c1dfea
O
1958]
1959
1960
94a5f62e 1961def process(cmd):
54277114 1962 """
94a5f62e 1963 Process switch
54277114 1964 """
f1c1dfea 1965 return dict(zip(cmdset, funcset)).get(cmd, reset)
94a5f62e 1966
1967
1968def listen():
42fde775 1969 """
1970 Listen to user's input
1971 """
d51b4107
O
1972 d = dict(zip(
1973 cmdset,
1974 [
ee4c94b1 1975 ['public', 'mine', 'list'], # switch
4592d231 1976 [], # trend
7e4ccbf3 1977 [], # home
99cd1fba 1978 [], # notification
7e4ccbf3 1979 ['@'], # view
305ce127 1980 [], # mentions
7e4ccbf3 1981 [], # tweet
1982 [], # retweet
80b70d60 1983 [], # quote
b9eeb635 1984 [], # view_my_tweets
1f24a05a 1985 [], # allretweet
fd87ddac 1986 [], # conversation
f5677fb1 1987 [], # favorite
7e4ccbf3 1988 [], # reply
ba6c09d1 1989 [], # reply_all
7e4ccbf3 1990 [], # delete
f5677fb1 1991 [], # unfavorite
413857b5 1992 [], # url
7e4ccbf3 1993 ['#'], # search
305ce127 1994 ['@'], # message
f5677fb1 1995 ['image'], # show image
80b70d60 1996 [''], # open url
305ce127 1997 ['fl', 'fr'], # list
1998 [], # inbox
03c0d30b 1999 [i for i in g['message_threads']], # sent
305ce127 2000 [], # trash
e2b81717 2001 ['@'], # whois
affcb149
O
2002 ['@'], # follow
2003 ['@'], # unfollow
5b2c4faf 2004 ['@'], # mute
2005 ['@'], # unmute
2006 ['@'], # muting
305ce127 2007 ['@'], # block
2008 ['@'], # unblock
2009 ['@'], # report
422dd385
O
2010 [
2011 'home',
2012 'all_mem',
2013 'all_sub',
2014 'add',
2015 'rm',
2016 'sub',
2017 'unsub',
2018 'own',
2019 'new',
2020 'update',
2021 'del'
2022 ], # list
813a5d80 2023 [], # cal
a8c5fce4 2024 [key for key in dict(get_all_config())], # config
ceec8593 2025 g['themes'], # theme
422dd385
O
2026 [
2027 'discover',
2028 'tweets',
2029 'messages',
2030 'friends_and_followers',
2031 'list',
2032 'stream'
2033 ], # help
d6cc4c67
O
2034 [], # pause
2035 [], # reconnect
7e4ccbf3 2036 [], # clear
7cfb8af4 2037 [], # version
806f42df 2038 [], # quit
337fa242 2039 [], # pocket
d51b4107 2040 ]
7e4ccbf3 2041 ))
d51b4107 2042 init_interactive_shell(d)
f5677fb1 2043 read_history()
819569e8 2044 reset()
b2b933a9 2045 while True:
b8c1f42a 2046 try:
39b8e6b3
O
2047 # raw_input
2048 if g['prefix']:
aa452ee9 2049 # Only use PREFIX as a string with raw_input
c285decf 2050 line = raw_input(g['decorated_name'](g['PREFIX']))
39b8e6b3
O
2051 else:
2052 line = raw_input()
2053 # Save cmd to compare with readline buffer
2054 g['cmd'] = line.strip()
2055 # Get short cmd to pass to handle function
2056 try:
2057 cmd = line.split()[0]
2058 except:
2059 cmd = ''
9683e61d 2060 # Lock the semaphore
99b52f5f 2061 c['lock'] = True
9683e61d 2062 # Save cmd to global variable and call process
b8c1f42a 2063 g['stuff'] = ' '.join(line.split()[1:])
fc460124 2064 # Check tweet length
9b48127c 2065 # Process the command
2066 process(cmd)()
2067 # Not re-display
2068 if cmd in ['switch', 't', 'rt', 'rep']:
2069 g['prefix'] = False
2070 else:
2071 g['prefix'] = True
39b8e6b3
O
2072 except EOFError:
2073 printNicely('')
9b48127c 2074 except TwitterHTTPError as e:
2075 detail_twitter_error(e)
eadd85a8 2076 except Exception:
7a8a52fc 2077 debug_option()
9b48127c 2078 printNicely(red('OMG something is wrong with Twitter API right now.'))
5b06fbff
MKN
2079 finally:
2080 # Release the semaphore lock
2081 c['lock'] = False
fc460124
BJ
2082
2083
47cee703
O
2084def reconn_notice():
2085 """
2086 Notice when Hangup or Timeout
2087 """
f07cfb6b 2088 guide = light_magenta('You can use ') + \
2089 light_green('switch') + \
2090 light_magenta(' command to return to your stream.\n')
2091 guide += light_magenta('Type ') + \
2092 light_green('h stream') + \
2093 light_magenta(' for more details.')
47cee703 2094 printNicely(guide)
211e8be1 2095 sys.stdout.write(g['decorated_name'](g['PREFIX']))
47cee703
O
2096 sys.stdout.flush()
2097
2098
42fde775 2099def stream(domain, args, name='Rainbow Stream'):
54277114 2100 """
f405a7d0 2101 Track the stream
54277114 2102 """
54277114 2103 # The Logo
42fde775 2104 art_dict = {
632c6fa5 2105 c['USER_DOMAIN']: name,
3e06aa8f 2106 c['PUBLIC_DOMAIN']: args.track_keywords or 'Global',
1f2f6159 2107 c['SITE_DOMAIN']: name,
42fde775 2108 }
687567eb 2109 if c['ASCII_ART']:
56702c8f 2110 ascii_art(art_dict.get(domain, name))
91476ec3
O
2111 # These arguments are optional:
2112 stream_args = dict(
e3927852 2113 timeout=0.5, # To check g['stream_stop'] after each 0.5 s
cb45dc23 2114 block=True,
2115 heartbeat_timeout=c['HEARTBEAT_TIMEOUT'] * 60)
91476ec3
O
2116 # Track keyword
2117 query_args = dict()
2118 if args.track_keywords:
2119 query_args['track'] = args.track_keywords
91476ec3 2120 # Get stream
2a6238f5 2121 stream = TwitterStream(
22be990e 2122 auth=authen(),
42fde775 2123 domain=domain,
2a6238f5 2124 **stream_args)
2a0cabee
O
2125 try:
2126 if domain == c['USER_DOMAIN']:
2127 tweet_iter = stream.user(**query_args)
2128 elif domain == c['SITE_DOMAIN']:
2129 tweet_iter = stream.site(**query_args)
42fde775 2130 else:
2a0cabee
O
2131 if args.track_keywords:
2132 tweet_iter = stream.statuses.filter(**query_args)
2133 else:
2134 tweet_iter = stream.statuses.sample()
92983945
BS
2135 # Block new stream until other one exits
2136 StreamLock.acquire()
2137 g['stream_stop'] = False
e53e2c70 2138 last_tweet_time = time.time()
72c02928
VNM
2139 for tweet in tweet_iter:
2140 if tweet is None:
f07cfb6b 2141 printNicely('-- None --')
72c02928 2142 elif tweet is Timeout:
47cee703
O
2143 # Because the stream check for each 0.3s
2144 # so we shouldn't output anything here
335e7803
O
2145 if(g['stream_stop']):
2146 StreamLock.release()
2147 break
72c02928 2148 elif tweet is HeartbeatTimeout:
f07cfb6b 2149 printNicely('-- Heartbeat Timeout --')
47cee703 2150 reconn_notice()
8715dda0
O
2151 StreamLock.release()
2152 break
72c02928 2153 elif tweet is Hangup:
f07cfb6b 2154 printNicely('-- Hangup --')
47cee703
O
2155 reconn_notice()
2156 StreamLock.release()
2157 break
72c02928 2158 elif tweet.get('text'):
84b41f58
O
2159 # Slow down the stream by STREAM_DELAY config key
2160 if time.time() - last_tweet_time < c['STREAM_DELAY']:
2161 continue
2162 last_tweet_time = time.time()
2163 # Check the semaphore pause and lock (stream process only)
2164 if g['pause']:
2165 continue
2166 while c['lock']:
2167 time.sleep(0.5)
2168 # Draw the tweet
2169 draw(
2170 t=tweet,
2171 keyword=args.track_keywords,
2172 humanize=False,
2173 fil=args.filter,
2174 ig=args.ignore,
2175 )
2176 # Current readline buffer
2177 current_buffer = readline.get_line_buffer().strip()
2178 # There is an unexpected behaviour in MacOSX readline + Python 2:
2179 # after completely delete a word after typing it,
2180 # somehow readline buffer still contains
2181 # the 1st character of that word
2182 if current_buffer and g['cmd'] != current_buffer:
2183 sys.stdout.write(
211e8be1 2184 g['decorated_name'](g['PREFIX']) + current_buffer)
84b41f58
O
2185 sys.stdout.flush()
2186 elif not c['HIDE_PROMPT']:
211e8be1 2187 sys.stdout.write(g['decorated_name'](g['PREFIX']))
84b41f58 2188 sys.stdout.flush()
14db58c7 2189 elif tweet.get('direct_message'):
4dc385b5
O
2190 # Check the semaphore pause and lock (stream process only)
2191 if g['pause']:
2192 continue
2193 while c['lock']:
2194 time.sleep(0.5)
2195 print_message(tweet['direct_message'])
99cd1fba 2196 elif tweet.get('event'):
d7d9c67c 2197 c['events'].append(tweet)
99cd1fba 2198 print_event(tweet)
742266f8 2199 except TwitterHTTPError as e:
2a0cabee 2200 printNicely('')
c075e6dc 2201 printNicely(
f07cfb6b 2202 magenta('We have connection problem with twitter stream API right now :('))
9e38891f 2203 detail_twitter_error(e)
211e8be1 2204 sys.stdout.write(g['decorated_name'](g['PREFIX']))
62058715 2205 sys.stdout.flush()
bb68c687 2206 except (URLError):
ba6c09d1
O
2207 printNicely(
2208 magenta('There seems to be a connection problem.'))
2209 save_history()
2210 sys.exit()
54277114
O
2211
2212
56702c8f 2213def spawn_public_stream(args, keyword=None):
2214 """
2215 Spawn a new public stream
2216 """
2217 # Only set keyword if specified
2218 if keyword:
2219 if keyword[0] == '#':
2220 keyword = keyword[1:]
2221 args.track_keywords = keyword
3e06aa8f 2222 g['keyword'] = keyword
2223 else:
2224 g['keyword'] = 'Global'
56702c8f 2225 g['PREFIX'] = u2str(emojize(format_prefix(keyword=g['keyword'])))
3e06aa8f 2226 g['listname'] = ''
56702c8f 2227 # Start new thread
2228 th = threading.Thread(
2229 target=stream,
2230 args=(
2231 c['PUBLIC_DOMAIN'],
2232 args))
2233 th.daemon = True
2234 th.start()
2235
2236
2237def spawn_list_stream(args, stuff=None):
2238 """
2239 Spawn a new list stream
2240 """
2241 try:
2242 owner, slug = check_slug(stuff)
2243 except:
2244 owner, slug = get_slug()
3e06aa8f 2245
56702c8f 2246 # Force python 2 not redraw readline buffer
2247 listname = '/'.join([owner, slug])
2248 # Set the listname variable
2249 # and reset tracked keyword
2250 g['listname'] = listname
2251 g['keyword'] = ''
2252 g['PREFIX'] = g['cmd'] = u2str(emojize(format_prefix(
2253 listname=g['listname']
2254 )))
2255 printNicely(light_yellow('getting list members ...'))
2256 # Get members
2257 t = Twitter(auth=authen())
2258 members = []
2259 next_cursor = -1
2260 while next_cursor != 0:
2261 m = t.lists.members(
2262 slug=slug,
2263 owner_screen_name=owner,
2264 cursor=next_cursor,
2265 include_entities=False)
2266 for u in m['users']:
2267 members.append('@' + u['screen_name'])
2268 next_cursor = m['next_cursor']
2269 printNicely(light_yellow('... done.'))
2270 # Build thread filter array
2271 args.filter = members
2272 # Start new thread
2273 th = threading.Thread(
2274 target=stream,
2275 args=(
2276 c['USER_DOMAIN'],
2277 args,
2278 slug))
2279 th.daemon = True
2280 th.start()
2281 printNicely('')
2282 if args.filter:
2283 printNicely(cyan('Include: ' + str(len(args.filter)) + ' people.'))
2284 if args.ignore:
2285 printNicely(red('Ignore: ' + str(len(args.ignore)) + ' people.'))
2286 printNicely('')
2287
2288
2289def spawn_personal_stream(args, stuff=None):
2290 """
2291 Spawn a new personal stream
2292 """
2293 # Reset the tracked keyword and listname
2294 g['keyword'] = g['listname'] = ''
2295 # Reset prefix
2296 g['PREFIX'] = u2str(emojize(format_prefix()))
2297 # Start new thread
2298 th = threading.Thread(
2299 target=stream,
2300 args=(
2301 c['USER_DOMAIN'],
2302 args,
2303 g['original_name']))
2304 th.daemon = True
2305 th.start()
2306
2307
54277114
O
2308def fly():
2309 """
2310 Main function
2311 """
531f5682 2312 # Initial
42fde775 2313 args = parse_arguments()
2a0cabee 2314 try:
a65129d4 2315 proxy_connect(args)
fe9bb33b 2316 init(args)
a65129d4 2317 # Twitter API connection problem
742266f8 2318 except TwitterHTTPError as e:
2a0cabee
O
2319 printNicely('')
2320 printNicely(
f07cfb6b 2321 magenta('We have connection problem with twitter REST API right now :('))
9e38891f 2322 detail_twitter_error(e)
2a0cabee 2323 save_history()
2a0cabee 2324 sys.exit()
a65129d4
O
2325 # Proxy connection problem
2326 except (socks.ProxyConnectionError, URLError):
c426a344 2327 printNicely(
f07cfb6b 2328 magenta('There seems to be a connection problem.'))
c426a344 2329 printNicely(
f07cfb6b 2330 magenta('You might want to check your proxy settings (host, port and type)!'))
c426a344 2331 save_history()
2332 sys.exit()
2333
92983945 2334 # Spawn stream thread
434c2160 2335 target = args.stream.split()[0]
fd2da0c3 2336 if target == 'mine':
56702c8f 2337 spawn_personal_stream(args)
3e06aa8f 2338 else:
434c2160 2339 try:
56702c8f 2340 stuff = args.stream.split()[1]
434c2160 2341 except:
3e06aa8f 2342 stuff = None
2343 spawn_dict = {
2344 'public': spawn_public_stream,
2345 'list': spawn_list_stream,
2346 }
2347 spawn_dict.get(target)(args, stuff)
2348
42fde775 2349 # Start listen process
819569e8 2350 time.sleep(0.5)
c91f75f2 2351 g['reset'] = True
1dd312f5 2352 g['prefix'] = True
0f6e4daf 2353 listen()