metadata
[rainbowstream.git] / rainbowstream / rainbow.py
CommitLineData
91476ec3
O
1"""
2Colorful user's timeline stream
3"""
78b81730
O
4from __future__ import print_function
5from multiprocessing import Process
6from dateutil import parser
7
b2b933a9 8import os
9import os.path
10import sys
11import signal
12import argparse
13import time
14import datetime
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
91476ec3 21
2a6238f5
O
22from .colors import *
23from .config import *
94a5f62e 24from .interactive import *
18cab06a 25from .db import *
2a6238f5 26
f405a7d0 27g = {}
18cab06a 28db = RainbowDB()
94a5f62e 29cmdset = [
30 'home',
31 'view',
32 't',
33 'rt',
34 'rep',
35 'del',
36 's',
37 'fr',
38 'fl',
39 'h',
40 'c',
41 'q'
42]
22be990e 43
b2b933a9 44
22be990e 45def draw(t, keyword=None):
91476ec3
O
46 """
47 Draw the rainbow
48 """
49 # Retrieve tweet
829cc2d8 50 tid = t['id']
91476ec3
O
51 text = t['text']
52 screen_name = t['user']['screen_name']
53 name = t['user']['name']
54 created_at = t['created_at']
55 date = parser.parse(created_at)
e20af1c3 56 date = date - datetime.timedelta(seconds=time.timezone)
57 clock = date.strftime('%Y/%m/%d %H:%M:%S')
91476ec3 58
18cab06a
O
59 res = db.tweet_query(tid)
60 if not res:
61 db.store(tid)
62 res = db.tweet_query(tid)
63 rid = res[0].rainbow_id
64
91476ec3 65 # Format info
54277114 66 user = cycle_color(name) + grey(' ' + '@' + screen_name + ' ')
e20af1c3 67 meta = grey('[' + clock + '] [id=' + str(rid) + ']')
8c840a83 68 tweet = text.split()
b8dda704 69 # Highlight RT
2a6238f5 70 tweet = map(lambda x: grey(x) if x == 'RT' else x, tweet)
b8dda704 71 # Highlight screen_name
2a6238f5 72 tweet = map(lambda x: cycle_color(x) if x[0] == '@' else x, tweet)
b8dda704 73 # Highlight link
2a6238f5 74 tweet = map(lambda x: cyan(x) if x[0:7] == 'http://' else x, tweet)
b8dda704 75 # Highlight search keyword
7a431249 76 if keyword:
22be990e 77 tweet = map(
78 lambda x: on_yellow(x) if
79 ''.join(c for c in x if c.isalnum()).lower() == keyword.lower()
80 else x,
81 tweet
82 )
8c840a83 83 tweet = ' '.join(tweet)
91476ec3
O
84
85 # Draw rainbow
06773ffe 86 line1 = u"{u:>{uw}}:".format(
2a6238f5
O
87 u=user,
88 uw=len(user) + 2,
91476ec3 89 )
06773ffe 90 line2 = u"{c:>{cw}}".format(
829cc2d8
O
91 c=meta,
92 cw=len(meta) + 2,
06773ffe
O
93 )
94 line3 = ' ' + tweet
91476ec3 95
94a5f62e 96 printNicely('')
f405a7d0
O
97 printNicely(line1)
98 printNicely(line2)
99 printNicely(line3)
91476ec3
O
100
101
102def parse_arguments():
103 """
104 Parse the arguments
105 """
91476ec3 106 parser = argparse.ArgumentParser(description=__doc__ or "")
2a6238f5
O
107 parser.add_argument(
108 '-to',
109 '--timeout',
110 help='Timeout for the stream (seconds).')
111 parser.add_argument(
112 '-ht',
113 '--heartbeat-timeout',
114 help='Set heartbeat timeout.',
115 default=90)
116 parser.add_argument(
117 '-nb',
118 '--no-block',
119 action='store_true',
120 help='Set stream to non-blocking.')
121 parser.add_argument(
122 '-tt',
123 '--track-keywords',
124 help='Search the stream for specific text.')
91476ec3
O
125 return parser.parse_args()
126
127
54277114
O
128def authen():
129 """
7b674cef 130 Authenticate with Twitter OAuth
54277114 131 """
8c840a83 132 # When using rainbow stream you must authorize.
2a6238f5
O
133 twitter_credential = os.environ.get(
134 'HOME',
135 os.environ.get(
136 'USERPROFILE',
137 '')) + os.sep + '.rainbow_oauth'
8c840a83
O
138 if not os.path.exists(twitter_credential):
139 oauth_dance("Rainbow Stream",
140 CONSUMER_KEY,
141 CONSUMER_SECRET,
142 twitter_credential)
143 oauth_token, oauth_token_secret = read_token_file(twitter_credential)
54277114 144 return OAuth(
2a6238f5
O
145 oauth_token,
146 oauth_token_secret,
147 CONSUMER_KEY,
148 CONSUMER_SECRET)
91476ec3 149
54277114
O
150
151def get_decorated_name():
152 """
153 Beginning of every line
154 """
155 t = Twitter(auth=authen())
c5ff542b 156 name = '@' + t.account.verify_credentials()['screen_name']
f405a7d0 157 g['decorated_name'] = grey('[') + grey(name) + grey(']: ')
54277114 158
f405a7d0 159
7b674cef 160def home():
161 """
162 Home
163 """
164 t = Twitter(auth=authen())
94a5f62e 165 num = HOME_TWEET_NUM
7b674cef 166 if g['stuff'].isdigit():
94a5f62e 167 num = g['stuff']
168 for tweet in reversed(t.statuses.home_timeline(count=num)):
7b674cef 169 draw(t=tweet)
94a5f62e 170 printNicely('')
7b674cef 171
172
173def view():
174 """
175 Friend view
176 """
177 t = Twitter(auth=authen())
178 user = g['stuff'].split()[0]
b8fbcb70 179 if user[0] == '@':
180 try:
94a5f62e 181 num = int(g['stuff'].split()[1])
b8fbcb70 182 except:
94a5f62e 183 num = HOME_TWEET_NUM
184 for tweet in reversed(t.statuses.user_timeline(count=num, screen_name=user[1:])):
b8fbcb70 185 draw(t=tweet)
94a5f62e 186 printNicely('')
b8fbcb70 187 else:
188 print(red('A name should begin with a \'@\''))
7b674cef 189
190
f405a7d0 191def tweet():
54277114 192 """
7b674cef 193 Tweet
54277114
O
194 """
195 t = Twitter(auth=authen())
f405a7d0 196 t.statuses.update(status=g['stuff'])
94a5f62e 197 g['prefix'] = False
f405a7d0 198
b2b933a9 199
1ba4abfd
O
200def retweet():
201 """
202 ReTweet
203 """
204 t = Twitter(auth=authen())
205 try:
206 id = int(g['stuff'].split()[0])
207 tid = db.rainbow_query(id)[0].tweet_id
b2b933a9 208 t.statuses.retweet(id=tid, include_entities=False, trim_user=True)
1ba4abfd
O
209 except:
210 print(red('Sorry I can\'t retweet for you.'))
94a5f62e 211 g['prefix'] = False
1ba4abfd
O
212
213
7b674cef 214def reply():
829cc2d8 215 """
7b674cef 216 Reply
829cc2d8
O
217 """
218 t = Twitter(auth=authen())
7b674cef 219 try:
220 id = int(g['stuff'].split()[0])
18cab06a
O
221 tid = db.rainbow_query(id)[0].tweet_id
222 user = t.statuses.show(id=tid)['user']['screen_name']
7b674cef 223 status = ' '.join(g['stuff'].split()[1:])
224 status = '@' + user + ' ' + status.decode('utf-8')
18cab06a 225 t.statuses.update(status=status, in_reply_to_status_id=tid)
7b674cef 226 except:
227 print(red('Sorry I can\'t understand.'))
94a5f62e 228 g['prefix'] = False
7b674cef 229
230
231def delete():
232 """
233 Delete
234 """
235 t = Twitter(auth=authen())
236 try:
237 id = int(g['stuff'].split()[0])
18cab06a
O
238 tid = db.rainbow_query(id)[0].tweet_id
239 t.statuses.destroy(id=tid)
7b674cef 240 print(green('Okay it\'s gone.'))
241 except:
242 print(red('Sorry I can\'t delete this tweet for you.'))
829cc2d8
O
243
244
f405a7d0
O
245def search():
246 """
7b674cef 247 Search
f405a7d0
O
248 """
249 t = Twitter(auth=authen())
94a5f62e 250 try:
251 if g['stuff'][0] == '#':
252 rel = t.search.tweets(q=g['stuff'])['statuses']
253 print('Newest', SEARCH_MAX_RECORD, 'tweet:')
88cbc498 254 for i in reversed(xrange(SEARCH_MAX_RECORD)):
94a5f62e 255 draw(t=rel[i], keyword=g['stuff'].strip()[1:])
256 printNicely('')
257 else:
258 print(red('A keyword should be a hashtag (like \'#AKB48\')'))
259 except:
260 print(red('Sorry I can\'t understand.'))
b2b933a9 261
f405a7d0 262
843647ad
O
263def friend():
264 """
265 List of friend (following)
266 """
267 t = Twitter(auth=authen())
268 g['friends'] = t.friends.ids()['ids']
269 for i in g['friends']:
270 screen_name = t.users.lookup(user_id=i)[0]['screen_name']
22be990e 271 user = cycle_color('@' + screen_name)
843647ad 272 print(user, end=' ')
22be990e 273 print('\n')
843647ad
O
274
275
276def follower():
277 """
278 List of follower
279 """
280 t = Twitter(auth=authen())
281 g['followers'] = t.followers.ids()['ids']
282 for i in g['followers']:
283 screen_name = t.users.lookup(user_id=i)[0]['screen_name']
22be990e 284 user = cycle_color('@' + screen_name)
843647ad 285 print(user, end=' ')
22be990e 286 print('\n')
843647ad
O
287
288
f405a7d0
O
289def help():
290 """
7b674cef 291 Help
f405a7d0
O
292 """
293 usage = '''
294 Hi boss! I'm ready to serve you right now!
006f8cc4 295 -------------------------------------------------------------
c8f6a173 296 "home" will show your timeline. "home 7" will show 7 tweet.
b8fbcb70 297 "view @bob" will show your friend @bob's home.
7b674cef 298 "t oops" will tweet "oops" immediately.
1ba4abfd 299 "rt 12345" will retweet to tweet with id "12345".
a30eeccb 300 "rep 12345 oops" will reply "oops" to tweet with id "12345".
7b674cef 301 "del 12345" will delete tweet with id "12345".
b8fbcb70 302 "s #AKB48" will search for "AKB48" and return 5 newest tweet.
a30eeccb 303 "fr" will list out your following people.
304 "fl" will list out your followers.
c8f6a173 305 "h" will show this help again.
a30eeccb 306 "c" will clear the terminal.
307 "q" will exit.
006f8cc4 308 -------------------------------------------------------------
843647ad 309 Have fun and hang tight!
f405a7d0
O
310 '''
311 printNicely(usage)
f405a7d0
O
312
313
843647ad 314def clear():
f405a7d0 315 """
7b674cef 316 Clear screen
f405a7d0 317 """
843647ad 318 os.system('clear')
7186f557 319 g['prefix'] = False
f405a7d0
O
320
321
843647ad 322def quit():
b8dda704
O
323 """
324 Exit all
325 """
8e633322 326 os.system('rm -rf rainbow.db')
843647ad
O
327 os.kill(g['stream_pid'], signal.SIGKILL)
328 sys.exit()
b8dda704
O
329
330
94a5f62e 331def reset():
f405a7d0 332 """
94a5f62e 333 Reset prefix of line
f405a7d0 334 """
94a5f62e 335 g['prefix'] = True
54277114
O
336
337
94a5f62e 338def process(cmd):
54277114 339 """
94a5f62e 340 Process switch
54277114 341 """
94a5f62e 342 return dict(zip(
343 cmdset,
b2b933a9 344 [
345 home,
346 view,
347 tweet,
348 retweet,
349 reply,
350 delete,
351 search,
352 friend,
353 follower,
354 help,
355 clear,
356 quit
357 ]
94a5f62e 358 )).get(cmd, reset)
359
360
361def listen():
362 init_interactive_shell(cmdset)
b2b933a9 363 first = True
364 while True:
94a5f62e 365 if g['prefix'] and not first:
366 line = raw_input(g['decorated_name'])
367 else:
368 line = raw_input()
843647ad
O
369 try:
370 cmd = line.split()[0]
371 except:
372 cmd = ''
f405a7d0 373 # Save cmd to global variable and call process
843647ad
O
374 g['stuff'] = ' '.join(line.split()[1:])
375 process(cmd)()
94a5f62e 376 first = False
54277114
O
377
378
379def stream():
380 """
f405a7d0 381 Track the stream
54277114
O
382 """
383 args = parse_arguments()
384
385 # The Logo
386 ascii_art()
91476ec3
O
387 # These arguments are optional:
388 stream_args = dict(
389 timeout=args.timeout,
390 block=not args.no_block,
391 heartbeat_timeout=args.heartbeat_timeout)
392
393 # Track keyword
394 query_args = dict()
395 if args.track_keywords:
396 query_args['track'] = args.track_keywords
397
398 # Get stream
2a6238f5 399 stream = TwitterStream(
22be990e 400 auth=authen(),
c0440e50 401 domain=DOMAIN,
2a6238f5 402 **stream_args)
91476ec3
O
403 tweet_iter = stream.user(**query_args)
404
405 # Iterate over the sample stream.
406 for tweet in tweet_iter:
407 if tweet is None:
408 printNicely("-- None --")
409 elif tweet is Timeout:
410 printNicely("-- Timeout --")
411 elif tweet is HeartbeatTimeout:
412 printNicely("-- Heartbeat Timeout --")
413 elif tweet is Hangup:
414 printNicely("-- Hangup --")
415 elif tweet.get('text'):
f405a7d0 416 draw(t=tweet)
54277114
O
417
418
419def fly():
420 """
421 Main function
422 """
423 get_decorated_name()
94a5f62e 424 g['prefix'] = True
22be990e 425 p = Process(target=stream)
54277114 426 p.start()
f405a7d0 427 g['stream_pid'] = p.pid
94a5f62e 428 listen()