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