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