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