trivival coloring
[rainbowstream.git] / rainbowstream / draw.py
1 import random
2 import textwrap
3 import itertools
4 import requests
5 import locale
6 import arrow
7 import re
8 import os
9
10 from twitter.util import printNicely
11 from functools import wraps
12 from pyfiglet import figlet_format
13 from dateutil import parser
14 from .c_image import *
15 from .colors import *
16 from .config import *
17 from .py3patch import *
18 from .emoji import *
19
20 # Draw global variables
21 dg = {}
22
23
24 def init_cycle():
25 """
26 Init the cycle
27 """
28 colors_shuffle = [globals()[i.encode('utf8')]
29 if not str(i).isdigit()
30 else term_color(int(i))
31 for i in c['CYCLE_COLOR']]
32 return itertools.cycle(colors_shuffle)
33
34
35 def start_cycle():
36 """
37 Notify from rainbow
38 """
39 dg['cyc'] = init_cycle()
40 dg['cache'] = {}
41 dg['humanize_unsupported'] = False
42
43
44 def order_rainbow(s):
45 """
46 Print a string with ordered color with each character
47 """
48 colors_shuffle = [globals()[i.encode('utf8')]
49 if not str(i).isdigit()
50 else term_color(int(i))
51 for i in c['CYCLE_COLOR']]
52 colored = [colors_shuffle[i % 7](s[i]) for i in xrange(len(s))]
53 return ''.join(colored)
54
55
56 def random_rainbow(s):
57 """
58 Print a string with random color with each character
59 """
60 colors_shuffle = [globals()[i.encode('utf8')]
61 if not str(i).isdigit()
62 else term_color(int(i))
63 for i in c['CYCLE_COLOR']]
64 colored = [random.choice(colors_shuffle)(i) for i in s]
65 return ''.join(colored)
66
67
68 def Memoize(func):
69 """
70 Memoize decorator
71 """
72 @wraps(func)
73 def wrapper(*args):
74 if args not in dg['cache']:
75 dg['cache'][args] = func(*args)
76 return dg['cache'][args]
77 return wrapper
78
79
80 @Memoize
81 def cycle_color(s):
82 """
83 Cycle the colors_shuffle
84 """
85 return next(dg['cyc'])(s)
86
87
88 def ascii_art(text):
89 """
90 Draw the Ascii Art
91 """
92 fi = figlet_format(text, font='doom')
93 print('\n'.join(
94 [next(dg['cyc'])(i) for i in fi.split('\n')]
95 ))
96
97
98 def check_config():
99 """
100 Check if config is changed
101 """
102 changed = False
103 data = get_all_config()
104 for key in c:
105 if key in data:
106 if data[key] != c[key]:
107 changed = True
108 if changed:
109 reload_config()
110
111
112 def validate_theme(theme):
113 """
114 Validate a theme exists or not
115 """
116 # Theme changed check
117 files = os.listdir(os.path.dirname(__file__) + '/colorset')
118 themes = [f.split('.')[0] for f in files if f.split('.')[-1] == 'json']
119 return theme in themes
120
121
122 def reload_theme(value, prev):
123 """
124 Check current theme and update if necessary
125 """
126 if value != prev:
127 config = os.path.dirname(
128 __file__) + '/colorset/' + value + '.json'
129 # Load new config
130 data = load_config(config)
131 if data:
132 for d in data:
133 c[d] = data[d]
134 # Restart color cycle and update config
135 start_cycle()
136 set_config('THEME', value)
137 return value
138 return prev
139
140
141 def color_func(func_name):
142 """
143 Call color function base on name
144 """
145 if str(func_name).isdigit():
146 return term_color(int(func_name))
147 return globals()[func_name]
148
149
150 def fallback_humanize(date, fallback_format=None, use_fallback=False):
151 """
152 Format date with arrow and a fallback format
153 """
154 # Convert to local timezone
155 date = arrow.get(date).to('local')
156 # Set default fallback format
157 if not fallback_format:
158 fallback_format = '%Y/%m/%d %H:%M:%S'
159 # Determine using fallback format or not by a variable
160 if use_fallback:
161 return date.datetime.strftime(fallback_format)
162 try:
163 # Use Arrow's humanize function
164 lang, encode = locale.getdefaultlocale()
165 clock = date.humanize(locale=lang)
166 except:
167 # Notice at the 1st time only
168 if not dg['humanize_unsupported']:
169 dg['humanize_unsupported'] = True
170 printNicely(
171 light_magenta('Humanized date display method does not support your $LC_ALL.'))
172 # Fallback when LC_ALL is not supported
173 clock = date.datetime.strftime(fallback_format)
174 return clock
175
176
177 def draw(t, keyword=None, humanize=True, noti=False, fil=[], ig=[]):
178 """
179 Draw the rainbow
180 """
181 # Check config
182 check_config()
183
184 # Retrieve tweet
185 tid = t['id']
186 text = t['text']
187 screen_name = t['user']['screen_name']
188 name = t['user']['name']
189 created_at = t['created_at']
190 favorited = t['favorited']
191 retweet_count = t['retweet_count']
192 favorite_count = t['favorite_count']
193 date = parser.parse(created_at)
194 try:
195 clock_format = c['FORMAT']['TWEET']['CLOCK_FORMAT']
196 except:
197 clock_format = '%Y/%m/%d %H:%M:%S'
198 clock = fallback_humanize(date, clock_format, not humanize)
199
200 # Pull extended retweet text
201 try:
202 text = 'RT @' + t['retweeted_status']['user']['screen_name'] + ': ' +\
203 t['retweeted_status']['text']
204 # Display as a notification
205 target = t['retweeted_status']['user']['screen_name']
206 if all([target == c['original_name'], not noti]):
207 # Add to evens for 'notification' command
208 t['event'] = 'retweet'
209 c['events'].append(t)
210 notify_retweet(t)
211 return
212 except:
213 pass
214
215 # Unescape HTML character
216 text = unescape(text)
217
218 # Get expanded url
219 try:
220 expanded_url = []
221 url = []
222 urls = t['entities']['urls']
223 for u in urls:
224 expanded_url.append(u['expanded_url'])
225 url.append(u['url'])
226 except:
227 expanded_url = None
228 url = None
229
230 # Get media
231 try:
232 media_url = []
233 media = t['entities']['media']
234 for m in media:
235 media_url.append(m['media_url'])
236 except:
237 media_url = None
238
239 # Filter and ignore
240 mytweet = screen_name == c['original_name']
241 screen_name = '@' + screen_name
242 fil = list(set((fil or []) + c['ONLY_LIST']))
243 ig = list(set((ig or []) + c['IGNORE_LIST']))
244 if fil and screen_name not in fil:
245 return
246 if ig and screen_name in ig:
247 return
248
249 # Get rainbow id
250 if tid not in c['tweet_dict']:
251 c['tweet_dict'].append(tid)
252 rid = len(c['tweet_dict']) - 1
253 else:
254 rid = c['tweet_dict'].index(tid)
255
256 # Format info
257 name = cycle_color(name)
258 if mytweet:
259 nick = color_func(c['TWEET']['mynick'])(screen_name)
260 else:
261 nick = color_func(c['TWEET']['nick'])(screen_name)
262 clock = clock
263 id = str(rid)
264 fav = ''
265 if favorited:
266 fav = color_func(c['TWEET']['favorited'])(u'\u2605')
267
268 tweet = text.split()
269 # Replace url
270 if expanded_url:
271 for index in xrange(len(expanded_url)):
272 tweet = lmap(
273 lambda x: expanded_url[index]
274 if x == url[index]
275 else x,
276 tweet)
277 # Highlight RT
278 tweet = lmap(
279 lambda x: color_func(c['TWEET']['rt'])(x)
280 if x == 'RT'
281 else x,
282 tweet)
283 # Highlight screen_name
284 tweet = lmap(lambda x: cycle_color(x) if x[0] == '@' else x, tweet)
285 # Highlight link
286 tweet = lmap(
287 lambda x: color_func(c['TWEET']['link'])(x)
288 if x.startswith('http')
289 else x,
290 tweet)
291 # Highlight hashtag
292 tweet = lmap(
293 lambda x: color_func(c['TWEET']['hashtag'])(x)
294 if x.startswith('#')
295 else x,
296 tweet)
297 # Highlight my tweet
298 if mytweet:
299 tweet = [color_func(c['TWEET']['mytweet'])(x)
300 for x in tweet
301 if not any([
302 x == 'RT',
303 x.startswith('http'),
304 x.startswith('#')])
305 ]
306 # Highlight keyword
307 tweet = ' '.join(tweet)
308 if keyword:
309 roj = re.search(keyword, tweet, re.IGNORECASE)
310 if roj:
311 occur = roj.group()
312 ary = tweet.split(occur)
313 delimiter = color_func(c['TWEET']['keyword'])(occur)
314 tweet = delimiter.join(ary)
315
316 # Load config formater
317 formater = ''
318 try:
319 formater = c['FORMAT']['TWEET']['DISPLAY']
320 formater = name.join(formater.split('#name'))
321 formater = nick.join(formater.split('#nick'))
322 formater = fav.join(formater.split('#fav'))
323 formater = tweet.join(formater.split('#tweet'))
324 # Change clock word
325 word = [wo for wo in formater.split() if '#clock' in wo][0]
326 delimiter = color_func(c['TWEET']['clock'])(
327 clock.join(word.split('#clock')))
328 formater = delimiter.join(formater.split(word))
329 # Change id word
330 word = [wo for wo in formater.split() if '#id' in wo][0]
331 delimiter = color_func(c['TWEET']['id'])(id.join(word.split('#id')))
332 formater = delimiter.join(formater.split(word))
333 # Change retweet count word
334 word = [wo for wo in formater.split() if '#rt_count' in wo][0]
335 delimiter = color_func(c['TWEET']['retweet_count'])(
336 str(retweet_count).join(word.split('#rt_count')))
337 formater = delimiter.join(formater.split(word))
338 # Change favorites count word
339 word = [wo for wo in formater.split() if '#fa_count' in wo][0]
340 delimiter = color_func(c['TWEET']['favorite_count'])(
341 str(favorite_count).join(word.split('#fa_count')))
342 formater = delimiter.join(formater.split(word))
343 formater = emojize(formater)
344 except:
345 pass
346
347 # Add spaces in begining of line if this is inside a notification
348 if noti:
349 formater = '\n '.join(formater.split('\n'))
350 # Reformat
351 if formater.startswith('\n'):
352 formater = formater[1:]
353
354 # Draw
355 printNicely(formater)
356
357 # Display Image
358 if c['IMAGE_ON_TERM'] and media_url:
359 for mu in media_url:
360 try:
361 response = requests.get(mu)
362 image_to_display(BytesIO(response.content))
363 except Exception:
364 printNicely(red('Sorry, image link is broken'))
365
366
367 def print_threads(d):
368 """
369 Print threads of messages
370 """
371 id = 1
372 rel = {}
373 for partner in d:
374 messages = d[partner]
375 count = len(messages)
376 screen_name = '@' + partner[0]
377 name = partner[1]
378 screen_name = color_func(c['MESSAGE']['partner'])(screen_name)
379 name = cycle_color(name)
380 thread_id = color_func(c['MESSAGE']['id'])('thread_id:' + str(id))
381 line = ' ' * 2 + name + ' ' + screen_name + \
382 ' (' + str(count) + ' message) ' + thread_id
383 printNicely(line)
384 rel[id] = partner
385 id += 1
386 dg['thread'] = d
387 return rel
388
389
390 def print_thread(partner, me_nick, me_name):
391 """
392 Print a thread of messages
393 """
394 # Sort messages by time
395 messages = dg['thread'][partner]
396 messages.sort(key=lambda x: parser.parse(x['created_at']))
397 # Use legacy display on non-ascii text message
398 ms = [m['text'] for m in messages]
399 ums = [m for m in ms if not all(ord(c) < 128 for c in m)]
400 if ums:
401 for m in messages:
402 print_message(m)
403 printNicely('')
404 return
405 # Print the first line
406 dg['frame_margin'] = margin = 2
407 partner_nick = partner[0]
408 partner_name = partner[1]
409 left_size = len(partner_nick) + len(partner_name) + 2
410 right_size = len(me_nick) + len(me_name) + 2
411 partner_nick = color_func(c['MESSAGE']['partner'])('@' + partner_nick)
412 partner_name = cycle_color(partner_name)
413 me_screen_name = color_func(c['MESSAGE']['me'])('@' + me_nick)
414 me_name = cycle_color(me_name)
415 left = ' ' * margin + partner_name + ' ' + partner_nick
416 right = me_name + ' ' + me_screen_name + ' ' * margin
417 h, w = os.popen('stty size', 'r').read().split()
418 w = int(w)
419 line = '{}{}{}'.format(
420 left, ' ' * (w - left_size - right_size - 2 * margin), right)
421 printNicely('')
422 printNicely(line)
423 printNicely('')
424 # Print messages
425 for m in messages:
426 if m['sender_screen_name'] == me_nick:
427 print_right_message(m)
428 elif m['recipient_screen_name'] == me_nick:
429 print_left_message(m)
430
431
432 def print_right_message(m):
433 """
434 Print a message on the right of screen
435 """
436 h, w = os.popen('stty size', 'r').read().split()
437 w = int(w)
438 frame_width = w // 3 - dg['frame_margin']
439 frame_width = max(c['THREAD_MIN_WIDTH'], frame_width)
440 step = frame_width - 2 * dg['frame_margin']
441 slicing = textwrap.wrap(m['text'], step)
442 spaces = w - frame_width - dg['frame_margin']
443 dotline = ' ' * spaces + '-' * frame_width
444 dotline = color_func(c['MESSAGE']['me_frame'])(dotline)
445 # Draw the frame
446 printNicely(dotline)
447 for line in slicing:
448 fill = step - len(line)
449 screen_line = ' ' * spaces + '| ' + line + ' ' * fill + ' '
450 if slicing[-1] == line:
451 screen_line = screen_line + ' >'
452 else:
453 screen_line = screen_line + '|'
454 screen_line = color_func(c['MESSAGE']['me_frame'])(screen_line)
455 printNicely(screen_line)
456 printNicely(dotline)
457 # Format clock
458 date = parser.parse(m['created_at'])
459 date = arrow.get(date).to('local').datetime
460 clock_format = '%Y/%m/%d %H:%M:%S'
461 try:
462 clock_format = c['FORMAT']['MESSAGE']['CLOCK_FORMAT']
463 except:
464 pass
465 clock = date.strftime(clock_format)
466 # Format id
467 if m['id'] not in c['message_dict']:
468 c['message_dict'].append(m['id'])
469 rid = len(c['message_dict']) - 1
470 else:
471 rid = c['message_dict'].index(m['id'])
472 id = str(rid)
473 # Print meta
474 formater = ''
475 try:
476 virtual_meta = formater = c['THREAD_META_RIGHT']
477 virtual_meta = clock.join(virtual_meta.split('#clock'))
478 virtual_meta = id.join(virtual_meta.split('#id'))
479 # Change clock word
480 word = [wo for wo in formater.split() if '#clock' in wo][0]
481 delimiter = color_func(c['MESSAGE']['clock'])(
482 clock.join(word.split('#clock')))
483 formater = delimiter.join(formater.split(word))
484 # Change id word
485 word = [wo for wo in formater.split() if '#id' in wo][0]
486 delimiter = color_func(c['MESSAGE']['id'])(id.join(word.split('#id')))
487 formater = delimiter.join(formater.split(word))
488 formater = emojize(formater)
489 except Exception:
490 printNicely(red('Wrong format in config.'))
491 return
492 meta = formater
493 line = ' ' * (w - len(virtual_meta) - dg['frame_margin']) + meta
494 printNicely(line)
495
496
497 def print_left_message(m):
498 """
499 Print a message on the left of screen
500 """
501 h, w = os.popen('stty size', 'r').read().split()
502 w = int(w)
503 frame_width = w // 3 - dg['frame_margin']
504 frame_width = max(c['THREAD_MIN_WIDTH'], frame_width)
505 step = frame_width - 2 * dg['frame_margin']
506 slicing = textwrap.wrap(m['text'], step)
507 spaces = dg['frame_margin']
508 dotline = ' ' * spaces + '-' * frame_width
509 dotline = color_func(c['MESSAGE']['partner_frame'])(dotline)
510 # Draw the frame
511 printNicely(dotline)
512 for line in slicing:
513 fill = step - len(line)
514 screen_line = ' ' + line + ' ' * fill + ' |'
515 if slicing[-1] == line:
516 screen_line = ' ' * (spaces - 1) + '< ' + screen_line
517 else:
518 screen_line = ' ' * spaces + '|' + screen_line
519 screen_line = color_func(c['MESSAGE']['partner_frame'])(screen_line)
520 printNicely(screen_line)
521 printNicely(dotline)
522 # Format clock
523 date = parser.parse(m['created_at'])
524 date = arrow.get(date).to('local').datetime
525 clock_format = '%Y/%m/%d %H:%M:%S'
526 try:
527 clock_format = c['FORMAT']['MESSAGE']['CLOCK_FORMAT']
528 except:
529 pass
530 clock = date.strftime(clock_format)
531 # Format id
532 if m['id'] not in c['message_dict']:
533 c['message_dict'].append(m['id'])
534 rid = len(c['message_dict']) - 1
535 else:
536 rid = c['message_dict'].index(m['id'])
537 id = str(rid)
538 # Print meta
539 formater = ''
540 try:
541 virtual_meta = formater = c['THREAD_META_LEFT']
542 virtual_meta = clock.join(virtual_meta.split('#clock'))
543 virtual_meta = id.join(virtual_meta.split('#id'))
544 # Change clock word
545 word = [wo for wo in formater.split() if '#clock' in wo][0]
546 delimiter = color_func(c['MESSAGE']['clock'])(
547 clock.join(word.split('#clock')))
548 formater = delimiter.join(formater.split(word))
549 # Change id word
550 word = [wo for wo in formater.split() if '#id' in wo][0]
551 delimiter = color_func(c['MESSAGE']['id'])(id.join(word.split('#id')))
552 formater = delimiter.join(formater.split(word))
553 formater = emojize(formater)
554 except Exception:
555 printNicely(red('Wrong format in config.'))
556 return
557 meta = formater
558 line = ' ' * dg['frame_margin'] + meta
559 printNicely(line)
560
561
562 def print_message(m):
563 """
564 Print direct message
565 """
566 # Retrieve message
567 sender_screen_name = '@' + m['sender_screen_name']
568 sender_name = m['sender']['name']
569 text = unescape(m['text'])
570 recipient_screen_name = '@' + m['recipient_screen_name']
571 recipient_name = m['recipient']['name']
572 mid = m['id']
573 date = parser.parse(m['created_at'])
574 date = arrow.get(date).to('local').datetime
575 clock_format = '%Y/%m/%d %H:%M:%S'
576 try:
577 clock_format = c['FORMAT']['MESSAGE']['CLOCK_FORMAT']
578 except:
579 pass
580 clock = date.strftime(clock_format)
581
582 # Get rainbow id
583 if mid not in c['message_dict']:
584 c['message_dict'].append(mid)
585 rid = len(c['message_dict']) - 1
586 else:
587 rid = c['message_dict'].index(mid)
588
589 # Draw
590 sender_name = cycle_color(sender_name)
591 sender_nick = color_func(c['MESSAGE']['sender'])(sender_screen_name)
592 recipient_name = cycle_color(recipient_name)
593 recipient_nick = color_func(
594 c['MESSAGE']['recipient'])(recipient_screen_name)
595 to = color_func(c['MESSAGE']['to'])('>>>')
596 clock = clock
597 id = str(rid)
598
599 text = ''.join(lmap(lambda x: x + ' ' if x == '\n' else x, text))
600
601 # Load config formater
602 try:
603 formater = c['FORMAT']['MESSAGE']['DISPLAY']
604 formater = sender_name.join(formater.split("#sender_name"))
605 formater = sender_nick.join(formater.split("#sender_nick"))
606 formater = to.join(formater.split("#to"))
607 formater = recipient_name.join(formater.split("#recipient_name"))
608 formater = recipient_nick.join(formater.split("#recipient_nick"))
609 formater = text.join(formater.split("#message"))
610 # Change clock word
611 word = [wo for wo in formater.split() if '#clock' in wo][0]
612 delimiter = color_func(c['MESSAGE']['clock'])(
613 clock.join(word.split('#clock')))
614 formater = delimiter.join(formater.split(word))
615 # Change id word
616 word = [wo for wo in formater.split() if '#id' in wo][0]
617 delimiter = color_func(c['MESSAGE']['id'])(id.join(word.split('#id')))
618 formater = delimiter.join(formater.split(word))
619 formater = emojize(formater)
620 except:
621 printNicely(red('Wrong format in config.'))
622 return
623
624 # Draw
625 printNicely(formater)
626
627
628 def notify_retweet(t):
629 """
630 Notify a retweet
631 """
632 source = t['user']
633 created_at = t['created_at']
634 # Format
635 source_user = cycle_color(source['name']) + \
636 color_func(c['NOTIFICATION']['source_nick'])(
637 ' @' + source['screen_name'])
638 notify = color_func(c['NOTIFICATION']['notify'])(
639 'retweeted your tweet')
640 date = parser.parse(created_at)
641 clock = fallback_humanize(date)
642 clock = color_func(c['NOTIFICATION']['clock'])(clock)
643 meta = c['NOTIFY_FORMAT']
644 meta = source_user.join(meta.split('#source_user'))
645 meta = notify.join(meta.split('#notify'))
646 meta = clock.join(meta.split('#clock'))
647 meta = emojize(meta)
648 # Output
649 printNicely('')
650 printNicely(meta)
651 draw(t=t['retweeted_status'], noti=True)
652
653
654 def notify_favorite(e):
655 """
656 Notify a favorite event
657 """
658 # Retrieve info
659 target = e['target']
660 if target['screen_name'] != c['original_name']:
661 return
662 source = e['source']
663 target_object = e['target_object']
664 created_at = e['created_at']
665 # Format
666 source_user = cycle_color(source['name']) + \
667 color_func(c['NOTIFICATION']['source_nick'])(
668 ' @' + source['screen_name'])
669 notify = color_func(c['NOTIFICATION']['notify'])(
670 'favorited your tweet')
671 date = parser.parse(created_at)
672 clock = fallback_humanize(date)
673 clock = color_func(c['NOTIFICATION']['clock'])(clock)
674 meta = c['NOTIFY_FORMAT']
675 meta = source_user.join(meta.split('#source_user'))
676 meta = notify.join(meta.split('#notify'))
677 meta = clock.join(meta.split('#clock'))
678 meta = emojize(meta)
679 # Output
680 printNicely('')
681 printNicely(meta)
682 draw(t=target_object, noti=True)
683
684
685 def notify_unfavorite(e):
686 """
687 Notify a unfavorite event
688 """
689 # Retrieve info
690 target = e['target']
691 if target['screen_name'] != c['original_name']:
692 return
693 source = e['source']
694 target_object = e['target_object']
695 created_at = e['created_at']
696 # Format
697 source_user = cycle_color(source['name']) + \
698 color_func(c['NOTIFICATION']['source_nick'])(
699 ' @' + source['screen_name'])
700 notify = color_func(c['NOTIFICATION']['notify'])(
701 'unfavorited your tweet')
702 date = parser.parse(created_at)
703 clock = fallback_humanize(date)
704 clock = color_func(c['NOTIFICATION']['clock'])(clock)
705 meta = c['NOTIFY_FORMAT']
706 meta = source_user.join(meta.split('#source_user'))
707 meta = notify.join(meta.split('#notify'))
708 meta = clock.join(meta.split('#clock'))
709 meta = emojize(meta)
710 # Output
711 printNicely('')
712 printNicely(meta)
713 draw(t=target_object, noti=True)
714
715
716 def notify_follow(e):
717 """
718 Notify a follow event
719 """
720 # Retrieve info
721 target = e['target']
722 if target['screen_name'] != c['original_name']:
723 return
724 source = e['source']
725 created_at = e['created_at']
726 # Format
727 source_user = cycle_color(source['name']) + \
728 color_func(c['NOTIFICATION']['source_nick'])(
729 ' @' + source['screen_name'])
730 notify = color_func(c['NOTIFICATION']['notify'])(
731 'followed you')
732 date = parser.parse(created_at)
733 clock = fallback_humanize(date)
734 clock = color_func(c['NOTIFICATION']['clock'])(clock)
735 meta = c['NOTIFY_FORMAT']
736 meta = source_user.join(meta.split('#source_user'))
737 meta = notify.join(meta.split('#notify'))
738 meta = clock.join(meta.split('#clock'))
739 meta = emojize(meta)
740 # Output
741 printNicely('')
742 printNicely(meta)
743
744
745 def notify_list_member_added(e):
746 """
747 Notify a list_member_added event
748 """
749 # Retrieve info
750 target = e['target']
751 if target['screen_name'] != c['original_name']:
752 return
753 source = e['source']
754 target_object = [e['target_object']] # list of Twitter list
755 created_at = e['created_at']
756 # Format
757 source_user = cycle_color(source['name']) + \
758 color_func(c['NOTIFICATION']['source_nick'])(
759 ' @' + source['screen_name'])
760 notify = color_func(c['NOTIFICATION']['notify'])(
761 'added you to a list')
762 date = parser.parse(created_at)
763 clock = fallback_humanize(date)
764 clock = color_func(c['NOTIFICATION']['clock'])(clock)
765 meta = c['NOTIFY_FORMAT']
766 meta = source_user.join(meta.split('#source_user'))
767 meta = notify.join(meta.split('#notify'))
768 meta = clock.join(meta.split('#clock'))
769 meta = emojize(meta)
770 # Output
771 printNicely('')
772 printNicely(meta)
773 print_list(target_object, noti=True)
774
775
776 def notify_list_member_removed(e):
777 """
778 Notify a list_member_removed event
779 """
780 # Retrieve info
781 target = e['target']
782 if target['screen_name'] != c['original_name']:
783 return
784 source = e['source']
785 target_object = [e['target_object']] # list of Twitter list
786 created_at = e['created_at']
787 # Format
788 source_user = cycle_color(source['name']) + \
789 color_func(c['NOTIFICATION']['source_nick'])(
790 ' @' + source['screen_name'])
791 notify = color_func(c['NOTIFICATION']['notify'])(
792 'removed you from a list')
793 date = parser.parse(created_at)
794 clock = fallback_humanize(date)
795 clock = color_func(c['NOTIFICATION']['clock'])(clock)
796 meta = c['NOTIFY_FORMAT']
797 meta = source_user.join(meta.split('#source_user'))
798 meta = notify.join(meta.split('#notify'))
799 meta = clock.join(meta.split('#clock'))
800 meta = emojize(meta)
801 # Output
802 printNicely('')
803 printNicely(meta)
804 print_list(target_object, noti=True)
805
806
807 def notify_list_user_subscribed(e):
808 """
809 Notify a list_user_subscribed event
810 """
811 # Retrieve info
812 target = e['target']
813 if target['screen_name'] != c['original_name']:
814 return
815 source = e['source']
816 target_object = [e['target_object']] # list of Twitter list
817 created_at = e['created_at']
818 # Format
819 source_user = cycle_color(source['name']) + \
820 color_func(c['NOTIFICATION']['source_nick'])(
821 ' @' + source['screen_name'])
822 notify = color_func(c['NOTIFICATION']['notify'])(
823 'subscribed to your list')
824 date = parser.parse(created_at)
825 clock = fallback_humanize(date)
826 clock = color_func(c['NOTIFICATION']['clock'])(clock)
827 meta = c['NOTIFY_FORMAT']
828 meta = source_user.join(meta.split('#source_user'))
829 meta = notify.join(meta.split('#notify'))
830 meta = clock.join(meta.split('#clock'))
831 meta = emojize(meta)
832 # Output
833 printNicely('')
834 printNicely(meta)
835 print_list(target_object, noti=True)
836
837
838 def notify_list_user_unsubscribed(e):
839 """
840 Notify a list_user_unsubscribed event
841 """
842 # Retrieve info
843 target = e['target']
844 if target['screen_name'] != c['original_name']:
845 return
846 source = e['source']
847 target_object = [e['target_object']] # list of Twitter list
848 created_at = e['created_at']
849 # Format
850 source_user = cycle_color(source['name']) + \
851 color_func(c['NOTIFICATION']['source_nick'])(
852 ' @' + source['screen_name'])
853 notify = color_func(c['NOTIFICATION']['notify'])(
854 'unsubscribed from your list')
855 date = parser.parse(created_at)
856 clock = fallback_humanize(date)
857 clock = color_func(c['NOTIFICATION']['clock'])(clock)
858 meta = c['NOTIFY_FORMAT']
859 meta = source_user.join(meta.split('#source_user'))
860 meta = notify.join(meta.split('#notify'))
861 meta = clock.join(meta.split('#clock'))
862 meta = emojize(meta)
863 # Output
864 printNicely('')
865 printNicely(meta)
866 print_list(target_object, noti=True)
867
868
869 def print_event(e):
870 """
871 Notify an event
872 """
873 event_dict = {
874 'retweet': notify_retweet,
875 'favorite': notify_favorite,
876 'unfavorite': notify_unfavorite,
877 'follow': notify_follow,
878 'list_member_added': notify_list_member_added,
879 'list_member_removed': notify_list_member_removed,
880 'list_user_subscribed': notify_list_user_subscribed,
881 'list_user_unsubscribed': notify_list_user_unsubscribed,
882 }
883 event_dict.get(e['event'], lambda *args: None)(e)
884
885
886 def show_profile(u):
887 """
888 Show a profile
889 """
890 # Retrieve info
891 name = u['name']
892 screen_name = u['screen_name']
893 description = u['description']
894 profile_image_url = u['profile_image_url']
895 location = u['location']
896 url = u['url']
897 created_at = u['created_at']
898 statuses_count = u['statuses_count']
899 friends_count = u['friends_count']
900 followers_count = u['followers_count']
901
902 # Create content
903 statuses_count = color_func(
904 c['PROFILE']['statuses_count'])(
905 str(statuses_count) +
906 ' tweets')
907 friends_count = color_func(
908 c['PROFILE']['friends_count'])(
909 str(friends_count) +
910 ' following')
911 followers_count = color_func(
912 c['PROFILE']['followers_count'])(
913 str(followers_count) +
914 ' followers')
915 count = statuses_count + ' ' + friends_count + ' ' + followers_count
916 user = cycle_color(
917 name) + color_func(c['PROFILE']['nick'])(' @' + screen_name + ' : ') + count
918 profile_image_raw_url = 'Profile photo: ' + \
919 color_func(c['PROFILE']['profile_image_url'])(profile_image_url)
920 description = ''.join(
921 lmap(lambda x: x + ' ' * 4 if x == '\n' else x, description))
922 description = color_func(c['PROFILE']['description'])(description)
923 location = 'Location : ' + color_func(c['PROFILE']['location'])(location)
924 url = 'URL : ' + (color_func(c['PROFILE']['url'])(url) if url else '')
925 date = parser.parse(created_at)
926 clock = fallback_humanize(date)
927 clock = 'Join at ' + color_func(c['PROFILE']['clock'])(clock)
928
929 # Format
930 line1 = u"{u:>{uw}}".format(
931 u=user,
932 uw=len(user) + 2,
933 )
934 line2 = u"{p:>{pw}}".format(
935 p=profile_image_raw_url,
936 pw=len(profile_image_raw_url) + 4,
937 )
938 line3 = u"{d:>{dw}}".format(
939 d=description,
940 dw=len(description) + 4,
941 )
942 line4 = u"{l:>{lw}}".format(
943 l=location,
944 lw=len(location) + 4,
945 )
946 line5 = u"{u:>{uw}}".format(
947 u=url,
948 uw=len(url) + 4,
949 )
950 line6 = u"{c:>{cw}}".format(
951 c=clock,
952 cw=len(clock) + 4,
953 )
954
955 # Display
956 printNicely('')
957 printNicely(line1)
958 if c['IMAGE_ON_TERM']:
959 try:
960 response = requests.get(profile_image_url)
961 image_to_display(BytesIO(response.content))
962 except:
963 pass
964 else:
965 printNicely(line2)
966 for line in [line3, line4, line5, line6]:
967 printNicely(line)
968 printNicely('')
969
970
971 def print_trends(trends):
972 """
973 Display topics
974 """
975 for topic in trends[:c['TREND_MAX']]:
976 name = topic['name']
977 url = topic['url']
978 line = cycle_color(name) + ': ' + color_func(c['TREND']['url'])(url)
979 printNicely(line)
980 printNicely('')
981
982
983 def print_list(group, noti=False):
984 """
985 Display a list
986 """
987 for grp in group:
988 # Format
989 name = grp['full_name']
990 name = color_func(c['GROUP']['name'])(name + ' : ')
991 member = str(grp['member_count'])
992 member = color_func(c['GROUP']['member'])(member + ' member')
993 subscriber = str(grp['subscriber_count'])
994 subscriber = color_func(
995 c['GROUP']['subscriber'])(
996 subscriber +
997 ' subscriber')
998 description = grp['description'].strip()
999 description = color_func(c['GROUP']['description'])(description)
1000 mode = grp['mode']
1001 mode = color_func(c['GROUP']['mode'])('Type: ' + mode)
1002 created_at = grp['created_at']
1003 date = parser.parse(created_at)
1004 clock = fallback_humanize(date)
1005 clock = 'Created at ' + color_func(c['GROUP']['clock'])(clock)
1006
1007 prefix = ' ' * 2
1008 # Add spaces in begining of line if this is inside a notification
1009 if noti:
1010 prefix = ' ' * 2 + prefix
1011 # Create lines
1012 line1 = prefix + name + member + ' ' + subscriber
1013 line2 = prefix + ' ' * 2 + description
1014 line3 = prefix + ' ' * 2 + mode
1015 line4 = prefix + ' ' * 2 + clock
1016
1017 # Display
1018 printNicely('')
1019 printNicely(line1)
1020 printNicely(line2)
1021 printNicely(line3)
1022 printNicely(line4)
1023
1024 if not noti:
1025 printNicely('')
1026
1027
1028 def show_calendar(month, date, rel):
1029 """
1030 Show the calendar in rainbow mode
1031 """
1032 month = random_rainbow(month)
1033 date = ' '.join([cycle_color(i) for i in date.split(' ')])
1034 today = str(int(os.popen('date +\'%d\'').read().strip()))
1035 # Display
1036 printNicely(month)
1037 printNicely(date)
1038 for line in rel:
1039 ary = line.split(' ')
1040 ary = lmap(
1041 lambda x: color_func(c['CAL']['today'])(x)
1042 if x == today
1043 else color_func(c['CAL']['days'])(x),
1044 ary)
1045 printNicely(' '.join(ary))
1046
1047
1048 def format_quote(tweet):
1049 """
1050 Quoting format
1051 """
1052 # Retrieve info
1053 screen_name = '@' + tweet['user']['screen_name']
1054 text = tweet['text']
1055 # Validate quote format
1056 if '#owner' not in c['QUOTE_FORMAT']:
1057 printNicely(light_magenta('Quote should contains #owner'))
1058 return False
1059 if '#comment' not in c['QUOTE_FORMAT']:
1060 printNicely(light_magenta('Quote format should have #comment'))
1061 return False
1062 # Build formater
1063 formater = ''
1064 try:
1065 formater = c['QUOTE_FORMAT']
1066 formater = screen_name.join(formater.split('#owner'))
1067 formater = text.join(formater.split('#tweet'))
1068 formater = u2str(formater)
1069 formater = emojize(formater)
1070 except:
1071 pass
1072 # Highlight like a tweet
1073 notice = formater.split()
1074 notice = lmap(
1075 lambda x: light_green(x)
1076 if x == '#comment'
1077 else x,
1078 notice)
1079 notice = lmap(
1080 lambda x: color_func(c['TWEET']['rt'])(x)
1081 if x == 'RT'
1082 else x,
1083 notice)
1084 notice = lmap(lambda x: cycle_color(x) if x[0] == '@' else x, notice)
1085 notice = lmap(
1086 lambda x: color_func(c['TWEET']['link'])(x)
1087 if x[0:4] == 'http'
1088 else x,
1089 notice)
1090 notice = lmap(
1091 lambda x: color_func(c['TWEET']['hashtag'])(x)
1092 if x.startswith('#')
1093 else x,
1094 notice)
1095 notice = ' '.join(notice)
1096 # Notice
1097 notice = light_magenta('Quoting: "') + notice + light_magenta('"')
1098 printNicely(notice)
1099 return formater
1100
1101
1102 # Start the color cycle
1103 start_cycle()