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