Update README.rst
[rainbowstream.git] / rainbowstream / rainbow.py
CommitLineData
91476ec3
O
1"""
2Colorful user's timeline stream
3"""
78b81730
O
4from multiprocessing import Process
5from dateutil import parser
6
b2b933a9 7import os
8import os.path
9import sys
10import signal
11import argparse
12import time
13import datetime
991c30af 14import requests
91476ec3 15
91476ec3 16from twitter.stream import TwitterStream, Timeout, HeartbeatTimeout, Hangup
54277114 17from twitter.api import *
91476ec3 18from twitter.oauth import OAuth, read_token_file
8c840a83 19from twitter.oauth_dance import oauth_dance
91476ec3 20from twitter.util import printNicely
991c30af 21from StringIO import StringIO
91476ec3 22
2a6238f5
O
23from .colors import *
24from .config import *
777c52d4 25from .consumer import *
94a5f62e 26from .interactive import *
18cab06a 27from .db import *
991c30af 28from .c_image import *
2a6238f5 29
f405a7d0 30g = {}
18cab06a 31db = RainbowDB()
94a5f62e 32cmdset = [
42fde775 33 'switch',
4592d231 34 'trend',
94a5f62e 35 'home',
36 'view',
305ce127 37 'mentions',
94a5f62e 38 't',
39 'rt',
1f24a05a 40 'allrt',
7e4ccbf3 41 'fav',
94a5f62e 42 'rep',
43 'del',
7e4ccbf3 44 'ufav',
94a5f62e 45 's',
305ce127 46 'mes',
f5677fb1 47 'show',
0f6e4daf 48 'ls',
305ce127 49 'inbox',
50 'sent',
51 'trash',
e2b81717 52 'whois',
94a5f62e 53 'fl',
f5677fb1 54 'ufl',
5b2c4faf 55 'mute',
56 'unmute',
57 'muting',
305ce127 58 'block',
59 'unblock',
60 'report',
813a5d80 61 'cal',
94a5f62e 62 'h',
63 'c',
64 'q'
65]
22be990e 66
b2b933a9 67
c1fa7c94 68def draw(t, iot=False, keyword=None, fil=[], ig=[]):
91476ec3
O
69 """
70 Draw the rainbow
71 """
d51b4107 72
91476ec3 73 # Retrieve tweet
829cc2d8 74 tid = t['id']
91476ec3
O
75 text = t['text']
76 screen_name = t['user']['screen_name']
77 name = t['user']['name']
78 created_at = t['created_at']
7e4ccbf3 79 favorited = t['favorited']
91476ec3 80 date = parser.parse(created_at)
e20af1c3 81 date = date - datetime.timedelta(seconds=time.timezone)
82 clock = date.strftime('%Y/%m/%d %H:%M:%S')
91476ec3 83
991c30af
O
84 # Get expanded url
85 try:
86 expanded_url = []
87 url = []
88 urls = t['entities']['urls']
89 for u in urls:
90 expanded_url.append(u['expanded_url'])
91 url.append(u['url'])
92 except:
93 expanded_url = None
94 url = None
95
96 # Get media
97 try:
98 media_url = []
99 media = t['entities']['media']
100 for m in media:
f5677fb1 101 media_url.append(m['media_url'])
991c30af
O
102 except:
103 media_url = None
104
d51b4107
O
105 # Filter and ignore
106 screen_name = '@' + screen_name
107 if fil and screen_name not in fil:
108 return
109 if ig and screen_name in ig:
110 return
111
305ce127 112 # Get rainbow id
113 res = db.tweet_to_rainbow_query(tid)
18cab06a 114 if not res:
305ce127 115 db.tweet_store(tid)
116 res = db.tweet_to_rainbow_query(tid)
18cab06a
O
117 rid = res[0].rainbow_id
118
91476ec3 119 # Format info
d51b4107 120 user = cycle_color(name) + grey(' ' + screen_name + ' ')
7e4ccbf3 121 meta = grey('[' + clock + '] [id=' + str(rid) + '] ')
122 if favorited:
123 meta = meta + green(u'\u2605')
8c840a83 124 tweet = text.split()
991c30af
O
125 # Replace url
126 if expanded_url:
127 for index in range(len(expanded_url)):
128 tweet = map(
129 lambda x: expanded_url[index] if x == url[index] else x,
130 tweet)
b8dda704 131 # Highlight RT
2a6238f5 132 tweet = map(lambda x: grey(x) if x == 'RT' else x, tweet)
b8dda704 133 # Highlight screen_name
2a6238f5 134 tweet = map(lambda x: cycle_color(x) if x[0] == '@' else x, tweet)
b8dda704 135 # Highlight link
147bb4a2 136 tweet = map(lambda x: cyan(x) if x[0:4] == 'http' else x, tweet)
b8dda704 137 # Highlight search keyword
7a431249 138 if keyword:
22be990e 139 tweet = map(
140 lambda x: on_yellow(x) if
141 ''.join(c for c in x if c.isalnum()).lower() == keyword.lower()
142 else x,
143 tweet
144 )
991c30af 145 # Recreate tweet
8c840a83 146 tweet = ' '.join(tweet)
91476ec3
O
147
148 # Draw rainbow
06773ffe 149 line1 = u"{u:>{uw}}:".format(
2a6238f5
O
150 u=user,
151 uw=len(user) + 2,
91476ec3 152 )
06773ffe 153 line2 = u"{c:>{cw}}".format(
829cc2d8
O
154 c=meta,
155 cw=len(meta) + 2,
06773ffe
O
156 )
157 line3 = ' ' + tweet
91476ec3 158
94a5f62e 159 printNicely('')
f405a7d0
O
160 printNicely(line1)
161 printNicely(line2)
162 printNicely(line3)
91476ec3 163
991c30af 164 # Display Image
c1fa7c94 165 if iot and media_url:
f5677fb1
O
166 for mu in media_url:
167 response = requests.get(mu)
168 image_to_display(StringIO(response.content))
991c30af 169
91476ec3 170
305ce127 171def print_message(m):
172 """
173 Print direct message
174 """
e2b81717
O
175 sender_screen_name = '@' + m['sender_screen_name']
176 sender_name = m['sender']['name']
305ce127 177 text = m['text']
e2b81717
O
178 recipient_screen_name = '@' + m['recipient_screen_name']
179 recipient_name = m['recipient']['name']
305ce127 180 mid = m['id']
181 date = parser.parse(m['created_at'])
182 date = date - datetime.timedelta(seconds=time.timezone)
183 clock = date.strftime('%Y/%m/%d %H:%M:%S')
184
185 # Get rainbow id
186 res = db.message_to_rainbow_query(mid)
187 if not res:
188 db.message_store(mid)
189 res = db.message_to_rainbow_query(mid)
190 rid = res[0].rainbow_id
191
e2b81717 192 sender = cycle_color(sender_name) + grey(' ' + sender_screen_name + ' ')
48a25fe8 193 recipient = cycle_color(
194 recipient_name) + grey(' ' + recipient_screen_name + ' ')
e2b81717 195 user = sender + magenta(' >>> ') + recipient
305ce127 196 meta = grey('[' + clock + '] [message_id=' + str(rid) + '] ')
48a25fe8 197 text = ''.join(map(lambda x: x + ' ' if x == '\n' else x, text))
305ce127 198
199 line1 = u"{u:>{uw}}:".format(
200 u=user,
201 uw=len(user) + 2,
202 )
203 line2 = u"{c:>{cw}}".format(
204 c=meta,
205 cw=len(meta) + 2,
206 )
e2b81717 207
305ce127 208 line3 = ' ' + text
209
210 printNicely('')
211 printNicely(line1)
212 printNicely(line2)
213 printNicely(line3)
e2b81717
O
214
215
216def show_profile(u):
217 """
218 Show a profile
219 """
220 # Retrieve info
221 name = u['name']
222 screen_name = u['screen_name']
223 description = u['description']
224 profile_image_url = u['profile_image_url']
225 location = u['location']
226 url = u['url']
227 created_at = u['created_at']
228 statuses_count = u['statuses_count']
229 friends_count = u['friends_count']
230 followers_count = u['followers_count']
231 # Create content
232 statuses_count = green(str(statuses_count) + ' tweets')
233 friends_count = green(str(friends_count) + ' following')
234 followers_count = green(str(followers_count) + ' followers')
235 count = statuses_count + ' ' + friends_count + ' ' + followers_count
45aa2032 236 user = cycle_color(name) + grey(' @' + screen_name + ' : ') + count
4592d231 237 profile_image_raw_url = 'Profile photo: ' + cyan(profile_image_url)
48a25fe8 238 description = ''.join(
239 map(lambda x: x + ' ' * 4 if x == '\n' else x, description))
e2b81717
O
240 description = yellow(description)
241 location = 'Location : ' + magenta(location)
242 url = 'URL : ' + (cyan(url) if url else '')
4592d231 243 date = parser.parse(created_at)
244 date = date - datetime.timedelta(seconds=time.timezone)
245 clock = date.strftime('%Y/%m/%d %H:%M:%S')
246 clock = 'Join at ' + white(clock)
e2b81717
O
247 # Format
248 line1 = u"{u:>{uw}}".format(
249 u=user,
250 uw=len(user) + 2,
251 )
252 line2 = u"{p:>{pw}}".format(
4592d231 253 p=profile_image_raw_url,
254 pw=len(profile_image_raw_url) + 4,
e2b81717
O
255 )
256 line3 = u"{d:>{dw}}".format(
257 d=description,
258 dw=len(description) + 4,
259 )
260 line4 = u"{l:>{lw}}".format(
261 l=location,
262 lw=len(location) + 4,
263 )
264 line5 = u"{u:>{uw}}".format(
265 u=url,
266 uw=len(url) + 4,
267 )
4592d231 268 line6 = u"{c:>{cw}}".format(
269 c=clock,
270 cw=len(clock) + 4,
e2b81717
O
271 )
272 # Display
273 printNicely('')
4592d231 274 printNicely(line1)
275 if g['iot']:
276 response = requests.get(profile_image_url)
48a25fe8 277 image_to_display(StringIO(response.content), 2, 20)
4592d231 278 else:
279 printNicely(line2)
48a25fe8 280 for line in [line3, line4, line5, line6]:
e2b81717
O
281 printNicely(line)
282 printNicely('')
283
305ce127 284
4592d231 285def print_trends(trends):
286 """
287 Display topics
288 """
289 for topic in trends[:TREND_MAX]:
290 name = topic['name']
291 url = topic['url']
48a25fe8 292 line = cycle_color(name) + ': ' + cyan(url)
4592d231 293 printNicely(line)
294 printNicely('')
48a25fe8 295
4592d231 296
91476ec3
O
297def parse_arguments():
298 """
299 Parse the arguments
300 """
91476ec3 301 parser = argparse.ArgumentParser(description=__doc__ or "")
2a6238f5
O
302 parser.add_argument(
303 '-to',
304 '--timeout',
305 help='Timeout for the stream (seconds).')
306 parser.add_argument(
307 '-ht',
308 '--heartbeat-timeout',
309 help='Set heartbeat timeout.',
310 default=90)
311 parser.add_argument(
312 '-nb',
313 '--no-block',
314 action='store_true',
315 help='Set stream to non-blocking.')
316 parser.add_argument(
317 '-tt',
318 '--track-keywords',
319 help='Search the stream for specific text.')
d51b4107
O
320 parser.add_argument(
321 '-fil',
322 '--filter',
323 help='Filter specific screen_name.')
324 parser.add_argument(
325 '-ig',
326 '--ignore',
327 help='Ignore specific screen_name.')
88af38d8 328 parser.add_argument(
c1fa7c94
O
329 '-iot',
330 '--image-on-term',
331 action='store_true',
332 help='Display all image on terminal.')
91476ec3
O
333 return parser.parse_args()
334
335
54277114
O
336def authen():
337 """
7b674cef 338 Authenticate with Twitter OAuth
54277114 339 """
8c840a83 340 # When using rainbow stream you must authorize.
2a6238f5
O
341 twitter_credential = os.environ.get(
342 'HOME',
343 os.environ.get(
344 'USERPROFILE',
345 '')) + os.sep + '.rainbow_oauth'
8c840a83
O
346 if not os.path.exists(twitter_credential):
347 oauth_dance("Rainbow Stream",
348 CONSUMER_KEY,
349 CONSUMER_SECRET,
350 twitter_credential)
351 oauth_token, oauth_token_secret = read_token_file(twitter_credential)
54277114 352 return OAuth(
2a6238f5
O
353 oauth_token,
354 oauth_token_secret,
355 CONSUMER_KEY,
356 CONSUMER_SECRET)
91476ec3 357
54277114
O
358
359def get_decorated_name():
360 """
361 Beginning of every line
362 """
363 t = Twitter(auth=authen())
c5ff542b 364 name = '@' + t.account.verify_credentials()['screen_name']
42fde775 365 g['original_name'] = name[1:]
f405a7d0 366 g['decorated_name'] = grey('[') + grey(name) + grey(']: ')
54277114 367
f405a7d0 368
42fde775 369def switch():
370 """
371 Switch stream
372 """
373 try:
374 target = g['stuff'].split()[0]
375
d51b4107
O
376 # Filter and ignore
377 args = parse_arguments()
7e4ccbf3 378 try:
d51b4107
O
379 if g['stuff'].split()[-1] == '-f':
380 only = raw_input('Only nicks: ')
381 ignore = raw_input('Ignore nicks: ')
7e4ccbf3 382 args.filter = filter(None, only.split(','))
383 args.ignore = filter(None, ignore.split(','))
d51b4107
O
384 elif g['stuff'].split()[-1] == '-d':
385 args.filter = ONLY_LIST
386 args.ignore = IGNORE_LIST
387 except:
388 printNicely(red('Sorry, wrong format.'))
389 return
390
42fde775 391 # Public stream
392 if target == 'public':
393 keyword = g['stuff'].split()[1]
394 if keyword[0] == '#':
395 keyword = keyword[1:]
42fde775 396 # Kill old process
397 os.kill(g['stream_pid'], signal.SIGKILL)
42fde775 398 args.track_keywords = keyword
42fde775 399 # Start new process
400 p = Process(
d51b4107 401 target=stream,
42fde775 402 args=(
d51b4107 403 PUBLIC_DOMAIN,
42fde775 404 args))
405 p.start()
406 g['stream_pid'] = p.pid
407
408 # Personal stream
409 elif target == 'mine':
42fde775 410 # Kill old process
411 os.kill(g['stream_pid'], signal.SIGKILL)
42fde775 412 # Start new process
413 p = Process(
414 target=stream,
415 args=(
416 USER_DOMAIN,
417 args,
418 g['original_name']))
419 p.start()
420 g['stream_pid'] = p.pid
d51b4107 421 printNicely('')
1551a7d3 422 printNicely(green('Stream switched.'))
d51b4107
O
423 if args.filter:
424 printNicely(cyan('Only: ' + str(args.filter)))
425 if args.ignore:
426 printNicely(red('Ignore: ' + str(args.ignore)))
427 printNicely('')
42fde775 428 except:
429 printNicely(red('Sorry I can\'t understand.'))
42fde775 430
431
4592d231 432def trend():
433 """
434 Trend
435 """
436 t = Twitter(auth=authen())
48a25fe8 437 # Get country and town
4592d231 438 try:
439 country = g['stuff'].split()[0]
440 except:
441 country = ''
48a25fe8 442 try:
443 town = g['stuff'].split()[1]
444 except:
445 town = ''
446
447 avail = t.trends.available()
448 # World wide
449 if not country:
450 trends = t.trends.place(_id=1)[0]['trends']
451 print_trends(trends)
452 else:
453 for location in avail:
454 # Search for country and Town
455 if town:
456 if location['countryCode'] == country \
457 and location['placeType']['name'] == 'Town' \
458 and location['name'] == town:
459 trends = t.trends.place(_id=location['woeid'])[0]['trends']
460 print_trends(trends)
461 # Search for country only
462 else:
463 if location['countryCode'] == country \
464 and location['placeType']['name'] == 'Country':
465 trends = t.trends.place(_id=location['woeid'])[0]['trends']
466 print_trends(trends)
4592d231 467
468
7b674cef 469def home():
470 """
471 Home
472 """
473 t = Twitter(auth=authen())
94a5f62e 474 num = HOME_TWEET_NUM
7b674cef 475 if g['stuff'].isdigit():
305ce127 476 num = int(g['stuff'])
94a5f62e 477 for tweet in reversed(t.statuses.home_timeline(count=num)):
c1fa7c94 478 draw(t=tweet, iot=g['iot'])
94a5f62e 479 printNicely('')
7b674cef 480
481
482def view():
483 """
484 Friend view
485 """
486 t = Twitter(auth=authen())
487 user = g['stuff'].split()[0]
b8fbcb70 488 if user[0] == '@':
489 try:
94a5f62e 490 num = int(g['stuff'].split()[1])
b8fbcb70 491 except:
94a5f62e 492 num = HOME_TWEET_NUM
493 for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])):
c1fa7c94 494 draw(t=tweet, iot=g['iot'])
94a5f62e 495 printNicely('')
b8fbcb70 496 else:
c91f75f2 497 printNicely(red('A name should begin with a \'@\''))
7b674cef 498
499
305ce127 500def mentions():
501 """
502 Mentions timeline
503 """
504 t = Twitter(auth=authen())
505 num = HOME_TWEET_NUM
506 if g['stuff'].isdigit():
507 num = int(g['stuff'])
508 for tweet in reversed(t.statuses.mentions_timeline(count=num)):
509 draw(t=tweet, iot=g['iot'])
510 printNicely('')
511
512
f405a7d0 513def tweet():
54277114 514 """
7b674cef 515 Tweet
54277114
O
516 """
517 t = Twitter(auth=authen())
f405a7d0 518 t.statuses.update(status=g['stuff'])
f405a7d0 519
b2b933a9 520
1ba4abfd
O
521def retweet():
522 """
523 ReTweet
524 """
525 t = Twitter(auth=authen())
526 try:
527 id = int(g['stuff'].split()[0])
1ba4abfd 528 except:
b8c1f42a
O
529 printNicely(red('Sorry I can\'t understand.'))
530 return
531 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
532 t.statuses.retweet(id=tid, include_entities=False, trim_user=True)
1ba4abfd
O
533
534
1f24a05a 535def allretweet():
536 """
537 List all retweet
538 """
539 t = Twitter(auth=authen())
540 # Get rainbow id
541 try:
542 id = int(g['stuff'].split()[0])
543 except:
544 printNicely(red('Sorry I can\'t understand.'))
545 return
546 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
547 # Get display num if exist
548 try:
549 num = int(g['stuff'].split()[1])
550 except:
551 num = RETWEETS_SHOW_NUM
552 # Get result and display
553 rt_ary = t.statuses.retweets(id=tid,count=num)
554 if not rt_ary:
555 printNicely(magenta('This tweet has no retweet.'))
556 return
557 for tweet in reversed(rt_ary):
558 draw(t=tweet, iot=g['iot'])
559 printNicely('')
560
561
7e4ccbf3 562def favorite():
563 """
564 Favorite
565 """
566 t = Twitter(auth=authen())
567 try:
568 id = int(g['stuff'].split()[0])
7e4ccbf3 569 except:
b8c1f42a
O
570 printNicely(red('Sorry I can\'t understand.'))
571 return
572 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
573 t.favorites.create(_id=tid, include_entities=False)
574 printNicely(green('Favorited.'))
575 draw(t.statuses.show(id=tid), iot=g['iot'])
576 printNicely('')
7e4ccbf3 577
578
7b674cef 579def reply():
829cc2d8 580 """
7b674cef 581 Reply
829cc2d8
O
582 """
583 t = Twitter(auth=authen())
7b674cef 584 try:
585 id = int(g['stuff'].split()[0])
7b674cef 586 except:
c91f75f2 587 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
588 return
589 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
590 user = t.statuses.show(id=tid)['user']['screen_name']
591 status = ' '.join(g['stuff'].split()[1:])
592 status = '@' + user + ' ' + status.decode('utf-8')
593 t.statuses.update(status=status, in_reply_to_status_id=tid)
7b674cef 594
595
596def delete():
597 """
598 Delete
599 """
600 t = Twitter(auth=authen())
601 try:
305ce127 602 rid = int(g['stuff'].split()[0])
7b674cef 603 except:
305ce127 604 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
605 return
606 tid = db.rainbow_to_tweet_query(rid)[0].tweet_id
607 t.statuses.destroy(id=tid)
608 printNicely(green('Okay it\'s gone.'))
829cc2d8
O
609
610
7e4ccbf3 611def unfavorite():
612 """
613 Unfavorite
614 """
615 t = Twitter(auth=authen())
616 try:
617 id = int(g['stuff'].split()[0])
7e4ccbf3 618 except:
b8c1f42a
O
619 printNicely(red('Sorry I can\'t understand.'))
620 return
621 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
622 t.favorites.destroy(_id=tid)
623 printNicely(green('Okay it\'s unfavorited.'))
624 draw(t.statuses.show(id=tid), iot=g['iot'])
625 printNicely('')
7e4ccbf3 626
627
f405a7d0
O
628def search():
629 """
7b674cef 630 Search
f405a7d0
O
631 """
632 t = Twitter(auth=authen())
b8c1f42a
O
633 if g['stuff'].startswith('#'):
634 rel = t.search.tweets(q=g['stuff'])['statuses']
5b2c4faf 635 if rel:
b8c1f42a
O
636 printNicely('Newest tweets:')
637 for i in reversed(xrange(SEARCH_MAX_RECORD)):
638 draw(t=rel[i],
639 iot=g['iot'],
640 keyword=g['stuff'].strip()[1:])
641 printNicely('')
94a5f62e 642 else:
b8c1f42a
O
643 printNicely(magenta('I\'m afraid there is no result'))
644 else:
645 printNicely(red('A keyword should be a hashtag (like \'#AKB48\')'))
b2b933a9 646
f405a7d0 647
305ce127 648def message():
649 """
650 Send a direct message
651 """
652 t = Twitter(auth=authen())
653 user = g['stuff'].split()[0]
b8c1f42a 654 if user[0].startswith('@'):
305ce127 655 try:
656 content = g['stuff'].split()[1]
305ce127 657 except:
658 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
659 t.direct_messages.new(
660 screen_name=user[1:],
661 text=content
662 )
663 printNicely(green('Message sent.'))
305ce127 664 else:
665 printNicely(red('A name should begin with a \'@\''))
666
667
f5677fb1 668def show():
843647ad 669 """
f5677fb1 670 Show image
843647ad
O
671 """
672 t = Twitter(auth=authen())
f5677fb1
O
673 try:
674 target = g['stuff'].split()[0]
675 if target != 'image':
676 return
677 id = int(g['stuff'].split()[1])
305ce127 678 tid = db.rainbow_to_tweet_query(id)[0].tweet_id
f5677fb1
O
679 tweet = t.statuses.show(id=tid)
680 media = tweet['entities']['media']
681 for m in media:
682 res = requests.get(m['media_url'])
683 img = Image.open(StringIO(res.content))
684 img.show()
685 except:
686 printNicely(red('Sorry I can\'t show this image.'))
843647ad
O
687
688
0f6e4daf 689def list():
690 """
691 List friends for followers
692 """
693 t = Twitter(auth=authen())
e2b81717
O
694 # Get name
695 try:
696 name = g['stuff'].split()[1]
b8c1f42a 697 if name.startswith('@'):
e2b81717
O
698 name = name[1:]
699 else:
700 printNicely(red('A name should begin with a \'@\''))
701 raise Exception('Invalid name')
702 except:
703 name = g['original_name']
704 # Get list followers or friends
0f6e4daf 705 try:
706 target = g['stuff'].split()[0]
0f6e4daf 707 except:
708 printNicely(red('Omg some syntax is wrong.'))
b8c1f42a
O
709 # Init cursor
710 d = {'fl': 'followers', 'fr': 'friends'}
711 next_cursor = -1
712 rel = {}
713 # Cursor loop
714 while next_cursor != 0:
715 list = getattr(t, d[target]).list(
716 screen_name=name,
717 cursor=next_cursor,
718 skip_status=True,
719 include_entities=False,
720 )
721 for u in list['users']:
722 rel[u['name']] = '@' + u['screen_name']
723 next_cursor = list['next_cursor']
724 # Print out result
725 printNicely('All: ' + str(len(rel)) + ' people.')
726 for name in rel:
727 user = ' ' + cycle_color(name) + grey(' ' + rel[name] + ' ')
728 printNicely(user)
0f6e4daf 729
730
305ce127 731def inbox():
732 """
733 Inbox direct messages
734 """
735 t = Twitter(auth=authen())
736 num = MESSAGES_DISPLAY
737 rel = []
738 if g['stuff'].isdigit():
739 num = g['stuff']
740 cur_page = 1
741 # Max message per page is 20 so we have to loop
742 while num > 20:
743 rel = rel + t.direct_messages(
744 count=20,
745 page=cur_page,
746 include_entities=False,
747 skip_status=False
48a25fe8 748 )
305ce127 749 num -= 20
750 cur_page += 1
751 rel = rel + t.direct_messages(
752 count=num,
753 page=cur_page,
754 include_entities=False,
755 skip_status=False
48a25fe8 756 )
e2b81717 757 # Display
305ce127 758 printNicely('Inbox: newest ' + str(len(rel)) + ' messages.')
759 for m in reversed(rel):
760 print_message(m)
761 printNicely('')
762
e2b81717 763
305ce127 764def sent():
765 """
766 Sent direct messages
767 """
768 t = Twitter(auth=authen())
769 num = MESSAGES_DISPLAY
770 rel = []
771 if g['stuff'].isdigit():
772 num = int(g['stuff'])
773 cur_page = 1
774 # Max message per page is 20 so we have to loop
775 while num > 20:
776 rel = rel + t.direct_messages.sent(
777 count=20,
778 page=cur_page,
779 include_entities=False,
780 skip_status=False
48a25fe8 781 )
305ce127 782 num -= 20
783 cur_page += 1
784 rel = rel + t.direct_messages.sent(
785 count=num,
786 page=cur_page,
787 include_entities=False,
788 skip_status=False
48a25fe8 789 )
e2b81717 790 # Display
305ce127 791 printNicely('Sent: newest ' + str(len(rel)) + ' messages.')
792 for m in reversed(rel):
793 print_message(m)
794 printNicely('')
e2b81717 795
305ce127 796
797def trash():
798 """
799 Remove message
800 """
801 t = Twitter(auth=authen())
802 try:
803 rid = int(g['stuff'].split()[0])
305ce127 804 except:
805 printNicely(red('Sorry I can\'t understand.'))
b8c1f42a
O
806 mid = db.rainbow_to_message_query(rid)[0].message_id
807 t.direct_messages.destroy(id=mid)
808 printNicely(green('Message deleted.'))
305ce127 809
810
e2b81717
O
811def whois():
812 """
813 Show profile of a specific user
814 """
815 t = Twitter(auth=authen())
816 screen_name = g['stuff'].split()[0]
b8c1f42a 817 if screen_name.startswith('@'):
e2b81717
O
818 try:
819 user = t.users.show(
820 screen_name=screen_name[1:],
821 include_entities=False)
822 show_profile(user)
823 except:
824 printNicely(red('Omg no user.'))
825 else:
b8c1f42a 826 printNicely(red('A name should begin with a \'@\''))
e2b81717
O
827
828
f5677fb1 829def follow():
843647ad 830 """
f5677fb1 831 Follow a user
843647ad
O
832 """
833 t = Twitter(auth=authen())
f5677fb1 834 screen_name = g['stuff'].split()[0]
b8c1f42a
O
835 if screen_name.startswith('@'):
836 t.friendships.create(screen_name=screen_name[1:], follow=True)
837 printNicely(green('You are following ' + screen_name + ' now!'))
f5677fb1 838 else:
b8c1f42a 839 printNicely(red('A name should begin with a \'@\''))
f5677fb1
O
840
841
842def unfollow():
843 """
844 Unfollow a user
845 """
846 t = Twitter(auth=authen())
847 screen_name = g['stuff'].split()[0]
b8c1f42a
O
848 if screen_name.startswith('@'):
849 t.friendships.destroy(
850 screen_name=screen_name[1:],
851 include_entities=False)
852 printNicely(green('Unfollow ' + screen_name + ' success!'))
f5677fb1 853 else:
b8c1f42a 854 printNicely(red('A name should begin with a \'@\''))
843647ad
O
855
856
5b2c4faf 857def mute():
858 """
859 Mute a user
860 """
861 t = Twitter(auth=authen())
862 try:
863 screen_name = g['stuff'].split()[0]
864 except:
865 printNicely(red('A name should be specified. '))
866 return
867 if screen_name.startswith('@'):
868 rel = t.mutes.users.create(screen_name=screen_name[1:])
869 if isinstance(rel, dict):
870 printNicely(green(screen_name + ' is muted.'))
871 else:
872 printNicely(red(rel))
873 else:
874 printNicely(red('A name should begin with a \'@\''))
875
876
877def unmute():
878 """
879 Unmute a user
880 """
881 t = Twitter(auth=authen())
882 try:
883 screen_name = g['stuff'].split()[0]
884 except:
885 printNicely(red('A name should be specified. '))
886 return
887 if screen_name.startswith('@'):
888 rel = t.mutes.users.destroy(screen_name=screen_name[1:])
889 if isinstance(rel, dict):
890 printNicely(green(screen_name + ' is unmuted.'))
891 else:
892 printNicely(red(rel))
893 else:
894 printNicely(red('A name should begin with a \'@\''))
895
896
897def muting():
898 """
899 List muting user
900 """
901 t = Twitter(auth=authen())
902 # Init cursor
903 d = {'fl': 'followers', 'fr': 'friends'}
904 next_cursor = -1
905 rel = {}
906 # Cursor loop
907 while next_cursor != 0:
908 list = t.mutes.users.list(
909 screen_name=g['original_name'],
910 cursor=next_cursor,
911 skip_status=True,
912 include_entities=False,
913 )
914 for u in list['users']:
915 rel[u['name']] = '@' + u['screen_name']
916 next_cursor = list['next_cursor']
917 # Print out result
918 printNicely('All: ' + str(len(rel)) + ' people.')
919 for name in rel:
920 user = ' ' + cycle_color(name) + grey(' ' + rel[name] + ' ')
921 printNicely(user)
922
923
305ce127 924def block():
925 """
926 Block a user
927 """
928 t = Twitter(auth=authen())
929 screen_name = g['stuff'].split()[0]
b8c1f42a
O
930 if screen_name.startswith('@'):
931 t.blocks.create(
5b2c4faf 932 screen_name=screen_name[1:],
933 include_entities=False,
934 skip_status=True)
b8c1f42a 935 printNicely(green('You blocked ' + screen_name + '.'))
305ce127 936 else:
b8c1f42a 937 printNicely(red('A name should begin with a \'@\''))
305ce127 938
939
940def unblock():
941 """
942 Unblock a user
943 """
944 t = Twitter(auth=authen())
945 screen_name = g['stuff'].split()[0]
b8c1f42a
O
946 if screen_name.startswith('@'):
947 t.blocks.destroy(
948 screen_name=screen_name[1:],
949 include_entities=False,
950 skip_status=True)
951 printNicely(green('Unblock ' + screen_name + ' success!'))
305ce127 952 else:
b8c1f42a 953 printNicely(red('A name should begin with a \'@\''))
305ce127 954
955
956def report():
957 """
958 Report a user as a spam account
959 """
960 t = Twitter(auth=authen())
961 screen_name = g['stuff'].split()[0]
b8c1f42a
O
962 if screen_name.startswith('@'):
963 t.users.report_spam(
964 screen_name=screen_name[1:])
965 printNicely(green('You reported ' + screen_name + '.'))
305ce127 966 else:
967 printNicely(red('Sorry I can\'t understand.'))
968
969
813a5d80 970def cal():
971 """
972 Unix's command `cal`
973 """
974 # Format
975 rel = os.popen('cal').read().split('\n')
976 month = rel.pop(0)
977 month = random_rainbow(month)
978 date = rel.pop(0)
979 date = ' '.join([cycle_color(i) for i in date.split(' ')])
980 today = os.popen('date +\'%d\'').read().strip()
981 # Display
982 print month
983 print date
984 for line in rel:
985 ary = line.split(' ')
986 ary = map(lambda x:on_grey(x) if x==today else grey(x),ary)
987 print ' '.join(ary)
988
989
f405a7d0
O
990def help():
991 """
7b674cef 992 Help
f405a7d0 993 """
7e4ccbf3 994 s = ' ' * 2
995 h, w = os.popen('stty size', 'r').read().split()
e3885f55 996
8bc30efd 997 # Start
e3885f55
O
998 usage = '\n'
999 usage += s + 'Hi boss! I\'m ready to serve you right now!\n'
7e4ccbf3 1000 usage += s + '-' * (int(w) - 4) + '\n'
7e4ccbf3 1001 usage += s + 'You are ' + yellow('already') + ' on your personal stream.\n'
5b2c4faf 1002 usage += s + 'Any update from Twitter will show up ' + \
1003 yellow('immediately') + '.\n'
1004 usage += s + 'In addtion, following commands are available right now:\n'
e3885f55 1005
1f24a05a 1006 # Discover the world
8bc30efd 1007 usage += '\n'
1008 usage += s + grey(u'\u266A' + ' Discover the world \n')
d03d632b 1009 usage += s * 2 + green('trend') + ' will show global trending topics. ' + \
48a25fe8 1010 'You can try ' + green('trend US') + ' or ' + \
1011 green('trend JP Tokyo') + '.\n'
7e4ccbf3 1012 usage += s * 2 + green('home') + ' will show your timeline. ' + \
305ce127 1013 green('home 7') + ' will show 7 tweets.\n'
305ce127 1014 usage += s * 2 + green('mentions') + ' will show mentions timeline. ' + \
e2b81717 1015 green('mentions 7') + ' will show 7 mention tweets.\n'
8bc30efd 1016 usage += s * 2 + green('whois @mdo') + ' will show profile of ' + \
1017 magenta('@mdo') + '.\n'
1018 usage += s * 2 + green('view @mdo') + \
1019 ' will show ' + magenta('@mdo') + '\'s home.\n'
1020 usage += s * 2 + green('s #AKB48') + ' will search for "' + \
1021 yellow('AKB48') + '" and return 5 newest tweet.\n'
1022
1f24a05a 1023 # Tweet
8bc30efd 1024 usage += '\n'
1025 usage += s + grey(u'\u266A' + ' Tweets \n')
7e4ccbf3 1026 usage += s * 2 + green('t oops ') + \
1027 'will tweet "' + yellow('oops') + '" immediately.\n'
1028 usage += s * 2 + \
1029 green('rt 12 ') + ' will retweet to tweet with ' + \
1030 yellow('[id=12]') + '.\n'
1f24a05a 1031 usage += s * 2 + \
1032 green('allrt 12 20 ') + ' list 20 newest retweet of the tweet with ' + \
1033 yellow('[id=12]') + '.\n'
7e4ccbf3 1034 usage += s * 2 + green('rep 12 oops') + ' will reply "' + \
1035 yellow('oops') + '" to tweet with ' + yellow('[id=12]') + '.\n'
1036 usage += s * 2 + \
8bc30efd 1037 green('fav 12 ') + ' will favorite the tweet with ' + \
7e4ccbf3 1038 yellow('[id=12]') + '.\n'
1039 usage += s * 2 + \
1040 green('ufav 12 ') + ' will unfavorite tweet with ' + \
1041 yellow('[id=12]') + '.\n'
8bc30efd 1042 usage += s * 2 + \
1043 green('del 12 ') + ' will delete tweet with ' + \
1044 yellow('[id=12]') + '.\n'
a99b7b04
O
1045 usage += s * 2 + green('show image 12') + ' will show image in tweet with ' + \
1046 yellow('[id=12]') + ' in your OS\'s image viewer.\n'
8bc30efd 1047
5b2c4faf 1048 # Direct message
8bc30efd 1049 usage += '\n'
1050 usage += s + grey(u'\u266A' + ' Direct messages \n')
305ce127 1051 usage += s * 2 + green('inbox') + ' will show inbox messages. ' + \
1052 green('inbox 7') + ' will show newest 7 messages.\n'
1053 usage += s * 2 + green('sent') + ' will show sent messages. ' + \
1054 green('sent 7') + ' will show newest 7 messages.\n'
8bc30efd 1055 usage += s * 2 + green('mes @dtvd88 hi') + ' will send a "hi" messege to ' + \
1056 magenta('@dtvd88') + '.\n'
305ce127 1057 usage += s * 2 + green('trash 5') + ' will remove message with ' + \
1058 yellow('[message_id=5]') + '.\n'
8bc30efd 1059
1060 # Follower and following
1061 usage += '\n'
1062 usage += s + grey(u'\u266A' + ' Fiends and followers \n')
1063 usage += s * 2 + \
1064 green('ls fl') + \
1065 ' will list all followers (people who are following you).\n'
1066 usage += s * 2 + \
1067 green('ls fr') + \
1068 ' will list all friends (people who you are following).\n'
f5677fb1 1069 usage += s * 2 + green('fl @dtvd88') + ' will follow ' + \
305ce127 1070 magenta('@dtvd88') + '.\n'
f5677fb1 1071 usage += s * 2 + green('ufl @dtvd88') + ' will unfollow ' + \
305ce127 1072 magenta('@dtvd88') + '.\n'
83320499 1073 usage += s * 2 + green('mute @dtvd88') + ' will mute ' + \
5b2c4faf 1074 magenta('@dtvd88') + '.\n'
83320499 1075 usage += s * 2 + green('unmute @dtvd88') + ' will unmute ' + \
5b2c4faf 1076 magenta('@dtvd88') + '.\n'
1077 usage += s * 2 + green('muting') + ' will list muting users.\n'
305ce127 1078 usage += s * 2 + green('block @dtvd88') + ' will block ' + \
1079 magenta('@dtvd88') + '.\n'
1080 usage += s * 2 + green('unblock @dtvd88') + ' will unblock ' + \
1081 magenta('@dtvd88') + '.\n'
1082 usage += s * 2 + green('report @dtvd88') + ' will report ' + \
1083 magenta('@dtvd88') + ' as a spam account.\n'
8bc30efd 1084
8bc30efd 1085 # Switch
1086 usage += '\n'
1087 usage += s + grey(u'\u266A' + ' Switching streams \n')
48a25fe8 1088 usage += s * 2 + green('switch public #AKB') + \
1089 ' will switch to public stream and follow "' + \
1090 yellow('AKB') + '" keyword.\n'
1091 usage += s * 2 + green('switch mine') + \
1092 ' will switch to your personal stream.\n'
1093 usage += s * 2 + green('switch mine -f ') + \
1094 ' will prompt to enter the filter.\n'
1095 usage += s * 3 + yellow('Only nicks') + \
1096 ' filter will decide nicks will be INCLUDE ONLY.\n'
1097 usage += s * 3 + yellow('Ignore nicks') + \
1098 ' filter will decide nicks will be EXCLUDE.\n'
1099 usage += s * 2 + green('switch mine -d') + \
1100 ' will use the config\'s ONLY_LIST and IGNORE_LIST.\n'
1101 usage += s * 3 + '(see ' + grey('rainbowstream/config.py') + ').\n'
1102
1f24a05a 1103 # Smart shell
1104 usage += '\n'
1105 usage += s + grey(u'\u266A' + ' Smart shell\n')
1106 usage += s*2 + green('111111 * 9 / 7') + ' or any math expression ' + \
1107 'will be evaluate by Python interpreter.\n'
1108 usage += s*2 + 'Even ' + green('cal') + ' will show the calendar' + \
1109 ' for current month.\n'
1110
1111 # Screening
1112 usage += '\n'
1113 usage += s + grey(u'\u266A' + ' Screening \n')
1114 usage += s * 2 + green('h') + ' will show this help again.\n'
1115 usage += s * 2 + green('c') + ' will clear the screen.\n'
1116 usage += s * 2 + green('q') + ' will quit.\n'
1117
8bc30efd 1118 # End
1119 usage += '\n'
7e4ccbf3 1120 usage += s + '-' * (int(w) - 4) + '\n'
8bc30efd 1121 usage += s + 'Have fun and hang tight! \n'
f405a7d0 1122 printNicely(usage)
f405a7d0
O
1123
1124
843647ad 1125def clear():
f405a7d0 1126 """
7b674cef 1127 Clear screen
f405a7d0 1128 """
843647ad 1129 os.system('clear')
f405a7d0
O
1130
1131
843647ad 1132def quit():
b8dda704
O
1133 """
1134 Exit all
1135 """
f5677fb1 1136 save_history()
8e633322 1137 os.system('rm -rf rainbow.db')
843647ad
O
1138 os.kill(g['stream_pid'], signal.SIGKILL)
1139 sys.exit()
b8dda704
O
1140
1141
94a5f62e 1142def reset():
f405a7d0 1143 """
94a5f62e 1144 Reset prefix of line
f405a7d0 1145 """
c91f75f2 1146 if g['reset']:
e3885f55 1147 printNicely(magenta('Need tips ? Type "h" and hit Enter key!'))
c91f75f2 1148 g['reset'] = False
d0a726d6 1149 try:
1150 print eval(g['cmd'])
1151 except:
1152 pass
54277114
O
1153
1154
94a5f62e 1155def process(cmd):
54277114 1156 """
94a5f62e 1157 Process switch
54277114 1158 """
94a5f62e 1159 return dict(zip(
1160 cmdset,
b2b933a9 1161 [
42fde775 1162 switch,
4592d231 1163 trend,
b2b933a9 1164 home,
1165 view,
305ce127 1166 mentions,
b2b933a9 1167 tweet,
1168 retweet,
1f24a05a 1169 allretweet,
7e4ccbf3 1170 favorite,
b2b933a9 1171 reply,
1172 delete,
7e4ccbf3 1173 unfavorite,
b2b933a9 1174 search,
305ce127 1175 message,
f5677fb1 1176 show,
0f6e4daf 1177 list,
305ce127 1178 inbox,
1179 sent,
1180 trash,
e2b81717 1181 whois,
f5677fb1
O
1182 follow,
1183 unfollow,
5b2c4faf 1184 mute,
1185 unmute,
1186 muting,
305ce127 1187 block,
1188 unblock,
1189 report,
813a5d80 1190 cal,
b2b933a9 1191 help,
1192 clear,
1193 quit
1194 ]
94a5f62e 1195 )).get(cmd, reset)
1196
1197
1198def listen():
42fde775 1199 """
1200 Listen to user's input
1201 """
d51b4107
O
1202 d = dict(zip(
1203 cmdset,
1204 [
affcb149 1205 ['public', 'mine'], # switch
4592d231 1206 [], # trend
7e4ccbf3 1207 [], # home
1208 ['@'], # view
305ce127 1209 [], # mentions
7e4ccbf3 1210 [], # tweet
1211 [], # retweet
1f24a05a 1212 [], # allretweet
f5677fb1 1213 [], # favorite
7e4ccbf3 1214 [], # reply
1215 [], # delete
f5677fb1 1216 [], # unfavorite
7e4ccbf3 1217 ['#'], # search
305ce127 1218 ['@'], # message
f5677fb1 1219 ['image'], # show image
305ce127 1220 ['fl', 'fr'], # list
1221 [], # inbox
1222 [], # sent
1223 [], # trash
e2b81717 1224 ['@'], # whois
affcb149
O
1225 ['@'], # follow
1226 ['@'], # unfollow
5b2c4faf 1227 ['@'], # mute
1228 ['@'], # unmute
1229 ['@'], # muting
305ce127 1230 ['@'], # block
1231 ['@'], # unblock
1232 ['@'], # report
813a5d80 1233 [], # cal
7e4ccbf3 1234 [], # help
1235 [], # clear
1236 [], # quit
d51b4107 1237 ]
7e4ccbf3 1238 ))
d51b4107 1239 init_interactive_shell(d)
f5677fb1 1240 read_history()
819569e8 1241 reset()
b2b933a9 1242 while True:
1dd312f5
O
1243 if g['prefix']:
1244 line = raw_input(g['decorated_name'])
1245 else:
1246 line = raw_input()
843647ad
O
1247 try:
1248 cmd = line.split()[0]
1249 except:
1250 cmd = ''
d0a726d6 1251 g['cmd'] = cmd
f405a7d0 1252 # Save cmd to global variable and call process
b8c1f42a
O
1253 try:
1254 g['stuff'] = ' '.join(line.split()[1:])
1255 process(cmd)()
1256 except Exception:
1257 printNicely(red('OMG something is wrong with Twitter right now.'))
1258 # Not redisplay prefix
7e4ccbf3 1259 if cmd in ['switch', 't', 'rt', 'rep']:
1dd312f5
O
1260 g['prefix'] = False
1261 else:
1262 g['prefix'] = True
54277114
O
1263
1264
42fde775 1265def stream(domain, args, name='Rainbow Stream'):
54277114 1266 """
f405a7d0 1267 Track the stream
54277114 1268 """
d51b4107 1269
54277114 1270 # The Logo
42fde775 1271 art_dict = {
1272 USER_DOMAIN: name,
1273 PUBLIC_DOMAIN: args.track_keywords,
1274 SITE_DOMAIN: 'Site Stream',
1275 }
1276 ascii_art(art_dict[domain])
d51b4107 1277
91476ec3
O
1278 # These arguments are optional:
1279 stream_args = dict(
1280 timeout=args.timeout,
1281 block=not args.no_block,
1282 heartbeat_timeout=args.heartbeat_timeout)
1283
1284 # Track keyword
1285 query_args = dict()
1286 if args.track_keywords:
1287 query_args['track'] = args.track_keywords
1288
1289 # Get stream
2a6238f5 1290 stream = TwitterStream(
22be990e 1291 auth=authen(),
42fde775 1292 domain=domain,
2a6238f5 1293 **stream_args)
91476ec3 1294
42fde775 1295 if domain == USER_DOMAIN:
1296 tweet_iter = stream.user(**query_args)
1297 elif domain == SITE_DOMAIN:
1298 tweet_iter = stream.site(**query_args)
1299 else:
1300 if args.track_keywords:
1301 tweet_iter = stream.statuses.filter(**query_args)
1302 else:
1303 tweet_iter = stream.statuses.sample()
1304
1305 # Iterate over the stream.
91476ec3
O
1306 for tweet in tweet_iter:
1307 if tweet is None:
1308 printNicely("-- None --")
1309 elif tweet is Timeout:
1310 printNicely("-- Timeout --")
1311 elif tweet is HeartbeatTimeout:
1312 printNicely("-- Heartbeat Timeout --")
1313 elif tweet is Hangup:
1314 printNicely("-- Hangup --")
1315 elif tweet.get('text'):
7e4ccbf3 1316 draw(
1317 t=tweet,
c1fa7c94 1318 iot=args.image_on_term,
7e4ccbf3 1319 keyword=args.track_keywords,
1320 fil=args.filter,
88af38d8 1321 ig=args.ignore,
0f6e4daf 1322 )
54277114
O
1323
1324
1325def fly():
1326 """
1327 Main function
1328 """
42fde775 1329 # Spawn stream process
1330 args = parse_arguments()
54277114 1331 get_decorated_name()
42fde775 1332 p = Process(target=stream, args=(USER_DOMAIN, args, g['original_name']))
1333 p.start()
1334
1335 # Start listen process
819569e8 1336 time.sleep(0.5)
c91f75f2 1337 g['reset'] = True
1dd312f5 1338 g['prefix'] = True
f405a7d0 1339 g['stream_pid'] = p.pid
c1fa7c94 1340 g['iot'] = args.image_on_term
0f6e4daf 1341 listen()