merge py3 support
[rainbowstream.git] / rainbowstream / draw.py
1 import random
2 import itertools
3 import requests
4 import datetime
5 import time
6
7 from twitter.util import printNicely
8 from functools import wraps
9 from pyfiglet import figlet_format
10 from dateutil import parser
11 from .c_image import *
12 from .colors import *
13 from .config import *
14 from .db import *
15 from .py3patch import *
16
17
18 db = RainbowDB()
19 g = {}
20
21 def init_cycle():
22 """
23 Init the cycle
24 """
25 colors_shuffle = [globals()[i.encode('utf8')]
26 if not i.startswith('term_')
27 else term_color(int(i[5:]))
28 for i in c['CYCLE_COLOR']]
29 return itertools.cycle(colors_shuffle)
30 g['cyc'] = init_cycle()
31 g['cache'] = {}
32
33
34 def reset_cycle():
35 """
36 Notify from rainbow
37 """
38 g['cyc'] = init_cycle()
39 g['cache'] = {}
40
41
42 def order_rainbow(s):
43 """
44 Print a string with ordered color with each character
45 """
46 colors_shuffle = [globals()[i.encode('utf8')]
47 if not i.startswith('term_')
48 else term_color(int(i[5:]))
49 for i in c['CYCLE_COLOR']]
50 colored = [colors_shuffle[i % 7](s[i]) for i in xrange(len(s))]
51 return ''.join(colored)
52
53
54 def random_rainbow(s):
55 """
56 Print a string with random color with each character
57 """
58 colors_shuffle = [globals()[i.encode('utf8')]
59 if not i.startswith('term_')
60 else term_color(int(i[5:]))
61 for i in c['CYCLE_COLOR']]
62 colored = [random.choice(colors_shuffle)(i) for i in s]
63 return ''.join(colored)
64
65
66 def Memoize(func):
67 """
68 Memoize decorator
69 """
70 @wraps(func)
71 def wrapper(*args):
72 if args not in g['cache']:
73 g['cache'][args] = func(*args)
74 return g['cache'][args]
75 return wrapper
76
77
78 @Memoize
79 def cycle_color(s):
80 """
81 Cycle the colors_shuffle
82 """
83 return next(g['cyc'])(s)
84
85
86 def ascii_art(text):
87 """
88 Draw the Ascii Art
89 """
90 fi = figlet_format(text, font='doom')
91 print('\n'.join(
92 [next(g['cyc'])(i) for i in fi.split('\n')]
93 ))
94
95
96 def show_calendar(month, date, rel):
97 """
98 Show the calendar in rainbow mode
99 """
100 month = random_rainbow(month)
101 date = ' '.join([cycle_color(i) for i in date.split(' ')])
102 today = str(int(os.popen('date +\'%d\'').read().strip()))
103 # Display
104 printNicely(month)
105 printNicely(date)
106 for line in rel:
107 ary = line.split(' ')
108 ary = lmap(lambda x: color_func(c['CAL']['today'])(x)
109 if x == today
110 else color_func(c['CAL']['days'])(x)
111 , ary)
112 printNicely(' '.join(ary))
113
114
115 def check_theme():
116 """
117 Check current theme and update if necessary
118 """
119 exists = db.theme_query()
120 themes = [t.theme_name for t in exists]
121 if c['theme'] != themes[0]:
122 c['theme'] = themes[0]
123 # Determine path
124 if c['theme'] == 'custom':
125 config = os.environ.get(
126 'HOME',
127 os.environ.get('USERPROFILE',
128 '')) + os.sep + '.rainbow_config.json'
129 else:
130 config = os.path.dirname(__file__) + '/colorset/'+c['theme']+'.json'
131 # Load new config
132 data = load_config(config)
133 if data:
134 for d in data:
135 c[d] = data[d]
136 # Re-init color cycle
137 g['cyc'] = init_cycle()
138
139
140 def color_func(func_name):
141 """
142 Call color function base on name
143 """
144 if func_name.startswith('term_') and func_name[5:].isdigit():
145 return term_color(int(func_name[5:]))
146 return globals()[func_name]
147
148
149 def draw(t, iot=False, keyword=None, fil=[], ig=[]):
150 """
151 Draw the rainbow
152 """
153
154 check_theme()
155 # Retrieve tweet
156 tid = t['id']
157 text = t['text']
158 screen_name = t['user']['screen_name']
159 name = t['user']['name']
160 created_at = t['created_at']
161 favorited = t['favorited']
162 date = parser.parse(created_at)
163 date = date - datetime.timedelta(seconds=time.timezone)
164 clock = date.strftime('%Y/%m/%d %H:%M:%S')
165
166 # Get expanded url
167 try:
168 expanded_url = []
169 url = []
170 urls = t['entities']['urls']
171 for u in urls:
172 expanded_url.append(u['expanded_url'])
173 url.append(u['url'])
174 except:
175 expanded_url = None
176 url = None
177
178 # Get media
179 try:
180 media_url = []
181 media = t['entities']['media']
182 for m in media:
183 media_url.append(m['media_url'])
184 except:
185 media_url = None
186
187 # Filter and ignore
188 screen_name = '@' + screen_name
189 if fil and screen_name not in fil:
190 return
191 if ig and screen_name in ig:
192 return
193
194 # Get rainbow id
195 res = db.tweet_to_rainbow_query(tid)
196 if not res:
197 db.tweet_store(tid)
198 res = db.tweet_to_rainbow_query(tid)
199 rid = res[0].rainbow_id
200
201 # Format info
202 user = cycle_color(
203 name) + color_func(c['TWEET']['nick'])(' ' + screen_name + ' ')
204 meta = color_func(c['TWEET']['clock'])(
205 '[' + clock + '] ') + color_func(c['TWEET']['id'])('[id=' + str(rid) + '] ')
206 if favorited:
207 meta = meta + color_func(c['TWEET']['favorited'])(u'\u2605')
208 tweet = text.split()
209 # Replace url
210 if expanded_url:
211 for index in range(len(expanded_url)):
212 tweet = lmap(
213 lambda x: expanded_url[index] if x == url[index] else x,
214 tweet)
215 # Highlight RT
216 tweet = lmap(
217 lambda x: color_func(
218 c['TWEET']['rt'])(x) if x == 'RT' else x,
219 tweet)
220 # Highlight screen_name
221 tweet = lmap(lambda x: cycle_color(x) if x[0] == '@' else x, tweet)
222 # Highlight link
223 tweet = lmap(
224 lambda x: color_func(
225 c['TWEET']['link'])(x) if x[
226 0:4] == 'http' else x,
227 tweet)
228 # Highlight search keyword
229 if keyword:
230 tweet = lmap(
231 lambda x: color_func(c['TWEET']['keyword'])(x) if
232 ''.join(c for c in x if c.isalnum()).lower() == keyword.lower()
233 else x,
234 tweet
235 )
236 # Recreate tweet
237 tweet = ' '.join(tweet)
238
239 # Draw rainbow
240 line1 = u"{u:>{uw}}:".format(
241 u=user,
242 uw=len(user) + 2,
243 )
244 line2 = u"{c:>{cw}}".format(
245 c=meta,
246 cw=len(meta) + 2,
247 )
248 line3 = ' ' + tweet
249
250 printNicely('')
251 printNicely(line1)
252 printNicely(line2)
253 printNicely(line3)
254
255 # Display Image
256 if iot and media_url:
257 for mu in media_url:
258 try:
259 response = requests.get(mu)
260 image_to_display(BytesIO(response.content))
261 except Exception:
262 printNicely(red('Sorry, image link is broken'))
263
264
265 def print_message(m):
266 """
267 Print direct message
268 """
269 sender_screen_name = '@' + m['sender_screen_name']
270 sender_name = m['sender']['name']
271 text = m['text']
272 recipient_screen_name = '@' + m['recipient_screen_name']
273 recipient_name = m['recipient']['name']
274 mid = m['id']
275 date = parser.parse(m['created_at'])
276 date = date - datetime.timedelta(seconds=time.timezone)
277 clock = date.strftime('%Y/%m/%d %H:%M:%S')
278
279 # Get rainbow id
280 res = db.message_to_rainbow_query(mid)
281 if not res:
282 db.message_store(mid)
283 res = db.message_to_rainbow_query(mid)
284 rid = res[0].rainbow_id
285
286 # Draw
287 sender = cycle_color(
288 sender_name) + color_func(c['MESSAGE']['sender'])(' ' + sender_screen_name + ' ')
289 recipient = cycle_color(recipient_name) + color_func(
290 c['MESSAGE']['recipient'])(
291 ' ' + recipient_screen_name + ' ')
292 user = sender + color_func(c['MESSAGE']['to'])(' >>> ') + recipient
293 meta = color_func(
294 c['MESSAGE']['clock'])(
295 '[' + clock + ']') + color_func(
296 c['MESSAGE']['id'])(
297 ' [message_id=' + str(rid) + '] ')
298 text = ''.join(lmap(lambda x: x + ' ' if x == '\n' else x, text))
299
300 line1 = u"{u:>{uw}}:".format(
301 u=user,
302 uw=len(user) + 2,
303 )
304 line2 = u"{c:>{cw}}".format(
305 c=meta,
306 cw=len(meta) + 2,
307 )
308
309 line3 = ' ' + text
310
311 printNicely('')
312 printNicely(line1)
313 printNicely(line2)
314 printNicely(line3)
315
316
317 def show_profile(u, iot=False):
318 """
319 Show a profile
320 """
321 # Retrieve info
322 name = u['name']
323 screen_name = u['screen_name']
324 description = u['description']
325 profile_image_url = u['profile_image_url']
326 location = u['location']
327 url = u['url']
328 created_at = u['created_at']
329 statuses_count = u['statuses_count']
330 friends_count = u['friends_count']
331 followers_count = u['followers_count']
332
333 # Create content
334 statuses_count = color_func(
335 c['PROFILE']['statuses_count'])(
336 str(statuses_count) +
337 ' tweets')
338 friends_count = color_func(
339 c['PROFILE']['friends_count'])(
340 str(friends_count) +
341 ' following')
342 followers_count = color_func(
343 c['PROFILE']['followers_count'])(
344 str(followers_count) +
345 ' followers')
346 count = statuses_count + ' ' + friends_count + ' ' + followers_count
347 user = cycle_color(
348 name) + color_func(c['PROFILE']['nick'])(' @' + screen_name + ' : ') + count
349 profile_image_raw_url = 'Profile photo: ' + \
350 color_func(c['PROFILE']['profile_image_url'])(profile_image_url)
351 description = ''.join(
352 lmap(lambda x: x + ' ' * 4 if x == '\n' else x, description))
353 description = color_func(c['PROFILE']['description'])(description)
354 location = 'Location : ' + color_func(c['PROFILE']['location'])(location)
355 url = 'URL : ' + (color_func(c['PROFILE']['url'])(url) if url else '')
356 date = parser.parse(created_at)
357 date = date - datetime.timedelta(seconds=time.timezone)
358 clock = date.strftime('%Y/%m/%d %H:%M:%S')
359 clock = 'Join at ' + color_func(c['PROFILE']['clock'])(clock)
360
361 # Format
362 line1 = u"{u:>{uw}}".format(
363 u=user,
364 uw=len(user) + 2,
365 )
366 line2 = u"{p:>{pw}}".format(
367 p=profile_image_raw_url,
368 pw=len(profile_image_raw_url) + 4,
369 )
370 line3 = u"{d:>{dw}}".format(
371 d=description,
372 dw=len(description) + 4,
373 )
374 line4 = u"{l:>{lw}}".format(
375 l=location,
376 lw=len(location) + 4,
377 )
378 line5 = u"{u:>{uw}}".format(
379 u=url,
380 uw=len(url) + 4,
381 )
382 line6 = u"{c:>{cw}}".format(
383 c=clock,
384 cw=len(clock) + 4,
385 )
386
387 # Display
388 printNicely('')
389 printNicely(line1)
390 if iot:
391 try:
392 response = requests.get(profile_image_url)
393 image_to_display(BytesIO(response.content), 2, 20)
394 except:
395 pass
396 else:
397 printNicely(line2)
398 for line in [line3, line4, line5, line6]:
399 printNicely(line)
400 printNicely('')
401
402
403 def print_trends(trends):
404 """
405 Display topics
406 """
407 for topic in trends[:c['TREND_MAX']]:
408 name = topic['name']
409 url = topic['url']
410 line = cycle_color(name) + ': ' + color_func(c['TREND']['url'])(url)
411 printNicely(line)
412 printNicely('')
413
414
415 def print_list(group):
416 """
417 Display a list
418 """
419 for g in group:
420 # Format
421 name = g['name']
422 name = color_func(c['GROUP']['name'])(name + ' : ')
423 member = str(g['member_count'])
424 member = color_func(c['GROUP']['member'])(member+' member')
425 subscriber = str(g['subscriber_count'])
426 subscriber = color_func(c['GROUP']['subscriber'])(subscriber+' subscriber')
427 description = g['description'].strip()
428 description = color_func(c['GROUP']['description'])(description)
429 mode = g['mode']
430 mode = color_func(c['GROUP']['mode'])('Type: '+mode)
431 created_at = g['created_at']
432 date = parser.parse(created_at)
433 date = date - datetime.timedelta(seconds=time.timezone)
434 clock = date.strftime('%Y/%m/%d %H:%M:%S')
435 clock = 'Created at ' + color_func(c['GROUP']['clock'])(clock)
436
437 # Save to db
438 db.list_store(g['id'],g['name'])
439
440 # Create lines
441 line1 = ' '*2 + name + member + ' ' + subscriber
442 line2 = ' '*4 + description
443 line3 = ' '*4 + mode
444 line4 = ' '*4 + clock
445
446 # Display
447 printNicely('')
448 printNicely(line1)
449 printNicely(line2)
450 printNicely(line3)
451 printNicely(line4)
452
453 printNicely('')
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490