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