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