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