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