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